import { expect, test } from '@playwright/test'; import type { Page } from '@playwright/test'; import { contextForUser } from './helpers'; /** * Page trash pack (issue #31). Runs against the local dev stack (api + * web); no Mailpit needed. The fixture pond accumulates pages across many * e2e runs, so trash-list assertions always scope to this test's own * unique title rather than matching by loose/shared text. */ const BASE_URL = process.env.E2E_BASE_URL ?? 'http://localhost:5173'; async function createPage( context: Awaited>, title: string, ): Promise<{ pondSlug: string; pageSlug: string }> { const ponds = await context.request.get('/api/v1/ponds'); const pond = (await ponds.json()).find((p: { type: string }) => p.type === 'personal'); const created = await context.request.post(`/api/v1/ponds/${pond.id}/pages`, { data: { title }, }); const page = await created.json(); return { pondSlug: pond.slug, pageSlug: page.slug }; } function acceptDialogs(page: Page): void { page.on('dialog', (dialog) => void dialog.accept()); } /** The trash list row for an exact page title, scoping restore/purge clicks to it. */ function trashItem(page: Page, title: string) { return page.locator('.trash-page__item').filter({ hasText: title }); } test('deleting a page hides it from the sidebar and shows a trash hint on direct URL', async ({ browser, }) => { const context = await contextForUser(browser, BASE_URL, 'fixture-user'); const { pondSlug, pageSlug } = await createPage(context, `E2E Trash Delete ${Date.now()}`); const page = await context.newPage(); acceptDialogs(page); await page.goto(`/p/${pondSlug}/${pageSlug}`); await page.getByRole('button', { name: /edit|bearbeiten/i }).click(); // Delete sits behind the TopBar overflow menu since #101. await page.getByRole('button', { name: /more actions|weitere aktionen/i }).click(); await page.getByRole('menuitem', { name: /move to trash|papierkorb verschieben/i }).click(); // Deleting navigates away; going back to the same URL now 404s with a hint. await page.goto(`/p/${pondSlug}/${pageSlug}`); await expect(page.getByText(/moved to the trash|papierkorb verschoben/i)).toBeVisible(); await expect( page.getByRole('link', { name: /view in trash|im papierkorb ansehen/i }), ).toBeVisible(); await context.close(); }); test('restoring a page from the trash brings it back', async ({ browser }) => { const context = await contextForUser(browser, BASE_URL, 'fixture-user'); const title = `E2E Trash Restore ${Date.now()}`; const { pondSlug, pageSlug } = await createPage(context, title); const page = await context.newPage(); acceptDialogs(page); await page.goto(`/p/${pondSlug}/${pageSlug}`); await page.getByRole('button', { name: /edit|bearbeiten/i }).click(); // Delete sits behind the TopBar overflow menu since #101. await page.getByRole('button', { name: /more actions|weitere aktionen/i }).click(); await page.getByRole('menuitem', { name: /move to trash|papierkorb verschieben/i }).click(); await page.goto(`/p/${pondSlug}/trash`); const item = trashItem(page, title); await expect(item).toBeVisible(); await item.getByRole('button', { name: /restore|wiederherstellen/i }).click(); await expect(item).toHaveCount(0); await page.goto(`/p/${pondSlug}/${pageSlug}`); await expect(page.locator('.editor-page__title')).toHaveValue(title); await context.close(); }); test('purging a page from the trash removes it for good', async ({ browser }) => { const context = await contextForUser(browser, BASE_URL, 'fixture-user'); const title = `E2E Trash Purge ${Date.now()}`; const { pondSlug, pageSlug } = await createPage(context, title); const page = await context.newPage(); acceptDialogs(page); await page.goto(`/p/${pondSlug}/${pageSlug}`); await page.getByRole('button', { name: /edit|bearbeiten/i }).click(); // Delete sits behind the TopBar overflow menu since #101. await page.getByRole('button', { name: /more actions|weitere aktionen/i }).click(); await page.getByRole('menuitem', { name: /move to trash|papierkorb verschieben/i }).click(); await page.goto(`/p/${pondSlug}/trash`); const item = trashItem(page, title); await expect(item).toBeVisible(); await item.getByRole('button', { name: /delete forever|endgültig löschen/i }).click(); await expect(item).toHaveCount(0); await context.close(); });