@vuu-ui/vuu-playwright-helpers 3.5.20 → 3.5.22

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/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.5.20",
2
+ "version": "3.5.22",
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.20"
12
+ "@vuu-ui/vuu-protocol-types": "3.5.22"
13
13
  },
14
14
  "peerDependencies": {
15
15
  "@playwright/test": "^1.62.1"
@@ -0,0 +1,44 @@
1
+ import { expect } from "@playwright/test";
2
+ import { PwComponent } from "./PwComponent.js";
3
+ import { cssAttributeValue } from "./selector-utils.js";
4
+ class PwColumnPicker extends PwComponent {
5
+ defaultCssSelector = ".vuuColumnPicker";
6
+ get selectedOptions() {
7
+ return this.locator.locator(".vuuItemPicker-selectedList [role='option']");
8
+ }
9
+ get availableOptions() {
10
+ return this.locator.locator(".vuuItemPicker-availableList [role='option']");
11
+ }
12
+ selectedColumns() {
13
+ return this.optionNames(this.selectedOptions);
14
+ }
15
+ availableColumns() {
16
+ return this.optionNames(this.availableOptions);
17
+ }
18
+ async addColumn(columnName) {
19
+ await this.clickAction(this.option(this.availableOptions, columnName));
20
+ }
21
+ async removeColumn(columnName) {
22
+ await this.clickAction(this.option(this.selectedOptions, columnName));
23
+ }
24
+ async search(text) {
25
+ await this.locator.getByRole("search").getByRole("textbox").fill(text);
26
+ }
27
+ async assertSelectedColumns(columnNames) {
28
+ await expect.poll(()=>this.selectedColumns()).toEqual(columnNames);
29
+ }
30
+ async assertAvailableColumns(columnNames) {
31
+ await expect.poll(()=>this.availableColumns()).toEqual(expect.arrayContaining(columnNames));
32
+ }
33
+ async clickAction(option) {
34
+ await option.hover();
35
+ await option.locator(".vuuItemPickerListItem-action").click();
36
+ }
37
+ option(options, columnName) {
38
+ return options.and(this.page.locator(`[data-name="${cssAttributeValue(columnName)}"]`));
39
+ }
40
+ optionNames(options) {
41
+ return options.evaluateAll((elements)=>elements.map((element)=>element.getAttribute("data-name") ?? ""));
42
+ }
43
+ }
44
+ export { PwColumnPicker };
package/src/PwTable.js CHANGED
@@ -34,11 +34,31 @@ class PwTable extends PwComponent {
34
34
  async columnNames() {
35
35
  return this.locator.locator(".vuuTableHeaderCell .vuuTableHeaderCell-label").allTextContents();
36
36
  }
37
+ headerColumnNames() {
38
+ return this.locator.locator(".vuuTableHeaderCell[data-column-name]").evaluateAll((elements)=>elements.map((element)=>element.getAttribute("data-column-name") ?? ""));
39
+ }
40
+ async assertColumns(columnNames) {
41
+ await expect.poll(()=>this.headerColumnNames()).toEqual(columnNames);
42
+ }
37
43
  async rowCount() {
38
44
  const ariaRowCount = await this.locator.getByRole("table").getAttribute("aria-rowcount");
39
45
  if (null === ariaRowCount) throw Error("PwTable, rowCount, table does not have an aria-rowcount");
40
46
  return parseInt(ariaRowCount, 10);
41
47
  }
48
+ get dataLoaded() {
49
+ return expect.poll(()=>this.rowCount()).toBeGreaterThan(0);
50
+ }
51
+ async columnValues(columnName) {
52
+ const headerCell = this.locator.locator(`xpath=.//*[@role='columnheader' and @data-column-name=${xpathStringLiteral(columnName)}]`).first();
53
+ const ariaColIndex = await headerCell.getAttribute("aria-colindex");
54
+ if (null === ariaColIndex) throw Error(`PwTable, columnValues, column "${columnName}" does not have an aria-colindex`);
55
+ const cells = this.locator.locator(`[role="row"] > [role="cell"][aria-colindex="${ariaColIndex}"]`);
56
+ const values = await cells.evaluateAll((elements)=>elements.map((cell)=>({
57
+ rowIndex: Number(cell.parentElement?.getAttribute("aria-rowindex")),
58
+ text: cell.textContent ?? ""
59
+ })));
60
+ return values.sort((v1, v2)=>v1.rowIndex - v2.rowIndex).map(({ text })=>text);
61
+ }
42
62
  async cellIcon(columnName, ariaRowIndex, iconName) {
43
63
  const cell = await this.cell(columnName, ariaRowIndex);
44
64
  return cell.locator(byDataIcon(iconName));
@@ -0,0 +1,16 @@
1
+ import { PwColumnPicker } from "./PwColumnPicker.js";
2
+ import { PwComponent } from "./PwComponent.js";
3
+ class PwTableFooter extends PwComponent {
4
+ defaultCssSelector = ".vuuTableFooter";
5
+ action(name) {
6
+ return this.locator.getByRole("button", {
7
+ name,
8
+ exact: true
9
+ });
10
+ }
11
+ async showColumnPicker(name = "Show column picker") {
12
+ await this.action(name).click();
13
+ return new PwColumnPicker(this.page).isReady();
14
+ }
15
+ }
16
+ export { PwTableFooter };
@@ -1,5 +1,6 @@
1
1
  import { expect } from "@playwright/test";
2
2
  import { PwComponent } from "./PwComponent.js";
3
+ import { PwContextMenu } from "./PwContextMenu.js";
3
4
  class PwTableHeaderCell extends PwComponent {
4
5
  defaultCssSelector = ".vuuTableHeaderCell";
5
6
  constructor(page, target){
@@ -11,5 +12,12 @@ class PwTableHeaderCell extends PwComponent {
11
12
  async assertSelected() {
12
13
  await expect(this.locator).toContainClass("vuuTableHeaderCell-selected");
13
14
  }
15
+ async showColumnMenu() {
16
+ await this.locator.getByRole("button", {
17
+ name: "Open Column Menu",
18
+ exact: true
19
+ }).click();
20
+ return new PwContextMenu(this.page, this.page.locator(".saltMenuPanel[role='menu']")).isReady();
21
+ }
14
22
  }
15
23
  export { PwTableHeaderCell };
package/src/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  export { PlaywrightPageObject } from "./PlaywrightPageObject.js";
2
2
  export { PwChart } from "./PwChart.js";
3
3
  export { PwChartDataPoint } from "./PwChartDataPoint.js";
4
+ export { PwColumnPicker } from "./PwColumnPicker.js";
4
5
  export { PwComponent } from "./PwComponent.js";
5
6
  export { PwContextMenu } from "./PwContextMenu.js";
6
7
  export { PwMenuItem } from "./PwMenuItem.js";
7
8
  export { PwTable } from "./PwTable.js";
9
+ export { PwTableFooter } from "./PwTableFooter.js";
8
10
  export { PwTableHeaderCell } from "./PwTableHeaderCell.js";
@@ -0,0 +1,116 @@
1
+ import { type Locator } from "@playwright/test";
2
+ import { PwComponent } from "./PwComponent";
3
+ /**
4
+ * A ColumnPicker. The upper list holds the columns in view (selected), the
5
+ * lower list the columns available to add. Columns are identified by name
6
+ * (the data-name of the list option), not by display label.
7
+ *
8
+ * @example
9
+ * test("add a column", async ({ page }) => {
10
+ * const picker = await new PwTableFooter(page).showColumnPicker();
11
+ * await picker.addColumn("lotSize");
12
+ * await picker.assertSelectedColumns(["bbg", "currency", "lotSize"]);
13
+ * });
14
+ */
15
+ export declare class PwColumnPicker extends PwComponent {
16
+ protected readonly defaultCssSelector = ".vuuColumnPicker";
17
+ /**
18
+ * Locator for the options in the list of selected columns (columns in view).
19
+ *
20
+ * @example
21
+ * test("four columns in view", async ({ page }) => {
22
+ * const picker = await new PwTableFooter(page).showColumnPicker();
23
+ * await expect(picker.selectedOptions).toHaveCount(4);
24
+ * });
25
+ */
26
+ get selectedOptions(): Locator;
27
+ /**
28
+ * Locator for the options in the list of available columns.
29
+ *
30
+ * @example
31
+ * test("three columns available", async ({ page }) => {
32
+ * const picker = await new PwTableFooter(page).showColumnPicker();
33
+ * await expect(picker.availableOptions).toHaveCount(3);
34
+ * });
35
+ */
36
+ get availableOptions(): Locator;
37
+ /**
38
+ * The names of the selected columns (columns in view), in list order.
39
+ *
40
+ * @example
41
+ * test("columns in view", async ({ page }) => {
42
+ * const picker = await new PwTableFooter(page).showColumnPicker();
43
+ * expect(await picker.selectedColumns()).toEqual(["bbg", "currency"]);
44
+ * });
45
+ */
46
+ selectedColumns(): Promise<string[]>;
47
+ /**
48
+ * The names of the available columns, in list order.
49
+ *
50
+ * @example
51
+ * test("columns available", async ({ page }) => {
52
+ * const picker = await new PwTableFooter(page).showColumnPicker();
53
+ * expect(await picker.availableColumns()).toContain("lotSize");
54
+ * });
55
+ */
56
+ availableColumns(): Promise<string[]>;
57
+ /**
58
+ * Add a column to the view. Hovers the column in the available list, then
59
+ * clicks its add button.
60
+ *
61
+ * @example
62
+ * test("add lotSize", async ({ page }) => {
63
+ * const picker = await new PwTableFooter(page).showColumnPicker();
64
+ * await picker.addColumn("lotSize");
65
+ * });
66
+ */
67
+ addColumn(columnName: string): Promise<void>;
68
+ /**
69
+ * Remove a column from the view. Hovers the column in the selected list,
70
+ * then clicks its remove button.
71
+ *
72
+ * @example
73
+ * test("remove currency", async ({ page }) => {
74
+ * const picker = await new PwTableFooter(page).showColumnPicker();
75
+ * await picker.removeColumn("currency");
76
+ * });
77
+ */
78
+ removeColumn(columnName: string): Promise<void>;
79
+ /**
80
+ * Enter text in the search input, filtering both lists.
81
+ *
82
+ * @example
83
+ * test("search for lot", async ({ page }) => {
84
+ * const picker = await new PwTableFooter(page).showColumnPicker();
85
+ * await picker.search("lot");
86
+ * expect(await picker.availableColumns()).toEqual(["lotSize"]);
87
+ * });
88
+ */
89
+ search(text: string): Promise<void>;
90
+ /**
91
+ * Assert the names of the selected columns, in order, retrying until the
92
+ * expect timeout.
93
+ *
94
+ * @example
95
+ * test("columns in view", async ({ page }) => {
96
+ * const picker = await new PwTableFooter(page).showColumnPicker();
97
+ * await picker.assertSelectedColumns(["bbg", "currency"]);
98
+ * });
99
+ */
100
+ assertSelectedColumns(columnNames: string[]): Promise<void>;
101
+ /**
102
+ * Assert that the named columns are in the available list, retrying until
103
+ * the expect timeout. Other columns may also be available.
104
+ *
105
+ * @example
106
+ * test("currency is available again", async ({ page }) => {
107
+ * const picker = await new PwTableFooter(page).showColumnPicker();
108
+ * await picker.removeColumn("currency");
109
+ * await picker.assertAvailableColumns(["currency"]);
110
+ * });
111
+ */
112
+ assertAvailableColumns(columnNames: string[]): Promise<void>;
113
+ private clickAction;
114
+ private option;
115
+ private optionNames;
116
+ }
@@ -89,6 +89,28 @@ export declare class PwTable extends PwComponent {
89
89
  * });
90
90
  */
91
91
  columnNames(): Promise<string[]>;
92
+ /**
93
+ * The names (not labels) of the columns in the table, from the
94
+ * data-column-name of the header cells, in rendered order.
95
+ *
96
+ * @example
97
+ * test("renders expected columns", async ({ page }) => {
98
+ * const table = new PwTable(page);
99
+ * expect(await table.headerColumnNames()).toEqual(["ccy", "exchange"]);
100
+ * });
101
+ */
102
+ headerColumnNames(): Promise<string[]>;
103
+ /**
104
+ * Assert the names (not labels) of the columns in the table, in order,
105
+ * retrying until the expect timeout.
106
+ *
107
+ * @example
108
+ * test("column added", async ({ page }) => {
109
+ * const table = new PwTable(page);
110
+ * await table.assertColumns(["ccy", "exchange", "lotSize"]);
111
+ * });
112
+ */
113
+ assertColumns(columnNames: string[]): Promise<void>;
92
114
  /**
93
115
  * The total row count, read from aria-rowcount on the element with role="table".
94
116
  * This is the dataSource size, not the number of rendered rows.
@@ -100,6 +122,32 @@ export declare class PwTable extends PwComponent {
100
122
  * });
101
123
  */
102
124
  rowCount(): Promise<number>;
125
+ /**
126
+ * Wait until the table has loaded data, i.e. rowCount is greater than zero.
127
+ *
128
+ * @example
129
+ * test("table loads", async ({ mount, page }) => {
130
+ * await mount("Filters/FilterContainer/FilterContainerWithTable");
131
+ * const table = new PwTable(page);
132
+ * await table.dataLoaded;
133
+ * });
134
+ */
135
+ get dataLoaded(): Promise<void>;
136
+ /**
137
+ * The text content of the data cells in a column, exactly as formatted in
138
+ * the table, in row order. These are the cells in data rows with the same
139
+ * aria-colindex as the column header. The table is virtualised, so only
140
+ * rendered rows are included.
141
+ *
142
+ * @example
143
+ * test("filter on currency", async ({ page }) => {
144
+ * const table = new PwTable(page);
145
+ * await expect
146
+ * .poll(async () => new Set(await table.columnValues("currency")))
147
+ * .toEqual(new Set(["GBP"]));
148
+ * });
149
+ */
150
+ columnValues(columnName: string): Promise<string[]>;
103
151
  /**
104
152
  * Locate the icon with the given data-icon name within a cell.
105
153
  *
@@ -0,0 +1,40 @@
1
+ import type { Locator } from "@playwright/test";
2
+ import { PwColumnPicker } from "./PwColumnPicker";
3
+ import { PwComponent } from "./PwComponent";
4
+ /**
5
+ * A TableFooter, usually rendered below a Table. Holds DataSourceStats and
6
+ * a tray of actions, e.g. the ColumnPickerAction.
7
+ *
8
+ * @example
9
+ * test("footer shows column picker", async ({ page }) => {
10
+ * const footer = new PwTableFooter(page);
11
+ * const picker = await footer.showColumnPicker();
12
+ * await picker.assertVisible();
13
+ * });
14
+ */
15
+ export declare class PwTableFooter extends PwComponent {
16
+ protected readonly defaultCssSelector = ".vuuTableFooter";
17
+ /**
18
+ * An action button in the footer, by accessible name.
19
+ *
20
+ * @example
21
+ * test("column picker action", async ({ page }) => {
22
+ * const footer = new PwTableFooter(page);
23
+ * await expect(footer.action("Show column picker")).toBeVisible();
24
+ * });
25
+ */
26
+ action(name: string): Locator;
27
+ /**
28
+ * Click the ColumnPickerAction. Resolves with the ColumnPicker once it is
29
+ * visible.
30
+ *
31
+ * @param name accessible name of the action, if not the default
32
+ *
33
+ * @example
34
+ * test("open column picker", async ({ page }) => {
35
+ * const picker = await new PwTableFooter(page).showColumnPicker();
36
+ * expect(await picker.selectedColumns()).toContain("bbg");
37
+ * });
38
+ */
39
+ showColumnPicker(name?: string): Promise<PwColumnPicker>;
40
+ }
@@ -1,5 +1,6 @@
1
1
  import { type Page } from "@playwright/test";
2
2
  import { PwComponent, type PwComponentTarget } from "./PwComponent";
3
+ import { PwContextMenu } from "./PwContextMenu";
3
4
  /**
4
5
  * A Table column header cell. Usually obtained from PwTable.headerCell.
5
6
  *
@@ -38,4 +39,17 @@ export declare class PwTableHeaderCell extends PwComponent {
38
39
  * });
39
40
  */
40
41
  assertSelected(): Promise<void>;
42
+ /**
43
+ * Click the column menu button in the header cell. Resolves with the column
44
+ * menu once it is visible.
45
+ *
46
+ * @example
47
+ * test("remove column from column menu", async ({ page }) => {
48
+ * const table = new PwTable(page);
49
+ * const menu = await table.headerCell("exchange").showColumnMenu();
50
+ * await menu.menuItem("Remove exchange column").click();
51
+ * await table.assertColumns(["bbg", "currency"]);
52
+ * });
53
+ */
54
+ showColumnMenu(): Promise<PwContextMenu>;
41
55
  }
package/types/index.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  export { PlaywrightPageObject } from "./PlaywrightPageObject";
2
2
  export { PwChart } from "./PwChart";
3
3
  export { type ChartDataPointInfo, PwChartDataPoint } from "./PwChartDataPoint";
4
+ export { PwColumnPicker } from "./PwColumnPicker";
4
5
  export { PwComponent, type PwComponentTarget } from "./PwComponent";
5
6
  export { PwContextMenu } from "./PwContextMenu";
6
7
  export { PwMenuItem } from "./PwMenuItem";
7
8
  export { PwTable } from "./PwTable";
9
+ export { PwTableFooter } from "./PwTableFooter";
8
10
  export { PwTableHeaderCell } from "./PwTableHeaderCell";