@rickcedwhat/playwright-smart-table 2.1.3 → 2.3.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/README.md CHANGED
@@ -20,12 +20,12 @@ For standard HTML tables (`<table>`, `<tr>`, `<td>`), the library works out of t
20
20
 
21
21
  <!-- embed: quick-start -->
22
22
  ```typescript
23
+ // Example from: https://datatables.net/examples/data_sources/dom
23
24
  const table = useTable(page.locator('#example'), {
24
25
  headerSelector: 'thead th' // Override for this specific site
25
26
  });
26
27
 
27
- // 🪄 Finds the row with Name="Airi Satou", then gets the Position cell.
28
- // If Airi is on Page 2, it handles pagination automatically.
28
+ // Find the row with Name="Airi Satou", then get the Position cell
29
29
  const row = await table.getByRow({ Name: 'Airi Satou' });
30
30
 
31
31
  await expect(row.getCell('Position')).toHaveText('Accountant');
@@ -43,14 +43,15 @@ The `SmartRow` is the core power of this library. Unlike a standard Playwright `
43
43
 
44
44
  <!-- embed: smart-row -->
45
45
  ```typescript
46
- // 1. Get SmartRow via getByRow
46
+ // Example from: https://datatables.net/examples/data_sources/dom
47
+
48
+ // Get SmartRow via getByRow
47
49
  const row = await table.getByRow({ Name: 'Airi Satou' });
48
50
 
49
- // 2. Interact with cell
50
- // ✅ Good: Resilient to column reordering
51
+ // Interact with cell using column name (resilient to column reordering)
51
52
  await row.getCell('Position').click();
52
53
 
53
- // 3. Dump data from row
54
+ // Extract row data as JSON
54
55
  const data = await row.toJSON();
55
56
  console.log(data);
56
57
  // { Name: "Airi Satou", Position: "Accountant", ... }
@@ -72,6 +73,7 @@ For tables that span multiple pages, configure a pagination strategy:
72
73
 
73
74
  <!-- embed: pagination -->
74
75
  ```typescript
76
+ // Example from: https://datatables.net/examples/data_sources/dom
75
77
  const table = useTable(page.locator('#example'), {
76
78
  rowSelector: 'tbody tr',
77
79
  headerSelector: 'thead th',
@@ -97,6 +99,7 @@ Enable debug logging to see exactly what the library is doing:
97
99
 
98
100
  <!-- embed: advanced-debug -->
99
101
  ```typescript
102
+ // Example from: https://datatables.net/examples/data_sources/dom
100
103
  const table = useTable(page.locator('#example'), {
101
104
  headerSelector: 'thead th',
102
105
  debug: true // Enables verbose logging of internal operations
@@ -115,6 +118,7 @@ If your tests navigate deep into a paginated table, use `.reset()` to return to
115
118
 
116
119
  <!-- embed: advanced-reset -->
117
120
  ```typescript
121
+ // Example from: https://datatables.net/examples/data_sources/dom
118
122
  // Navigate deep into the table by searching for a row on a later page
119
123
  try {
120
124
  await table.getByRow({ Name: 'Angelica Ramos' });
@@ -135,6 +139,7 @@ Efficiently extract all values from a specific column:
135
139
 
136
140
  <!-- embed: advanced-column-scan -->
137
141
  ```typescript
142
+ // Example from: https://datatables.net/examples/data_sources/dom
138
143
  // Quickly grab all text values from the "Office" column
139
144
  const offices = await table.getColumnValues('Office');
140
145
  expect(offices).toContain('Tokyo');
@@ -142,6 +147,63 @@ expect(offices.length).toBeGreaterThan(0);
142
147
  ```
143
148
  <!-- /embed: advanced-column-scan -->
144
149
 
150
+ ### Filling Row Data
151
+
152
+ Use `fill()` to intelligently populate form fields in a table row. The method automatically detects input types (text inputs, selects, checkboxes, contenteditable divs) and fills them appropriately.
153
+
154
+ <!-- embed: fill-basic -->
155
+ ```typescript
156
+ // Find a row and fill it with new data
157
+ const row = await table.getByRow({ ID: '1' });
158
+
159
+ await row.fill({
160
+ Name: 'John Updated',
161
+ Status: 'Inactive',
162
+ Active: false,
163
+ Notes: 'Updated notes here'
164
+ });
165
+
166
+ // Verify the values were filled correctly
167
+ await expect(row.getCell('Name').locator('input')).toHaveValue('John Updated');
168
+ await expect(row.getCell('Status').locator('select')).toHaveValue('Inactive');
169
+ await expect(row.getCell('Active').locator('input[type="checkbox"]')).not.toBeChecked();
170
+ await expect(row.getCell('Notes').locator('textarea')).toHaveValue('Updated notes here');
171
+ ```
172
+ <!-- /embed: fill-basic -->
173
+
174
+ **Auto-detection supports:**
175
+ - Text inputs (`input[type="text"]`, `textarea`)
176
+ - Select dropdowns (`select`)
177
+ - Checkboxes/radios (`input[type="checkbox"]`, `input[type="radio"]`, `[role="checkbox"]`)
178
+ - Contenteditable divs (`[contenteditable="true"]`)
179
+
180
+ **Custom input mappers:**
181
+
182
+ For edge cases where auto-detection doesn't work (e.g., custom components, multiple inputs in a cell), use per-column mappers:
183
+
184
+ <!-- embed: fill-custom-mappers -->
185
+ ```typescript
186
+ const row = await table.getByRow({ ID: '1' });
187
+
188
+ // Use custom input mappers for specific columns
189
+ await row.fill({
190
+ Name: 'John Updated',
191
+ Status: 'Inactive'
192
+ }, {
193
+ inputMappers: {
194
+ // Name column has multiple inputs - target the primary one
195
+ Name: (cell) => cell.locator('.primary-input'),
196
+ // Status uses standard select, but we could customize if needed
197
+ Status: (cell) => cell.locator('select')
198
+ }
199
+ });
200
+
201
+ // Verify the values
202
+ await expect(row.getCell('Name').locator('.primary-input')).toHaveValue('John Updated');
203
+ await expect(row.getCell('Status').locator('select')).toHaveValue('Inactive');
204
+ ```
205
+ <!-- /embed: fill-custom-mappers -->
206
+
145
207
  ### Transforming Column Headers
146
208
 
147
209
  Use `headerTransformer` to normalize or rename column headers. This is especially useful for tables with empty headers, inconsistent formatting, or when you want to use cleaner names in your tests.
@@ -152,6 +214,7 @@ Tables with empty header cells (like Material UI DataGrids) get auto-assigned na
152
214
 
153
215
  <!-- embed: header-transformer -->
154
216
  ```typescript
217
+ // Example from: https://mui.com/material-ui/react-table/
155
218
  const table = useTable(page.locator('.MuiDataGrid-root').first(), {
156
219
  rowSelector: '.MuiDataGrid-row',
157
220
  headerSelector: '.MuiDataGrid-columnHeader',
@@ -186,6 +249,7 @@ Clean up inconsistent column names (extra spaces, inconsistent casing):
186
249
 
187
250
  <!-- embed: header-transformer-normalize -->
188
251
  ```typescript
252
+ // Example from: https://the-internet.herokuapp.com/tables
189
253
  const table = useTable(page.locator('#table1'), {
190
254
  // Normalize column names: remove extra spaces, handle inconsistent casing
191
255
  headerTransformer: ({ text }) => {
@@ -215,10 +279,19 @@ await expect(row.getCell("Email")).toHaveText("jdoe@hotmail.com");
215
279
  - ✅ Returns `SmartRow` if exactly one match
216
280
  - ❌ Throws error if multiple matches (ambiguous query)
217
281
  - 👻 Returns sentinel locator if no match (allows `.not.toBeVisible()` assertions)
218
- - 🔄 Auto-paginates if row isn't on current page
282
+ - 🔄 Auto-paginates if row isn't on current page (when `maxPages > 1` and pagination strategy is configured)
283
+
284
+ **Type Signature:**
285
+ ```typescript
286
+ getByRow: <T extends { asJSON?: boolean }>(
287
+ filters: Record<string, string | RegExp | number>,
288
+ options?: { exact?: boolean, maxPages?: number } & T
289
+ ) => Promise<T['asJSON'] extends true ? Record<string, string> : SmartRow>;
290
+ ```
219
291
 
220
292
  <!-- embed: get-by-row -->
221
293
  ```typescript
294
+ // Example from: https://datatables.net/examples/data_sources/dom
222
295
  // Find a row where Name is "Airi Satou" AND Office is "Tokyo"
223
296
  const row = await table.getByRow({ Name: "Airi Satou", Office: "Tokyo" });
224
297
  await expect(row).toBeVisible();
@@ -228,37 +301,17 @@ await expect(await table.getByRow({ Name: "Ghost User" })).not.toBeVisible();
228
301
  ```
229
302
  <!-- /embed: get-by-row -->
230
303
 
231
- **Type Signature:**
232
- <!-- embed-type: TableResult -->
304
+ Get row data as JSON:
305
+ <!-- embed: get-by-row-json -->
233
306
  ```typescript
234
- export interface TableResult {
235
- getHeaders: () => Promise<string[]>;
236
- getHeaderCell: (columnName: string) => Promise<Locator>;
237
-
238
- getByRow: <T extends { asJSON?: boolean }>(
239
- filters: Record<string, string | RegExp | number>,
240
- options?: { exact?: boolean, maxPages?: number } & T
241
- ) => Promise<T['asJSON'] extends true ? Record<string, string> : SmartRow>;
242
-
243
- getAllRows: <T extends { asJSON?: boolean }>(
244
- options?: { filter?: Record<string, any>, exact?: boolean } & T
245
- ) => Promise<T['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
246
-
247
- generateConfigPrompt: (options?: PromptOptions) => Promise<void>;
248
- generateStrategyPrompt: (options?: PromptOptions) => Promise<void>;
249
-
250
- /**
251
- * Resets the table state (clears cache, flags) and invokes the onReset strategy.
252
- */
253
- reset: () => Promise<void>;
307
+ // Get row data directly as JSON object
308
+ const data = await table.getByRow({ Name: 'Airi Satou' }, { asJSON: true });
309
+ // Returns: { Name: "Airi Satou", Position: "Accountant", Office: "Tokyo", ... }
254
310
 
255
- /**
256
- * Scans a specific column across all pages and returns the values.
257
- */
258
- getColumnValues: <V = string>(column: string, options?: { mapper?: (cell: Locator) => Promise<V> | V, maxPages?: number }) => Promise<V[]>;
259
- }
311
+ expect(data).toHaveProperty('Name', 'Airi Satou');
312
+ expect(data).toHaveProperty('Position');
260
313
  ```
261
- <!-- /embed-type: TableResult -->
314
+ <!-- /embed: get-by-row-json -->
262
315
 
263
316
  #### <a name="getallrows"></a>`getAllRows(options?)`
264
317
 
@@ -266,8 +319,16 @@ export interface TableResult {
266
319
 
267
320
  **Best for:** Checking existence, validating sort order, bulk data extraction.
268
321
 
322
+ **Type Signature:**
323
+ ```typescript
324
+ getAllRows: <T extends { asJSON?: boolean }>(
325
+ options?: { filter?: Record<string, any>, exact?: boolean } & T
326
+ ) => Promise<T['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
327
+ ```
328
+
269
329
  <!-- embed: get-all-rows -->
270
330
  ```typescript
331
+ // Example from: https://datatables.net/examples/data_sources/dom
271
332
  // 1. Get ALL rows on the current page
272
333
  const allRows = await table.getAllRows();
273
334
  expect(allRows.length).toBeGreaterThan(0);
@@ -286,24 +347,6 @@ expect(data[0]).toHaveProperty('Name');
286
347
  ```
287
348
  <!-- /embed: get-all-rows -->
288
349
 
289
- #### <a name="getcolumnvalues"></a>`getColumnValues(column, options?)`
290
-
291
- Scans a specific column across all pages and returns values. Supports custom mappers for extracting non-text data.
292
-
293
- **Additional Examples:**
294
-
295
- Get row data as JSON:
296
- <!-- embed: get-by-row-json -->
297
- ```typescript
298
- // Get row data directly as JSON object
299
- const data = await table.getByRow({ Name: 'Airi Satou' }, { asJSON: true });
300
- // Returns: { Name: "Airi Satou", Position: "Accountant", Office: "Tokyo", ... }
301
-
302
- expect(data).toHaveProperty('Name', 'Airi Satou');
303
- expect(data).toHaveProperty('Position');
304
- ```
305
- <!-- /embed: get-by-row-json -->
306
-
307
350
  Filter rows with exact match:
308
351
  <!-- embed: get-all-rows-exact -->
309
352
  ```typescript
@@ -317,7 +360,33 @@ expect(exactMatches.length).toBeGreaterThan(0);
317
360
  ```
318
361
  <!-- /embed: get-all-rows-exact -->
319
362
 
320
- Column scanning with custom mapper:
363
+ #### <a name="getcolumnvalues"></a>`getColumnValues(column, options?)`
364
+
365
+ Scans a specific column across all pages and returns values. Supports custom mappers for extracting non-text data.
366
+
367
+ **Type Signature:**
368
+ ```typescript
369
+ getColumnValues: <V = string>(
370
+ column: string,
371
+ options?: {
372
+ mapper?: (cell: Locator) => Promise<V> | V,
373
+ maxPages?: number
374
+ }
375
+ ) => Promise<V[]>;
376
+ ```
377
+
378
+ Basic usage:
379
+ <!-- embed: advanced-column-scan -->
380
+ ```typescript
381
+ // Example from: https://datatables.net/examples/data_sources/dom
382
+ // Quickly grab all text values from the "Office" column
383
+ const offices = await table.getColumnValues('Office');
384
+ expect(offices).toContain('Tokyo');
385
+ expect(offices.length).toBeGreaterThan(0);
386
+ ```
387
+ <!-- /embed: advanced-column-scan -->
388
+
389
+ With custom mapper:
321
390
  <!-- embed: advanced-column-scan-mapper -->
322
391
  ```typescript
323
392
  // Extract numeric values from a column
@@ -334,18 +403,33 @@ expect(ages.length).toBeGreaterThan(0);
334
403
  ```
335
404
  <!-- /embed: advanced-column-scan-mapper -->
336
405
 
337
- #### <a name="reset"></a>`reset()`
338
-
339
- Resets table state (clears cache, pagination flags) and invokes the `onReset` strategy to return to the first page.
340
-
341
406
  #### <a name="getheaders"></a>`getHeaders()`
342
407
 
343
408
  Returns an array of all column names in the table.
344
409
 
410
+ **Type Signature:**
411
+ ```typescript
412
+ getHeaders: () => Promise<string[]>;
413
+ ```
414
+
345
415
  #### <a name="getheadercell"></a>`getHeaderCell(columnName)`
346
416
 
347
417
  Returns a Playwright `Locator` for the specified header cell.
348
418
 
419
+ **Type Signature:**
420
+ ```typescript
421
+ getHeaderCell: (columnName: string) => Promise<Locator>;
422
+ ```
423
+
424
+ #### <a name="reset"></a>`reset()`
425
+
426
+ Resets table state (clears cache, pagination flags) and invokes the `onReset` strategy to return to the first page.
427
+
428
+ **Type Signature:**
429
+ ```typescript
430
+ reset: () => Promise<void>;
431
+ ```
432
+
349
433
  ---
350
434
 
351
435
  ## 🧩 Pagination Strategies
@@ -396,7 +480,7 @@ export type PaginationStrategy = (context: TableContext) => Promise<boolean>;
396
480
  ```typescript
397
481
  export interface TableContext {
398
482
  root: Locator;
399
- config: Required<TableConfig>;
483
+ config: FinalTableConfig;
400
484
  page: Page;
401
485
  resolve: (selector: Selector, parent: Locator | Page) => Locator;
402
486
  }
@@ -429,7 +513,7 @@ await table.generateStrategyPrompt({ output: 'console' });
429
513
  export interface PromptOptions {
430
514
  /**
431
515
  * Output Strategy:
432
- * - 'error': Throws an error with the prompt (Best for Cloud/QA Wolf to get clean text).
516
+ * - 'error': Throws an error with the prompt (useful for platforms that capture error output cleanly).
433
517
  * - 'console': Standard console logs (Default).
434
518
  */
435
519
  output?: 'console' | 'error';
@@ -450,9 +534,13 @@ A `SmartRow` extends Playwright's `Locator` with table-aware methods.
450
534
 
451
535
  <!-- embed-type: SmartRow -->
452
536
  ```typescript
453
- export type SmartRow = Locator & {
537
+ export type SmartRow = Omit<Locator, 'fill'> & {
454
538
  getCell(column: string): Locator;
455
539
  toJSON(): Promise<Record<string, string>>;
540
+ /**
541
+ * Fills the row with data. Automatically detects input types (text input, select, checkbox, etc.).
542
+ */
543
+ fill: (data: Record<string, any>, options?: FillOptions) => Promise<void>;
456
544
  };
457
545
  ```
458
546
  <!-- /embed-type: SmartRow -->
@@ -460,6 +548,7 @@ export type SmartRow = Locator & {
460
548
  **Methods:**
461
549
  - `getCell(column: string)`: Returns a `Locator` for the specified cell in this row
462
550
  - `toJSON()`: Extracts all cell data as a key-value object
551
+ - `fill(data, options?)`: Intelligently fills form fields in the row. Automatically detects input types or use `inputMappers` for custom control
463
552
 
464
553
  All standard Playwright `Locator` methods (`.click()`, `.isVisible()`, `.textContent()`, etc.) are also available.
465
554
 
@@ -474,6 +563,7 @@ export interface TableConfig {
474
563
  headerSelector?: Selector;
475
564
  cellSelector?: Selector;
476
565
  pagination?: PaginationStrategy;
566
+ sorting?: SortingStrategy;
477
567
  maxPages?: number;
478
568
  /**
479
569
  * Hook to rename columns dynamically.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  export * from './useTable';
2
2
  export * from './types';
3
- export * from './strategies';
package/dist/index.js CHANGED
@@ -16,4 +16,3 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./useTable"), exports);
18
18
  __exportStar(require("./types"), exports);
19
- __exportStar(require("./strategies"), exports);
@@ -1,15 +1,2 @@
1
- import { PaginationStrategy, Selector } from '../types';
2
- export declare const TableStrategies: {
3
- /**
4
- * Strategy: Clicks a "Next" button and waits for the first row of data to change.
5
- */
6
- clickNext: (nextButtonSelector: Selector, timeout?: number) => PaginationStrategy;
7
- /**
8
- * Strategy: Clicks a "Load More" button and waits for the row count to increase.
9
- */
10
- clickLoadMore: (buttonSelector: Selector, timeout?: number) => PaginationStrategy;
11
- /**
12
- * Strategy: Scrolls to the bottom and waits for more rows to appear.
13
- */
14
- infiniteScroll: (timeout?: number) => PaginationStrategy;
15
- };
1
+ export * from './pagination';
2
+ export * from './sorting';
@@ -1,112 +1,19 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.TableStrategies = void 0;
13
- /**
14
- * Internal helper to wait for a condition to be met.
15
- * Replaces the dependency on 'expect(...).toPass()' to ensure compatibility
16
- * with environments like QA Wolf where 'expect' is not globally available.
17
- */
18
- const waitForCondition = (predicate, timeout, page // Context page for pauses
19
- ) => __awaiter(void 0, void 0, void 0, function* () {
20
- const startTime = Date.now();
21
- while (Date.now() - startTime < timeout) {
22
- if (yield predicate()) {
23
- return true;
24
- }
25
- // Wait 100ms before next check (Standard Polling)
26
- yield page.waitForTimeout(100).catch(() => new Promise(r => setTimeout(r, 100)));
27
- }
28
- return false;
29
- });
30
- exports.TableStrategies = {
31
- /**
32
- * Strategy: Clicks a "Next" button and waits for the first row of data to change.
33
- */
34
- clickNext: (nextButtonSelector, timeout = 5000) => {
35
- return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve, page }) {
36
- const nextBtn = resolve(nextButtonSelector, root).first();
37
- // Debug log (can be verbose, maybe useful for debugging only)
38
- // console.log(`[Strategy: clickNext] Checking button...`);
39
- // Check if button exists/enabled before clicking.
40
- // We do NOT wait here because if the button isn't visible/enabled,
41
- // we assume we reached the last page.
42
- if (!(yield nextBtn.isVisible())) {
43
- console.log(`[Strategy: clickNext] Button not visible. Stopping pagination.`);
44
- return false;
45
- }
46
- if (!(yield nextBtn.isEnabled())) {
47
- console.log(`[Strategy: clickNext] Button disabled. Stopping pagination.`);
48
- return false;
49
- }
50
- // 1. Snapshot current state
51
- const firstRow = resolve(config.rowSelector, root).first();
52
- const oldText = yield firstRow.innerText().catch(() => "");
53
- // 2. Click
54
- console.log(`[Strategy: clickNext] Clicking next button...`);
55
- try {
56
- yield nextBtn.click({ timeout: 2000 });
57
- }
58
- catch (e) {
59
- console.warn(`[Strategy: clickNext] Click failed (blocked or detached): ${e}`);
60
- return false;
61
- }
62
- // 3. Smart Wait (Polling)
63
- const success = yield waitForCondition(() => __awaiter(void 0, void 0, void 0, function* () {
64
- const newText = yield firstRow.innerText().catch(() => "");
65
- return newText !== oldText;
66
- }), timeout, page);
67
- if (!success) {
68
- console.warn(`[Strategy: clickNext] Warning: Table content did not change after clicking Next.`);
69
- }
70
- return success;
71
- });
72
- },
73
- /**
74
- * Strategy: Clicks a "Load More" button and waits for the row count to increase.
75
- */
76
- clickLoadMore: (buttonSelector, timeout = 5000) => {
77
- return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve, page }) {
78
- const loadMoreBtn = resolve(buttonSelector, root).first();
79
- if (!(yield loadMoreBtn.isVisible()) || !(yield loadMoreBtn.isEnabled())) {
80
- return false;
81
- }
82
- // 1. Snapshot count
83
- const rows = resolve(config.rowSelector, root);
84
- const oldCount = yield rows.count();
85
- // 2. Click
86
- yield loadMoreBtn.click();
87
- // 3. Smart Wait (Polling)
88
- return yield waitForCondition(() => __awaiter(void 0, void 0, void 0, function* () {
89
- const newCount = yield rows.count();
90
- return newCount > oldCount;
91
- }), timeout, page);
92
- });
93
- },
94
- /**
95
- * Strategy: Scrolls to the bottom and waits for more rows to appear.
96
- */
97
- infiniteScroll: (timeout = 5000) => {
98
- return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve, page }) {
99
- const rows = resolve(config.rowSelector, root);
100
- const oldCount = yield rows.count();
101
- if (oldCount === 0)
102
- return false;
103
- // 1. Trigger Scroll
104
- yield rows.last().scrollIntoViewIfNeeded();
105
- // 2. Smart Wait (Polling)
106
- return yield waitForCondition(() => __awaiter(void 0, void 0, void 0, function* () {
107
- const newCount = yield rows.count();
108
- return newCount > oldCount;
109
- }), timeout, page);
110
- });
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
111
7
  }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
112
15
  };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // src/strategies/index.ts
18
+ __exportStar(require("./pagination"), exports);
19
+ __exportStar(require("./sorting"), exports);
@@ -0,0 +1,32 @@
1
+ import type { PaginationStrategy, Selector } from '../types';
2
+ export declare const PaginationStrategies: {
3
+ /**
4
+ * Strategy: Clicks a "Next" button and waits for the first row of data to change.
5
+ */
6
+ clickNext: (nextButtonSelector: Selector, timeout?: number) => PaginationStrategy;
7
+ /**
8
+ * Strategy: Clicks a "Load More" button and waits for the row count to increase.
9
+ */
10
+ clickLoadMore: (buttonSelector: Selector, timeout?: number) => PaginationStrategy;
11
+ /**
12
+ * Strategy: Scrolls to the bottom and waits for more rows to appear.
13
+ */
14
+ infiniteScroll: (timeout?: number) => PaginationStrategy;
15
+ };
16
+ /**
17
+ * @deprecated Use `PaginationStrategies` instead. This alias will be removed in a future major version.
18
+ */
19
+ export declare const TableStrategies: {
20
+ /**
21
+ * Strategy: Clicks a "Next" button and waits for the first row of data to change.
22
+ */
23
+ clickNext: (nextButtonSelector: Selector, timeout?: number) => PaginationStrategy;
24
+ /**
25
+ * Strategy: Clicks a "Load More" button and waits for the row count to increase.
26
+ */
27
+ clickLoadMore: (buttonSelector: Selector, timeout?: number) => PaginationStrategy;
28
+ /**
29
+ * Strategy: Scrolls to the bottom and waits for more rows to appear.
30
+ */
31
+ infiniteScroll: (timeout?: number) => PaginationStrategy;
32
+ };
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TableStrategies = exports.PaginationStrategies = void 0;
13
+ const utils_1 = require("../utils");
14
+ exports.PaginationStrategies = {
15
+ /**
16
+ * Strategy: Clicks a "Next" button and waits for the first row of data to change.
17
+ */
18
+ clickNext: (nextButtonSelector, timeout = 5000) => {
19
+ return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve, page }) {
20
+ const nextBtn = resolve(nextButtonSelector, root).first();
21
+ if (!(yield nextBtn.isVisible()) || !(yield nextBtn.isEnabled())) {
22
+ return false;
23
+ }
24
+ const firstRow = resolve(config.rowSelector, root).first();
25
+ const oldText = yield firstRow.innerText().catch(() => "");
26
+ yield nextBtn.click({ timeout: 2000 }).catch(() => { });
27
+ const success = yield (0, utils_1.waitForCondition)(() => __awaiter(void 0, void 0, void 0, function* () {
28
+ const newText = yield firstRow.innerText().catch(() => "");
29
+ return newText !== oldText;
30
+ }), timeout, page);
31
+ return success;
32
+ });
33
+ },
34
+ /**
35
+ * Strategy: Clicks a "Load More" button and waits for the row count to increase.
36
+ */
37
+ clickLoadMore: (buttonSelector, timeout = 5000) => {
38
+ return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve, page }) {
39
+ const loadMoreBtn = resolve(buttonSelector, root).first();
40
+ if (!(yield loadMoreBtn.isVisible()) || !(yield loadMoreBtn.isEnabled())) {
41
+ return false;
42
+ }
43
+ const rows = resolve(config.rowSelector, root);
44
+ const oldCount = yield rows.count();
45
+ yield loadMoreBtn.click();
46
+ return yield (0, utils_1.waitForCondition)(() => __awaiter(void 0, void 0, void 0, function* () {
47
+ const newCount = yield rows.count();
48
+ return newCount > oldCount;
49
+ }), timeout, page);
50
+ });
51
+ },
52
+ /**
53
+ * Strategy: Scrolls to the bottom and waits for more rows to appear.
54
+ */
55
+ infiniteScroll: (timeout = 5000) => {
56
+ return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve, page }) {
57
+ const rows = resolve(config.rowSelector, root);
58
+ const oldCount = yield rows.count();
59
+ if (oldCount === 0)
60
+ return false;
61
+ yield rows.last().scrollIntoViewIfNeeded();
62
+ return yield (0, utils_1.waitForCondition)(() => __awaiter(void 0, void 0, void 0, function* () {
63
+ const newCount = yield rows.count();
64
+ return newCount > oldCount;
65
+ }), timeout, page);
66
+ });
67
+ }
68
+ };
69
+ /**
70
+ * @deprecated Use `PaginationStrategies` instead. This alias will be removed in a future major version.
71
+ */
72
+ exports.TableStrategies = exports.PaginationStrategies;
@@ -0,0 +1,12 @@
1
+ import type { SortingStrategy } from '../types';
2
+ /**
3
+ * A collection of pre-built sorting strategies.
4
+ */
5
+ export declare const SortingStrategies: {
6
+ /**
7
+ * A sorting strategy that interacts with column headers based on ARIA attributes.
8
+ * - `doSort`: Clicks the header repeatedly until the desired `aria-sort` state is achieved.
9
+ * - `getSortState`: Reads the `aria-sort` attribute from the header.
10
+ */
11
+ AriaSort: () => SortingStrategy;
12
+ };