@rickcedwhat/playwright-smart-table 4.0.0 → 5.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/README.md +286 -102
- package/dist/smartRow.js +2 -5
- package/dist/strategies/columns.d.ts +0 -34
- package/dist/strategies/columns.js +1 -34
- package/dist/strategies/headers.d.ts +0 -16
- package/dist/strategies/headers.js +1 -113
- package/dist/strategies/index.d.ts +0 -28
- package/dist/strategies/index.js +0 -3
- package/dist/strategies/pagination.d.ts +0 -21
- package/dist/strategies/pagination.js +1 -23
- package/dist/strategies/validation.d.ts +22 -0
- package/dist/strategies/validation.js +54 -0
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +94 -24
- package/dist/types.d.ts +89 -24
- package/dist/useTable.d.ts +2 -9
- package/dist/useTable.js +54 -32
- package/package.json +2 -2
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import type { Locator, Page } from '@playwright/test';
|
|
2
|
+
/**
|
|
3
|
+
* Flexible selector type - can be a CSS string, function returning a Locator, or Locator itself.
|
|
4
|
+
* @example
|
|
5
|
+
* // String selector
|
|
6
|
+
* rowSelector: 'tbody tr'
|
|
7
|
+
*
|
|
8
|
+
* // Function selector
|
|
9
|
+
* rowSelector: (root) => root.locator('[role="row"]')
|
|
10
|
+
*/
|
|
2
11
|
export type Selector = string | ((root: Locator | Page) => Locator);
|
|
3
12
|
/**
|
|
4
13
|
* Function to get a cell locator given row, column info.
|
|
@@ -21,24 +30,67 @@ export type GetActiveCellFn = (args: TableContext) => Promise<{
|
|
|
21
30
|
columnName?: string;
|
|
22
31
|
locator: Locator;
|
|
23
32
|
} | null>;
|
|
33
|
+
/**
|
|
34
|
+
* SmartRow - A Playwright Locator with table-aware methods.
|
|
35
|
+
*
|
|
36
|
+
* Extends all standard Locator methods (click, isVisible, etc.) with table-specific functionality.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const row = table.getRow({ Name: 'John Doe' });
|
|
40
|
+
* await row.click(); // Standard Locator method
|
|
41
|
+
* const email = row.getCell('Email'); // Table-aware method
|
|
42
|
+
* const data = await row.toJSON(); // Extract all row data
|
|
43
|
+
* await row.smartFill({ Name: 'Jane', Status: 'Active' }); // Fill form fields
|
|
44
|
+
*/
|
|
24
45
|
export type SmartRow<T = any> = Locator & {
|
|
25
|
-
|
|
46
|
+
/** Optional row index (0-based) if known */
|
|
26
47
|
rowIndex?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Get a cell locator by column name.
|
|
50
|
+
* @param column - Column name (case-sensitive)
|
|
51
|
+
* @returns Locator for the cell
|
|
52
|
+
* @example
|
|
53
|
+
* const emailCell = row.getCell('Email');
|
|
54
|
+
* await expect(emailCell).toHaveText('john@example.com');
|
|
55
|
+
*/
|
|
27
56
|
getCell(column: string): Locator;
|
|
57
|
+
/**
|
|
58
|
+
* Extract all cell data as a key-value object.
|
|
59
|
+
* @param options - Optional configuration
|
|
60
|
+
* @param options.columns - Specific columns to extract (extracts all if not specified)
|
|
61
|
+
* @returns Promise resolving to row data
|
|
62
|
+
* @example
|
|
63
|
+
* const data = await row.toJSON();
|
|
64
|
+
* // { Name: 'John', Email: 'john@example.com', ... }
|
|
65
|
+
*
|
|
66
|
+
* const partial = await row.toJSON({ columns: ['Name', 'Email'] });
|
|
67
|
+
* // { Name: 'John', Email: 'john@example.com' }
|
|
68
|
+
*/
|
|
28
69
|
toJSON(options?: {
|
|
29
70
|
columns?: string[];
|
|
30
71
|
}): Promise<T>;
|
|
31
72
|
/**
|
|
32
73
|
* Scrolls/paginates to bring this row into view.
|
|
33
|
-
* Only works if rowIndex is known.
|
|
74
|
+
* Only works if rowIndex is known (e.g., from getRowByIndex).
|
|
75
|
+
* @throws Error if rowIndex is unknown
|
|
34
76
|
*/
|
|
35
77
|
bringIntoView(): Promise<void>;
|
|
36
78
|
/**
|
|
37
|
-
*
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
*
|
|
79
|
+
* Intelligently fills form fields in the row.
|
|
80
|
+
* Automatically detects input types (text, select, checkbox, contenteditable).
|
|
81
|
+
*
|
|
82
|
+
* @param data - Column-value pairs to fill
|
|
83
|
+
* @param options - Optional configuration
|
|
84
|
+
* @param options.inputMappers - Custom input selectors per column
|
|
85
|
+
* @example
|
|
86
|
+
* // Auto-detection
|
|
87
|
+
* await row.smartFill({ Name: 'John', Status: 'Active', Subscribe: true });
|
|
88
|
+
*
|
|
89
|
+
* // Custom input mappers
|
|
90
|
+
* await row.smartFill(
|
|
91
|
+
* { Name: 'John' },
|
|
92
|
+
* { inputMappers: { Name: (cell) => cell.locator('.custom-input') } }
|
|
93
|
+
* );
|
|
42
94
|
*/
|
|
43
95
|
smartFill: (data: Partial<T> | Record<string, any>, options?: FillOptions) => Promise<void>;
|
|
44
96
|
};
|
|
@@ -191,13 +243,18 @@ export interface TableResult<T = any> {
|
|
|
191
243
|
init(options?: {
|
|
192
244
|
timeout?: number;
|
|
193
245
|
}): Promise<TableResult>;
|
|
246
|
+
/**
|
|
247
|
+
* SYNC: Checks if the table has been initialized.
|
|
248
|
+
* @returns true if init() has been called and completed, false otherwise
|
|
249
|
+
*/
|
|
250
|
+
isInitialized(): boolean;
|
|
194
251
|
getHeaders: () => Promise<string[]>;
|
|
195
252
|
getHeaderCell: (columnName: string) => Promise<Locator>;
|
|
196
253
|
/**
|
|
197
254
|
* Finds a row by filters on the current page only. Returns immediately (sync).
|
|
198
255
|
* Throws error if table is not initialized.
|
|
199
256
|
*/
|
|
200
|
-
|
|
257
|
+
getRow: (filters: Record<string, string | RegExp | number>, options?: {
|
|
201
258
|
exact?: boolean;
|
|
202
259
|
}) => SmartRow;
|
|
203
260
|
/**
|
|
@@ -206,38 +263,46 @@ export interface TableResult<T = any> {
|
|
|
206
263
|
* @param index 1-based row index
|
|
207
264
|
* @param options Optional settings including bringIntoView
|
|
208
265
|
*/
|
|
209
|
-
|
|
266
|
+
getRowByIndex: (index: number, options?: {
|
|
210
267
|
bringIntoView?: boolean;
|
|
211
268
|
}) => SmartRow;
|
|
212
269
|
/**
|
|
213
|
-
* Searches for a row across
|
|
214
|
-
* Auto-initializes if
|
|
270
|
+
* ASYNC: Searches for a single row across pages using pagination.
|
|
271
|
+
* Auto-initializes the table if not already initialized.
|
|
272
|
+
* @param filters - The filter criteria to match
|
|
273
|
+
* @param options - Search options including exact match and max pages
|
|
215
274
|
*/
|
|
216
|
-
|
|
275
|
+
findRow: (filters: Record<string, string | RegExp | number>, options?: {
|
|
217
276
|
exact?: boolean;
|
|
218
277
|
maxPages?: number;
|
|
219
278
|
}) => Promise<SmartRow>;
|
|
220
279
|
/**
|
|
221
|
-
*
|
|
280
|
+
* ASYNC: Searches for all matching rows across pages using pagination.
|
|
281
|
+
* Auto-initializes the table if not already initialized.
|
|
282
|
+
* @param filters - The filter criteria to match
|
|
283
|
+
* @param options - Search options including exact match, max pages, and asJSON
|
|
222
284
|
*/
|
|
223
|
-
|
|
224
|
-
getAllCurrentRows: <T extends {
|
|
285
|
+
findRows: <R extends {
|
|
225
286
|
asJSON?: boolean;
|
|
226
|
-
}>(options?: {
|
|
227
|
-
filter?: Record<string, any>;
|
|
287
|
+
}>(filters: Record<string, string | RegExp | number>, options?: {
|
|
228
288
|
exact?: boolean;
|
|
229
|
-
|
|
289
|
+
maxPages?: number;
|
|
290
|
+
} & R) => Promise<R['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
|
|
291
|
+
/**
|
|
292
|
+
* Navigates to a specific column using the configured CellNavigationStrategy.
|
|
293
|
+
*/
|
|
294
|
+
scrollToColumn: (columnName: string) => Promise<void>;
|
|
230
295
|
/**
|
|
231
|
-
*
|
|
296
|
+
* ASYNC: Gets all rows on the current page only (does not paginate).
|
|
297
|
+
* Auto-initializes the table if not already initialized.
|
|
298
|
+
* @param options - Filter and formatting options
|
|
232
299
|
*/
|
|
233
|
-
|
|
300
|
+
getRows: <R extends {
|
|
234
301
|
asJSON?: boolean;
|
|
235
302
|
}>(options?: {
|
|
236
303
|
filter?: Record<string, any>;
|
|
237
304
|
exact?: boolean;
|
|
238
|
-
} &
|
|
239
|
-
generateConfigPrompt: (options?: PromptOptions) => Promise<void>;
|
|
240
|
-
generateStrategyPrompt: (options?: PromptOptions) => Promise<void>;
|
|
305
|
+
} & R) => Promise<R['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
|
|
241
306
|
/**
|
|
242
307
|
* Resets the table state (clears cache, flags) and invokes the onReset strategy.
|
|
243
308
|
*/
|
|
@@ -308,4 +373,4 @@ export interface TableResult<T = any> {
|
|
|
308
373
|
/**
|
|
309
374
|
* Restricted table result that excludes methods that shouldn't be called during iteration.
|
|
310
375
|
*/
|
|
311
|
-
export type RestrictedTableResult<T = any> = Omit<TableResult<T>, 'searchForRow' | 'iterateThroughTable' | 'reset'
|
|
376
|
+
export type RestrictedTableResult<T = any> = Omit<TableResult<T>, 'searchForRow' | 'iterateThroughTable' | 'reset'>;
|
package/dist/useTable.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Locator } from '@playwright/test';
|
|
|
2
2
|
import { TableConfig, Selector, TableResult, PaginationStrategy } from './types';
|
|
3
3
|
import { FillStrategies } from './strategies/fill';
|
|
4
4
|
import { HeaderStrategies } from './strategies/headers';
|
|
5
|
-
import { CellNavigationStrategies
|
|
5
|
+
import { CellNavigationStrategies } from './strategies/columns';
|
|
6
6
|
import { ResolutionStrategies } from './strategies/resolution';
|
|
7
7
|
import { Strategies } from './strategies';
|
|
8
8
|
/**
|
|
@@ -11,16 +11,9 @@ import { Strategies } from './strategies';
|
|
|
11
11
|
export declare const useTable: <T = any>(rootLocator: Locator, configOptions?: TableConfig) => TableResult<T>;
|
|
12
12
|
export declare const PaginationStrategies: {
|
|
13
13
|
clickNext: (nextButtonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
14
|
-
clickLoadMore: (buttonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
15
|
-
infiniteScroll: (timeout?: number) => PaginationStrategy;
|
|
16
|
-
};
|
|
17
|
-
/** @deprecated Use Strategies.Pagination instead */
|
|
18
|
-
export declare const DeprecatedTableStrategies: {
|
|
19
|
-
clickNext: (nextButtonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
20
|
-
clickLoadMore: (buttonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
21
14
|
infiniteScroll: (timeout?: number) => PaginationStrategy;
|
|
22
15
|
};
|
|
23
16
|
export declare const SortingStrategies: {
|
|
24
17
|
AriaSort: () => import("./types").SortingStrategy;
|
|
25
18
|
};
|
|
26
|
-
export { FillStrategies, HeaderStrategies, CellNavigationStrategies,
|
|
19
|
+
export { FillStrategies, HeaderStrategies, CellNavigationStrategies, ResolutionStrategies, Strategies };
|
package/dist/useTable.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.Strategies = exports.ResolutionStrategies = exports.
|
|
12
|
+
exports.Strategies = exports.ResolutionStrategies = exports.CellNavigationStrategies = exports.HeaderStrategies = exports.FillStrategies = exports.SortingStrategies = exports.PaginationStrategies = exports.useTable = void 0;
|
|
13
13
|
const typeContext_1 = require("./typeContext");
|
|
14
14
|
const sorting_1 = require("./strategies/sorting");
|
|
15
15
|
const pagination_1 = require("./strategies/pagination");
|
|
@@ -19,13 +19,13 @@ const headers_1 = require("./strategies/headers");
|
|
|
19
19
|
Object.defineProperty(exports, "HeaderStrategies", { enumerable: true, get: function () { return headers_1.HeaderStrategies; } });
|
|
20
20
|
const columns_1 = require("./strategies/columns");
|
|
21
21
|
Object.defineProperty(exports, "CellNavigationStrategies", { enumerable: true, get: function () { return columns_1.CellNavigationStrategies; } });
|
|
22
|
-
Object.defineProperty(exports, "ColumnStrategies", { enumerable: true, get: function () { return columns_1.ColumnStrategies; } });
|
|
23
22
|
const smartRow_1 = require("./smartRow");
|
|
24
23
|
const filterEngine_1 = require("./filterEngine");
|
|
25
24
|
const resolution_1 = require("./strategies/resolution");
|
|
26
25
|
Object.defineProperty(exports, "ResolutionStrategies", { enumerable: true, get: function () { return resolution_1.ResolutionStrategies; } });
|
|
27
26
|
const strategies_1 = require("./strategies");
|
|
28
27
|
Object.defineProperty(exports, "Strategies", { enumerable: true, get: function () { return strategies_1.Strategies; } });
|
|
28
|
+
const validation_1 = require("./strategies/validation");
|
|
29
29
|
/**
|
|
30
30
|
* Main hook to interact with a table.
|
|
31
31
|
*/
|
|
@@ -162,7 +162,8 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
162
162
|
page: rootLocator.page(),
|
|
163
163
|
resolve: resolve
|
|
164
164
|
};
|
|
165
|
-
const
|
|
165
|
+
const paginationResult = yield config.strategies.pagination(context);
|
|
166
|
+
const didLoadMore = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
|
|
166
167
|
if (didLoadMore) {
|
|
167
168
|
_hasPaginated = true;
|
|
168
169
|
currentPage++;
|
|
@@ -242,7 +243,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
242
243
|
}),
|
|
243
244
|
getHeaders: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
244
245
|
if (!_isInitialized || !_headerMap)
|
|
245
|
-
throw new Error('Table not initialized. Call await table.init() first.');
|
|
246
|
+
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
|
|
246
247
|
return Array.from(_headerMap.keys());
|
|
247
248
|
}),
|
|
248
249
|
getHeaderCell: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -299,22 +300,22 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
299
300
|
}
|
|
300
301
|
return results;
|
|
301
302
|
}),
|
|
302
|
-
|
|
303
|
+
getRow: (filters, options = { exact: false }) => {
|
|
303
304
|
if (!_isInitialized || !_headerMap)
|
|
304
|
-
throw new Error('Table not initialized. Call await table.init() first.');
|
|
305
|
+
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
|
|
305
306
|
const allRows = resolve(config.rowSelector, rootLocator);
|
|
306
307
|
const matchedRows = filterEngine.applyFilters(allRows, filters, _headerMap, options.exact || false, rootLocator.page());
|
|
307
308
|
const rowLocator = matchedRows.first();
|
|
308
309
|
return _makeSmart(rowLocator, _headerMap, 0); // fallback index 0
|
|
309
310
|
},
|
|
310
|
-
|
|
311
|
+
getRowByIndex: (index, options = {}) => {
|
|
311
312
|
if (!_isInitialized || !_headerMap)
|
|
312
|
-
throw new Error('Table not initialized. Call await table.init() first.');
|
|
313
|
+
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
|
|
313
314
|
const rowIndex = index - 1; // Convert 1-based to 0-based
|
|
314
315
|
const rowLocator = resolve(config.rowSelector, rootLocator).nth(rowIndex);
|
|
315
316
|
return _makeSmart(rowLocator, _headerMap, rowIndex);
|
|
316
317
|
},
|
|
317
|
-
|
|
318
|
+
findRow: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
318
319
|
yield _ensureInitialized();
|
|
319
320
|
let row = yield _findRowLocator(filters, options);
|
|
320
321
|
if (!row) {
|
|
@@ -322,7 +323,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
322
323
|
}
|
|
323
324
|
return _makeSmart(row, _headerMap, 0);
|
|
324
325
|
}),
|
|
325
|
-
|
|
326
|
+
getRows: (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
326
327
|
yield _ensureInitialized();
|
|
327
328
|
let rowLocators = resolve(config.rowSelector, rootLocator);
|
|
328
329
|
if (options === null || options === void 0 ? void 0 : options.filter) {
|
|
@@ -335,22 +336,44 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
335
336
|
}
|
|
336
337
|
return smartRows;
|
|
337
338
|
}),
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
339
|
+
findRows: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
340
|
+
var _a, _b, _c, _d;
|
|
341
|
+
yield _ensureInitialized();
|
|
342
|
+
const allRows = [];
|
|
343
|
+
const effectiveMaxPages = (_b = (_a = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages) !== null && _b !== void 0 ? _b : Infinity;
|
|
344
|
+
let pageCount = 0;
|
|
345
|
+
// Collect rows from current page
|
|
346
|
+
let rowLocators = resolve(config.rowSelector, rootLocator);
|
|
347
|
+
rowLocators = filterEngine.applyFilters(rowLocators, filters, _headerMap, (_c = options === null || options === void 0 ? void 0 : options.exact) !== null && _c !== void 0 ? _c : false, rootLocator.page());
|
|
348
|
+
let rows = yield rowLocators.all();
|
|
349
|
+
allRows.push(...rows.map((loc, i) => _makeSmart(loc, _headerMap, i)));
|
|
350
|
+
// Paginate and collect more rows
|
|
351
|
+
while (pageCount < effectiveMaxPages && config.strategies.pagination) {
|
|
352
|
+
const paginationResult = yield config.strategies.pagination({
|
|
353
|
+
root: rootLocator,
|
|
354
|
+
config,
|
|
355
|
+
resolve,
|
|
356
|
+
page: rootLocator.page()
|
|
357
|
+
});
|
|
358
|
+
const didPaginate = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
|
|
359
|
+
if (!didPaginate)
|
|
360
|
+
break;
|
|
361
|
+
pageCount++;
|
|
362
|
+
_hasPaginated = true;
|
|
363
|
+
// Collect rows from new page
|
|
364
|
+
rowLocators = resolve(config.rowSelector, rootLocator);
|
|
365
|
+
rowLocators = filterEngine.applyFilters(rowLocators, filters, _headerMap, (_d = options === null || options === void 0 ? void 0 : options.exact) !== null && _d !== void 0 ? _d : false, rootLocator.page());
|
|
366
|
+
rows = yield rowLocators.all();
|
|
367
|
+
allRows.push(...rows.map((loc, i) => _makeSmart(loc, _headerMap, i)));
|
|
368
|
+
}
|
|
369
|
+
if (options === null || options === void 0 ? void 0 : options.asJSON) {
|
|
370
|
+
return Promise.all(allRows.map(r => r.toJSON()));
|
|
371
|
+
}
|
|
372
|
+
return allRows;
|
|
353
373
|
}),
|
|
374
|
+
isInitialized: () => {
|
|
375
|
+
return _isInitialized;
|
|
376
|
+
},
|
|
354
377
|
sorting: {
|
|
355
378
|
apply: (columnName, direction) => __awaiter(void 0, void 0, void 0, function* () {
|
|
356
379
|
yield _ensureInitialized();
|
|
@@ -381,12 +404,13 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
381
404
|
init: result.init,
|
|
382
405
|
getHeaders: result.getHeaders,
|
|
383
406
|
getHeaderCell: result.getHeaderCell,
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
407
|
+
getRow: result.getRow,
|
|
408
|
+
getRowByIndex: result.getRowByIndex,
|
|
409
|
+
findRow: result.findRow,
|
|
410
|
+
getRows: result.getRows,
|
|
411
|
+
findRows: result.findRows,
|
|
387
412
|
getColumnValues: result.getColumnValues,
|
|
388
|
-
|
|
389
|
-
generateStrategyPrompt: result.generateStrategyPrompt,
|
|
413
|
+
isInitialized: result.isInitialized,
|
|
390
414
|
sorting: result.sorting,
|
|
391
415
|
scrollToColumn: result.scrollToColumn,
|
|
392
416
|
revalidate: result.revalidate,
|
|
@@ -444,6 +468,4 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
444
468
|
};
|
|
445
469
|
exports.useTable = useTable;
|
|
446
470
|
exports.PaginationStrategies = pagination_1.PaginationStrategies;
|
|
447
|
-
/** @deprecated Use Strategies.Pagination instead */
|
|
448
|
-
exports.DeprecatedTableStrategies = pagination_1.DeprecatedPaginationStrategies;
|
|
449
471
|
exports.SortingStrategies = sorting_1.SortingStrategies;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rickcedwhat/playwright-smart-table",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
|
+
"description": "Production-ready table testing for Playwright with smart column-aware locators. Core library with plugin support for custom table implementations.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/rickcedwhat/playwright-smart-table.git"
|