mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 19:11:11 +00:00
f2451944fd
- AZ-474 tile-split + YOLO parser + auto-zoom + indicator + malformed (FT-P-51..55, FT-N-10): 13 fast (6 it.fails for AC-1..6 + 7 controls) + 2 e2e (test.fail for FT-P-51 + FT-P-53). The split surface is QUARANTINED today (D11) — no Split-tile button, no parser, no <TileViewer>; all 6 ACs are documented drift, every it.fails paired with a control PASS pinning current behaviour. - AZ-480 prod image + nginx routing + RAM (NFT-RES-LIM-02 /03/08/09/10): 4 new static checks promoted into the per-commit profile (STC-RES02 500M cap, STC-RES03 Dockerfile final-stage nginx:alpine no Node, STC-RES09 exactly 9 /api/* location blocks, STC-RES10 prefix-strip on every route). 3 e2e (docker-no-Node probe, runtime prefix-strip, long-running RAM soak — all gated on docker availability + image build; RAM soak also on RUN_LONG_RUNNING=1). Phase A — One-time baseline setup is now COMPLETE. The todo/ directory is empty after this batch's archival. Cumulative review for batches 07-08 is the next autodev action; after that, Step 7 (Run Tests) auto-chains. Code review: PASS (0 findings). Fast: 26/26 files, 163 passed / 13 skipped. Static: 29/29 PASS (incl. 4 new STC-RES* gates). Co-authored-by: Cursor <cursoragent@cursor.com>
118 lines
4.5 KiB
TypeScript
118 lines
4.5 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
|
|
// AZ-474 — e2e companion for FT-P-51 (tile-split endpoint contract) and
|
|
// FT-P-53 (DatasetItem.isSplit honored).
|
|
//
|
|
// Per the task spec, only FT-P-51 and FT-P-53 are `fast + e2e`. The other
|
|
// rows (FT-P-52 parser, FT-P-54 auto-zoom, FT-P-55 indicator, FT-N-10
|
|
// malformed) are fast-only. Both e2e tests are `test.fail()` today
|
|
// because the split surface is QUARANTINED in production (per
|
|
// `_docs/04_refactoring/01-testability-refactoring/deferred_to_refactor.md`
|
|
// row D11 and the traceability matrix's `[Q]` marker on AC-39).
|
|
//
|
|
// Once the SPA wires a "Split tile" affordance and starts honoring
|
|
// `DatasetItem.isSplit`, remove the `test.fail` and these flip green.
|
|
|
|
test.describe('AZ-474 — tile-split surface (e2e companion)', () => {
|
|
test.fail(
|
|
'FT-P-51 — clicking Split tile POSTs /api/annotations/dataset/<id>/split',
|
|
async ({ page }) => {
|
|
test.setTimeout(20_000)
|
|
|
|
const splitPosts: string[] = []
|
|
await page.route('**/api/annotations/dataset/*/split', async (route) => {
|
|
if (route.request().method() === 'POST') {
|
|
splitPosts.push(route.request().url())
|
|
}
|
|
await route.continue()
|
|
})
|
|
|
|
await page.goto('/dataset')
|
|
|
|
// Suite seed must include at least one dataset item — if not, mark
|
|
// the gap explicitly. The seed today produces images via the
|
|
// annotations service; if it doesn't, the test reports the seed gap
|
|
// and skips rather than hiding the contract.
|
|
const firstCard = page.locator('img').first()
|
|
if (!(await firstCard.isVisible({ timeout: 5000 }).catch(() => false))) {
|
|
test.skip(true, 'Suite seed has no dataset items')
|
|
}
|
|
|
|
await firstCard.hover()
|
|
// Drift today: no Split-tile button is rendered. The locator below
|
|
// is intentionally tolerant of any reasonable button shape so that
|
|
// when the affordance lands, the test does not need surgery.
|
|
const splitBtn = page.getByRole('button', { name: /split/i })
|
|
await expect(splitBtn.first()).toBeVisible({ timeout: 1500 })
|
|
await splitBtn.first().click()
|
|
|
|
await expect.poll(() => splitPosts.length, { timeout: 2000 }).toBeGreaterThan(0)
|
|
expect(splitPosts[0]).toMatch(/\/api\/annotations\/dataset\/[^/]+\/split$/)
|
|
},
|
|
)
|
|
|
|
test.fail(
|
|
'FT-P-53 — items with isSplit:true render a distinct affordance vs non-split',
|
|
async ({ page }) => {
|
|
test.setTimeout(15_000)
|
|
|
|
// Stub the dataset response so the test is independent of seed
|
|
// shape — what matters is the renderer's behaviour given the
|
|
// contract, not which rows happen to live in the suite seed.
|
|
await page.route('**/api/annotations/dataset*', async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
items: [
|
|
{
|
|
annotationId: 'ann-split',
|
|
imageName: 'split.jpg',
|
|
thumbnailPath: '/thumbs/split.jpg',
|
|
status: 20,
|
|
createdDate: '2026-05-11T10:00:00Z',
|
|
createdEmail: 'op_alice@test.local',
|
|
flightName: 'Flight 1',
|
|
source: 0,
|
|
isSeed: false,
|
|
isSplit: true,
|
|
},
|
|
{
|
|
annotationId: 'ann-nosplit',
|
|
imageName: 'nosplit.jpg',
|
|
thumbnailPath: '/thumbs/nosplit.jpg',
|
|
status: 10,
|
|
createdDate: '2026-05-11T10:01:00Z',
|
|
createdEmail: 'op_alice@test.local',
|
|
flightName: 'Flight 1',
|
|
source: 1,
|
|
isSeed: false,
|
|
isSplit: false,
|
|
},
|
|
],
|
|
totalCount: 2,
|
|
}),
|
|
})
|
|
})
|
|
|
|
await page.goto('/dataset')
|
|
await expect(page.locator('img').first()).toBeVisible({ timeout: 5_000 })
|
|
|
|
// Spec: the rendered card for an isSplit annotation MUST carry a
|
|
// visible affordance the non-split card does NOT carry.
|
|
const splitCard = page.locator('img[alt*="split"]').first()
|
|
const nonSplitCard = page.locator('img[alt*="nosplit"]').first()
|
|
|
|
const splitData = await splitCard.evaluate((n) =>
|
|
(n.closest('div') as HTMLElement | null)?.getAttribute('data-is-split'),
|
|
)
|
|
const nonSplitData = await nonSplitCard.evaluate((n) =>
|
|
(n.closest('div') as HTMLElement | null)?.getAttribute('data-is-split'),
|
|
)
|
|
|
|
expect(splitData).toBe('true')
|
|
expect(nonSplitData).not.toBe('true')
|
|
},
|
|
)
|
|
})
|