@elastic/eui-test-helpers 1.3.0 → 1.4.0-snapshot.1787665877015
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 +2 -0
- package/lib/cjs/components/basic_table/selectors.d.ts +17 -0
- package/lib/cjs/components/basic_table/selectors.js +31 -0
- package/lib/cjs/components/selectable/selectors.d.ts +11 -0
- package/lib/cjs/components/selectable/selectors.js +25 -0
- package/lib/cjs/index.d.ts +2 -0
- package/lib/cjs/index.js +15 -1
- package/lib/cjs/playwright/components/basic_table/object.d.ts +50 -0
- package/lib/cjs/playwright/components/basic_table/object.js +93 -0
- package/lib/cjs/playwright/components/selectable/object.d.ts +53 -0
- package/lib/cjs/playwright/components/selectable/object.js +92 -0
- package/lib/cjs/selectors.d.ts +1 -0
- package/lib/cjs/selectors.js +8 -1
- package/lib/esm/components/basic_table/selectors.d.ts +17 -0
- package/lib/esm/components/basic_table/selectors.js +25 -0
- package/lib/esm/components/basic_table/selectors.js.map +1 -0
- package/lib/esm/components/selectable/selectors.d.ts +11 -0
- package/lib/esm/components/selectable/selectors.js +19 -0
- package/lib/esm/components/selectable/selectors.js.map +1 -0
- package/lib/esm/index.d.ts +2 -0
- package/lib/esm/index.js +2 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/playwright/components/basic_table/object.d.ts +50 -0
- package/lib/esm/playwright/components/basic_table/object.js +76 -0
- package/lib/esm/playwright/components/basic_table/object.js.map +1 -0
- package/lib/esm/playwright/components/selectable/object.d.ts +53 -0
- package/lib/esm/playwright/components/selectable/object.js +83 -0
- package/lib/esm/playwright/components/selectable/object.js.map +1 -0
- package/lib/esm/selectors.d.ts +1 -0
- package/lib/esm/selectors.js +1 -0
- package/lib/esm/selectors.js.map +1 -1
- package/package.json +3 -2
- package/src/components/basic_table/README.md +30 -0
- package/src/components/selectable/README.md +27 -0
package/README.md
CHANGED
|
@@ -50,6 +50,8 @@ their own version at runtime.
|
|
|
50
50
|
| `EuiDataGridObject` | [src/components/datagrid/README.md](src/components/datagrid/README.md) |
|
|
51
51
|
| `EuiSuperSelectObject` | [src/components/form/super_select/README.md](src/components/form/super_select/README.md) |
|
|
52
52
|
| `EuiGlobalToastListObject` | [src/components/toast/README.md](src/components/toast/README.md) |
|
|
53
|
+
| `EuiSelectableObject` | [src/components/selectable/README.md](src/components/selectable/README.md) |
|
|
54
|
+
| `EuiBasicTableObject` | [src/components/basic_table/README.md](src/components/basic_table/README.md) |
|
|
53
55
|
|
|
54
56
|
## Contributing
|
|
55
57
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable selectors for
|
|
3
|
+
* {@link https://eui.elastic.co/docs/components/tabular-content/tables/|EuiBasicTable}
|
|
4
|
+
* (also covers `EuiInMemoryTable`, which renders a `EuiBasicTable` underneath).
|
|
5
|
+
* `*_SELECTOR` values are CSS.
|
|
6
|
+
*/
|
|
7
|
+
export declare const EuiBasicTableSelectors: {
|
|
8
|
+
/** Root element (the `.euiBasicTable` container carrying the consumer's `data-test-subj`). */
|
|
9
|
+
ROOT_SELECTOR: string;
|
|
10
|
+
/** A data row. Also matches EUI's own empty/error message row — callers must exclude those. */
|
|
11
|
+
ROW_SELECTOR: string;
|
|
12
|
+
/**
|
|
13
|
+
* A field-data column's header cell. `%s` is the column's `field`. EUI appends a
|
|
14
|
+
* positional index (`tableHeaderCell_<field>_<index>`), so this is a prefix match.
|
|
15
|
+
*/
|
|
16
|
+
HEADER_CELL_SELECTOR_PREFIX: string;
|
|
17
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.EuiBasicTableSelectors = void 0;
|
|
7
|
+
/*
|
|
8
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
9
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
10
|
+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
11
|
+
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
12
|
+
* Side Public License, v 1.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Stable selectors for
|
|
17
|
+
* {@link https://eui.elastic.co/docs/components/tabular-content/tables/|EuiBasicTable}
|
|
18
|
+
* (also covers `EuiInMemoryTable`, which renders a `EuiBasicTable` underneath).
|
|
19
|
+
* `*_SELECTOR` values are CSS.
|
|
20
|
+
*/
|
|
21
|
+
const EuiBasicTableSelectors = exports.EuiBasicTableSelectors = {
|
|
22
|
+
/** Root element (the `.euiBasicTable` container carrying the consumer's `data-test-subj`). */
|
|
23
|
+
ROOT_SELECTOR: '.euiBasicTable',
|
|
24
|
+
/** A data row. Also matches EUI's own empty/error message row — callers must exclude those. */
|
|
25
|
+
ROW_SELECTOR: '.euiTableRow',
|
|
26
|
+
/**
|
|
27
|
+
* A field-data column's header cell. `%s` is the column's `field`. EUI appends a
|
|
28
|
+
* positional index (`tableHeaderCell_<field>_<index>`), so this is a prefix match.
|
|
29
|
+
*/
|
|
30
|
+
HEADER_CELL_SELECTOR_PREFIX: 'tableHeaderCell_'
|
|
31
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable selectors for
|
|
3
|
+
* {@link https://eui.elastic.co/docs/components/forms/selection/selectable/|EuiSelectable}.
|
|
4
|
+
* `*_SELECTOR` values are CSS.
|
|
5
|
+
*/
|
|
6
|
+
export declare const EuiSelectableSelectors: {
|
|
7
|
+
/** Root element (the `.euiSelectable` container carrying the consumer's `data-test-subj`). */
|
|
8
|
+
ROOT_SELECTOR: string;
|
|
9
|
+
/** The search box, present only when the selectable is `searchable`. */
|
|
10
|
+
SEARCH_SELECTOR: string;
|
|
11
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.EuiSelectableSelectors = void 0;
|
|
7
|
+
/*
|
|
8
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
9
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
10
|
+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
11
|
+
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
12
|
+
* Side Public License, v 1.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Stable selectors for
|
|
17
|
+
* {@link https://eui.elastic.co/docs/components/forms/selection/selectable/|EuiSelectable}.
|
|
18
|
+
* `*_SELECTOR` values are CSS.
|
|
19
|
+
*/
|
|
20
|
+
const EuiSelectableSelectors = exports.EuiSelectableSelectors = {
|
|
21
|
+
/** Root element (the `.euiSelectable` container carrying the consumer's `data-test-subj`). */
|
|
22
|
+
ROOT_SELECTOR: '.euiSelectable',
|
|
23
|
+
/** The search box, present only when the selectable is `searchable`. */
|
|
24
|
+
SEARCH_SELECTOR: '.euiSelectableSearch'
|
|
25
|
+
};
|
package/lib/cjs/index.d.ts
CHANGED
|
@@ -3,3 +3,5 @@ export { EuiComboBoxObject } from './playwright/components/combo_box/object';
|
|
|
3
3
|
export { EuiDataGridObject } from './playwright/components/datagrid/object';
|
|
4
4
|
export { EuiSuperSelectObject } from './playwright/components/form/super_select/object';
|
|
5
5
|
export { EuiGlobalToastListObject } from './playwright/components/toast/object';
|
|
6
|
+
export { EuiSelectableObject } from './playwright/components/selectable/object';
|
|
7
|
+
export { EuiBasicTableObject } from './playwright/components/basic_table/object';
|
package/lib/cjs/index.js
CHANGED
|
@@ -9,6 +9,12 @@ Object.defineProperty(exports, "BaseObject", {
|
|
|
9
9
|
return _base_object.BaseObject;
|
|
10
10
|
}
|
|
11
11
|
});
|
|
12
|
+
Object.defineProperty(exports, "EuiBasicTableObject", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _object6.EuiBasicTableObject;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
12
18
|
Object.defineProperty(exports, "EuiComboBoxObject", {
|
|
13
19
|
enumerable: true,
|
|
14
20
|
get: function () {
|
|
@@ -27,6 +33,12 @@ Object.defineProperty(exports, "EuiGlobalToastListObject", {
|
|
|
27
33
|
return _object4.EuiGlobalToastListObject;
|
|
28
34
|
}
|
|
29
35
|
});
|
|
36
|
+
Object.defineProperty(exports, "EuiSelectableObject", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
get: function () {
|
|
39
|
+
return _object5.EuiSelectableObject;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
30
42
|
Object.defineProperty(exports, "EuiSuperSelectObject", {
|
|
31
43
|
enumerable: true,
|
|
32
44
|
get: function () {
|
|
@@ -37,4 +49,6 @@ var _base_object = require("./playwright/base_object");
|
|
|
37
49
|
var _object = require("./playwright/components/combo_box/object");
|
|
38
50
|
var _object2 = require("./playwright/components/datagrid/object");
|
|
39
51
|
var _object3 = require("./playwright/components/form/super_select/object");
|
|
40
|
-
var _object4 = require("./playwright/components/toast/object");
|
|
52
|
+
var _object4 = require("./playwright/components/toast/object");
|
|
53
|
+
var _object5 = require("./playwright/components/selectable/object");
|
|
54
|
+
var _object6 = require("./playwright/components/basic_table/object");
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Locator } from '@playwright/test';
|
|
2
|
+
import { BaseObject, type ObjectScope } from '../../base_object';
|
|
3
|
+
/**
|
|
4
|
+
* Playwright Component Object for {@link
|
|
5
|
+
* https://eui.elastic.co/docs/components/tabular-content/tables/ EuiBasicTable}
|
|
6
|
+
* (also covers `EuiInMemoryTable`, which renders a `EuiBasicTable` underneath and
|
|
7
|
+
* passes the `data-test-subj` straight through — there is no separate object for it).
|
|
8
|
+
*
|
|
9
|
+
* `testSubj` must match the `data-test-subj` set by the consumer on the
|
|
10
|
+
* `<EuiBasicTable>`/`<EuiInMemoryTable>` (EUI spreads it onto the `.euiBasicTable`
|
|
11
|
+
* root). See the package README for what's deliberately excluded from v1.
|
|
12
|
+
*/
|
|
13
|
+
export declare class EuiBasicTableObject extends BaseObject {
|
|
14
|
+
constructor(scope: ObjectScope, testSubj: string);
|
|
15
|
+
/**
|
|
16
|
+
* The table's data rows, as a `Locator` so callers keep Playwright auto-retry
|
|
17
|
+
* for count and content assertions (e.g. `expect(table.rows).toHaveCount(3)`).
|
|
18
|
+
*
|
|
19
|
+
* Excludes EUI's own "no items found" (and error-message) row: when the table
|
|
20
|
+
* is empty, EUI still renders a single `.euiTableRow` whose one cell spans
|
|
21
|
+
* every column (`colSpan` on `EuiTableRowCell`, the `td[colspan]` HTML
|
|
22
|
+
* attribute). Real data rows don't set `colspan` on their cells, so filtering
|
|
23
|
+
* it out is what keeps `toHaveCount(0)` correct on an empty table instead of
|
|
24
|
+
* reading `1`.
|
|
25
|
+
*/
|
|
26
|
+
get rows(): Locator;
|
|
27
|
+
/**
|
|
28
|
+
* The cells of the given field-data column, one per row, as a `Locator` for
|
|
29
|
+
* retrying content/count assertions (e.g.
|
|
30
|
+
* `expect(await table.cells('status')).toHaveText(['Running'])`).
|
|
31
|
+
*
|
|
32
|
+
* Resolves the column's position from EUI's own header cell
|
|
33
|
+
* (`tableHeaderCell_<field>_<index>`, matched exactly so a column named e.g.
|
|
34
|
+
* `status` doesn't also match a `status_detail` header) via its native
|
|
35
|
+
* `cellIndex`, then reads the cell at that position in every row — the same
|
|
36
|
+
* positional alignment EUI itself relies on, so it holds regardless of a
|
|
37
|
+
* leading selection-checkbox column. Matches both `<td>` and `<th>`: EUI
|
|
38
|
+
* renders the `rowHeader` column's body cell as a `<th scope="row">` for
|
|
39
|
+
* accessibility, not a `<td>`. Throws if no column with that `field` is
|
|
40
|
+
* rendered.
|
|
41
|
+
*
|
|
42
|
+
* EUI generates a computed column's header test-subj from its `name` using
|
|
43
|
+
* this exact same `tableHeaderCell_<name>_<index>` shape (there's no `field`
|
|
44
|
+
* to key on), so a computed column whose `name` happens to equal another
|
|
45
|
+
* column's `field` can resolve ambiguously — this method can't distinguish
|
|
46
|
+
* the two from the DOM alone. Not expected in practice; call it out if you
|
|
47
|
+
* hit it.
|
|
48
|
+
*/
|
|
49
|
+
cells(field: string): Promise<Locator>;
|
|
50
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.EuiBasicTableObject = void 0;
|
|
7
|
+
var _base_object = require("../../base_object");
|
|
8
|
+
var _selectors = require("../../../components/basic_table/selectors");
|
|
9
|
+
/*
|
|
10
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
11
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
12
|
+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
13
|
+
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
14
|
+
* Side Public License, v 1.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Playwright Component Object for {@link
|
|
19
|
+
* https://eui.elastic.co/docs/components/tabular-content/tables/ EuiBasicTable}
|
|
20
|
+
* (also covers `EuiInMemoryTable`, which renders a `EuiBasicTable` underneath and
|
|
21
|
+
* passes the `data-test-subj` straight through — there is no separate object for it).
|
|
22
|
+
*
|
|
23
|
+
* `testSubj` must match the `data-test-subj` set by the consumer on the
|
|
24
|
+
* `<EuiBasicTable>`/`<EuiInMemoryTable>` (EUI spreads it onto the `.euiBasicTable`
|
|
25
|
+
* root). See the package README for what's deliberately excluded from v1.
|
|
26
|
+
*/
|
|
27
|
+
class EuiBasicTableObject extends _base_object.BaseObject {
|
|
28
|
+
constructor(scope, testSubj) {
|
|
29
|
+
super(scope, testSubj, _selectors.EuiBasicTableSelectors.ROOT_SELECTOR);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The table's data rows, as a `Locator` so callers keep Playwright auto-retry
|
|
34
|
+
* for count and content assertions (e.g. `expect(table.rows).toHaveCount(3)`).
|
|
35
|
+
*
|
|
36
|
+
* Excludes EUI's own "no items found" (and error-message) row: when the table
|
|
37
|
+
* is empty, EUI still renders a single `.euiTableRow` whose one cell spans
|
|
38
|
+
* every column (`colSpan` on `EuiTableRowCell`, the `td[colspan]` HTML
|
|
39
|
+
* attribute). Real data rows don't set `colspan` on their cells, so filtering
|
|
40
|
+
* it out is what keeps `toHaveCount(0)` correct on an empty table instead of
|
|
41
|
+
* reading `1`.
|
|
42
|
+
*/
|
|
43
|
+
get rows() {
|
|
44
|
+
return this.root.locator(_selectors.EuiBasicTableSelectors.ROW_SELECTOR).filter({
|
|
45
|
+
hasNot: this.root.page().locator('td[colspan]')
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The cells of the given field-data column, one per row, as a `Locator` for
|
|
51
|
+
* retrying content/count assertions (e.g.
|
|
52
|
+
* `expect(await table.cells('status')).toHaveText(['Running'])`).
|
|
53
|
+
*
|
|
54
|
+
* Resolves the column's position from EUI's own header cell
|
|
55
|
+
* (`tableHeaderCell_<field>_<index>`, matched exactly so a column named e.g.
|
|
56
|
+
* `status` doesn't also match a `status_detail` header) via its native
|
|
57
|
+
* `cellIndex`, then reads the cell at that position in every row — the same
|
|
58
|
+
* positional alignment EUI itself relies on, so it holds regardless of a
|
|
59
|
+
* leading selection-checkbox column. Matches both `<td>` and `<th>`: EUI
|
|
60
|
+
* renders the `rowHeader` column's body cell as a `<th scope="row">` for
|
|
61
|
+
* accessibility, not a `<td>`. Throws if no column with that `field` is
|
|
62
|
+
* rendered.
|
|
63
|
+
*
|
|
64
|
+
* EUI generates a computed column's header test-subj from its `name` using
|
|
65
|
+
* this exact same `tableHeaderCell_<name>_<index>` shape (there's no `field`
|
|
66
|
+
* to key on), so a computed column whose `name` happens to equal another
|
|
67
|
+
* column's `field` can resolve ambiguously — this method can't distinguish
|
|
68
|
+
* the two from the DOM alone. Not expected in practice; call it out if you
|
|
69
|
+
* hit it.
|
|
70
|
+
*/
|
|
71
|
+
async cells(field) {
|
|
72
|
+
const escaped = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
73
|
+
const cellIndex = await this.root.evaluate((tableEl, {
|
|
74
|
+
prefix,
|
|
75
|
+
pattern,
|
|
76
|
+
field: originalField
|
|
77
|
+
}) => {
|
|
78
|
+
const regex = new RegExp(`^${prefix}${pattern}_\\d+$`);
|
|
79
|
+
const headers = Array.from(tableEl.querySelectorAll('thead th, thead td'));
|
|
80
|
+
const header = headers.find(el => regex.test(el.getAttribute('data-test-subj') ?? ''));
|
|
81
|
+
if (!header) {
|
|
82
|
+
throw new Error(`EuiBasicTableObject.cells: no column with field "${originalField}" found in this table's header.`);
|
|
83
|
+
}
|
|
84
|
+
return header.cellIndex;
|
|
85
|
+
}, {
|
|
86
|
+
prefix: _selectors.EuiBasicTableSelectors.HEADER_CELL_SELECTOR_PREFIX,
|
|
87
|
+
pattern: escaped,
|
|
88
|
+
field
|
|
89
|
+
});
|
|
90
|
+
return this.rows.locator(`:is(td, th):nth-child(${cellIndex + 1})`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.EuiBasicTableObject = EuiBasicTableObject;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Locator } from '@playwright/test';
|
|
2
|
+
import { BaseObject, type ObjectScope } from '../../base_object';
|
|
3
|
+
/**
|
|
4
|
+
* Playwright Component Object for {@link
|
|
5
|
+
* https://eui.elastic.co/docs/components/forms/selection/selectable/ EuiSelectable}.
|
|
6
|
+
*
|
|
7
|
+
* `testSubj` must match the `data-test-subj` set by the consumer on the
|
|
8
|
+
* `<EuiSelectable>` (EUI spreads it onto the `.euiSelectable` root). When the
|
|
9
|
+
* selectable renders inside a popover the consumer opens, pass that popover as
|
|
10
|
+
* `scope` and drive the open/close from the test — the popover is not this
|
|
11
|
+
* component's concern.
|
|
12
|
+
*/
|
|
13
|
+
export declare class EuiSelectableObject extends BaseObject {
|
|
14
|
+
constructor(scope: ObjectScope, testSubj: string);
|
|
15
|
+
/**
|
|
16
|
+
* The options currently mounted in the list, as a `Locator` so callers keep
|
|
17
|
+
* Playwright auto-retry for count and content assertions
|
|
18
|
+
* (e.g. `expect(options).toHaveCount(3)`). The list is virtualized, so this is
|
|
19
|
+
* the rendered window — `search()` first to filter the target into view
|
|
20
|
+
* rather than scanning a long list.
|
|
21
|
+
*/
|
|
22
|
+
get options(): Locator;
|
|
23
|
+
/**
|
|
24
|
+
* Selects the option with the given label. A no-op if it is already selected.
|
|
25
|
+
* EUI reports that as `aria-checked` for a multi-selection list, or
|
|
26
|
+
* `aria-selected` for a single-selection one, so both are checked.
|
|
27
|
+
*
|
|
28
|
+
* Matches on the option's label element (`.euiSelectableListItem__text`), not
|
|
29
|
+
* its accessible name. `append`/`prepend` badges render in a sibling element,
|
|
30
|
+
* so they are excluded. EUI appends screen-reader state text inside the label
|
|
31
|
+
* element (a checked option reads `"<label> . Checked option."`), which always
|
|
32
|
+
* starts with a `.`, so the match allows an optional `.`-prefixed suffix. That
|
|
33
|
+
* boundary is what keeps `selectOption('New York')` from also matching
|
|
34
|
+
* `New York City`: a real longer label continues with a word, not a `.`.
|
|
35
|
+
*
|
|
36
|
+
* This is for lists in their default state. Once you call `search()`, EUI
|
|
37
|
+
* wraps the matched text in `<mark>` and injects highlight markers, so a label
|
|
38
|
+
* match no longer resolves. On a searched list, select via the option's own
|
|
39
|
+
* `data-test-subj`, or drive it through {@link options} directly.
|
|
40
|
+
*
|
|
41
|
+
* Only matches the plain label. A consumer using a custom `renderOption` to
|
|
42
|
+
* add its own content (e.g. a description) inside the option renders that
|
|
43
|
+
* content in the same label element, which breaks the anchored match. Select
|
|
44
|
+
* those options via {@link options} instead (e.g.
|
|
45
|
+
* `options.filter({ hasText: label })`).
|
|
46
|
+
*/
|
|
47
|
+
selectOption(label: string): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Type into the selectable's search box to filter the options. Throws if the
|
|
50
|
+
* selectable is not searchable (no search box rendered).
|
|
51
|
+
*/
|
|
52
|
+
search(term: string): Promise<void>;
|
|
53
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.EuiSelectableObject = void 0;
|
|
7
|
+
var _base_object = require("../../base_object");
|
|
8
|
+
var _selectors = require("../../../components/selectable/selectors");
|
|
9
|
+
/*
|
|
10
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
11
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
12
|
+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
13
|
+
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
14
|
+
* Side Public License, v 1.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Playwright Component Object for {@link
|
|
19
|
+
* https://eui.elastic.co/docs/components/forms/selection/selectable/ EuiSelectable}.
|
|
20
|
+
*
|
|
21
|
+
* `testSubj` must match the `data-test-subj` set by the consumer on the
|
|
22
|
+
* `<EuiSelectable>` (EUI spreads it onto the `.euiSelectable` root). When the
|
|
23
|
+
* selectable renders inside a popover the consumer opens, pass that popover as
|
|
24
|
+
* `scope` and drive the open/close from the test — the popover is not this
|
|
25
|
+
* component's concern.
|
|
26
|
+
*/
|
|
27
|
+
class EuiSelectableObject extends _base_object.BaseObject {
|
|
28
|
+
constructor(scope, testSubj) {
|
|
29
|
+
super(scope, testSubj, _selectors.EuiSelectableSelectors.ROOT_SELECTOR);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The options currently mounted in the list, as a `Locator` so callers keep
|
|
34
|
+
* Playwright auto-retry for count and content assertions
|
|
35
|
+
* (e.g. `expect(options).toHaveCount(3)`). The list is virtualized, so this is
|
|
36
|
+
* the rendered window — `search()` first to filter the target into view
|
|
37
|
+
* rather than scanning a long list.
|
|
38
|
+
*/
|
|
39
|
+
get options() {
|
|
40
|
+
return this.root.getByRole('option');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Selects the option with the given label. A no-op if it is already selected.
|
|
45
|
+
* EUI reports that as `aria-checked` for a multi-selection list, or
|
|
46
|
+
* `aria-selected` for a single-selection one, so both are checked.
|
|
47
|
+
*
|
|
48
|
+
* Matches on the option's label element (`.euiSelectableListItem__text`), not
|
|
49
|
+
* its accessible name. `append`/`prepend` badges render in a sibling element,
|
|
50
|
+
* so they are excluded. EUI appends screen-reader state text inside the label
|
|
51
|
+
* element (a checked option reads `"<label> . Checked option."`), which always
|
|
52
|
+
* starts with a `.`, so the match allows an optional `.`-prefixed suffix. That
|
|
53
|
+
* boundary is what keeps `selectOption('New York')` from also matching
|
|
54
|
+
* `New York City`: a real longer label continues with a word, not a `.`.
|
|
55
|
+
*
|
|
56
|
+
* This is for lists in their default state. Once you call `search()`, EUI
|
|
57
|
+
* wraps the matched text in `<mark>` and injects highlight markers, so a label
|
|
58
|
+
* match no longer resolves. On a searched list, select via the option's own
|
|
59
|
+
* `data-test-subj`, or drive it through {@link options} directly.
|
|
60
|
+
*
|
|
61
|
+
* Only matches the plain label. A consumer using a custom `renderOption` to
|
|
62
|
+
* add its own content (e.g. a description) inside the option renders that
|
|
63
|
+
* content in the same label element, which breaks the anchored match. Select
|
|
64
|
+
* those options via {@link options} instead (e.g.
|
|
65
|
+
* `options.filter({ hasText: label })`).
|
|
66
|
+
*/
|
|
67
|
+
async selectOption(label) {
|
|
68
|
+
const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
69
|
+
const labelText = new RegExp(`^${escaped}(\\s*\\..*)?$`);
|
|
70
|
+
const option = this.root.getByRole('option').filter({
|
|
71
|
+
has: this.root.page().locator('.euiSelectableListItem__text', {
|
|
72
|
+
hasText: labelText
|
|
73
|
+
})
|
|
74
|
+
});
|
|
75
|
+
const [ariaChecked, ariaSelected] = await Promise.all([option.getAttribute('aria-checked'), option.getAttribute('aria-selected')]);
|
|
76
|
+
if (ariaChecked === 'true' || ariaSelected === 'true') {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
await option.click();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Type into the selectable's search box to filter the options. Throws if the
|
|
84
|
+
* selectable is not searchable (no search box rendered).
|
|
85
|
+
*/
|
|
86
|
+
async search(term) {
|
|
87
|
+
// `fill()` auto-waits for the search box; if the selectable is not
|
|
88
|
+
// searchable it surfaces a clear locator-timeout on its own.
|
|
89
|
+
await this.root.locator(_selectors.EuiSelectableSelectors.SEARCH_SELECTOR).fill(term);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.EuiSelectableObject = EuiSelectableObject;
|
package/lib/cjs/selectors.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export { EuiComboBoxSelectors } from './components/combo_box/selectors';
|
|
|
2
2
|
export { EuiDataGridSelectors } from './components/datagrid/selectors';
|
|
3
3
|
export { EuiSuperSelectSelectors } from './components/form/super_select/selectors';
|
|
4
4
|
export { EuiGlobalToastListSelectors } from './components/toast/selectors';
|
|
5
|
+
export { EuiSelectableSelectors } from './components/selectable/selectors';
|
package/lib/cjs/selectors.js
CHANGED
|
@@ -21,6 +21,12 @@ Object.defineProperty(exports, "EuiGlobalToastListSelectors", {
|
|
|
21
21
|
return _selectors4.EuiGlobalToastListSelectors;
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
|
+
Object.defineProperty(exports, "EuiSelectableSelectors", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () {
|
|
27
|
+
return _selectors5.EuiSelectableSelectors;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
24
30
|
Object.defineProperty(exports, "EuiSuperSelectSelectors", {
|
|
25
31
|
enumerable: true,
|
|
26
32
|
get: function () {
|
|
@@ -30,4 +36,5 @@ Object.defineProperty(exports, "EuiSuperSelectSelectors", {
|
|
|
30
36
|
var _selectors = require("./components/combo_box/selectors");
|
|
31
37
|
var _selectors2 = require("./components/datagrid/selectors");
|
|
32
38
|
var _selectors3 = require("./components/form/super_select/selectors");
|
|
33
|
-
var _selectors4 = require("./components/toast/selectors");
|
|
39
|
+
var _selectors4 = require("./components/toast/selectors");
|
|
40
|
+
var _selectors5 = require("./components/selectable/selectors");
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable selectors for
|
|
3
|
+
* {@link https://eui.elastic.co/docs/components/tabular-content/tables/|EuiBasicTable}
|
|
4
|
+
* (also covers `EuiInMemoryTable`, which renders a `EuiBasicTable` underneath).
|
|
5
|
+
* `*_SELECTOR` values are CSS.
|
|
6
|
+
*/
|
|
7
|
+
export declare const EuiBasicTableSelectors: {
|
|
8
|
+
/** Root element (the `.euiBasicTable` container carrying the consumer's `data-test-subj`). */
|
|
9
|
+
ROOT_SELECTOR: string;
|
|
10
|
+
/** A data row. Also matches EUI's own empty/error message row — callers must exclude those. */
|
|
11
|
+
ROW_SELECTOR: string;
|
|
12
|
+
/**
|
|
13
|
+
* A field-data column's header cell. `%s` is the column's `field`. EUI appends a
|
|
14
|
+
* positional index (`tableHeaderCell_<field>_<index>`), so this is a prefix match.
|
|
15
|
+
*/
|
|
16
|
+
HEADER_CELL_SELECTOR_PREFIX: string;
|
|
17
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
3
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
4
|
+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
5
|
+
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
6
|
+
* Side Public License, v 1.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Stable selectors for
|
|
10
|
+
* {@link https://eui.elastic.co/docs/components/tabular-content/tables/|EuiBasicTable}
|
|
11
|
+
* (also covers `EuiInMemoryTable`, which renders a `EuiBasicTable` underneath).
|
|
12
|
+
* `*_SELECTOR` values are CSS.
|
|
13
|
+
*/
|
|
14
|
+
export const EuiBasicTableSelectors = {
|
|
15
|
+
/** Root element (the `.euiBasicTable` container carrying the consumer's `data-test-subj`). */
|
|
16
|
+
ROOT_SELECTOR: '.euiBasicTable',
|
|
17
|
+
/** A data row. Also matches EUI's own empty/error message row — callers must exclude those. */
|
|
18
|
+
ROW_SELECTOR: '.euiTableRow',
|
|
19
|
+
/**
|
|
20
|
+
* A field-data column's header cell. `%s` is the column's `field`. EUI appends a
|
|
21
|
+
* positional index (`tableHeaderCell_<field>_<index>`), so this is a prefix match.
|
|
22
|
+
*/
|
|
23
|
+
HEADER_CELL_SELECTOR_PREFIX: 'tableHeaderCell_',
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=selectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectors.js","sourceRoot":"","sources":["../../../../src/components/basic_table/selectors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,8FAA8F;IAC9F,aAAa,EAAE,gBAAgB;IAE/B,+FAA+F;IAC/F,YAAY,EAAE,cAAc;IAE5B;;;OAGG;IACH,2BAA2B,EAAE,kBAAkB;CAChD,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable selectors for
|
|
3
|
+
* {@link https://eui.elastic.co/docs/components/forms/selection/selectable/|EuiSelectable}.
|
|
4
|
+
* `*_SELECTOR` values are CSS.
|
|
5
|
+
*/
|
|
6
|
+
export declare const EuiSelectableSelectors: {
|
|
7
|
+
/** Root element (the `.euiSelectable` container carrying the consumer's `data-test-subj`). */
|
|
8
|
+
ROOT_SELECTOR: string;
|
|
9
|
+
/** The search box, present only when the selectable is `searchable`. */
|
|
10
|
+
SEARCH_SELECTOR: string;
|
|
11
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
3
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
4
|
+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
5
|
+
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
6
|
+
* Side Public License, v 1.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Stable selectors for
|
|
10
|
+
* {@link https://eui.elastic.co/docs/components/forms/selection/selectable/|EuiSelectable}.
|
|
11
|
+
* `*_SELECTOR` values are CSS.
|
|
12
|
+
*/
|
|
13
|
+
export const EuiSelectableSelectors = {
|
|
14
|
+
/** Root element (the `.euiSelectable` container carrying the consumer's `data-test-subj`). */
|
|
15
|
+
ROOT_SELECTOR: '.euiSelectable',
|
|
16
|
+
/** The search box, present only when the selectable is `searchable`. */
|
|
17
|
+
SEARCH_SELECTOR: '.euiSelectableSearch',
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=selectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectors.js","sourceRoot":"","sources":["../../../../src/components/selectable/selectors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,8FAA8F;IAC9F,aAAa,EAAE,gBAAgB;IAE/B,wEAAwE;IACxE,eAAe,EAAE,sBAAsB;CACxC,CAAC"}
|
package/lib/esm/index.d.ts
CHANGED
|
@@ -3,3 +3,5 @@ export { EuiComboBoxObject } from './playwright/components/combo_box/object';
|
|
|
3
3
|
export { EuiDataGridObject } from './playwright/components/datagrid/object';
|
|
4
4
|
export { EuiSuperSelectObject } from './playwright/components/form/super_select/object';
|
|
5
5
|
export { EuiGlobalToastListObject } from './playwright/components/toast/object';
|
|
6
|
+
export { EuiSelectableObject } from './playwright/components/selectable/object';
|
|
7
|
+
export { EuiBasicTableObject } from './playwright/components/basic_table/object';
|
package/lib/esm/index.js
CHANGED
|
@@ -10,4 +10,6 @@ export { EuiComboBoxObject } from './playwright/components/combo_box/object';
|
|
|
10
10
|
export { EuiDataGridObject } from './playwright/components/datagrid/object';
|
|
11
11
|
export { EuiSuperSelectObject } from './playwright/components/form/super_select/object';
|
|
12
12
|
export { EuiGlobalToastListObject } from './playwright/components/toast/object';
|
|
13
|
+
export { EuiSelectableObject } from './playwright/components/selectable/object';
|
|
14
|
+
export { EuiBasicTableObject } from './playwright/components/basic_table/object';
|
|
13
15
|
//# sourceMappingURL=index.js.map
|
package/lib/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAoB,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,kDAAkD,CAAC;AACxF,OAAO,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAoB,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,kDAAkD,CAAC;AACxF,OAAO,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Locator } from '@playwright/test';
|
|
2
|
+
import { BaseObject, type ObjectScope } from '../../base_object';
|
|
3
|
+
/**
|
|
4
|
+
* Playwright Component Object for {@link
|
|
5
|
+
* https://eui.elastic.co/docs/components/tabular-content/tables/ EuiBasicTable}
|
|
6
|
+
* (also covers `EuiInMemoryTable`, which renders a `EuiBasicTable` underneath and
|
|
7
|
+
* passes the `data-test-subj` straight through — there is no separate object for it).
|
|
8
|
+
*
|
|
9
|
+
* `testSubj` must match the `data-test-subj` set by the consumer on the
|
|
10
|
+
* `<EuiBasicTable>`/`<EuiInMemoryTable>` (EUI spreads it onto the `.euiBasicTable`
|
|
11
|
+
* root). See the package README for what's deliberately excluded from v1.
|
|
12
|
+
*/
|
|
13
|
+
export declare class EuiBasicTableObject extends BaseObject {
|
|
14
|
+
constructor(scope: ObjectScope, testSubj: string);
|
|
15
|
+
/**
|
|
16
|
+
* The table's data rows, as a `Locator` so callers keep Playwright auto-retry
|
|
17
|
+
* for count and content assertions (e.g. `expect(table.rows).toHaveCount(3)`).
|
|
18
|
+
*
|
|
19
|
+
* Excludes EUI's own "no items found" (and error-message) row: when the table
|
|
20
|
+
* is empty, EUI still renders a single `.euiTableRow` whose one cell spans
|
|
21
|
+
* every column (`colSpan` on `EuiTableRowCell`, the `td[colspan]` HTML
|
|
22
|
+
* attribute). Real data rows don't set `colspan` on their cells, so filtering
|
|
23
|
+
* it out is what keeps `toHaveCount(0)` correct on an empty table instead of
|
|
24
|
+
* reading `1`.
|
|
25
|
+
*/
|
|
26
|
+
get rows(): Locator;
|
|
27
|
+
/**
|
|
28
|
+
* The cells of the given field-data column, one per row, as a `Locator` for
|
|
29
|
+
* retrying content/count assertions (e.g.
|
|
30
|
+
* `expect(await table.cells('status')).toHaveText(['Running'])`).
|
|
31
|
+
*
|
|
32
|
+
* Resolves the column's position from EUI's own header cell
|
|
33
|
+
* (`tableHeaderCell_<field>_<index>`, matched exactly so a column named e.g.
|
|
34
|
+
* `status` doesn't also match a `status_detail` header) via its native
|
|
35
|
+
* `cellIndex`, then reads the cell at that position in every row — the same
|
|
36
|
+
* positional alignment EUI itself relies on, so it holds regardless of a
|
|
37
|
+
* leading selection-checkbox column. Matches both `<td>` and `<th>`: EUI
|
|
38
|
+
* renders the `rowHeader` column's body cell as a `<th scope="row">` for
|
|
39
|
+
* accessibility, not a `<td>`. Throws if no column with that `field` is
|
|
40
|
+
* rendered.
|
|
41
|
+
*
|
|
42
|
+
* EUI generates a computed column's header test-subj from its `name` using
|
|
43
|
+
* this exact same `tableHeaderCell_<name>_<index>` shape (there's no `field`
|
|
44
|
+
* to key on), so a computed column whose `name` happens to equal another
|
|
45
|
+
* column's `field` can resolve ambiguously — this method can't distinguish
|
|
46
|
+
* the two from the DOM alone. Not expected in practice; call it out if you
|
|
47
|
+
* hit it.
|
|
48
|
+
*/
|
|
49
|
+
cells(field: string): Promise<Locator>;
|
|
50
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
3
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
4
|
+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
5
|
+
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
6
|
+
* Side Public License, v 1.
|
|
7
|
+
*/
|
|
8
|
+
import { BaseObject } from '../../base_object';
|
|
9
|
+
import { EuiBasicTableSelectors } from '../../../components/basic_table/selectors';
|
|
10
|
+
/**
|
|
11
|
+
* Playwright Component Object for {@link
|
|
12
|
+
* https://eui.elastic.co/docs/components/tabular-content/tables/ EuiBasicTable}
|
|
13
|
+
* (also covers `EuiInMemoryTable`, which renders a `EuiBasicTable` underneath and
|
|
14
|
+
* passes the `data-test-subj` straight through — there is no separate object for it).
|
|
15
|
+
*
|
|
16
|
+
* `testSubj` must match the `data-test-subj` set by the consumer on the
|
|
17
|
+
* `<EuiBasicTable>`/`<EuiInMemoryTable>` (EUI spreads it onto the `.euiBasicTable`
|
|
18
|
+
* root). See the package README for what's deliberately excluded from v1.
|
|
19
|
+
*/
|
|
20
|
+
export class EuiBasicTableObject extends BaseObject {
|
|
21
|
+
constructor(scope, testSubj) {
|
|
22
|
+
super(scope, testSubj, EuiBasicTableSelectors.ROOT_SELECTOR);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The table's data rows, as a `Locator` so callers keep Playwright auto-retry
|
|
26
|
+
* for count and content assertions (e.g. `expect(table.rows).toHaveCount(3)`).
|
|
27
|
+
*
|
|
28
|
+
* Excludes EUI's own "no items found" (and error-message) row: when the table
|
|
29
|
+
* is empty, EUI still renders a single `.euiTableRow` whose one cell spans
|
|
30
|
+
* every column (`colSpan` on `EuiTableRowCell`, the `td[colspan]` HTML
|
|
31
|
+
* attribute). Real data rows don't set `colspan` on their cells, so filtering
|
|
32
|
+
* it out is what keeps `toHaveCount(0)` correct on an empty table instead of
|
|
33
|
+
* reading `1`.
|
|
34
|
+
*/
|
|
35
|
+
get rows() {
|
|
36
|
+
return this.root
|
|
37
|
+
.locator(EuiBasicTableSelectors.ROW_SELECTOR)
|
|
38
|
+
.filter({ hasNot: this.root.page().locator('td[colspan]') });
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The cells of the given field-data column, one per row, as a `Locator` for
|
|
42
|
+
* retrying content/count assertions (e.g.
|
|
43
|
+
* `expect(await table.cells('status')).toHaveText(['Running'])`).
|
|
44
|
+
*
|
|
45
|
+
* Resolves the column's position from EUI's own header cell
|
|
46
|
+
* (`tableHeaderCell_<field>_<index>`, matched exactly so a column named e.g.
|
|
47
|
+
* `status` doesn't also match a `status_detail` header) via its native
|
|
48
|
+
* `cellIndex`, then reads the cell at that position in every row — the same
|
|
49
|
+
* positional alignment EUI itself relies on, so it holds regardless of a
|
|
50
|
+
* leading selection-checkbox column. Matches both `<td>` and `<th>`: EUI
|
|
51
|
+
* renders the `rowHeader` column's body cell as a `<th scope="row">` for
|
|
52
|
+
* accessibility, not a `<td>`. Throws if no column with that `field` is
|
|
53
|
+
* rendered.
|
|
54
|
+
*
|
|
55
|
+
* EUI generates a computed column's header test-subj from its `name` using
|
|
56
|
+
* this exact same `tableHeaderCell_<name>_<index>` shape (there's no `field`
|
|
57
|
+
* to key on), so a computed column whose `name` happens to equal another
|
|
58
|
+
* column's `field` can resolve ambiguously — this method can't distinguish
|
|
59
|
+
* the two from the DOM alone. Not expected in practice; call it out if you
|
|
60
|
+
* hit it.
|
|
61
|
+
*/
|
|
62
|
+
async cells(field) {
|
|
63
|
+
const escaped = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
64
|
+
const cellIndex = await this.root.evaluate((tableEl, { prefix, pattern, field: originalField }) => {
|
|
65
|
+
const regex = new RegExp(`^${prefix}${pattern}_\\d+$`);
|
|
66
|
+
const headers = Array.from(tableEl.querySelectorAll('thead th, thead td'));
|
|
67
|
+
const header = headers.find((el) => regex.test(el.getAttribute('data-test-subj') ?? ''));
|
|
68
|
+
if (!header) {
|
|
69
|
+
throw new Error(`EuiBasicTableObject.cells: no column with field "${originalField}" found in this table's header.`);
|
|
70
|
+
}
|
|
71
|
+
return header.cellIndex;
|
|
72
|
+
}, { prefix: EuiBasicTableSelectors.HEADER_CELL_SELECTOR_PREFIX, pattern: escaped, field });
|
|
73
|
+
return this.rows.locator(`:is(td, th):nth-child(${cellIndex + 1})`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=object.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"object.js","sourceRoot":"","sources":["../../../../../src/playwright/components/basic_table/object.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAEnF;;;;;;;;;GASG;AACH,MAAM,OAAO,mBAAoB,SAAQ,UAAU;IACjD,YAAY,KAAkB,EAAE,QAAgB;QAC9C,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,sBAAsB,CAAC,aAAa,CAAC,CAAC;IAC/D,CAAC;IAED;;;;;;;;;;OAUG;IACH,IAAW,IAAI;QACb,OAAO,IAAI,CAAC,IAAI;aACb,OAAO,CAAC,sBAAsB,CAAC,YAAY,CAAC;aAC5C,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IACjE,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,KAAK,CAAC,KAAK,CAAC,KAAa;QACvB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;QAC7D,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CACxC,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE;YACrD,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,MAAM,GAAG,OAAO,QAAQ,CAAC,CAAC;YACvD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,oBAAoB,CAAC,CAAC,CAAC;YAC3E,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CACjC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAChB,CAAC;YACtC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CACb,oDAAoD,aAAa,iCAAiC,CACnG,CAAC;YACJ,CAAC;YACD,OAAO,MAAM,CAAC,SAAS,CAAC;QAC1B,CAAC,EACD,EAAE,MAAM,EAAE,sBAAsB,CAAC,2BAA2B,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CACxF,CAAC;QACF,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,yBAAyB,SAAS,GAAG,CAAC,GAAG,CAAC,CAAC;IACtE,CAAC;CACF"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Locator } from '@playwright/test';
|
|
2
|
+
import { BaseObject, type ObjectScope } from '../../base_object';
|
|
3
|
+
/**
|
|
4
|
+
* Playwright Component Object for {@link
|
|
5
|
+
* https://eui.elastic.co/docs/components/forms/selection/selectable/ EuiSelectable}.
|
|
6
|
+
*
|
|
7
|
+
* `testSubj` must match the `data-test-subj` set by the consumer on the
|
|
8
|
+
* `<EuiSelectable>` (EUI spreads it onto the `.euiSelectable` root). When the
|
|
9
|
+
* selectable renders inside a popover the consumer opens, pass that popover as
|
|
10
|
+
* `scope` and drive the open/close from the test — the popover is not this
|
|
11
|
+
* component's concern.
|
|
12
|
+
*/
|
|
13
|
+
export declare class EuiSelectableObject extends BaseObject {
|
|
14
|
+
constructor(scope: ObjectScope, testSubj: string);
|
|
15
|
+
/**
|
|
16
|
+
* The options currently mounted in the list, as a `Locator` so callers keep
|
|
17
|
+
* Playwright auto-retry for count and content assertions
|
|
18
|
+
* (e.g. `expect(options).toHaveCount(3)`). The list is virtualized, so this is
|
|
19
|
+
* the rendered window — `search()` first to filter the target into view
|
|
20
|
+
* rather than scanning a long list.
|
|
21
|
+
*/
|
|
22
|
+
get options(): Locator;
|
|
23
|
+
/**
|
|
24
|
+
* Selects the option with the given label. A no-op if it is already selected.
|
|
25
|
+
* EUI reports that as `aria-checked` for a multi-selection list, or
|
|
26
|
+
* `aria-selected` for a single-selection one, so both are checked.
|
|
27
|
+
*
|
|
28
|
+
* Matches on the option's label element (`.euiSelectableListItem__text`), not
|
|
29
|
+
* its accessible name. `append`/`prepend` badges render in a sibling element,
|
|
30
|
+
* so they are excluded. EUI appends screen-reader state text inside the label
|
|
31
|
+
* element (a checked option reads `"<label> . Checked option."`), which always
|
|
32
|
+
* starts with a `.`, so the match allows an optional `.`-prefixed suffix. That
|
|
33
|
+
* boundary is what keeps `selectOption('New York')` from also matching
|
|
34
|
+
* `New York City`: a real longer label continues with a word, not a `.`.
|
|
35
|
+
*
|
|
36
|
+
* This is for lists in their default state. Once you call `search()`, EUI
|
|
37
|
+
* wraps the matched text in `<mark>` and injects highlight markers, so a label
|
|
38
|
+
* match no longer resolves. On a searched list, select via the option's own
|
|
39
|
+
* `data-test-subj`, or drive it through {@link options} directly.
|
|
40
|
+
*
|
|
41
|
+
* Only matches the plain label. A consumer using a custom `renderOption` to
|
|
42
|
+
* add its own content (e.g. a description) inside the option renders that
|
|
43
|
+
* content in the same label element, which breaks the anchored match. Select
|
|
44
|
+
* those options via {@link options} instead (e.g.
|
|
45
|
+
* `options.filter({ hasText: label })`).
|
|
46
|
+
*/
|
|
47
|
+
selectOption(label: string): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Type into the selectable's search box to filter the options. Throws if the
|
|
50
|
+
* selectable is not searchable (no search box rendered).
|
|
51
|
+
*/
|
|
52
|
+
search(term: string): Promise<void>;
|
|
53
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
3
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
4
|
+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
5
|
+
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
6
|
+
* Side Public License, v 1.
|
|
7
|
+
*/
|
|
8
|
+
import { BaseObject } from '../../base_object';
|
|
9
|
+
import { EuiSelectableSelectors } from '../../../components/selectable/selectors';
|
|
10
|
+
/**
|
|
11
|
+
* Playwright Component Object for {@link
|
|
12
|
+
* https://eui.elastic.co/docs/components/forms/selection/selectable/ EuiSelectable}.
|
|
13
|
+
*
|
|
14
|
+
* `testSubj` must match the `data-test-subj` set by the consumer on the
|
|
15
|
+
* `<EuiSelectable>` (EUI spreads it onto the `.euiSelectable` root). When the
|
|
16
|
+
* selectable renders inside a popover the consumer opens, pass that popover as
|
|
17
|
+
* `scope` and drive the open/close from the test — the popover is not this
|
|
18
|
+
* component's concern.
|
|
19
|
+
*/
|
|
20
|
+
export class EuiSelectableObject extends BaseObject {
|
|
21
|
+
constructor(scope, testSubj) {
|
|
22
|
+
super(scope, testSubj, EuiSelectableSelectors.ROOT_SELECTOR);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The options currently mounted in the list, as a `Locator` so callers keep
|
|
26
|
+
* Playwright auto-retry for count and content assertions
|
|
27
|
+
* (e.g. `expect(options).toHaveCount(3)`). The list is virtualized, so this is
|
|
28
|
+
* the rendered window — `search()` first to filter the target into view
|
|
29
|
+
* rather than scanning a long list.
|
|
30
|
+
*/
|
|
31
|
+
get options() {
|
|
32
|
+
return this.root.getByRole('option');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Selects the option with the given label. A no-op if it is already selected.
|
|
36
|
+
* EUI reports that as `aria-checked` for a multi-selection list, or
|
|
37
|
+
* `aria-selected` for a single-selection one, so both are checked.
|
|
38
|
+
*
|
|
39
|
+
* Matches on the option's label element (`.euiSelectableListItem__text`), not
|
|
40
|
+
* its accessible name. `append`/`prepend` badges render in a sibling element,
|
|
41
|
+
* so they are excluded. EUI appends screen-reader state text inside the label
|
|
42
|
+
* element (a checked option reads `"<label> . Checked option."`), which always
|
|
43
|
+
* starts with a `.`, so the match allows an optional `.`-prefixed suffix. That
|
|
44
|
+
* boundary is what keeps `selectOption('New York')` from also matching
|
|
45
|
+
* `New York City`: a real longer label continues with a word, not a `.`.
|
|
46
|
+
*
|
|
47
|
+
* This is for lists in their default state. Once you call `search()`, EUI
|
|
48
|
+
* wraps the matched text in `<mark>` and injects highlight markers, so a label
|
|
49
|
+
* match no longer resolves. On a searched list, select via the option's own
|
|
50
|
+
* `data-test-subj`, or drive it through {@link options} directly.
|
|
51
|
+
*
|
|
52
|
+
* Only matches the plain label. A consumer using a custom `renderOption` to
|
|
53
|
+
* add its own content (e.g. a description) inside the option renders that
|
|
54
|
+
* content in the same label element, which breaks the anchored match. Select
|
|
55
|
+
* those options via {@link options} instead (e.g.
|
|
56
|
+
* `options.filter({ hasText: label })`).
|
|
57
|
+
*/
|
|
58
|
+
async selectOption(label) {
|
|
59
|
+
const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
60
|
+
const labelText = new RegExp(`^${escaped}(\\s*\\..*)?$`);
|
|
61
|
+
const option = this.root
|
|
62
|
+
.getByRole('option')
|
|
63
|
+
.filter({ has: this.root.page().locator('.euiSelectableListItem__text', { hasText: labelText }) });
|
|
64
|
+
const [ariaChecked, ariaSelected] = await Promise.all([
|
|
65
|
+
option.getAttribute('aria-checked'),
|
|
66
|
+
option.getAttribute('aria-selected'),
|
|
67
|
+
]);
|
|
68
|
+
if (ariaChecked === 'true' || ariaSelected === 'true') {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
await option.click();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Type into the selectable's search box to filter the options. Throws if the
|
|
75
|
+
* selectable is not searchable (no search box rendered).
|
|
76
|
+
*/
|
|
77
|
+
async search(term) {
|
|
78
|
+
// `fill()` auto-waits for the search box; if the selectable is not
|
|
79
|
+
// searchable it surfaces a clear locator-timeout on its own.
|
|
80
|
+
await this.root.locator(EuiSelectableSelectors.SEARCH_SELECTOR).fill(term);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=object.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"object.js","sourceRoot":"","sources":["../../../../../src/playwright/components/selectable/object.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAC;AAElF;;;;;;;;;GASG;AACH,MAAM,OAAO,mBAAoB,SAAQ,UAAU;IACjD,YAAY,KAAkB,EAAE,QAAgB;QAC9C,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,sBAAsB,CAAC,aAAa,CAAC,CAAC;IAC/D,CAAC;IAED;;;;;;OAMG;IACH,IAAW,OAAO;QAChB,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,KAAK,CAAC,YAAY,CAAC,KAAa;QAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;QAC7D,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,IAAI,OAAO,eAAe,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI;aACrB,SAAS,CAAC,QAAQ,CAAC;aACnB,MAAM,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,8BAA8B,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QACrG,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACpD,MAAM,CAAC,YAAY,CAAC,cAAc,CAAC;YACnC,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC;SACrC,CAAC,CAAC;QACH,IAAI,WAAW,KAAK,MAAM,IAAI,YAAY,KAAK,MAAM,EAAE,CAAC;YACtD,OAAO;QACT,CAAC;QACD,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,mEAAmE;QACnE,6DAA6D;QAC7D,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,sBAAsB,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7E,CAAC;CACF"}
|
package/lib/esm/selectors.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export { EuiComboBoxSelectors } from './components/combo_box/selectors';
|
|
|
2
2
|
export { EuiDataGridSelectors } from './components/datagrid/selectors';
|
|
3
3
|
export { EuiSuperSelectSelectors } from './components/form/super_select/selectors';
|
|
4
4
|
export { EuiGlobalToastListSelectors } from './components/toast/selectors';
|
|
5
|
+
export { EuiSelectableSelectors } from './components/selectable/selectors';
|
package/lib/esm/selectors.js
CHANGED
|
@@ -9,4 +9,5 @@ export { EuiComboBoxSelectors } from './components/combo_box/selectors';
|
|
|
9
9
|
export { EuiDataGridSelectors } from './components/datagrid/selectors';
|
|
10
10
|
export { EuiSuperSelectSelectors } from './components/form/super_select/selectors';
|
|
11
11
|
export { EuiGlobalToastListSelectors } from './components/toast/selectors';
|
|
12
|
+
export { EuiSelectableSelectors } from './components/selectable/selectors';
|
|
12
13
|
//# sourceMappingURL=selectors.js.map
|
package/lib/esm/selectors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selectors.js","sourceRoot":"","sources":["../../src/selectors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC"}
|
|
1
|
+
{"version":3,"file":"selectors.js","sourceRoot":"","sources":["../../src/selectors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elastic/eui-test-helpers",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0-snapshot.1787665877015",
|
|
4
4
|
"description": "A library of EUI test helpers for RTL, Cypress and Scout",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"repository": {
|
|
@@ -57,5 +57,6 @@
|
|
|
57
57
|
"!**/*.spec.js.map",
|
|
58
58
|
"README.md",
|
|
59
59
|
"licenses"
|
|
60
|
-
]
|
|
60
|
+
],
|
|
61
|
+
"stableVersion": "1.4.0"
|
|
61
62
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# EuiBasicTableObject
|
|
2
|
+
|
|
3
|
+
Playwright Component Object for [EuiBasicTable](https://eui.elastic.co/docs/components/tabular-content/tables/). Also covers `EuiInMemoryTable`, which renders a `EuiBasicTable` underneath and passes the `data-test-subj` straight through — there is no separate object for it.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { EuiBasicTableObject } from '@elastic/eui-test-helpers';
|
|
9
|
+
|
|
10
|
+
const table = new EuiBasicTableObject(page, 'myTable');
|
|
11
|
+
expect(await table.rows).toHaveCount(3);
|
|
12
|
+
expect(await table.cells('status')).toHaveText(['Running', 'Finished', 'Archived']);
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Set `data-test-subj` on the `<EuiBasicTable>`/`<EuiInMemoryTable>` (EUI spreads it onto the `.euiBasicTable` root, which the component-type guard verifies).
|
|
16
|
+
|
|
17
|
+
## API
|
|
18
|
+
|
|
19
|
+
| Member | Description |
|
|
20
|
+
|---|---|
|
|
21
|
+
| `rows` | `Locator` for the table's data rows, excluding EUI's own "no items found"/error-message row. |
|
|
22
|
+
| `cells(field)` | `Locator` for a field-data column's cells across all rows, resolved from EUI's own header `data-test-subj`. |
|
|
23
|
+
|
|
24
|
+
`rows` excludes EUI's empty/error message row by filtering out any row whose cell carries a `colspan` — real data rows don't set one, EUI's own placeholder row always does (it spans every column).
|
|
25
|
+
|
|
26
|
+
`cells(field)` resolves the column's position from the header cell EUI renders as `tableHeaderCell_<field>_<index>` (matched exactly, so a column named `status` won't also match a `status_detail` header), then reads the cell at that position in every row. This holds regardless of a leading selection-checkbox column, since the checkbox column is a real cell in both the header and body rows, and it matches both `<td>` and `<th>` since EUI renders a `rowHeader` column's body cell as `<th scope="row">`. Throws if no column with that `field` is rendered.
|
|
27
|
+
|
|
28
|
+
## Deliberately excluded
|
|
29
|
+
|
|
30
|
+
Row actions (consumer-supplied popovers/buttons), sorting, and pagination are not covered. They are either app-specific wiring rather than EUI-internal DOM knowledge, or had no evidenced consumer need at the time this was written. Open an issue with a concrete use case if you need one of them.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# EuiSelectableObject
|
|
2
|
+
|
|
3
|
+
Playwright Component Object for [EuiSelectable](https://eui.elastic.co/docs/components/forms/selection/selectable/).
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { EuiSelectableObject } from '@elastic/eui-test-helpers';
|
|
9
|
+
|
|
10
|
+
const selectable = new EuiSelectableObject(page, 'mySelectable');
|
|
11
|
+
await selectable.search('logs');
|
|
12
|
+
await selectable.selectOption('logs-*');
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Set `data-test-subj` on the `<EuiSelectable>` (EUI spreads it onto the `.euiSelectable` root, which the component-type guard verifies). When the selectable renders inside a popover, pass the popover as the `scope` and open or close it from your test. The popover is not this component's concern.
|
|
16
|
+
|
|
17
|
+
## API
|
|
18
|
+
|
|
19
|
+
| Member | Description |
|
|
20
|
+
|---|---|
|
|
21
|
+
| `options` | `Locator` for the rendered options, keeping Playwright auto-retry for count and content assertions. The list is virtualized, so `search()` first rather than scanning a long list. |
|
|
22
|
+
| `selectOption(label)` | Selects the option with the given label. A no-op if it is already checked. |
|
|
23
|
+
| `search(term)` | Types into the search box. Throws if the selectable is not searchable. |
|
|
24
|
+
|
|
25
|
+
`selectOption(label)` matches the label element and suits lists in their default state. It tolerates the screen-reader state text EUI appends (a checked option reads `<label> . Checked option.`) and `append` badges, and it will not match a different option whose label merely starts with the same text. Once you call `search()`, EUI injects highlight markers into the option's label, so a label match no longer resolves. On a searched list, select via the option's own `data-test-subj` or drive it through `options` directly.
|
|
26
|
+
|
|
27
|
+
It also only matches the plain label. A consumer using a custom `renderOption` to add its own content (e.g. a description) inside the option renders that content in the same label element, which breaks the anchored match. Select those options via `options` instead (e.g. `options.filter({ hasText: label })`).
|