Merge pull request '[READY][i18n][P19] Issue #207 smoke/e2e artifact: da+en locale switch + primary-only audio policy' (#215) from dev/issue-207-i18n-audio-smoke into main
All checks were successful
CI / test-and-quality (push) Successful in 2m30s

This commit was merged in pull request #215.
This commit is contained in:
2026-03-01 21:46:03 +01:00
2 changed files with 79 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
# Issue #207 Artifact — da/en locale switch + audio routing (primary-only)
## Scope
Acceptance for `[READY][i18n][P19]`:
1. Verify one MVP host+player flow in both `en` and `da` locale context.
2. Verify controlled fallback to `en` when a locale key is missing.
3. Verify audio-routing policy is primary-only (client/player has no audio output).
No new feature behavior added — this is verification-only evidence.
## Smoke/e2e evidence (Angular shell test run)
Executed from `frontend/angular`:
```bash
npm test -- --reporter=dot src/app/lobby-i18n.spec.ts src/app/features/host/host-shell.component.spec.ts src/app/features/player/player-shell.component.spec.ts
```
Evidence covered by the run:
- `lobby-i18n.spec.ts`
- subscriber locale switch updates (`en -> da -> en`)
- controlled fallback where `da` key is removed during test and `en` copy is returned
- `clientHasNoAudioOutput === true` (primary-only audio routing)
- `host-shell.component.spec.ts`
- host actions and route sync in SPA (no reload)
- `player-shell.component.spec.ts`
- player session refresh/sync path and retry behavior
## Policy evidence
- Shared catalog capability: `shared/i18n/lobby.json`
- `frontend.capabilities.client_has_no_audio_output = true`
- Angular host/player shells bind this capability:
- `HostShellComponent.clientHasNoAudioOutput`
- `PlayerShellComponent.clientHasNoAudioOutput`
Conclusion: locale switching works in `da`/`en`, fallback resolves to `en` when locale key is intentionally missing, and client audio remains disabled by policy (`primary-only`).

View File

@@ -44,4 +44,48 @@ describe('lobby i18n locale propagation', () => {
i18n.setPreferredLocale('en');
expect(updates).toEqual(['en', 'da']);
});
it('falls back to default en translation when da key is intentionally missing', async () => {
vi.stubGlobal('window', {
location: { search: '' },
localStorage: storageMock({ 'wpp.locale': 'da' }),
});
vi.stubGlobal('navigator', { language: 'da-DK' });
const i18n = await import('./lobby-i18n');
const catalogModule = await import('../../../../shared/i18n/lobby.json');
const catalog = catalogModule.default as {
frontend: {
ui: {
common: {
refresh: {
en?: string;
da?: string;
};
};
};
};
};
const originalDa = catalog.frontend.ui.common.refresh.da;
catalog.frontend.ui.common.refresh.da = undefined;
try {
expect(i18n.t('common.refresh', 'da')).toBe(catalog.frontend.ui.common.refresh.en);
} finally {
catalog.frontend.ui.common.refresh.da = originalDa;
}
});
it('exposes primary-only audio routing policy to clients', async () => {
vi.stubGlobal('window', {
location: { search: '' },
localStorage: storageMock({ 'wpp.locale': 'en' }),
});
vi.stubGlobal('navigator', { language: 'en-US' });
const i18n = await import('./lobby-i18n');
expect(i18n.clientHasNoAudioOutput).toBe(true);
});
});