murashit_codex-panel/tests/scripts/development-scripts.test.ts

137 lines
5.9 KiB
TypeScript
Raw Normal View History

import { spawnSync } from "node:child_process";
import { chmod, mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
const repoRoot = process.cwd();
describe("development scripts", () => {
it("propagates CI check failures", async () => {
const cwd = await tempWorkspace();
await writeFile(path.join(cwd, "fail.mjs"), "process.exit(7);\n");
await writeJson(path.join(cwd, "package.json"), {
scripts: {
"typecheck:ci": "node fail.mjs",
"test:ci": 'node -e "process.exit(0)"',
"lint:ts:ci": 'node -e "process.exit(0)"',
"lint:css": 'node -e "process.exit(0)"',
"format:check:ci": 'node -e "process.exit(0)"',
2026-05-30 02:00:08 +00:00
build: 'node -e "process.exit(0)"',
},
});
const result = runNodeScript("scripts/check.mjs", ["--ci"], cwd);
expect(result.status).toBe(7);
});
2026-05-31 11:26:11 +00:00
it("fails style builds when CSS files are missing from the style order file", async () => {
2026-05-30 02:00:08 +00:00
const cwd = await tempWorkspace();
await mkdir(path.join(cwd, "src", "styles"), { recursive: true });
2026-05-31 11:26:11 +00:00
await writeJson(path.join(cwd, "src", "styles", "order.json"), ["00-tokens.css"]);
2026-05-30 02:00:08 +00:00
await writeFile(path.join(cwd, "src", "styles", "00-tokens.css"), ".codex-panel { color: var(--text-normal); }\n");
await writeFile(path.join(cwd, "src", "styles", "10-unlisted.css"), ".codex-panel__extra { display: block; }\n");
const result = runNodeScript("scripts/build-styles.mjs", ["--check"], cwd);
expect(result.status).toBe(1);
2026-05-31 11:26:11 +00:00
expect(result.stderr).toContain("CSS files missing from src/styles/order.json: 10-unlisted.css");
2026-05-30 02:00:08 +00:00
});
it("passes the expected codex generate-ts arguments", async () => {
const cwd = await tempWorkspace();
const binDir = path.join(cwd, "bin");
await mkdir(binDir, { recursive: true });
await mkdir(path.join(cwd, "src", "generated", "app-server"), { recursive: true });
const codexBin = path.join(binDir, "codex");
await writeFile(
codexBin,
[
"#!/usr/bin/env node",
"import { appendFileSync, mkdirSync, writeFileSync } from 'node:fs';",
"appendFileSync('codex-args.txt', `${process.argv.slice(2).join('\\n')}\\n`);",
"mkdirSync('src/generated/app-server/v2', { recursive: true });",
"writeFileSync('src/generated/app-server/v2/Example.ts', '// GENERATED CODE! DO NOT MODIFY BY HAND!\\nexport type Example = string | null | null;\\n');",
].join("\n"),
);
await chmod(codexBin, 0o755);
const result = runNodeScript("scripts/generate-app-server-types.mjs", [], cwd, {
PATH: `${binDir}${path.delimiter}${process.env["PATH"] ?? ""}`,
});
expect(result.status).toBe(0);
await expect(readFile(path.join(cwd, "codex-args.txt"), "utf8")).resolves.toBe(
"app-server\ngenerate-ts\n--experimental\n--out\nsrc/generated/app-server\n",
);
await expect(readFile(path.join(cwd, "src", "generated", "app-server", "v2", "Example.ts"), "utf8")).resolves.toContain(
"export type Example = string | null;",
);
});
it("keeps files unchanged when format check finds unformatted content", async () => {
const cwd = await tempWorkspace();
await mkdir(path.join(cwd, "src"), { recursive: true });
await mkdir(path.join(cwd, "tests"), { recursive: true });
await mkdir(path.join(cwd, "scripts"), { recursive: true });
const sourcePath = path.join(cwd, "src", "unformatted.ts");
const source = "export const value=1\n";
await writeFile(sourcePath, source);
const result = runNodeScript("scripts/format.mjs", ["--check"], cwd);
expect(result.status).toBe(1);
expect(result.stderr).toContain("Unformatted files:");
expect(result.stderr).toContain("src/unformatted.ts");
await expect(readFile(sourcePath, "utf8")).resolves.toBe(source);
});
it("fails release prepare before changing version files when release notes already exist", async () => {
const cwd = await tempWorkspace();
await mkdir(path.join(cwd, ".github", "release-notes"), { recursive: true });
const packageJson = { version: "2.3.2" };
const packageLockJson = { version: "2.3.2", packages: { "": { version: "2.3.2" } } };
const manifestJson = { version: "2.3.2", minAppVersion: "1.12.0" };
const versionsJson = { "2.3.2": "1.12.0" };
await writeJson(path.join(cwd, "package.json"), packageJson);
await writeJson(path.join(cwd, "package-lock.json"), packageLockJson);
await writeJson(path.join(cwd, "manifest.json"), manifestJson);
await writeJson(path.join(cwd, "versions.json"), versionsJson);
await writeFile(path.join(cwd, ".github", "release-notes", "2.3.3.md"), "## Changes\n\n- Existing\n");
const result = runNodeScript("scripts/release/prepare.mjs", ["2.3.3"], cwd);
expect(result.status).toBe(1);
expect(result.stderr).toContain(".github/release-notes/2.3.3.md already exists");
await expect(readJson(path.join(cwd, "package.json"))).resolves.toEqual(packageJson);
await expect(readJson(path.join(cwd, "package-lock.json"))).resolves.toEqual(packageLockJson);
await expect(readJson(path.join(cwd, "manifest.json"))).resolves.toEqual(manifestJson);
await expect(readJson(path.join(cwd, "versions.json"))).resolves.toEqual(versionsJson);
});
});
async function tempWorkspace(): Promise<string> {
return mkdtemp(path.join(tmpdir(), "codex-panel-scripts-"));
}
function runNodeScript(script: string, args: string[] = [], cwd = repoRoot, env: NodeJS.ProcessEnv = {}) {
return spawnSync(process.execPath, [path.join(repoRoot, script), ...args], {
cwd,
encoding: "utf8",
env: { ...process.env, ...env },
shell: false,
});
}
async function writeJson(file: string, value: unknown): Promise<void> {
await writeFile(file, `${JSON.stringify(value, null, 2)}\n`);
}
async function readJson(file: string): Promise<unknown> {
return JSON.parse(await readFile(file, "utf8"));
}