06-search-and-filters asserted the French words a count renders — toContain('aucun'). It passed here and failed on both CI runners with Received string: " no results ✕".
The suite does not pin a language. AppSettings.locale defaults to 'system', LocaleService.restore() resolves that against the machine, and nothing in wdio.conf.ts or reset-profile.ts says otherwise. So a scenario reads French on a French developer machine and English on GitHub's runners — and an assertion on a translated word is a test that only one of the two can pass.
That one is fixed (it asserts the badge says something, and that the no-results state is shown). The hole is that nothing stops the next one.
What is fine today, and why it is luck
Every other literal in the specs is data the scenario itself created — a note titled Delete me once, a space named Veille — which is language-independent by construction. Nobody has needed to assert on UI text yet. That is not a rule anybody wrote down.
Shape
Two ways, and they are not exclusive:
- Pin the language for the run.
reset-profile.ts already writes the profile; it could seed devbox.locale so every scenario knows which language it is reading. ⚠️ Then 12-preferences and the titlebar's own FR/EN switch have to set it back, or they leak into the files after them — the run shares one process and one preferences.json.
- Or keep the suite language-agnostic and say so. Assert on
data-testid, on counts, on state — never on a rendered sentence. Cheaper, and it keeps the suite honest about what it is really checking.
⚠️ Whichever, it belongs in CLAUDE.md next to the other things that bite, because the failure is invisible to whoever writes the assertion: it passes on their machine.
Done when
A scenario cannot assert on a translated string without that being a deliberate, pinned choice — and the rule is written down where the next person will read it before making the same mistake.
06-search-and-filtersasserted the French words a count renders —toContain('aucun'). It passed here and failed on both CI runners withReceived string: " no results ✕".The suite does not pin a language.
AppSettings.localedefaults to'system',LocaleService.restore()resolves that against the machine, and nothing inwdio.conf.tsorreset-profile.tssays otherwise. So a scenario reads French on a French developer machine and English on GitHub's runners — and an assertion on a translated word is a test that only one of the two can pass.That one is fixed (it asserts the badge says something, and that the no-results state is shown). The hole is that nothing stops the next one.
What is fine today, and why it is luck
Every other literal in the specs is data the scenario itself created — a note titled
Delete me once, a space namedVeille— which is language-independent by construction. Nobody has needed to assert on UI text yet. That is not a rule anybody wrote down.Shape
Two ways, and they are not exclusive:
reset-profile.tsalready writes the profile; it could seeddevbox.localeso every scenario knows which language it is reading.12-preferencesand the titlebar's own FR/EN switch have to set it back, or they leak into the files after them — the run shares one process and onepreferences.json.data-testid, on counts, on state — never on a rendered sentence. Cheaper, and it keeps the suite honest about what it is really checking.CLAUDE.mdnext to the other things that bite, because the failure is invisible to whoever writes the assertion: it passes on their machine.Done when
A scenario cannot assert on a translated string without that being a deliberate, pinned choice — and the rule is written down where the next person will read it before making the same mistake.