@cerios/playwright-table 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,21 +1,21 @@
1
- import { Locator } from '@playwright/test';
2
-
1
+ import { Expect, ExpectMatcherState, Locator, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, Selectors, TestType } from "@playwright/test";
3
2
  /**
4
3
  * Enum representing the possible types of content that can be extracted from a table cell.
5
4
  *
6
- * - `TextContent`: The raw text content of the cell, including hidden elements and extra whitespace.
7
- * - `InnerText`: The rendered text content of the cell, as seen by the user, with whitespace collapsed and hidden elements excluded.
5
+ * - `TextContent`: all text in the DOM of the cell, also the text of hidden elements.
6
+ * - `InnerText`: the text as it is shown, without hidden text and with CSS `text-transform`.
7
+ *
8
+ * `TableLocator` normalizes the text either way (whitespace collapsed, icon glyphs and `ignore`d elements
9
+ * left out), unless you ask for raw text. `PlaywrightTable` trims it.
8
10
  */
9
- declare enum CellContentType {
10
- TextContent = "textContent",
11
- InnerText = "innerText"
11
+ export declare enum CellContentType {
12
+ TextContent = "textContent",
13
+ InnerText = "innerText"
12
14
  }
13
-
14
15
  type PollingOptions = {
15
- timeout?: number;
16
- interval?: number;
16
+ timeout?: number;
17
+ interval?: number;
17
18
  };
18
-
19
19
  /**
20
20
  * Represents a single cell value within a table row.
21
21
  *
@@ -23,7 +23,7 @@ type PollingOptions = {
23
23
  * A `Cell` is a string that holds the textual content of a table cell,
24
24
  * whether in the header or body of the table.
25
25
  */
26
- type Cell = string;
26
+ export type Cell = string;
27
27
  /**
28
28
  * Represents a row in the table body, defined as an array of `Cell` objects.
29
29
  *
@@ -32,7 +32,7 @@ type Cell = string;
32
32
  *
33
33
  * @see Cell
34
34
  */
35
- type BodyRow = Cell[];
35
+ export type BodyRow = Cell[];
36
36
  /**
37
37
  * Represents a row in the table header, defined as an array of `Cell` objects.
38
38
  *
@@ -41,8 +41,7 @@ type BodyRow = Cell[];
41
41
  *
42
42
  * @see Cell
43
43
  */
44
- type HeaderRow = Cell[];
45
-
44
+ export type HeaderRow = Cell[];
46
45
  /**
47
46
  * Options for configuring a table header row.
48
47
  *
@@ -52,19 +51,20 @@ type HeaderRow = Cell[];
52
51
  * @property colspan - Configuration for column spanning in the header row.
53
52
  * @property colspan.enabled - If true, enables column spanning.
54
53
  * @property colspan.suffix - If true, appends a suffix to spanned columns.
54
+ * @property normalizeWhitespace - If true, collapses line breaks, tabs and runs of whitespace to one space.
55
+ * @property stripIconGlyphs - If true, removes icon-font glyphs (Unicode private use characters).
55
56
  */
56
57
  type HeaderRowOptions = {
57
- cellContentType?: CellContentType;
58
- emptyCellReplacement?: boolean;
59
- duplicateSuffix?: boolean;
60
- colspan?: {
61
- enabled?: boolean;
62
- suffix?: boolean;
63
- };
64
- normalizeWhitespace?: boolean;
65
- stripIconGlyphs?: boolean;
58
+ cellContentType?: CellContentType;
59
+ emptyCellReplacement?: boolean;
60
+ duplicateSuffix?: boolean;
61
+ colspan?: {
62
+ enabled?: boolean;
63
+ suffix?: boolean;
64
+ };
65
+ normalizeWhitespace?: boolean;
66
+ stripIconGlyphs?: boolean;
66
67
  };
67
-
68
68
  /**
69
69
  * Options for loading table data, headers, and rows.
70
70
  *
@@ -80,407 +80,1903 @@ type HeaderRowOptions = {
80
80
  * };
81
81
  * await table.getBodyRows(options);
82
82
  */
83
- type TableOptions = {
84
- /** See {@link HeaderRowOptions} for available options. */
85
- headerRowOptions?: HeaderRowOptions;
86
- /** Options for parsing body rows. */
87
- bodyRowOptions?: {
88
- cellContentType?: CellContentType;
89
- };
90
- /** @deprecated Use `bodyRowOptions.cellContentType` instead. */
83
+ export type TableOptions = {
84
+ /** See {@link HeaderRowOptions} for available options. */
85
+ headerRowOptions?: HeaderRowOptions;
86
+ /** Options for parsing body rows. */
87
+ bodyRowOptions?: {
91
88
  cellContentType?: CellContentType;
89
+ };
90
+ /** @deprecated Use `bodyRowOptions.cellContentType` instead. */
91
+ cellContentType?: CellContentType;
92
92
  };
93
93
  /**
94
94
  * Represents a table in Playwright, providing methods to interact with its headers, rows, and cells.
95
95
  */
96
- declare class PlaywrightTable {
97
- private readonly _tableLocator;
98
- private readonly _options?;
99
- private _bodyRowLocator;
100
- private _bodyRowColumnSelector;
101
- private _headerRowLocator;
102
- private _headerColumnSelector;
103
- /**
104
- * Creates an instance of the table handler with customizable selectors for headers and rows.
105
- *
106
- * @param _tableLocator - The Playwright `Locator` pointing to the root table element.
107
- * @param _options - Optional configuration for customizing header and row selectors.
108
- * @param _options.header - Options for header row and columns.
109
- * @param _options.header.setMainHeaderRow - (Optional) Index of the main header row if multiple header rows exist.
110
- * @param _options.header.rowSelector - (Optional) CSS selector for locating header rows. Defaults to `"thead>tr"`.
111
- * @param _options.header.columnSelector - (Optional) CSS selector for locating header columns. Defaults to `"th"`.
112
- * @param _options.row - Options for body rows and columns.
113
- * @param _options.row.rowSelector - (Optional) CSS selector for locating body rows. Defaults to `"tbody>tr"`.
114
- * @param _options.row.columnSelector - (Optional) CSS selector for locating body columns. Defaults to `"td"`.
115
- *
116
- * @example
117
- * const table = new PlaywrightTable(
118
- * page.locator('table.my-table'),
119
- * {
120
- * header: {
121
- * rowSelector: 'thead tr.header',
122
- * columnSelector: 'th.header-cell'
123
- * },
124
- * row: {
125
- * rowSelector: 'tbody tr.data-row',
126
- * columnSelector: 'td.data-cell'
127
- * }
128
- * }
129
- * );
130
- */
131
- constructor(_tableLocator: Locator, _options?: {
132
- header?: {
133
- setMainHeaderRow?: number;
134
- rowSelector?: string;
135
- columnSelector?: string;
136
- };
137
- row?: {
138
- rowSelector?: string;
139
- columnSelector?: string;
140
- };
141
- } | undefined);
142
- /**
143
- * Retrieves all header rows of the table.
144
- * @param options - Optional parameters for header row parsing configuration.
145
- * @returns A promise that resolves to an array of header rows.
146
- * @throws Error if no header rows found after table loads.
147
- *
148
- * @example
149
- * // Get headers with colspan enabled
150
- * const headers = await table.getHeaderRows({
151
- * colspan: { enabled: true, suffix: true },
152
- * duplicateSuffix: true
153
- * });
154
- *
155
- * @see {@link getMainHeaderRow} for getting only the main header row
156
- * @see {@link HeaderRowOptions} for all available configuration options
157
- */
158
- getHeaderRows(options?: HeaderRowOptions): Promise<HeaderRow[]>;
159
- /**
160
- * Retrieves the main header row of the table.
161
- * By default, uses the last header row if multiple exist. Can be customized via constructor.
162
- * @param options - Optional parameters for header row parsing configuration.
163
- * @returns A promise that resolves to the main header row.
164
- * @throws Error if no header rows available.
165
- * @throws Error if setMainHeaderRow index is out of bounds (negative or >= header row count).
166
- *
167
- * @example
168
- * // Get main header with custom index set in constructor
169
- * const table = new PlaywrightTable(page.locator('table'), {
170
- * header: { setMainHeaderRow: 0 } // Use first row as main header
171
- * });
172
- * const mainHeader = await table.getMainHeaderRow();
173
- *
174
- * @see {@link getHeaderRows} for getting all header rows
175
- */
176
- getMainHeaderRow(options?: HeaderRowOptions): Promise<HeaderRow>;
177
- /**
178
- * Retrieves all body rows of the table.
179
- * @param options - Optional table or body-row configuration.
180
- * @param options.cellContentType - Legacy shortcut for body row content extraction. See {@link CellContentType}.
181
- * @param options.bodyRowOptions - Body row parsing options.
182
- * @param options.headerRowOptions - Header parsing options used when resolving row/colspan layouts.
183
- * @returns A promise that resolves to an array of body rows.
184
- * @throws Error if no body rows found after table loads.
185
- *
186
- * @example
187
- * // Get body rows with TextContent (includes hidden elements)
188
- * const rows = await table.getBodyRows({
189
- * cellContentType: CellContentType.TextContent
190
- * });
191
- *
192
- * @example
193
- * // Get body rows with default InnerText
194
- * const rows = await table.getBodyRows();
195
- *
196
- * @example
197
- * // Use full table options (for example to sanitize noisy treegrid headers)
198
- * const rows = await table.getBodyRows({
199
- * bodyRowOptions: { cellContentType: CellContentType.InnerText },
200
- * headerRowOptions: { normalizeWhitespace: true, stripIconGlyphs: true }
201
- * });
202
- */
203
- getBodyRows(options?: TableOptions): Promise<BodyRow[]>;
204
- /**
205
- * Retrieves a specific cell locator in the body of the table by row and column index.
206
- * @param rowNumber - The row number (0-based index).
207
- * @param headerPosition - The column/header position (0-based index).
208
- * @returns A Playwright Locator for the specified cell.
209
- *
210
- * @example
211
- * // Get cell at row 1, column 2
212
- * const cellLocator = table.getBodyCellLocator(1, 2);
213
- * await expect(cellLocator).toHaveText('Expected Text');
214
- *
215
- * @example
216
- * // Get first cell in first row
217
- * const firstCell = table.getBodyCellLocator(0, 0);
218
- * await firstCell.click();
219
- *
220
- * @see {@link getBodyCellLocatorByRowConditions} for finding cells by content matching
221
- * @see {@link getAllBodyCellLocatorsByHeaderIndex} for getting all cells in a column
222
- * @see {@link getAllBodyCellLocatorsByHeaderName} for getting all cells under a header
223
- */
224
- getBodyCellLocator(rowNumber: number, headerPosition: number): Locator;
225
- /**
226
- * Retrieves a cell locator in the body of the table based on row conditions and a target header.
227
- * Finds the first row where all conditions match, then returns the cell in the target column.
228
- * @param conditions - An object where keys are header names and values are expected cell contents, or a single [header, value] tuple.
229
- * @param targetHeader - The header name of the column containing the cell to retrieve.
230
- * @param options - Optional table loading options. See {@link TableOptions}.
231
- * @returns A promise that resolves to a Playwright Locator for the matching cell.
232
- * @throws Error if no row matches all specified conditions.
233
- * @throws Error if any condition header or target header is not found in the table.
234
- *
235
- * @example
236
- * // Find cell with multiple conditions
237
- * const emailLocator = await table.getBodyCellLocatorByRowConditions(
238
- * { "First name": "John", "Status": "Active" },
239
- * "Email"
240
- * );
241
- * await expect(emailLocator).toContainText('@example.com');
242
- *
243
- * @example
244
- * // Find cell with single condition using tuple syntax
245
- * const actionCell = await table.getBodyCellLocatorByRowConditions(
246
- * ["Username", "john.doe"],
247
- * "Actions"
248
- * );
249
- * await actionCell.locator('button').click();
250
- *
251
- * @example
252
- * // Find cell with single condition using object syntax
253
- * const statusCell = await table.getBodyCellLocatorByRowConditions(
254
- * { "Username": "john.doe" },
255
- * "Status"
256
- * );
257
- *
258
- * @see {@link getBodyCellLocator} for getting cells by direct index
259
- * @see {@link getAllBodyCellLocatorsByHeaderName} for getting all cells in a column
260
- */
261
- getBodyCellLocatorByRowConditions(conditions: Record<string, string> | [string, string], targetHeader: string, options?: TableOptions): Promise<Locator>;
262
- /**
263
- * Retrieves all cell locators in the body of the table for a specific header name.
264
- * Returns locators for every cell in the column identified by the header.
265
- * @param header - The header name identifying the column.
266
- * @param options - Optional table loading options. See {@link TableOptions}.
267
- * @returns A promise that resolves to an array of Playwright Locators for all cells in the column.
268
- * @throws Error if the header is not found in the table.
269
- *
270
- * @example
271
- * // Get all cells in the "Status" column
272
- * const statusCells = await table.getAllBodyCellLocatorsByHeaderName("Status");
273
- * for (const cell of statusCells) {
274
- * await expect(cell).toHaveText(/Active|Inactive/);
275
- * }
276
- *
277
- * @example
278
- * // Get text from all cells in "Name" column
279
- * const nameCells = await table.getAllBodyCellLocatorsByHeaderName("Name");
280
- * const names = await Promise.all(nameCells.map(cell => cell.textContent()));
281
- *
282
- * @see {@link getAllBodyCellLocatorsByHeaderIndex} for getting cells by column index
283
- * @see {@link getBodyCellLocatorByRowConditions} for finding specific cells by content
284
- */
285
- getAllBodyCellLocatorsByHeaderName(header: string, options?: TableOptions): Promise<Locator[]>;
286
- /**
287
- * Retrieves all cell locators in the body of the table for a specific header index.
288
- * Returns locators for every cell in the column at the specified position.
289
- * @param headerIndex - The header/column index (0-based).
290
- * @param options - Optional table loading options. See {@link TableOptions}.
291
- * @returns A promise that resolves to an array of Playwright Locators for all cells in the column.
292
- * @throws Error if the header index is out of bounds (negative or >= column count).
293
- *
294
- * @example
295
- * // Get all cells in the third column (index 2)
296
- * const thirdColumnCells = await table.getAllBodyCellLocatorsByHeaderIndex(2);
297
- * for (const cell of thirdColumnCells) {
298
- * await expect(cell).toBeVisible();
299
- * }
300
- *
301
- * @example
302
- * // Get all cells in first column
303
- * const firstColumnCells = await table.getAllBodyCellLocatorsByHeaderIndex(0);
304
- * const values = await Promise.all(firstColumnCells.map(c => c.textContent()));
305
- *
306
- * @see {@link getAllBodyCellLocatorsByHeaderName} for getting cells by header name
307
- * @see {@link getBodyCellLocator} for getting a specific cell by row and column index
308
- */
309
- getAllBodyCellLocatorsByHeaderIndex(headerIndex: number, options?: TableOptions): Promise<Locator[]>;
310
- /**
311
- * Converts the table data into a JSON array where each object represents a row.
312
- * Keys are header names, values are cell contents.
313
- * @param options - Optional table loading options. See {@link TableOptions}.
314
- * @returns A promise that resolves to an array of objects representing table rows.
315
- * @throws Error if no header rows or body rows found.
316
- *
317
- * @example
318
- * // Convert table to JSON with default options
319
- * const data = await table.getJson();
320
- * // Result: [{"Name": "John", "Age": "30"}, {"Name": "Jane", "Age": "25"}]
321
- *
322
- * @example
323
- * // Convert with colspan enabled and duplicate suffix
324
- * const data = await table.getJson({
325
- * headerRowOptions: {
326
- * colspan: { enabled: true, suffix: true },
327
- * duplicateSuffix: true,
328
- * emptyCellReplacement: true
329
- * }
330
- * });
331
- */
332
- getJson(options?: TableOptions): Promise<Record<string, string>[]>;
333
- /**
334
- * Waits for the table body to be empty (no body rows).
335
- * @param options - Polling options (timeout, interval, retries).
336
- * @returns A promise that resolves when table body is empty.
337
- * @throws Error if table is not empty within timeout period.
338
- *
339
- * @example
340
- * // Wait for table to clear after delete operation
341
- * await table.waitForEmpty({ timeout: 5000 });
342
- *
343
- * @see {@link waitForNonEmpty} for waiting until table has data
344
- *
345
- * @remarks
346
- * **Alternative:** Consider using Playwright's `expect.poll` for more flexibility:
347
- * ```ts
348
- * await expect.poll(async () => (await table.getBodyRows()).length).toBe(0);
349
- * ```
350
- */
351
- waitForEmpty(options?: PollingOptions): Promise<void>;
352
- /**
353
- * Waits for the table body to be non-empty (at least one body row with content).
354
- * A row is considered valid if it has at least one cell containing text.
355
- * @param options - Polling options (timeout, interval, retries).
356
- * @returns A promise that resolves when table has at least one body row with content.
357
- * @throws Error if table remains empty or has no rows with content within timeout period.
358
- *
359
- * @example
360
- * // Wait for table to populate after search
361
- * await table.waitForNonEmpty({ timeout: 5000 });
362
- *
363
- * @see {@link waitForEmpty} for waiting until table is cleared
364
- *
365
- * @remarks
366
- * **Alternative:** Consider using Playwright's `expect.poll` for more flexibility:
367
- * ```ts
368
- * await expect.poll(async () => (await table.getBodyRows()).length).toBeGreaterThan(0);
369
- * ```
370
- */
371
- waitForNonEmpty(options?: PollingOptions): Promise<void>;
372
- /**
373
- * Waits for the table body to have exactly N rows.
374
- * @param count - The exact number of body rows expected.
375
- * @param options - Polling options (timeout, interval, retries).
376
- * @returns A promise that resolves when the table has exactly the specified number of rows.
377
- * @throws Error if row count doesn't match within timeout period.
378
- *
379
- * @example
380
- * // Wait for exactly 10 rows after pagination
381
- * await table.waitForExactRowCount(10, { timeout: 5000 });
382
- *
383
- * @example
384
- * // Verify table has exactly 0 rows (alternative to waitForEmpty)
385
- * await table.waitForExactRowCount(0);
386
- *
387
- * @see {@link getRowCount} for getting current row counts
388
- *
389
- * @remarks
390
- * **Alternative:** Consider using Playwright's `expect.poll` for more flexibility:
391
- * ```ts
392
- * await expect.poll(async () => (await table.getBodyRows()).length).toBe(10);
393
- * ```
394
- */
395
- waitForExactRowCount(count: number, options?: PollingOptions): Promise<void>;
396
- /**
397
- * Gets the count of header and body rows in the table.
398
- * Lightweight method that doesn't fetch cell data, only counts rows.
399
- * @returns A promise that resolves to an object with header and body row counts.
400
- *
401
- * @example
402
- * // Get current row counts
403
- * const counts = await table.getRowCount();
404
- * console.log(`Headers: ${counts.header}, Body: ${counts.body}`);
405
- *
406
- * @example
407
- * // Assert specific row count
408
- * const { body } = await table.getRowCount();
409
- * expect(body).toBe(10);
410
- */
411
- getRowCount(): Promise<{
412
- header: number;
413
- body: number;
414
- }>;
96
+ export declare class PlaywrightTable {
97
+ private readonly _tableLocator;
98
+ private readonly _options?;
99
+ private _bodyRowLocator;
100
+ private _bodyRowColumnSelector;
101
+ private _headerRowLocator;
102
+ private _headerColumnSelector;
103
+ /**
104
+ * Creates an instance of the table handler with customizable selectors for headers and rows.
105
+ *
106
+ * @param _tableLocator - The Playwright `Locator` pointing to the root table element.
107
+ * @param _options - Optional configuration for customizing header and row selectors.
108
+ * @param _options.header - Options for header row and columns.
109
+ * @param _options.header.setMainHeaderRow - (Optional) Index of the main header row if multiple header rows exist.
110
+ * @param _options.header.rowSelector - (Optional) CSS selector for locating header rows. Defaults to `"thead>tr"`.
111
+ * @param _options.header.columnSelector - (Optional) CSS selector for locating header columns. Defaults to `"th"`.
112
+ * @param _options.row - Options for body rows and columns.
113
+ * @param _options.row.rowSelector - (Optional) CSS selector for locating body rows. Defaults to `"tbody>tr"`.
114
+ * @param _options.row.columnSelector - (Optional) CSS selector for locating body columns. Defaults to `"td"`.
115
+ *
116
+ * @example
117
+ * const table = new PlaywrightTable(
118
+ * page.locator('table.my-table'),
119
+ * {
120
+ * header: {
121
+ * rowSelector: 'thead tr.header',
122
+ * columnSelector: 'th.header-cell'
123
+ * },
124
+ * row: {
125
+ * rowSelector: 'tbody tr.data-row',
126
+ * columnSelector: 'td.data-cell'
127
+ * }
128
+ * }
129
+ * );
130
+ */
131
+ constructor(_tableLocator: Locator, _options?: {
132
+ header?: {
133
+ setMainHeaderRow?: number;
134
+ rowSelector?: string;
135
+ columnSelector?: string;
136
+ };
137
+ row?: {
138
+ rowSelector?: string;
139
+ columnSelector?: string;
140
+ };
141
+ } | undefined);
142
+ /**
143
+ * Retrieves all header rows of the table.
144
+ * @param options - Optional parameters for header row parsing configuration.
145
+ * @returns A promise that resolves to an array of header rows.
146
+ * @throws Error if the table has no header rows or no body rows.
147
+ *
148
+ * @example
149
+ * // Get headers with colspan enabled
150
+ * const headers = await table.getHeaderRows({
151
+ * colspan: { enabled: true, suffix: true },
152
+ * duplicateSuffix: true
153
+ * });
154
+ *
155
+ * @see {@link getMainHeaderRow} for getting only the main header row
156
+ * @see {@link HeaderRowOptions} for all available configuration options
157
+ */
158
+ getHeaderRows(options?: HeaderRowOptions): Promise<HeaderRow[]>;
159
+ /**
160
+ * Retrieves the main header row of the table.
161
+ * By default, uses the last header row if multiple exist. Can be customized via constructor.
162
+ * @param options - Optional parameters for header row parsing configuration.
163
+ * @returns A promise that resolves to the main header row.
164
+ * @throws Error if the table has no header rows or no body rows.
165
+ * @throws Error if setMainHeaderRow index is out of bounds (negative or >= header row count).
166
+ *
167
+ * @example
168
+ * // Get main header with custom index set in constructor
169
+ * const table = new PlaywrightTable(page.locator('table'), {
170
+ * header: { setMainHeaderRow: 0 } // Use first row as main header
171
+ * });
172
+ * const mainHeader = await table.getMainHeaderRow();
173
+ *
174
+ * @see {@link getHeaderRows} for getting all header rows
175
+ */
176
+ getMainHeaderRow(options?: HeaderRowOptions): Promise<HeaderRow>;
177
+ /**
178
+ * Retrieves all body rows of the table.
179
+ * @param options - Optional table or body-row configuration.
180
+ * @param options.cellContentType - Legacy shortcut for body row content extraction. See {@link CellContentType}.
181
+ * @param options.bodyRowOptions - Body row parsing options.
182
+ * @param options.headerRowOptions - Header parsing options used when resolving row/colspan layouts.
183
+ * @returns A promise that resolves to an array of body rows.
184
+ * @throws Error if the table has no header rows or no body rows.
185
+ *
186
+ * @example
187
+ * // Get body rows with TextContent (includes hidden elements)
188
+ * const rows = await table.getBodyRows({
189
+ * cellContentType: CellContentType.TextContent
190
+ * });
191
+ *
192
+ * @example
193
+ * // Get body rows with default InnerText
194
+ * const rows = await table.getBodyRows();
195
+ *
196
+ * @example
197
+ * // Use full table options (for example to sanitize noisy treegrid headers)
198
+ * const rows = await table.getBodyRows({
199
+ * bodyRowOptions: { cellContentType: CellContentType.InnerText },
200
+ * headerRowOptions: { normalizeWhitespace: true, stripIconGlyphs: true }
201
+ * });
202
+ */
203
+ getBodyRows(options?: TableOptions): Promise<BodyRow[]>;
204
+ /**
205
+ * Retrieves a specific cell locator in the body of the table by row and column index.
206
+ * Both are positions in the DOM: `headerPosition` is the index among the cells of the row, so with
207
+ * colspan or rowspan it can differ from the column. The methods that find cells by header name
208
+ * account for spans.
209
+ * @param rowNumber - The row number (0-based index).
210
+ * @param headerPosition - The cell position within the row (0-based index).
211
+ * @returns A Playwright Locator for the specified cell.
212
+ *
213
+ * @example
214
+ * // Get cell at row 1, column 2
215
+ * const cellLocator = table.getBodyCellLocator(1, 2);
216
+ * await expect(cellLocator).toHaveText('Expected Text');
217
+ *
218
+ * @example
219
+ * // Get first cell in first row
220
+ * const firstCell = table.getBodyCellLocator(0, 0);
221
+ * await firstCell.click();
222
+ *
223
+ * @see {@link getBodyCellLocatorByRowConditions} for finding cells by content matching
224
+ * @see {@link getAllBodyCellLocatorsByHeaderIndex} for getting all cells in a column
225
+ * @see {@link getAllBodyCellLocatorsByHeaderName} for getting all cells under a header
226
+ */
227
+ getBodyCellLocator(rowNumber: number, headerPosition: number): Locator;
228
+ /**
229
+ * Retrieves a cell locator in the body of the table based on row conditions and a target header.
230
+ * Finds the first row where all conditions match, then returns the cell in the target column.
231
+ * @param conditions - An object where keys are header names and values are expected cell contents, or a single [header, value] tuple.
232
+ * @param targetHeader - The header name of the column containing the cell to retrieve.
233
+ * @param options - Optional table loading options. See {@link TableOptions}.
234
+ * @returns A promise that resolves to a Playwright Locator for the matching cell.
235
+ * @throws Error if no row matches all specified conditions.
236
+ * @throws Error if any condition header or target header is not found in the table.
237
+ *
238
+ * @example
239
+ * // Find cell with multiple conditions
240
+ * const emailLocator = await table.getBodyCellLocatorByRowConditions(
241
+ * { "First name": "John", "Status": "Active" },
242
+ * "Email"
243
+ * );
244
+ * await expect(emailLocator).toContainText('@example.com');
245
+ *
246
+ * @example
247
+ * // Find cell with single condition using tuple syntax
248
+ * const actionCell = await table.getBodyCellLocatorByRowConditions(
249
+ * ["Username", "john.doe"],
250
+ * "Actions"
251
+ * );
252
+ * await actionCell.locator('button').click();
253
+ *
254
+ * @example
255
+ * // Find cell with single condition using object syntax
256
+ * const statusCell = await table.getBodyCellLocatorByRowConditions(
257
+ * { "Username": "john.doe" },
258
+ * "Status"
259
+ * );
260
+ *
261
+ * @see {@link getBodyCellLocator} for getting cells by direct index
262
+ * @see {@link getAllBodyCellLocatorsByHeaderName} for getting all cells in a column
263
+ */
264
+ getBodyCellLocatorByRowConditions(conditions: Record<string, string> | [string, string], targetHeader: string, options?: TableOptions): Promise<Locator>;
265
+ /**
266
+ * Retrieves all cell locators in the body of the table for a specific header name.
267
+ * Returns locators for every cell in the column identified by the header.
268
+ * @param header - The header name identifying the column.
269
+ * @param options - Optional table loading options. See {@link TableOptions}.
270
+ * @returns A promise that resolves to an array of Playwright Locators for all cells in the column.
271
+ * @throws Error if the header is not found, or if the table has no header rows or no body rows.
272
+ *
273
+ * @example
274
+ * // Get all cells in the "Status" column
275
+ * const statusCells = await table.getAllBodyCellLocatorsByHeaderName("Status");
276
+ * for (const cell of statusCells) {
277
+ * await expect(cell).toHaveText(/Active|Inactive/);
278
+ * }
279
+ *
280
+ * @example
281
+ * // Get text from all cells in "Name" column
282
+ * const nameCells = await table.getAllBodyCellLocatorsByHeaderName("Name");
283
+ * const names = await Promise.all(nameCells.map(cell => cell.textContent()));
284
+ *
285
+ * @see {@link getAllBodyCellLocatorsByHeaderIndex} for getting cells by column index
286
+ * @see {@link getBodyCellLocatorByRowConditions} for finding specific cells by content
287
+ */
288
+ getAllBodyCellLocatorsByHeaderName(header: string, options?: TableOptions): Promise<Locator[]>;
289
+ /**
290
+ * Retrieves all cell locators in the body of the table for a specific header index.
291
+ * Returns locators for every cell in the column at the specified position.
292
+ * @param headerIndex - The header/column index (0-based).
293
+ * @param options - Optional table loading options. See {@link TableOptions}.
294
+ * @returns A promise that resolves to an array of Playwright Locators for all cells in the column.
295
+ * @throws Error if the header index is out of bounds (negative or >= the number of columns, counting colspans).
296
+ *
297
+ * @example
298
+ * // Get all cells in the third column (index 2)
299
+ * const thirdColumnCells = await table.getAllBodyCellLocatorsByHeaderIndex(2);
300
+ * for (const cell of thirdColumnCells) {
301
+ * await expect(cell).toBeVisible();
302
+ * }
303
+ *
304
+ * @example
305
+ * // Get all cells in first column
306
+ * const firstColumnCells = await table.getAllBodyCellLocatorsByHeaderIndex(0);
307
+ * const values = await Promise.all(firstColumnCells.map(c => c.textContent()));
308
+ *
309
+ * @see {@link getAllBodyCellLocatorsByHeaderName} for getting cells by header name
310
+ * @see {@link getBodyCellLocator} for getting a specific cell by row and column index
311
+ */
312
+ getAllBodyCellLocatorsByHeaderIndex(headerIndex: number, options?: TableOptions): Promise<Locator[]>;
313
+ /**
314
+ * Converts the table data into a JSON array where each object represents a row.
315
+ * Keys are header names, values are cell contents.
316
+ * @param options - Optional table loading options. See {@link TableOptions}.
317
+ * @returns A promise that resolves to an array of objects representing table rows.
318
+ * @throws Error if no header rows or body rows found.
319
+ *
320
+ * @example
321
+ * // Convert table to JSON with default options
322
+ * const data = await table.getJson();
323
+ * // Result: [{"Name": "John", "Age": "30"}, {"Name": "Jane", "Age": "25"}]
324
+ *
325
+ * @example
326
+ * // Convert with colspan enabled and duplicate suffix
327
+ * const data = await table.getJson({
328
+ * headerRowOptions: {
329
+ * colspan: { enabled: true, suffix: true },
330
+ * duplicateSuffix: true,
331
+ * emptyCellReplacement: true
332
+ * }
333
+ * });
334
+ */
335
+ getJson(options?: TableOptions): Promise<Record<string, string>[]>;
336
+ /**
337
+ * Waits for the table body to be empty (no body rows).
338
+ * @param options - Polling options (`timeout`, default 30000 ms; `interval`, default 100 ms).
339
+ * @returns A promise that resolves when table body is empty.
340
+ * @throws Error if table is not empty within timeout period.
341
+ *
342
+ * @example
343
+ * // Wait for table to clear after delete operation
344
+ * await table.waitForEmpty({ timeout: 5000 });
345
+ *
346
+ * @see {@link waitForNonEmpty} for waiting until table has data
347
+ *
348
+ * @remarks
349
+ * **Alternative:** Consider using Playwright's `expect.poll` for more flexibility. Count the rows with
350
+ * `getRowCount()`: the methods that read the table throw while the body is empty.
351
+ * ```ts
352
+ * await expect.poll(async () => (await table.getRowCount()).body).toBe(0);
353
+ * ```
354
+ */
355
+ waitForEmpty(options?: PollingOptions): Promise<void>;
356
+ /**
357
+ * Waits for the table body to be non-empty (at least one body row with content).
358
+ * A row is considered valid if it has at least one cell containing text.
359
+ * @param options - Polling options (`timeout`, default 30000 ms; `interval`, default 100 ms).
360
+ * @returns A promise that resolves when table has at least one body row with content.
361
+ * @throws Error if table remains empty or has no rows with content within timeout period.
362
+ *
363
+ * @example
364
+ * // Wait for table to populate after search
365
+ * await table.waitForNonEmpty({ timeout: 5000 });
366
+ *
367
+ * @see {@link waitForEmpty} for waiting until table is cleared
368
+ *
369
+ * @remarks
370
+ * **Alternative:** Consider using Playwright's `expect.poll` for more flexibility:
371
+ * ```ts
372
+ * await expect.poll(async () => (await table.getBodyRows()).length).toBeGreaterThan(0);
373
+ * ```
374
+ */
375
+ waitForNonEmpty(options?: PollingOptions): Promise<void>;
376
+ /**
377
+ * Waits for the table body to have exactly N rows.
378
+ * @param count - The exact number of body rows expected.
379
+ * @param options - Polling options (`timeout`, default 30000 ms; `interval`, default 100 ms).
380
+ * @returns A promise that resolves when the table has exactly the specified number of rows.
381
+ * @throws Error if row count doesn't match within timeout period.
382
+ *
383
+ * @example
384
+ * // Wait for exactly 10 rows after pagination
385
+ * await table.waitForExactRowCount(10, { timeout: 5000 });
386
+ *
387
+ * @example
388
+ * // Verify table has exactly 0 rows (alternative to waitForEmpty)
389
+ * await table.waitForExactRowCount(0);
390
+ *
391
+ * @see {@link getRowCount} for getting current row counts
392
+ *
393
+ * @remarks
394
+ * **Alternative:** Consider using Playwright's `expect.poll` for more flexibility:
395
+ * ```ts
396
+ * await expect.poll(async () => (await table.getBodyRows()).length).toBe(10);
397
+ * ```
398
+ */
399
+ waitForExactRowCount(count: number, options?: PollingOptions): Promise<void>;
400
+ /**
401
+ * Gets the count of header and body rows in the table.
402
+ * Lightweight method that doesn't fetch cell data, only counts rows.
403
+ * @returns A promise that resolves to an object with header and body row counts.
404
+ *
405
+ * @example
406
+ * // Get current row counts
407
+ * const counts = await table.getRowCount();
408
+ * console.log(`Headers: ${counts.header}, Body: ${counts.body}`);
409
+ *
410
+ * @example
411
+ * // Assert specific row count
412
+ * const { body } = await table.getRowCount();
413
+ * expect(body).toBe(10);
414
+ */
415
+ getRowCount(): Promise<{
416
+ header: number;
417
+ body: number;
418
+ }>;
419
+ /**
420
+ * Gets all distinct (unique) values from a specific column.
421
+ * Returns a sorted array of unique string values, excluding empty values.
422
+ * @param headerName - The header name of the column to extract distinct values from.
423
+ * @param options - Optional table loading options. See {@link TableOptions}.
424
+ * @returns A promise that resolves to a sorted array of distinct values.
425
+ * @throws Error if the header is not found, or if the table has no header rows or no body rows.
426
+ *
427
+ * @example
428
+ * // Get all unique status values
429
+ * const statuses = await table.getDistinctColumnValues("Status");
430
+ * // Result: ["Active", "Inactive", "Pending"]
431
+ *
432
+ * @example
433
+ * // Get distinct countries for filtering
434
+ * const countries = await table.getDistinctColumnValues("Country");
435
+ * console.log(`Available countries: ${countries.join(", ")}`);
436
+ *
437
+ * @example
438
+ * // Verify expected values exist
439
+ * const roles = await table.getDistinctColumnValues("Role");
440
+ * expect(roles).toContain("Admin");
441
+ * expect(roles).toHaveLength(3);
442
+ *
443
+ * @see {@link getAllBodyCellLocatorsByHeaderName} for getting all cell locators in a column
444
+ * @see {@link getJson} for getting full table data
445
+ */
446
+ getDistinctColumnValues(headerName: string, options?: TableOptions): Promise<string[]>;
447
+ /**
448
+ * Finds the index of the first body row matching the specified conditions.
449
+ * Returns -1 if no matching row is found.
450
+ * @param conditions - Record of header names and expected cell values to match.
451
+ * @param options - Optional table loading options. See {@link TableOptions}.
452
+ * @returns A promise that resolves to the 0-based index of the first matching row, or -1 if not found.
453
+ * @throws Error if specified headers don't exist, or if the table has no header rows or no body rows.
454
+ *
455
+ * @example
456
+ * // Find index of row where Status is "Active"
457
+ * const index = await table.findRowIndex({ Status: "Active" });
458
+ * if (index >= 0) {
459
+ * console.log(`Found at row ${index}`);
460
+ * }
461
+ *
462
+ * @example
463
+ * // Find row and interact with it
464
+ * const rowIndex = await table.findRowIndex({ Username: "john.doe" });
465
+ * if (rowIndex >= 0) {
466
+ * const cell = table.getBodyCellLocator(rowIndex, 0);
467
+ * await cell.click();
468
+ * }
469
+ *
470
+ * @see {@link getBodyCellLocatorByRowConditions} for getting cell locators from matching rows
471
+ */
472
+ findRowIndex(conditions: Record<string, string>, options?: TableOptions): Promise<number>;
473
+ private getTable;
474
+ /**
475
+ * The locator of the DOM cell at a grid position, so colspan and rowspan don't shift it. A cell that
476
+ * spans rows is located in the row it starts in.
477
+ */
478
+ private gridCellLocator;
479
+ private normalizeGetBodyRowsOptions;
480
+ private resolveHeaderRowOptionsForJsonKeys;
481
+ private resolveTableOptionsForHeaderKeyMethods;
482
+ private resolveHeaderRowOptions;
483
+ private resolveColspanOptions;
484
+ private mainHeaderRow;
485
+ /**
486
+ * Gets the index of a header in the main header row, throwing an error if not found.
487
+ * @param header - The header name to find.
488
+ * @param mainHeader - The main header row to search in.
489
+ * @returns The 0-based index of the header.
490
+ * @throws Error if the header is not found.
491
+ */
492
+ private getHeaderIndex;
493
+ }
494
+ /**
495
+ * A header key accepted by {@link TableLocator}: a plain header name, a colspan continuation
496
+ * (`Name__C1`, `Name__C2`, ...) or a duplicate header (`Name__D1`, `Name__D2`, ...).
497
+ *
498
+ * These are the same keys that `PlaywrightTable.getJson()` produces.
499
+ *
500
+ * @example
501
+ * type Keys = HeaderKey<"Name" | "Status">;
502
+ * // "Name" | "Status" | `Name__C${number}` | `Status__C${number}` | `Name__D${number}` | `Status__D${number}`
503
+ */
504
+ type HeaderKey<H extends string> = H | `${H}__C${number}` | `${H}__D${number}`;
505
+ /**
506
+ * A column of a {@link TableLocator}: a {@link HeaderKey}, or a 0-based column index on the visual grid
507
+ * (rowspan and colspan aware). Column indexes also work for tables without header rows.
508
+ *
509
+ * @example
510
+ * table.rowAt(0).cell("Name");
511
+ * table.rowAt(0).cell(2);
512
+ */
513
+ type ColumnKey<H extends string> = HeaderKey<H> | number;
514
+ /**
515
+ * A declarative cell condition. Exactly one key per condition.
516
+ *
517
+ * Cell text is compared as the user sees it: each line whitespace-collapsed and trimmed, lines joined
518
+ * with one `\n`, without icon glyphs, invisible characters or visually hidden text. Expected values are
519
+ * normalized the same way, so `"Street 1\r\nAmsterdam"` matches a cell showing two lines.
520
+ *
521
+ * @example
522
+ * table.row({ Name: { contains: "john", ignoreCase: true } });
523
+ * table.row({ Status: { oneOf: ["Active", "Pending"] } });
524
+ * table.row({ Remarks: { empty: true } });
525
+ * // Runs in the browser, so it must not use variables from the test scope.
526
+ * table.row({ Age: { predicate: text => Number(text) >= 18 } });
527
+ * // The second argument is the raw cell text (`innerText` or `textContent`, unnormalized).
528
+ * table.row({ Name: { predicate: (text, raw) => raw.includes("\u00a0") } });
529
+ * // Combinators
530
+ * table.row({ Status: { not: "Deleted" } });
531
+ * table.row({ Email: { anyOf: [{ endsWith: "@example.com" }, /@test\./] } });
532
+ * table.row({ Name: { allOf: [{ startsWith: "J" }, { not: { contains: "Doe" } }] } });
533
+ * // Numeric comparisons on the number in the cell (see {@link NumberCondition})
534
+ * table.row({ Age: { gte: 18 } });
535
+ * table.row({ Price: { between: [10, 20], decimal: "," } });
536
+ */
537
+ type CellCondition = ExclusiveCondition<{
538
+ equals: string;
539
+ ignoreCase?: boolean;
540
+ }> | ExclusiveCondition<{
541
+ contains: string;
542
+ ignoreCase?: boolean;
543
+ }> | ExclusiveCondition<{
544
+ startsWith: string;
545
+ ignoreCase?: boolean;
546
+ }> | ExclusiveCondition<{
547
+ endsWith: string;
548
+ ignoreCase?: boolean;
549
+ }> | ExclusiveCondition<{
550
+ oneOf: readonly string[];
551
+ ignoreCase?: boolean;
552
+ }> | ExclusiveCondition<{
553
+ empty: boolean;
554
+ }> | ExclusiveCondition<{
555
+ /**
556
+ * Serialized with `toString()` and executed in the page, like a `page.evaluate` function.
557
+ * It must be self-contained: it cannot use variables from the test scope.
558
+ *
559
+ * `text` is the normalized cell text that the other conditions compare; `raw` is the cell text
560
+ * exactly as the browser reads it (`innerText`, or `textContent`), without any normalization.
561
+ */
562
+ predicate: (text: string, raw: string) => boolean;
563
+ }> | ExclusiveCondition<{
564
+ not: CellMatcher;
565
+ }> | ExclusiveCondition<{
566
+ anyOf: readonly [CellMatcher, ...CellMatcher[]];
567
+ }> | ExclusiveCondition<{
568
+ allOf: readonly [CellMatcher, ...CellMatcher[]];
569
+ }> | NumberCondition;
570
+ /**
571
+ * A numeric cell condition: the cell holds exactly one number, and it compares as asked. `between` is
572
+ * inclusive. A cell without a number, or with more than one, never matches.
573
+ *
574
+ * The number is read like {@link DecimalSeparator} describes: `"€ 1,200.50"` is 1200.5 and, with
575
+ * `decimal: ","`, `"1.234,5 kg"` is 1234.5.
576
+ *
577
+ * @example
578
+ * table.row({ Age: { gt: 65 } });
579
+ * table.row({ Total: { between: [100, 200] } });
580
+ */
581
+ type NumberCondition = ExclusiveCondition<{
582
+ gt: number;
583
+ decimal?: DecimalSeparator;
584
+ }> | ExclusiveCondition<{
585
+ gte: number;
586
+ decimal?: DecimalSeparator;
587
+ }> | ExclusiveCondition<{
588
+ lt: number;
589
+ decimal?: DecimalSeparator;
590
+ }> | ExclusiveCondition<{
591
+ lte: number;
592
+ decimal?: DecimalSeparator;
593
+ }> | ExclusiveCondition<{
594
+ between: readonly [number, number];
595
+ decimal?: DecimalSeparator;
596
+ }>;
597
+ /**
598
+ * The decimal separator of the numbers in a table. Defaults to `"."`.
599
+ *
600
+ * A number is the one run of digits in the text, with an optional `-` or `−` in front (a currency symbol
601
+ * may come in between). Digits are grouped by the other separator, `'`, or a (non-breaking) space, in
602
+ * groups of three: with `"."`, `"1,234.5"`, `"1 234.5"` and `"1'234.5"` are all 1234.5. Other text, such
603
+ * as a currency or a unit, is ignored. Text with no number, or with more than one, is not a number.
604
+ */
605
+ type DecimalSeparator = "." | ",";
606
+ type CellConditionKey = "equals" | "contains" | "startsWith" | "endsWith" | "oneOf" | "empty" | "predicate" | "not" | "anyOf" | "allOf" | "gt" | "gte" | "lt" | "lte" | "between" | "ignoreCase" | "decimal";
607
+ /** Forbids the keys of the other condition kinds, so a condition object has exactly one kind. */
608
+ type ExclusiveCondition<T> = T & { [K in Exclude<CellConditionKey, keyof T>]?: never; };
609
+ /**
610
+ * How a cell is matched: an exact string, a RegExp, or a {@link CellCondition}.
611
+ */
612
+ type CellMatcher = string | RegExp | CellCondition;
613
+ /**
614
+ * Row conditions keyed by header. Every condition must match for a row to be selected.
615
+ */
616
+ type RowWhere<H extends string> = { [K in HeaderKey<H>]?: CellMatcher; };
617
+ /**
618
+ * Compile-time guard for {@link RowWhere}: rejects an empty object and keys that are not a
619
+ * {@link HeaderKey} of `H`. Intersect it with the inferred conditions type `W`.
620
+ */
621
+ type ValidRowWhere<H extends string, W> = ([keyof W] extends [never] ? "row() requires at least one condition" : unknown) & ([Exclude<keyof W, HeaderKey<H>>] extends [never] ? unknown : { [K in Exclude<keyof W, HeaderKey<H>>]: never; });
622
+ /** An ARIA role, as Playwright's `getByRole` accepts it. */
623
+ type AriaRole = Parameters<Locator["getByRole"]>[0];
624
+ /**
625
+ * Which elements an option of {@link TableLocator} selects: a CSS selector, or a descriptor that combines a
626
+ * CSS selector, a role and the text of the element. Every field of a descriptor must match.
627
+ *
628
+ * These options are not Playwright `Locator`s, because the table is read in the browser on every retry of a
629
+ * locator or matcher, and only a CSS selector or a descriptor can be checked there.
630
+ *
631
+ * @example
632
+ * row: { rowSelector: "tbody>tr.data" }
633
+ * row: { rowSelector: { role: "row", hasNotText: "Loading" } }
634
+ * row: { columnSelector: { role: ["cell", "rowheader"] } }
635
+ * emptyRow: { hasText: "No data" }
636
+ * ignore: [...DEFAULT_IGNORE, { css: "span", hasText: /^sort/ }]
637
+ */
638
+ type ElementSelector = string | ElementDescriptor;
639
+ /** A descriptor of {@link ElementSelector}. It needs at least one field. */
640
+ type ElementDescriptor = {
641
+ /** A CSS selector the element matches. */
642
+ css?: string;
643
+ /**
644
+ * The role of the element, or one of several roles: its `role` attribute (the first role in it), or
645
+ * else the role of a table element. `<table>` is a `table`, `<thead>`, `<tbody>` and `<tfoot>` are a
646
+ * `rowgroup`, and `<tr>` is a `row`. `<td>` is a `gridcell` in a table with the `grid` or `treegrid` role,
647
+ * and a `cell` otherwise. `<th>` is a `rowheader` with `scope="row"` or in a row that also has a `<td>`
648
+ * (outside a `<thead>`), and a `columnheader` otherwise.
649
+ */
650
+ role?: AriaRole | readonly [AriaRole, ...AriaRole[]];
651
+ /**
652
+ * Text inside the element, like `Locator.filter({ hasText })`: a string matches case-insensitively,
653
+ * anywhere in the text, with whitespace collapsed; a RegExp is tested on that text. The text is the
654
+ * `textContent` of the element, hidden text included.
655
+ */
656
+ hasText?: string | RegExp;
657
+ /** Text that is not inside the element, the opposite of `hasText`. */
658
+ hasNotText?: string | RegExp;
659
+ };
660
+ /**
661
+ * Options for {@link TableLocator}.
662
+ *
663
+ * @example
664
+ * const options: TableLocatorOptions<"Name" | "Status"> = {
665
+ * headers: ["Name", "Status"],
666
+ * header: { rowSelector: "thead>tr", columnSelector: "th" },
667
+ * row: { rowSelector: "tbody>tr", columnSelector: "td" },
668
+ * cellContentType: CellContentType.InnerText,
669
+ * };
670
+ */
671
+ type TableLocatorOptions<H extends string> = {
672
+ /**
673
+ * The header names of the table. Used to infer typed header keys and by
674
+ * {@link TableLocator.validateHeaders}. Does not restrict anything at runtime by itself.
675
+ */
676
+ headers?: readonly H[];
677
+ /**
678
+ * Use ARIA roles instead of HTML table elements for the default selectors, for grids built from
679
+ * other elements (such as AG Grid or MUI Data Grid). Selectors you set yourself still win.
680
+ * Defaults to `false`.
681
+ *
682
+ * - Header rows: `[role=row]` with a `[role=columnheader]` inside; header cells: `[role=columnheader]`.
683
+ * - Body rows: the other `[role=row]`s; body cells: `[role=gridcell]`, `[role=cell]` and `[role=rowheader]`.
684
+ *
685
+ * `aria-colindex`, `aria-rowindex`, `aria-colspan` and `aria-rowspan` are used whenever they are
686
+ * present, with or without this option.
687
+ */
688
+ aria?: boolean;
689
+ header?: {
690
+ /** Index of the main header row. Defaults to the last header row. */
691
+ setMainHeaderRow?: number;
415
692
  /**
416
- * Gets all distinct (unique) values from a specific column.
417
- * Returns a sorted array of unique string values, excluding empty values.
418
- * @param headerName - The header name of the column to extract distinct values from.
419
- * @param options - Optional table loading options. See {@link TableOptions}.
420
- * @returns A promise that resolves to a sorted array of distinct values.
421
- * @throws Error if the specified header is not found in the table.
422
- *
423
- * @example
424
- * // Get all unique status values
425
- * const statuses = await table.getDistinctColumnValues("Status");
426
- * // Result: ["Active", "Inactive", "Pending"]
427
- *
428
- * @example
429
- * // Get distinct countries for filtering
430
- * const countries = await table.getDistinctColumnValues("Country");
431
- * console.log(`Available countries: ${countries.join(", ")}`);
432
- *
433
- * @example
434
- * // Verify expected values exist
435
- * const roles = await table.getDistinctColumnValues("Role");
436
- * expect(roles).toContain("Admin");
437
- * expect(roles).toHaveLength(3);
438
- *
439
- * @see {@link getAllBodyCellLocatorsByHeaderName} for getting all cell locators in a column
440
- * @see {@link getJson} for getting full table data
693
+ * Header rows, see {@link ElementSelector}. Defaults to `"thead>tr"`, plus the leading rows of `<th>`
694
+ * cells (and no `<td>`) in the first `<tbody>` of a table without a `<thead>`.
441
695
  */
442
- getDistinctColumnValues(headerName: string, options?: TableOptions): Promise<string[]>;
696
+ rowSelector?: ElementSelector;
697
+ /** Header cells, see {@link ElementSelector}. Defaults to `"th, td"`. */
698
+ columnSelector?: ElementSelector;
699
+ };
700
+ row?: {
443
701
  /**
444
- * Finds the index of the first body row matching the specified conditions.
445
- * Returns -1 if no matching row is found.
446
- * @param conditions - Record of header names and expected cell values to match.
447
- * @param options - Optional table loading options. See {@link TableOptions}.
448
- * @returns A promise that resolves to the 0-based index of the first matching row, or -1 if not found.
449
- * @throws Error if specified headers don't exist in the table.
450
- *
451
- * @example
452
- * // Find index of row where Status is "Active"
453
- * const index = await table.findRowIndex({ Status: "Active" });
454
- * if (index >= 0) {
455
- * console.log(`Found at row ${index}`);
456
- * }
457
- *
458
- * @example
459
- * // Find row and interact with it
460
- * const rowIndex = await table.findRowIndex({ Username: "john.doe" });
461
- * if (rowIndex >= 0) {
462
- * const cell = table.getBodyCellLocator(rowIndex, 0);
463
- * await cell.click();
464
- * }
465
- *
466
- * @see {@link getBodyCellLocatorByRowConditions} for getting cell locators from matching rows
467
- * @see {@link waitForRowByConditions} for waiting until matching row appears
702
+ * Body rows, see {@link ElementSelector}. Defaults to `"tbody>tr"` (and `<tr>` right inside a
703
+ * `<table>` built without a `<tbody>`), without the header rows of a table without a `<thead>`.
468
704
  */
469
- findRowIndex(conditions: Record<string, string>, options?: TableOptions): Promise<number>;
470
- private getTable;
471
- private normalizeGetBodyRowsOptions;
472
- private resolveHeaderRowOptionsForJsonKeys;
473
- private resolveTableOptionsForHeaderKeyMethods;
474
- private resolveHeaderRowOptions;
475
- private mainHeaderRow;
705
+ rowSelector?: ElementSelector;
706
+ /** Body cells, see {@link ElementSelector}. Defaults to `"td, th"`, so row header cells are columns too. */
707
+ columnSelector?: ElementSelector;
708
+ };
709
+ footer?: {
476
710
  /**
477
- * Gets the index of a header in the main header row, throwing an error if not found.
478
- * @param header - The header name to find.
479
- * @param mainHeader - The main header row to search in.
480
- * @returns The 0-based index of the header.
481
- * @throws Error if the header is not found.
711
+ * Footer rows, such as a totals row, see {@link ElementSelector}. Their cells are found with
712
+ * `row.columnSelector`. Defaults to `"tfoot>tr"`, or no footer rows with `aria: true`. Footer rows are
713
+ * not body rows.
482
714
  */
483
- private getHeaderIndex;
715
+ rowSelector?: ElementSelector;
716
+ };
717
+ /**
718
+ * How to go to the next page of a paginated table, so {@link TableLocator.findRow},
719
+ * `toJson({ allRows: true })` and {@link TableLocator.totalRowCount} see every page.
720
+ * See {@link PaginateOptions}.
721
+ */
722
+ paginate?: PaginateOptions;
723
+ /**
724
+ * How to scroll a virtualized grid (one that only renders the rows in view), so
725
+ * {@link TableLocator.findRow}, `toJson({ allRows: true })` and {@link TableLocator.totalRowCount}
726
+ * see every row. See {@link ScrollOptions}.
727
+ */
728
+ scroll?: ScrollOptions;
729
+ /**
730
+ * The loading indicator of the table, such as a spinner or an overlay. {@link TableLocator.waitForStable}
731
+ * and the page and scroll navigation wait until it is hidden.
732
+ */
733
+ loading?: LocatorRef;
734
+ /**
735
+ * Placeholder rows, such as a "No data" row, that are never body rows: `rows`, `row()`, `toJson()`,
736
+ * the matchers and the row counts leave them out, so an empty table has 0 body rows. See
737
+ * {@link ElementSelector}.
738
+ *
739
+ * @example
740
+ * emptyRow: ".no-data"
741
+ * emptyRow: { hasText: "No data" }
742
+ */
743
+ emptyRow?: ElementSelector;
744
+ /**
745
+ * Read the current value of text inputs and text areas in body cells as cell text, in row conditions,
746
+ * `toJson()`, `columnValues()` and the matchers, for editable grids and cells in edit mode. Text-like
747
+ * inputs only (`text`, `email`, `number`, `search`, `tel`, `url` and the date and time types):
748
+ * checkboxes, buttons, files and passwords stay out, and a `<select>` always reads as its selected option.
749
+ * Header cells never read their inputs, so filter fields don't end up in the header keys. Raw text
750
+ * (`raw: true`, a predicate's `raw`) is not affected. Defaults to `false`, like Playwright's `toHaveText`.
751
+ *
752
+ * @example
753
+ * const people = defineTable(page.locator("#people"), { readInputValues: true });
754
+ * await expect(people).toHaveRow({ Email: "john@example.org" }); // <td><input value="john@example.org"></td>
755
+ */
756
+ readInputValues?: boolean;
757
+ /**
758
+ * The control that selects a row, from the row locator, for {@link TableRow.select},
759
+ * {@link TableRow.deselect} and {@link TableRow.isSelected}. Defaults to the checkbox in a selection
760
+ * column: a column without header text (such as one with only a "select all" checkbox). A checkbox in a
761
+ * column with header text, such as "Active", is data and never selects the row.
762
+ *
763
+ * @example
764
+ * select: row => row.getByRole("checkbox", { name: "Select row" })
765
+ */
766
+ select?: (row: Locator) => Locator;
767
+ /**
768
+ * How cell text is read, for both header and body cells. Defaults to `InnerText`: the text as it is
769
+ * shown, without hidden text and with CSS `text-transform`. `TextContent` reads all text in the DOM,
770
+ * hidden text included. Either way, the text is normalized (see {@link CellCondition}).
771
+ */
772
+ cellContentType?: CellContentType;
773
+ /**
774
+ * Elements inside header and body cells whose text is left out, such as screen reader only text and
775
+ * icons, see {@link ElementSelector}. Defaults to {@link DEFAULT_IGNORE}: `.sr-only`, `.visually-hidden`,
776
+ * the Material Icons and Material Symbols classes, and `[aria-hidden=true]`.
777
+ *
778
+ * Setting it replaces the defaults. Spread them to add your own (`[...DEFAULT_IGNORE, ".sort-icon"]`),
779
+ * or pass `[]` to read all text. Raw values (`toJson({ raw: true })`, a predicate's `raw`) are never
780
+ * filtered.
781
+ */
782
+ ignore?: readonly ElementSelector[];
783
+ };
784
+ /**
785
+ * A Playwright `Locator`, or a function that finds it from the table root. A function lets an option point
786
+ * inside the table before the root is known, as the grid presets do.
787
+ *
788
+ * @example
789
+ * const next: LocatorRef = root => root.getByRole("button", { name: "Next page" });
790
+ */
791
+ type LocatorRef = Locator | ((root: Locator) => Locator);
792
+ /**
793
+ * Options for the `paginate` option of {@link TableLocator}.
794
+ *
795
+ * @example
796
+ * defineTable(page.locator("#users"), {
797
+ * paginate: { next: page.getByRole("button", { name: "Next" }), first: page.getByRole("button", { name: "First" }) },
798
+ * });
799
+ */
800
+ type PaginateOptions = {
801
+ /** The control that goes to the next page. */
802
+ next: LocatorRef;
803
+ /**
804
+ * The control that goes to the first page. When set, navigation starts by clicking it (unless the table
805
+ * shows the first page), so it always covers every page. Without it, navigation starts at the current
806
+ * page.
807
+ *
808
+ * After the click, navigation waits for the table to change. A control that stays enabled on the first
809
+ * page doesn't change the table, so that wait ends after 2 seconds, or after 0.5 seconds for a table with
810
+ * a `loading` indicator that doesn't show. Set `isFirstPage` to skip the wait altogether.
811
+ */
812
+ first?: LocatorRef;
813
+ /**
814
+ * Whether the table shows the first page, for a `first` control that stays enabled there. When it says
815
+ * so, `first` is not clicked; after a click, navigation waits until it says so. Defaults to: `first` is
816
+ * missing, hidden or disabled (see `isLastPage`), and after a click, the table changed.
817
+ *
818
+ * @example
819
+ * isFirstPage: async () => (await page.getByRole("button", { name: "1", exact: true }).getAttribute("aria-current")) === "page"
820
+ */
821
+ isFirstPage?: (first: Locator) => Promise<boolean>;
822
+ /**
823
+ * Whether the table shows the last page. Defaults to: `next` is missing, hidden or disabled (with the
824
+ * `disabled` attribute, `aria-disabled="true"` or the `disabled` class, on `next` or an element around
825
+ * it, such as a Bootstrap `<li class="disabled">`). When `next` matches several elements (a pager above
826
+ * and below the table), the first one is used.
827
+ */
828
+ isLastPage?: (next: Locator) => Promise<boolean>;
829
+ /**
830
+ * The most pages to visit, a positive integer: `3` reads the first 3 pages, also when the table has
831
+ * more. Defaults to every page. A table that never reaches its last page is stopped by the `timeout`.
832
+ */
833
+ maxPages?: number;
834
+ };
835
+ /**
836
+ * Options for the `scroll` option of {@link TableLocator}.
837
+ *
838
+ * Navigation scrolls the container from the top to the bottom, one step at a time, and reads the rows in
839
+ * view after each step. Rows are told apart by their key, so a row seen twice is read once.
840
+ *
841
+ * @example
842
+ * defineTable(page.getByRole("grid"), { aria: true, scroll: { container: root => root.locator(".viewport") } });
843
+ */
844
+ type ScrollOptions = {
845
+ /**
846
+ * The element that scrolls. Defaults to the table root. A container that can never scroll
847
+ * (`overflow-y: visible`) is an error that names the element around or inside it that does.
848
+ */
849
+ container?: LocatorRef;
850
+ /** How far to scroll per step, in pixels. Defaults to 90% of the container height. */
851
+ step?: number;
852
+ /**
853
+ * The attribute of a row element that identifies the row, such as `"data-id"`. Defaults to the first
854
+ * of `aria-rowindex`, `row-index`, `data-rowindex`, `data-index` and `data-id` that the row has, or
855
+ * else the text of its cells. Rows with the same text are told apart by their order within one view,
856
+ * so a repeated row is not lost, but the overlap between two scroll steps can't tell which of two
857
+ * identical rows it shows: give such rows a key.
858
+ *
859
+ * The position attributes (all of these but `data-id`) also order the rows. Other keys only identify
860
+ * a row, and rows are kept in the order they were shown.
861
+ */
862
+ rowKey?: string;
863
+ /**
864
+ * How long the table must stay the same after a scroll before it is read, in
865
+ * milliseconds. Defaults to 50; `0` reads the rows two animation frames after the scroll. The `loading`
866
+ * indicator, if set, is always waited for.
867
+ */
868
+ settle?: number;
869
+ /**
870
+ * The most scroll steps per page, a positive integer. Defaults to scrolling to the bottom. A grid that
871
+ * keeps growing is stopped by the `timeout`.
872
+ */
873
+ maxSteps?: number;
874
+ };
875
+ /**
876
+ * Options for {@link TableLocator.findRow} and {@link TableLocator.totalRowCount}.
877
+ */
878
+ type NavigationOptions = {
879
+ /** The time for the whole search, in milliseconds. Defaults to 30000. */
880
+ timeout?: number;
881
+ };
882
+ /**
883
+ * How {@link TableLocator.toJson} converts the text of a column:
884
+ * - `"number"`: a number, read as {@link DecimalSeparator} describes; an empty cell is `null`.
885
+ * - `"date"`: a `Date`, parsed with `Date.parse`; an empty cell is `null`.
886
+ * - A function: called in Node with the cell text, so it can use test variables. Its result is the value.
887
+ *
888
+ * A cell that `"number"` or `"date"` cannot parse, or a function that throws, fails `toJson()` with the
889
+ * row, the column and the text.
890
+ */
891
+ type ColumnParser = "number" | "date" | ((text: string) => unknown);
892
+ /**
893
+ * `T`, but not a place to infer `T` from: like `NoInfer`, which needs TypeScript 5.4.
894
+ * @internal
895
+ */
896
+ type NotInferred<T> = [T][T extends unknown ? 0 : never];
897
+ /** The {@link ColumnParser}s of {@link TableJsonOptions.parse}, by header key. */
898
+ type ColumnParsers<C extends string> = { readonly [K in C]?: ColumnParser; };
899
+ /** The value a {@link ColumnParser} produces. */
900
+ type ParsedValue<P> = P extends "number" ? number | null : P extends "date" ? Date | null : P extends ((text: string) => infer R) ? R : string;
901
+ /**
902
+ * One row of {@link TableLocator.toJson}: the text of every column in `C`, or the parsed value for the
903
+ * columns in `P`.
904
+ */
905
+ type TableJsonRow<C extends string, P = {}> = { [K in C]: K extends keyof P ? ParsedValue<P[K]> : string; };
906
+ /**
907
+ * Options for {@link TableLocator.waitForStable}.
908
+ *
909
+ * @example
910
+ * await users.waitForStable({ quiet: 300, loading: page.getByRole("progressbar") });
911
+ */
912
+ type StableOptions = {
913
+ /**
914
+ * How long the header keys and body cell texts must stay the same, in milliseconds. Defaults to 500.
915
+ */
916
+ quiet?: number;
917
+ /** The time to wait in milliseconds. Defaults to 5000. */
918
+ timeout?: number;
919
+ /**
920
+ * A loading indicator, such as a spinner or an overlay. The table is only stable while it is hidden
921
+ * (not attached, or not visible), so a table that is about to refresh isn't read too early.
922
+ */
923
+ loading?: LocatorRef;
924
+ };
925
+ /**
926
+ * Options for {@link TableLocator.toJson}.
927
+ *
928
+ * @example
929
+ * await users.toJson({ minRows: 1, timeout: 10_000 });
930
+ *
931
+ * @example
932
+ * // Some columns, with typed values
933
+ * await orders.toJson({ columns: ["Id", "Total"], parse: { Total: "number" }, decimal: "," });
934
+ */
935
+ type TableJsonOptions<C extends string = string, P = {}> = {
936
+ /**
937
+ * Only these columns, in this order. Defaults to every column of the main header row. A key that is not
938
+ * a header key of the table fails `toJson()` with the available headers.
939
+ */
940
+ columns?: readonly C[];
941
+ /** Converts the text of some columns to typed values, see {@link ColumnParser}. */
942
+ parse?: P & ColumnParsers<NotInferred<C>>;
943
+ /** The decimal separator of the `"number"` columns of `parse`. Defaults to `"."`. */
944
+ decimal?: DecimalSeparator;
945
+ /**
946
+ * Wait until the table stops changing before reading it, see {@link TableLocator.waitForStable}.
947
+ * `true` uses the default options. The `timeout` of the stable options defaults to (and can't exceed)
948
+ * the time left of `timeout`.
949
+ */
950
+ stable?: boolean | StableOptions;
951
+ /**
952
+ * Read every row: on every page with the `paginate` option, and from top to bottom with the `scroll`
953
+ * option of the table. `minRows` and `stable` apply to the first view. Defaults to `false`: the rows the
954
+ * table shows now.
955
+ *
956
+ * It leaves the table on its last page, scrolled down; without `paginate.first`, the next read starts
957
+ * there. It reads up to `paginate.maxPages` and `scroll.maxSteps`, if set. Reaching the `timeout` before that
958
+ * is an error that says how many rows were read, so part of the rows is never returned as all of them.
959
+ */
960
+ allRows?: boolean;
961
+ /**
962
+ * Wait for a header row and at least this many body rows with content (a row where at least one cell
963
+ * has text) before reading. Defaults to `0`: read once, without waiting.
964
+ */
965
+ minRows?: number;
966
+ /**
967
+ * With `minRows` or `stable`, the time for the whole read, waits included, in milliseconds (default
968
+ * 5000). With `allRows`, the time for the whole read, pages and scrolling included (default 30000).
969
+ * Without any of them, the time to wait for the table element to be attached.
970
+ */
971
+ timeout?: number;
972
+ /**
973
+ * Return every body cell as the browser reads it (`innerText`, or `textContent`), without any
974
+ * normalization: line breaks, tabs, non-breaking spaces, icon glyphs and visually hidden text stay.
975
+ * Header keys are normalized either way. Defaults to `false`.
976
+ */
977
+ raw?: boolean;
978
+ };
979
+ /** Filter options accepted by `Locator.filter()`. */
980
+ export type RowFilterOptions = Parameters<Locator["filter"]>[0];
981
+ /**
982
+ * One or more body rows of a {@link TableLocator}, with typed access to their cells.
983
+ *
984
+ * Instances are created by {@link TableLocator.row}, {@link TableLocator.rowAt} and {@link TableLocator.rows}.
985
+ * Their locators read like your code in error messages and traces, such as
986
+ * `locator('#users').row({ "Name": "John" }).cell("Status")` (Playwright 1.53 and later).
987
+ *
988
+ * @example
989
+ * const row = table.row({ Name: "John" });
990
+ * await expect(row.locator).toBeVisible();
991
+ * await expect(row.cell("Status")).toHaveText("Active");
992
+ */
993
+ export declare class TableRow<H extends string = string> {
994
+ readonly table: TableLocator<H>;
995
+ readonly locator: Locator;
996
+ /**
997
+ * @param table - The table this row belongs to.
998
+ * @param locator - The Playwright `Locator` for the row element(s). Its `toString()` is used to describe
999
+ * the locators built from this row.
1000
+ */
1001
+ constructor(table: TableLocator<H>, locator: Locator);
1002
+ /**
1003
+ * Returns the cell of this row under `column`.
1004
+ * If this row locator matches several rows, the cell locator matches one cell per row.
1005
+ *
1006
+ * @param column - The header key of the column, or its 0-based index (also works without header rows).
1007
+ * @returns A Playwright Locator for the cell.
1008
+ *
1009
+ * @example
1010
+ * await table.row({ Name: "John" }).cell("Email").click();
1011
+ *
1012
+ * @example
1013
+ * // By column index, for example in a table without header rows
1014
+ * await expect(table.rowAt(0).cell(2)).toHaveText("john@example.com");
1015
+ */
1016
+ cell(column: ColumnKey<H>): Locator;
1017
+ /**
1018
+ * The first of these rows, with typed cell access. Use it when several rows may match and any is fine.
1019
+ *
1020
+ * @example
1021
+ * await table.row({ Status: "Active" }).first().cell("Edit").click();
1022
+ */
1023
+ first(): TableRow<H>;
1024
+ /**
1025
+ * The last of these rows, with typed cell access.
1026
+ *
1027
+ * @example
1028
+ * await expect(table.rows.last().cell("Name")).toHaveText("Jane");
1029
+ */
1030
+ last(): TableRow<H>;
1031
+ /**
1032
+ * The row at `index` among these rows, with typed cell access. Negative indexes count from the end,
1033
+ * like `Locator.nth()`.
1034
+ *
1035
+ * @param index - The 0-based index among these rows.
1036
+ * @returns A {@link TableRow} for the row.
1037
+ * @throws Error if index is not an integer.
1038
+ *
1039
+ * @example
1040
+ * await expect(table.row({ Status: "Active" }).nth(1).cell("Name")).toHaveText("Joe");
1041
+ */
1042
+ nth(index: number): TableRow<H>;
1043
+ /**
1044
+ * Narrows these rows with `Locator.filter()` and keeps typed cell access.
1045
+ *
1046
+ * @param options - The same options as `Locator.filter()`.
1047
+ * @returns A new {@link TableRow} for the filtered rows.
1048
+ *
1049
+ * @example
1050
+ * await expect(table.rows.filter({ hasText: "Jane" }).cell("Status")).toHaveText("Active");
1051
+ */
1052
+ filter(options?: RowFilterOptions): TableRow<H>;
1053
+ /**
1054
+ * Fills the controls in the cells of this row, one column after the other. A boolean checks or unchecks
1055
+ * a checkbox or switch; a string selects an option of a `<select>`, or fills an input, a text area or
1056
+ * an editable element.
1057
+ *
1058
+ * A cell without a control is edited in place, as data grids do: it is double-clicked, the editor that
1059
+ * appears in the cell is filled, and `commit` is pressed. Editors that open outside the cell (a popup)
1060
+ * are not found.
1061
+ *
1062
+ * Columns are filled in the order of `values`. When the row is found by the value of a column you fill,
1063
+ * fill that column last: after it changes, the row no longer matches.
1064
+ *
1065
+ * @param values - The value per column.
1066
+ * @param options - Optional settings.
1067
+ * @param options.commit - (Optional) The key that ends in-place editing. Defaults to `"Enter"`; `false` presses nothing.
1068
+ * @param options.timeout - (Optional) The time per action, in milliseconds. Defaults to 5000.
1069
+ * @throws Error if a cell has no control, also not after a double-click.
1070
+ *
1071
+ * @example
1072
+ * await users.row({ Name: "John" }).fill({ Email: "john@example.org", Active: false, Role: "Admin" });
1073
+ */
1074
+ fill(values: { [K in HeaderKey<H>]?: string | boolean; }, options?: {
1075
+ commit?: string | false;
1076
+ timeout?: number;
1077
+ }): Promise<void>;
1078
+ /**
1079
+ * Selects this row: checks its selection checkbox (see the `select` option of {@link TableLocator}), or
1080
+ * clicks the row if it has none, and waits until the row is selected (see {@link isSelected}). A
1081
+ * selected row is left as it is.
1082
+ *
1083
+ * @param options - Optional timeout, in milliseconds. Defaults to 5000.
1084
+ * @throws Error if the row is not selected within the timeout.
1085
+ *
1086
+ * @example
1087
+ * await users.row({ Name: "John" }).select();
1088
+ * await expect(users.row({ Name: "John" })).toBeSelected();
1089
+ */
1090
+ select(options?: {
1091
+ timeout?: number;
1092
+ }): Promise<void>;
1093
+ /**
1094
+ * Deselects this row: unchecks its selection checkbox, or clicks the row if it has none, and waits
1095
+ * until the row is not selected. A row that is not selected is left as it is.
1096
+ *
1097
+ * @param options - Optional timeout, in milliseconds. Defaults to 5000.
1098
+ * @throws Error if the row is still selected after the timeout.
1099
+ */
1100
+ deselect(options?: {
1101
+ timeout?: number;
1102
+ }): Promise<void>;
1103
+ /**
1104
+ * Whether this row is selected. A row with `aria-selected` is selected when it is `"true"`. Otherwise,
1105
+ * the row is selected when its selection checkbox is checked (see the `select` option of
1106
+ * {@link TableLocator}). Reads the row once; for a retrying check, use `expect(row).toBeSelected()`.
1107
+ *
1108
+ * @param options - Optional timeout for finding the row, in milliseconds.
1109
+ * @returns A promise that resolves to whether the row is selected.
1110
+ */
1111
+ isSelected(options?: {
1112
+ timeout?: number;
1113
+ }): Promise<boolean>;
1114
+ /**
1115
+ * Expands this row of a tree grid, and waits until it is expanded. An expanded row is left as it is.
1116
+ * It clicks `toggle`, or else the element in the row with `aria-expanded` (not a menu button, which has
1117
+ * `aria-haspopup`), or else, when the row itself has `aria-expanded`, the button in its first cell.
1118
+ * Without any of them, it focuses the row and presses `ArrowRight`, as tree grids do.
1119
+ *
1120
+ * @param options - Optional settings.
1121
+ * @param options.toggle - (Optional) The control that expands and collapses the row, from the row locator.
1122
+ * @param options.timeout - (Optional) The time to wait, in milliseconds. Defaults to 5000.
1123
+ * @throws Error if the row has no `aria-expanded` (on the row or an element in it), or doesn't expand.
1124
+ *
1125
+ * @example
1126
+ * await files.row({ Name: "src" }).expand();
1127
+ * await expect(files.row({ Name: "index.ts" }).locator).toBeVisible();
1128
+ */
1129
+ expand(options?: {
1130
+ toggle?: (row: Locator) => Locator;
1131
+ timeout?: number;
1132
+ }): Promise<void>;
1133
+ /**
1134
+ * Collapses this row of a tree grid, like {@link expand} does, but with `ArrowLeft` as the fallback.
1135
+ *
1136
+ * @param options - Optional settings, the same as for {@link expand}.
1137
+ * @throws Error if the row has no `aria-expanded` (on the row or an element in it), or doesn't collapse.
1138
+ */
1139
+ collapse(options?: {
1140
+ toggle?: (row: Locator) => Locator;
1141
+ timeout?: number;
1142
+ }): Promise<void>;
1143
+ /**
1144
+ * Whether this row is expanded: the `aria-expanded` of the row, or else of the first element in it that
1145
+ * has one and is not a menu button (with `aria-haspopup`). `null` if there is none, for a row that
1146
+ * cannot expand.
1147
+ *
1148
+ * @param options - Optional settings.
1149
+ * @param options.timeout - (Optional) The time to wait for the row, in milliseconds.
1150
+ * @returns A promise that resolves to `true`, `false` or `null`.
1151
+ */
1152
+ isExpanded(options?: {
1153
+ timeout?: number;
1154
+ }): Promise<boolean | null>;
1155
+ /**
1156
+ * The `aria-level` of this row in a tree grid (1 for the top level), or `null` if it has none.
1157
+ *
1158
+ * @param options - Optional settings.
1159
+ * @param options.timeout - (Optional) The time to wait for the row, in milliseconds.
1160
+ * @example
1161
+ * expect(await files.row({ Name: "index.ts" }).level()).toBe(2);
1162
+ */
1163
+ level(options?: {
1164
+ timeout?: number;
1165
+ }): Promise<number | null>;
1166
+ /**
1167
+ * The control that selects this row: the `select` option of the table, or else the checkbox in the
1168
+ * first column without header text (in a table without header rows, the first column) that has one.
1169
+ * `undefined` if there is none.
1170
+ */
1171
+ private selectionControl;
1172
+ /**
1173
+ * The control that expands and collapses this row: `toggle` (waited for, as you passed it), or else the
1174
+ * element in the row with `aria-expanded` that is not a menu button, or else, when the row itself has
1175
+ * `aria-expanded`, the button in its first cell. `undefined` if there is none.
1176
+ * @throws Error if `toggle` doesn't show up.
1177
+ */
1178
+ private expandToggle;
1179
+ private setSelected;
1180
+ private setExpanded;
1181
+ }
1182
+ /**
1183
+ * A type-safe table API that only returns Playwright `Locator`s.
1184
+ *
1185
+ * Header names are resolved to columns inside the browser by the `pw-table-v1` selector engine on every
1186
+ * evaluation, so every locator auto-waits for late headers and rows, and keeps working when columns are
1187
+ * reordered at runtime. Building locators is synchronous.
1188
+ *
1189
+ * Text is read as the user sees it. Header text is one line: `First<br>name` is the header `"First name"`.
1190
+ * Body cells keep their line breaks as one `\n`. Icon glyphs, invisible characters and the text of the elements
1191
+ * matching the `ignore` option (screen reader only text and icons by default) are left out. Header names
1192
+ * you pass are normalized the same way.
1193
+ *
1194
+ * The `pw-table-v1` selector engine must be registered first: import `test` from this package, or call
1195
+ * {@link registerTableSelectors} before the page is created.
1196
+ *
1197
+ * @example
1198
+ * import { test, expect, TableLocator } from "@cerios/playwright-table";
1199
+ *
1200
+ * test("user is active", async ({ page }) => {
1201
+ * const users = new TableLocator(page.locator("table"), { headers: ["Name", "Status", "Email"] });
1202
+ * await expect(users.row({ Name: "John" }).cell("Status")).toHaveText("Active");
1203
+ * });
1204
+ */
1205
+ export declare class TableLocator<const H extends string = string> {
1206
+ /** The Playwright `Locator` pointing to the root table element. */
1207
+ readonly root: Locator;
1208
+ private readonly _config;
1209
+ private readonly _headers;
1210
+ private readonly _loading;
1211
+ private readonly _navigator;
1212
+ private readonly _rowKey;
1213
+ private readonly _select;
1214
+ /**
1215
+ * Creates a table with customizable selectors for headers and rows.
1216
+ *
1217
+ * Rows and cells inside a cell (such as a nested table) never belong to this table, and both `<th>`
1218
+ * and `<td>` count as cells in header and body rows. `PlaywrightTable` only takes `<th>` in header rows
1219
+ * and `<td>` in body rows.
1220
+ *
1221
+ * @param root - The Playwright `Locator` pointing to the root table element.
1222
+ * @param options - Optional configuration. See {@link TableLocatorOptions}.
1223
+ * @param options.headers - (Optional) Header names, used for typed header keys and {@link validateHeaders}.
1224
+ * @param options.aria - (Optional) Use ARIA roles for the default selectors. Defaults to `false`.
1225
+ * @param options.header.setMainHeaderRow - (Optional) Index of the main header row. Defaults to the last header row.
1226
+ * @param options.header.rowSelector - (Optional) Header rows: a CSS selector or an {@link ElementSelector} descriptor. Defaults to `"thead>tr"`, plus the leading `<th>`-only rows of a table without `<thead>`.
1227
+ * @param options.header.columnSelector - (Optional) Header cells, see {@link ElementSelector}. Defaults to `"th, td"`.
1228
+ * @param options.row.rowSelector - (Optional) Body rows, see {@link ElementSelector}. Defaults to `"tbody>tr"`, without those header rows.
1229
+ * @param options.row.columnSelector - (Optional) Body cells, see {@link ElementSelector}. Defaults to `"td, th"`.
1230
+ * @param options.cellContentType - (Optional) How cell text is read. Defaults to `InnerText`.
1231
+ * @param options.ignore - (Optional) Elements inside cells whose text is left out, see {@link ElementSelector}. Defaults to {@link DEFAULT_IGNORE}.
1232
+ * @param options.footer.rowSelector - (Optional) Footer rows, see {@link ElementSelector}. Defaults to `"tfoot>tr"`, or none with `aria`.
1233
+ * @param options.paginate - (Optional) How to go to the next page, see {@link PaginateOptions}.
1234
+ * @param options.scroll - (Optional) How to scroll a virtualized grid, see {@link ScrollOptions}.
1235
+ * @param options.loading - (Optional) The loading indicator of the table.
1236
+ * @param options.emptyRow - (Optional) Placeholder rows, such as a "No data" row, that are never body rows, see {@link ElementSelector}.
1237
+ * @param options.readInputValues - (Optional) Read the value of text inputs in body cells as cell text. Defaults to `false`.
1238
+ * @param options.select - (Optional) The control that selects a row, from the row locator. Defaults to the checkbox in a selection column.
1239
+ *
1240
+ * @example
1241
+ * // Typed headers: header arguments are checked at compile time
1242
+ * const users = new TableLocator(page.locator("table"), { headers: ["Name", "Status"] });
1243
+ *
1244
+ * @example
1245
+ * // Div based table with custom selectors
1246
+ * const table = new TableLocator(page.locator(".divTable"), {
1247
+ * header: { rowSelector: ".divTableHeading > .divTableRow", columnSelector: ".divTableHead" },
1248
+ * row: { rowSelector: ".divTableBody > .divTableRow", columnSelector: ".divTableCell" },
1249
+ * });
1250
+ *
1251
+ * @example
1252
+ * // ARIA grid, such as AG Grid or MUI Data Grid
1253
+ * const grid = new TableLocator(page.getByRole("grid"), { aria: true });
1254
+ *
1255
+ * @example
1256
+ * // Rows by role and text instead of CSS
1257
+ * const orders = new TableLocator(page.locator("#orders"), {
1258
+ * row: { rowSelector: { role: "row", hasNotText: "Loading" } },
1259
+ * emptyRow: { hasText: "No data" },
1260
+ * });
1261
+ */
1262
+ constructor(root: Locator, options?: TableLocatorOptions<H>);
1263
+ /**
1264
+ * The footer rows of the table, such as a totals row, with typed cell access. Footer rows are not body
1265
+ * rows: `rows`, `row()`, `toJson()` and the matchers leave them out.
1266
+ *
1267
+ * @example
1268
+ * await expect(orders.footer.cell("Total")).toHaveText("€ 1,295.75");
1269
+ */
1270
+ get footer(): TableRow<H>;
1271
+ /**
1272
+ * All body rows of the table.
1273
+ *
1274
+ * @example
1275
+ * await expect(table.rows.locator).toHaveCount(3);
1276
+ *
1277
+ * @example
1278
+ * // Compose with Locator.filter and keep typed cell access
1279
+ * await expect(table.rows.filter({ hasText: "Jane" }).cell("Status")).toHaveText("Active");
1280
+ */
1281
+ get rows(): TableRow<H>;
1282
+ /**
1283
+ * Body rows where every condition matches. Conditions are keyed by header; values are an exact
1284
+ * string, a RegExp, or a {@link CellCondition}. Cell text is compared as the user sees it (see
1285
+ * {@link CellCondition}), and header names are normalized like header text.
1286
+ *
1287
+ * The returned locator follows Playwright's strict mode: acting on it when several rows match throws.
1288
+ *
1289
+ * @param where - At least one condition. Unknown headers are compile-time errors.
1290
+ * @returns A {@link TableRow} for the matching rows.
1291
+ * @throws Error if no condition has a value.
1292
+ *
1293
+ * @example
1294
+ * await expect(table.row({ Name: "John", Status: /active/i }).cell("Email")).toHaveText("john@example.com");
1295
+ *
1296
+ * @example
1297
+ * // Declarative conditions
1298
+ * await table.row({ Name: { startsWith: "Jo" }, Remarks: { empty: true } }).cell("Actions").click();
1299
+ */
1300
+ row<W extends RowWhere<H>>(where: W & ValidRowWhere<H, W>): TableRow<H>;
1301
+ /** {@link row}, without the compile-time checks of the conditions. */
1302
+ private matchingRows;
1303
+ /**
1304
+ * The body row at `index`.
1305
+ *
1306
+ * @param index - The row number (0-based).
1307
+ * @returns A {@link TableRow} for the row.
1308
+ * @throws Error if index is not a non-negative integer.
1309
+ *
1310
+ * @example
1311
+ * await expect(table.rowAt(0).cell("Name")).toHaveText("John");
1312
+ */
1313
+ rowAt(index: number): TableRow<H>;
1314
+ /**
1315
+ * The cell under `column` for any row locator, for example one built with `filter()` or `nth()`.
1316
+ *
1317
+ * @param row - A locator for one or more body rows of this table.
1318
+ * @param column - The header key of the column, or its 0-based index (also works without header rows).
1319
+ * @returns A Playwright Locator for the cell.
1320
+ * @throws Error if column is a number that is not a non-negative integer.
1321
+ *
1322
+ * @example
1323
+ * const lastRow = table.rows.locator.last();
1324
+ * await expect(table.cell(lastRow, "Name")).toHaveText("Jane");
1325
+ */
1326
+ cell(row: Locator, column: ColumnKey<H>): Locator;
1327
+ /**
1328
+ * All body cells under `column`. A cell spanning several rows is included once.
1329
+ *
1330
+ * @param column - The header key of the column, or its 0-based index (also works without header rows).
1331
+ * @returns A Playwright Locator for the cells.
1332
+ * @throws Error if column is a number that is not a non-negative integer.
1333
+ *
1334
+ * @example
1335
+ * await expect(table.column("Status")).toHaveText(["Active", "Inactive", "Active"]);
1336
+ * await expect(table.column(1)).toHaveText(["Active", "Inactive", "Active"]);
1337
+ */
1338
+ column(column: ColumnKey<H>): Locator;
1339
+ /**
1340
+ * The header cell element for `column` in the main header row.
1341
+ *
1342
+ * @param column - The header key of the column, or its 0-based index.
1343
+ * @returns A Playwright Locator for the header cell.
1344
+ * @throws Error if column is a number that is not a non-negative integer.
1345
+ *
1346
+ * @example
1347
+ * await table.header("Name").click(); // sort by name
1348
+ */
1349
+ header(column: ColumnKey<H>): Locator;
1350
+ /**
1351
+ * Reads the current header keys of the main header row, exactly as the engine resolves them
1352
+ * (including `__C`/`__D` suffixes and `{{Empty}}`). This is a snapshot, not a locator.
1353
+ *
1354
+ * @param options - Optional timeout for waiting until the table element is attached.
1355
+ * @returns A promise that resolves to the header keys, or an empty array if there are no header rows.
1356
+ *
1357
+ * @example
1358
+ * expect(await table.headerKeys()).toEqual(["Name", "Status", "Email"]);
1359
+ */
1360
+ headerKeys(options?: {
1361
+ timeout?: number;
1362
+ }): Promise<string[]>;
1363
+ /**
1364
+ * Reads every body row as an object keyed by header key, like `PlaywrightTable.getJson()`. Cells that
1365
+ * span rows or columns are repeated in every position they cover; positions without a cell are `""`.
1366
+ * Cell text is normalized as in row conditions: line breaks stay as one `\n` between trimmed lines.
1367
+ * With `raw: true`, cells are returned exactly as the browser reads them.
1368
+ *
1369
+ * This is a snapshot, not a locator: without `minRows` or `stable` it reads the table once and does not
1370
+ * wait for rows. With `minRows`, it first waits for a header row and at least that many body rows with
1371
+ * content (a row where at least one cell has text), so empty placeholder rows don't count. With
1372
+ * `stable`, it then waits until the table stops changing (see {@link waitForStable}). To wait for
1373
+ * specific content, assert first (for example with `toHaveRow` or `toHaveRows`).
1374
+ *
1375
+ * @param options - Optional settings. See {@link TableJsonOptions}.
1376
+ * @param options.columns - (Optional) Only these columns, in this order. Defaults to every column.
1377
+ * @param options.parse - (Optional) Typed values for some columns: `"number"`, `"date"` or a function.
1378
+ * @param options.decimal - (Optional) The decimal separator of `"number"` columns. Defaults to `"."`.
1379
+ * @param options.minRows - (Optional) Wait for at least this many body rows with content. Defaults to `0` (no waiting).
1380
+ * @param options.stable - (Optional) Wait until the table stops changing before reading it. Defaults to `false`.
1381
+ * @param options.timeout - (Optional) With `minRows` or `stable`, the time to wait in milliseconds (default 5000).
1382
+ * Without them, the time to wait for the table element to be attached.
1383
+ * @param options.raw - (Optional) Return the cell texts without normalization. Defaults to `false`.
1384
+ * @returns A promise that resolves to one object per body row, or an empty array if there are no body rows.
1385
+ * @throws Error if the table has no header row (without `minRows`).
1386
+ * @throws Error if a key of `columns` or `parse` is not a header key of the table, with the available headers.
1387
+ * @throws Error if a cell of a `parse` column cannot be parsed, with the row, the column and the text.
1388
+ * @throws Error if `minRows` is not reached, or the table doesn't become stable, within the timeout.
1389
+ * @throws Error if `minRows` is not a non-negative integer.
1390
+ *
1391
+ * @example
1392
+ * expect(await users.toJson()).toEqual([
1393
+ * { Name: "John", Status: "Active" },
1394
+ * { Name: "Jane", Status: "Inactive" },
1395
+ * ]);
1396
+ *
1397
+ * @example
1398
+ * // Wait until at least one row is loaded
1399
+ * const data = await users.toJson({ minRows: 1 });
1400
+ *
1401
+ * @example
1402
+ * // Some columns, with typed values: { Id: string; Total: number | null }[]
1403
+ * const totals = await orders.toJson({ columns: ["Id", "Total"], parse: { Total: "number" } });
1404
+ */
1405
+ toJson<const C extends HeaderKey<H> = HeaderKey<H>, const P extends ColumnParsers<C> = Record<never, never>>(options?: TableJsonOptions<C, P>): Promise<TableJsonRow<C, P>[]>;
1406
+ /**
1407
+ * Reads the text of every body cell under `column`, one per body row. A cell that spans rows is
1408
+ * repeated in every row it covers; a row without a cell there reads as `""`. Text is normalized as in
1409
+ * row conditions.
1410
+ *
1411
+ * This is a snapshot, not a locator: it reads the table once. To wait for content, assert first, or
1412
+ * use `toHaveColumnValues`.
1413
+ *
1414
+ * @param column - The header key of the column, or its 0-based index (also works without header rows).
1415
+ * @param options - Optional timeout for waiting until the table element is attached.
1416
+ * @returns A promise that resolves to the cell texts, or an empty array if there are no body rows.
1417
+ * @throws Error if the table has no such column, with the available headers.
1418
+ *
1419
+ * @example
1420
+ * expect(await users.columnValues("Status")).toEqual(["Active", "Inactive", "Active"]);
1421
+ *
1422
+ * @example
1423
+ * // The distinct values of a column
1424
+ * const statuses = new Set(await users.columnValues("Status"));
1425
+ */
1426
+ columnValues(column: ColumnKey<H>, options?: {
1427
+ timeout?: number;
1428
+ }): Promise<string[]>;
1429
+ /**
1430
+ * Waits until the table stops changing: the header keys and the text of every body cell stay the same
1431
+ * for `quiet` milliseconds, and the `loading` indicator (if any) is hidden. Use it after an action that
1432
+ * refreshes the table (searching, filtering, paging, sorting) before you read it with a snapshot method
1433
+ * such as {@link toJson}.
1434
+ *
1435
+ * Locators and the table matchers retry by themselves and don't need it. A table that has not started
1436
+ * to refresh yet looks stable, so pass `loading` when the table shows an indicator while it loads.
1437
+ *
1438
+ * @param options - Optional settings. See {@link StableOptions}.
1439
+ * @param options.quiet - (Optional) How long nothing may change, in milliseconds. Defaults to 500.
1440
+ * @param options.timeout - (Optional) The time to wait in milliseconds. Defaults to 5000.
1441
+ * @param options.loading - (Optional) A loading indicator that must be hidden. Defaults to the `loading` option of the table.
1442
+ * @returns A promise that resolves once the table is stable.
1443
+ * @throws Error if the table is not stable within the timeout: it kept changing, the loading indicator
1444
+ * stayed visible, or the table could not be read.
1445
+ *
1446
+ * @example
1447
+ * await page.getByLabel("Search").fill("Jo");
1448
+ * await users.waitForStable({ loading: page.locator(".spinner") });
1449
+ * const rows = await users.toJson();
1450
+ */
1451
+ waitForStable(options?: StableOptions): Promise<void>;
1452
+ /**
1453
+ * Finds a body row on any page or scroll position: it searches the rows in view, then (with the
1454
+ * `paginate` and `scroll` options) goes through the pages from the first one (with `paginate.first`)
1455
+ * and scrolls each page from the top, until a row matches. The table is left showing that row, and the
1456
+ * returned row locator resolves to it.
1457
+ *
1458
+ * Without `paginate` and `scroll`, it waits until a row in view matches, like a locator.
1459
+ *
1460
+ * @param where - At least one condition, as for {@link row}.
1461
+ * @param options - Optional timeout for the whole search, in milliseconds. Defaults to 30000.
1462
+ * @returns A promise that resolves to the {@link TableRow} for `row(where)`.
1463
+ * @throws Error if no row matches on any page (or within `paginate.maxPages` and `scroll.maxSteps`), with
1464
+ * the number of pages and scroll steps searched, and whether the search stopped early at the timeout.
1465
+ *
1466
+ * @example
1467
+ * const orders = defineTable(page.getByRole("grid"), {
1468
+ * aria: true,
1469
+ * paginate: { next: page.getByRole("button", { name: "Next page" }) },
1470
+ * });
1471
+ * const row = await orders.findRow({ Id: "1042" });
1472
+ * await row.cell("Status").click();
1473
+ */
1474
+ findRow<W extends RowWhere<H>>(where: W & ValidRowWhere<H, W>, options?: NavigationOptions): Promise<TableRow<H>>;
1475
+ /**
1476
+ * The number of body rows in the whole table, also the rows that are not rendered or are on other pages.
1477
+ *
1478
+ * - A grid with `aria-rowcount` (on the root, inside it or around it) declares it: `aria-rowcount` minus
1479
+ * the rows it counts that are not body rows, such as header rows.
1480
+ * - Otherwise, with the `paginate` or `scroll` option, every row is read, as `toJson({ allRows: true })` does.
1481
+ * That leaves the table on its last page, scrolled down.
1482
+ * - Otherwise, the number of body rows.
1483
+ *
1484
+ * @param options - Optional timeout, in milliseconds. Defaults to 30000.
1485
+ * @returns A promise that resolves to the number of body rows.
1486
+ * @throws Error if it reads every row and the timeout passes before the end of the table.
1487
+ *
1488
+ * @example
1489
+ * expect(await grid.totalRowCount()).toBe(200);
1490
+ */
1491
+ totalRowCount(options?: NavigationOptions): Promise<number>;
1492
+ /**
1493
+ * The body rows in view as an aligned text table, with the row index in front, for logs and debugging.
1494
+ * Cells longer than 30 characters are cut off, and line breaks show as `↵`.
1495
+ *
1496
+ * @param options - Optional settings.
1497
+ * @param options.columns - (Optional) Only these columns. Unknown columns are skipped; if none is known,
1498
+ * every column is shown. Defaults to every column.
1499
+ * @param options.maxRows - (Optional) The most rows to show. Defaults to 50.
1500
+ * @param options.timeout - (Optional) The time to wait for the table, in milliseconds.
1501
+ * @returns A promise that resolves to the table as text.
1502
+ *
1503
+ * @example
1504
+ * console.log(await users.format());
1505
+ * // # | Name | Status
1506
+ * // 0 | John | Active
1507
+ * // 1 | Jane | Inactive
1508
+ */
1509
+ format(options?: {
1510
+ columns?: readonly ColumnKey<H>[];
1511
+ maxRows?: number;
1512
+ timeout?: number;
1513
+ }): Promise<string>;
1514
+ /**
1515
+ * Waits until every expected header exists, and fails fast with the available headers if they don't.
1516
+ *
1517
+ * @param expected - The headers to check. Defaults to the `headers` option of this table.
1518
+ * @param options - Optional timeout in milliseconds. Defaults to 5000.
1519
+ * @returns A promise that resolves once all headers exist.
1520
+ * @throws Error listing the missing and available headers when the timeout is reached.
1521
+ * @throws Error if there are no headers to check.
1522
+ *
1523
+ * @example
1524
+ * const users = new TableLocator(page.locator("table"), { headers: ["Name", "Status"] });
1525
+ * await users.validateHeaders();
1526
+ * // Error: Missing headers: [Status]
1527
+ * // Available headers: [Name, State]
1528
+ */
1529
+ validateHeaders(expected?: readonly HeaderKey<H>[], options?: {
1530
+ timeout?: number;
1531
+ }): Promise<void>;
1532
+ /** The texts for `toJson()`: read once, or after waiting for `minRows` and `stable`. */
1533
+ private readJsonTexts;
1534
+ /**
1535
+ * The waits of `toJson()`: for `minRows` (with the texts it read), then until the table is `stable`,
1536
+ * each within the time `left`.
1537
+ */
1538
+ private waitForJson;
1539
+ /** @throws Error if the table has no header row, which `toJson()` needs for its keys. */
1540
+ private checkJsonHeader;
1541
+ /**
1542
+ * Checks the `columns` and `parse` options of `toJson()` against the header keys of the table.
1543
+ * @throws Error with the available headers if a key is not a header key, or a `parse` key is not in `columns`.
1544
+ */
1545
+ private checkJsonColumns;
1546
+ /** The loading indicator for `loading`, or else the `loading` option of the table. */
1547
+ private resolveLoading;
1548
+ /** A fingerprint of the header keys and body cell texts, and the number of body rows. */
1549
+ private fingerprint;
1550
+ /**
1551
+ * Reads every row on every page and scroll position. Rows are told apart by their key per page, and
1552
+ * ordered by their index when every row has one. Header keys are merged across positions, so a grid
1553
+ * that renders only some columns at a time still gets every column it showed.
1554
+ *
1555
+ * @throws Error if the deadline passes before every row is read (up to `paginate.maxPages` and
1556
+ * `scroll.maxSteps`), so part of the rows is never returned as all of them.
1557
+ */
1558
+ private readAllTexts;
1559
+ private readTexts;
1560
+ /** Reads the table once it has a header row and at least `minRows` body rows with content. */
1561
+ private readTextsWithRows;
1562
+ private selector;
484
1563
  }
485
-
486
- export { type BodyRow, type Cell, CellContentType, type HeaderRow, type HeaderRowOptions, PlaywrightTable, type PollingOptions, type TableOptions };
1564
+ /**
1565
+ * Creates a {@link TableLocator}. Header names passed in `options.headers` are inferred as literal
1566
+ * types, so header arguments are checked and autocompleted.
1567
+ *
1568
+ * @param root - The Playwright `Locator` pointing to the root table element.
1569
+ * @param options - Optional configuration. See {@link TableLocatorOptions}.
1570
+ * @returns A new {@link TableLocator}.
1571
+ *
1572
+ * @example
1573
+ * const users = defineTable(page.locator("#users"), { headers: ["Name", "Status", "Email"] });
1574
+ * await expect(users.row({ Name: "John" }).cell("Email")).toHaveText("john@example.com");
1575
+ * // @ts-expect-error "Nme" is not a header of this table
1576
+ * users.row({ Nme: "John" });
1577
+ */
1578
+ export declare function defineTable<const H extends string = string>(root: Locator, options?: TableLocatorOptions<H>): TableLocator<H>;
1579
+ /**
1580
+ * {@link TableLocatorOptions} without `headers`, so a preset can be spread into options with your own
1581
+ * typed headers.
1582
+ */
1583
+ type TablePreset = Omit<TableLocatorOptions<string>, "headers">;
1584
+ /**
1585
+ * Options for AG Grid (tested with AG Grid Community 36): ARIA roles, the header rows without the
1586
+ * floating filter row, the pinned top and bottom rows (such as totals) as footer rows, the body viewport to scroll, the
1587
+ * pagination panel, and the loading overlay. The table root is the element you created the grid in, or
1588
+ * any element inside it that holds the whole grid.
1589
+ *
1590
+ * AG Grid also renders only the columns in view (column virtualization). Rows are read with every
1591
+ * column AG Grid has rendered; to read columns outside the view, turn it off with
1592
+ * `suppressColumnVirtualisation: true`, or make the grid wide enough in your tests.
1593
+ *
1594
+ * @example
1595
+ * const cars = defineTable(page.locator("#myGrid"), { ...presets.agGrid(), headers: ["Make", "Model", "Price"] });
1596
+ * const row = await cars.findRow({ Model: "Model 150" });
1597
+ * await expect(cars).toHaveTotalRowCount(200);
1598
+ */
1599
+ declare function agGrid(): TablePreset;
1600
+ /**
1601
+ * Options for MUI X Data Grid (based on the markup of version 7 and 8): ARIA roles, the row and cell
1602
+ * classes (without filler and skeleton cells), the virtual scroller, the pagination buttons, and the
1603
+ * loading indicator. The table root is the `.MuiDataGrid-root` element, such as `page.getByRole("grid")`.
1604
+ *
1605
+ * @example
1606
+ * const users = defineTable(page.getByRole("grid"), presets.muiDataGrid());
1607
+ * await expect(users).toHaveTotalRowCount(100);
1608
+ */
1609
+ declare function muiDataGrid(): TablePreset;
1610
+ /**
1611
+ * Options for an Angular Material table (`mat-table`, as a native `<table>` or with the `<mat-table>`
1612
+ * elements), in both the MDC (`mat-mdc-*`) and the legacy class names. The "no data" row is not a body
1613
+ * row, and footer rows are footer rows. With `paginator`, the `mat-paginator` that pages the table: a
1614
+ * `Locator`, or a function that finds it from the table root.
1615
+ *
1616
+ * @example
1617
+ * const orders = defineTable(page.locator("table[mat-table]"), presets.angularMaterial({ paginator: page.locator("mat-paginator") }));
1618
+ * const row = await orders.findRow({ Id: "42" });
1619
+ */
1620
+ declare function angularMaterial(options?: {
1621
+ paginator?: LocatorRef;
1622
+ }): TablePreset;
1623
+ /**
1624
+ * Options for a table virtualized with TanStack Virtual (`@tanstack/react-virtual` and friends), which
1625
+ * renders only the rows in view and marks each with `data-index`. TanStack Table itself renders plain
1626
+ * HTML tables, which work without a preset.
1627
+ *
1628
+ * @param options.container - The element that scrolls, usually a `div` around the table.
1629
+ *
1630
+ * @example
1631
+ * const rows = defineTable(page.locator("table"), presets.tanstackVirtual({ container: page.locator(".scroll-container") }));
1632
+ * const all = await rows.toJson({ allRows: true });
1633
+ */
1634
+ declare function tanstackVirtual(options: {
1635
+ container: Locator;
1636
+ }): TablePreset;
1637
+ /**
1638
+ * Ready-made {@link TableLocatorOptions} for popular data grids. Spread a preset into your own options to
1639
+ * add typed headers or change a setting.
1640
+ *
1641
+ * @example
1642
+ * const grid = defineTable(page.locator("#grid"), { ...presets.agGrid(), headers: ["Id", "Name"] });
1643
+ */
1644
+ export declare const presets: {
1645
+ agGrid: typeof agGrid;
1646
+ muiDataGrid: typeof muiDataGrid;
1647
+ angularMaterial: typeof angularMaterial;
1648
+ tanstackVirtual: typeof tanstackVirtual;
1649
+ };
1650
+ /**
1651
+ * Registers the `pw-table-v1` selector engine used by {@link TableLocator}.
1652
+ *
1653
+ * Safe to call more than once: later calls reuse the first registration, and an
1654
+ * "already registered" error (for example from another copy of this package) is ignored.
1655
+ *
1656
+ * Registration must happen **before the page is created**. You don't need this function if you
1657
+ * import `test` from this package, which registers the engine with a worker-scoped auto fixture.
1658
+ *
1659
+ * @param target - The Playwright `selectors` object to register on. Defaults to `selectors` from `@playwright/test`.
1660
+ * @returns A promise that resolves once the engine is registered.
1661
+ *
1662
+ * @example
1663
+ * // With your own test fixtures
1664
+ * import { test as base } from "@playwright/test";
1665
+ * import { registerTableSelectors } from "@cerios/playwright-table";
1666
+ *
1667
+ * export const test = base.extend<{}, { tableSelectors: void }>({
1668
+ * tableSelectors: [async ({ playwright }, use) => {
1669
+ * await registerTableSelectors(playwright.selectors);
1670
+ * await use();
1671
+ * }, { scope: "worker", auto: true }],
1672
+ * });
1673
+ *
1674
+ * @example
1675
+ * // Without fixtures
1676
+ * test.beforeAll(async () => {
1677
+ * await registerTableSelectors();
1678
+ * });
1679
+ */
1680
+ export declare function registerTableSelectors(target?: Selectors): Promise<void>;
1681
+ /**
1682
+ * Playwright `test` with the `pw-table-v1` selector engine registered for every worker, so
1683
+ * {@link TableLocator} works without any setup. Use it instead of `test` from `@playwright/test`.
1684
+ *
1685
+ * The engine is registered by a worker-scoped auto fixture, so it is always registered before
1686
+ * any page is created.
1687
+ *
1688
+ * @example
1689
+ * import { test, expect, defineTable } from "@cerios/playwright-table";
1690
+ *
1691
+ * test("shows the user", async ({ page }) => {
1692
+ * const users = defineTable(page.locator("table"), { headers: ["Name", "Status"] });
1693
+ * await expect(users.row({ Name: "John" }).cell("Status")).toHaveText("Active");
1694
+ * });
1695
+ */
1696
+ export declare const test: TestType<PlaywrightTestArgs & PlaywrightTestOptions, PlaywrightWorkerArgs & PlaywrightWorkerOptions & {
1697
+ _pwTableSelectors: void;
1698
+ }>;
1699
+ /**
1700
+ * The default `ignore` option of {@link TableLocator}: CSS selectors of the elements inside a cell whose
1701
+ * text is left out. They cover screen reader only text (`.sr-only`, `.visually-hidden`), ligature icons
1702
+ * (Material Icons and Material Symbols, whose text such as `arrow_upward` is drawn as an icon) and
1703
+ * decorative content (`[aria-hidden=true]`).
1704
+ *
1705
+ * Spread it to add your own selectors: `ignore: [...DEFAULT_IGNORE, ".sort-icon"]`.
1706
+ */
1707
+ export declare const DEFAULT_IGNORE: readonly string[];
1708
+ /**
1709
+ * What a matcher returns to `expect.extend`. Declared here instead of importing `MatcherReturnType`,
1710
+ * which older versions of `@playwright/test` within the peer range don't export.
1711
+ */
1712
+ type MatcherResult = {
1713
+ name: string;
1714
+ pass: boolean;
1715
+ message: () => string;
1716
+ expected?: unknown;
1717
+ actual?: unknown;
1718
+ timeout?: number;
1719
+ };
1720
+ /** Options shared by the table matchers. */
1721
+ type TableMatcherOptions = {
1722
+ /** Time to retry the assertion for, in milliseconds. Defaults to the `expect` timeout. */
1723
+ timeout?: number;
1724
+ };
1725
+ /** Options for the `toBeSortedBy` matcher. */
1726
+ type TableSortOptions = TableMatcherOptions & {
1727
+ /** The expected order. Defaults to `"asc"`. */
1728
+ order?: "asc" | "desc";
1729
+ /**
1730
+ * How cell texts are compared. Defaults to `"text"`.
1731
+ * - `"text"`: case- and accent-insensitive, with digit runs compared as numbers (`"item 9"` before
1732
+ * `"item 10"`).
1733
+ * - `"number"`: the one number in the text, read with the `decimal` separator (`"€ 1,200"` is 1200).
1734
+ * See {@link DecimalSeparator} for the rules.
1735
+ * - `"date"`: parsed with `Date.parse`.
1736
+ */
1737
+ type?: "text" | "number" | "date";
1738
+ /** The decimal separator for `type: "number"`. Defaults to `"."`. */
1739
+ decimal?: DecimalSeparator;
1740
+ /**
1741
+ * Custom comparison of two cell texts, like `Array.sort`. Runs in Node, so it can use test variables.
1742
+ * Overrides `type`.
1743
+ */
1744
+ compare?: (a: string, b: string) => number;
1745
+ /** Also require `aria-sort="ascending"` or `"descending"` on the header cell. Defaults to `false`. */
1746
+ ariaSort?: boolean;
1747
+ };
1748
+ /** Options for the `toHaveRows` and `toHaveColumnValues` matchers. */
1749
+ type TableRowsOptions = TableMatcherOptions & {
1750
+ /**
1751
+ * Accept the rows (or values) in any order: each expected entry must match a different body row.
1752
+ * Defaults to `false`: body row `i` must match entry `i`.
1753
+ */
1754
+ anyOrder?: boolean;
1755
+ };
1756
+ /**
1757
+ * Asserts that the table has at least one body row matching every condition (or none, with `.not`).
1758
+ * Retries until the timeout. On failure, unknown headers are reported with the available headers.
1759
+ *
1760
+ * Conditions are checked against header names at runtime. With the `expect` of this package, they are
1761
+ * also checked at compile time against the `headers` of the table.
1762
+ */
1763
+ declare function toHaveRow(this: ExpectMatcherState, table: TableLocator, where: RowWhere<string>, options?: TableMatcherOptions): Promise<MatcherResult>;
1764
+ /**
1765
+ * Asserts that the table has exactly `count` body rows. Retries until the timeout.
1766
+ */
1767
+ declare function toHaveRowCount(this: ExpectMatcherState, table: TableLocator, count: number, options?: TableMatcherOptions): Promise<MatcherResult>;
1768
+ /**
1769
+ * Asserts that the main header row contains every given header key, or with `exact: true`, that it
1770
+ * equals the given keys in order. Retries until the timeout.
1771
+ */
1772
+ declare function toHaveHeaders(this: ExpectMatcherState, table: TableLocator, headers: readonly string[], options?: TableMatcherOptions & {
1773
+ /** Require exactly these header keys, in this order. Defaults to `false`. */
1774
+ exact?: boolean;
1775
+ }): Promise<MatcherResult>;
1776
+ /**
1777
+ * Asserts that a {@link TableRow} resolves to exactly one row whose cells match every condition.
1778
+ * With `.not`, the row must exist and not match. Retries until the timeout.
1779
+ *
1780
+ * @example
1781
+ * await expect(users.row({ Name: "John" })).toHaveCells({ Status: "Active", Email: /@example\.com$/ });
1782
+ * await expect(users.rowAt(0)).not.toHaveCells({ Status: "Deleted" });
1783
+ */
1784
+ declare function toHaveCells(this: ExpectMatcherState, row: TableRow, cells: RowWhere<string>, options?: TableMatcherOptions): Promise<MatcherResult>;
1785
+ /**
1786
+ * Asserts that the table has exactly as many body rows as `rows`, and that body row `i` matches every
1787
+ * condition of `rows[i]`. Headers that a row does not mention are not checked, so `{}` matches any row.
1788
+ * With `anyOrder: true`, the rows may come in any order: each entry must match a different body row.
1789
+ * With `.not`, the table must not match. Retries until the timeout.
1790
+ *
1791
+ * @example
1792
+ * await expect(users).toHaveRows([
1793
+ * { Name: "John", Status: "Active" },
1794
+ * { Name: "Jane", Status: /inactive/i },
1795
+ * ]);
1796
+ */
1797
+ declare function toHaveRows(this: ExpectMatcherState, table: TableLocator, rows: readonly RowWhere<string>[], options?: TableRowsOptions): Promise<MatcherResult>;
1798
+ /**
1799
+ * Asserts that every entry of `rows` matches a different body row, in any order; other body rows are
1800
+ * allowed. With `.not`, at least one entry has no body row of its own. Retries until the timeout.
1801
+ *
1802
+ * @example
1803
+ * await expect(users).toContainRows([
1804
+ * { Name: "Joe", Status: "Active" },
1805
+ * { Name: "John" },
1806
+ * ]);
1807
+ */
1808
+ declare function toContainRows(this: ExpectMatcherState, table: TableLocator, rows: readonly RowWhere<string>[], options?: TableMatcherOptions): Promise<MatcherResult>;
1809
+ /**
1810
+ * Asserts that the table has body rows and that every one of them matches `where`, for example after
1811
+ * filtering. With `.not`, at least one body row doesn't match. A table without body rows fails both,
1812
+ * so a filter that leaves nothing never passes. Retries until the timeout.
1813
+ *
1814
+ * @example
1815
+ * await page.getByLabel("Status").selectOption("Active");
1816
+ * await expect(users).toHaveEveryRow({ Status: "Active" });
1817
+ */
1818
+ declare function toHaveEveryRow(this: ExpectMatcherState, table: TableLocator, where: RowWhere<string>, options?: TableMatcherOptions): Promise<MatcherResult>;
1819
+ /**
1820
+ * Asserts that `column` has exactly these cell values, one per body row: body row `i` matches `values[i]`,
1821
+ * or with `anyOrder: true`, each value matches a different body row. Values are an exact string, a RegExp
1822
+ * or a {@link CellCondition}, compared as in row conditions. With `.not`, the column must not match.
1823
+ * Retries until the timeout.
1824
+ *
1825
+ * @example
1826
+ * await expect(users).toHaveColumnValues("Status", ["Active", "Inactive", "Active"]);
1827
+ * await expect(orders).toHaveColumnValues("Total", [{ gt: 0 }, { gt: 0 }], { anyOrder: true });
1828
+ */
1829
+ declare function toHaveColumnValues(this: ExpectMatcherState, table: TableLocator, column: string | number, values: readonly CellMatcher[], options?: TableRowsOptions): Promise<MatcherResult>;
1830
+ /**
1831
+ * Asserts that the body rows are sorted by `column`. Values are compared in Node, so `compare` can use
1832
+ * variables from the test. An empty body, an unknown column or a value that cannot be parsed fails the
1833
+ * assertion, also with `.not`. Retries until the timeout.
1834
+ *
1835
+ * @example
1836
+ * await users.header("Age").click();
1837
+ * await expect(users).toBeSortedBy("Age", { type: "number" });
1838
+ * await expect(users).toBeSortedBy("Name", { order: "desc", ariaSort: true });
1839
+ */
1840
+ declare function toBeSortedBy(this: ExpectMatcherState, table: TableLocator, column: string | number, options?: TableSortOptions): Promise<MatcherResult>;
1841
+ /**
1842
+ * Asserts the number of body rows in the whole table, also the rows that are not rendered or are on
1843
+ * other pages (see {@link TableLocator.totalRowCount}). Retries until the timeout.
1844
+ *
1845
+ * @example
1846
+ * await expect(grid).toHaveTotalRowCount(200);
1847
+ */
1848
+ declare function toHaveTotalRowCount(this: ExpectMatcherState, table: TableLocator, count: number, options?: TableMatcherOptions): Promise<MatcherResult>;
1849
+ /**
1850
+ * Asserts that a {@link TableRow} is selected: it has `aria-selected="true"`, or the first checkbox in it
1851
+ * is checked. Retries until the timeout.
1852
+ *
1853
+ * @example
1854
+ * await users.row({ Name: "John" }).select();
1855
+ * await expect(users.row({ Name: "John" })).toBeSelected();
1856
+ */
1857
+ declare function toBeSelected(this: ExpectMatcherState, row: TableRow, options?: TableMatcherOptions): Promise<MatcherResult>;
1858
+ /**
1859
+ * Asserts that a {@link TableRow} of a tree grid is expanded (see {@link TableRow.isExpanded}). A row that
1860
+ * cannot expand fails both the assertion and its negation. Retries until the timeout.
1861
+ *
1862
+ * @example
1863
+ * await expect(files.row({ Name: "src" })).not.toBeExpanded();
1864
+ */
1865
+ declare function toBeExpanded(this: ExpectMatcherState, row: TableRow, options?: TableMatcherOptions): Promise<MatcherResult>;
1866
+ /**
1867
+ * Custom `expect` matchers for {@link TableLocator} and {@link TableRow}. Use them to extend your own `expect`.
1868
+ *
1869
+ * - `toHaveRow(where, options?)`: at least one body row matches every condition.
1870
+ * - `toHaveRowCount(count, options?)`: exactly `count` body rows.
1871
+ * - `toHaveRows(rows, options?)`: exactly these body rows, in order (or `anyOrder`), each matching its conditions.
1872
+ * - `toContainRows(rows, options?)`: each entry matches a different body row, in any order; other rows are allowed.
1873
+ * - `toHaveEveryRow(where, options?)`: there are body rows, and every one matches the conditions.
1874
+ * - `toHaveColumnValues(column, values, options?)`: one value per body row, in order (or `anyOrder`).
1875
+ * - `toHaveHeaders(headers, options?)`: the main header row contains these header keys (`exact` for an ordered match).
1876
+ * - `toBeSortedBy(column, options?)`: the body rows are sorted by a column (see {@link TableSortOptions}).
1877
+ * - `toHaveTotalRowCount(count, options?)`: the number of body rows in the whole table, on every page.
1878
+ * - `toHaveCells(cells, options?)` on a {@link TableRow}: exactly one row, whose cells match every condition.
1879
+ * - `toBeSelected(options?)` on a {@link TableRow}: the row is selected.
1880
+ * - `toBeExpanded(options?)` on a {@link TableRow}: the row of a tree grid is expanded.
1881
+ *
1882
+ * When a table matcher fails inside a test, the body rows are attached to the test report as a Markdown
1883
+ * table, once per test and table.
1884
+ *
1885
+ * @example
1886
+ * // Combine with your own matchers
1887
+ * import { expect as base } from "@playwright/test";
1888
+ * import { tableMatchers } from "@cerios/playwright-table";
1889
+ *
1890
+ * export const expect = base.extend({
1891
+ * ...tableMatchers,
1892
+ * async toHaveEmailFor(table: TableLocator, name: string) { ... },
1893
+ * });
1894
+ *
1895
+ * @example
1896
+ * // Or merge with another extended expect
1897
+ * import { mergeExpects } from "@playwright/test";
1898
+ * import { expect as tableExpect } from "@cerios/playwright-table";
1899
+ *
1900
+ * export const expect = mergeExpects(tableExpect, myExpect);
1901
+ */
1902
+ export declare const tableMatchers: {
1903
+ toHaveRow: typeof toHaveRow;
1904
+ toHaveRowCount: typeof toHaveRowCount;
1905
+ toHaveRows: typeof toHaveRows;
1906
+ toContainRows: typeof toContainRows;
1907
+ toHaveEveryRow: typeof toHaveEveryRow;
1908
+ toHaveColumnValues: typeof toHaveColumnValues;
1909
+ toHaveHeaders: typeof toHaveHeaders;
1910
+ toHaveCells: typeof toHaveCells;
1911
+ toBeSortedBy: typeof toBeSortedBy;
1912
+ toHaveTotalRowCount: typeof toHaveTotalRowCount;
1913
+ toBeSelected: typeof toBeSelected;
1914
+ toBeExpanded: typeof toBeExpanded;
1915
+ };
1916
+ /**
1917
+ * Playwright `expect` extended with {@link tableMatchers}.
1918
+ *
1919
+ * @example
1920
+ * import { test, expect, defineTable } from "@cerios/playwright-table";
1921
+ *
1922
+ * test("users", async ({ page }) => {
1923
+ * const users = defineTable(page.locator("table"), { headers: ["Name", "Status"] });
1924
+ * await expect(users).toHaveHeaders(["Name", "Status"]);
1925
+ * await expect(users).toHaveRow({ Name: "John", Status: /active/i });
1926
+ * await expect(users).not.toHaveRow({ Name: "Ghost" });
1927
+ * await expect(users).toHaveRowCount(3);
1928
+ * await expect(users).toHaveRows([{ Name: "John" }, { Name: "Jane" }, { Name: "Joe" }]);
1929
+ * await expect(users).toContainRows([{ Name: "Joe" }, { Name: "John" }]);
1930
+ * await expect(users).toHaveColumnValues("Status", ["Active", "Inactive", "Active"]);
1931
+ * await users.header("Name").click();
1932
+ * await expect(users).toBeSortedBy("Name");
1933
+ * await expect(users.row({ Name: "John" })).toHaveCells({ Status: "Active" });
1934
+ * });
1935
+ */
1936
+ declare const tableExpect: Expect<typeof tableMatchers>;
1937
+ /** The matchers Playwright's `expect` (extended with {@link tableMatchers}) has for a value of type `T`. */
1938
+ type BaseAssertions<T> = ReturnType<typeof tableExpect<T>>;
1939
+ /** `Base` with the matchers of `Typed` replaced by their typed versions, also after `.not`. */
1940
+ type WithTyped<Base, Typed> = Omit<Base, keyof Typed | "not"> & Typed & {
1941
+ not: Omit<Base, keyof Typed | "not"> & Typed;
1942
+ };
1943
+ /**
1944
+ * The table matchers of {@link expect} on a {@link TableLocator} with typed headers: header keys in
1945
+ * conditions and columns are checked at compile time, like in {@link TableLocator.row}.
1946
+ */
1947
+ interface TableAssertions<H extends string> {
1948
+ toHaveRow<W extends RowWhere<H>>(where: W & ValidRowWhere<H, W>, options?: TableMatcherOptions): Promise<void>;
1949
+ toHaveRowCount(count: number, options?: TableMatcherOptions): Promise<void>;
1950
+ toHaveRows(rows: readonly RowWhere<H>[], options?: TableRowsOptions): Promise<void>;
1951
+ toContainRows(rows: readonly RowWhere<H>[], options?: TableMatcherOptions): Promise<void>;
1952
+ toHaveEveryRow<W extends RowWhere<H>>(where: W & ValidRowWhere<H, W>, options?: TableMatcherOptions): Promise<void>;
1953
+ toHaveColumnValues(column: ColumnKey<H>, values: readonly CellMatcher[], options?: TableRowsOptions): Promise<void>;
1954
+ /** Header keys are not checked: a header that the table doesn't declare may be asserted absent. */
1955
+ toHaveHeaders(headers: readonly string[], options?: TableMatcherOptions & {
1956
+ exact?: boolean;
1957
+ }): Promise<void>;
1958
+ toBeSortedBy(column: ColumnKey<H>, options?: TableSortOptions): Promise<void>;
1959
+ toHaveTotalRowCount(count: number, options?: TableMatcherOptions): Promise<void>;
1960
+ }
1961
+ /** The row matchers of {@link expect} on a {@link TableRow} with typed headers. */
1962
+ interface RowAssertions<H extends string> {
1963
+ toHaveCells<W extends RowWhere<H>>(cells: W & ValidRowWhere<H, W>, options?: TableMatcherOptions): Promise<void>;
1964
+ toBeSelected(options?: TableMatcherOptions): Promise<void>;
1965
+ toBeExpanded(options?: TableMatcherOptions): Promise<void>;
1966
+ }
1967
+ /**
1968
+ * The type of {@link expect}: Playwright's `expect` extended with {@link tableMatchers}, whose table and
1969
+ * row matchers check header keys at compile time (also with `expect.soft`).
1970
+ */
1971
+ type TableExpect = {
1972
+ <H extends string>(actual: TableLocator<H>, messageOrOptions?: string | {
1973
+ message?: string;
1974
+ }): WithTyped<BaseAssertions<TableLocator<H>>, TableAssertions<H>>;
1975
+ <H extends string>(actual: TableRow<H>, messageOrOptions?: string | {
1976
+ message?: string;
1977
+ }): WithTyped<BaseAssertions<TableRow<H>>, RowAssertions<H>>;
1978
+ soft: TableExpect;
1979
+ } & Expect<typeof tableMatchers>;
1980
+ export declare const expect: TableExpect;
1981
+ export type { CellCondition, CellMatcher, ColumnKey, ColumnParser, ColumnParsers, DecimalSeparator, ElementDescriptor, ElementSelector, HeaderKey, HeaderRowOptions, LocatorRef, NavigationOptions, NumberCondition, PaginateOptions, ParsedValue, PollingOptions, RowAssertions, RowWhere, ScrollOptions, StableOptions, TableAssertions, TableExpect, TableJsonOptions, TableJsonRow, TableLocatorOptions, TableMatcherOptions, TablePreset, TableRowsOptions, TableSortOptions, ValidRowWhere };
1982
+ //# sourceMappingURL=index.d.ts.map