chore: 前置快照-assetUrl前缀与学校切换收敛会话遗留(执行方案B1~B5前既有未提交变更)
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
/**
|
||||
* 路径参数型 POST 接口的 siteId 透传回归(publish 曾因 postJSON 丢第三参,
|
||||
* 请求裸发 POST /api/article/publish/{id},后端报「缺少必填参数:siteId」)。
|
||||
* alova 实例与 host.config(顶层 document 依赖)均 mock,node 环境纯断言。
|
||||
* host.config 用 vi.hoisted 可变对象:assetUrl 用例需要切换 serviceHost 模拟生产 /yschcms。
|
||||
*/
|
||||
const confState = vi.hoisted(() => ({
|
||||
conf: { baseHost: "", ssoHost: "", serviceHost: "", isMock: false, isDebug: false },
|
||||
}));
|
||||
|
||||
vi.mock("@/assets/config/host.config", () => ({
|
||||
default: confState.conf,
|
||||
}));
|
||||
|
||||
vi.mock("@/utils/alova/alova-iview", () => ({
|
||||
alovaInstance: {
|
||||
Get: vi.fn(),
|
||||
Post: vi.fn(() => ({})),
|
||||
},
|
||||
}));
|
||||
|
||||
import { articleApi, assetUrl } from "./cms";
|
||||
import { alovaInstance } from "@/utils/alova/alova-iview";
|
||||
|
||||
const JSON_HEADERS = { "Content-Type": "application/json;charset=UTF-8" };
|
||||
|
||||
describe("cms.ts 路径参数型 POST 的 query 透传", () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(alovaInstance.Post).mockClear();
|
||||
});
|
||||
|
||||
it("publish: siteId 以 query 参数随 Post config 发出", () => {
|
||||
articleApi.publish(5, 2);
|
||||
expect(alovaInstance.Post).toHaveBeenCalledWith("/api/article/publish/2", undefined, {
|
||||
headers: JSON_HEADERS,
|
||||
params: { siteId: 5 },
|
||||
});
|
||||
});
|
||||
|
||||
it("offline: 同款透传", () => {
|
||||
articleApi.offline(5, 3);
|
||||
expect(alovaInstance.Post).toHaveBeenCalledWith("/api/article/offline/3", undefined, {
|
||||
headers: JSON_HEADERS,
|
||||
params: { siteId: 5 },
|
||||
});
|
||||
});
|
||||
|
||||
it("topmost: siteId 与 enable 一并透传", () => {
|
||||
articleApi.topmost(5, 3, true);
|
||||
expect(alovaInstance.Post).toHaveBeenCalledWith("/api/article/topmost/3", undefined, {
|
||||
headers: JSON_HEADERS,
|
||||
params: { siteId: 5, enable: true },
|
||||
});
|
||||
});
|
||||
|
||||
it("save/delete: siteId 走 body,不带 params(守卫勿误伤)", () => {
|
||||
articleApi.delete({ id: 2, siteId: 5 });
|
||||
expect(alovaInstance.Post).toHaveBeenCalledWith("/api/article/delete", { id: 2, siteId: 5 }, {
|
||||
headers: JSON_HEADERS,
|
||||
params: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("assetUrl 站内资产路径补 serviceHost 前缀(线上主题截图破图回归)", () => {
|
||||
afterEach(() => {
|
||||
confState.conf.serviceHost = "";
|
||||
confState.conf.isMock = false;
|
||||
});
|
||||
|
||||
it("生产:/api 裸路径拼上 context-path(/yschcms)", () => {
|
||||
confState.conf.serviceHost = "/yschcms";
|
||||
expect(assetUrl("/api/theme/default/asset/screenshot.png")).toBe(
|
||||
"/yschcms/api/theme/default/asset/screenshot.png"
|
||||
);
|
||||
});
|
||||
|
||||
it("dev/mock:serviceHost 为空串时原样返回", () => {
|
||||
expect(assetUrl("/api/theme/default/asset/screenshot.png")).toBe(
|
||||
"/api/theme/default/asset/screenshot.png"
|
||||
);
|
||||
});
|
||||
|
||||
it("绝对地址(http/data/blob)不拼前缀", () => {
|
||||
confState.conf.serviceHost = "/yschcms";
|
||||
expect(assetUrl("https://cdn.example.com/x.png")).toBe("https://cdn.example.com/x.png");
|
||||
expect(assetUrl("data:image/png;base64,AAAA")).toBe("data:image/png;base64,AAAA");
|
||||
expect(assetUrl("blob:http://localhost/uuid")).toBe("blob:http://localhost/uuid");
|
||||
});
|
||||
|
||||
it("空值原样返回;isMock 时不拼 serviceHost", () => {
|
||||
expect(assetUrl("")).toBe("");
|
||||
confState.conf.serviceHost = "/yschcms";
|
||||
confState.conf.isMock = true;
|
||||
expect(assetUrl("/api/theme/default/asset/screenshot.png")).toBe(
|
||||
"/api/theme/default/asset/screenshot.png"
|
||||
);
|
||||
});
|
||||
});
|
||||
+16
-11
@@ -41,10 +41,11 @@ function get<T>(url: string, params?: IObj): Method {
|
||||
return alovaInstance.Get<T>(url, { params });
|
||||
}
|
||||
|
||||
/** POST JSON(@RequestBody) */
|
||||
function postJSON<T>(url: string, data?: unknown): Method {
|
||||
/** POST JSON(@RequestBody);params 为追加到 URL 的 query 参数(alova Post config 同 Get) */
|
||||
function postJSON<T>(url: string, data?: unknown, params?: IObj): Method {
|
||||
return alovaInstance.Post<T>(url, data, {
|
||||
headers: { "Content-Type": "application/json;charset=UTF-8" },
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -58,6 +59,16 @@ export function portalBase(siteCode: string): string {
|
||||
return `${conf.isMock ? "" : conf.serviceHost}/portal/${siteCode}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 后端下发的站内资产路径补 serviceHost 前缀(如主题截图 /api/theme/{name}/asset/screenshot.png)。
|
||||
* 生产 context-path=/yschcms,裸 /api 绝对路径会打到网关根上 404(破图);
|
||||
* dev/mock 下 serviceHost 为空串,拼接后与原路径一致不受影响。
|
||||
*/
|
||||
export function assetUrl(url: string): string {
|
||||
if (!url || /^(https?:|data:|blob:)/i.test(url)) return url;
|
||||
return `${conf.isMock ? "" : conf.serviceHost}${url}`;
|
||||
}
|
||||
|
||||
class AuthApi {
|
||||
/** 登录态:用户 + 可管理站点 + 是否超管 */
|
||||
getUser() {
|
||||
@@ -137,21 +148,15 @@ class ArticleApi {
|
||||
|
||||
/** 发布(publishTime 为未来时间即定时发布) */
|
||||
publish(siteId: number, id: number) {
|
||||
return postJSON<void>(`${ARTICLE}/publish/${id}`, undefined, {
|
||||
params: { siteId },
|
||||
});
|
||||
return postJSON<void>(`${ARTICLE}/publish/${id}`, undefined, { siteId });
|
||||
}
|
||||
|
||||
offline(siteId: number, id: number) {
|
||||
return postJSON<void>(`${ARTICLE}/offline/${id}`, undefined, {
|
||||
params: { siteId },
|
||||
});
|
||||
return postJSON<void>(`${ARTICLE}/offline/${id}`, undefined, { siteId });
|
||||
}
|
||||
|
||||
topmost(siteId: number, id: number, enable: boolean) {
|
||||
return postJSON<void>(`${ARTICLE}/topmost/${id}`, undefined, {
|
||||
params: { siteId, enable },
|
||||
});
|
||||
return postJSON<void>(`${ARTICLE}/topmost/${id}`, undefined, { siteId, enable });
|
||||
}
|
||||
|
||||
delete(data: DeleteRequest) {
|
||||
|
||||
Reference in New Issue
Block a user