/** * Playwright end-to-end smoke test * * Preconditions (operator must supply): * - The backend is running on port 8080 with the built UI served via --webui-dir * - SVAPI_ADMIN_KEY env var contains the admin API key (e.g. "e2eadmin") * - SVAPI_BASE env var (optional) overrides the base URL (default http://localhost:8080) * * Run sequence (example): * cmake --build build -j"$(nproc)" * SMARTBOTIC_VECTORAPI_KEY=e2eadmin ./build/src/smartbotic-vectorapi \ * --config config/config.json --share-dir api --webui-dir build/webui/dist & * cd webui && SVAPI_ADMIN_KEY=e2eadmin SVAPI_BASE=http://localhost:8080 npx playwright test * * The test creates a collection "e2e_docs" and a document inside it, then * deletes the collection (cleanup) before logging out. */ import { test, expect } from '@playwright/test' const ADMIN_KEY = process.env['SVAPI_ADMIN_KEY'] ?? 'e2eadmin' test('login → collections → documents → logout smoke', async ({ page }) => { // ── 1. Navigate to / → should redirect to /login ───────────────────────── await page.goto('/') await expect(page).toHaveURL(/\/login/) // ── 2. Fill API key and submit ──────────────────────────────────────────── await page.locator('[data-testid="api-key-input"]').fill(ADMIN_KEY) await page.locator('[data-testid="login-submit-btn"]').click() // After login, should land on dashboard (root path) await expect(page).toHaveURL(/\/$/) // Project selector should be visible and show "default" const projectSelector = page.getByRole('combobox', { name: 'Select project' }) await expect(projectSelector).toBeVisible() await expect(projectSelector).toHaveValue('default') // ── 3. Navigate to Collections ──────────────────────────────────────────── await page.getByRole('link', { name: 'Collections' }).click() await expect(page).toHaveURL(/\/collections/) // Open "New collection" modal await page.locator('[data-testid="new-collection-btn"]').click() // Fill collection name const nameInput = page.locator('[data-testid="collection-name-input"]') await expect(nameInput).toBeVisible() await nameInput.fill('e2e_docs') // Kind is already "json" by default — just submit await page.locator('[data-testid="collection-create-btn"]').click() // Wait for modal to close and collection to appear await expect(page.locator('[data-testid="collection-row-e2e_docs"]')).toBeVisible({ timeout: 10_000 }) // ── 4. Navigate to Documents ────────────────────────────────────────────── await page.getByRole('link', { name: 'Documents' }).click() await expect(page).toHaveURL(/\/documents/) // Collection picker should auto-select e2e_docs (it will be in the list) const collPicker = page.locator('[data-testid="collection-picker"]') await expect(collPicker).toBeVisible() // Select e2e_docs in the collection picker await collPicker.selectOption('e2e_docs') // Click "New document" const newDocBtn = page.locator('[data-testid="new-doc-btn"]') await expect(newDocBtn).toBeEnabled({ timeout: 5_000 }) await newDocBtn.click() // Monaco editor: wait for it to load and type the document // Monaco renders a contenteditable div; we use keyboard to replace the default "{}" const monacoEditor = page.locator('.monaco-editor').first() await expect(monacoEditor).toBeVisible({ timeout: 15_000 }) // Click into the editor, select all and type our JSON await monacoEditor.click() await page.keyboard.press('Control+a') await page.keyboard.type('{"name":"e2e"}') // Save button const saveBtn = page.locator('[data-testid="doc-save-btn"]') await expect(saveBtn).toBeEnabled({ timeout: 5_000 }) await saveBtn.click() // Document should appear in the list await expect(page.locator('[data-testid="doc-row"]').first()).toBeVisible({ timeout: 10_000 }) // ── 5. Delete the collection (cleanup) ──────────────────────────────────── await page.getByRole('link', { name: 'Collections' }).click() await expect(page).toHaveURL(/\/collections/) // Click Delete button for e2e_docs row await page.getByRole('button', { name: 'Delete e2e_docs' }).click() // Confirm deletion await page.locator('[data-testid="confirm-dialog-confirm-btn"]').click() // Collection should be gone await expect(page.locator('[data-testid="collection-row-e2e_docs"]')).not.toBeVisible({ timeout: 10_000 }) // ── 6. Logout ────────────────────────────────────────────────────────────── await page.locator('[data-testid="logout-btn"]').click() await expect(page).toHaveURL(/\/login/) })