All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m57s
The Import empty state now shows an "Expected structure" box in both of its variants (no folder, folder without sources): the parent folder to choose, one subfolder per account, and files at its root ignored, as scan_import_folder reads them. New ImportFolderStructure component, a nested list with decorative icons. The getting-started cards ask for the parent folder and one subfolder per account instead of one per bank. The preview card says that an expense must count among the outflows and names "Flip the signs" next to "Check duplicates": WIZARD_ROWS rows now carry every label their sentence names. Every Import card sentence quotes the controls it names, in French and in English, pinned by a test that reads the locale files. Help tips, docs.import.steps, the user guide, the QA list and architecture.md follow; CHANGELOG entry under Changed. Resolves #422 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
47 lines
2 KiB
TypeScript
47 lines
2 KiB
TypeScript
// The Import step card's sentences, as the locale files write them (#422).
|
|
//
|
|
// A sentence names a control through an interpolation (`StepCardText.labels`,
|
|
// translated by `StepCard`); the label then reads between quotation marks,
|
|
// « » in French and “ ” in English (D11), so the reader tells the button's
|
|
// name from the sentence around it. The folder name of `noSources` is quoted
|
|
// the same way. These tests hold the real files to that rule.
|
|
|
|
import { describe, it, expect } from "vitest";
|
|
import fr from "../i18n/locales/fr.json";
|
|
import en from "../i18n/locales/en.json";
|
|
|
|
type Cards = Record<string, string>;
|
|
|
|
const IMPORT_CARDS: Record<"fr" | "en", Cards> = {
|
|
fr: (fr as { onboarding: { card: { import: Cards } } }).onboarding.card.import,
|
|
en: (en as { onboarding: { card: { import: Cards } } }).onboarding.card.import,
|
|
};
|
|
|
|
const QUOTED: Record<"fr" | "en", (name: string) => RegExp> = {
|
|
fr: (name) => new RegExp(`« \\{\\{${name}\\}\\} »`),
|
|
en: (name) => new RegExp(`“\\{\\{${name}\\}\\}”`),
|
|
};
|
|
|
|
/** The names of the plain interpolations of a sentence (`{{name}}`). */
|
|
function interpolations(sentence: string): string[] {
|
|
return [...sentence.matchAll(/\{\{(\w+)\}\}/g)].map((match) => match[1]);
|
|
}
|
|
|
|
describe("Import step card sentences quote every control they name (#422)", () => {
|
|
it.each(["fr", "en"] as const)("%s: each interpolation sits between quotation marks", (lang) => {
|
|
const cards = IMPORT_CARDS[lang];
|
|
let named = 0;
|
|
for (const [key, sentence] of Object.entries(cards)) {
|
|
for (const name of interpolations(sentence)) {
|
|
named += 1;
|
|
expect(sentence, `onboarding.card.import.${key}: ${name}`).toMatch(QUOTED[lang](name));
|
|
}
|
|
}
|
|
// noFolder 1, noSources 3, sourceConfig 1, filePreview 2, duplicateCheck 1, confirm 1.
|
|
expect(named).toBe(9);
|
|
});
|
|
|
|
it.each(["fr", "en"] as const)("%s: the preview names the flip and the next button", (lang) => {
|
|
expect(interpolations(IMPORT_CARDS[lang].filePreview).sort()).toEqual(["button", "flip"]);
|
|
});
|
|
});
|