Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
import { readFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
const here = dirname(fileURLToPath(import.meta.url));
const pkgPath = resolve(here, "..", "package.json");
describe("dep-graph guard", () => {
it("no @suaveplan/* in dependencies or peerDependencies", {
timeout: 5000,
}, async () => {
const pkg = JSON.parse(await readFile(pkgPath, "utf8")) as {
dependencies?: Record<string, string>;
peerDependencies?: Record<string, string>;
};
const sections: Array<
[
"dependencies" | "peerDependencies",
Record<string, string> | undefined,
]
> = [
["dependencies", pkg.dependencies],
["peerDependencies", pkg.peerDependencies],
];
for (const [name, deps] of sections) {
if (!deps) continue;
const offending = Object.keys(deps).filter((k) =>
k.startsWith("@suaveplan/")
);
expect(
offending,
`${name} contains @suaveplan/* deps: ${offending.join(", ")}`
).toEqual([]);
}
});
it("devDependencies may contain @suaveplan/testing only", {
timeout: 5000,
}, async () => {
const pkg = JSON.parse(await readFile(pkgPath, "utf8")) as {
devDependencies?: Record<string, string>;
};
const ours = Object.keys(pkg.devDependencies ?? {}).filter((k) =>
k.startsWith("@suaveplan/")
);
const allowed = new Set(["@suaveplan/testing"]);
for (const dep of ours) {
expect(
allowed.has(dep),
`unexpected @suaveplan/* devDep: ${dep}`
).toBe(true);
}
});
it("cloakbrowser is a direct dependency", { timeout: 5000 }, async () => {
const pkg = JSON.parse(await readFile(pkgPath, "utf8")) as {
dependencies?: Record<string, string>;
};
expect(pkg.dependencies?.cloakbrowser).toBeDefined();
// Either a catalog reference or an absolute range is acceptable.
expect(pkg.dependencies?.cloakbrowser).toMatch(
/^(catalog:testing|\^?\d|~?\d|\d)/
);
});
});

View File

@@ -0,0 +1,76 @@
import { readdir, readFile } from "node:fs/promises";
import { dirname, join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
const here = dirname(fileURLToPath(import.meta.url));
const srcRoot = resolve(here, "..", "src");
const PATTERNS: Array<{ name: string; regex: RegExp }> = [
{ name: "any", regex: /(?<![a-zA-Z0-9_])any(?![a-zA-Z0-9_])/ },
{ name: "ts-ignore", regex: /@ts-ignore/ },
{ name: "ts-expect-error", regex: /@ts-expect-error/ },
{ name: "TODO", regex: /\bTODO\b/ },
{ name: "FIXME", regex: /\bFIXME\b/ },
{ name: "@deprecated", regex: /@deprecated/ },
{ name: ".skip(", regex: /\.skip\s*\(/ },
{ name: ".only(", regex: /\.only\s*\(/ },
// waitForTimeout / wait_for_timeout emit CDP protocol traffic that
// reCAPTCHA v3 / Enterprise and other behavioural scorers
// detect — use a native sleep instead. Pattern matches both the
// Playwright JS form and any incidental Python form that might
// leak into TS via codegen.
{ name: "waitForTimeout", regex: /\bwaitForTimeout\s*\(/ },
{ name: "wait_for_timeout", regex: /\bwait_for_timeout\s*\(/ },
];
const EXCLUDED_FILE_SUFFIXES = [".test.ts", ".bench.ts"];
async function walk(dir: string, out: string[]): Promise<void> {
const entries = await readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const full = join(dir, entry.name);
if (entry.isDirectory()) {
await walk(full, out);
} else if (entry.isFile() && entry.name.endsWith(".ts")) {
out.push(full);
}
}
}
describe("no-disallowed source guard", () => {
it("src/ contains no disallowed constructs", {
timeout: 10000,
}, async () => {
const all: string[] = [];
await walk(srcRoot, all);
const files = all.filter((file) => {
const rel = relative(srcRoot, file).replace(/\\/g, "/");
return !EXCLUDED_FILE_SUFFIXES.some((s) => rel.endsWith(s));
});
const violations: string[] = [];
for (const file of files) {
const text = await readFile(file, "utf8");
const lines = text.split("\n");
const rel = relative(srcRoot, file).replace(/\\/g, "/");
for (const { name, regex } of PATTERNS) {
for (let i = 0; i < lines.length; i++) {
const line = lines[i] ?? "";
const trimmed = line.trim();
if (
trimmed.startsWith("//") ||
trimmed.startsWith("*") ||
trimmed.startsWith("/*")
)
continue;
if (regex.test(line)) {
violations.push(
`${rel}:${i + 1} contains "${name}": ${trimmed}`
);
}
}
}
}
expect(violations).toEqual([]);
});
});

View File

@@ -0,0 +1,50 @@
/**
* Side-effecting polyfills module for happy-dom / jsdom / bare-Node test
* environments on WSL2 or older toolchains.
*
* Import via vitest `setupFiles`:
*
* ```ts
* // vitest.config.ts
* export default defineConfig({
* test: {
* setupFiles: ["@suaveplan/testing/polyfills"],
* },
* });
* ```
*
* Installs:
* - `globalThis.crypto` (from `node:crypto`'s `webcrypto` export) if missing
* - `globalThis.TextEncoder` / `TextDecoder` (usually present, covered defensively)
*
* Exports nothing. Safe to import multiple times; operations are idempotent.
*/
import { webcrypto } from "node:crypto";
import {
TextDecoder as NodeTextDecoder,
TextEncoder as NodeTextEncoder,
} from "node:util";
type WritableGlobal = {
crypto?: Crypto;
TextEncoder?: typeof NodeTextEncoder;
TextDecoder?: typeof NodeTextDecoder;
};
const g = globalThis as unknown as WritableGlobal;
if (
typeof g.crypto === "undefined" ||
typeof g.crypto.getRandomValues !== "function"
) {
g.crypto = webcrypto as unknown as Crypto;
}
if (typeof g.TextEncoder === "undefined") {
g.TextEncoder = NodeTextEncoder;
}
if (typeof g.TextDecoder === "undefined") {
g.TextDecoder = NodeTextDecoder;
}

View File

@@ -0,0 +1,40 @@
import { readFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
const here = dirname(fileURLToPath(import.meta.url));
const distRoot = resolve(here, "..", "dist");
async function distExists(): Promise<boolean> {
try {
await readFile(resolve(distRoot, "index.js"), "utf8");
return true;
} catch {
return false;
}
}
async function readDist(rel: string): Promise<string> {
return readFile(resolve(distRoot, rel), "utf8");
}
describe("tree-shake guards", () => {
it("page-object subpath does not pull config", {
timeout: 10000,
}, async () => {
if (!(await distExists())) return;
const text = await readDist("page-objects/page-object.js");
expect(text).not.toMatch(/createPlaywrightConfig/);
expect(text).not.toMatch(/waitForNetworkIdle/);
});
it("wait-for-network-idle does not pull dialog helper", {
timeout: 10000,
}, async () => {
if (!(await distExists())) return;
const text = await readDist("helpers/wait-for-network-idle.js");
expect(text).not.toMatch(/dismissDialog/);
expect(text).not.toMatch(/mockGeolocation/);
});
});