1
1
import type { Locator , Page } from '@playwright/test'
2
2
import { expect } from '@playwright/test'
3
3
4
- export async function forEach ( loc : Locator , fn : ( loc0 : Locator ) => void ) {
4
+ export async function forEach ( loc : Locator , fn : ( loc0 : Locator , i : number ) => void ) {
5
5
const count = await loc . count ( )
6
6
for ( let i = 0 ; i < count ; i ++ ) {
7
- await fn ( loc . nth ( i ) )
7
+ await fn ( loc . nth ( i ) , i )
8
8
}
9
9
}
10
10
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
+
11
22
export async function expectVisible ( page : Page , selectors : string [ ] ) {
12
23
for ( const selector of selectors ) {
13
24
await expect ( page . locator ( selector ) ) . toBeVisible ( )
@@ -21,26 +32,36 @@ export async function expectNotVisible(page: Page, selectors: string[]) {
21
32
}
22
33
23
34
/**
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.
31
39
*/
32
40
export async function expectRowVisible (
33
- page : Page ,
34
- rowSelectorText : string ,
35
- cellTexts : Array < string | null >
41
+ table : Locator ,
42
+ expectedRow : Record < string , string >
36
43
) {
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
+ // wait for header and rows to avoid flake town
45
+ const headerLoc = table . locator ( 'thead >> role=cell' )
46
+ await headerLoc . locator ( 'nth=0' ) . waitFor ( ) // nth=0 bc error if there's more than 1
47
+
48
+ const rowLoc = table . locator ( 'tbody >> role=row' )
49
+ await rowLoc . locator ( 'nth=0' ) . waitFor ( )
50
+
51
+ const headerKeys = await map (
52
+ table . locator ( 'thead >> role=cell' ) ,
53
+ async ( cell ) => await cell . textContent ( )
54
+ )
55
+
56
+ const rows = await map ( table . locator ( 'tbody >> role=row' ) , async ( row ) => {
57
+ const rowPairs = await map ( row . locator ( 'role=cell' ) , async ( cell , i ) => [
58
+ headerKeys [ i ] ,
59
+ // accessible name would be better but it's not in yet
60
+ // https://github.com/microsoft/playwright/issues/13517
61
+ await cell . textContent ( ) ,
62
+ ] )
63
+ return Object . fromEntries ( rowPairs . filter ( ( [ k ] ) => k && k . length > 0 ) )
64
+ } )
65
+
66
+ await expect ( rows ) . toEqual ( expect . arrayContaining ( [ expect . objectContaining ( expectedRow ) ] ) )
46
67
}
0 commit comments