From f710913a0ba92f1deb018062cba32b1ae80e60b2 Mon Sep 17 00:00:00 2001 From: araharah-executor Date: Sat, 24 Oct 2026 05:25:03 +0800 Subject: [PATCH] =?UTF-8?q?test(05-04):=20extend=20copy=20gate=20for=20rou?= =?UTF-8?q?te=20copy=20=E2=80=94=20COPY7=20verbatim=20+=20floor=2040=20+?= =?UTF-8?q?=20COPY5=20src/route=20scope=20(RED)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/test/copy-gate.test.mjs | 44 ++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/web/test/copy-gate.test.mjs b/web/test/copy-gate.test.mjs index 032ff17..ef8fb3b 100644 --- a/web/test/copy-gate.test.mjs +++ b/web/test/copy-gate.test.mjs @@ -1,10 +1,11 @@ #!/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 · * 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 { 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 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) ————— */ @@ -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 msKeys = Object.keys(strings.ms).sort(); 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("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 ————— */ @@ -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 ————— */ -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 = - /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) ? readdirSync(UI_DIR) .filter((f) => f.endsWith(".ts")) .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 assert.ok(files.length >= 1, `scanned a real shell tree (found ${files.length} 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("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", + ); +});