Skip to content

Commit

Permalink
assert rows with an object instead of an array
Browse files Browse the repository at this point in the history
  • Loading branch information
david-crespo committed Jul 26, 2022
1 parent ffff5a4 commit 95fd19a
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 58 deletions.
24 changes: 5 additions & 19 deletions app/pages/__tests__/instance/networking.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,9 @@ test('Instance networking tab', async ({ page }) => {

// Instance networking tab
await page.click('role=tab[name="Networking"]')
await expectRowVisible(page, 'my-nic', [
'my-nic',
'a network interface',
'172.30.0.10',
'123.4.56.7',
'mock-vpc',
'mock-subnet',
'primary',
])

const table = page.locator('table')
await expectRowVisible(table, { name: 'my-nic', primary: 'primary' })

This comment has been minimized.

Copy link
@zephraph

zephraph Jul 26, 2022

Contributor

Yep, much better.


// check VPC link in table points to the right page
await expect(page.locator('role=cell >> role=link[name="mock-vpc"]')).toHaveAttribute(
Expand Down Expand Up @@ -55,16 +49,8 @@ test('Instance networking tab', async ({ page }) => {
.locator('role=button[name="Row actions"]')
.click()
await page.click('role=menuitem[name="Make primary"]')
await expectRowVisible(page, 'my-nic', [
'my-nic',
'a network interface',
'172.30.0.10',
'—',
'mock-vpc',
'mock-subnet',
'',
])
await expectRowVisible(page, 'nic-2', ['nic-2', null, null, null, null, null, 'primary'])
await expectRowVisible(table, { name: 'my-nic', primary: '' })
await expectRowVisible(table, { name: 'nic-2', primary: 'primary' })

// Make an edit to the network interface
await page
Expand Down
18 changes: 10 additions & 8 deletions app/pages/__tests__/org-access.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import { expectNotVisible, expectRowVisible, expectVisible } from 'app/util/e2e'
test('Click through org access page', async ({ page }) => {
await page.goto('/orgs/maze-war')

// page is there, we see AL but not FDR
const table = page.locator('role=table')

// page is there, we see user 1 but not 2
await page.click('role=link[name*="Access & IAM"]')
await expectVisible(page, ['role=heading[name*="Access & IAM"]'])
await expectRowVisible(page, 'user-1', ['user-1', 'Hannah Arendt', 'admin'])
await expectRowVisible(table, { ID: 'user-1', Name: 'Hannah Arendt', Role: 'admin' })
await expectNotVisible(page, ['role=cell[name="user-2"]'])

// Add FDR as collab
// Add user 2 as collab
await page.click('role=button[name="Add user to organization"]')
await expectVisible(page, ['role=heading[name*="Add user to organization"]'])

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

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

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

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

// now delete FDR
// now delete user 2
await page
.locator('role=row', { hasText: 'user-2' })
.locator('role=button[name="Row actions"]')
Expand Down
19 changes: 10 additions & 9 deletions app/pages/__tests__/project-access.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { expectNotVisible, expectRowVisible, expectVisible } from 'app/util/e2e'

test('Click through project access page', async ({ page }) => {
await page.goto('/orgs/maze-war/projects/mock-project')

// page is there, we see AL but not FDR
await page.click('role=link[name*="Access & IAM"]')

// page is there, we see user 1 but not 2
await expectVisible(page, ['role=heading[name*="Access & IAM"]'])
await expectRowVisible(page, 'user-1', ['user-1', 'Hannah Arendt', 'admin'])
const table = page.locator('table')
await expectRowVisible(table, { ID: 'user-1', Name: 'Hannah Arendt', Role: 'admin' })
await expectNotVisible(page, ['role=cell[name="user-2"]'])

// Add FDR as collab
// Add user 2 as collab
await page.click('role=button[name="Add user to project"]')
await expectVisible(page, ['role=heading[name*="Add user to project"]'])

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

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

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

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

// now delete FDR
// now delete user 2
await page
.locator('role=row', { hasText: 'user-2' })
.locator('role=button[name="Row actions"]')
Expand Down
3 changes: 2 additions & 1 deletion app/pages/__tests__/ssh-keys.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ test('SSH keys', async ({ page }) => {

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

// now delete it
await page.click('role=button[name="Row actions"]')
Expand Down
58 changes: 37 additions & 21 deletions app/util/e2e.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import type { Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'

export async function forEach(loc: Locator, fn: (loc0: Locator) => void) {
export async function forEach(loc: Locator, fn: (loc0: Locator, i: number) => void) {
const count = await loc.count()
for (let i = 0; i < count; i++) {
await fn(loc.nth(i))
await fn(loc.nth(i), i)
}
}

export async function map<T>(
loc: Locator,
fn: (loc0: Locator, i: number) => Promise<T>
): Promise<T[]> {
const result: T[] = []
await forEach(loc, async (loc0, i) => {
result.push(await fn(loc0, i))
})
return result
}

export async function expectVisible(page: Page, selectors: string[]) {
for (const selector of selectors) {
await expect(page.locator(selector)).toBeVisible()
Expand All @@ -21,26 +32,31 @@ export async function expectNotVisible(page: Page, selectors: string[]) {
}

/**
* Assert about the values of a row, identified by `rowSelectorText`. It doesn't
* need to be the entire row; the test will pass as long as the identified row
* exists and the first N cells match the N values in `cellTexts`. Pass `''` for
* a checkbox cell.
*
* @param rowSelectorText Text that should uniquely identify the row, like an ID
* @param cellTexts Text to match in each cell of that row
* Assert that a row matching `expectedRow` is present in `table`. The match
* uses `objectContaining`, so `expectedRow` does not need to contain every
* cell. Works by converting `table` to a list of objects where the keys are
* header cell text and the values are row cell text.
*/
export async function expectRowVisible(
page: Page,
rowSelectorText: string,
cellTexts: Array<string | null>
table: Locator,
expectedRow: Record<string, string>
) {
const row = page.locator(`tr:has-text("${rowSelectorText}")`)
await expect(row).toBeVisible()
for (let i = 0; i < cellTexts.length; i++) {
const text = cellTexts[i]
if (text === null) {
continue
}
await expect(row.locator(`role=cell >> nth=${i}`)).toHaveText(text)
}
await table.waitFor() // sometimes the table is re-rendering and the tests flake

This comment has been minimized.

Copy link
@zephraph

zephraph Jul 26, 2022

Contributor

This is awesome, ha.


const headerKeys = await map(
table.locator('thead >> role=cell'),
async (cell) => await cell.textContent()
)

const rows = await map(table.locator('tbody >> role=row'), async (row) => {
const rowPairs = await map(row.locator('role=cell'), async (cell, i) => [
headerKeys[i],
// accessible name would be better but it's not in yet
// https://github.com/microsoft/playwright/issues/13517
await cell.textContent(),
])
return Object.fromEntries(rowPairs.filter(([k]) => k && k.length > 0))
})

await expect(rows).toEqual(expect.arrayContaining([expect.objectContaining(expectedRow)]))
}

0 comments on commit 95fd19a

Please sign in to comment.