test(i18n): give the per-locale copy guards their pt-BR vocabulary

The delete-confirmation, sleep-debt, mood-prognosis, MCP-token and
AI-consent guards match each locale against its own phrases, and the
sharing-boundary guard against the word for account; pt-BR now has
them. The locale-count floors read nine, and the managed-profile picker
test counts the locale list instead of a literal.
This commit is contained in:
Marc-André Bombeck committed 2026-10-22 14:57:32 +02:00
1 parent 71123e5fae
commit 51f7d38b3c
6 files changed
+38 -10

No files matched your search

@@ -92,6 +92,13 @@ const FINALITY_PHRASES: Record<string, readonly string[]> = {
pl: ["nie można cofnąć", "nie można tego cofnąć", "trwale", "nieodwracaln"],
ko: ["되돌릴 수 없", "복구할 수 없", "영구", "완전히 삭제"],
nl: ["niet ongedaan", "definitief", "permanent", "onherroepelijk"],
"pt-BR": [
"não pode ser desfeit",
"não é possível desfazer",
"permanentemente",
"definitivamente",
"irreversível",
],
};
/** Per-locale phrase that promises the short undo window. */
@@ -104,6 +111,7 @@ const UNDO_PHRASES: Record<string, string> = {
pl: "cofnąć",
ko: "되돌릴 수 있",
nl: "ongedaan maken",
"pt-BR": "desfazer",
};
/** Confirmation bodies whose surface renders an Undo affordance. */
@@ -189,6 +197,7 @@ describe("sleep debt is labelled as a balance, not a summed deficit", () => {
pl: ["skumulowan", "niedobór", "niedoboru"],
ko: ["누적", "부족분", "적자", "쌓인"],
nl: ["cumulatief", "opgeteld", "tekort", "opgebouwd"],
"pt-BR": ["acumulad", "déficit", "deficit", "somad"],
};
for (const locale of LOCALES) {
@@ -326,6 +335,17 @@ describe("the mood prognosis never states a cause", () => {
"verlaagt je",
"verhoogt je",
],
"pt-BR": [
"porque",
"por causa de",
"devido a",
"provoca",
"leva a",
"melhora o seu",
"melhora a sua",
"piora o seu",
"piora a sua",
],
};
/** Every leaf string under `insights.mood.prognosis`, flattened. */
@@ -430,6 +450,12 @@ describe("the MCP token copy never claims the mint is read-only", () => {
"alleen-lezentoken",
"token met alleen leesrechten",
],
"pt-BR": [
"token somente leitura",
"token de somente leitura",
"token só de leitura",
"token apenas de leitura",
],
};
for (const locale of LOCALES) {
@@ -480,6 +506,7 @@ describe("AI consent withdrawal copy matches what withdrawing does", () => {
pl: ["własnym kluczu", "lokalnym modelu"],
ko: ["자신의 키", "로컬 모델"],
nl: ["eigen sleutel", "lokaal model"],
"pt-BR": ["sua própria chave", "modelo local"],
};
for (const locale of LOCALES) {
+2 -2
View File
@@ -105,7 +105,7 @@ describe("the published boundary matches the one the code draws", () => {
expect(accessDescription()).toMatch(/allergies, family history/i);
});
it("keeps the refusal paragraph honest in all six locales", () => {
it("keeps the refusal paragraph honest in every locale", () => {
// The delegate-facing half. It is shown on a page sharing does NOT cover,
// so a sentence that over-claims there is read at the exact moment
// somebody is working out where the edge is.
@@ -125,7 +125,7 @@ describe("the published boundary matches the one the code draws", () => {
// And it must still say the account itself stays with its owner —
// dropping that would fix one over-claim by removing the boundary.
expect(text, locale).toMatch(
/konto|account|compte|cuenta|konta|koncie|계정/i,
/konto|account|compte|cuenta|konta|koncie|conta|계정/i,
);
}
expect(missing).toEqual([]);
@@ -223,9 +223,9 @@ const FULL_RECORD = {
const DOTTED_KEY = /\b[a-z][A-Za-z0-9]*(\.[A-Za-z0-9_]+){2,}\b/g;
describe("<ClinicianView> resolves every label in every locale", () => {
it("covers all eight shipped locales", () => {
it("covers all nine shipped locales", () => {
// A floor: an empty locale list would make the loop below assert nothing.
expect(locales.length).toBe(8);
expect(locales.length).toBe(9);
});
for (const locale of locales) {
@@ -34,6 +34,7 @@ import { join } from "node:path";
import { renderToStaticMarkup } from "react-dom/server";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { locales } from "@/lib/i18n/config";
import { I18nProvider } from "@/lib/i18n/context";
import { ApiError } from "@/lib/api/api-fetch";
import type { AccountAccessEntry } from "@/lib/sharing/account-access-view";
@@ -285,12 +286,12 @@ describe("the managed-profile card, in the shared-access section", () => {
}
// The timezone control is the shared picker, which labels itself.
expect(html).toContain('id="managed-profile-timezone"');
// Eight languages offered, which is the route's enum exactly.
// Every shipped language offered, which is the route's enum exactly.
const options =
/<select[^>]*data-slot="managed-profile-locale"[\s\S]*?<\/select>/
.exec(html)?.[0]
.match(/<option/g) ?? [];
expect(options).toHaveLength(8);
expect(options).toHaveLength(locales.length);
// Four sex options, and the first is the empty one: null is a real answer
// ("not recorded") rather than an absent field, and a form offering only
// the three enum members would make it unsayable.
@@ -73,10 +73,10 @@ const VALUE_GROUPS: Array<{ group: string; keys: readonly string[] }> = [
];
describe("mood context vocabulary labels", () => {
it("reads eight locales and a non-empty vocabulary", () => {
it("reads nine locales and a non-empty vocabulary", () => {
// Both ends have to be there before anything below proves anything. A
// guard that walks an empty list is a green light nobody earned.
expect(LOCALES.length).toBe(8);
expect(LOCALES.length).toBe(9);
expect(VALUE_GROUPS.length).toBeGreaterThan(0);
const total = VALUE_GROUPS.reduce((n, g) => n + g.keys.length, 0);
expect(total).toBeGreaterThan(30);
@@ -186,8 +186,8 @@ describe("connect-source state copy", () => {
) as Record<string, never>,
}));
it("covers all eight locales", () => {
expect(LOCALES.length).toBe(8);
it("covers all nine locales", () => {
expect(LOCALES.length).toBe(9);
});
it.each(LOCALES)(