fix(magazine): mock /api/settings in magazine specs and await lazy module in deep-link spec

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
mARTin-B78 2026-10-04 20:06:52 +02:00
parent c7d5134734
commit 38f88958c6
2 changed files with 5 additions and 0 deletions

View File

@ -15,6 +15,7 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
- **Voice list rows no longer show the "Source unknown" badge** (it crowded the play buttons). The badge stays in the voice inspector, next to the Source & consent panel. - **Voice list rows no longer show the "Source unknown" badge** (it crowded the play buttons). The badge stays in the voice inspector, next to the Source & consent panel.
### Fixed ### Fixed
- **Magazine specs no longer touch the live settings.** The export-provenance checkboxes read and saved `/api/settings` on the real app (a run left the default off, failing the next one); the spec now mocks it. The hostile deep-link spec waits for the lazily loaded magazine module before calling the parser.
- **Duplicate German i18n keys and dead frontend code.** Removed 14 shadowed duplicate keys in `i18n.js` (effective translations unchanged), the unreachable body of `audiobookShowPreview`, and two identical-branch ternaries. - **Duplicate German i18n keys and dead frontend code.** Removed 14 shadowed duplicate keys in `i18n.js` (effective translations unchanged), the unreachable body of `audiobookShowPreview`, and two identical-branch ternaries.
- **Security hardening (Sonar findings).** The Docker installer downloads `get-docker.sh` into a private `mkdtemp` dir (not a predictable `/tmp` path) and removes it afterwards; the OMDb poster lookup uses HTTPS; the fish.audio cache key marks MD5 as `usedforsecurity=False`. - **Security hardening (Sonar findings).** The Docker installer downloads `get-docker.sh` into a private `mkdtemp` dir (not a predictable `/tmp` path) and removes it afterwards; the OMDb poster lookup uses HTTPS; the fish.audio cache key marks MD5 as `usedforsecurity=False`.
- **Background jobs could be garbage-collected mid-run.** Fire-and-forget `asyncio.create_task` calls (magazine, conversation, tts, stt, library, sources) now go through `core.tasks.spawn`, which keeps a strong reference until the task ends and logs failures by exception class only. - **Background jobs could be garbage-collected mid-run.** Fire-and-forget `asyncio.create_task` calls (magazine, conversation, tts, stt, library, sources) now go through `core.tasks.spawn`, which keeps a strong reference until the task ends and logs failures by exception class only.

View File

@ -122,6 +122,8 @@ async function mockBackend(page, { articleStatus = 'detected', hasAudio = false,
await page.route('**/api/voices*', route => route.fulfill({ await page.route('**/api/voices*', route => route.fulfill({
json: [{ id: 'smoke_voice', name: 'Smoke voice', enabled: true, tag: 'Magazine' }], json: [{ id: 'smoke_voice', name: 'Smoke voice', enabled: true, tag: 'Magazine' }],
})); }));
// The export-provenance default is read and saved through /api/settings; never let a spec reach the live one.
await page.route('**/api/settings', route => route.fulfill({ json: { export_provenance_default: true } }));
} }
test.describe('Magazine reader', () => { test.describe('Magazine reader', () => {
@ -1797,6 +1799,8 @@ test.describe('Share link and deep link into driving mode', () => {
const bad = `/#s-magazine?doc=${DOC_ID}&article=%22%3E%3Cimg%20src%3Dx%20onerror%3Dwindow.__xss%3D1%3E&mode=drive`; const bad = `/#s-magazine?doc=${DOC_ID}&article=%22%3E%3Cimg%20src%3Dx%20onerror%3Dwindow.__xss%3D1%3E&mode=drive`;
await openLink(page, bad); await openLink(page, bad);
await navTo(page, 's-magazine'); await navTo(page, 's-magazine');
// The magazine module loads lazily: wait for it, so the checks below run against the loaded parser.
await page.waitForFunction(() => typeof window.magParseDeepLink === 'function', null, { timeout: 15_000 });
await expect(page.locator('#mag-driving')).toBeHidden(); await expect(page.locator('#mag-driving')).toBeHidden();
expect(await page.evaluate(() => window.__xss)).toBeUndefined(); expect(await page.evaluate(() => window.__xss)).toBeUndefined();
const { parse } = await page.evaluate(() => ({ parse: [ const { parse } = await page.evaluate(() => ({ parse: [