import { expect, test, type Page } from '@playwright/test'; import { login as loginAs } from './helpers'; // Writing-first capture as a USER sees it: pick a documentation type, write in // the editor, get a refined version of the section at the cursor, accept it, // read the diff before saving, and publish. The refine step drives the real // LLM, so it can take a few seconds. test.describe.configure({ mode: 'serial' }); test.setTimeout(180_000); // Template names are matched in German on purpose: a template is product // CONTENT in the instance's own language (PABLAN_DEFAULT_LOCALE), not UI copy // that follows the reader's language setting. /** Draft documents this spec created, so cleanup removes only its own. */ let created: string[] = []; async function newDraft(page: Page, templateText: string): Promise { await page.goto('/documents/new'); await page.locator('body[data-hydrated]').waitFor(); await page.getByTestId('capture-template').filter({ hasText: templateText }).first().click(); await expect(page).toHaveURL(/\/documents\/[0-9a-f-]{36}\/edit$/); const id = page.url().split('/')[4]; created.push(id); await page.locator('.cm-content').waitFor(); return id; } test.afterEach(async ({ page }) => { for (const id of created) { await page.request.delete(`/api/documents/${id}`); } created = []; await page.request.post('/api/auth/logout'); }); test('picking a type opens the editor on the template skeleton', async ({ page }) => { await loginAs(page, 'pablo@pablan.dev'); await newDraft(page, 'Über dich'); // The editor opens on the skeleton headings, not an empty box, and nothing // else: the suggestion is a block inside the text that appears when one // streams, so before any typing there is no widget and nothing to accept. await expect(page.getByTestId('editor-source')).toContainText('##'); await expect(page.getByTestId('editor-suggestion')).toHaveCount(0); await expect(page.getByTestId('editor-accept')).toHaveCount(0); }); test('a typing pause yields a section suggestion that overwrites on accept', async ({ page }) => { await loginAs(page, 'pablo@pablan.dev'); await newDraft(page, 'Über dich'); // Write rough notes under the first heading. await page.locator('.cm-content').click(); await page.keyboard.press('Control+Home'); await page.keyboard.press('ArrowDown'); await page.keyboard.type( 'also der neue kollege wartet die cnc maschinen und ist ansprechpartner für den einkauf.' ); // After the pause the model streams a refined version of the section. await expect(page.getByTestId('editor-accept')).toBeVisible({ timeout: 40_000 }); await page.getByTestId('editor-accept').click(); // Accepting consumes the suggestion (the pane returns to its empty state) // and replaces the rough notes — the lowercase draft phrasing is gone. await expect(page.getByTestId('editor-accept')).toHaveCount(0); await expect(page.getByTestId('editor-source')).not.toContainText('also der neue kollege'); // Saving shows what changed first, VSCode-style, and asks again. await page.getByTestId('editor-open-save').click(); await expect(page.getByTestId('editor-diff')).toBeVisible(); }); test('a draft is published from the editor in one step', async ({ page }) => { await loginAs(page, 'pablo@pablan.dev'); const id = await newDraft(page, 'Notiz'); await page.locator('.cm-content').click(); await page.keyboard.type('\nEin kurzer, brauchbarer Inhalt zum Veröffentlichen.'); await page.getByTestId('editor-open-save').click(); await page.getByTestId('editor-publish').click(); // Published, and the author is offered a colleague to check it. await expect(page.getByTestId('capture-success')).toBeVisible(); await page.getByTestId('success-view').click(); await expect(page).toHaveURL(new RegExp(`/documents/${id}$`)); await expect(page.getByTestId('document-status')).toHaveAttribute('data-status', 'published'); });