mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 13:21:11 +00:00
6d03643c2c
ci/woodpecker/push/build-arm Pipeline was successful
- AZ-461 sync image detect URL canary (FT-P-11) PASS;
async-video QUARANTINE (FT-P-12) + X-Refresh-Token drift
(FT-P-13) recorded as it.fails() with controls.
- AZ-464 bulk-validate URL + UI sync (≤2 s) PASS;
body shape drift {annotationIds,status} vs contract
{ids,targetStatus:30} captured as it.fails().
- AZ-470 panel-width debounce + rehydration: entire task
is Phase-B target (useResizablePanel has no PUT writer
/ no rehydration); 3 ACs as it.fails() with controls.
- AZ-472 DetectionClasses load + click + fallback PASS;
hotkey arithmetic P=0 PASS, P=20/P=40 it.fails() for
classes[idx+P]-against-dense-array drift.
Code review: PASS (0 findings). Fast: 18/18 files,
102 passed / 13 skipped. Static: 21/21 PASS.
Co-authored-by: Cursor <cursoragent@cursor.com>
86 lines
3.4 KiB
TypeScript
86 lines
3.4 KiB
TypeScript
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<string, Record<string, string>> = {}
|
|
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('')
|
|
}
|
|
})
|
|
})
|