236 lines
7.9 KiB
TypeScript
236 lines
7.9 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
import { compileWireFile } from "@wrnexus/compiler";
|
|
import { mountHtml } from "@wrnexus/test";
|
|
|
|
const root = join(import.meta.dir, "..");
|
|
const frameworkVersion = JSON.parse(readFileSync(join(root, "package.json"), "utf8")).wrnexus
|
|
.version as string;
|
|
const packagePages = join(root, "app", "pages", "packages");
|
|
const expected = [
|
|
"ai",
|
|
"auth",
|
|
"authz",
|
|
"benchmark",
|
|
"cache",
|
|
"captcha",
|
|
"cli",
|
|
"compiler",
|
|
"content",
|
|
"core",
|
|
"csr",
|
|
"db",
|
|
"dev-server",
|
|
"dev-toolbar",
|
|
"encryption",
|
|
"graphql",
|
|
"helpers",
|
|
"i18n",
|
|
"identity",
|
|
"image",
|
|
"jwt",
|
|
"language-server",
|
|
"mcp",
|
|
"mobile",
|
|
"native",
|
|
"oauth",
|
|
"observability",
|
|
"playground",
|
|
"plugin",
|
|
"pubsub",
|
|
"pwa",
|
|
"queue",
|
|
"reactive",
|
|
"realtime",
|
|
"router",
|
|
"security",
|
|
"ssr",
|
|
"store",
|
|
"styles",
|
|
"syntax",
|
|
"test",
|
|
"tracking",
|
|
"typecheck",
|
|
"ui",
|
|
"uploader",
|
|
"validation",
|
|
];
|
|
|
|
test("generates one detailed page for every published package", () => {
|
|
const generated = readdirSync(packagePages)
|
|
.filter((file) => file.endsWith(".wrn"))
|
|
.map((file) => file.replace(/\.wrn$/, ""))
|
|
.sort();
|
|
expect(generated).toEqual([...expected].sort());
|
|
});
|
|
|
|
test("every package page contains installation, guide, and complete API sections", () => {
|
|
for (const name of expected) {
|
|
const source = readFileSync(join(packagePages, `${name}.wrn`), "utf8");
|
|
expect(source).toContain(`bun add @wrnexus/${name}@${frameworkVersion}`);
|
|
expect(source).toContain('id="guide"');
|
|
expect(source).toContain('id="api"');
|
|
expect(source).toContain("Complete TypeScript API");
|
|
expect(source).toContain('class="on-this-page"');
|
|
expect(source).toContain('id="examples"');
|
|
expect(source).toContain('class="example-card"');
|
|
}
|
|
});
|
|
|
|
test("UI components use the dedicated external showcase", () => {
|
|
const reference = JSON.parse(
|
|
readFileSync(join(root, "node_modules", "@wrnexus", "ui", "component-reference.json"), "utf8"),
|
|
) as { count: number; components: Array<{ name: string; props: unknown[] }> };
|
|
expect(existsSync(join(root, "app", "pages", "components.wrn"))).toBe(false);
|
|
expect(
|
|
readdirSync(join(root, "app", "pages", "components")).filter((file) => file.endsWith(".wrn")),
|
|
).toHaveLength(0);
|
|
const uiPackage = readFileSync(join(packagePages, "ui.wrn"), "utf8");
|
|
expect(uiPackage).toContain('href="https://component.wrnexusjs.dev/"');
|
|
expect(reference.count).toBeGreaterThan(0);
|
|
expect(uiPackage).not.toContain("All 901 UI components and props");
|
|
});
|
|
|
|
test("primary navigation exposes the component catalog", () => {
|
|
for (const file of [
|
|
join(root, "app", "pages", "index.wrn"),
|
|
join(root, "app", "pages", "getting-started.wrn"),
|
|
join(root, "app", "pages", "packages", "core.wrn"),
|
|
]) {
|
|
expect(readFileSync(file, "utf8")).toContain(
|
|
'<a href="https://component.wrnexusjs.dev/">Components</a>',
|
|
);
|
|
}
|
|
});
|
|
|
|
test("every package page shows multiple named usage examples", () => {
|
|
for (const name of expected) {
|
|
const source = readFileSync(join(packagePages, `${name}.wrn`), "utf8");
|
|
expect(source.match(/class="example-card"/g)?.length ?? 0).toBeGreaterThanOrEqual(2);
|
|
expect(source).not.toContain("<h3>Example 1</h3>");
|
|
expect(source).not.toContain(`import * as packageApi from "@wrnexus/${name}"`);
|
|
}
|
|
});
|
|
|
|
test("site includes core guides and production configuration", () => {
|
|
for (const route of [
|
|
"index.wrn",
|
|
"packages.wrn",
|
|
"getting-started.wrn",
|
|
"language.wrn",
|
|
"architecture.wrn",
|
|
]) {
|
|
expect(existsSync(join(root, "app", "pages", route))).toBe(true);
|
|
}
|
|
expect(readFileSync(join(root, "wrnexus.config.ts"), "utf8")).toContain(
|
|
"WRNexusJS Documentation",
|
|
);
|
|
const config = readFileSync(join(root, "wrnexus.config.ts"), "utf8");
|
|
expect(config).toContain("contentSecurityPolicy:");
|
|
expect(config).not.toMatch(/^\s*csp:\s*\{/m);
|
|
expect(config).toContain('"connect-src": ["\'self\'", "https://fonts.gstatic.com"]');
|
|
expect(config).toContain(
|
|
'"style-src": ["\'self\'", "\'unsafe-inline\'", "https://fonts.googleapis.com"]',
|
|
);
|
|
});
|
|
|
|
test("language reference covers directives, events, loops, and conditionals", () => {
|
|
const source = readFileSync(join(root, "app", "pages", "language.wrn"), "utf8");
|
|
for (const section of [
|
|
"anatomy",
|
|
"state",
|
|
"server",
|
|
"directives",
|
|
"forms",
|
|
"security",
|
|
"errors",
|
|
]) {
|
|
expect(source).toContain(`id="${section}"`);
|
|
}
|
|
expect(source).toContain("data-show");
|
|
expect(source).toContain("mobile behavior");
|
|
});
|
|
|
|
test("portal exposes truthful access and machine discovery", () => {
|
|
expect(readFileSync(join(root, "app", "pages", "index.wrn"), "utf8")).toContain(
|
|
"Private Developer Preview",
|
|
);
|
|
expect(readFileSync(join(root, "app", "pages", "access.wrn"), "utf8")).toContain(
|
|
"private registry credentials",
|
|
);
|
|
for (const asset of [
|
|
"robots.txt",
|
|
"sitemap.xml",
|
|
"llms.txt",
|
|
"llms-full.txt",
|
|
"docs-index.json",
|
|
]) {
|
|
expect(existsSync(join(root, "public", asset))).toBe(true);
|
|
}
|
|
|
|
const llms = readFileSync(join(root, "public", "llms.txt"), "utf8");
|
|
expect(llms).toContain(`# WRNexusJS documentation ${frameworkVersion}`);
|
|
expect(llms).toContain("# Installed package index");
|
|
expect(llms).toContain("- @wrnexus/helpers");
|
|
expect(llms).toContain("redirectToLogin");
|
|
expect(llms).toContain("https://component.wrnexusjs.dev/");
|
|
const llmsFull = readFileSync(join(root, "public", "llms-full.txt"), "utf8");
|
|
expect(llmsFull).toContain("# Installed package documentation");
|
|
expect(llmsFull).toContain("# Complete @wrnexus/ui component reference and source contracts");
|
|
expect(llmsFull).toContain("### Complete .wrn source contract");
|
|
});
|
|
|
|
test("workspace guide documents app addition and safe forward-auth redirects", () => {
|
|
const source = readFileSync(
|
|
join(root, "app", "pages", "guides", "workspaces-and-gateway.wrn"),
|
|
"utf8",
|
|
);
|
|
expect(source).toContain("wrnexus workspace add reports --domain=reports.localhost");
|
|
expect(source).toContain("@wrnexus/helpers");
|
|
expect(source).toContain("redirectToLogin");
|
|
expect(source).toContain("allowedHosts");
|
|
expect(source).toContain("/api/verify");
|
|
});
|
|
|
|
test("package index includes searchable category filters", () => {
|
|
const source = readFileSync(join(root, "app", "pages", "packages.wrn"), "utf8");
|
|
expect(source).toContain('state category = "All"');
|
|
expect(source).toContain("category = 'Security'");
|
|
expect(source).toContain("category = 'Native'");
|
|
expect(source).toContain("category === 'All'");
|
|
expect(source).not.toContain("category === 'All' ||");
|
|
expect(source).not.toContain(") && (query");
|
|
expect(source.match(/class="package-card"/g)).toHaveLength(expected.length);
|
|
});
|
|
|
|
test("clicking a category hides packages from other categories", async () => {
|
|
const source = readFileSync(join(root, "app", "pages", "packages.wrn"), "utf8");
|
|
const directory = join(tmpdir(), "wrnexus-doc-tests");
|
|
mkdirSync(directory, { recursive: true });
|
|
const modulePath = join(directory, `packages-${Date.now()}.ts`);
|
|
writeFileSync(modulePath, compileWireFile(source));
|
|
const page = (await import(pathToFileURL(modulePath).href)).default as () => string;
|
|
const mounted = mountHtml(page());
|
|
const dataButton = mounted
|
|
.querySelectorAll(".category-row button")
|
|
.find((button) => button.textContent?.startsWith("Data"));
|
|
expect(dataButton).toBeDefined();
|
|
(dataButton as HTMLElement).click();
|
|
const visible = mounted
|
|
.querySelectorAll(".package-card")
|
|
.filter((card) => (card as HTMLElement).style.display !== "none")
|
|
.map((card) => card.getAttribute("href"));
|
|
expect(visible).toEqual([
|
|
"/packages/cache",
|
|
"/packages/content",
|
|
"/packages/db",
|
|
"/packages/graphql",
|
|
"/packages/queue",
|
|
"/packages/store",
|
|
"/packages/uploader",
|
|
]);
|
|
});
|