Files

136 lines
3.4 KiB
TypeScript
Raw Permalink Normal View History

2026-05-09 17:58:44 +10:00
import path from "node:path";
2026-06-30 20:57:55 +10:00
import { describe, it } from "node:test";
2026-05-09 17:58:44 +10:00
import { fileURLToPath } from "node:url";
2026-06-30 20:57:55 +10:00
import { expect } from "expect";
2026-05-09 17:58:44 +10:00
import { execa } from "execa";
const repoRoot = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"..",
);
2026-05-09 17:58:44 +10:00
const localizedReadmeFiles = [
"docs/readmes/README.ar.md",
"docs/readmes/README.de.md",
"docs/readmes/README.el.md",
"docs/readmes/README.es.md",
"docs/readmes/README.fr.md",
"docs/readmes/README.id.md",
"docs/readmes/README.it.md",
"docs/readmes/README.ja.md",
"docs/readmes/README.ko.md",
"docs/readmes/README.pl.md",
"docs/readmes/README.pt.md",
"docs/readmes/README.ru.md",
"docs/readmes/README.tr.md",
"docs/readmes/README.vi.md",
"docs/readmes/README.zh.md",
"docs/README.md",
];
const expectedPackageFiles = [
"LICENSE",
"docs/assets/truthmark-workflow-mobile.svg",
"docs/assets/truthmark-workflow.svg",
...localizedReadmeFiles,
"README.md",
"dist/main.js",
"dist/main.js.map",
"package.json",
];
type PackFile = {
path: string;
mode: number;
};
const parsePackOutput = (
stdout: string,
): Array<{
files: PackFile[];
}> => {
const jsonStartIndex = stdout.search(/^\s*\[\s*\{\s*$/m);
if (jsonStartIndex === -1) {
throw new SyntaxError("npm pack output did not include a JSON array");
}
return JSON.parse(stdout.slice(jsonStartIndex)) as Array<{
files: PackFile[];
}>;
};
const readPackageJson = async (): Promise<{
scripts?: Record<string, string>;
}> => {
const { stdout } = await execa(
process.execPath,
["-e", "console.log(JSON.stringify(require('./package.json')))"],
{ cwd: repoRoot },
);
return JSON.parse(stdout) as { scripts?: Record<string, string> };
};
const readPackFiles = async (): Promise<PackFile[]> => {
const { stdout } = await execa(
"npm",
["pack", "--dry-run", "--json", "--silent"],
{
2026-05-09 17:58:44 +10:00
cwd: repoRoot,
},
);
const packOutput = parsePackOutput(stdout);
return packOutput[0]?.files ?? [];
};
describe("package and release integrity", () => {
it("includes localized README files linked from the published README", async () => {
const tarballPaths = (await readPackFiles()).map((file) => file.path);
2026-05-09 17:58:44 +10:00
2026-06-22 03:28:02 +10:00
expect(tarballPaths).toEqual(expect.arrayContaining(localizedReadmeFiles));
2026-05-09 17:58:44 +10:00
});
it("publishes only expected files with intentional executable modes", async () => {
const packFiles = await readPackFiles();
const modesByPath = new Map(
packFiles.map((file) => [file.path, file.mode]),
);
expect(packFiles.map((file) => file.path).sort()).toEqual(
[...expectedPackageFiles].sort(),
);
expect(modesByPath.get("dist/main.js")).toBe(0o755);
for (const filePath of expectedPackageFiles.filter(
(entry) => entry !== "dist/main.js",
)) {
expect(modesByPath.get(filePath)).toBe(0o644);
}
});
it("exposes package validation scripts for local and release checks", async () => {
const packageJson = await readPackageJson();
expect(packageJson.scripts).toMatchObject({
lint: expect.any(String),
"format:check": expect.any(String),
"package:check": expect.any(String),
"release:check": expect.any(String),
});
});
it("parses npm pack JSON after bracketed build output", () => {
const packOutput = parsePackOutput(`build [1/2]
[
{
"files": []
}
]`);
expect(packOutput).toEqual([{ files: [] }]);
});
2026-05-09 17:58:44 +10:00
});