test(06-05): failing guidance-card suite GS8-GS12 — evolved card + confirm/trip-end cards + token-only shell.css block scan (RED)

This commit is contained in:
araharah-executor
2026-10-24 14:15:12 +08:00
parent 432f33a983
commit f0931d6195
+151
View File
@@ -45,6 +45,9 @@ const {
buildManeuverList, buildManeuverList,
startGantryBlink, startGantryBlink,
stopGantryBlink, stopGantryBlink,
buildGuidanceCard,
buildConfirmCard,
buildTripEndCard,
MANEUVER_LIST_ID, MANEUVER_LIST_ID,
} = await import("../src/guidance/banner.ts"); } = await import("../src/guidance/banner.ts");
@@ -471,3 +474,151 @@ test("GS7b svgHost parses a committed glyph into a sized inline-SVG element", ()
assert.equal(svg.attributes.viewBox, "0 0 32 32"); assert.equal(svg.attributes.viewBox, "0 0 32 32");
}); });
}); });
/* ————— GS8 — the guidance card (D-06 — the route card evolved) ————— */
const CARD_VIEW = {
destinationName: "Suria KLCC",
minutesRemaining: 18,
kmRemaining: 7.842,
etaClock: "18:42",
progressPct: 42,
showNoReroute: false,
};
test("GS8 guidance card — GUIDE register, verbatim headline, live telemetry row, 4px progressbar, ONE btn-secondary END TRIP", () => {
withDocument(() => {
let ended = 0;
const card = buildGuidanceCard("en", CARD_VIEW, { onEndTrip() { ended += 1; } });
assert.ok(card.className.includes("card"), "same card class as the route card it evolves");
const ref = findByClass(card, "card-ref");
assert.equal(ref.textContent, strings.en.guideRegister, "the ROUTE register, evolved to GUIDE");
assert.equal(findByClass(card, "text-headline-md").textContent, "Suria KLCC", "the continuity anchor holds (verbatim)");
// telemetry row: minutes + MIN + {km} KM + ETA clock — instrument formats
const telemetry = findByClass(card, "route-telemetry");
assert.ok(telemetry, "the route card's telemetry row class, evolved");
assert.deepEqual(
telemetry.children.map((c) => c.textContent),
["18", "MIN", "7.8 KM", "ETA 18:42"],
);
// progress bar: role + full aria value set + inline width
const progress = findByClass(card, "guide-progress");
assert.equal(progress.attributes.role, "progressbar");
assert.equal(progress.attributes["aria-valuemin"], "0");
assert.equal(progress.attributes["aria-valuemax"], "100");
assert.equal(progress.attributes["aria-valuenow"], "42");
assert.equal(progress.attributes["aria-label"], strings.en.ariaTripProgress);
const fill = findByClass(progress, "guide-progress-fill");
assert.equal(fill.style.width, "42%", "width set by inline style from the pct — instant, no transition");
// state line ABSENT in the normal state (less, but better)
assert.equal(findByClass(card, "text-body-sm"), null, "no state line unless degrading");
// actions: exactly ONE button, btn-secondary, END TRIP
const actionsRow = findByClass(card, "card-actions");
assert.equal(actionsRow.children.length, 1, "the guidance card has exactly one action");
const button = actionsRow.children[0];
assert.equal(button.className, "btn-secondary", "NO .btn-primary on the guidance card — the drive's forward action is driving");
assert.equal(button.textContent, strings.en.ctaEndTrip);
button.click();
assert.equal(ended, 1, "END TRIP opens the D-09 confirm surface");
assert.equal(findAllByClass(card, "btn-primary").length, 0);
});
});
test("GS8b guidance card no-reroute state line — the longer honest copy ONLY when showNoReroute (standard ink, no red)", () => {
withDocument(() => {
const card = buildGuidanceCard("en", { ...CARD_VIEW, showNoReroute: true }, { onEndTrip() {} });
const stateLine = findByClass(card, "text-body-sm");
assert.ok(stateLine, "the degradation adds the state line");
assert.equal(stateLine.textContent, strings.en.guideNoRerouteBody);
assert.equal(findByClass(card, "card-actions").children.length, 1, "still exactly one action");
// ms: same key, ms copy
const cardMs = buildGuidanceCard("ms", { ...CARD_VIEW, showNoReroute: true }, { onEndTrip() {} });
assert.equal(findByClass(cardMs, "text-body-sm").textContent, strings.ms.guideNoRerouteBody);
});
});
/* ————— GS9 — the abandon-route confirm card (D-09/PX-03) ————— */
test("GS9 confirm card — GUIDE // CONFIRM, the exact privacy copy, btn-primary YES ABANDON + btn-secondary CONTINUE, bottom-register hook", () => {
withDocument(() => {
for (const locale of ["en", "ms"]) {
let abandoned = 0;
let continued = 0;
const card = buildConfirmCard(locale, { onAbandon() { abandoned += 1; }, onContinue() { continued += 1; } });
assert.equal(findByClass(card, "card-ref").textContent, `${strings[locale].guideRegister} // CONFIRM`);
assert.equal(findByClass(card, "text-headline-md").textContent, strings[locale].guideConfirmHeadline);
const body = findByClass(card, "text-body-sm");
assert.equal(body.textContent, strings[locale].guideConfirmBody, "the privacy clause at the exact moment it executes");
const primary = findByClass(card, "btn-primary");
const secondary = findByClass(card, "btn-secondary");
assert.ok(primary && secondary, "both CTAs exist");
assert.equal(primary.textContent, strings[locale].ctaConfirmAbandon, "the working forward path is primary — deliberately NOT red");
assert.equal(secondary.textContent, strings[locale].ctaConfirmContinue);
primary.click();
secondary.click();
assert.equal(abandoned, 1);
assert.equal(continued, 1);
// the ONE .btn-primary in all guidance chrome lives on THIS card
assert.equal(findAllByClass(card, "btn-primary").length, 1);
}
// the bottom-register placement hook (thumb-reach, D-09) rides the card
withDocument(() => {
const card = buildConfirmCard("en", { onAbandon() {}, onContinue() {} });
assert.ok(card.className.includes("guide-confirm"), "the mount reads guide-confirm → bottom register");
});
});
});
/* ————— GS10 — the trip-ended card (D-14) ————— */
test("GS10 trip-end card — GUIDE // END, the exact deletion copy, NO actions", () => {
withDocument(() => {
for (const locale of ["en", "ms"]) {
const card = buildTripEndCard(locale);
assert.equal(findByClass(card, "card-ref").textContent, `${strings[locale].guideRegister} // END`);
assert.equal(findByClass(card, "text-headline-md").textContent, strings[locale].guideEndHeadline);
const body = findByClass(card, "text-body-sm");
assert.equal(body.textContent, strings[locale].guideEndBody);
assert.equal(findByClass(card, "card-actions"), null, "no actions — ONE card for arrival and abandon");
const buttons = [];
const walk = (el) => {
for (const child of el.children) {
if (child.tagName === "BUTTON") buttons.push(child);
walk(child);
}
};
walk(card);
assert.equal(buttons.length, 0, "the trip-end card carries no buttons at all");
}
});
});
/* ————— GS11 — the shell.css guidance block (token-only CSS law) ————— */
test("GS11 shell.css guidance block — zero transition/animation/box-shadow/border-radius, zero hex literals, token vars only; strip compaction is CSS-only", () => {
const cssPath = path.join(HERE, "..", "src", "shell.css");
const css = readFileSync(cssPath, "utf8");
const marker = css.indexOf("guidance cockpit");
assert.ok(marker > 0, "the guidance block exists in shell.css");
const block = css.slice(marker);
assert.ok(block.length > 500, `the block dresses the whole cockpit (found ${block.length} chars)`);
for (const banned of ["transition", "animation", "box-shadow", "border-radius"]) {
assert.ok(!block.includes(banned), `guidance block uses ${banned} — states are instant hard swaps, zero depth`);
}
const hex = /#[0-9a-fA-F]{3,8}/.exec(block);
assert.equal(hex, null, `guidance block carries a hex literal (${hex && hex[0]}) — token vars only`);
assert.ok(block.includes('[data-guidance="active"]'), "the strip compaction hook exists");
assert.ok(block.includes("--color-signal-orange"), "the accent rides the token");
// the compaction is PURE CSS — strip.ts logic untouched (Phase 3 table stands)
const stripSrc = codeText(path.join(HERE, "..", "src", "ui", "strip.ts"));
assert.ok(!stripSrc.includes("data-guidance"), "strip.ts never reads data-guidance — compaction is layout-only CSS");
assert.ok(!stripSrc.includes("guide"), "strip.ts carries no guidance furniture");
});
/* ————— GS12 — the ONE .btn-primary placement ————— */
test("GS12 banner.ts places .btn-primary exactly once — the confirm card's YES, ABANDON (comment-stripped)", () => {
const src = codeText(path.join(GUIDANCE_DIR, "banner.ts"));
assert.equal((src.match(/btn-primary/g) ?? []).length, 1, "only the confirm card builds a primary CTA");
assert.ok((src.match(/btn-secondary/g) ?? []).length >= 2, "END TRIP + CONTINUE are secondary");
});