@cerios/playwright-table 1.1.1 → 2.0.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,14 +1,58 @@
1
1
  import { Locator } from '@playwright/test';
2
2
 
3
- type Cell = string | boolean | number;
4
- type BodyRow = Cell[];
5
- type HeaderRow = string[];
6
-
3
+ /**
4
+ * Enum representing the possible types of content that can be extracted from a table cell.
5
+ *
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.
8
+ */
7
9
  declare enum CellContentType {
8
10
  TextContent = "textContent",
9
11
  InnerText = "innerText"
10
12
  }
11
13
 
14
+ type PollingOptions = {
15
+ timeout?: number;
16
+ interval?: number;
17
+ };
18
+
19
+ /**
20
+ * Represents a single cell value within a table row.
21
+ *
22
+ * @remarks
23
+ * A `Cell` is a string that holds the textual content of a table cell,
24
+ * whether in the header or body of the table.
25
+ */
26
+ type Cell = string;
27
+ /**
28
+ * Represents a row in the table body, defined as an array of `Cell` objects.
29
+ *
30
+ * @remarks
31
+ * Each `BodyRow` corresponds to a single row in the table's body section.
32
+ *
33
+ * @see Cell
34
+ */
35
+ type BodyRow = Cell[];
36
+ /**
37
+ * Represents a row in the table header, defined as an array of `Cell` objects.
38
+ *
39
+ * @remarks
40
+ * Each `HeaderRow` corresponds to a single row in the table's header section.
41
+ *
42
+ * @see Cell
43
+ */
44
+ type HeaderRow = Cell[];
45
+
46
+ /**
47
+ * Options for configuring a table header row.
48
+ *
49
+ * @property cellContentType - The type of content allowed in the header cell.
50
+ * @property emptyCellReplacement - If true, replaces empty header cells with a default value.
51
+ * @property duplicateSuffix - If true, appends a suffix to duplicate header values.
52
+ * @property colspan - Configuration for column spanning in the header row.
53
+ * @property colspan.enabled - If true, enables column spanning.
54
+ * @property colspan.suffix - If true, appends a suffix to spanned columns.
55
+ */
12
56
  type HeaderRowOptions = {
13
57
  cellContentType?: CellContentType;
14
58
  emptyCellReplacement?: boolean;
@@ -19,15 +63,67 @@ type HeaderRowOptions = {
19
63
  };
20
64
  };
21
65
 
66
+ /**
67
+ * Options for loading table data, headers, and rows.
68
+ *
69
+ * @example
70
+ * // Use custom cell content type for body rows and header options
71
+ * const options: TableOptions = {
72
+ * bodyRowOptions: { cellContentType: CellContentType.TextContent },
73
+ * headerRowOptions: {
74
+ * colspan: { enabled: true, suffix: true },
75
+ * duplicateSuffix: true,
76
+ * emptyCellReplacement: false,
77
+ * },
78
+ * };
79
+ * await table.getBodyRows(options);
80
+ */
81
+ type TableOptions = {
82
+ /** See {@link HeaderRowOptions} for available options. */
83
+ headerRowOptions?: HeaderRowOptions;
84
+ /** Options for parsing body rows. */
85
+ bodyRowOptions?: {
86
+ cellContentType?: CellContentType;
87
+ };
88
+ };
89
+ /**
90
+ * Represents a table in Playwright, providing methods to interact with its headers, rows, and cells.
91
+ */
22
92
  declare class PlaywrightTable {
23
93
  private readonly _tableLocator;
24
94
  private readonly _options?;
25
- private _headers;
26
- private _rows;
27
95
  private _bodyRowLocator;
28
96
  private _bodyRowColumnSelector;
29
97
  private _headerRowLocator;
30
98
  private _headerColumnSelector;
99
+ /**
100
+ * Creates an instance of the table handler with customizable selectors for headers and rows.
101
+ *
102
+ * @param _tableLocator - The Playwright `Locator` pointing to the root table element.
103
+ * @param _options - Optional configuration for customizing header and row selectors.
104
+ * @param _options.header - Options for header row and columns.
105
+ * @param _options.header.setMainHeaderRow - (Optional) Index of the main header row if multiple header rows exist.
106
+ * @param _options.header.rowSelector - (Optional) CSS selector for locating header rows. Defaults to `"thead>tr"`.
107
+ * @param _options.header.columnSelector - (Optional) CSS selector for locating header columns. Defaults to `"th"`.
108
+ * @param _options.row - Options for body rows and columns.
109
+ * @param _options.row.rowSelector - (Optional) CSS selector for locating body rows. Defaults to `"tbody>tr"`.
110
+ * @param _options.row.columnSelector - (Optional) CSS selector for locating body columns. Defaults to `"td"`.
111
+ *
112
+ * @example
113
+ * const table = new PlaywrightTable(
114
+ * page.locator('table.my-table'),
115
+ * {
116
+ * header: {
117
+ * rowSelector: 'thead tr.header',
118
+ * columnSelector: 'th.header-cell'
119
+ * },
120
+ * row: {
121
+ * rowSelector: 'tbody tr.data-row',
122
+ * columnSelector: 'td.data-cell'
123
+ * }
124
+ * }
125
+ * );
126
+ */
31
127
  constructor(_tableLocator: Locator, _options?: {
32
128
  header?: {
33
129
  setMainHeaderRow?: number;
@@ -39,53 +135,337 @@ declare class PlaywrightTable {
39
135
  columnSelector?: string;
40
136
  };
41
137
  } | undefined);
42
- getHeaderRows(options?: {
43
- timeout?: number;
44
- duplicateSuffix?: boolean;
45
- headerRowOptions?: HeaderRowOptions;
46
- }): Promise<HeaderRow[]>;
47
- getMainHeaderRow(options?: {
48
- timeout?: number;
49
- duplicateSuffix?: boolean;
50
- headerRowOptions?: HeaderRowOptions;
51
- }): Promise<HeaderRow>;
138
+ /**
139
+ * Retrieves all header rows of the table.
140
+ * @param options - Optional parameters for header row parsing configuration.
141
+ * @returns A promise that resolves to an array of header rows.
142
+ * @throws Error if no header rows found after table loads.
143
+ *
144
+ * @example
145
+ * // Get headers with colspan enabled
146
+ * const headers = await table.getHeaderRows({
147
+ * colspan: { enabled: true, suffix: true },
148
+ * duplicateSuffix: true
149
+ * });
150
+ *
151
+ * @see {@link getMainHeaderRow} for getting only the main header row
152
+ * @see {@link HeaderRowOptions} for all available configuration options
153
+ */
154
+ getHeaderRows(options?: HeaderRowOptions): Promise<HeaderRow[]>;
155
+ /**
156
+ * Retrieves the main header row of the table.
157
+ * By default, uses the last header row if multiple exist. Can be customized via constructor.
158
+ * @param options - Optional parameters for header row parsing configuration.
159
+ * @returns A promise that resolves to the main header row.
160
+ * @throws Error if no header rows available.
161
+ * @throws Error if setMainHeaderRow index is out of bounds (negative or >= header row count).
162
+ *
163
+ * @example
164
+ * // Get main header with custom index set in constructor
165
+ * const table = new PlaywrightTable(page.locator('table'), {
166
+ * header: { setMainHeaderRow: 0 } // Use first row as main header
167
+ * });
168
+ * const mainHeader = await table.getMainHeaderRow();
169
+ *
170
+ * @see {@link getHeaderRows} for getting all header rows
171
+ */
172
+ getMainHeaderRow(options?: HeaderRowOptions): Promise<HeaderRow>;
173
+ /**
174
+ * Retrieves all body rows of the table.
175
+ * @param options - Optional configuration for body row parsing.
176
+ * @param options.cellContentType - Content extraction type. See {@link CellContentType}. Defaults to InnerText.
177
+ * @returns A promise that resolves to an array of body rows.
178
+ * @throws Error if no body rows found after table loads.
179
+ *
180
+ * @example
181
+ * // Get body rows with TextContent (includes hidden elements)
182
+ * const rows = await table.getBodyRows({
183
+ * cellContentType: CellContentType.TextContent
184
+ * });
185
+ *
186
+ * @example
187
+ * // Get body rows with default InnerText
188
+ * const rows = await table.getBodyRows();
189
+ */
52
190
  getBodyRows(options?: {
53
- timeout?: number;
191
+ cellContentType?: CellContentType;
54
192
  }): Promise<BodyRow[]>;
193
+ /**
194
+ * Retrieves a specific cell locator in the body of the table by row and column index.
195
+ * @param rowNumber - The row number (0-based index).
196
+ * @param headerPosition - The column/header position (0-based index).
197
+ * @returns A Playwright Locator for the specified cell.
198
+ *
199
+ * @example
200
+ * // Get cell at row 1, column 2
201
+ * const cellLocator = table.getBodyCellLocator(1, 2);
202
+ * await expect(cellLocator).toHaveText('Expected Text');
203
+ *
204
+ * @example
205
+ * // Get first cell in first row
206
+ * const firstCell = table.getBodyCellLocator(0, 0);
207
+ * await firstCell.click();
208
+ *
209
+ * @see {@link getBodyCellLocatorByRowConditions} for finding cells by content matching
210
+ * @see {@link getAllBodyCellLocatorsByHeaderIndex} for getting all cells in a column
211
+ * @see {@link getAllBodyCellLocatorsByHeaderName} for getting all cells under a header
212
+ */
55
213
  getBodyCellLocator(rowNumber: number, headerPosition: number): Locator;
56
- getBodyCellLocatorByRowConditions(conditions: Record<string, string>, targetHeader: string, options?: {
57
- timeout?: number;
58
- duplicateSuffix?: boolean;
59
- colspanEnabled?: boolean;
60
- }): Promise<Locator>;
61
- getAllBodyCellLocatorsByHeaderName(header: string, options?: {
62
- timeout?: number;
63
- }): Promise<Locator[]>;
64
- getAllBodyCellLocatorsByHeaderIndex(headerIndex: number): Promise<Locator[]>;
65
- getJson(options?: {
66
- timeout?: number;
67
- }): Promise<any>;
68
- waitForHeaderRows(options?: {
69
- timeout?: number;
70
- row?: {
71
- amount?: number;
72
- cell?: {
73
- totalCount?: number;
74
- contentCount?: number;
75
- };
76
- };
77
- }): Promise<void>;
78
- waitForBodyRows(options?: {
79
- timeout?: number;
80
- row?: {
81
- amount?: number;
82
- cell?: {
83
- totalCount?: number;
84
- contentCount?: number;
85
- };
86
- };
87
- }): Promise<void>;
88
- private load;
214
+ /**
215
+ * Retrieves a cell locator in the body of the table based on row conditions and a target header.
216
+ * Finds the first row where all conditions match, then returns the cell in the target column.
217
+ * @param conditions - An object where keys are header names and values are expected cell contents, or a single [header, value] tuple.
218
+ * @param targetHeader - The header name of the column containing the cell to retrieve.
219
+ * @param options - Optional table loading options. See {@link TableOptions}.
220
+ * @returns A promise that resolves to a Playwright Locator for the matching cell.
221
+ * @throws Error if no row matches all specified conditions.
222
+ * @throws Error if any condition header or target header is not found in the table.
223
+ *
224
+ * @example
225
+ * // Find cell with multiple conditions
226
+ * const emailLocator = await table.getBodyCellLocatorByRowConditions(
227
+ * { "First name": "John", "Status": "Active" },
228
+ * "Email"
229
+ * );
230
+ * await expect(emailLocator).toContainText('@example.com');
231
+ *
232
+ * @example
233
+ * // Find cell with single condition using tuple syntax
234
+ * const actionCell = await table.getBodyCellLocatorByRowConditions(
235
+ * ["Username", "john.doe"],
236
+ * "Actions"
237
+ * );
238
+ * await actionCell.locator('button').click();
239
+ *
240
+ * @example
241
+ * // Find cell with single condition using object syntax
242
+ * const statusCell = await table.getBodyCellLocatorByRowConditions(
243
+ * { "Username": "john.doe" },
244
+ * "Status"
245
+ * );
246
+ *
247
+ * @see {@link getBodyCellLocator} for getting cells by direct index
248
+ * @see {@link getAllBodyCellLocatorsByHeaderName} for getting all cells in a column
249
+ */
250
+ getBodyCellLocatorByRowConditions(conditions: Record<string, string> | [string, string], targetHeader: string, options?: TableOptions): Promise<Locator>;
251
+ /**
252
+ * Retrieves all cell locators in the body of the table for a specific header name.
253
+ * Returns locators for every cell in the column identified by the header.
254
+ * @param header - The header name identifying the column.
255
+ * @param options - Optional table loading options. See {@link TableOptions}.
256
+ * @returns A promise that resolves to an array of Playwright Locators for all cells in the column.
257
+ * @throws Error if the header is not found in the table.
258
+ *
259
+ * @example
260
+ * // Get all cells in the "Status" column
261
+ * const statusCells = await table.getAllBodyCellLocatorsByHeaderName("Status");
262
+ * for (const cell of statusCells) {
263
+ * await expect(cell).toHaveText(/Active|Inactive/);
264
+ * }
265
+ *
266
+ * @example
267
+ * // Get text from all cells in "Name" column
268
+ * const nameCells = await table.getAllBodyCellLocatorsByHeaderName("Name");
269
+ * const names = await Promise.all(nameCells.map(cell => cell.textContent()));
270
+ *
271
+ * @see {@link getAllBodyCellLocatorsByHeaderIndex} for getting cells by column index
272
+ * @see {@link getBodyCellLocatorByRowConditions} for finding specific cells by content
273
+ */
274
+ getAllBodyCellLocatorsByHeaderName(header: string, options?: TableOptions): Promise<Locator[]>;
275
+ /**
276
+ * Retrieves all cell locators in the body of the table for a specific header index.
277
+ * Returns locators for every cell in the column at the specified position.
278
+ * @param headerIndex - The header/column index (0-based).
279
+ * @param options - Optional table loading options. See {@link TableOptions}.
280
+ * @returns A promise that resolves to an array of Playwright Locators for all cells in the column.
281
+ * @throws Error if the header index is out of bounds (negative or >= column count).
282
+ *
283
+ * @example
284
+ * // Get all cells in the third column (index 2)
285
+ * const thirdColumnCells = await table.getAllBodyCellLocatorsByHeaderIndex(2);
286
+ * for (const cell of thirdColumnCells) {
287
+ * await expect(cell).toBeVisible();
288
+ * }
289
+ *
290
+ * @example
291
+ * // Get all cells in first column
292
+ * const firstColumnCells = await table.getAllBodyCellLocatorsByHeaderIndex(0);
293
+ * const values = await Promise.all(firstColumnCells.map(c => c.textContent()));
294
+ *
295
+ * @see {@link getAllBodyCellLocatorsByHeaderName} for getting cells by header name
296
+ * @see {@link getBodyCellLocator} for getting a specific cell by row and column index
297
+ */
298
+ getAllBodyCellLocatorsByHeaderIndex(headerIndex: number, options?: TableOptions): Promise<Locator[]>;
299
+ /**
300
+ * Converts the table data into a JSON array where each object represents a row.
301
+ * Keys are header names, values are cell contents.
302
+ * @param options - Optional table loading options. See {@link TableOptions}.
303
+ * @returns A promise that resolves to an array of objects representing table rows.
304
+ * @throws Error if no header rows or body rows found.
305
+ *
306
+ * @example
307
+ * // Convert table to JSON with default options
308
+ * const data = await table.getJson();
309
+ * // Result: [{"Name": "John", "Age": "30"}, {"Name": "Jane", "Age": "25"}]
310
+ *
311
+ * @example
312
+ * // Convert with colspan enabled and duplicate suffix
313
+ * const data = await table.getJson({
314
+ * headerRowOptions: {
315
+ * colspan: { enabled: true, suffix: true },
316
+ * duplicateSuffix: true,
317
+ * emptyCellReplacement: true
318
+ * }
319
+ * });
320
+ */
321
+ getJson(options?: TableOptions): Promise<Record<string, string>[]>;
322
+ /**
323
+ * Waits for the table body to be empty (no body rows).
324
+ * @param options - Polling options (timeout, interval, retries).
325
+ * @returns A promise that resolves when table body is empty.
326
+ * @throws Error if table is not empty within timeout period.
327
+ *
328
+ * @example
329
+ * // Wait for table to clear after delete operation
330
+ * await table.waitForEmpty({ timeout: 5000 });
331
+ *
332
+ * @see {@link waitForNonEmpty} for waiting until table has data
333
+ *
334
+ * @remarks
335
+ * **Alternative:** Consider using Playwright's `expect.poll` for more flexibility:
336
+ * ```ts
337
+ * await expect.poll(async () => (await table.getBodyRows()).length).toBe(0);
338
+ * ```
339
+ */
340
+ waitForEmpty(options?: PollingOptions): Promise<void>;
341
+ /**
342
+ * Waits for the table body to be non-empty (at least one body row with content).
343
+ * A row is considered valid if it has at least one cell containing text.
344
+ * @param options - Polling options (timeout, interval, retries).
345
+ * @returns A promise that resolves when table has at least one body row with content.
346
+ * @throws Error if table remains empty or has no rows with content within timeout period.
347
+ *
348
+ * @example
349
+ * // Wait for table to populate after search
350
+ * await table.waitForNonEmpty({ timeout: 5000 });
351
+ *
352
+ * @see {@link waitForEmpty} for waiting until table is cleared
353
+ *
354
+ * @remarks
355
+ * **Alternative:** Consider using Playwright's `expect.poll` for more flexibility:
356
+ * ```ts
357
+ * await expect.poll(async () => (await table.getBodyRows()).length).toBeGreaterThan(0);
358
+ * ```
359
+ */
360
+ waitForNonEmpty(options?: PollingOptions): Promise<void>;
361
+ /**
362
+ * Waits for the table body to have exactly N rows.
363
+ * @param count - The exact number of body rows expected.
364
+ * @param options - Polling options (timeout, interval, retries).
365
+ * @returns A promise that resolves when the table has exactly the specified number of rows.
366
+ * @throws Error if row count doesn't match within timeout period.
367
+ *
368
+ * @example
369
+ * // Wait for exactly 10 rows after pagination
370
+ * await table.waitForExactRowCount(10, { timeout: 5000 });
371
+ *
372
+ * @example
373
+ * // Verify table has exactly 0 rows (alternative to waitForEmpty)
374
+ * await table.waitForExactRowCount(0);
375
+ *
376
+ * @see {@link getRowCount} for getting current row counts
377
+ *
378
+ * @remarks
379
+ * **Alternative:** Consider using Playwright's `expect.poll` for more flexibility:
380
+ * ```ts
381
+ * await expect.poll(async () => (await table.getBodyRows()).length).toBe(10);
382
+ * ```
383
+ */
384
+ waitForExactRowCount(count: number, options?: PollingOptions): Promise<void>;
385
+ /**
386
+ * Gets the count of header and body rows in the table.
387
+ * Lightweight method that doesn't fetch cell data, only counts rows.
388
+ * @returns A promise that resolves to an object with header and body row counts.
389
+ *
390
+ * @example
391
+ * // Get current row counts
392
+ * const counts = await table.getRowCount();
393
+ * console.log(`Headers: ${counts.header}, Body: ${counts.body}`);
394
+ *
395
+ * @example
396
+ * // Assert specific row count
397
+ * const { body } = await table.getRowCount();
398
+ * expect(body).toBe(10);
399
+ */
400
+ getRowCount(): Promise<{
401
+ header: number;
402
+ body: number;
403
+ }>;
404
+ /**
405
+ * Gets all distinct (unique) values from a specific column.
406
+ * Returns a sorted array of unique string values, excluding empty values.
407
+ * @param headerName - The header name of the column to extract distinct values from.
408
+ * @param options - Optional table loading options. See {@link TableOptions}.
409
+ * @returns A promise that resolves to a sorted array of distinct values.
410
+ * @throws Error if the specified header is not found in the table.
411
+ *
412
+ * @example
413
+ * // Get all unique status values
414
+ * const statuses = await table.getDistinctColumnValues("Status");
415
+ * // Result: ["Active", "Inactive", "Pending"]
416
+ *
417
+ * @example
418
+ * // Get distinct countries for filtering
419
+ * const countries = await table.getDistinctColumnValues("Country");
420
+ * console.log(`Available countries: ${countries.join(", ")}`);
421
+ *
422
+ * @example
423
+ * // Verify expected values exist
424
+ * const roles = await table.getDistinctColumnValues("Role");
425
+ * expect(roles).toContain("Admin");
426
+ * expect(roles).toHaveLength(3);
427
+ *
428
+ * @see {@link getAllBodyCellLocatorsByHeaderName} for getting all cell locators in a column
429
+ * @see {@link getJson} for getting full table data
430
+ */
431
+ getDistinctColumnValues(headerName: string, options?: TableOptions): Promise<string[]>;
432
+ /**
433
+ * Finds the index of the first body row matching the specified conditions.
434
+ * Returns -1 if no matching row is found.
435
+ * @param conditions - Record of header names and expected cell values to match.
436
+ * @param options - Optional table loading options. See {@link TableOptions}.
437
+ * @returns A promise that resolves to the 0-based index of the first matching row, or -1 if not found.
438
+ * @throws Error if specified headers don't exist in the table.
439
+ *
440
+ * @example
441
+ * // Find index of row where Status is "Active"
442
+ * const index = await table.findRowIndex({ Status: "Active" });
443
+ * if (index >= 0) {
444
+ * console.log(`Found at row ${index}`);
445
+ * }
446
+ *
447
+ * @example
448
+ * // Find row and interact with it
449
+ * const rowIndex = await table.findRowIndex({ Username: "john.doe" });
450
+ * if (rowIndex >= 0) {
451
+ * const cell = table.getBodyCellLocator(rowIndex, 0);
452
+ * await cell.click();
453
+ * }
454
+ *
455
+ * @see {@link getBodyCellLocatorByRowConditions} for getting cell locators from matching rows
456
+ * @see {@link waitForRowByConditions} for waiting until matching row appears
457
+ */
458
+ findRowIndex(conditions: Record<string, string>, options?: TableOptions): Promise<number>;
459
+ private getTable;
460
+ private mainHeaderRow;
461
+ /**
462
+ * Gets the index of a header in the main header row, throwing an error if not found.
463
+ * @param header - The header name to find.
464
+ * @param mainHeader - The main header row to search in.
465
+ * @returns The 0-based index of the header.
466
+ * @throws Error if the header is not found.
467
+ */
468
+ private getHeaderIndex;
89
469
  }
90
470
 
91
- export { type BodyRow, type Cell, CellContentType, type HeaderRow, type HeaderRowOptions, PlaywrightTable };
471
+ export { type BodyRow, type Cell, CellContentType, type HeaderRow, type HeaderRowOptions, PlaywrightTable, type PollingOptions, type TableOptions };