@vuu-ui/vuu-playwright-helpers 3.5.13 → 3.5.15

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
@@ -3,8 +3,16 @@
3
3
  Playwright helpers for Vuu components.
4
4
 
5
5
  All helpers extend `PlaywrightPageObject` and are instantiated from the Playwright `page`.
6
- `PwTable` locates its target element using a default CSS selector, which can be
7
- overridden at runtime. `PwTableHeaderCell` requires an explicit CSS selector.
6
+ Component helpers extend `PwComponent`, which locates the component root element using
7
+ a default CSS selector. This can be overridden at runtime by passing a CSS selector or a
8
+ `Locator`. `PwComponent` provides common helpers:
9
+
10
+ - `locator` - the component root `Locator`, for web-first assertions not covered by a helper
11
+ - `isReady()` - waits until the component is visible, resolves with the component
12
+ - `isVisible()` - is the component currently visible (no waiting)
13
+ - `assertVisible()`, `assertNotAttached()`
14
+
15
+ ## PwTable
8
16
 
9
17
  ```ts
10
18
  // uses the default selector (.vuuTable)
@@ -23,3 +31,84 @@ await table.editCell("currency", 2, "EUR", "cancel");
23
31
  Prefer `cell(colName, ariaRowIndex)` for new usage. `ariaRowIndex` is the 1-based
24
32
  ARIA row index, including header rows (with one header, the first data row is 2).
25
33
  The existing `locateCell` overloads remain available for gradual migration.
34
+
35
+ ## PwContextMenu, PwMenuItem
36
+
37
+ `PwTable.showContextMenu(colName, ariaRowIndex)` right clicks a cell and resolves with a
38
+ `PwContextMenu` once the menu is visible.
39
+
40
+ ```ts
41
+ const menu = await table.showContextMenu("ric", 2);
42
+
43
+ expect(await menu.hasMenuItems()).toBe(true);
44
+ // exact match, in order - a partial list is false
45
+ expect(
46
+ await menu.hasMenuItems(["Copy text", "Filter", "Sort", "Delete row"]),
47
+ ).toBe(true);
48
+ expect(await menu.menuItemLabels()).toEqual([
49
+ "Copy text",
50
+ "Filter",
51
+ "Sort",
52
+ "Delete row",
53
+ ]);
54
+
55
+ const copy = menu.menuItem("Copy text"); // or by 0 based index, menu.menuItem(0)
56
+ expect(await copy.hasIcon("edit")).toBe(true);
57
+ expect(await copy.isEnabled()).toBe(true);
58
+ expect(await menu.menuItem("Filter").hasChildren()).toBe(true);
59
+
60
+ // sub menus, opened with mouse (default) or keyboard
61
+ const filterMenu = await menu.menuItem("Filter").openSubMenu();
62
+ await filterMenu.menuItem("Include value").click();
63
+ await menu.assertNotAttached();
64
+
65
+ // keyboard
66
+ await menu.press("ArrowDown");
67
+ await menu.assertFocusedMenuItem("Copy text");
68
+ await menu.press("Enter");
69
+ await menu.close(); // Escape
70
+ ```
71
+
72
+ To assert that right clicking a cell does not show a context menu, use
73
+ `assertNoContextMenu`. It waits for the page to render after the right click
74
+ before asserting, so it cannot pass before a menu would have appeared.
75
+
76
+ ```ts
77
+ await table.assertNoContextMenu("ccy", 2);
78
+ ```
79
+
80
+ When a key press depends on the focus change made by the previous key, call
81
+ `assertFocusedMenuItem` in between. Keys pressed before the menu has processed a
82
+ focus change may be delivered to the previously focused item.
83
+
84
+ ## PwChart, PwChartDataPoint
85
+
86
+ `PwChart` wraps a Vuu `Chart` (ECharts, svg renderer). Data points are located by
87
+ series name (the Vuu column name) and 0 based data index, or category (x axis)
88
+ value, mirroring `PwTable.cell(colName, ariaRowIndex)`.
89
+
90
+ ```ts
91
+ const chart = await new PwChart(page).isReady();
92
+ expect(await chart.seriesNames()).toEqual(["price", "volume"]);
93
+ expect(await chart.categories()).toHaveLength(25);
94
+
95
+ const point = await chart.dataPoint("price", 3); // or chart.dataPoint("price", "2026-04-05")
96
+ expect(point.value).toEqual(103);
97
+ expect(point.isExcluded).toBe(false);
98
+ await point.hover();
99
+ await point.click(); // selects, with a chart selectionModel
100
+ await expect(point.symbol).toHaveAttribute("fill", "purple");
101
+
102
+ const menu = await chart.showContextMenu("price", 3); // or point.showContextMenu()
103
+ await menu.menuItem("Exclude Item").click();
104
+ await chart.assertNoContextMenu("price", 3); // when no menu is configured
105
+ ```
106
+
107
+ The data point properties (`value`, `x`, `y`, `isExcluded`) are a snapshot, taken
108
+ when the data point is obtained. Mouse actions resolve the position again and wait
109
+ until it is stable, so they work while the chart is resizing. To assert a change to
110
+ the data, obtain the data point again, e.g.
111
+ `await expect.poll(async () => (await chart.dataPoint("price", 3)).isExcluded).toBe(true)`.
112
+
113
+ The Vuu `Chart` exposes its ECharts instance on the chart canvas element
114
+ (`echartsInstance`), which `PwChart` uses to map data points to pixel positions.
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.5.13",
2
+ "version": "3.5.15",
3
3
  "author": "heswell",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./src/index.js",
@@ -9,7 +9,7 @@
9
9
  "type-defs": "node ../../scripts/build-type-defs.mjs"
10
10
  },
11
11
  "dependencies": {
12
- "@vuu-ui/vuu-protocol-types": "3.5.13"
12
+ "@vuu-ui/vuu-protocol-types": "3.5.15"
13
13
  },
14
14
  "peerDependencies": {
15
15
  "@playwright/test": "^1.62.1"
package/src/PwChart.js ADDED
@@ -0,0 +1,77 @@
1
+ import { expect } from "@playwright/test";
2
+ import { PwComponent } from "./PwComponent.js";
3
+ import { PwContextMenu } from "./PwContextMenu.js";
4
+ import { PwChartDataPoint } from "./PwChartDataPoint.js";
5
+ class PwChart extends PwComponent {
6
+ defaultCssSelector = ".vuuChart";
7
+ get canvas() {
8
+ return this.locator.locator("[_echarts_instance_]");
9
+ }
10
+ async isReady(options) {
11
+ await super.isReady(options);
12
+ await expect.poll(()=>this.canvas.evaluate((element)=>(element.echartsInstance?.getOption().series ?? []).some((series)=>series.data.length > 0)), options).toBe(true);
13
+ return this;
14
+ }
15
+ async seriesNames() {
16
+ await this.isReady();
17
+ return this.canvas.evaluate((element)=>{
18
+ const series = element.echartsInstance?.getOption().series ?? [];
19
+ return Array.from(new Set(series.map(({ name })=>name)));
20
+ });
21
+ }
22
+ async categories() {
23
+ await this.isReady();
24
+ return this.canvas.evaluate((element)=>element.echartsInstance?.getOption().xAxis?.[0]?.data ?? []);
25
+ }
26
+ async dataPoint(seriesName, dataIndexOrCategory) {
27
+ await this.isReady();
28
+ const resolve = ()=>this.resolveDataPoint(seriesName, dataIndexOrCategory);
29
+ return new PwChartDataPoint(this.page, this.canvas, await resolve(), resolve);
30
+ }
31
+ async resolveDataPoint(seriesName, dataIndexOrCategory) {
32
+ const info = await this.canvas.evaluate((element, [seriesName, dataIndexOrCategory])=>{
33
+ const chart = element.echartsInstance;
34
+ if (!chart) return "chart has no echartsInstance";
35
+ const { series = [], xAxis = [] } = chart.getOption();
36
+ const categories = xAxis[0]?.data ?? [];
37
+ const dataIndex = "number" == typeof dataIndexOrCategory ? dataIndexOrCategory : categories.indexOf(dataIndexOrCategory);
38
+ if (-1 === dataIndex) return `no category "${dataIndexOrCategory}"`;
39
+ const namedSeries = series.filter(({ name })=>name === seriesName);
40
+ if (0 === namedSeries.length) return `no series "${seriesName}"`;
41
+ const valueSeries = namedSeries.find(({ data })=>"number" == typeof data[dataIndex]?.value);
42
+ if (!valueSeries) return `series "${seriesName}" has no value at data index ${dataIndex}`;
43
+ const value = valueSeries.data[dataIndex].value;
44
+ const [x, y] = chart.convertToPixel({
45
+ seriesId: valueSeries.id
46
+ }, [
47
+ dataIndex,
48
+ value
49
+ ]);
50
+ return {
51
+ seriesName,
52
+ dataIndex,
53
+ category: categories[dataIndex],
54
+ value,
55
+ isExcluded: valueSeries.id !== seriesName,
56
+ x,
57
+ y
58
+ };
59
+ }, [
60
+ seriesName,
61
+ dataIndexOrCategory
62
+ ]);
63
+ if ("string" == typeof info) throw Error(`PwChart, dataPoint, ${info}`);
64
+ return info;
65
+ }
66
+ async showContextMenu(seriesName, dataIndexOrCategory) {
67
+ const dataPoint = await this.dataPoint(seriesName, dataIndexOrCategory);
68
+ return dataPoint.showContextMenu();
69
+ }
70
+ async assertNoContextMenu(seriesName, dataIndexOrCategory) {
71
+ const dataPoint = await this.dataPoint(seriesName, dataIndexOrCategory);
72
+ await PwContextMenu.assertNotOpened(this.page, ()=>dataPoint.click({
73
+ button: "right"
74
+ }));
75
+ }
76
+ }
77
+ export { PwChart };
@@ -0,0 +1,61 @@
1
+ import { PlaywrightPageObject } from "./PlaywrightPageObject.js";
2
+ import { PwContextMenu } from "./PwContextMenu.js";
3
+ class PwChartDataPoint extends PlaywrightPageObject {
4
+ canvas;
5
+ resolve;
6
+ seriesName;
7
+ dataIndex;
8
+ category;
9
+ value;
10
+ isExcluded;
11
+ x;
12
+ y;
13
+ constructor(page, canvas, info, resolve){
14
+ super(page), this.canvas = canvas, this.resolve = resolve;
15
+ this.seriesName = info.seriesName;
16
+ this.dataIndex = info.dataIndex;
17
+ this.category = info.category;
18
+ this.value = info.value;
19
+ this.isExcluded = info.isExcluded;
20
+ this.x = info.x;
21
+ this.y = info.y;
22
+ }
23
+ get symbol() {
24
+ const position = `,${round(this.x)},${round(this.y)})`;
25
+ return this.canvas.locator(`svg path[transform$="${position}"]`);
26
+ }
27
+ async click(options) {
28
+ const position = await this.stablePosition();
29
+ await this.canvas.click({
30
+ ...options,
31
+ position
32
+ });
33
+ }
34
+ async hover() {
35
+ await this.canvas.hover({
36
+ position: await this.stablePosition()
37
+ });
38
+ }
39
+ showContextMenu() {
40
+ return PwContextMenu.open(this.page, ()=>this.click({
41
+ button: "right"
42
+ }));
43
+ }
44
+ async stablePosition() {
45
+ let { x, y } = await this.resolve();
46
+ for(let attempt = 0; attempt < MAX_STABLE_ATTEMPTS; attempt++){
47
+ await nextAnimationFrames(this.page);
48
+ const current = await this.resolve();
49
+ if (current.x === x && current.y === y) return {
50
+ x,
51
+ y
52
+ };
53
+ ({ x, y } = current);
54
+ }
55
+ throw Error(`PwChartDataPoint, ${this.seriesName}[${this.dataIndex}] position is not stable`);
56
+ }
57
+ }
58
+ const MAX_STABLE_ATTEMPTS = 50;
59
+ const round = (value)=>parseFloat(value.toFixed(4));
60
+ const nextAnimationFrames = (page)=>page.evaluate(()=>new Promise((resolve)=>requestAnimationFrame(()=>requestAnimationFrame(()=>resolve()))));
61
+ export { PwChartDataPoint };
@@ -0,0 +1,29 @@
1
+ import { expect } from "@playwright/test";
2
+ import { PlaywrightPageObject } from "./PlaywrightPageObject.js";
3
+ class PwComponent extends PlaywrightPageObject {
4
+ target;
5
+ constructor(page, target){
6
+ super(page), this.target = target;
7
+ }
8
+ get locator() {
9
+ if ("string" == typeof this.target || void 0 === this.target) return this.page.locator(this.target ?? this.defaultCssSelector);
10
+ return this.target;
11
+ }
12
+ async isReady(options) {
13
+ await this.locator.waitFor({
14
+ state: "visible",
15
+ ...options
16
+ });
17
+ return this;
18
+ }
19
+ isVisible() {
20
+ return this.locator.isVisible();
21
+ }
22
+ async assertVisible() {
23
+ await expect(this.locator).toBeVisible();
24
+ }
25
+ async assertNotAttached() {
26
+ await expect(this.locator).not.toBeAttached();
27
+ }
28
+ }
29
+ export { PwComponent };
@@ -0,0 +1,53 @@
1
+ import { expect } from "@playwright/test";
2
+ import { PwComponent } from "./PwComponent.js";
3
+ import { PwMenuItem } from "./PwMenuItem.js";
4
+ import { byId } from "./selector-utils.js";
5
+ class PwContextMenu extends PwComponent {
6
+ defaultCssSelector = '.vuuContextMenuPanel[role="menu"]';
7
+ static async open(page, openAction) {
8
+ await openAction();
9
+ const menu = await new PwContextMenu(page).isReady();
10
+ const menuId = await menu.locator.getAttribute("id");
11
+ const contextMenu = menuId ? new PwContextMenu(page, byId(menuId)) : menu;
12
+ await expect(contextMenu.locator.and(page.locator(":focus-within"))).toHaveCount(1);
13
+ return contextMenu;
14
+ }
15
+ static async assertNotOpened(page, openAction) {
16
+ await openAction();
17
+ await page.evaluate(()=>new Promise((resolve)=>requestAnimationFrame(()=>requestAnimationFrame(()=>resolve()))));
18
+ await new PwContextMenu(page).assertNotAttached();
19
+ }
20
+ get menuItems() {
21
+ return this.locator.getByRole("menuitem");
22
+ }
23
+ async menuItemLabels() {
24
+ return (await this.menuItems.allInnerTexts()).map((text)=>text.trim());
25
+ }
26
+ async hasMenuItems(labels) {
27
+ if (void 0 === labels) return await this.menuItems.count() > 0;
28
+ const menuItemLabels = await this.menuItemLabels();
29
+ return menuItemLabels.length === labels.length && menuItemLabels.every((label, i)=>label === labels[i]);
30
+ }
31
+ menuItem(labelOrIndex) {
32
+ const locator = "number" == typeof labelOrIndex ? this.menuItems.nth(labelOrIndex) : this.menuItems.filter({
33
+ has: this.page.getByText(labelOrIndex, {
34
+ exact: true
35
+ })
36
+ });
37
+ return new PwMenuItem(this.page, locator);
38
+ }
39
+ get focusedMenuItem() {
40
+ return new PwMenuItem(this.page, this.menuItems.and(this.page.locator(':focus[tabindex="0"]')));
41
+ }
42
+ async assertFocusedMenuItem(label) {
43
+ await expect(this.focusedMenuItem.locator).toHaveText(label);
44
+ }
45
+ async press(...keys) {
46
+ for (const key of keys)await this.page.keyboard.press(key);
47
+ }
48
+ async close() {
49
+ await this.press("Escape");
50
+ await this.assertNotAttached();
51
+ }
52
+ }
53
+ export { PwContextMenu };
@@ -0,0 +1,58 @@
1
+ import { expect } from "@playwright/test";
2
+ import { PwComponent } from "./PwComponent.js";
3
+ import { PwContextMenu } from "./PwContextMenu.js";
4
+ import { byId } from "./selector-utils.js";
5
+ class PwMenuItem extends PwComponent {
6
+ defaultCssSelector = '[role="menuitem"]';
7
+ async label() {
8
+ return (await this.locator.innerText()).trim();
9
+ }
10
+ async hasChildren() {
11
+ return await this.locator.getAttribute("aria-haspopup") === "menu";
12
+ }
13
+ async isDisabled() {
14
+ return await this.locator.getAttribute("aria-disabled") === "true";
15
+ }
16
+ async isEnabled() {
17
+ return !await this.isDisabled();
18
+ }
19
+ async isExpanded() {
20
+ return await this.locator.getAttribute("aria-expanded") === "true";
21
+ }
22
+ isFocused() {
23
+ return this.locator.evaluate((element)=>element === element.ownerDocument.activeElement);
24
+ }
25
+ async hasIcon(iconName) {
26
+ return await this.locator.getAttribute("data-icon") === iconName;
27
+ }
28
+ async click(options) {
29
+ await this.locator.click(options);
30
+ }
31
+ async hover() {
32
+ await this.locator.hover();
33
+ }
34
+ async focus() {
35
+ await this.locator.focus();
36
+ }
37
+ async press(key) {
38
+ await this.locator.press(key);
39
+ }
40
+ async subMenu() {
41
+ const subMenuId = await this.locator.getAttribute("aria-controls");
42
+ if (null === subMenuId) throw Error(`PwMenuItem, subMenu, menu item "${await this.label()}" has no open sub menu`);
43
+ return new PwContextMenu(this.page, byId(subMenuId)).isReady();
44
+ }
45
+ async openSubMenu(using = "mouse") {
46
+ if (!await this.hasChildren()) throw Error(`PwMenuItem, openSubMenu, menu item "${await this.label()}" has no sub menu`);
47
+ if ("mouse" === using) await this.hover();
48
+ else {
49
+ await this.focus();
50
+ await this.press("ArrowRight");
51
+ }
52
+ await this.locator.and(this.page.locator('[aria-expanded="true"]')).waitFor();
53
+ const subMenu = await this.subMenu();
54
+ if ("keyboard" === using) await expect(subMenu.focusedMenuItem.locator).toHaveCount(1);
55
+ return subMenu;
56
+ }
57
+ }
58
+ export { PwMenuItem };
package/src/PwTable.js CHANGED
@@ -1,34 +1,24 @@
1
1
  import { expect } from "@playwright/test";
2
- import { PlaywrightPageObject } from "./PlaywrightPageObject.js";
2
+ import { PwComponent } from "./PwComponent.js";
3
+ import { PwContextMenu } from "./PwContextMenu.js";
3
4
  import { PwTableHeaderCell } from "./PwTableHeaderCell.js";
5
+ import { byDataIcon, cssAttributeValue } from "./selector-utils.js";
4
6
  const byAriaRowIndex = (ariaRowIndex)=>`[aria-rowindex="${ariaRowIndex}"]:scope`;
5
7
  const xpathStringLiteral = (value)=>{
6
8
  if (!value.includes("'")) return `'${value}'`;
7
9
  if (!value.includes('"')) return `"${value}"`;
8
10
  return `concat('${value.split("'").join("', \"'\", '")}')`;
9
11
  };
10
- const cssAttributeValue = (value)=>Array.from(value, (character)=>{
11
- const code = character.charCodeAt(0);
12
- return '"' === character || "\\" === character || code < 32 || 127 === code ? `\\${code.toString(16)} ` : character;
13
- }).join("");
14
- const byDataIcon = (iconName)=>`[data-icon="${cssAttributeValue(iconName)}"]`;
15
- class PwTable extends PlaywrightPageObject {
16
- cssSelector;
12
+ class PwTable extends PwComponent {
17
13
  defaultCssSelector = ".vuuTable";
18
- constructor(page, cssSelector){
19
- super(page), this.cssSelector = cssSelector;
20
- }
21
- get #locator() {
22
- return this.page.locator(this.cssSelector ?? this.defaultCssSelector);
23
- }
24
14
  get contentContainer() {
25
- return this.#locator.locator(".vuuTable-contentContainer");
15
+ return this.locator.locator(".vuuTable-contentContainer");
26
16
  }
27
17
  get scrollbarContainer() {
28
- return this.#locator.locator(".vuuTable-scrollbarContainer");
18
+ return this.locator.locator(".vuuTable-scrollbarContainer");
29
19
  }
30
20
  row(row) {
31
- return this.#locator.locator(`[aria-rowindex="${row}"]`);
21
+ return this.locator.locator(`[aria-rowindex="${row}"]`);
32
22
  }
33
23
  cell(colName, ariaRowIndex) {
34
24
  return this.locateCell(ariaRowIndex, colName);
@@ -42,10 +32,10 @@ class PwTable extends PlaywrightPageObject {
42
32
  await textbox.press("commit" === action ? "Enter" : "Escape");
43
33
  }
44
34
  async columnNames() {
45
- return this.#locator.locator(".vuuTableHeaderCell .vuuTableHeaderCell-label").allTextContents();
35
+ return this.locator.locator(".vuuTableHeaderCell .vuuTableHeaderCell-label").allTextContents();
46
36
  }
47
37
  async rowCount() {
48
- const ariaRowCount = await this.#locator.getByRole("table").getAttribute("aria-rowcount");
38
+ const ariaRowCount = await this.locator.getByRole("table").getAttribute("aria-rowcount");
49
39
  if (null === ariaRowCount) throw Error("PwTable, rowCount, table does not have an aria-rowcount");
50
40
  return parseInt(ariaRowCount, 10);
51
41
  }
@@ -54,23 +44,36 @@ class PwTable extends PlaywrightPageObject {
54
44
  return cell.locator(byDataIcon(iconName));
55
45
  }
56
46
  cellsWithIcon(iconName) {
57
- return this.#locator.getByRole("cell").filter({
47
+ return this.locator.getByRole("cell").filter({
58
48
  has: this.page.locator(byDataIcon(iconName))
59
49
  });
60
50
  }
61
51
  headerCell(columnName) {
62
- return new PwTableHeaderCell(this.page, `${this.cssSelector ?? this.defaultCssSelector} >> .vuuTableHeaderCell[data-column-name="${cssAttributeValue(columnName)}"]`);
52
+ return new PwTableHeaderCell(this.page, this.locator.locator(`.vuuTableHeaderCell[data-column-name="${cssAttributeValue(columnName)}"]`));
53
+ }
54
+ async showContextMenu(columnName, ariaRowIndex) {
55
+ const cell = await this.cell(columnName, ariaRowIndex);
56
+ return PwContextMenu.open(this.page, ()=>cell.click({
57
+ button: "right"
58
+ }));
59
+ }
60
+ async assertNoContextMenu(columnName, ariaRowIndex) {
61
+ const cell = await this.cell(columnName, ariaRowIndex);
62
+ await PwContextMenu.assertNotOpened(this.page, ()=>cell.click({
63
+ button: "right"
64
+ }));
65
+ await expect(cell).not.toContainClass("ContextOpen");
63
66
  }
64
67
  locateCell(rowOrText, column) {
65
- if ("number" == typeof rowOrText && "number" == typeof column) return this.#locator.locator(`[aria-rowindex="${rowOrText}"] > [aria-colindex="${column}"]`);
68
+ if ("number" == typeof rowOrText && "number" == typeof column) return this.locator.locator(`[aria-rowindex="${rowOrText}"] > [aria-colindex="${column}"]`);
66
69
  if ("number" == typeof rowOrText && "string" == typeof column) {
67
- const columnHeader = this.#locator.locator(`xpath=.//*[@data-column-name=${xpathStringLiteral(column)}]`).first();
70
+ const columnHeader = this.locator.locator(`xpath=.//*[@data-column-name=${xpathStringLiteral(column)}]`).first();
68
71
  return columnHeader.getAttribute("aria-colindex").then((ariaColIndex)=>{
69
72
  if (null === ariaColIndex) throw Error(`PwTable, locateCell, column "${column}" does not have an aria-colindex`);
70
- return this.#locator.locator(`[aria-rowindex="${rowOrText}"] > [aria-colindex="${ariaColIndex}"]`);
73
+ return this.locator.locator(`[aria-rowindex="${rowOrText}"] > [aria-colindex="${ariaColIndex}"]`);
71
74
  });
72
75
  }
73
- if ("string" == typeof rowOrText) return this.#locator.getByRole("cell", {
76
+ if ("string" == typeof rowOrText) return this.locator.getByRole("cell", {
74
77
  name: rowOrText
75
78
  });
76
79
  }
@@ -88,9 +91,6 @@ class PwTable extends PlaywrightPageObject {
88
91
  }
89
92
  }
90
93
  }
91
- async assertVisible() {
92
- return expect(this.#locator).toBeVisible();
93
- }
94
94
  getCellText(arg1, arg2, arg3, arg4) {
95
95
  if ("number" == typeof arg1 && "number" == typeof arg2) return [
96
96
  this.locateCell(arg1, arg2),
@@ -146,16 +146,16 @@ class PwTable extends PlaywrightPageObject {
146
146
  const offsetFromEnd = totalRowCount - to;
147
147
  const trailingBufferedRows = offsetFromEnd < renderBufferSize ? Math.min(0, offsetFromEnd) : renderBufferSize;
148
148
  const renderedRowCount = to - from + leadingBufferedRows + trailingBufferedRows;
149
- await expect(this.#locator.getByRole("row")).toHaveCount(renderedRowCount + headerCount);
149
+ await expect(this.locator.getByRole("row")).toHaveCount(renderedRowCount + headerCount);
150
150
  const firstRenderedRow = from - leadingBufferedRows + headerCount + 1;
151
151
  const lastVisibleRow = to + headerCount;
152
152
  const lastRenderedRow = to + headerCount + trailingBufferedRows;
153
- await expect(this.#locator.getByRole("row").locator(byAriaRowIndex(firstRenderedRow))).toBeVisible();
154
- await expect(this.#locator.getByRole("row").locator(byAriaRowIndex(lastVisibleRow))).toBeVisible();
153
+ await expect(this.locator.getByRole("row").locator(byAriaRowIndex(firstRenderedRow))).toBeVisible();
154
+ await expect(this.locator.getByRole("row").locator(byAriaRowIndex(lastVisibleRow))).toBeVisible();
155
155
  if (trailingBufferedRows > 0) {
156
- await expect(this.#locator.getByRole("row").locator(byAriaRowIndex(lastVisibleRow + 1))).not.toBeInViewport();
157
- await expect(this.#locator.getByRole("row").locator(byAriaRowIndex(lastRenderedRow))).not.toBeInViewport();
158
- await expect(this.#locator.getByRole("row").locator(byAriaRowIndex(lastRenderedRow + 1))).not.toBeAttached();
156
+ await expect(this.locator.getByRole("row").locator(byAriaRowIndex(lastVisibleRow + 1))).not.toBeInViewport();
157
+ await expect(this.locator.getByRole("row").locator(byAriaRowIndex(lastRenderedRow))).not.toBeInViewport();
158
+ await expect(this.locator.getByRole("row").locator(byAriaRowIndex(lastRenderedRow + 1))).not.toBeAttached();
159
159
  }
160
160
  }
161
161
  async assertRenderedColumns({ rendered, visible }) {
@@ -1,15 +1,15 @@
1
1
  import { expect } from "@playwright/test";
2
- import { PlaywrightPageObject } from "./PlaywrightPageObject.js";
3
- class PwTableHeaderCell extends PlaywrightPageObject {
4
- cssSelector;
5
- constructor(page, cssSelector){
6
- super(page), this.cssSelector = cssSelector;
2
+ import { PwComponent } from "./PwComponent.js";
3
+ class PwTableHeaderCell extends PwComponent {
4
+ defaultCssSelector = ".vuuTableHeaderCell";
5
+ constructor(page, target){
6
+ super(page, target);
7
7
  }
8
8
  get isSelected() {
9
- return this.page.locator(this.cssSelector).evaluate((element)=>element.classList.contains("vuuTableHeaderCell-selected"));
9
+ return this.locator.evaluate((element)=>element.classList.contains("vuuTableHeaderCell-selected"));
10
10
  }
11
11
  async assertSelected() {
12
- await expect(this.page.locator(this.cssSelector)).toContainClass("vuuTableHeaderCell-selected");
12
+ await expect(this.locator).toContainClass("vuuTableHeaderCell-selected");
13
13
  }
14
14
  }
15
15
  export { PwTableHeaderCell };
package/src/index.js CHANGED
@@ -1,3 +1,8 @@
1
1
  export { PlaywrightPageObject } from "./PlaywrightPageObject.js";
2
+ export { PwChart } from "./PwChart.js";
3
+ export { PwChartDataPoint } from "./PwChartDataPoint.js";
4
+ export { PwComponent } from "./PwComponent.js";
5
+ export { PwContextMenu } from "./PwContextMenu.js";
6
+ export { PwMenuItem } from "./PwMenuItem.js";
2
7
  export { PwTable } from "./PwTable.js";
3
8
  export { PwTableHeaderCell } from "./PwTableHeaderCell.js";
@@ -0,0 +1,7 @@
1
+ const cssAttributeValue = (value)=>Array.from(value, (character)=>{
2
+ const code = character.charCodeAt(0);
3
+ return '"' === character || "\\" === character || code < 32 || 127 === code ? `\\${code.toString(16)} ` : character;
4
+ }).join("");
5
+ const byDataIcon = (iconName)=>`[data-icon="${cssAttributeValue(iconName)}"]`;
6
+ const byId = (id)=>`[id="${cssAttributeValue(id)}"]`;
7
+ export { byDataIcon, byId, cssAttributeValue };