@serenity-js/web 3.0.0-rc.13 → 3.0.0-rc.14
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 +171 -0
- package/lib/errors/index.js +5 -1
- package/lib/errors/index.js.map +1 -1
- package/lib/expectations/index.js +5 -1
- package/lib/expectations/index.js.map +1 -1
- package/lib/expectations/isVisible.d.ts +4 -1
- package/lib/expectations/isVisible.js +4 -1
- package/lib/expectations/isVisible.js.map +1 -1
- package/lib/index.js +5 -1
- package/lib/index.js.map +1 -1
- package/lib/input/index.js +5 -1
- package/lib/input/index.js.map +1 -1
- package/lib/screenplay/abilities/index.js +5 -1
- package/lib/screenplay/abilities/index.js.map +1 -1
- package/lib/screenplay/index.js +5 -1
- package/lib/screenplay/index.js.map +1 -1
- package/lib/screenplay/interactions/index.js +5 -1
- package/lib/screenplay/interactions/index.js.map +1 -1
- package/lib/screenplay/models/PageElement.d.ts +9 -0
- package/lib/screenplay/models/PageElement.js +1 -2
- package/lib/screenplay/models/PageElement.js.map +1 -1
- package/lib/screenplay/models/PageElements.js +2 -2
- package/lib/screenplay/models/PageElements.js.map +1 -1
- package/lib/screenplay/models/index.js +5 -1
- package/lib/screenplay/models/index.js.map +1 -1
- package/lib/screenplay/models/selectors/index.js +5 -1
- package/lib/screenplay/models/selectors/index.js.map +1 -1
- package/lib/screenplay/questions/Attribute.d.ts +62 -25
- package/lib/screenplay/questions/Attribute.js +68 -27
- package/lib/screenplay/questions/Attribute.js.map +1 -1
- package/lib/screenplay/questions/CssClasses.d.ts +70 -26
- package/lib/screenplay/questions/CssClasses.js +76 -30
- package/lib/screenplay/questions/CssClasses.js.map +1 -1
- package/lib/screenplay/questions/Selected.d.ts +5 -5
- package/lib/screenplay/questions/Selected.js +2 -2
- package/lib/screenplay/questions/Selected.js.map +1 -1
- package/lib/screenplay/questions/Text.d.ts +22 -18
- package/lib/screenplay/questions/Text.js +61 -19
- package/lib/screenplay/questions/Text.js.map +1 -1
- package/lib/screenplay/questions/Value.d.ts +55 -16
- package/lib/screenplay/questions/Value.js +59 -18
- package/lib/screenplay/questions/Value.js.map +1 -1
- package/lib/screenplay/questions/index.js +5 -1
- package/lib/screenplay/questions/index.js.map +1 -1
- package/lib/stage/crew/index.js +5 -1
- package/lib/stage/crew/index.js.map +1 -1
- package/lib/stage/crew/photographer/index.js +5 -1
- package/lib/stage/crew/photographer/index.js.map +1 -1
- package/lib/stage/crew/photographer/strategies/index.js +5 -1
- package/lib/stage/crew/photographer/strategies/index.js.map +1 -1
- package/lib/stage/index.js +5 -1
- package/lib/stage/index.js.map +1 -1
- package/package.json +6 -6
- package/src/expectations/isVisible.ts +4 -1
- package/src/screenplay/models/PageElement.ts +11 -2
- package/src/screenplay/models/PageElements.ts +3 -3
- package/src/screenplay/questions/Attribute.ts +85 -34
- package/src/screenplay/questions/CssClasses.ts +82 -30
- package/src/screenplay/questions/Selected.ts +7 -7
- package/src/screenplay/questions/Text.ts +80 -25
- package/src/screenplay/questions/Value.ts +67 -20
- package/lib/screenplay/questions/ElementQuestion.d.ts +0 -33
- package/lib/screenplay/questions/ElementQuestion.js +0 -53
- package/lib/screenplay/questions/ElementQuestion.js.map +0 -1
- package/src/screenplay/questions/ElementQuestion.ts +0 -58
|
@@ -5,7 +5,10 @@ import { ElementExpectation } from './ElementExpectation';
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @desc
|
|
8
|
-
* Expectation that the element is present in the DOM of the page and
|
|
8
|
+
* Expectation that the element is present in the DOM of the page and:
|
|
9
|
+
* - is not hidden, so doesn't have `display: none`, `visibility: hidden` or `opacity: 0`
|
|
10
|
+
* - is within the browser viewport
|
|
11
|
+
* - doesn't have its centre covered by other elements
|
|
9
12
|
*
|
|
10
13
|
* @returns {@serenity-js/core/lib/screenplay/questions~Expectation<boolean, Element<'async'>>}
|
|
11
14
|
*
|
|
@@ -16,9 +16,8 @@ export abstract class PageElement<Native_Element_Type = any> implements Optional
|
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
// todo: review usages and consider removing if not used
|
|
20
19
|
static of<NET>(childElement: Answerable<PageElement<NET>>, parentElement: Answerable<PageElement<NET>>): QuestionAdapter<PageElement<NET>> {
|
|
21
|
-
return Question.about(d`${ childElement } of ${ parentElement }
|
|
20
|
+
return Question.about(d`${ childElement } of ${ parentElement }`, async actor => {
|
|
22
21
|
const child = await actor.answer(childElement);
|
|
23
22
|
const parent = await actor.answer(parentElement);
|
|
24
23
|
|
|
@@ -83,5 +82,15 @@ export abstract class PageElement<Native_Element_Type = any> implements Optional
|
|
|
83
82
|
abstract isPresent(): Promise<boolean>;
|
|
84
83
|
|
|
85
84
|
abstract isSelected(): Promise<boolean>;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @desc
|
|
88
|
+
* Checks if the PageElement:
|
|
89
|
+
* - is not hidden, so doesn't have CSS style like `display: none`, `visibility: hidden` or `opacity: 0`
|
|
90
|
+
* - is within the browser viewport
|
|
91
|
+
* - doesn't have its centre covered by other elements
|
|
92
|
+
*
|
|
93
|
+
* @returns {Promise<boolean>}
|
|
94
|
+
*/
|
|
86
95
|
abstract isVisible(): Promise<boolean>;
|
|
87
96
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Answerable,
|
|
1
|
+
import { Answerable, List, MetaQuestion, Question } from '@serenity-js/core';
|
|
2
2
|
|
|
3
3
|
import { BrowseTheWeb } from '../abilities';
|
|
4
4
|
import { Locator } from './Locator';
|
|
@@ -22,7 +22,7 @@ export class PageElements<Native_Element_Type = any>
|
|
|
22
22
|
|
|
23
23
|
of(parent: Answerable<PageElement<Native_Element_Type>>): PageElements<Native_Element_Type> {
|
|
24
24
|
return new PageElements<Native_Element_Type>(relativeToParent(this.locator, parent))
|
|
25
|
-
.describedAs(
|
|
25
|
+
.describedAs(`${ this.toString() } of ${ parent }`);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -40,7 +40,7 @@ function relativeToDocumentRoot<Native_Element_Type>(selector: Answerable<Select
|
|
|
40
40
|
* @package
|
|
41
41
|
*/
|
|
42
42
|
function relativeToParent<Native_Element_Type>(relativeLocator: Answerable<Locator<Native_Element_Type>>, parent: Answerable<PageElement<Native_Element_Type>>): Question<Promise<Locator<Native_Element_Type>>> {
|
|
43
|
-
return Question.about(relativeLocator.toString()
|
|
43
|
+
return Question.about(`${ relativeLocator.toString() } of ${ parent }`, async actor => {
|
|
44
44
|
const locator: Locator<Native_Element_Type> = await actor.answer(relativeLocator);
|
|
45
45
|
const parentElement: PageElement<Native_Element_Type> = await actor.answer(parent);
|
|
46
46
|
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { Answerable, AnswersQuestions, LogicError, MetaQuestion, Question, UsesAbilities } from '@serenity-js/core';
|
|
1
|
+
import { Answerable, AnswersQuestions, d, LogicError, MetaQuestion, Question, QuestionAdapter, UsesAbilities } from '@serenity-js/core';
|
|
2
2
|
|
|
3
3
|
import { PageElement } from '../models';
|
|
4
|
-
import { ElementQuestion } from './ElementQuestion';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* @desc
|
|
8
|
-
* Returns the value of the
|
|
9
|
-
* represented by Answerable<{@link @wdio/types~Element}>
|
|
7
|
+
* Returns the value of the specified HTML attribute of a given {@link PageElement}.
|
|
10
8
|
*
|
|
11
9
|
* @example <caption>Example widget</caption>
|
|
12
10
|
* <ul id="shopping-list" data-items-left="2">
|
|
@@ -15,37 +13,60 @@ import { ElementQuestion } from './ElementQuestion';
|
|
|
15
13
|
* <li data-state="buy">Chocolate<li>
|
|
16
14
|
* </ul>
|
|
17
15
|
*
|
|
18
|
-
* @example <caption>Retrieve
|
|
16
|
+
* @example <caption>Retrieve an HTML attribute of a given PageElement</caption>
|
|
19
17
|
* import { actorCalled } from '@serenity-js/core';
|
|
20
18
|
* import { Ensure, equals } from '@serenity-js/assertions';
|
|
21
|
-
* import { Attribute,
|
|
19
|
+
* import { Attribute, By, PageElement } from '@serenity-js/web';
|
|
20
|
+
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
|
|
22
21
|
*
|
|
23
22
|
* const shoppingList = () =>
|
|
24
|
-
*
|
|
23
|
+
* PageElement.located(By.id('shopping-list')).describedAs('shopping list');
|
|
25
24
|
*
|
|
26
25
|
* actorCalled('Lisa')
|
|
27
|
-
* .whoCan(
|
|
26
|
+
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
|
|
28
27
|
* .attemptsTo(
|
|
29
|
-
* Ensure.that(
|
|
28
|
+
* Ensure.that(
|
|
29
|
+
* Attribute.called('data-items-left').of(shoppingList()),
|
|
30
|
+
* equals('2')
|
|
31
|
+
* ),
|
|
32
|
+
* )
|
|
33
|
+
*
|
|
34
|
+
* @example <caption>Using Attribute as QuestionAdapter</caption>
|
|
35
|
+
* import { actorCalled } from '@serenity-js/core';
|
|
36
|
+
* import { Ensure, equals } from '@serenity-js/assertions';
|
|
37
|
+
* import { Attribute, By, PageElement } from '@serenity-js/web';
|
|
38
|
+
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
|
|
39
|
+
*
|
|
40
|
+
* const shoppingList = () =>
|
|
41
|
+
* PageElement.located(By.css('#shopping-list')).describedAs('shopping list')
|
|
42
|
+
*
|
|
43
|
+
* actorCalled('Lisa')
|
|
44
|
+
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
|
|
45
|
+
* .attemptsTo(
|
|
46
|
+
* Ensure.that(
|
|
47
|
+
* Attribute.called('id').of(shoppingList()).toLocaleUpperCase(),
|
|
48
|
+
* equals('SHOPPING-LIST')
|
|
49
|
+
* ),
|
|
30
50
|
* )
|
|
31
51
|
*
|
|
32
|
-
* @example <caption>Find
|
|
52
|
+
* @example <caption>Find PageElements with a given attribute</caption>
|
|
33
53
|
* import { actorCalled } from '@serenity-js/core';
|
|
34
54
|
* import { Ensure, includes } from '@serenity-js/assertions';
|
|
35
|
-
* import { Attribute,
|
|
55
|
+
* import { Attribute, By, PageElements } from '@serenity-js/web';
|
|
56
|
+
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
|
|
36
57
|
*
|
|
37
58
|
* class ShoppingList {
|
|
38
59
|
* static items = () =>
|
|
39
|
-
*
|
|
40
|
-
* .
|
|
60
|
+
* PageElements.located(By.css('#shopping-list li'))
|
|
61
|
+
* .describedAs('items');
|
|
41
62
|
*
|
|
42
63
|
* static outstandingItems = () =>
|
|
43
|
-
* ShoppingList.items
|
|
44
|
-
* .where(Attribute.called('data-state'), includes('buy'))
|
|
64
|
+
* ShoppingList.items()
|
|
65
|
+
* .where(Attribute.called('data-state'), includes('buy'));
|
|
45
66
|
* }
|
|
46
67
|
*
|
|
47
68
|
* actorCalled('Lisa')
|
|
48
|
-
* .whoCan(
|
|
69
|
+
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
|
|
49
70
|
* .attemptsTo(
|
|
50
71
|
* Ensure.that(
|
|
51
72
|
* Text.ofAll(ShoppingList.outstandingItems()),
|
|
@@ -53,13 +74,18 @@ import { ElementQuestion } from './ElementQuestion';
|
|
|
53
74
|
* ),
|
|
54
75
|
* )
|
|
55
76
|
*
|
|
56
|
-
* @extends {
|
|
77
|
+
* @extends {@serenity-js/core/lib/screenplay~Question}
|
|
57
78
|
* @implements {@serenity-js/core/lib/screenplay/questions~MetaQuestion}
|
|
58
79
|
*/
|
|
59
80
|
export class Attribute
|
|
60
|
-
extends
|
|
81
|
+
extends Question<Promise<string>>
|
|
61
82
|
implements MetaQuestion<Answerable<PageElement>, Promise<string>>
|
|
62
83
|
{
|
|
84
|
+
/**
|
|
85
|
+
* @private
|
|
86
|
+
*/
|
|
87
|
+
private subject: string;
|
|
88
|
+
|
|
63
89
|
/**
|
|
64
90
|
* @param {Answerable<string>} name
|
|
65
91
|
* @returns {Attribute}
|
|
@@ -72,41 +98,66 @@ export class Attribute
|
|
|
72
98
|
* @param {Answerable<string>} name
|
|
73
99
|
* @param {@serenity-js/core/lib/screenplay~Answerable<Element>} [element]
|
|
74
100
|
*/
|
|
75
|
-
constructor(
|
|
101
|
+
protected constructor(
|
|
76
102
|
private readonly name: Answerable<string>,
|
|
77
103
|
private readonly element?: Answerable<PageElement>,
|
|
78
104
|
) {
|
|
79
|
-
super(
|
|
105
|
+
super();
|
|
106
|
+
this.subject = element
|
|
107
|
+
? d`${ name } attribute of ${ element }`
|
|
108
|
+
: d`${ name } attribute`
|
|
80
109
|
}
|
|
81
110
|
|
|
82
111
|
/**
|
|
83
112
|
* @desc
|
|
84
|
-
* Resolves to the value of
|
|
85
|
-
* located
|
|
113
|
+
* Resolves to the value of an HTML attribute of the `target` element,
|
|
114
|
+
* located within the `parent` element.
|
|
86
115
|
*
|
|
87
|
-
* @param {Answerable<PageElement>} parent
|
|
88
|
-
* @returns {
|
|
116
|
+
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} parent
|
|
117
|
+
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string>}
|
|
89
118
|
*
|
|
90
|
-
* @see {@link Target.all}
|
|
91
119
|
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
|
|
92
120
|
*/
|
|
93
|
-
of(parent: Answerable<PageElement>):
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
121
|
+
of(parent: Answerable<PageElement>): QuestionAdapter<string> & MetaQuestion<Answerable<PageElement>, Promise<string>> {
|
|
122
|
+
return Question.createAdapter(
|
|
123
|
+
new Attribute(
|
|
124
|
+
this.name,
|
|
125
|
+
this.element
|
|
126
|
+
? PageElement.of(this.element, parent)
|
|
127
|
+
: parent
|
|
128
|
+
)
|
|
129
|
+
) as QuestionAdapter<string> & MetaQuestion<Answerable<PageElement>, Promise<string>>;
|
|
100
130
|
}
|
|
101
131
|
|
|
102
132
|
async answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<string> {
|
|
133
|
+
const name = await actor.answer(this.name);
|
|
134
|
+
|
|
103
135
|
if (! this.element) {
|
|
104
|
-
throw new LogicError(`
|
|
136
|
+
throw new LogicError(d`Couldn't read attribute ${ name } of an unspecified page element.`);
|
|
105
137
|
}
|
|
106
138
|
|
|
107
139
|
const element = await actor.answer(this.element);
|
|
108
|
-
const name = await actor.answer(this.name);
|
|
109
140
|
|
|
110
141
|
return element.attribute(name);
|
|
111
142
|
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @desc
|
|
146
|
+
* Changes the description of this question's subject.
|
|
147
|
+
*
|
|
148
|
+
* @param {string} subject
|
|
149
|
+
* @returns {Question<T>}
|
|
150
|
+
*/
|
|
151
|
+
describedAs(subject: string): this {
|
|
152
|
+
this.subject = subject;
|
|
153
|
+
return this;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @returns {string}
|
|
158
|
+
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
|
|
159
|
+
*/
|
|
160
|
+
toString(): string {
|
|
161
|
+
return this.subject;
|
|
162
|
+
}
|
|
112
163
|
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { Answerable, AnswersQuestions, MetaQuestion, Question, UsesAbilities } from '@serenity-js/core';
|
|
2
|
-
import { formatted } from '@serenity-js/core/lib/io';
|
|
1
|
+
import { Answerable, AnswersQuestions, d, MetaQuestion, Question, QuestionAdapter, UsesAbilities } from '@serenity-js/core';
|
|
3
2
|
|
|
4
3
|
import { PageElement } from '../models';
|
|
5
|
-
import { ElementQuestion } from './ElementQuestion';
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* @desc
|
|
9
7
|
* Resolves to an array of [CSS classes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#attr-class)
|
|
10
|
-
* of a given {@link
|
|
8
|
+
* of a given {@link PageElement}.
|
|
11
9
|
*
|
|
12
10
|
* @example <caption>Example widget</caption>
|
|
13
11
|
* <ul id="shopping-list" class="active favourite">
|
|
@@ -16,37 +14,64 @@ import { ElementQuestion } from './ElementQuestion';
|
|
|
16
14
|
* <li class="buy">Chocolate<li>
|
|
17
15
|
* </ul>
|
|
18
16
|
*
|
|
19
|
-
* @example <caption>Retrieve CSS classes of a given
|
|
17
|
+
* @example <caption>Retrieve CSS classes of a given PageElement</caption>
|
|
20
18
|
* import { actorCalled } from '@serenity-js/core';
|
|
21
19
|
* import { Ensure, equals } from '@serenity-js/assertions';
|
|
22
|
-
* import {
|
|
20
|
+
* import { By, CssClasses, PageElement } from '@serenity-js/web';
|
|
21
|
+
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
|
|
23
22
|
*
|
|
24
23
|
* const shoppingList = () =>
|
|
25
|
-
*
|
|
24
|
+
* PageElement.located(By.css('#shopping-list')).describedAs('shopping list')
|
|
26
25
|
*
|
|
27
26
|
* actorCalled('Lisa')
|
|
28
|
-
* .whoCan(
|
|
27
|
+
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
|
|
29
28
|
* .attemptsTo(
|
|
30
|
-
* Ensure.that(
|
|
29
|
+
* Ensure.that(
|
|
30
|
+
* CssClasses.of(shoppingList()),
|
|
31
|
+
* equals([ 'active', 'favourite' ])
|
|
32
|
+
* ),
|
|
33
|
+
* )
|
|
34
|
+
*
|
|
35
|
+
* @example <caption>Using CssClasses as QuestionAdapter</caption>
|
|
36
|
+
* import { actorCalled } from '@serenity-js/core';
|
|
37
|
+
* import { Ensure, equals } from '@serenity-js/assertions';
|
|
38
|
+
* import { By, CssClasses, PageElement } from '@serenity-js/web';
|
|
39
|
+
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
|
|
40
|
+
*
|
|
41
|
+
* const shoppingList = () =>
|
|
42
|
+
* PageElement.located(By.css('#shopping-list')).describedAs('shopping list')
|
|
43
|
+
*
|
|
44
|
+
* actorCalled('Lisa')
|
|
45
|
+
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
|
|
46
|
+
* .attemptsTo(
|
|
47
|
+
* Ensure.that(
|
|
48
|
+
* CssClasses.of(shoppingList()).length,
|
|
49
|
+
* equals(2)
|
|
50
|
+
* ),
|
|
51
|
+
* Ensure.that(
|
|
52
|
+
* CssClasses.of(shoppingList())[0],
|
|
53
|
+
* equals('active')
|
|
54
|
+
* ),
|
|
31
55
|
* )
|
|
32
56
|
*
|
|
33
|
-
* @example <caption>Find
|
|
57
|
+
* @example <caption>Find PageElements with a given class</caption>
|
|
34
58
|
* import { actorCalled } from '@serenity-js/core';
|
|
35
59
|
* import { Ensure, contain } from '@serenity-js/assertions';
|
|
36
|
-
* import {
|
|
60
|
+
* import { By, CssClasses, PageElement } from '@serenity-js/web';
|
|
61
|
+
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
|
|
37
62
|
*
|
|
38
63
|
* class ShoppingList {
|
|
39
64
|
* static items = () =>
|
|
40
|
-
*
|
|
41
|
-
* .
|
|
65
|
+
* PageElements.located(By.css('#shopping-list li'))
|
|
66
|
+
* .describedAs('items')
|
|
42
67
|
*
|
|
43
68
|
* static outstandingItems = () =>
|
|
44
|
-
* ShoppingList.items
|
|
69
|
+
* ShoppingList.items()
|
|
45
70
|
* .where(CssClasses, contain('buy'))
|
|
46
71
|
* }
|
|
47
72
|
*
|
|
48
73
|
* actorCalled('Lisa')
|
|
49
|
-
* .whoCan(
|
|
74
|
+
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
|
|
50
75
|
* .attemptsTo(
|
|
51
76
|
* Ensure.that(
|
|
52
77
|
* Text.ofAll(ShoppingList.outstandingItems()),
|
|
@@ -54,41 +79,48 @@ import { ElementQuestion } from './ElementQuestion';
|
|
|
54
79
|
* ),
|
|
55
80
|
* )
|
|
56
81
|
*
|
|
57
|
-
* @extends {
|
|
82
|
+
* @extends {@serenity-js/core/lib/screenplay~Question}
|
|
58
83
|
* @implements {@serenity-js/core/lib/screenplay/questions~MetaQuestion}
|
|
59
84
|
*/
|
|
60
85
|
export class CssClasses
|
|
61
|
-
extends
|
|
86
|
+
extends Question<Promise<string[]>>
|
|
62
87
|
implements MetaQuestion<Answerable<PageElement>, Promise<string[]>>
|
|
63
88
|
{
|
|
64
89
|
/**
|
|
65
|
-
* @
|
|
66
|
-
* @returns {CssClasses}
|
|
90
|
+
* @private
|
|
67
91
|
*/
|
|
68
|
-
|
|
69
|
-
|
|
92
|
+
private subject: string;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} pageElement
|
|
96
|
+
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string[]>}
|
|
97
|
+
*
|
|
98
|
+
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
|
|
99
|
+
*/
|
|
100
|
+
static of(pageElement: Answerable<PageElement>): QuestionAdapter<string[]> & MetaQuestion<Answerable<PageElement>, Promise<string[]>> {
|
|
101
|
+
return Question.createAdapter(new CssClasses(pageElement)) as QuestionAdapter<string[]> & MetaQuestion<Answerable<PageElement>, Promise<string[]>>;
|
|
70
102
|
}
|
|
71
103
|
|
|
72
104
|
/**
|
|
73
|
-
* @param {
|
|
105
|
+
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} pageElement
|
|
74
106
|
*/
|
|
75
|
-
constructor(private readonly
|
|
76
|
-
super(
|
|
107
|
+
protected constructor(private readonly pageElement: Answerable<PageElement>) {
|
|
108
|
+
super();
|
|
109
|
+
this.subject = d`CSS classes of ${ pageElement}`;
|
|
77
110
|
}
|
|
78
111
|
|
|
79
112
|
/**
|
|
80
113
|
* @desc
|
|
81
|
-
* Resolves to an array of CSS classes of the `
|
|
82
|
-
* located
|
|
114
|
+
* Resolves to an array of CSS classes of the `pageElement`,
|
|
115
|
+
* located within the `parent` element.
|
|
83
116
|
*
|
|
84
|
-
* @param {@serenity-js/core/lib/screenplay~Answerable<
|
|
117
|
+
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} parent
|
|
85
118
|
* @returns {Question<Promise<string[]>>}
|
|
86
119
|
*
|
|
87
|
-
* @see {@link Target.all}
|
|
88
120
|
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
|
|
89
121
|
*/
|
|
90
122
|
of(parent: Answerable<PageElement>): Question<Promise<string[]>> {
|
|
91
|
-
return new CssClasses(PageElement.of(this.
|
|
123
|
+
return new CssClasses(PageElement.of(this.pageElement, parent));
|
|
92
124
|
}
|
|
93
125
|
|
|
94
126
|
/**
|
|
@@ -104,7 +136,7 @@ export class CssClasses
|
|
|
104
136
|
* @see {@link @serenity-js/core/lib/screenplay/actor~UsesAbilities}
|
|
105
137
|
*/
|
|
106
138
|
async answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<string[]> {
|
|
107
|
-
const element = await
|
|
139
|
+
const element = await actor.answer(this.pageElement);
|
|
108
140
|
|
|
109
141
|
return element.attribute('class')
|
|
110
142
|
.then(attribute => attribute ?? '')
|
|
@@ -115,4 +147,24 @@ export class CssClasses
|
|
|
115
147
|
.filter(cssClass => !! cssClass),
|
|
116
148
|
);
|
|
117
149
|
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @desc
|
|
153
|
+
* Changes the description of this question's subject.
|
|
154
|
+
*
|
|
155
|
+
* @param {string} subject
|
|
156
|
+
* @returns {Question<T>}
|
|
157
|
+
*/
|
|
158
|
+
describedAs(subject: string): this {
|
|
159
|
+
this.subject = subject;
|
|
160
|
+
return this;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @returns {string}
|
|
165
|
+
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
|
|
166
|
+
*/
|
|
167
|
+
toString(): string {
|
|
168
|
+
return this.subject;
|
|
169
|
+
}
|
|
118
170
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Answerable, Question } from '@serenity-js/core';
|
|
1
|
+
import { Answerable, List, Question } from '@serenity-js/core';
|
|
2
2
|
import { formatted } from '@serenity-js/core/lib/io';
|
|
3
3
|
|
|
4
4
|
import { By, PageElement, PageElements } from '../models';
|
|
@@ -99,15 +99,15 @@ export class Selected {
|
|
|
99
99
|
* @param {Answerable<PageElement>} pageElement
|
|
100
100
|
* A {@link Target} identifying the `<select>` element of interest
|
|
101
101
|
*
|
|
102
|
-
* @returns {
|
|
102
|
+
* @returns {@serenity-js/core/lib/screenplay/questions~List<string>}
|
|
103
103
|
*
|
|
104
104
|
* @see {@link Select.values}
|
|
105
105
|
*/
|
|
106
|
-
static valuesOf(pageElement: Answerable<PageElement>):
|
|
106
|
+
static valuesOf(pageElement: Answerable<PageElement>): List<string> {
|
|
107
107
|
return PageElements.located(By.css('option:checked'))
|
|
108
108
|
.of(pageElement)
|
|
109
109
|
.eachMappedTo(Value)
|
|
110
|
-
.describedAs(formatted `values selected in ${ pageElement }`)
|
|
110
|
+
.describedAs(formatted `values selected in ${ pageElement }`);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/**
|
|
@@ -201,14 +201,14 @@ export class Selected {
|
|
|
201
201
|
* @param {Answerable<PageElement>} pageElement
|
|
202
202
|
* A {@link Target} identifying the `<select>` element of interest
|
|
203
203
|
*
|
|
204
|
-
* @returns {
|
|
204
|
+
* @returns {@serenity-js/core/lib/screenplay/questions~List<string>}
|
|
205
205
|
*
|
|
206
206
|
* @see {@link Select.options}
|
|
207
207
|
*/
|
|
208
|
-
static optionsIn(pageElement: Answerable<PageElement>):
|
|
208
|
+
static optionsIn(pageElement: Answerable<PageElement>): List<string> {
|
|
209
209
|
return PageElements.located(By.css('option:checked'))
|
|
210
210
|
.of(pageElement)
|
|
211
211
|
.eachMappedTo(Text)
|
|
212
|
-
.describedAs(formatted `options selected in ${ pageElement }`)
|
|
212
|
+
.describedAs(formatted `options selected in ${ pageElement }`);
|
|
213
213
|
}
|
|
214
214
|
}
|