102 lines
5.3 KiB
JavaScript
102 lines
5.3 KiB
JavaScript
const assert = require("node:assert/strict");
|
|
const fs = require("node:fs");
|
|
const path = require("node:path");
|
|
const vm = require("node:vm");
|
|
const { test } = require("node:test");
|
|
const ts = require("typescript");
|
|
|
|
function loadModule(relativePath, mocks = {}) {
|
|
const source = fs.readFileSync(path.join(__dirname, "..", relativePath), "utf8");
|
|
const compiled = ts.transpileModule(source, {
|
|
compilerOptions: { target: ts.ScriptTarget.ES2020, module: ts.ModuleKind.CommonJS, jsx: ts.JsxEmit.ReactJSX },
|
|
}).outputText;
|
|
const module = { exports: {} };
|
|
vm.runInNewContext(compiled, {
|
|
module, exports: module.exports, process, URLSearchParams, URL,
|
|
require: (name) => name in mocks ? mocks[name] : require(name),
|
|
});
|
|
return module.exports;
|
|
}
|
|
|
|
function nodes(element) {
|
|
if (!element || typeof element !== "object") return [];
|
|
const children = element.props?.children;
|
|
return [element, ...(Array.isArray(children) ? children.flat(Infinity) : [children]).flatMap(nodes)];
|
|
}
|
|
|
|
test("header reads company name and CMS navigation", async () => {
|
|
const { default: Header } = loadModule("components/layout/Header.tsx", {
|
|
"@/services/navigation.service": { getNavigationMenu: async () => ({ items: [{ label: "关于我们", url: "/about", open_in_new_tab: false }] }) },
|
|
"@/services/site-settings.service": { getSiteSettings: async () => ({ company_name: "测试软件公司" }) },
|
|
"./MobileNavigation": { default: () => null },
|
|
});
|
|
const tree = nodes(await Header());
|
|
assert.ok(tree.some((node) => node.props?.["aria-label"] === "测试软件公司首页"));
|
|
assert.ok(tree.some((node) => node.props?.href === "/about"));
|
|
assert.ok(tree.some((node) => node.props?.children === "测试软件公司"));
|
|
assert.ok(!tree.some((node) => node.props?.children === "世耀科技"));
|
|
});
|
|
|
|
test("footer does not invent an ICP number for an unconfigured company", async () => {
|
|
const { default: Footer } = loadModule("components/layout/Footer.tsx", {
|
|
"@/services/navigation.service": { getNavigationMenu: async () => null },
|
|
"@/services/site-settings.service": { getSiteSettings: async () => ({ company_name: "测试软件公司", icp_number: "" }) },
|
|
});
|
|
const tree = nodes(await Footer());
|
|
assert.ok(!tree.some((node) => node.props?.href === "https://beian.miit.gov.cn/"));
|
|
assert.ok(!tree.some((node) => typeof node.props?.children === "string" && node.props.children.includes("冀ICP备")));
|
|
});
|
|
|
|
test("about page uses CMS content, supports drafts and returns 404 when missing", async () => {
|
|
const page = { title: "关于我们", meta: { slug: "about" }, body: "<p>企业介绍</p>" };
|
|
let content = page;
|
|
let draft = null;
|
|
const { default: About } = loadModule("app/about/page.tsx", {
|
|
"next/navigation": { notFound: () => { throw new Error("NOT_FOUND"); } },
|
|
"@/services/legal.service": { getSimpleContentPageBySlug: async (slug) => { assert.equal(slug, "about"); return content; } },
|
|
"@/services/preview.service": { getPreviewOrFallback: async (type, fields, fallback) => { assert.equal(type, "core.simplecontentpage"); assert.equal(fields, "body"); return draft ?? fallback(); } },
|
|
"@/services/seo.service": {},
|
|
"@/components/layout/PageSEO": { default: () => null },
|
|
});
|
|
assert.ok(nodes(await About()).some((node) => node.props?.dangerouslySetInnerHTML?.__html === page.body));
|
|
draft = { ...page, body: "<p>未发布的介绍</p>" };
|
|
assert.ok(nodes(await About()).some((node) => node.props?.dangerouslySetInnerHTML?.__html === draft.body));
|
|
draft = { ...page, meta: { slug: "privacy-policy" }, body: "<p>其他页面的草稿</p>" };
|
|
assert.ok(nodes(await About()).some((node) => node.props?.dangerouslySetInnerHTML?.__html === page.body));
|
|
draft = null;
|
|
content = null;
|
|
await assert.rejects(About(), /NOT_FOUND/);
|
|
});
|
|
|
|
test("simple content preview resolves only supported about route", () => {
|
|
const { resolvePreviewPath } = loadModule("services/preview.service.ts", {
|
|
"next/headers": {}, "./api-client": {},
|
|
});
|
|
assert.equal(resolvePreviewPath("core.simplecontentpage", { meta: { slug: "about" } }), "/about");
|
|
assert.equal(resolvePreviewPath("core.simplecontentpage", { meta: { slug: "unknown" } }), null);
|
|
});
|
|
|
|
test("demo requests isolate industry paths and bypass shared caches", async () => {
|
|
const calls = [];
|
|
const { getDemoPage } = loadModule("services/demo.service.ts", {
|
|
"./api-client": { fetchAPI: async (url, options) => { calls.push({ url, options }); return {}; } },
|
|
});
|
|
await getDemoPage("manufacturing", ["products", "precision-parts"]);
|
|
await getDemoPage("consulting", ["about"]);
|
|
assert.equal(calls[0].url, "/api/v1/custom/core/demos/manufacturing/?path=products%2Fprecision-parts");
|
|
assert.equal(calls[1].url, "/api/v1/custom/core/demos/consulting/?path=about");
|
|
assert.ok(calls.every((call) => call.options.revalidate === 0));
|
|
});
|
|
|
|
test("default site chrome is hidden only on demo routes", () => {
|
|
let pathname = "/demo/software/products";
|
|
const { default: SiteChrome } = loadModule("components/layout/SiteChrome.tsx", {
|
|
"next/navigation": { usePathname: () => pathname },
|
|
});
|
|
assert.equal(SiteChrome({ children: "default header" }), null);
|
|
pathname = "/demo";
|
|
assert.equal(SiteChrome({ children: "default header" }), null);
|
|
pathname = "/products";
|
|
assert.equal(SiteChrome({ children: "default header" }).props.children, "default header");
|
|
});
|