test: stabilize browser smoke fixtures
This commit is contained in:
parent
3c6245a5f8
commit
2cda7df636
23
TODO.md
23
TODO.md
@ -86,10 +86,11 @@ the first block before investing in architecture or polish.
|
|||||||
|
|
||||||
- [x] Establish a Playwright baseline before large refactors (app shell, bundle, i18n, clone tabs, and Rehearser parsing; installed Chromium supported).
|
- [x] Establish a Playwright baseline before large refactors (app shell, bundle, i18n, clone tabs, and Rehearser parsing; installed Chromium supported).
|
||||||
- [x] Add explicit Docker memory limits where appropriate; the web container defaults to an 8 GB limit and 2 GB reservation with environment overrides.
|
- [x] Add explicit Docker memory limits where appropriate; the web container defaults to an 8 GB limit and 2 GB reservation with environment overrides.
|
||||||
- [ ] Limit upload sizes, temporary files, and concurrent jobs.
|
- [x] Limit upload sizes, temporary files, and concurrent jobs. Upload/request caps, bounded caches, and bounded
|
||||||
|
TTS/STT/image-generation pools are implemented; durable background-job conversion remains a separate P1 item.
|
||||||
- [x] Use atomic writes for `.meta.json` files.
|
- [x] Use atomic writes for `.meta.json` files.
|
||||||
- [ ] Create automatic backups before bulk deletes, voice moves, and recasts.
|
- [x] Create automatic backups before bulk deletes, voice moves, and recasts.
|
||||||
- [ ] Add request-size limits, rate limits, and authentication for destructive/API endpoints.
|
- [x] Add request-size limits, rate limits, and authentication for destructive/API endpoints.
|
||||||
|
|
||||||
## P1 — Modular frontend architecture
|
## P1 — Modular frontend architecture
|
||||||
|
|
||||||
@ -120,21 +121,23 @@ the first block before investing in architecture or polish.
|
|||||||
|
|
||||||
- [ ] Introduce background jobs for voice design, transcription, casting, and image generation.
|
- [ ] Introduce background jobs for voice design, transcription, casting, and image generation.
|
||||||
- [ ] Give every job an ID, progress, cancellation, timeout, retry, and idempotency key.
|
- [ ] Give every job an ID, progress, cancellation, timeout, retry, and idempotency key.
|
||||||
- [ ] Prevent duplicate generation when a user clicks twice.
|
- [x] Prevent duplicate generation when a user clicks twice for image, voice-design, and casting submissions.
|
||||||
- [ ] Centralize provider health checks and error handling.
|
- [ ] Centralize provider health checks and error handling.
|
||||||
- [ ] Limit concurrency to 1–2 image generations, 1–2 voice generations, and 1 STT request per user. Consistent
|
- [x] Limit concurrency to 1–2 image generations, 1–2 voice generations, and 1 STT request per user. Consistent
|
||||||
portrait/reference rendering, preview/style/seed-finder synthesis, and the OpenAI-compatible speech proxy are
|
portrait/reference rendering, preview/style/seed-finder synthesis, and the OpenAI-compatible speech proxy are
|
||||||
capped at two active jobs; STT transcription and benchmarks are capped at one active job.
|
capped at two active jobs; STT transcription and benchmarks are capped at one active job.
|
||||||
- [ ] Use bounded server queues.
|
- [x] Use bounded server queues for the implemented synchronous generation paths; durable background-job queues remain
|
||||||
|
a separate outstanding item above.
|
||||||
|
|
||||||
## P0 — Security
|
## P0 — Security
|
||||||
|
|
||||||
- [ ] Never expose API keys to frontend JavaScript or logs. Provider keys are masked on read and absent from browser
|
- [ ] Never expose API keys to frontend JavaScript or logs. Provider keys are masked on read and absent from browser
|
||||||
storage; the deliberate external-client key display and transient entry path still need a final design review.
|
storage; the deliberate external-client key display and transient entry path still need a final design review.
|
||||||
- [ ] Validate uploaded filenames and paths.
|
- [x] Validate uploaded filenames and paths.
|
||||||
- [ ] Strictly validate custom URLs to prevent SSRF.
|
- [x] Strictly validate custom URLs to prevent SSRF, with private destinations available only when explicitly allowed
|
||||||
- [ ] Add request-size limits, rate limits, and authentication for destructive/API endpoints.
|
for local provider/container operation.
|
||||||
- [ ] Run containers as non-root with dropped capabilities and read-only filesystems where practical.
|
- [x] Add request-size limits, rate limits, and authentication for destructive/API endpoints.
|
||||||
|
- [x] Run containers as non-root with dropped capabilities and read-only filesystems where practical.
|
||||||
- [x] Isolate Docker socket access behind a minimal privileged service.
|
- [x] Isolate Docker socket access behind a minimal privileged service.
|
||||||
|
|
||||||
## P0 — Data integrity and recovery
|
## P0 — Data integrity and recovery
|
||||||
|
|||||||
@ -24,6 +24,9 @@ async function navTo(page, section) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test('parses a screenplay and builds the cast', async ({ page }) => {
|
test('parses a screenplay and builds the cast', async ({ page }) => {
|
||||||
|
await page.route('**/api/voices*', route => route.fulfill({
|
||||||
|
json: [{ id: 'smoke_voice', name: 'Smoke voice', enabled: true }],
|
||||||
|
}));
|
||||||
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
||||||
await navTo(page, 's-rehearser');
|
await navTo(page, 's-rehearser');
|
||||||
await page.waitForFunction(() => typeof window.navRehearserPhase === 'function', null, { timeout: 15_000 });
|
await page.waitForFunction(() => typeof window.navRehearserPhase === 'function', null, { timeout: 15_000 });
|
||||||
|
|||||||
@ -47,12 +47,17 @@ test('clone tabs switch and show one panel at a time', async ({ page }) => {
|
|||||||
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
||||||
await navTo(page, 's-clone');
|
await navTo(page, 's-clone');
|
||||||
await page.waitForFunction(() => typeof window._cloneScheduleAutoSave === 'function', null, { timeout: 15_000 });
|
await page.waitForFunction(() => typeof window._cloneScheduleAutoSave === 'function', null, { timeout: 15_000 });
|
||||||
await expect(page.locator('.clone-src-tab[data-src="url"]')).toBeVisible({ timeout: 10_000 });
|
// The loader applies the persisted startup section during its final reveal;
|
||||||
|
// wait for that initialization to finish before changing sections.
|
||||||
|
await page.waitForSelector('#app-skeleton', { state: 'detached', timeout: 15_000 });
|
||||||
|
await page.evaluate(() => window.navTo('s-clone'));
|
||||||
|
const clone = page.locator('#s-clone');
|
||||||
|
await expect(clone.locator('.clone-src-tab[data-src="url"]')).toBeVisible({ timeout: 10_000 });
|
||||||
// The app uses a fixed bottom status bar; dispatching the real DOM click
|
// The app uses a fixed bottom status bar; dispatching the real DOM click
|
||||||
// avoids a viewport-dependent overlap while still exercising the handler.
|
// avoids a viewport-dependent overlap while still exercising the handler.
|
||||||
await page.locator('.clone-src-tab[data-src="url"]').evaluate((el) => el.click());
|
await clone.locator('.clone-src-tab[data-src="url"]').evaluate((el) => el.click());
|
||||||
await expect(page.locator('#clone-src-url')).toBeVisible();
|
await expect(clone.locator('#clone-src-url')).toBeVisible();
|
||||||
await expect(page.locator('#clone-src-mic')).toBeHidden();
|
await expect(clone.locator('#clone-src-mic')).toBeHidden();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('PWA manifest and service worker are served', async ({ page, request }) => {
|
test('PWA manifest and service worker are served', async ({ page, request }) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user