Drag-dropping page images then clicking a nav link silently discarded the whole form. Add a reusable guardUnsavedChanges helper (SvelteKit beforeNavigate confirm + native beforeunload) and wire it into the manga upload, manga edit, and chapter upload forms. The dirty check returns false while submitting so the post-save redirect isn't prompted. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
60 lines
2.1 KiB
TypeScript
60 lines
2.1 KiB
TypeScript
import { test, expect, type Page } from './fixtures';
|
|
|
|
// Leaving a form with unsaved work (e.g. a half-filled upload) must prompt so a
|
|
// misclicked nav link doesn't silently discard everything.
|
|
|
|
const DESKTOP = { width: 1280, height: 720 } as const;
|
|
|
|
async function authed(page: Page) {
|
|
await page.route('**/api/v1/auth/config', (r) =>
|
|
r.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ self_register_enabled: true, private_mode: false })
|
|
})
|
|
);
|
|
await page.route('**/api/v1/auth/me', (r) =>
|
|
r.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
user: { id: 'u1', username: 'uploader', created_at: '2026-01-01T00:00:00Z', is_admin: false }
|
|
})
|
|
})
|
|
);
|
|
await page.route('**/api/v1/auth/me/preferences', (r) =>
|
|
r.fulfill({ status: 200, contentType: 'application/json', body: '{}' })
|
|
);
|
|
await page.route('**/api/v1/genres', (r) =>
|
|
r.fulfill({ status: 200, contentType: 'application/json', body: '[]' })
|
|
);
|
|
await page.route('**/api/v1/me/bookmarks*', (r) =>
|
|
r.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ items: [], page: { limit: 50, offset: 0, total: 0 } })
|
|
})
|
|
);
|
|
}
|
|
|
|
test('upload form prompts before navigating away with unsaved changes', async ({ page }) => {
|
|
await authed(page);
|
|
await page.setViewportSize(DESKTOP);
|
|
await page.goto('/upload');
|
|
|
|
// Make the form dirty.
|
|
await page.getByLabel(/Title/).fill('WIP Manga');
|
|
|
|
// Cancel the confirm → navigation is blocked, we stay put with our input.
|
|
let dialogSeen = false;
|
|
page.once('dialog', (d) => {
|
|
dialogSeen = true;
|
|
d.dismiss();
|
|
});
|
|
await page.getByRole('link', { name: 'Bookmarks' }).click();
|
|
|
|
await expect.poll(() => dialogSeen).toBe(true);
|
|
await expect(page).toHaveURL(/\/upload/);
|
|
await expect(page.getByLabel(/Title/)).toHaveValue('WIP Manga');
|
|
});
|