|
|
@@ -0,0 +1,108 @@
|
|
|
+/**
|
|
|
+ * 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/)
|
|
|
+})
|