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>
105 lines
4.1 KiB
TypeScript
105 lines
4.1 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
|
|
// AZ-464 — e2e companion for bulk-validate URL + body + UI sync.
|
|
//
|
|
// AC-1 (FT-P-20 URL): outbound POST URL is `/api/annotations/dataset/bulk-status`.
|
|
// AC-2 (FT-P-20 body): drift today — production sends `{annotationIds, status}`,
|
|
// contract wants `{ids, targetStatus: 30}`. `test.fail()`.
|
|
// AC-3 (FT-P-21): UI rows show `Validated` within 2 s of the 200 response.
|
|
//
|
|
// Requires the suite docker-compose stack with seeded dataset items. The seed
|
|
// must include at least 3 items in Created status so the bulk-validate UI
|
|
// path is exercised end-to-end.
|
|
|
|
test.describe('AZ-464 — bulk-validate (e2e companion)', () => {
|
|
test('AC-1 (FT-P-20) — outbound URL is /api/annotations/dataset/bulk-status', async ({ page }) => {
|
|
const posts: { url: string; body: string | null }[] = []
|
|
await page.route('**/api/annotations/dataset/bulk-status', async (route) => {
|
|
const req = route.request()
|
|
if (req.method() === 'POST') {
|
|
posts.push({ url: req.url(), body: req.postData() })
|
|
}
|
|
await route.continue()
|
|
})
|
|
|
|
await page.goto('/dataset')
|
|
// Suite seed must surface at least 3 selectable rows; otherwise skip.
|
|
const rows = page.locator('div.cursor-pointer')
|
|
const visibleCount = await rows.count().catch(() => 0)
|
|
if (visibleCount < 3) {
|
|
test.skip(true, 'Suite seed has fewer than 3 dataset rows for bulk-validate')
|
|
}
|
|
for (let i = 0; i < 3; i++) {
|
|
await rows.nth(i).click({ modifiers: ['Control'] })
|
|
}
|
|
const validateBtn = page.getByRole('button', { name: /Validate \(\d+\)/i })
|
|
if (!(await validateBtn.isVisible({ timeout: 5000 }).catch(() => false))) {
|
|
test.skip(true, 'Validate button not visible — selection not applied?')
|
|
}
|
|
await validateBtn.click()
|
|
|
|
await page.waitForFunction(() => true, undefined, { timeout: 3000 }).catch(() => null)
|
|
expect(posts.length).toBe(1)
|
|
const path = new URL(posts[0].url).pathname
|
|
expect(path).toBe('/api/annotations/dataset/bulk-status')
|
|
})
|
|
|
|
test.fail('AC-2 (FT-P-20) — body shape `{ids, targetStatus: 30}` (drift)', async ({ page }) => {
|
|
const captured: Record<string, unknown>[] = []
|
|
await page.route('**/api/annotations/dataset/bulk-status', async (route) => {
|
|
const req = route.request()
|
|
if (req.method() === 'POST') {
|
|
const text = req.postData()
|
|
if (text) captured.push(JSON.parse(text))
|
|
}
|
|
await route.continue()
|
|
})
|
|
|
|
await page.goto('/dataset')
|
|
const rows = page.locator('div.cursor-pointer')
|
|
if ((await rows.count().catch(() => 0)) < 3) {
|
|
test.skip(true, 'Seed gap')
|
|
}
|
|
for (let i = 0; i < 3; i++) {
|
|
await rows.nth(i).click({ modifiers: ['Control'] })
|
|
}
|
|
const validateBtn = page.getByRole('button', { name: /Validate \(\d+\)/i })
|
|
await validateBtn.click()
|
|
await page.waitForTimeout(1000)
|
|
expect(captured.length).toBeGreaterThan(0)
|
|
for (const body of captured) {
|
|
expect(body).toHaveProperty('ids')
|
|
expect(body).toHaveProperty('targetStatus', 30)
|
|
}
|
|
})
|
|
|
|
test('AC-3 (FT-P-21) — UI shows Validated badge ≤ 2 000 ms after success', async ({ page }) => {
|
|
await page.goto('/dataset')
|
|
const rows = page.locator('div.cursor-pointer')
|
|
if ((await rows.count().catch(() => 0)) < 3) {
|
|
test.skip(true, 'Seed gap — need 3 rows in Created status')
|
|
}
|
|
for (let i = 0; i < 3; i++) {
|
|
await rows.nth(i).click({ modifiers: ['Control'] })
|
|
}
|
|
const validateBtn = page.getByRole('button', { name: /Validate \(\d+\)/i })
|
|
if (!(await validateBtn.isVisible({ timeout: 5000 }).catch(() => false))) {
|
|
test.skip(true, 'Validate button not visible')
|
|
}
|
|
const t0 = Date.now()
|
|
await validateBtn.click()
|
|
// Wait for at least one row to flip to the Validated badge.
|
|
await page.waitForFunction(
|
|
() => {
|
|
const badges = Array.from(
|
|
document.querySelectorAll('span'),
|
|
).filter((el) => /Validated/i.test(el.textContent ?? ''))
|
|
return badges.length > 0
|
|
},
|
|
undefined,
|
|
{ timeout: 2000 },
|
|
)
|
|
expect(Date.now() - t0).toBeLessThanOrEqual(2000)
|
|
})
|
|
})
|