import { test, expect, createAdminContext, uniqueSuffix } from './fixtures'; /** * The public submission page (#222). * * Every fixture here carries a run id and every assertion names only what this * run created. The suite is fullyParallel against one shared database, so a * spec that asserts on anything catalogue-wide is asserting on other specs too * (#241). */ const RUN = `i${uniqueSuffix()}`; let token = ''; test.beforeAll(async ({ playwright }) => { const api = await createAdminContext(playwright); const res = await api.post('/api/admin/upload-links', { data: { label: `Intake spec ${RUN}`, email: `intake-${RUN}@example.com` } }); expect(res.status(), 'creating the upload link').toBe(201); token = (await res.json()).token; await api.dispose(); }); test.describe('Sending in an item through a link', () => { test('shows the form for a link that works', async ({ page }) => { await page.goto(`/submit/${token}`); await expect(page.getByRole('heading', { name: 'Send in an item' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Choose photos' })).toBeVisible(); }); // Nothing to send is not a submission, and the server would refuse it — but // the sender should not have to find that out by pressing the button. test('will not send until a photo is chosen', async ({ page }) => { await page.goto(`/submit/${token}`); await expect(page.getByRole('button', { name: 'Send' })).toBeDisabled(); }); // One state for every refusal, matching the server's single 404. Saying which // kind of dead a link is would tell a stranger whether one they guessed at // exists. test('explains an unusable link without saying which kind', async ({ page }) => { await page.goto('/submit/not-a-real-token'); await expect(page.getByRole('heading', { name: 'This link is not active' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Choose photos' })).toHaveCount(0); }); test('accepts a photo and says it arrived', async ({ page }) => { await page.goto(`/submit/${token}`); // A real 1x1 PNG, so the server's magic-byte check sees what it expects // rather than a buffer that merely starts correctly. const png = Buffer.from( 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==', 'base64' ); await page.setInputFiles('input[type="file"]', { name: `${RUN}.png`, mimeType: 'image/png', buffer: png }); await page.getByLabel('Anything you know about this item').fill(`Stoneware ${RUN}`); await page.getByRole('button', { name: 'Send' }).click(); await expect(page.getByRole('heading', { name: 'Thank you — it arrived' })).toBeVisible(); // The wording matters: it is what stops a sender wondering why their item // is not on the site. await expect(page.getByText(/Nothing is listed for sale until/)).toBeVisible(); }); // Ticked by default, because that is the decision: most items look better // cut out, and the reverse default would mean almost nobody got it. test('offers to remove the background, already ticked', async ({ page }) => { await page.goto(`/submit/${token}`); const control = page.getByRole('checkbox', { name: /remove the background/i }); await expect(control).toBeVisible(); await expect(control).toBeChecked(); }); test('lets a sender turn it off and still send', async ({ page }) => { await page.goto(`/submit/${token}`); const png = Buffer.from( 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==', 'base64' ); await page.setInputFiles('input[type="file"]', { name: `${RUN}-nobg.png`, mimeType: 'image/png', buffer: png }); await page.getByRole('checkbox', { name: /remove the background/i }).uncheck(); await page.getByRole('button', { name: 'Send' }).click(); await expect(page.getByRole('heading', { name: 'Thank you — it arrived' })).toBeVisible(); }); });