Skip to content

Commit 1e8500c

Browse files
author
GanJiaKouN16
committed
test: add e2e tests for annotation queue flows
Add Playwright acceptance tests for the annotation queue feature, following the existing human-annotation test patterns. Tests cover (WEB-ACC-ANNOTATION-001 through 006): 1. Navigate to annotation queues page and verify empty state or list 2. Create a new annotation queue with testcases kind 3. Create a new annotation queue with traces kind 4. Open a queue detail page by clicking a queue row 5. Search for annotation queues by name 6. Delete an annotation queue via the actions menu Structure mirrors human-annotation/: - annotation-queue.spec.ts — entry point - index.ts — test definitions with tags - tests.ts — fixtures (navigateToAnnotations, createAnnotationQueue) - assets/types.ts — type definitions Closes #4528
1 parent 6821d0f commit 1e8500c

4 files changed

Lines changed: 312 additions & 0 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import {test} from "@agenta/web-tests/tests/fixtures/base.fixture"
2+
import annotationQueueTests from "."
3+
4+
test.describe("Annotation Queue", annotationQueueTests)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type {BaseFixture} from "@agenta/web-tests/tests/fixtures/base.fixture/types"
2+
3+
export interface AnnotationQueueFixtures extends BaseFixture {
4+
navigateToAnnotations: () => Promise<void>
5+
createAnnotationQueue: (config: {name: string; kind: "traces" | "testcases"}) => Promise<void>
6+
}
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import {
2+
createTagString,
3+
TestCoverage,
4+
TestPath,
5+
TestSpeedType,
6+
TestScope,
7+
} from "@agenta/web-tests/playwright/config/testTags"
8+
import {getProjectScopedBasePath} from "@agenta/web-tests/tests/fixtures/base.fixture/apiHelpers"
9+
10+
import {expect, test as baseTest} from "./tests"
11+
12+
const QUEUE_NAME_PREFIX = "e2e-annotation-queue"
13+
14+
const annotationQueueTests = () => {
15+
// WEB-ACC-ANNOTATION-001
16+
baseTest(
17+
"should navigate to annotation queues page and see the queue list or empty state",
18+
{
19+
tag: [
20+
createTagString("scope", TestScope.EVALUATIONS),
21+
createTagString("coverage", TestCoverage.SMOKE),
22+
createTagString("coverage", TestCoverage.LIGHT),
23+
createTagString("coverage", TestCoverage.FULL),
24+
createTagString("path", TestPath.HAPPY),
25+
createTagString("license", "oss"),
26+
],
27+
},
28+
async ({navigateToAnnotations, page}) => {
29+
await navigateToAnnotations()
30+
31+
// The page should render either the empty state or the queue table
32+
const hasEmptyState = await page
33+
.getByText("Create your first annotation queue")
34+
.isVisible()
35+
.catch(() => false)
36+
const hasTableRows = (await page.locator("[data-row-key]").count()) > 0
37+
expect(hasEmptyState || hasTableRows).toBe(true)
38+
39+
// The "New Queue" button should always be visible
40+
await expect(page.getByRole("button", {name: "New Queue"})).toBeVisible()
41+
},
42+
)
43+
44+
// WEB-ACC-ANNOTATION-002
45+
baseTest(
46+
"should create a new annotation queue with testcases kind",
47+
{
48+
tag: [
49+
createTagString("scope", TestScope.EVALUATIONS),
50+
createTagString("coverage", TestCoverage.SMOKE),
51+
createTagString("coverage", TestCoverage.LIGHT),
52+
createTagString("coverage", TestCoverage.FULL),
53+
createTagString("path", TestPath.HAPPY),
54+
createTagString("license", "oss"),
55+
],
56+
},
57+
async ({navigateToAnnotations, createAnnotationQueue, page}) => {
58+
await navigateToAnnotations()
59+
60+
const queueName = `${QUEUE_NAME_PREFIX}-testcases-${Date.now()}`
61+
await createAnnotationQueue({name: queueName, kind: "testcases"})
62+
63+
// After creation, the queue should appear in the list
64+
await expect(page.getByText(queueName).first()).toBeVisible({timeout: 15000})
65+
},
66+
)
67+
68+
// WEB-ACC-ANNOTATION-003
69+
baseTest(
70+
"should create a new annotation queue with traces kind",
71+
{
72+
tag: [
73+
createTagString("scope", TestScope.EVALUATIONS),
74+
createTagString("coverage", TestCoverage.SMOKE),
75+
createTagString("coverage", TestCoverage.LIGHT),
76+
createTagString("coverage", TestCoverage.FULL),
77+
createTagString("path", TestPath.HAPPY),
78+
createTagString("license", "oss"),
79+
],
80+
},
81+
async ({navigateToAnnotations, createAnnotationQueue, page}) => {
82+
await navigateToAnnotations()
83+
84+
const queueName = `${QUEUE_NAME_PREFIX}-traces-${Date.now()}`
85+
await createAnnotationQueue({name: queueName, kind: "traces"})
86+
87+
// After creation, the queue should appear in the list
88+
await expect(page.getByText(queueName).first()).toBeVisible({timeout: 15000})
89+
},
90+
)
91+
92+
// WEB-ACC-ANNOTATION-004
93+
baseTest(
94+
"should open a queue detail page when clicking on a queue row",
95+
{
96+
tag: [
97+
createTagString("scope", TestScope.EVALUATIONS),
98+
createTagString("coverage", TestCoverage.LIGHT),
99+
createTagString("coverage", TestCoverage.FULL),
100+
createTagString("path", TestPath.HAPPY),
101+
createTagString("license", "oss"),
102+
createTagString("speed", TestSpeedType.SLOW),
103+
],
104+
},
105+
async ({navigateToAnnotations, createAnnotationQueue, page}, testInfo) => {
106+
testInfo.setTimeout(120000)
107+
108+
await navigateToAnnotations()
109+
110+
const queueName = `${QUEUE_NAME_PREFIX}-detail-${Date.now()}`
111+
await createAnnotationQueue({name: queueName, kind: "testcases"})
112+
113+
// Click on the queue row to navigate to detail page
114+
const queueRow = page.locator("[data-row-key]").filter({hasText: queueName}).first()
115+
await expect(queueRow).toBeVisible({timeout: 15000})
116+
await queueRow.click()
117+
118+
// Should navigate to the queue detail page
119+
await expect
120+
.poll(() => new URL(page.url()).pathname, {timeout: 15000})
121+
.toContain(`${getProjectScopedBasePath(page)}/annotations/`)
122+
123+
// The queue name should be visible on the detail page
124+
await expect(page.getByText(queueName).first()).toBeVisible({timeout: 10000})
125+
},
126+
)
127+
128+
// WEB-ACC-ANNOTATION-005
129+
baseTest(
130+
"should search for annotation queues by name",
131+
{
132+
tag: [
133+
createTagString("scope", TestScope.EVALUATIONS),
134+
createTagString("coverage", TestCoverage.LIGHT),
135+
createTagString("coverage", TestCoverage.FULL),
136+
createTagString("path", TestPath.HAPPY),
137+
createTagString("license", "oss"),
138+
createTagString("speed", TestSpeedType.SLOW),
139+
],
140+
},
141+
async ({navigateToAnnotations, createAnnotationQueue, page}, testInfo) => {
142+
testInfo.setTimeout(120000)
143+
144+
await navigateToAnnotations()
145+
146+
// Create a uniquely named queue so search is deterministic
147+
const uniqueSuffix = Date.now()
148+
const queueName = `${QUEUE_NAME_PREFIX}-search-${uniqueSuffix}`
149+
await createAnnotationQueue({name: queueName, kind: "testcases"})
150+
151+
// Use the search input to filter
152+
const searchInput = page.locator('input[placeholder="Search queues"]').first()
153+
await expect(searchInput).toBeVisible({timeout: 10000})
154+
await searchInput.click()
155+
await searchInput.fill("")
156+
await searchInput.pressSequentially(queueName, {delay: 30})
157+
158+
// The matching queue should be visible
159+
await expect(page.getByText(queueName).first()).toBeVisible({timeout: 15000})
160+
161+
// Non-matching queues should be filtered out (if any other queues exist)
162+
const allRows = page.locator("[data-row-key]")
163+
const rowCount = await allRows.count()
164+
if (rowCount > 0) {
165+
// All visible rows should contain the search term
166+
for (let i = 0; i < rowCount; i++) {
167+
await expect(allRows.nth(i)).toContainText(queueName)
168+
}
169+
}
170+
},
171+
)
172+
173+
// WEB-ACC-ANNOTATION-006
174+
baseTest(
175+
"should delete an annotation queue via the actions menu",
176+
{
177+
tag: [
178+
createTagString("scope", TestScope.EVALUATIONS),
179+
createTagString("coverage", TestCoverage.LIGHT),
180+
createTagString("coverage", TestCoverage.FULL),
181+
createTagString("path", TestPath.HAPPY),
182+
createTagString("license", "oss"),
183+
createTagString("speed", TestSpeedType.SLOW),
184+
],
185+
},
186+
async ({navigateToAnnotations, createAnnotationQueue, page}, testInfo) => {
187+
testInfo.setTimeout(120000)
188+
189+
await navigateToAnnotations()
190+
191+
const queueName = `${QUEUE_NAME_PREFIX}-delete-${Date.now()}`
192+
await createAnnotationQueue({name: queueName, kind: "testcases"})
193+
194+
// Find the queue row
195+
const queueRow = page.locator("[data-row-key]").filter({hasText: queueName}).first()
196+
await expect(queueRow).toBeVisible({timeout: 15000})
197+
198+
// Click the actions (gear/more) button in the row
199+
const actionsButton = queueRow.locator("button").last()
200+
await actionsButton.click()
201+
202+
// Click Delete from the dropdown menu
203+
const deleteMenuItem = page.getByText("Delete", {exact: true}).last()
204+
await expect(deleteMenuItem).toBeVisible({timeout: 5000})
205+
await deleteMenuItem.click()
206+
207+
// Confirm the deletion in the modal/alert
208+
const confirmButton = page.getByRole("button", {name: /Delete|Confirm|OK/i}).last()
209+
if (await confirmButton.isVisible().catch(() => false)) {
210+
await confirmButton.click()
211+
}
212+
213+
// The queue should no longer appear in the list
214+
await expect(page.getByText(queueName)).toBeHidden({timeout: 15000})
215+
},
216+
)
217+
}
218+
219+
export default annotationQueueTests
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import {test as baseTest} from "@agenta/web-tests/tests/fixtures/base.fixture"
2+
import {getProjectScopedBasePath} from "@agenta/web-tests/tests/fixtures/base.fixture/apiHelpers"
3+
import {expect} from "@agenta/web-tests/utils"
4+
import type {Page} from "@playwright/test"
5+
6+
import type {AnnotationQueueFixtures} from "./assets/types"
7+
8+
const getAnnotationsPath = (page: Page) =>
9+
`${getProjectScopedBasePath(page)}/annotations`
10+
11+
const waitForQueueListLoad = async (page: Page) => {
12+
// Wait for the annotations page to finish loading — either the table
13+
// renders rows or the empty state appears.
14+
await expect
15+
.poll(
16+
async () => {
17+
const hasTable = (await page.locator("[data-row-key]").count()) > 0
18+
const hasEmptyState = await page
19+
.getByText("Create your first annotation queue")
20+
.isVisible()
21+
.catch(() => false)
22+
const hasNewQueueButton = await page
23+
.getByRole("button", {name: "New Queue"})
24+
.isVisible()
25+
.catch(() => false)
26+
return hasTable || hasEmptyState || hasNewQueueButton
27+
},
28+
{timeout: 30000},
29+
)
30+
.toBe(true)
31+
}
32+
33+
const testWithAnnotationFixtures = baseTest.extend<AnnotationQueueFixtures>({
34+
navigateToAnnotations: async ({page}, use) => {
35+
await use(async () => {
36+
const annotationsPath = getAnnotationsPath(page)
37+
await page.goto(annotationsPath, {waitUntil: "domcontentloaded"})
38+
await expect.poll(() => new URL(page.url()).pathname).toBe(annotationsPath)
39+
await waitForQueueListLoad(page)
40+
})
41+
},
42+
43+
createAnnotationQueue: async ({page}, use) => {
44+
await use(async ({name, kind}) => {
45+
// Click the "New Queue" button
46+
const newQueueButton = page.getByRole("button", {name: "New Queue"})
47+
await expect(newQueueButton).toBeVisible({timeout: 10000})
48+
await newQueueButton.click()
49+
50+
// Wait for the drawer to open
51+
const drawer = page.locator(".ant-drawer-content-wrapper").last()
52+
await expect(drawer).toBeVisible({timeout: 10000})
53+
await expect(drawer.getByText("Create annotation queue")).toBeVisible()
54+
55+
// Fill in the queue name
56+
const nameInput = drawer.locator('input[placeholder="Enter name"]').first()
57+
await expect(nameInput).toBeVisible()
58+
await nameInput.click()
59+
await nameInput.fill("")
60+
await nameInput.pressSequentially(name, {delay: 20})
61+
await expect(nameInput).toHaveValue(name)
62+
63+
// Select queue type if different from default ("traces")
64+
if (kind === "testcases") {
65+
const kindSelect = drawer.locator(".ant-select").first()
66+
await kindSelect.click()
67+
const testcaseOption = page.getByText("Test cases", {exact: true}).last()
68+
await expect(testcaseOption).toBeVisible()
69+
await testcaseOption.click()
70+
}
71+
72+
// Click Create button
73+
const createButton = drawer.getByRole("button", {name: "Create"}).last()
74+
await expect(createButton).toBeEnabled({timeout: 10000})
75+
await createButton.click()
76+
77+
// Wait for drawer to close (indicating success)
78+
await expect(drawer).toBeHidden({timeout: 30000})
79+
})
80+
},
81+
})
82+
83+
export {testWithAnnotationFixtures as test, expect}

0 commit comments

Comments
 (0)