mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 14:01:10 +00:00
[AZ-461] [AZ-464] [AZ-470] [AZ-472] Batch 5 - detection/bulk-validate/panel-width/classes tests
ci/woodpecker/push/build-arm Pipeline was successful
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>
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
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)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user