Skip to content

Commit 95fd19a

Browse files
committed
assert rows with an object instead of an array
1 parent ffff5a4 commit 95fd19a

File tree

5 files changed

+64
-58
lines changed

5 files changed

+64
-58
lines changed

app/pages/__tests__/instance/networking.e2e.ts

+5-19
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,9 @@ test('Instance networking tab', async ({ page }) => {
99

1010
// Instance networking tab
1111
await page.click('role=tab[name="Networking"]')
12-
await expectRowVisible(page, 'my-nic', [
13-
'my-nic',
14-
'a network interface',
15-
'172.30.0.10',
16-
'123.4.56.7',
17-
'mock-vpc',
18-
'mock-subnet',
19-
'primary',
20-
])
12+
13+
const table = page.locator('table')
14+
await expectRowVisible(table, { name: 'my-nic', primary: 'primary' })
2115

2216
// check VPC link in table points to the right page
2317
await expect(page.locator('role=cell >> role=link[name="mock-vpc"]')).toHaveAttribute(
@@ -55,16 +49,8 @@ test('Instance networking tab', async ({ page }) => {
5549
.locator('role=button[name="Row actions"]')
5650
.click()
5751
await page.click('role=menuitem[name="Make primary"]')
58-
await expectRowVisible(page, 'my-nic', [
59-
'my-nic',
60-
'a network interface',
61-
'172.30.0.10',
62-
'—',
63-
'mock-vpc',
64-
'mock-subnet',
65-
'',
66-
])
67-
await expectRowVisible(page, 'nic-2', ['nic-2', null, null, null, null, null, 'primary'])
52+
await expectRowVisible(table, { name: 'my-nic', primary: '' })
53+
await expectRowVisible(table, { name: 'nic-2', primary: 'primary' })
6854

6955
// Make an edit to the network interface
7056
await page

app/pages/__tests__/org-access.e2e.ts

+10-8
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import { expectNotVisible, expectRowVisible, expectVisible } from 'app/util/e2e'
55
test('Click through org access page', async ({ page }) => {
66
await page.goto('/orgs/maze-war')
77

8-
// page is there, we see AL but not FDR
8+
const table = page.locator('role=table')
9+
10+
// page is there, we see user 1 but not 2
911
await page.click('role=link[name*="Access & IAM"]')
1012
await expectVisible(page, ['role=heading[name*="Access & IAM"]'])
11-
await expectRowVisible(page, 'user-1', ['user-1', 'Hannah Arendt', 'admin'])
13+
await expectRowVisible(table, { ID: 'user-1', Name: 'Hannah Arendt', Role: 'admin' })
1214
await expectNotVisible(page, ['role=cell[name="user-2"]'])
1315

14-
// Add FDR as collab
16+
// Add user 2 as collab
1517
await page.click('role=button[name="Add user to organization"]')
1618
await expectVisible(page, ['role=heading[name*="Add user to organization"]'])
1719

@@ -32,10 +34,10 @@ test('Click through org access page', async ({ page }) => {
3234
await page.click('role=option[name="Collaborator"]')
3335
await page.click('role=button[name="Add user"]')
3436

35-
// FDR shows up in the table
36-
await expectRowVisible(page, 'user-2', ['user-2', 'Hans Jonas', 'collaborator'])
37+
// User 2 shows up in the table
38+
await expectRowVisible(table, { ID: 'user-2', Name: 'Hans Jonas', Role: 'collaborator' })
3739

38-
// now change FDR's role from collab to viewer
40+
// now change user 2's role from collab to viewer
3941
await page
4042
.locator('role=row', { hasText: 'user-2' })
4143
.locator('role=button[name="Row actions"]')
@@ -49,9 +51,9 @@ test('Click through org access page', async ({ page }) => {
4951
await page.click('role=option[name="Viewer"]')
5052
await page.click('role=button[name="Update role"]')
5153

52-
await expectRowVisible(page, 'user-2', ['user-2', 'Hans Jonas', 'viewer'])
54+
await expectRowVisible(table, { ID: 'user-2', Role: 'viewer' })
5355

54-
// now delete FDR
56+
// now delete user 2
5557
await page
5658
.locator('role=row', { hasText: 'user-2' })
5759
.locator('role=button[name="Row actions"]')

app/pages/__tests__/project-access.e2e.ts

+10-9
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import { expectNotVisible, expectRowVisible, expectVisible } from 'app/util/e2e'
44

55
test('Click through project access page', async ({ page }) => {
66
await page.goto('/orgs/maze-war/projects/mock-project')
7-
8-
// page is there, we see AL but not FDR
97
await page.click('role=link[name*="Access & IAM"]')
8+
9+
// page is there, we see user 1 but not 2
1010
await expectVisible(page, ['role=heading[name*="Access & IAM"]'])
11-
await expectRowVisible(page, 'user-1', ['user-1', 'Hannah Arendt', 'admin'])
11+
const table = page.locator('table')
12+
await expectRowVisible(table, { ID: 'user-1', Name: 'Hannah Arendt', Role: 'admin' })
1213
await expectNotVisible(page, ['role=cell[name="user-2"]'])
1314

14-
// Add FDR as collab
15+
// Add user 2 as collab
1516
await page.click('role=button[name="Add user to project"]')
1617
await expectVisible(page, ['role=heading[name*="Add user to project"]'])
1718

@@ -32,10 +33,10 @@ test('Click through project access page', async ({ page }) => {
3233
await page.click('role=option[name="Collaborator"]')
3334
await page.click('role=button[name="Add user"]')
3435

35-
// FDR shows up in the table
36-
await expectRowVisible(page, 'user-2', ['user-2', 'Hans Jonas', 'collaborator'])
36+
// User 2 shows up in the table
37+
await expectRowVisible(table, { ID: 'user-2', Name: 'Hans Jonas', Role: 'collaborator' })
3738

38-
// now change FDR's role from collab to viewer
39+
// now change user 2 role from collab to viewer
3940
await page
4041
.locator('role=row', { hasText: 'user-2' })
4142
.locator('role=button[name="Row actions"]')
@@ -49,9 +50,9 @@ test('Click through project access page', async ({ page }) => {
4950
await page.click('role=option[name="Viewer"]')
5051
await page.click('role=button[name="Update role"]')
5152

52-
await expectRowVisible(page, 'user-2', ['user-2', 'Hans Jonas', 'viewer'])
53+
await expectRowVisible(table, { ID: 'user-2', Role: 'viewer' })
5354

54-
// now delete FDR
55+
// now delete user 2
5556
await page
5657
.locator('role=row', { hasText: 'user-2' })
5758
.locator('role=button[name="Row actions"]')

app/pages/__tests__/ssh-keys.e2e.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ test('SSH keys', async ({ page }) => {
1818

1919
// it's there in the table
2020
await expectNotVisible(page, ['text="No SSH keys"'])
21-
await expectRowVisible(page, 'my-key', ['my-key', 'definitely a key'])
21+
const table = page.locator('role=table')
22+
await expectRowVisible(table, { Name: 'my-key', Description: 'definitely a key' })
2223

2324
// now delete it
2425
await page.click('role=button[name="Row actions"]')

app/util/e2e.ts

+37-21
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
import type { Locator, Page } from '@playwright/test'
22
import { expect } from '@playwright/test'
33

4-
export async function forEach(loc: Locator, fn: (loc0: Locator) => void) {
4+
export async function forEach(loc: Locator, fn: (loc0: Locator, i: number) => void) {
55
const count = await loc.count()
66
for (let i = 0; i < count; i++) {
7-
await fn(loc.nth(i))
7+
await fn(loc.nth(i), i)
88
}
99
}
1010

11+
export async function map<T>(
12+
loc: Locator,
13+
fn: (loc0: Locator, i: number) => Promise<T>
14+
): Promise<T[]> {
15+
const result: T[] = []
16+
await forEach(loc, async (loc0, i) => {
17+
result.push(await fn(loc0, i))
18+
})
19+
return result
20+
}
21+
1122
export async function expectVisible(page: Page, selectors: string[]) {
1223
for (const selector of selectors) {
1324
await expect(page.locator(selector)).toBeVisible()
@@ -21,26 +32,31 @@ export async function expectNotVisible(page: Page, selectors: string[]) {
2132
}
2233

2334
/**
24-
* Assert about the values of a row, identified by `rowSelectorText`. It doesn't
25-
* need to be the entire row; the test will pass as long as the identified row
26-
* exists and the first N cells match the N values in `cellTexts`. Pass `''` for
27-
* a checkbox cell.
28-
*
29-
* @param rowSelectorText Text that should uniquely identify the row, like an ID
30-
* @param cellTexts Text to match in each cell of that row
35+
* Assert that a row matching `expectedRow` is present in `table`. The match
36+
* uses `objectContaining`, so `expectedRow` does not need to contain every
37+
* cell. Works by converting `table` to a list of objects where the keys are
38+
* header cell text and the values are row cell text.
3139
*/
3240
export async function expectRowVisible(
33-
page: Page,
34-
rowSelectorText: string,
35-
cellTexts: Array<string | null>
41+
table: Locator,
42+
expectedRow: Record<string, string>
3643
) {
37-
const row = page.locator(`tr:has-text("${rowSelectorText}")`)
38-
await expect(row).toBeVisible()
39-
for (let i = 0; i < cellTexts.length; i++) {
40-
const text = cellTexts[i]
41-
if (text === null) {
42-
continue
43-
}
44-
await expect(row.locator(`role=cell >> nth=${i}`)).toHaveText(text)
45-
}
44+
await table.waitFor() // sometimes the table is re-rendering and the tests flake
45+
46+
const headerKeys = await map(
47+
table.locator('thead >> role=cell'),
48+
async (cell) => await cell.textContent()
49+
)
50+
51+
const rows = await map(table.locator('tbody >> role=row'), async (row) => {
52+
const rowPairs = await map(row.locator('role=cell'), async (cell, i) => [
53+
headerKeys[i],
54+
// accessible name would be better but it's not in yet
55+
// https://github.com/microsoft/playwright/issues/13517
56+
await cell.textContent(),
57+
])
58+
return Object.fromEntries(rowPairs.filter(([k]) => k && k.length > 0))
59+
})
60+
61+
await expect(rows).toEqual(expect.arrayContaining([expect.objectContaining(expectedRow)]))
4662
}

0 commit comments

Comments
 (0)