import { test, expect } from '@playwright/test' // AZ-461 — e2e companion for sync image detect. // // AC-1 (FT-P-11): clicking the Detect button on an image issues exactly one // POST whose URL matches `^/api/detect/[0-9]+$`. // AC-2 (FT-P-12) — async video detect — is QUARANTINEd in CI (fast-profile // it.fails() handles the assertion shape; the e2e companion // intentionally omits it until AC-25 lands so the suite-e2e // lane stays green). // AC-3 (FT-P-13): drift today — `test.fail()` until production adds the // `X-Refresh-Token` header for long-video detect. // // Requires the suite docker-compose stack and a media fixture exposing at // least one image item that the Detect button can target. Skips with a clear // reason when the seed is absent. test.describe('AZ-461 — detection endpoints (e2e companion)', () => { test('AC-1 (FT-P-11) — sync image detect URL canary', async ({ page }) => { const detectRequests: { url: string; method: string }[] = [] await page.route('**/api/detect/**', async (route) => { const req = route.request() detectRequests.push({ url: req.url(), method: req.method() }) await route.continue() }) await page.goto('/annotations') const detectBtn = page.getByRole('button', { name: /AI Detect/i }).first() if (!(await detectBtn.isVisible({ timeout: 5000 }).catch(() => false))) { test.skip(true, 'Suite seed has no media for detect') } if (await detectBtn.isDisabled().catch(() => true)) { // Need a media selected first. Click the first media-list row. const firstMedia = page.locator('div.cursor-pointer').first() if (!(await firstMedia.isVisible({ timeout: 5000 }).catch(() => false))) { test.skip(true, 'No media row visible for detect target') } await firstMedia.click() } await detectBtn.click({ timeout: 5000 }).catch(() => {}) await page.waitForFunction( () => true, undefined, { timeout: 3000 }, ).catch(() => null) expect(detectRequests.length).toBeGreaterThan(0) for (const r of detectRequests) { const path = new URL(r.url).pathname expect(path).toMatch(/^\/api\/detect\/[0-9a-zA-Z-]+$/) expect(r.method).toBe('POST') } }) test.fail('AC-3 (FT-P-13) — long-video detect carries `X-Refresh-Token` header (drift)', async ({ page }) => { const headersByUrl: Record> = {} await page.route('**/api/detect/**', async (route) => { const req = route.request() headersByUrl[req.url()] = req.headers() await route.continue() }) await page.goto('/annotations') const detectBtn = page.getByRole('button', { name: /AI Detect/i }).first() if (!(await detectBtn.isVisible({ timeout: 5000 }).catch(() => false))) { test.skip(true, 'Suite seed has no media for detect') } if (await detectBtn.isDisabled().catch(() => true)) { const firstMedia = page.locator('div.cursor-pointer').first() await firstMedia.click({ timeout: 5000 }).catch(() => {}) } await detectBtn.click({ timeout: 5000 }).catch(() => {}) await page.waitForTimeout(1000) const urls = Object.keys(headersByUrl) expect(urls.length).toBeGreaterThan(0) for (const u of urls) { const h = headersByUrl[u] expect(h).toHaveProperty('x-refresh-token') expect(h['x-refresh-token']).not.toBe('') } }) })