@serenity-js/web 3.34.2 → 3.35.1
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/CHANGELOG.md +19 -0
- package/lib/screenplay/models/selectors/By.d.ts +80 -8
- package/lib/screenplay/models/selectors/By.d.ts.map +1 -1
- package/lib/screenplay/models/selectors/By.js +96 -7
- package/lib/screenplay/models/selectors/By.js.map +1 -1
- package/lib/screenplay/models/selectors/ByRole.d.ts +126 -0
- package/lib/screenplay/models/selectors/ByRole.d.ts.map +1 -0
- package/lib/screenplay/models/selectors/ByRole.js +23 -0
- package/lib/screenplay/models/selectors/ByRole.js.map +1 -0
- package/lib/screenplay/models/selectors/index.d.ts +1 -0
- package/lib/screenplay/models/selectors/index.d.ts.map +1 -1
- package/lib/screenplay/models/selectors/index.js +1 -0
- package/lib/screenplay/models/selectors/index.js.map +1 -1
- package/package.json +4 -4
- package/src/screenplay/models/selectors/By.ts +110 -15
- package/src/screenplay/models/selectors/ByRole.ts +219 -0
- package/src/screenplay/models/selectors/index.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [3.35.1](https://github.com/serenity-js/serenity-js/compare/v3.35.0...v3.35.1) (2025-09-28)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @serenity-js/web
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# [3.35.0](https://github.com/serenity-js/serenity-js/compare/v3.34.2...v3.35.0) (2025-09-07)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* **web:** support for identifying page elements by their ARIA role ([cf3672a](https://github.com/serenity-js/serenity-js/commit/cf3672a6fe3051eab9c195f2f342ebf55c19e2d6))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
6
25
|
## [3.34.2](https://github.com/serenity-js/serenity-js/compare/v3.34.1...v3.34.2) (2025-09-07)
|
|
7
26
|
|
|
8
27
|
**Note:** Version bump only for package @serenity-js/web
|
|
@@ -1,33 +1,37 @@
|
|
|
1
|
-
import type { Answerable } from '@serenity-js/core';
|
|
1
|
+
import type { Answerable, WithAnswerableProperties } from '@serenity-js/core';
|
|
2
2
|
import { Question } from '@serenity-js/core';
|
|
3
3
|
import { ByCss } from './ByCss';
|
|
4
4
|
import { ByCssContainingText } from './ByCssContainingText';
|
|
5
5
|
import { ById } from './ById';
|
|
6
|
+
import type { ByRoleSelectorOptions, ByRoleSelectorValue } from './ByRole';
|
|
7
|
+
import { ByRole } from './ByRole';
|
|
6
8
|
import { ByTagName } from './ByTagName';
|
|
7
9
|
import { ByXPath } from './ByXPath';
|
|
8
10
|
/**
|
|
9
11
|
* `By` produces a [`Selector`](https://serenity-js.org/api/web/class/Selector/) used to locate a [`PageElement`](https://serenity-js.org/api/web/class/PageElement/) or [`PageElement`](https://serenity-js.org/api/web/class/PageElements/) on a web page.
|
|
10
12
|
* Selectors can be defined using a static value or a [`Question`](https://serenity-js.org/api/core/class/Question/) to be resolved at runtime.
|
|
11
13
|
*
|
|
12
|
-
*
|
|
14
|
+
* ## Defining a selector using a string
|
|
15
|
+
*
|
|
16
|
+
* Every selector method on this class accepts a static `string` value to define a selector.
|
|
13
17
|
*
|
|
14
18
|
* ```typescript
|
|
15
19
|
* import { PageElement, By } from '@serenity-js/web'
|
|
16
20
|
*
|
|
17
21
|
* class LoginForm {
|
|
18
22
|
* static usernameField = () =>
|
|
19
|
-
* PageElement.located(By.
|
|
20
|
-
* .describedAs('username field')
|
|
23
|
+
* PageElement.located(By.role('textbox', { name: 'Username' }))
|
|
24
|
+
* .describedAs('username field'),
|
|
21
25
|
*
|
|
22
26
|
* static passwordField = () =>
|
|
23
|
-
* PageElement.located(By.css('[data-test="password"]'))
|
|
27
|
+
* PageElement.located(By.css('[data-test-id="password"]'))
|
|
24
28
|
* .describedAs('password field')
|
|
25
29
|
* }
|
|
26
30
|
* ```
|
|
27
31
|
*
|
|
28
|
-
*
|
|
32
|
+
* ## Defining a selector using a Question
|
|
29
33
|
*
|
|
30
|
-
* Each method on this class accepts an [`Answerable`](https://serenity-js.org/api/core/#Answerable) to allow for dynamic resolution of the selector.
|
|
34
|
+
* Each method on this class also accepts an [`Answerable`](https://serenity-js.org/api/core/#Answerable) to allow for dynamic resolution of the selector.
|
|
31
35
|
* This can be useful when the selector is not known at the time of writing the test, or when the selector
|
|
32
36
|
* needs to be calculated based on the state of the system under test.
|
|
33
37
|
*
|
|
@@ -45,7 +49,7 @@ import { ByXPath } from './ByXPath';
|
|
|
45
49
|
*
|
|
46
50
|
* ```
|
|
47
51
|
*
|
|
48
|
-
*
|
|
52
|
+
* ## Learn more
|
|
49
53
|
* - [Page Element Query Language](https://serenity-js.org/handbook/web-testing/page-element-query-language)
|
|
50
54
|
* - [`PageElement`](https://serenity-js.org/api/web/class/PageElement/)
|
|
51
55
|
* - [`PageElement`](https://serenity-js.org/api/web/class/PageElements/)
|
|
@@ -81,6 +85,74 @@ export declare class By {
|
|
|
81
85
|
* @param selector
|
|
82
86
|
*/
|
|
83
87
|
static id(selector: Answerable<string>): Question<Promise<ById>>;
|
|
88
|
+
/**
|
|
89
|
+
* Locates a [`PageElement`](https://serenity-js.org/api/web/class/PageElement/) by its [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),
|
|
90
|
+
* [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
|
|
91
|
+
*
|
|
92
|
+
* ### Example usage
|
|
93
|
+
*
|
|
94
|
+
* Given the following HTML structure:
|
|
95
|
+
*
|
|
96
|
+
* ```html
|
|
97
|
+
* <h3>Sign up</h3>
|
|
98
|
+
* <label>
|
|
99
|
+
* <input type="checkbox" /> Subscribe
|
|
100
|
+
* </label>
|
|
101
|
+
* <br/>
|
|
102
|
+
* <button>Submit</button>
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* Each element can be located by its implicit accessibility role:
|
|
106
|
+
*
|
|
107
|
+
* ```ts
|
|
108
|
+
* const heading = PageElement.located(By.role('heading', { name: 'Sign up' })).describedAs('Sign up heading');
|
|
109
|
+
* const checkbox = PageElement.located(By.role('checkbox', { name: 'Subscribe' })).describedAs('Subscribe checkbox');
|
|
110
|
+
* const button = PageElement.located(By.role('button', { name: 'Submit' })).describedAs('Submit button');
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* #### Playwright Test
|
|
114
|
+
*
|
|
115
|
+
* ```ts
|
|
116
|
+
* import { Ensure } from '@serenity-js/assertions'
|
|
117
|
+
* import { Click, PageElement, By, isVisible } from '@serenity-js/web'
|
|
118
|
+
*
|
|
119
|
+
* // ... page element definitions as above
|
|
120
|
+
*
|
|
121
|
+
* describe('ARIA role selector', () => {
|
|
122
|
+
* it('locates an element by its accessible name', async ({ actor }) => {
|
|
123
|
+
* await actor.attemptsTo(
|
|
124
|
+
* Ensure.that(heading, isVisible()),
|
|
125
|
+
* Click.on(checkbox),
|
|
126
|
+
* Click.on(button),
|
|
127
|
+
* )
|
|
128
|
+
* })
|
|
129
|
+
* })
|
|
130
|
+
* ```
|
|
131
|
+
*
|
|
132
|
+
* #### WebdriverIO
|
|
133
|
+
*
|
|
134
|
+
* ```ts
|
|
135
|
+
* import { actorCalled } from '@serenity-js/core'
|
|
136
|
+
* import { Ensure } from '@serenity-js/assertions'
|
|
137
|
+
* import { Click, PageElement, By, isVisible } from '@serenity-js/web'
|
|
138
|
+
*
|
|
139
|
+
* // ... page element definitions as above
|
|
140
|
+
*
|
|
141
|
+
* describe('ARIA role selector', () => {
|
|
142
|
+
* it('locates an element by its accessible name', async () => {
|
|
143
|
+
* await actorCalled('Nick').attemptsTo(
|
|
144
|
+
* Ensure.that(heading, isVisible()),
|
|
145
|
+
* Click.on(checkbox),
|
|
146
|
+
* Click.on(button),
|
|
147
|
+
* )
|
|
148
|
+
* })
|
|
149
|
+
* })
|
|
150
|
+
* ```
|
|
151
|
+
*
|
|
152
|
+
* @param role
|
|
153
|
+
* @param options
|
|
154
|
+
*/
|
|
155
|
+
static role(role: ByRoleSelectorValue, options?: Answerable<WithAnswerableProperties<ByRoleSelectorOptions>>): Question<Promise<ByRole>>;
|
|
84
156
|
/**
|
|
85
157
|
* Locates a [`PageElement`](https://serenity-js.org/api/web/class/PageElement/) using the name of its [HTML tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element).
|
|
86
158
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"By.d.ts","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/By.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"By.d.ts","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/By.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAAK,QAAQ,EAAO,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,qBAAa,EAAE;IAEX;;;;OAIG;IACH,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAOlE;;;;;;OAMG;IACH,MAAM,CAAC,iBAAiB,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAQxH;;;;;OAKG;IACH,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAOtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAOhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkEG;IACH,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,mBAAmB,EAAE,OAAO,GAAE,UAAU,CAAC,wBAAwB,CAAC,qBAAqB,CAAC,CAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAwB5I;;;;OAIG;IACH,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAO1E;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;CAMzE"}
|
|
@@ -6,31 +6,34 @@ const ByCss_1 = require("./ByCss");
|
|
|
6
6
|
const ByCssContainingText_1 = require("./ByCssContainingText");
|
|
7
7
|
const ByDeepCss_1 = require("./ByDeepCss");
|
|
8
8
|
const ById_1 = require("./ById");
|
|
9
|
+
const ByRole_1 = require("./ByRole");
|
|
9
10
|
const ByTagName_1 = require("./ByTagName");
|
|
10
11
|
const ByXPath_1 = require("./ByXPath");
|
|
11
12
|
/**
|
|
12
13
|
* `By` produces a [`Selector`](https://serenity-js.org/api/web/class/Selector/) used to locate a [`PageElement`](https://serenity-js.org/api/web/class/PageElement/) or [`PageElement`](https://serenity-js.org/api/web/class/PageElements/) on a web page.
|
|
13
14
|
* Selectors can be defined using a static value or a [`Question`](https://serenity-js.org/api/core/class/Question/) to be resolved at runtime.
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
+
* ## Defining a selector using a string
|
|
17
|
+
*
|
|
18
|
+
* Every selector method on this class accepts a static `string` value to define a selector.
|
|
16
19
|
*
|
|
17
20
|
* ```typescript
|
|
18
21
|
* import { PageElement, By } from '@serenity-js/web'
|
|
19
22
|
*
|
|
20
23
|
* class LoginForm {
|
|
21
24
|
* static usernameField = () =>
|
|
22
|
-
* PageElement.located(By.
|
|
23
|
-
* .describedAs('username field')
|
|
25
|
+
* PageElement.located(By.role('textbox', { name: 'Username' }))
|
|
26
|
+
* .describedAs('username field'),
|
|
24
27
|
*
|
|
25
28
|
* static passwordField = () =>
|
|
26
|
-
* PageElement.located(By.css('[data-test="password"]'))
|
|
29
|
+
* PageElement.located(By.css('[data-test-id="password"]'))
|
|
27
30
|
* .describedAs('password field')
|
|
28
31
|
* }
|
|
29
32
|
* ```
|
|
30
33
|
*
|
|
31
|
-
*
|
|
34
|
+
* ## Defining a selector using a Question
|
|
32
35
|
*
|
|
33
|
-
* Each method on this class accepts an [`Answerable`](https://serenity-js.org/api/core/#Answerable) to allow for dynamic resolution of the selector.
|
|
36
|
+
* Each method on this class also accepts an [`Answerable`](https://serenity-js.org/api/core/#Answerable) to allow for dynamic resolution of the selector.
|
|
34
37
|
* This can be useful when the selector is not known at the time of writing the test, or when the selector
|
|
35
38
|
* needs to be calculated based on the state of the system under test.
|
|
36
39
|
*
|
|
@@ -48,7 +51,7 @@ const ByXPath_1 = require("./ByXPath");
|
|
|
48
51
|
*
|
|
49
52
|
* ```
|
|
50
53
|
*
|
|
51
|
-
*
|
|
54
|
+
* ## Learn more
|
|
52
55
|
* - [Page Element Query Language](https://serenity-js.org/handbook/web-testing/page-element-query-language)
|
|
53
56
|
* - [`PageElement`](https://serenity-js.org/api/web/class/PageElement/)
|
|
54
57
|
* - [`PageElement`](https://serenity-js.org/api/web/class/PageElements/)
|
|
@@ -105,6 +108,92 @@ class By {
|
|
|
105
108
|
return new ById_1.ById(bySelector);
|
|
106
109
|
});
|
|
107
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Locates a [`PageElement`](https://serenity-js.org/api/web/class/PageElement/) by its [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),
|
|
113
|
+
* [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
|
|
114
|
+
*
|
|
115
|
+
* ### Example usage
|
|
116
|
+
*
|
|
117
|
+
* Given the following HTML structure:
|
|
118
|
+
*
|
|
119
|
+
* ```html
|
|
120
|
+
* <h3>Sign up</h3>
|
|
121
|
+
* <label>
|
|
122
|
+
* <input type="checkbox" /> Subscribe
|
|
123
|
+
* </label>
|
|
124
|
+
* <br/>
|
|
125
|
+
* <button>Submit</button>
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* Each element can be located by its implicit accessibility role:
|
|
129
|
+
*
|
|
130
|
+
* ```ts
|
|
131
|
+
* const heading = PageElement.located(By.role('heading', { name: 'Sign up' })).describedAs('Sign up heading');
|
|
132
|
+
* const checkbox = PageElement.located(By.role('checkbox', { name: 'Subscribe' })).describedAs('Subscribe checkbox');
|
|
133
|
+
* const button = PageElement.located(By.role('button', { name: 'Submit' })).describedAs('Submit button');
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* #### Playwright Test
|
|
137
|
+
*
|
|
138
|
+
* ```ts
|
|
139
|
+
* import { Ensure } from '@serenity-js/assertions'
|
|
140
|
+
* import { Click, PageElement, By, isVisible } from '@serenity-js/web'
|
|
141
|
+
*
|
|
142
|
+
* // ... page element definitions as above
|
|
143
|
+
*
|
|
144
|
+
* describe('ARIA role selector', () => {
|
|
145
|
+
* it('locates an element by its accessible name', async ({ actor }) => {
|
|
146
|
+
* await actor.attemptsTo(
|
|
147
|
+
* Ensure.that(heading, isVisible()),
|
|
148
|
+
* Click.on(checkbox),
|
|
149
|
+
* Click.on(button),
|
|
150
|
+
* )
|
|
151
|
+
* })
|
|
152
|
+
* })
|
|
153
|
+
* ```
|
|
154
|
+
*
|
|
155
|
+
* #### WebdriverIO
|
|
156
|
+
*
|
|
157
|
+
* ```ts
|
|
158
|
+
* import { actorCalled } from '@serenity-js/core'
|
|
159
|
+
* import { Ensure } from '@serenity-js/assertions'
|
|
160
|
+
* import { Click, PageElement, By, isVisible } from '@serenity-js/web'
|
|
161
|
+
*
|
|
162
|
+
* // ... page element definitions as above
|
|
163
|
+
*
|
|
164
|
+
* describe('ARIA role selector', () => {
|
|
165
|
+
* it('locates an element by its accessible name', async () => {
|
|
166
|
+
* await actorCalled('Nick').attemptsTo(
|
|
167
|
+
* Ensure.that(heading, isVisible()),
|
|
168
|
+
* Click.on(checkbox),
|
|
169
|
+
* Click.on(button),
|
|
170
|
+
* )
|
|
171
|
+
* })
|
|
172
|
+
* })
|
|
173
|
+
* ```
|
|
174
|
+
*
|
|
175
|
+
* @param role
|
|
176
|
+
* @param options
|
|
177
|
+
*/
|
|
178
|
+
static role(role, options = {}) {
|
|
179
|
+
const descriptionOf = (selectorOptions) => {
|
|
180
|
+
if (core_1.Question.isAQuestion(selectorOptions)) {
|
|
181
|
+
return (0, core_1.the) `by role ${role} (options: ${options})`;
|
|
182
|
+
}
|
|
183
|
+
if (Object.keys(selectorOptions).length === 0) {
|
|
184
|
+
return `by role "${role}"`;
|
|
185
|
+
}
|
|
186
|
+
const description = [];
|
|
187
|
+
for (const [key, value] of Object.entries(selectorOptions)) {
|
|
188
|
+
description.push(key + (0, core_1.f) `: ${value}`);
|
|
189
|
+
}
|
|
190
|
+
return `by role "${role}" (${description.join(', ')})`;
|
|
191
|
+
};
|
|
192
|
+
return core_1.Question.about(descriptionOf(options), async (actor) => {
|
|
193
|
+
const optionsValue = await actor.answer(core_1.Question.fromObject(options));
|
|
194
|
+
return new ByRole_1.ByRole(role, optionsValue);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
108
197
|
/**
|
|
109
198
|
* Locates a [`PageElement`](https://serenity-js.org/api/web/class/PageElement/) using the name of its [HTML tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element).
|
|
110
199
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"By.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/By.ts"],"names":[],"mappings":";;;AACA,
|
|
1
|
+
{"version":3,"file":"By.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/By.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Selector } from './Selector';
|
|
2
|
+
/**
|
|
3
|
+
* @group Models
|
|
4
|
+
*/
|
|
5
|
+
export type ByRoleSelectorValue = 'alert' | 'alertdialog' | 'application' | 'article' | 'banner' | 'blockquote' | 'button' | 'caption' | 'cell' | 'checkbox' | 'code' | 'columnheader' | 'combobox' | 'complementary' | 'contentinfo' | 'definition' | 'deletion' | 'dialog' | 'directory' | 'document' | 'emphasis' | 'feed' | 'figure' | 'form' | 'generic' | 'grid' | 'gridcell' | 'group' | 'heading' | 'img' | 'insertion' | 'link' | 'list' | 'listbox' | 'listitem' | 'log' | 'main' | 'marquee' | 'math' | 'meter' | 'menu' | 'menubar' | 'menuitem' | 'menuitemcheckbox' | 'menuitemradio' | 'navigation' | 'none' | 'note' | 'option' | 'paragraph' | 'presentation' | 'progressbar' | 'radio' | 'radiogroup' | 'region' | 'row' | 'rowgroup' | 'rowheader' | 'scrollbar' | 'search' | 'searchbox' | 'separator' | 'slider' | 'spinbutton' | 'status' | 'strong' | 'subscript' | 'superscript' | 'switch' | 'tab' | 'table' | 'tablist' | 'tabpanel' | 'term' | 'textbox' | 'time' | 'timer' | 'toolbar' | 'tooltip' | 'tree' | 'treegrid' | 'treeitem';
|
|
6
|
+
/**
|
|
7
|
+
* Locates a [`PageElement`](https://serenity-js.org/api/web/class/PageElement/) by its [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),
|
|
8
|
+
* [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
|
|
9
|
+
*
|
|
10
|
+
* **Pro tip:** Instantiate using [`By.role`](https://serenity-js.org/api/web/class/By/#role)
|
|
11
|
+
*
|
|
12
|
+
* @group Models
|
|
13
|
+
*/
|
|
14
|
+
export declare class ByRole extends Selector {
|
|
15
|
+
readonly value: ByRoleSelectorValue;
|
|
16
|
+
readonly options: ByRoleSelectorOptions;
|
|
17
|
+
constructor(value: ByRoleSelectorValue, options: ByRoleSelectorOptions);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* @group Models
|
|
21
|
+
*/
|
|
22
|
+
export interface ByRoleSelectorOptions {
|
|
23
|
+
/**
|
|
24
|
+
* An attribute that is usually set by [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked) or
|
|
25
|
+
* native `<input type=checkbox>` controls.
|
|
26
|
+
*
|
|
27
|
+
* :::tip Playwright only
|
|
28
|
+
* This property is supported only by Playwright.
|
|
29
|
+
* :::
|
|
30
|
+
*/
|
|
31
|
+
checked?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* An attribute that is usually set by `aria-disabled` or `disabled`.
|
|
34
|
+
*
|
|
35
|
+
* **NOTE** Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about
|
|
36
|
+
* [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).
|
|
37
|
+
*
|
|
38
|
+
* :::tip Playwright only
|
|
39
|
+
* This property is supported only by Playwright.
|
|
40
|
+
* :::
|
|
41
|
+
*/
|
|
42
|
+
disabled?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Whether [`name`](https://serenity-js.org/api/web/interface/ByRoleSelectorOptions/#name) is matched exactly:
|
|
45
|
+
* case-sensitive and whole-string.
|
|
46
|
+
*
|
|
47
|
+
* :::tip Playwright matching defaults
|
|
48
|
+
* Playwright defaults to `false` for backwards compatibility, but we recommend setting this property to `true`
|
|
49
|
+
* to avoid unintentional matches.
|
|
50
|
+
* :::
|
|
51
|
+
*
|
|
52
|
+
* :::tip Playwright and name RegExp
|
|
53
|
+
* Playwright supports using a `RegExp` to match the name.
|
|
54
|
+
* When using a `RegExp`, exact matching is not applicable and this option is ignored.
|
|
55
|
+
* Note that **exact match still trims whitespace**.
|
|
56
|
+
* :::
|
|
57
|
+
*
|
|
58
|
+
* :::tip WebdriverIO
|
|
59
|
+
* WebdriverIO always performs exact matching, so this option is ignored.
|
|
60
|
+
* :::
|
|
61
|
+
*/
|
|
62
|
+
exact?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* An attribute that is usually set by `aria-expanded`.
|
|
65
|
+
*
|
|
66
|
+
* Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).
|
|
67
|
+
*
|
|
68
|
+
* :::tip Playwright only
|
|
69
|
+
* This property is supported only by Playwright.
|
|
70
|
+
* :::
|
|
71
|
+
*/
|
|
72
|
+
expanded?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Option that controls whether hidden elements are matched. By default, only non-hidden elements, as
|
|
75
|
+
* [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.
|
|
76
|
+
*
|
|
77
|
+
* Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).
|
|
78
|
+
*
|
|
79
|
+
* :::tip Playwright only
|
|
80
|
+
* This property is supported only by Playwright.
|
|
81
|
+
* :::
|
|
82
|
+
*/
|
|
83
|
+
includeHidden?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values
|
|
86
|
+
* for `<h1>-<h6>` elements.
|
|
87
|
+
*
|
|
88
|
+
* Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).
|
|
89
|
+
*
|
|
90
|
+
* :::tip Playwright only
|
|
91
|
+
* This property is supported only by Playwright.
|
|
92
|
+
* :::
|
|
93
|
+
*/
|
|
94
|
+
level?: number;
|
|
95
|
+
/**
|
|
96
|
+
* Option to match the [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
|
|
97
|
+
*
|
|
98
|
+
* :::tip Playwright
|
|
99
|
+
* Only Playwright supports using a `RegExp` to match the name. When using a `string` instead, Playwright performs
|
|
100
|
+
* a **case-insensitive** match and searches for a **substring**. Use
|
|
101
|
+
* [`exact`](https://serenity-js.org/api/web/interface/ByRoleSelectorOptions/#exact) to control this behavior.
|
|
102
|
+
* :::
|
|
103
|
+
*
|
|
104
|
+
* :::tip WebdriverIO
|
|
105
|
+
* WebdriverIO does not support using `RegExp` and performs a **case-sensitive** and **exact** matching.
|
|
106
|
+
* :::
|
|
107
|
+
*/
|
|
108
|
+
name?: string | RegExp;
|
|
109
|
+
/**
|
|
110
|
+
* An attribute that is usually set by [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).
|
|
111
|
+
*
|
|
112
|
+
* :::tip Playwright only
|
|
113
|
+
* This property is supported only by Playwright.
|
|
114
|
+
* :::
|
|
115
|
+
*/
|
|
116
|
+
pressed?: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* An attribute that is usually set by [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).
|
|
119
|
+
*
|
|
120
|
+
* :::tip Playwright only
|
|
121
|
+
* This property is supported only by Playwright.
|
|
122
|
+
* :::
|
|
123
|
+
*/
|
|
124
|
+
selected?: boolean;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=ByRole.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ByRole.d.ts","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/ByRole.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,mBAAmB,GACzB,OAAO,GACP,aAAa,GACb,aAAa,GACb,SAAS,GACT,QAAQ,GACR,YAAY,GACZ,QAAQ,GACR,SAAS,GACT,MAAM,GACN,UAAU,GACV,MAAM,GACN,cAAc,GACd,UAAU,GACV,eAAe,GACf,aAAa,GACb,YAAY,GACZ,UAAU,GACV,QAAQ,GACR,WAAW,GACX,UAAU,GACV,UAAU,GACV,MAAM,GACN,QAAQ,GACR,MAAM,GACN,SAAS,GACT,MAAM,GACN,UAAU,GACV,OAAO,GACP,SAAS,GACT,KAAK,GACL,WAAW,GACX,MAAM,GACN,MAAM,GACN,SAAS,GACT,UAAU,GACV,KAAK,GACL,MAAM,GACN,SAAS,GACT,MAAM,GACN,OAAO,GACP,MAAM,GACN,SAAS,GACT,UAAU,GACV,kBAAkB,GAClB,eAAe,GACf,YAAY,GACZ,MAAM,GACN,MAAM,GACN,QAAQ,GACR,WAAW,GACX,cAAc,GACd,aAAa,GACb,OAAO,GACP,YAAY,GACZ,QAAQ,GACR,KAAK,GACL,UAAU,GACV,WAAW,GACX,WAAW,GACX,QAAQ,GACR,WAAW,GACX,WAAW,GACX,QAAQ,GACR,YAAY,GACZ,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,aAAa,GACb,QAAQ,GACR,KAAK,GACL,OAAO,GACP,SAAS,GACT,UAAU,GACV,MAAM,GACN,SAAS,GACT,MAAM,GACN,OAAO,GACP,SAAS,GACT,SAAS,GACT,MAAM,GACN,UAAU,GACV,UAAU,CAAA;AAEhB;;;;;;;GAOG;AACH,qBAAa,MAAO,SAAQ,QAAQ;aACJ,KAAK,EAAE,mBAAmB;aAAkB,OAAO,EAAE,qBAAqB;gBAA1E,KAAK,EAAE,mBAAmB,EAAkB,OAAO,EAAE,qBAAqB;CAGzG;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAElC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;;;;;;;;;;;;;;;OAkBG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;;;;;;;;;OAYG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAEvB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ByRole = void 0;
|
|
4
|
+
const Selector_1 = require("./Selector");
|
|
5
|
+
/**
|
|
6
|
+
* Locates a [`PageElement`](https://serenity-js.org/api/web/class/PageElement/) by its [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),
|
|
7
|
+
* [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
|
|
8
|
+
*
|
|
9
|
+
* **Pro tip:** Instantiate using [`By.role`](https://serenity-js.org/api/web/class/By/#role)
|
|
10
|
+
*
|
|
11
|
+
* @group Models
|
|
12
|
+
*/
|
|
13
|
+
class ByRole extends Selector_1.Selector {
|
|
14
|
+
value;
|
|
15
|
+
options;
|
|
16
|
+
constructor(value, options) {
|
|
17
|
+
super();
|
|
18
|
+
this.value = value;
|
|
19
|
+
this.options = options;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.ByRole = ByRole;
|
|
23
|
+
//# sourceMappingURL=ByRole.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ByRole.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/ByRole.ts"],"names":[],"mappings":";;;AAAA,yCAAsC;AAyFtC;;;;;;;GAOG;AACH,MAAa,MAAO,SAAQ,mBAAQ;IACJ;IAA4C;IAAxE,YAA4B,KAA0B,EAAkB,OAA8B;QAClG,KAAK,EAAE,CAAC;QADgB,UAAK,GAAL,KAAK,CAAqB;QAAkB,YAAO,GAAP,OAAO,CAAuB;IAEtG,CAAC;CACJ;AAJD,wBAIC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/index.ts"],"names":[],"mappings":"AAAA,cAAc,MAAM,CAAC;AACrB,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/index.ts"],"names":[],"mappings":"AAAA,cAAc,MAAM,CAAC;AACrB,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
|
|
@@ -19,6 +19,7 @@ __exportStar(require("./ByCss"), exports);
|
|
|
19
19
|
__exportStar(require("./ByCssContainingText"), exports);
|
|
20
20
|
__exportStar(require("./ByDeepCss"), exports);
|
|
21
21
|
__exportStar(require("./ById"), exports);
|
|
22
|
+
__exportStar(require("./ByRole"), exports);
|
|
22
23
|
__exportStar(require("./ByTagName"), exports);
|
|
23
24
|
__exportStar(require("./ByXPath"), exports);
|
|
24
25
|
__exportStar(require("./Selector"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,uCAAqB;AACrB,0CAAwB;AACxB,wDAAsC;AACtC,8CAA4B;AAC5B,yCAAuB;AACvB,8CAA4B;AAC5B,4CAA0B;AAC1B,6CAA2B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,uCAAqB;AACrB,0CAAwB;AACxB,wDAAsC;AACtC,8CAA4B;AAC5B,yCAAuB;AACvB,2CAAyB;AACzB,8CAA4B;AAC5B,4CAA0B;AAC1B,6CAA2B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serenity-js/web",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.35.1",
|
|
4
4
|
"description": "Serenity/JS Screenplay Pattern library offering a flexible, web driver-agnostic approach for interacting with web-based user interfaces and components, suitable for various testing contexts",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Jan Molak",
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"node": "^18.12 || ^20 || ^22"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@serenity-js/assertions": "3.
|
|
57
|
-
"@serenity-js/core": "3.
|
|
56
|
+
"@serenity-js/assertions": "3.35.1",
|
|
57
|
+
"@serenity-js/core": "3.35.1",
|
|
58
58
|
"tiny-types": "1.24.1"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"ts-node": "10.9.2",
|
|
68
68
|
"typescript": "5.9.2"
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "f8b08ebf12dbf37a3176d4a27c67afd466ed9b92"
|
|
71
71
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { Answerable} from '@serenity-js/core';
|
|
2
|
-
import { f, Question } from '@serenity-js/core';
|
|
1
|
+
import type { Answerable, WithAnswerableProperties } from '@serenity-js/core';
|
|
2
|
+
import { f, Question, the } from '@serenity-js/core';
|
|
3
3
|
|
|
4
4
|
import { ByCss } from './ByCss';
|
|
5
5
|
import { ByCssContainingText } from './ByCssContainingText';
|
|
6
6
|
import { ByDeepCss } from './ByDeepCss';
|
|
7
7
|
import { ById } from './ById';
|
|
8
|
+
import type { ByRoleSelectorOptions, ByRoleSelectorValue } from './ByRole';
|
|
9
|
+
import { ByRole } from './ByRole';
|
|
8
10
|
import { ByTagName } from './ByTagName';
|
|
9
11
|
import { ByXPath } from './ByXPath';
|
|
10
12
|
|
|
@@ -12,25 +14,27 @@ import { ByXPath } from './ByXPath';
|
|
|
12
14
|
* `By` produces a [`Selector`](https://serenity-js.org/api/web/class/Selector/) used to locate a [`PageElement`](https://serenity-js.org/api/web/class/PageElement/) or [`PageElement`](https://serenity-js.org/api/web/class/PageElements/) on a web page.
|
|
13
15
|
* Selectors can be defined using a static value or a [`Question`](https://serenity-js.org/api/core/class/Question/) to be resolved at runtime.
|
|
14
16
|
*
|
|
15
|
-
*
|
|
17
|
+
* ## Defining a selector using a string
|
|
18
|
+
*
|
|
19
|
+
* Every selector method on this class accepts a static `string` value to define a selector.
|
|
16
20
|
*
|
|
17
21
|
* ```typescript
|
|
18
22
|
* import { PageElement, By } from '@serenity-js/web'
|
|
19
23
|
*
|
|
20
24
|
* class LoginForm {
|
|
21
25
|
* static usernameField = () =>
|
|
22
|
-
* PageElement.located(By.
|
|
23
|
-
* .describedAs('username field')
|
|
26
|
+
* PageElement.located(By.role('textbox', { name: 'Username' }))
|
|
27
|
+
* .describedAs('username field'),
|
|
24
28
|
*
|
|
25
29
|
* static passwordField = () =>
|
|
26
|
-
* PageElement.located(By.css('[data-test="password"]'))
|
|
30
|
+
* PageElement.located(By.css('[data-test-id="password"]'))
|
|
27
31
|
* .describedAs('password field')
|
|
28
32
|
* }
|
|
29
33
|
* ```
|
|
30
34
|
*
|
|
31
|
-
*
|
|
35
|
+
* ## Defining a selector using a Question
|
|
32
36
|
*
|
|
33
|
-
* Each method on this class accepts an [`Answerable`](https://serenity-js.org/api/core/#Answerable) to allow for dynamic resolution of the selector.
|
|
37
|
+
* Each method on this class also accepts an [`Answerable`](https://serenity-js.org/api/core/#Answerable) to allow for dynamic resolution of the selector.
|
|
34
38
|
* This can be useful when the selector is not known at the time of writing the test, or when the selector
|
|
35
39
|
* needs to be calculated based on the state of the system under test.
|
|
36
40
|
*
|
|
@@ -48,7 +52,7 @@ import { ByXPath } from './ByXPath';
|
|
|
48
52
|
*
|
|
49
53
|
* ```
|
|
50
54
|
*
|
|
51
|
-
*
|
|
55
|
+
* ## Learn more
|
|
52
56
|
* - [Page Element Query Language](https://serenity-js.org/handbook/web-testing/page-element-query-language)
|
|
53
57
|
* - [`PageElement`](https://serenity-js.org/api/web/class/PageElement/)
|
|
54
58
|
* - [`PageElement`](https://serenity-js.org/api/web/class/PageElements/)
|
|
@@ -64,7 +68,7 @@ export class By {
|
|
|
64
68
|
* @param selector
|
|
65
69
|
*/
|
|
66
70
|
static css(selector: Answerable<string>): Question<Promise<ByCss>> {
|
|
67
|
-
return Question.about(f`by css (${selector})`, async actor => {
|
|
71
|
+
return Question.about(f`by css (${ selector })`, async actor => {
|
|
68
72
|
const bySelector = await actor.answer(selector);
|
|
69
73
|
return new ByCss(bySelector);
|
|
70
74
|
});
|
|
@@ -78,7 +82,7 @@ export class By {
|
|
|
78
82
|
* @param text
|
|
79
83
|
*/
|
|
80
84
|
static cssContainingText(selector: Answerable<string>, text: Answerable<string>): Question<Promise<ByCssContainingText>> {
|
|
81
|
-
return Question.about(f`by css (${selector}) containing text ${ text }`, async actor => {
|
|
85
|
+
return Question.about(f`by css (${ selector }) containing text ${ text }`, async actor => {
|
|
82
86
|
const bySelector = await actor.answer(selector);
|
|
83
87
|
const textSelector = await actor.answer(text);
|
|
84
88
|
return new ByCssContainingText(bySelector, textSelector);
|
|
@@ -92,7 +96,7 @@ export class By {
|
|
|
92
96
|
* @param selector
|
|
93
97
|
*/
|
|
94
98
|
static deepCss(selector: Answerable<string>): Question<Promise<ByCss>> {
|
|
95
|
-
return Question.about(f`by deep css (${selector})`, async actor => {
|
|
99
|
+
return Question.about(f`by deep css (${ selector })`, async actor => {
|
|
96
100
|
const bySelector = await actor.answer(selector);
|
|
97
101
|
return new ByDeepCss(bySelector);
|
|
98
102
|
});
|
|
@@ -104,19 +108,110 @@ export class By {
|
|
|
104
108
|
* @param selector
|
|
105
109
|
*/
|
|
106
110
|
static id(selector: Answerable<string>): Question<Promise<ById>> {
|
|
107
|
-
return Question.about(f`by id (${selector})`, async actor => {
|
|
111
|
+
return Question.about(f`by id (${ selector })`, async actor => {
|
|
108
112
|
const bySelector = await actor.answer(selector);
|
|
109
113
|
return new ById(bySelector);
|
|
110
114
|
});
|
|
111
115
|
}
|
|
112
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Locates a [`PageElement`](https://serenity-js.org/api/web/class/PageElement/) by its [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),
|
|
119
|
+
* [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
|
|
120
|
+
*
|
|
121
|
+
* ### Example usage
|
|
122
|
+
*
|
|
123
|
+
* Given the following HTML structure:
|
|
124
|
+
*
|
|
125
|
+
* ```html
|
|
126
|
+
* <h3>Sign up</h3>
|
|
127
|
+
* <label>
|
|
128
|
+
* <input type="checkbox" /> Subscribe
|
|
129
|
+
* </label>
|
|
130
|
+
* <br/>
|
|
131
|
+
* <button>Submit</button>
|
|
132
|
+
* ```
|
|
133
|
+
*
|
|
134
|
+
* Each element can be located by its implicit accessibility role:
|
|
135
|
+
*
|
|
136
|
+
* ```ts
|
|
137
|
+
* const heading = PageElement.located(By.role('heading', { name: 'Sign up' })).describedAs('Sign up heading');
|
|
138
|
+
* const checkbox = PageElement.located(By.role('checkbox', { name: 'Subscribe' })).describedAs('Subscribe checkbox');
|
|
139
|
+
* const button = PageElement.located(By.role('button', { name: 'Submit' })).describedAs('Submit button');
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* #### Playwright Test
|
|
143
|
+
*
|
|
144
|
+
* ```ts
|
|
145
|
+
* import { Ensure } from '@serenity-js/assertions'
|
|
146
|
+
* import { Click, PageElement, By, isVisible } from '@serenity-js/web'
|
|
147
|
+
*
|
|
148
|
+
* // ... page element definitions as above
|
|
149
|
+
*
|
|
150
|
+
* describe('ARIA role selector', () => {
|
|
151
|
+
* it('locates an element by its accessible name', async ({ actor }) => {
|
|
152
|
+
* await actor.attemptsTo(
|
|
153
|
+
* Ensure.that(heading, isVisible()),
|
|
154
|
+
* Click.on(checkbox),
|
|
155
|
+
* Click.on(button),
|
|
156
|
+
* )
|
|
157
|
+
* })
|
|
158
|
+
* })
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* #### WebdriverIO
|
|
162
|
+
*
|
|
163
|
+
* ```ts
|
|
164
|
+
* import { actorCalled } from '@serenity-js/core'
|
|
165
|
+
* import { Ensure } from '@serenity-js/assertions'
|
|
166
|
+
* import { Click, PageElement, By, isVisible } from '@serenity-js/web'
|
|
167
|
+
*
|
|
168
|
+
* // ... page element definitions as above
|
|
169
|
+
*
|
|
170
|
+
* describe('ARIA role selector', () => {
|
|
171
|
+
* it('locates an element by its accessible name', async () => {
|
|
172
|
+
* await actorCalled('Nick').attemptsTo(
|
|
173
|
+
* Ensure.that(heading, isVisible()),
|
|
174
|
+
* Click.on(checkbox),
|
|
175
|
+
* Click.on(button),
|
|
176
|
+
* )
|
|
177
|
+
* })
|
|
178
|
+
* })
|
|
179
|
+
* ```
|
|
180
|
+
*
|
|
181
|
+
* @param role
|
|
182
|
+
* @param options
|
|
183
|
+
*/
|
|
184
|
+
static role(role: ByRoleSelectorValue, options: Answerable<WithAnswerableProperties<ByRoleSelectorOptions>> = {}): Question<Promise<ByRole>> {
|
|
185
|
+
const descriptionOf = (selectorOptions: Answerable<WithAnswerableProperties<ByRoleSelectorOptions>>) => {
|
|
186
|
+
if (Question.isAQuestion(selectorOptions)) {
|
|
187
|
+
return the`by role ${ role } (options: ${ options })`
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (Object.keys(selectorOptions).length === 0) {
|
|
191
|
+
return `by role "${ role }"`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const description = [];
|
|
195
|
+
for (const [ key, value ] of Object.entries(selectorOptions)) {
|
|
196
|
+
description.push(key + f`: ${ value }`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return `by role "${ role }" (${ description.join(', ') })`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return Question.about(descriptionOf(options), async actor => {
|
|
203
|
+
const optionsValue = await actor.answer(Question.fromObject(options)) as ByRoleSelectorOptions;
|
|
204
|
+
return new ByRole(role, optionsValue);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
113
208
|
/**
|
|
114
209
|
* Locates a [`PageElement`](https://serenity-js.org/api/web/class/PageElement/) using the name of its [HTML tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element).
|
|
115
210
|
*
|
|
116
211
|
* @param selector
|
|
117
212
|
*/
|
|
118
213
|
static tagName(selector: Answerable<string>): Question<Promise<ByTagName>> {
|
|
119
|
-
return Question.about(f`by tag name (${selector})`, async actor => {
|
|
214
|
+
return Question.about(f`by tag name (${ selector })`, async actor => {
|
|
120
215
|
const bySelector = await actor.answer(selector);
|
|
121
216
|
return new ByTagName(bySelector);
|
|
122
217
|
});
|
|
@@ -128,7 +223,7 @@ export class By {
|
|
|
128
223
|
* @param selector
|
|
129
224
|
*/
|
|
130
225
|
static xpath(selector: Answerable<string>): Question<Promise<ByXPath>> {
|
|
131
|
-
return Question.about(f`by xpath (${selector})`, async actor => {
|
|
226
|
+
return Question.about(f`by xpath (${ selector })`, async actor => {
|
|
132
227
|
const bySelector = await actor.answer(selector);
|
|
133
228
|
return new ByXPath(bySelector);
|
|
134
229
|
});
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { Selector } from './Selector';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @group Models
|
|
5
|
+
*/
|
|
6
|
+
export type ByRoleSelectorValue =
|
|
7
|
+
| 'alert'
|
|
8
|
+
| 'alertdialog'
|
|
9
|
+
| 'application'
|
|
10
|
+
| 'article'
|
|
11
|
+
| 'banner'
|
|
12
|
+
| 'blockquote'
|
|
13
|
+
| 'button'
|
|
14
|
+
| 'caption'
|
|
15
|
+
| 'cell'
|
|
16
|
+
| 'checkbox'
|
|
17
|
+
| 'code'
|
|
18
|
+
| 'columnheader'
|
|
19
|
+
| 'combobox'
|
|
20
|
+
| 'complementary'
|
|
21
|
+
| 'contentinfo'
|
|
22
|
+
| 'definition'
|
|
23
|
+
| 'deletion'
|
|
24
|
+
| 'dialog'
|
|
25
|
+
| 'directory'
|
|
26
|
+
| 'document'
|
|
27
|
+
| 'emphasis'
|
|
28
|
+
| 'feed'
|
|
29
|
+
| 'figure'
|
|
30
|
+
| 'form'
|
|
31
|
+
| 'generic'
|
|
32
|
+
| 'grid'
|
|
33
|
+
| 'gridcell'
|
|
34
|
+
| 'group'
|
|
35
|
+
| 'heading'
|
|
36
|
+
| 'img'
|
|
37
|
+
| 'insertion'
|
|
38
|
+
| 'link'
|
|
39
|
+
| 'list'
|
|
40
|
+
| 'listbox'
|
|
41
|
+
| 'listitem'
|
|
42
|
+
| 'log'
|
|
43
|
+
| 'main'
|
|
44
|
+
| 'marquee'
|
|
45
|
+
| 'math'
|
|
46
|
+
| 'meter'
|
|
47
|
+
| 'menu'
|
|
48
|
+
| 'menubar'
|
|
49
|
+
| 'menuitem'
|
|
50
|
+
| 'menuitemcheckbox'
|
|
51
|
+
| 'menuitemradio'
|
|
52
|
+
| 'navigation'
|
|
53
|
+
| 'none'
|
|
54
|
+
| 'note'
|
|
55
|
+
| 'option'
|
|
56
|
+
| 'paragraph'
|
|
57
|
+
| 'presentation'
|
|
58
|
+
| 'progressbar'
|
|
59
|
+
| 'radio'
|
|
60
|
+
| 'radiogroup'
|
|
61
|
+
| 'region'
|
|
62
|
+
| 'row'
|
|
63
|
+
| 'rowgroup'
|
|
64
|
+
| 'rowheader'
|
|
65
|
+
| 'scrollbar'
|
|
66
|
+
| 'search'
|
|
67
|
+
| 'searchbox'
|
|
68
|
+
| 'separator'
|
|
69
|
+
| 'slider'
|
|
70
|
+
| 'spinbutton'
|
|
71
|
+
| 'status'
|
|
72
|
+
| 'strong'
|
|
73
|
+
| 'subscript'
|
|
74
|
+
| 'superscript'
|
|
75
|
+
| 'switch'
|
|
76
|
+
| 'tab'
|
|
77
|
+
| 'table'
|
|
78
|
+
| 'tablist'
|
|
79
|
+
| 'tabpanel'
|
|
80
|
+
| 'term'
|
|
81
|
+
| 'textbox'
|
|
82
|
+
| 'time'
|
|
83
|
+
| 'timer'
|
|
84
|
+
| 'toolbar'
|
|
85
|
+
| 'tooltip'
|
|
86
|
+
| 'tree'
|
|
87
|
+
| 'treegrid'
|
|
88
|
+
| 'treeitem'
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Locates a [`PageElement`](https://serenity-js.org/api/web/class/PageElement/) by its [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),
|
|
92
|
+
* [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
|
|
93
|
+
*
|
|
94
|
+
* **Pro tip:** Instantiate using [`By.role`](https://serenity-js.org/api/web/class/By/#role)
|
|
95
|
+
*
|
|
96
|
+
* @group Models
|
|
97
|
+
*/
|
|
98
|
+
export class ByRole extends Selector {
|
|
99
|
+
constructor(public readonly value: ByRoleSelectorValue, public readonly options: ByRoleSelectorOptions) {
|
|
100
|
+
super();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @group Models
|
|
106
|
+
*/
|
|
107
|
+
export interface ByRoleSelectorOptions {
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* An attribute that is usually set by [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked) or
|
|
111
|
+
* native `<input type=checkbox>` controls.
|
|
112
|
+
*
|
|
113
|
+
* :::tip Playwright only
|
|
114
|
+
* This property is supported only by Playwright.
|
|
115
|
+
* :::
|
|
116
|
+
*/
|
|
117
|
+
checked?: boolean;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* An attribute that is usually set by `aria-disabled` or `disabled`.
|
|
121
|
+
*
|
|
122
|
+
* **NOTE** Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about
|
|
123
|
+
* [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).
|
|
124
|
+
*
|
|
125
|
+
* :::tip Playwright only
|
|
126
|
+
* This property is supported only by Playwright.
|
|
127
|
+
* :::
|
|
128
|
+
*/
|
|
129
|
+
disabled?: boolean;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Whether [`name`](https://serenity-js.org/api/web/interface/ByRoleSelectorOptions/#name) is matched exactly:
|
|
133
|
+
* case-sensitive and whole-string.
|
|
134
|
+
*
|
|
135
|
+
* :::tip Playwright matching defaults
|
|
136
|
+
* Playwright defaults to `false` for backwards compatibility, but we recommend setting this property to `true`
|
|
137
|
+
* to avoid unintentional matches.
|
|
138
|
+
* :::
|
|
139
|
+
*
|
|
140
|
+
* :::tip Playwright and name RegExp
|
|
141
|
+
* Playwright supports using a `RegExp` to match the name.
|
|
142
|
+
* When using a `RegExp`, exact matching is not applicable and this option is ignored.
|
|
143
|
+
* Note that **exact match still trims whitespace**.
|
|
144
|
+
* :::
|
|
145
|
+
*
|
|
146
|
+
* :::tip WebdriverIO
|
|
147
|
+
* WebdriverIO always performs exact matching, so this option is ignored.
|
|
148
|
+
* :::
|
|
149
|
+
*/
|
|
150
|
+
exact?: boolean;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* An attribute that is usually set by `aria-expanded`.
|
|
154
|
+
*
|
|
155
|
+
* Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).
|
|
156
|
+
*
|
|
157
|
+
* :::tip Playwright only
|
|
158
|
+
* This property is supported only by Playwright.
|
|
159
|
+
* :::
|
|
160
|
+
*/
|
|
161
|
+
expanded?: boolean;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Option that controls whether hidden elements are matched. By default, only non-hidden elements, as
|
|
165
|
+
* [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.
|
|
166
|
+
*
|
|
167
|
+
* Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).
|
|
168
|
+
*
|
|
169
|
+
* :::tip Playwright only
|
|
170
|
+
* This property is supported only by Playwright.
|
|
171
|
+
* :::
|
|
172
|
+
*/
|
|
173
|
+
includeHidden?: boolean;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values
|
|
177
|
+
* for `<h1>-<h6>` elements.
|
|
178
|
+
*
|
|
179
|
+
* Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).
|
|
180
|
+
*
|
|
181
|
+
* :::tip Playwright only
|
|
182
|
+
* This property is supported only by Playwright.
|
|
183
|
+
* :::
|
|
184
|
+
*/
|
|
185
|
+
level?: number;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Option to match the [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
|
|
189
|
+
*
|
|
190
|
+
* :::tip Playwright
|
|
191
|
+
* Only Playwright supports using a `RegExp` to match the name. When using a `string` instead, Playwright performs
|
|
192
|
+
* a **case-insensitive** match and searches for a **substring**. Use
|
|
193
|
+
* [`exact`](https://serenity-js.org/api/web/interface/ByRoleSelectorOptions/#exact) to control this behavior.
|
|
194
|
+
* :::
|
|
195
|
+
*
|
|
196
|
+
* :::tip WebdriverIO
|
|
197
|
+
* WebdriverIO does not support using `RegExp` and performs a **case-sensitive** and **exact** matching.
|
|
198
|
+
* :::
|
|
199
|
+
*/
|
|
200
|
+
name?: string | RegExp;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* An attribute that is usually set by [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).
|
|
204
|
+
*
|
|
205
|
+
* :::tip Playwright only
|
|
206
|
+
* This property is supported only by Playwright.
|
|
207
|
+
* :::
|
|
208
|
+
*/
|
|
209
|
+
pressed?: boolean;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* An attribute that is usually set by [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).
|
|
213
|
+
*
|
|
214
|
+
* :::tip Playwright only
|
|
215
|
+
* This property is supported only by Playwright.
|
|
216
|
+
* :::
|
|
217
|
+
*/
|
|
218
|
+
selected?: boolean;
|
|
219
|
+
}
|