test(05-04): extend copy gate for route copy — COPY7 verbatim + floor 40 + COPY5 src/route scope (RED)
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
/*
|
/*
|
||||||
* copy-gate.test.mjs — COPY1..COPY6, the UI-SPEC copy gate (mirrors bot CS1-CS3).
|
* copy-gate.test.mjs — COPY1..COPY7, the UI-SPEC copy gate (mirrors bot CS1-CS3).
|
||||||
* --------------------------------------------------------------------------------
|
* --------------------------------------------------------------------------------
|
||||||
* COPY1 locale key-set parity · COPY2/COPY3 verbatim-contract content checks ·
|
* COPY1 locale key-set parity · COPY2/COPY3 verbatim-contract content checks ·
|
||||||
* COPY4 pickLocale fallback rule · COPY5 no inline copy in shell modules ·
|
* COPY4 pickLocale fallback rule · COPY5 no inline copy in shell modules ·
|
||||||
* COPY6 localeFromInitData over the raw initData string.
|
* COPY6 localeFromInitData over the raw initData string ·
|
||||||
|
* COPY7 route copy verbatim (the Phase 5 route card + pending card keys).
|
||||||
*/
|
*/
|
||||||
import { strict as assert } from "node:assert";
|
import { strict as assert } from "node:assert";
|
||||||
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
||||||
@@ -15,6 +16,7 @@ import { test } from "./harness.mjs";
|
|||||||
const { strings, pickLocale, localeFromInitData } = await import("../src/i18n/strings.ts");
|
const { strings, pickLocale, localeFromInitData } = await import("../src/i18n/strings.ts");
|
||||||
|
|
||||||
const UI_DIR = path.join(path.dirname(fileURLToPath(new URL(import.meta.url))), "..", "src", "ui");
|
const UI_DIR = path.join(path.dirname(fileURLToPath(new URL(import.meta.url))), "..", "src", "ui");
|
||||||
|
const ROUTE_DIR = path.join(path.dirname(fileURLToPath(new URL(import.meta.url))), "..", "src", "route");
|
||||||
|
|
||||||
/* ————— COPY1 — en/ms key-set identity (Phase 2 D-05 precedent) ————— */
|
/* ————— COPY1 — en/ms key-set identity (Phase 2 D-05 precedent) ————— */
|
||||||
|
|
||||||
@@ -22,9 +24,12 @@ test("COPY1 en and ms carry identical key-sets (parity gate, both locales)", ()
|
|||||||
const enKeys = Object.keys(strings.en).sort();
|
const enKeys = Object.keys(strings.en).sort();
|
||||||
const msKeys = Object.keys(strings.ms).sort();
|
const msKeys = Object.keys(strings.ms).sort();
|
||||||
assert.deepEqual(msKeys, enKeys, "ms missing/duplicated keys vs en (D-05 locale parity)");
|
assert.deepEqual(msKeys, enKeys, "ms missing/duplicated keys vs en (D-05 locale parity)");
|
||||||
assert.ok(enKeys.length >= 25, `expected the full ShellStrings surface (25 keys), found ${enKeys.length}`);
|
assert.ok(enKeys.length >= 40, `expected the full ShellStrings surface (40 keys incl. the 10 route keys), found ${enKeys.length}`);
|
||||||
assert.ok(enKeys.includes("deniedRegister"), "deniedRegister is a keys — cards.ts renders GEO //");
|
assert.ok(enKeys.includes("deniedRegister"), "deniedRegister is a keys — cards.ts renders GEO //");
|
||||||
assert.ok(enKeys.includes("lostRegister"), "lostRegister is a key — cards.ts renders SYS //");
|
assert.ok(enKeys.includes("lostRegister"), "lostRegister is a key — cards.ts renders SYS //");
|
||||||
|
assert.ok(enKeys.includes("routeRegister"), "routeRegister is a key — route-card.ts renders ROUTE");
|
||||||
|
assert.ok(enKeys.includes("routePendingRegister"), "routePendingRegister is a key — the pending card renders ROUTE // ROUTER");
|
||||||
|
assert.ok(enKeys.includes("ctaSearchAgain"), "ctaSearchAgain is a key — the D-10 restart affordance");
|
||||||
});
|
});
|
||||||
|
|
||||||
/* ————— COPY2 — denied copy names location ————— */
|
/* ————— COPY2 — denied copy names location ————— */
|
||||||
@@ -57,14 +62,21 @@ test("COPY4 pickLocale: ms and ms-MY select ms; en, en-GB, id, null, and empty s
|
|||||||
|
|
||||||
/* ————— COPY5 — no inline copy in the shell ————— */
|
/* ————— COPY5 — no inline copy in the shell ————— */
|
||||||
|
|
||||||
test("COPY5 no shell module inlines copy — source-introspection grep over web/src/ui/*.ts and web/src/main.ts", () => {
|
test("COPY5 no shell module inlines copy — source-introspection grep over web/src/ui/*.ts, web/src/route/*.ts and web/src/main.ts", () => {
|
||||||
const banned =
|
const banned =
|
||||||
/Validating session|Acquiring location|TRY AGAIN|ASK VIA CHAT|Location is switched off|Signal lost|MENYEMAK|MENDAPAT|LANGSUNG|GEO \/\/ LOCATION|SYS \/\/ POSITION|BOOT-01 \/\/|BOOT-02 \/\/|CUBA LAGI|MINTA VIA CHAT|ISYARAT HILANG|LOKASI MATI|AWAITING PIN|OpenStreetMap contributors|Recenter map|Tetapkan semula peta/;
|
/Validating session|Acquiring location|TRY AGAIN|ASK VIA CHAT|Location is switched off|Signal lost|MENYEMAK|MENDAPAT|LANGSUNG|GEO \/\/ LOCATION|SYS \/\/ POSITION|BOOT-01 \/\/|BOOT-02 \/\/|CUBA LAGI|MINTA VIA CHAT|ISYARAT HILANG|LOKASI MATI|AWAITING PIN|OpenStreetMap contributors|Recenter map|Tetapkan semula peta|Routing not wired up yet|SEARCH AGAIN|CARI SEMULA|ROUTE \/\/ ROUTER|Laluan terpantas|Penghalaan belum dipasang|Alternative routes/;
|
||||||
const files = existsSync(UI_DIR)
|
const files = existsSync(UI_DIR)
|
||||||
? readdirSync(UI_DIR)
|
? readdirSync(UI_DIR)
|
||||||
.filter((f) => f.endsWith(".ts"))
|
.filter((f) => f.endsWith(".ts"))
|
||||||
.map((f) => path.join(UI_DIR, f))
|
.map((f) => path.join(UI_DIR, f))
|
||||||
: [];
|
: [];
|
||||||
|
if (existsSync(ROUTE_DIR)) {
|
||||||
|
files.push(
|
||||||
|
...readdirSync(ROUTE_DIR)
|
||||||
|
.filter((f) => f.endsWith(".ts"))
|
||||||
|
.map((f) => path.join(ROUTE_DIR, f)),
|
||||||
|
); // the route module family joins the scanned shell surface (Phase 5)
|
||||||
|
}
|
||||||
files.push(path.join(UI_DIR, "..", "main.ts")); // the composition root — always scanned
|
files.push(path.join(UI_DIR, "..", "main.ts")); // the composition root — always scanned
|
||||||
assert.ok(files.length >= 1, `scanned a real shell tree (found ${files.length} files)`);
|
assert.ok(files.length >= 1, `scanned a real shell tree (found ${files.length} files)`);
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
@@ -97,3 +109,25 @@ test("COPY6 localeFromInitData derives the locale from the raw initData string",
|
|||||||
assert.equal(localeFromInitData("not-initdata"), "en", "no user= component falls back to en");
|
assert.equal(localeFromInitData("not-initdata"), "en", "no user= component falls back to en");
|
||||||
assert.equal(localeFromInitData("user=%7Bbad-json"), "en", "decode/JSON failure must not throw");
|
assert.equal(localeFromInitData("user=%7Bbad-json"), "en", "decode/JSON failure must not throw");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* ————— COPY7 — route copy verbatim (the 10 route keys, en + ms) ————— */
|
||||||
|
|
||||||
|
test("COPY7 route copy verbatim (the 10 route keys, en + ms)", () => {
|
||||||
|
assert.equal(strings.en.routePendingHeadline, "Routing not wired up yet");
|
||||||
|
assert.ok(
|
||||||
|
strings.en.routePendingBody.includes("never sent anywhere"),
|
||||||
|
"the en pending body carries the privacy clause",
|
||||||
|
);
|
||||||
|
assert.equal(strings.en.ctaSearchAgain, "SEARCH AGAIN");
|
||||||
|
assert.equal(strings.ms.routePendingHeadline, "Penghalaan belum dipasang");
|
||||||
|
assert.equal(strings.ms.ctaSearchAgain, "CARI SEMULA");
|
||||||
|
assert.equal(strings.en.routeDiffSlower, "+{min} MIN VS ROUTE {n}");
|
||||||
|
assert.equal(strings.ms.routeDiffSlower, "+{min} MIN VS LALUAN {n}");
|
||||||
|
assert.equal(strings.en.ariaRouteChip, "Route {n}: {min} minutes, {km} kilometres");
|
||||||
|
assert.equal(strings.en.routePendingRegister, "ROUTE // ROUTER");
|
||||||
|
assert.equal(
|
||||||
|
strings.ms.routePendingRegister,
|
||||||
|
"ROUTE // ROUTER",
|
||||||
|
"instrument vocabulary — identical in both locales",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user