Some checks failed
CD / Deploy to Test (push) Blocked by required conditions
CD / Smoke tests against Test (push) Blocked by required conditions
CD / Promote to Int (push) Blocked by required conditions
CI / Auth e2e pack (push) Blocked by required conditions
CI / Import/export fidelity gate (push) Blocked by required conditions
CI / Build container images (push) Blocked by required conditions
CD / Build and push images (push) Has been cancelled
CI / Lint, typecheck, test (push) Has been cancelled
- lucide-react (MIT, tree-shaken, compiled into the bundle — no runtime requests; fonts.spec's off-origin assertion covers the page route) - page-actions slot: TopBar registers a DOM element via context, the active page portals its actions into it, TopBar stays page-agnostic - PageActions: mode toggle, watch (WatchToggle icon variant), comments (unread badge kept), attachments, plugin page tools, labels, history as icon buttons with localized aria-label+tooltip (de+en), plus an overflow menu for markdown copy/download, docx/odt/pdf export and the destructive delete (confirm kept) - page header keeps only the title; the editor-shell tools row is gone; panel state lives in PageEditorPage now - hamburger/search/bell adopt the same icon set - e2e: content/export open the overflow menu; class hooks (editor-page__mode-toggle, editor-shell__*-toggle, editor-page__labels-toggle, editor-page__export) kept stable Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
88 lines
3.6 KiB
TypeScript
88 lines
3.6 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
|
|
import { contextForUser } from './helpers';
|
|
|
|
/**
|
|
* Export pack (issue #65): the pond-settings ZIP download and the page-menu
|
|
* office-format export. The ZIP needs no conversion sidecar; `.docx` runs a
|
|
* conversion job and self-skips without `E2E_PANDOC` (CI's e2e stack has no
|
|
* reachable pandoc — same as the import pack, #64). Selectors are
|
|
* language-neutral (CSS classes, not button text).
|
|
*/
|
|
const BASE_URL = process.env.E2E_BASE_URL ?? 'http://localhost:5173';
|
|
|
|
async function personalPond(
|
|
context: Awaited<ReturnType<typeof contextForUser>>,
|
|
): Promise<{ id: string; slug: string }> {
|
|
const ponds = await context.request.get('/api/v1/ponds');
|
|
const pond = (await ponds.json()).find((p: { type: string }) => p.type === 'personal');
|
|
return { id: pond.id, slug: pond.slug };
|
|
}
|
|
|
|
test('downloads a pond as a Markdown ZIP from pond settings', async ({ browser }) => {
|
|
const context = await contextForUser(browser, BASE_URL, 'fixture-user');
|
|
const pond = await personalPond(context);
|
|
// Make sure the pond has at least one page to export.
|
|
await context.request.post(`/api/v1/ponds/${pond.id}/pages`, {
|
|
data: { title: `Export Me ${Date.now()}` },
|
|
});
|
|
const page = await context.newPage();
|
|
await page.goto(`/p/${pond.slug}/settings`);
|
|
|
|
const [download] = await Promise.all([
|
|
page.waitForEvent('download'),
|
|
page.locator('.pond-export a').click(),
|
|
]);
|
|
expect(download.suggestedFilename()).toBe(`${pond.slug}.zip`);
|
|
|
|
await context.close();
|
|
});
|
|
|
|
test('exports a page to .docx from the page menu', async ({ browser }) => {
|
|
test.skip(!process.env.E2E_PANDOC, 'needs a reachable pandoc sidecar');
|
|
const context = await contextForUser(browser, BASE_URL, 'fixture-user');
|
|
const pond = await personalPond(context);
|
|
const created = await context.request.post(`/api/v1/ponds/${pond.id}/pages`, {
|
|
data: { title: `Docx Export ${Date.now()}` },
|
|
});
|
|
const created_page = await created.json();
|
|
const page = await context.newPage();
|
|
await page.goto(`/p/${pond.slug}/${created_page.slug}`);
|
|
|
|
// The export entries live in the TopBar overflow menu since #101; the
|
|
// first one is `.docx`. Clicking runs the job and downloads the result.
|
|
await page.locator('.page-actions__more .icon-button').click();
|
|
const [download] = await Promise.all([
|
|
page.waitForEvent('download', { timeout: 30000 }),
|
|
page.locator('.editor-page__export button').first().click(),
|
|
]);
|
|
expect(download.suggestedFilename()).toBe(`${created_page.slug}.docx`);
|
|
|
|
await context.close();
|
|
});
|
|
|
|
test('exports a page to PDF from the page menu', async ({ browser }) => {
|
|
// PDF needs the Gotenberg sidecar reachable by the api (like the .docx case
|
|
// needs pandoc). CI's e2e stack has none, so this runs locally / on a stage
|
|
// with E2E_GOTENBERG set.
|
|
test.skip(!process.env.E2E_GOTENBERG, 'needs a reachable Gotenberg sidecar');
|
|
const context = await contextForUser(browser, BASE_URL, 'fixture-user');
|
|
const pond = await personalPond(context);
|
|
const created = await context.request.post(`/api/v1/ponds/${pond.id}/pages`, {
|
|
data: { title: `Pdf Export ${Date.now()}` },
|
|
});
|
|
const created_page = await created.json();
|
|
const page = await context.newPage();
|
|
await page.goto(`/p/${pond.slug}/${created_page.slug}`);
|
|
|
|
// The third export entry in the overflow menu is PDF (docx, odt, pdf).
|
|
await page.locator('.page-actions__more .icon-button').click();
|
|
const [download] = await Promise.all([
|
|
page.waitForEvent('download', { timeout: 30000 }),
|
|
page.locator('.editor-page__export button').nth(2).click(),
|
|
]);
|
|
expect(download.suggestedFilename()).toBe(`${created_page.slug}.pdf`);
|
|
|
|
await context.close();
|
|
});
|