Web fix: deterministic feature strings, suggest_category vectors, raw bodies in the fake server

- Root cause of the raw keys (web_accounts_load_failed, the settle missing state) on main:
  Vitest 5's file-system module cache (on by default) skips only modules whose source
  contains the literal `import.meta.glob(`. catalog.ts wrote `import.meta.glob<…>(`, so
  its transform was cached by its own content and kept the glob result from before a
  feature added its i18n/*.json; the main checkout's node_modules/.vitest-cache served
  that stale catalog until a config change invalidated it. Fresh worktrees passed.
- catalog.ts: glob without a type argument (typed by annotation), with the reason.
- catalog.test.ts: the loaded feature catalogs must equal the i18n files on disk.
- A string two features render moves to the shared catalog: web_accounts_load_failed
  (accounts/PageStates, also rendered by Home) is now web_ledger_load_failed in
  src/i18n/web-strings.csv; sv.json/en.json regenerated.
- vectors.test.ts: suggest_category is PORTED and runs against features/expenses/suggest.
- fakeServer: bodies by Content-Type (JSON parsed, text/* as text, else raw bytes);
  ProfilePage's picture test asserts the uploaded bytes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
2026-10-21 21:44:30 +02:00
parent 1aecf5172f
commit 83f52937cf
11 changed files with 79 additions and 10 deletions
+22
View File
@@ -1,3 +1,5 @@
import { readdirSync } from 'node:fs';
import { join } from 'node:path';
import { describe, expect, it } from 'vitest';
import { featureCatalogs, fill, LOADED_FEATURE_CATALOGS, mergeCatalogs, translate, translatePlural } from './catalog';
import en from './en.json';
@@ -46,6 +48,26 @@ describe('feature catalogs', () => {
expect(merged).toEqual({ common_save: 'Spara', web_settle_hint: 'Tips' });
});
it('loads every feature catalog on disk (no stale transform of catalog.ts)', () => {
const featuresDir = join(process.cwd(), 'src', 'features'); // Vitest runs from web/
const onDisk = readdirSync(featuresDir, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.flatMap((entry) => {
let files: string[] = [];
try {
files = readdirSync(join(featuresDir, entry.name, 'i18n'));
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
}
return files.filter((file) => /^(sv|en)\.json$/.test(file)).map((file) => `${entry.name}/${file}`);
})
.sort();
const loaded = Object.entries(LOADED_FEATURE_CATALOGS)
.flatMap(([feature, catalogs]) => Object.keys(catalogs).map((language) => `${feature}/${language}.json`))
.sort();
expect(loaded).toEqual(onDisk);
});
it('every loaded feature has the same web_ keys in sv and en, none shared', () => {
for (const [feature, catalogs] of Object.entries(LOADED_FEATURE_CATALOGS)) {
const svKeys = Object.keys(catalogs.sv ?? {}).sort();