fix(onboarding): apply the three review follow-ups on the stack head
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m47s

- Guide: drop the internal provenance (author, date, issue number) from the
  BMO status line in the three copies (fr.json, en.json, guide-utilisateur.md).
  The status now only says the procedure is reconstructed and unconfirmed.
- Dashboard: gate `showCompactChecklist` on `budgetReady` so a Base+ profile
  with the five other items done no longer sees the compact list vanish (5/5
  under the fail-closed entitlement) and reappear (5/6) once the licence
  resolves.
- Test: pin TZ=America/Toronto around the `formatLocalDate` tests and assert
  that `toISOString()` disagrees, so a UTC regression is caught on the UTC CI
  runner too (measured: the mutation passed 7/7 under TZ=UTC before, fails now).

Follow-ups raised by the independent reviews of PRs #381, #378 and #382.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
le king fu 2026-10-04 08:47:30 -04:00
parent d11337ccf6
commit 667e058a77
5 changed files with 22 additions and 6 deletions

View file

@ -183,7 +183,7 @@ Les cartes de crédit n'offrent qu'environ trois mois d'historique.
#### BMO #### BMO
*Procédure reconstituée (fournie par Max le 2026-10-03, d'après une description générale), à confirmer sur un compte réel ; format de fichier non encore reconnu (#371)* *Procédure reconstituée d'après une description générale, à confirmer sur un compte réel ; format de fichier non encore reconnu*
1. Connectez-vous aux Services bancaires en ligne BMO sur bmo.com (le site, pas l'application) 1. Connectez-vous aux Services bancaires en ligne BMO sur bmo.com (le site, pas l'application)
2. Dans le sommaire, cliquez sur le compte voulu (chèques, épargne ou Mastercard) 2. Dans le sommaire, cliquez sur le compte voulu (chèques, épargne ou Mastercard)

View file

@ -8,7 +8,7 @@
// dispatches), and never touched by an error — which also leaves the scanned // dispatches), and never touched by an error — which also leaves the scanned
// sources exactly as they were. Plus the local-date helper handed to Rust. // sources exactly as they were. Plus the local-date helper handed to Rust.
import { describe, it, expect, vi } from "vitest"; import { describe, it, expect, vi, beforeAll, afterAll } from "vitest";
// The hook module imports the import services (→ db → tauri plugins). Stub the // The hook module imports the import services (→ db → tauri plugins). Stub the
// connection so importing the module never touches a real database. // connection so importing the module never touches a real database.
@ -111,11 +111,23 @@ describe("wizardReducer — sampleCreated (#356)", () => {
}); });
describe("formatLocalDate (#356)", () => { describe("formatLocalDate (#356)", () => {
// Pin a timezone west of Greenwich: under TZ=UTC (the CI runner) local and
// UTC dates coincide, so a `toISOString()` regression would pass unnoticed.
const originalTz = process.env.TZ;
beforeAll(() => {
process.env.TZ = "America/Toronto";
});
afterAll(() => {
if (originalTz === undefined) delete process.env.TZ;
else process.env.TZ = originalTz;
});
it("formats the LOCAL calendar date, zero-padded", () => { it("formats the LOCAL calendar date, zero-padded", () => {
// Built from local components, so the expectation holds in every timezone — // Built from local components, so the expectation holds in every timezone —
// unlike `toISOString()`, which would shift this evening date to the 6th // unlike `toISOString()`, which would shift this evening date to the 6th
// east of Greenwich and to the 4th west of it. // in Toronto (04:30Z the next day).
expect(formatLocalDate(new Date(2026, 0, 5, 23, 30))).toBe("2026-01-05"); expect(formatLocalDate(new Date(2026, 0, 5, 23, 30))).toBe("2026-01-05");
expect(new Date(2026, 0, 5, 23, 30).toISOString().slice(0, 10)).not.toBe("2026-01-05");
expect(formatLocalDate(new Date(2026, 11, 31, 0, 0))).toBe("2026-12-31"); expect(formatLocalDate(new Date(2026, 11, 31, 0, 0))).toBe("2026-12-31");
}); });
}); });

View file

@ -996,7 +996,7 @@
{ {
"id": "import-bmo", "id": "import-bmo",
"title": "BMO", "title": "BMO",
"status": "Procedure reconstructed (provided by Max on 2026-10-03 from a general description), to be confirmed on a real account; file format not recognised yet (#371)", "status": "Procedure reconstructed from a general description, to be confirmed on a real account; file format not recognised yet",
"steps": [ "steps": [
"Sign in to BMO Online Banking at bmo.com (the website, not the app)", "Sign in to BMO Online Banking at bmo.com (the website, not the app)",
"In the summary, click the account you want (chequing, savings or Mastercard)", "In the summary, click the account you want (chequing, savings or Mastercard)",

View file

@ -996,7 +996,7 @@
{ {
"id": "import-bmo", "id": "import-bmo",
"title": "BMO", "title": "BMO",
"status": "Procédure reconstituée (fournie par Max le 2026-10-03, d'après une description générale), à confirmer sur un compte réel ; format de fichier non encore reconnu (#371)", "status": "Procédure reconstituée d'après une description générale, à confirmer sur un compte réel ; format de fichier non encore reconnu",
"steps": [ "steps": [
"Connectez-vous aux Services bancaires en ligne BMO sur bmo.com (le site, pas l'application)", "Connectez-vous aux Services bancaires en ligne BMO sur bmo.com (le site, pas l'application)",
"Dans le sommaire, cliquez sur le compte voulu (chèques, épargne ou Mastercard)", "Dans le sommaire, cliquez sur le compte voulu (chèques, épargne ou Mastercard)",

View file

@ -109,7 +109,11 @@ export default function DashboardPage() {
); );
} }
const showCompactChecklist = checklistReady && !onboarding.hidden && !checklist.complete; // `complete` is computed with the fail-closed entitlement (Budget locked → 5/5)
// until the licence resolves; gate on `budgetReady` so a Base+ profile with the
// five other items done does not see the list vanish, then reappear as 5/6.
const showCompactChecklist =
checklistReady && !onboarding.hidden && !(onboarding.budgetReady && checklist.complete);
return ( return (
<div className={isLoading ? "opacity-50 pointer-events-none" : ""}> <div className={isLoading ? "opacity-50 pointer-events-none" : ""}>