@serenity-js/web 3.9.1 → 3.10.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.
Files changed (42) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/lib/screenplay/models/Page.d.ts.map +1 -1
  3. package/lib/screenplay/models/Page.js.map +1 -1
  4. package/lib/screenplay/models/PageElement.d.ts +3 -3
  5. package/lib/screenplay/models/PageElement.d.ts.map +1 -1
  6. package/lib/screenplay/models/PageElement.js.map +1 -1
  7. package/lib/screenplay/models/PageElements.d.ts +6 -16
  8. package/lib/screenplay/models/PageElements.d.ts.map +1 -1
  9. package/lib/screenplay/models/PageElements.js +5 -60
  10. package/lib/screenplay/models/PageElements.js.map +1 -1
  11. package/lib/screenplay/models/PageElementsLocator.d.ts +19 -0
  12. package/lib/screenplay/models/PageElementsLocator.d.ts.map +1 -0
  13. package/lib/screenplay/models/PageElementsLocator.js +41 -0
  14. package/lib/screenplay/models/PageElementsLocator.js.map +1 -0
  15. package/lib/screenplay/models/index.d.ts +1 -0
  16. package/lib/screenplay/models/index.d.ts.map +1 -1
  17. package/lib/screenplay/models/index.js +1 -0
  18. package/lib/screenplay/models/index.js.map +1 -1
  19. package/lib/screenplay/questions/Attribute.d.ts +1 -1
  20. package/lib/screenplay/questions/Attribute.d.ts.map +1 -1
  21. package/lib/screenplay/questions/CssClasses.d.ts +2 -31
  22. package/lib/screenplay/questions/CssClasses.d.ts.map +1 -1
  23. package/lib/screenplay/questions/CssClasses.js +11 -48
  24. package/lib/screenplay/questions/CssClasses.js.map +1 -1
  25. package/lib/screenplay/questions/Text.d.ts +2 -1
  26. package/lib/screenplay/questions/Text.d.ts.map +1 -1
  27. package/lib/screenplay/questions/Text.js +11 -71
  28. package/lib/screenplay/questions/Text.js.map +1 -1
  29. package/lib/screenplay/questions/Value.d.ts +2 -30
  30. package/lib/screenplay/questions/Value.d.ts.map +1 -1
  31. package/lib/screenplay/questions/Value.js +5 -41
  32. package/lib/screenplay/questions/Value.js.map +1 -1
  33. package/package.json +5 -5
  34. package/src/screenplay/models/Page.ts +1 -0
  35. package/src/screenplay/models/PageElement.ts +5 -5
  36. package/src/screenplay/models/PageElements.ts +8 -91
  37. package/src/screenplay/models/PageElementsLocator.ts +57 -0
  38. package/src/screenplay/models/index.ts +1 -0
  39. package/src/screenplay/questions/Attribute.ts +1 -1
  40. package/src/screenplay/questions/CssClasses.ts +17 -58
  41. package/src/screenplay/questions/Text.ts +22 -100
  42. package/src/screenplay/questions/Value.ts +10 -51
@@ -1 +1 @@
1
- {"version":3,"file":"Text.js","sourceRoot":"","sources":["../../../src/screenplay/questions/Text.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Text.js","sourceRoot":"","sources":["../../../src/screenplay/questions/Text.ts"],"names":[],"mappings":";;;AACA,4CAAgD;AAChD,iDAAoD;AAGpD,sCAAwC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFG;AACH,MAAa,IAAI;IAEb;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,CAAC,WAAuD;QAC7D,OAAO,eAAQ,CAAC,KAAK,CAAC,IAAA,QAAC,EAAA,eAAgB,WAAY,EAAE,EACjD,KAAK,EAAC,KAAK,EAAC,EAAE;YACV,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YAEhD,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;QAC1B,CAAC,EACD,CAAC,MAA+B,EAAE,EAAE,CAChC,IAAI,CAAC,EAAE,CAAC,oBAAW,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CACnD,CAAC;IACN,CAAC;IAcD,MAAM,CAAC,KAAK,CAAC,YAAsD;QAC/D,IAAI,eAAQ,CAAC,eAAe,CAAC,YAAY,CAAC,EAAE;YACxC,OAAO,eAAQ,CAAC,KAAK,CAAC,IAAA,QAAC,EAAA,eAAgB,YAAa,EAAE,EAClD,cAAc,CAAC,YAAY,CAAC,EAC5B,CAAC,MAA+B,EAAE,EAAE,CAChC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAC1C,CAAC;SACL;QAED,OAAO,eAAQ,CAAC,KAAK,CAAC,IAAA,QAAC,EAAA,eAAgB,YAAa,EAAE,EAAE,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;IAC1F,CAAC;CACJ;AA/CD,oBA+CC;AAED,SAAS,cAAc,CAAC,YAAuC;IAC3D,OAAO,KAAK,EAAE,KAAuB,EAAE,EAAE;QACrC,MAAM,QAAQ,GAAkB,MAAM,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QACjE,OAAO,IAAA,aAAQ,EAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC,CAAA;AACL,CAAC"}
@@ -1,5 +1,4 @@
1
- import type { AnswersQuestions, MetaQuestion, MetaQuestionAdapter, QuestionAdapter, UsesAbilities } from '@serenity-js/core';
2
- import { Question } from '@serenity-js/core';
1
+ import type { MetaQuestionAdapter, QuestionAdapter } from '@serenity-js/core';
3
2
  import { PageElement } from '../models';
4
3
  /**
5
4
  * Uses the {@apilink Actor|actor's} {@apilink Ability|ability} to {@apilink BrowseTheWeb} to retrieve
@@ -55,9 +54,7 @@ import { PageElement } from '../models';
55
54
  *
56
55
  * @group Questions
57
56
  */
58
- export declare class Value extends Question<Promise<string>> implements MetaQuestion<PageElement, string> {
59
- private readonly element;
60
- private subject;
57
+ export declare class Value {
61
58
  /**
62
59
  * Instantiates a {@apilink Question} that uses
63
60
  * the {@apilink Actor|actor's} {@apilink Ability|ability} to {@apilink BrowseTheWeb} to retrieve
@@ -69,30 +66,5 @@ export declare class Value extends Question<Promise<string>> implements MetaQues
69
66
  * @param pageElement
70
67
  */
71
68
  static of(pageElement: QuestionAdapter<PageElement> | PageElement): MetaQuestionAdapter<PageElement, string>;
72
- protected constructor(element: QuestionAdapter<PageElement> | PageElement);
73
- /**
74
- * Instantiates a {@apilink Question} that uses
75
- * the {@apilink Actor|actor's} {@apilink Ability|ability} to {@apilink BrowseTheWeb} to retrieve
76
- * the `value` attribute of a given {@apilink PageElement}
77
- * located within the `parent` element.
78
- *
79
- * #### Learn more
80
- * - {@apilink MetaQuestion}
81
- *
82
- * @param parent
83
- */
84
- of(parent: QuestionAdapter<PageElement> | PageElement): Question<Promise<string>>;
85
- /**
86
- * @inheritDoc
87
- */
88
- answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<string>;
89
- /**
90
- * @inheritDoc
91
- */
92
- describedAs(subject: string): this;
93
- /**
94
- * @inheritDoc
95
- */
96
- toString(): string;
97
69
  }
98
70
  //# sourceMappingURL=Value.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Value.d.ts","sourceRoot":"","sources":["../../../src/screenplay/questions/Value.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,mBAAmB,EAAC,eAAe,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC5H,OAAO,EAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,qBAAa,KACT,SAAQ,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAChC,YAAW,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC;IAkBtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhB9C,OAAO,CAAC,OAAO,CAAS;IAExB;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,eAAe,CAAC,WAAW,CAAC,GAAG,WAAW,GAAG,mBAAmB,CAAC,WAAW,EAAE,MAAM,CAAC;IAI5G,SAAS,aAA8B,OAAO,EAAE,eAAe,CAAC,WAAW,CAAC,GAAG,WAAW;IAK1F;;;;;;;;;;OAUG;IACH,EAAE,CAAC,MAAM,EAAE,eAAe,CAAC,WAAW,CAAC,GAAG,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAIjF;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,gBAAgB,GAAG,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAM1E;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAKlC;;OAEG;IACH,QAAQ,IAAI,MAAM;CAGrB"}
1
+ {"version":3,"file":"Value.d.ts","sourceRoot":"","sources":["../../../src/screenplay/questions/Value.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,mBAAmB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzF,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,qBAAa,KAAK;IAEd;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,eAAe,CAAC,WAAW,CAAC,GAAG,WAAW,GAAG,mBAAmB,CAAC,WAAW,EAAE,MAAM,CAAC;CAU/G"}
@@ -57,7 +57,7 @@ const models_1 = require("../models");
57
57
  *
58
58
  * @group Questions
59
59
  */
60
- class Value extends core_1.Question {
60
+ class Value {
61
61
  /**
62
62
  * Instantiates a {@apilink Question} that uses
63
63
  * the {@apilink Actor|actor's} {@apilink Ability|ability} to {@apilink BrowseTheWeb} to retrieve
@@ -69,46 +69,10 @@ class Value extends core_1.Question {
69
69
  * @param pageElement
70
70
  */
71
71
  static of(pageElement) {
72
- return core_1.Question.createAdapter(new Value(pageElement));
73
- }
74
- constructor(element) {
75
- super();
76
- this.element = element;
77
- this.subject = (0, core_1.d) `the value of ${element}`;
78
- }
79
- /**
80
- * Instantiates a {@apilink Question} that uses
81
- * the {@apilink Actor|actor's} {@apilink Ability|ability} to {@apilink BrowseTheWeb} to retrieve
82
- * the `value` attribute of a given {@apilink PageElement}
83
- * located within the `parent` element.
84
- *
85
- * #### Learn more
86
- * - {@apilink MetaQuestion}
87
- *
88
- * @param parent
89
- */
90
- of(parent) {
91
- return new Value(models_1.PageElement.of(this.element, parent));
92
- }
93
- /**
94
- * @inheritDoc
95
- */
96
- async answeredBy(actor) {
97
- const element = await actor.answer(this.element);
98
- return element.value();
99
- }
100
- /**
101
- * @inheritDoc
102
- */
103
- describedAs(subject) {
104
- this.subject = subject;
105
- return this;
106
- }
107
- /**
108
- * @inheritDoc
109
- */
110
- toString() {
111
- return this.subject;
72
+ return core_1.Question.about((0, core_1.d) `the value of ${pageElement}`, async (actor) => {
73
+ const element = await actor.answer(pageElement);
74
+ return element.value();
75
+ }, (parent) => Value.of(models_1.PageElement.of(pageElement, parent)));
112
76
  }
113
77
  }
114
78
  exports.Value = Value;
@@ -1 +1 @@
1
- {"version":3,"file":"Value.js","sourceRoot":"","sources":["../../../src/screenplay/questions/Value.ts"],"names":[],"mappings":";;;AACA,4CAAgD;AAEhD,sCAAwC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,MAAa,KACT,SAAQ,eAAyB;IAKjC;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,CAAC,WAAuD;QAC7D,OAAO,eAAQ,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,WAAW,CAAC,CAA6C,CAAC;IACtG,CAAC;IAED,YAAuC,OAAmD;QACtF,KAAK,EAAE,CAAC;QAD2B,YAAO,GAAP,OAAO,CAA4C;QAEtF,IAAI,CAAC,OAAO,GAAG,IAAA,QAAC,EAAA,gBAAiB,OAAQ,EAAE,CAAC;IAChD,CAAC;IAED;;;;;;;;;;OAUG;IACH,EAAE,CAAC,MAAkD;QACjD,OAAO,IAAI,KAAK,CAAC,oBAAW,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,KAAuC;QACpD,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEjD,OAAO,OAAO,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,OAAe;QACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,QAAQ;QACJ,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;CACJ;AA/DD,sBA+DC"}
1
+ {"version":3,"file":"Value.js","sourceRoot":"","sources":["../../../src/screenplay/questions/Value.ts"],"names":[],"mappings":";;;AACA,4CAAgD;AAEhD,sCAAwC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,MAAa,KAAK;IAEd;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,CAAC,WAAuD;QAC7D,OAAO,eAAQ,CAAC,KAAK,CAAC,IAAA,QAAC,EAAA,gBAAiB,WAAY,EAAE,EAClD,KAAK,EAAC,KAAK,EAAC,EAAE;YACV,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YAChD,OAAO,OAAO,CAAC,KAAK,EAAE,CAAC;QAC3B,CAAC,EACD,CAAC,MAA+B,EAAE,EAAE,CAChC,KAAK,CAAC,EAAE,CAAC,oBAAW,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CACpD,CAAC;IACN,CAAC;CACJ;AAtBD,sBAsBC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serenity-js/web",
3
- "version": "3.9.1",
3
+ "version": "3.10.1",
4
4
  "description": "Serenity/JS Screenplay Pattern APIs for the Web",
5
5
  "author": {
6
6
  "name": "Jan Molak",
@@ -44,8 +44,8 @@
44
44
  "node": "^16.13 || ^18.12 || ^20"
45
45
  },
46
46
  "dependencies": {
47
- "@serenity-js/assertions": "3.9.1",
48
- "@serenity-js/core": "3.9.1",
47
+ "@serenity-js/assertions": "3.10.1",
48
+ "@serenity-js/core": "3.10.1",
49
49
  "tiny-types": "^1.20.0"
50
50
  },
51
51
  "devDependencies": {
@@ -56,7 +56,7 @@
56
56
  "mocha": "^10.2.0",
57
57
  "mocha-multi": "^1.1.7",
58
58
  "ts-node": "^10.9.1",
59
- "typescript": "^5.1.6"
59
+ "typescript": "5.1.6"
60
60
  },
61
- "gitHead": "237e0291a7eca71e5f5c0777e1f9d637e8622113"
61
+ "gitHead": "7ab90f30b17ed745cad713363a27edfd7cdce08a"
62
62
  }
@@ -276,6 +276,7 @@ export abstract class Page<Native_Element_Type = any> implements Optional, Switc
276
276
  *
277
277
  * @param selector
278
278
  */
279
+ // abstract locateAll(selector: Selector): PageElements<Native_Element_Type>;
279
280
  abstract locateAll(selector: Selector): PageElements<Native_Element_Type>;
280
281
 
281
282
  /**
@@ -1,4 +1,4 @@
1
- import type { Answerable, MetaQuestion, MetaQuestionAdapter,Optional, QuestionAdapter } from '@serenity-js/core';
1
+ import type { Answerable, MetaQuestionAdapter, Optional } from '@serenity-js/core';
2
2
  import { d, Question } from '@serenity-js/core';
3
3
  import { ensure, isDefined } from 'tiny-types';
4
4
 
@@ -22,21 +22,21 @@ import type { SwitchableOrigin } from './SwitchableOrigin';
22
22
  */
23
23
  export abstract class PageElement<Native_Element_Type = any> implements Optional, Switchable {
24
24
 
25
- static from<NET>(nativeElement: NET): QuestionAdapter<PageElement<NET>> & MetaQuestion<PageElement, PageElement> {
25
+ static from<NET>(nativeElement: NET): MetaQuestionAdapter<PageElement<NET>, PageElement<NET>> {
26
26
  return Question.about(`native page element`, async actor => {
27
27
  const currentPage = await BrowseTheWeb.as<BrowseTheWeb<NET>>(actor).currentPage();
28
28
 
29
29
  return currentPage.createPageElement(nativeElement);
30
- }) as QuestionAdapter<PageElement<NET>> & MetaQuestion<PageElement, PageElement>;
30
+ });
31
31
  }
32
32
 
33
- static located<NET>(selector: Answerable<Selector>): QuestionAdapter<PageElement<NET>> & MetaQuestion<PageElement, PageElement> {
33
+ static located<NET>(selector: Answerable<Selector>): MetaQuestionAdapter<PageElement<NET>, PageElement<NET>> {
34
34
  return Question.about(d`page element located ${ selector }`, async actor => {
35
35
  const bySelector = await actor.answer(selector);
36
36
  const currentPage = await BrowseTheWeb.as<BrowseTheWeb<NET>>(actor).currentPage();
37
37
 
38
38
  return currentPage.locate(bySelector);
39
- }) as QuestionAdapter<PageElement<NET>> & MetaQuestion<PageElement, PageElement>;
39
+ });
40
40
  }
41
41
 
42
42
  static of<NET>(
@@ -1,9 +1,8 @@
1
- import type { Answerable, MetaQuestion, MetaQuestionAdapter } from '@serenity-js/core';
2
- import { List, Question } from '@serenity-js/core';
1
+ import type { Answerable } from '@serenity-js/core';
2
+ import { MetaList } from '@serenity-js/core';
3
3
 
4
- import { BrowseTheWeb } from '../abilities';
5
- import type { Locator } from './Locator';
6
4
  import type { PageElement } from './PageElement';
5
+ import { PageElementsLocator } from './PageElementsLocator';
7
6
  import type { Selector } from './selectors';
8
7
 
9
8
  /**
@@ -13,98 +12,16 @@ import type { Selector } from './selectors';
13
12
  * ## Learn more
14
13
  *
15
14
  * - [Page Element Query Language](/handbook/web-testing/page-element-query-language)
15
+ * - {@apilink MetaList}
16
16
  * - {@apilink List}
17
- * - {@apilink MetaQuestion}
17
+ * - {@apilink ChainableMetaQuestion}
18
18
  *
19
19
  * @group Models
20
20
  */
21
21
  export class PageElements<Native_Element_Type = any>
22
- extends List<PageElement<Native_Element_Type>>
23
- implements MetaQuestion<PageElement<Native_Element_Type>, Promise<Array<PageElement<Native_Element_Type>>>>
22
+ extends MetaList<PageElement<Native_Element_Type>, PageElement<Native_Element_Type>>
24
23
  {
25
- static located<NET>(selector: Answerable<Selector>): PageElements<NET> {
26
- return new PageElements(relativeToDocumentRoot(selector));
24
+ static located<NET>(selector: Answerable<Selector>): PageElements {
25
+ return new PageElements(PageElementsLocator.fromDocumentRoot<NET>(selector));
27
26
  }
28
-
29
- /**
30
- * @param locator
31
- */
32
- constructor(protected readonly locator: Answerable<Locator<Native_Element_Type>>) {
33
- super(allElementsOf(locator));
34
- }
35
-
36
- of(parent: Answerable<PageElement<Native_Element_Type>>): PageElements<Native_Element_Type> {
37
- return new PageElements<Native_Element_Type>(relativeToParent(this.locator, parent))
38
- .describedAs(`${ this.toString() } of ${ parent }`);
39
- }
40
-
41
- override count(): MetaQuestionAdapter<PageElement<Native_Element_Type>, number> {
42
- const count = super.count();
43
- return Question.about(
44
- count.toString(),
45
- actor => actor.answer(count),
46
- (parent: Answerable<PageElement>) => this.of(parent).count(),
47
- );
48
- }
49
-
50
- override first(): MetaQuestionAdapter<PageElement<Native_Element_Type>, PageElement<Native_Element_Type>> {
51
- const first = super.first();
52
- return Question.about<PageElement, PageElement>(
53
- first.toString(),
54
- async actor => actor.answer(first),
55
- (parent: Answerable<PageElement>) => this.of(parent).first(),
56
- );
57
- }
58
-
59
- override last(): MetaQuestionAdapter<PageElement<Native_Element_Type>, PageElement<Native_Element_Type>> {
60
- const last = super.last();
61
- return Question.about<PageElement, PageElement>(
62
- last.toString(),
63
- async actor => actor.answer(last),
64
- (parent: Answerable<PageElement>) => this.of(parent).last(),
65
- );
66
- }
67
-
68
- override nth(index: number): MetaQuestionAdapter<PageElement<Native_Element_Type>, PageElement<Native_Element_Type>> {
69
- const nth = super.nth(index);
70
- return Question.about<PageElement, PageElement>(
71
- nth.toString(),
72
- async actor => actor.answer(nth),
73
- (parent: Answerable<PageElement>) => this.of(parent).nth(index),
74
- );
75
- }
76
- }
77
-
78
- /**
79
- * @package
80
- */
81
- function relativeToDocumentRoot<Native_Element_Type>(selector: Answerable<Selector>): Question<Promise<Locator<Native_Element_Type>>> {
82
- return Question.about(String(selector), async actor => {
83
- const bySelector = await actor.answer(selector);
84
- const currentPage = await BrowseTheWeb.as<BrowseTheWeb<Native_Element_Type>>(actor).currentPage();
85
-
86
- return currentPage.locate(bySelector).locator;
87
- });
88
- }
89
-
90
- /**
91
- * @package
92
- */
93
- function relativeToParent<Native_Element_Type>(relativeLocator: Answerable<Locator<Native_Element_Type>>, parent: Answerable<PageElement<Native_Element_Type>>): Question<Promise<Locator<Native_Element_Type>>> {
94
- return Question.about(`${ relativeLocator.toString() } of ${ parent }`, async actor => {
95
- const locator: Locator<Native_Element_Type> = await actor.answer(relativeLocator);
96
- const parentElement: PageElement<Native_Element_Type> = await actor.answer(parent);
97
-
98
- return locator.of(parentElement.locator);
99
- });
100
- }
101
-
102
- /**
103
- * @package
104
- */
105
- function allElementsOf<Native_Element_Type>(locator: Answerable<Locator<Native_Element_Type>>): Question<Promise<Array<PageElement<Native_Element_Type>>>> {
106
- return Question.about(`page elements located ${ String(locator) }`, async actor => {
107
- const resolved: Locator<Native_Element_Type> = await actor.answer(locator);
108
- return resolved.allElements();
109
- });
110
27
  }
@@ -0,0 +1,57 @@
1
+ import type { Answerable, AnswersQuestions, ChainableMetaQuestion, UsesAbilities } from '@serenity-js/core';
2
+ import { d, Question } from '@serenity-js/core';
3
+
4
+ import { BrowseTheWeb } from '../abilities';
5
+ import type { Locator } from './Locator';
6
+ import type { PageElement } from './PageElement';
7
+ import type { Selector } from './selectors';
8
+
9
+ /**
10
+ * @group Models
11
+ */
12
+ export class PageElementsLocator<Native_Element_Type = any>
13
+ extends Question<Promise<Array<PageElement<Native_Element_Type>>>>
14
+ implements ChainableMetaQuestion<PageElement<Native_Element_Type>, Question<Promise<Array<PageElement<Native_Element_Type>>>>>
15
+ {
16
+ static fromDocumentRoot<NET>(selector: Answerable<Selector>): PageElementsLocator<NET> {
17
+ return new PageElementsLocator(
18
+ Question.about(d`page elements located ${ selector }`, async actor => {
19
+ const bySelector = await actor.answer(selector);
20
+ const currentPage = await BrowseTheWeb.as<BrowseTheWeb<NET>>(actor).currentPage();
21
+
22
+ return currentPage.locate(bySelector).locator;
23
+ })
24
+ );
25
+ }
26
+
27
+ private subject?: string;
28
+
29
+ constructor(private readonly locator: Answerable<Locator<Native_Element_Type>>) {
30
+ super();
31
+ }
32
+
33
+ of(parent: Answerable<PageElement<Native_Element_Type>>): Question<Promise<Array<PageElement<Native_Element_Type>>>> & ChainableMetaQuestion<PageElement<Native_Element_Type>, Question<Promise<Array<PageElement<Native_Element_Type>>>>> {
34
+ return new PageElementsLocator(
35
+ Question.about(this.toString() + d` of ${ parent }`, async actor => {
36
+ const locator: Locator<Native_Element_Type> = await actor.answer(this.locator);
37
+ const parentElement: PageElement<Native_Element_Type> = await actor.answer(parent);
38
+
39
+ return locator.of(parentElement.locator);
40
+ })
41
+ );
42
+ }
43
+
44
+ async answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<Array<PageElement<Native_Element_Type>>> {
45
+ const resolved: Locator<Native_Element_Type> = await actor.answer(this.locator);
46
+ return resolved.allElements();
47
+ }
48
+
49
+ describedAs(subject: string): this {
50
+ this.subject = subject;
51
+ return this;
52
+ }
53
+
54
+ toString(): string {
55
+ return this.subject ?? d`${ this.locator }`;
56
+ }
57
+ }
@@ -8,6 +8,7 @@ export * from './Locator';
8
8
  export * from './Page';
9
9
  export * from './PageElement';
10
10
  export * from './PageElements';
11
+ export * from './PageElementsLocator';
11
12
  export * from './RootLocator';
12
13
  export * from './SelectOption';
13
14
  export * from './selectors';
@@ -97,7 +97,7 @@ import { PageElement } from '../models';
97
97
  */
98
98
  export class Attribute
99
99
  extends Question<Promise<string>>
100
- implements MetaQuestion<PageElement, string>
100
+ implements MetaQuestion<PageElement, Question<Promise<string>>>
101
101
  {
102
102
  private subject: string;
103
103
 
@@ -1,4 +1,4 @@
1
- import type { AnswersQuestions, MetaQuestion, MetaQuestionAdapter,QuestionAdapter, UsesAbilities } from '@serenity-js/core';
1
+ import type { Answerable,MetaQuestionAdapter, QuestionAdapter } from '@serenity-js/core';
2
2
  import { d, Question } from '@serenity-js/core';
3
3
 
4
4
  import { PageElement } from '../models';
@@ -96,11 +96,7 @@ import { PageElement } from '../models';
96
96
  *
97
97
  * @group Questions
98
98
  */
99
- export class CssClasses
100
- extends Question<Promise<string[]>>
101
- implements MetaQuestion<PageElement, string[]>
102
- {
103
- private subject: string;
99
+ export class CssClasses {
104
100
 
105
101
  /**
106
102
  * Instantiates a {@apilink Question} that uses
@@ -114,58 +110,21 @@ export class CssClasses
114
110
  * @param pageElement
115
111
  */
116
112
  static of(pageElement: QuestionAdapter<PageElement> | PageElement): MetaQuestionAdapter<PageElement, string[]> {
117
- return Question.createAdapter(new CssClasses(pageElement)) as MetaQuestionAdapter<PageElement, string[]>;
118
- }
119
-
120
- protected constructor(private readonly pageElement: QuestionAdapter<PageElement> | PageElement) {
121
- super();
122
- this.subject = d`CSS classes of ${ pageElement}`;
123
- }
113
+ return Question.about(d`CSS classes of ${ pageElement }`,
114
+ async actor => {
115
+ const element = await actor.answer(pageElement);
124
116
 
125
- /**
126
- * Instantiates a {@apilink Question} that uses
127
- * the {@apilink Actor|actor's} {@apilink Ability|ability} to {@apilink BrowseTheWeb} to retrieve
128
- * a list of [CSS classes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#attr-class)
129
- * of a given {@apilink PageElement},
130
- * located in a given `parent` element.
131
- *
132
- * #### Learn more
133
- * - {@apilink MetaQuestion}
134
- *
135
- * @param parent
136
- */
137
- of(parent: QuestionAdapter<PageElement> | PageElement): Question<Promise<string[]>> {
138
- return new CssClasses(PageElement.of(this.pageElement, parent));
139
- }
140
-
141
- /**
142
- * @inheritDoc
143
- */
144
- async answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<string[]> {
145
- const element = await actor.answer(this.pageElement);
146
-
147
- return element.attribute('class')
148
- .then(attribute => attribute ?? '')
149
- .then(attribute => attribute
150
- .replace(/\s+/, ' ')
151
- .trim()
152
- .split(' ')
153
- .filter(cssClass => !! cssClass),
154
- );
155
- }
156
-
157
- /**
158
- * @inheritDoc
159
- */
160
- describedAs(subject: string): this {
161
- this.subject = subject;
162
- return this;
163
- }
164
-
165
- /**
166
- * @inheritDoc
167
- */
168
- toString(): string {
169
- return this.subject;
117
+ return element.attribute('class')
118
+ .then(attribute => attribute ?? '')
119
+ .then(attribute => attribute
120
+ .replace(/\s+/, ' ')
121
+ .trim()
122
+ .split(' ')
123
+ .filter(cssClass => !! cssClass),
124
+ );
125
+ },
126
+ (parent: Answerable<PageElement>) =>
127
+ CssClasses.of(PageElement.of(pageElement, parent))
128
+ );
170
129
  }
171
130
  }
@@ -1,8 +1,9 @@
1
- import type { Answerable, AnswersQuestions, MetaQuestion, MetaQuestionAdapter,QuestionAdapter, UsesAbilities } from '@serenity-js/core';
1
+ import type { Answerable, AnswersQuestions, MetaQuestionAdapter, QuestionAdapter } from '@serenity-js/core';
2
2
  import { d, Question } from '@serenity-js/core';
3
3
  import { asyncMap } from '@serenity-js/core/lib/io';
4
4
 
5
- import { PageElement, PageElements } from '../models';
5
+ import type { PageElements } from '../models';
6
+ import { PageElement } from '../models';
6
7
 
7
8
  /**
8
9
  * Uses the {@apilink Actor|actor's} {@apilink Ability|ability} to {@apilink BrowseTheWeb} to retrieve
@@ -104,7 +105,15 @@ export class Text {
104
105
  * @param pageElement
105
106
  */
106
107
  static of(pageElement: QuestionAdapter<PageElement> | PageElement): MetaQuestionAdapter<PageElement, string> {
107
- return TextOfSingleElement.of(pageElement);
108
+ return Question.about(d`the text of ${ pageElement }`,
109
+ async actor => {
110
+ const element = await actor.answer(pageElement);
111
+
112
+ return element.text();
113
+ },
114
+ (parent: Answerable<PageElement>) =>
115
+ Text.of(PageElement.of(pageElement, parent))
116
+ );
108
117
  }
109
118
 
110
119
  /**
@@ -120,108 +129,21 @@ export class Text {
120
129
  static ofAll(pageElements: PageElements): MetaQuestionAdapter<PageElement, string[]>
121
130
  static ofAll(pageElements: Answerable<PageElement[]>): QuestionAdapter<string[]>
122
131
  static ofAll(pageElements: PageElements | Answerable<PageElement[]>): QuestionAdapter<string[]> {
123
- if (pageElements instanceof PageElements) {
124
- return TextOfMultipleElements.of(pageElements);
132
+ if (Question.isAMetaQuestion(pageElements)) {
133
+ return Question.about(d`the text of ${ pageElements }`,
134
+ textOfMultiple(pageElements),
135
+ (parent: Answerable<PageElement>) =>
136
+ Text.ofAll(pageElements.of(parent))
137
+ );
125
138
  }
126
139
 
127
- return Question.about(d`the text of ${ pageElements }`, async actor => {
128
- const elements: PageElement[] = await actor.answer(pageElements);
129
-
130
- return asyncMap(elements, element => element.text());
131
- });
132
- }
133
- }
134
-
135
- class TextOfSingleElement
136
- extends Question<Promise<string>>
137
- implements MetaQuestion<PageElement, string>
138
- {
139
- /**
140
- * @private
141
- */
142
- private subject: string;
143
-
144
- static of(element: QuestionAdapter<PageElement> | PageElement): MetaQuestionAdapter<PageElement, string> {
145
- return Question.createAdapter(new TextOfSingleElement(element)) as MetaQuestionAdapter<PageElement, string>;
146
- }
147
-
148
- protected constructor(private readonly element: QuestionAdapter<PageElement> | PageElement) {
149
- super();
150
- this.subject = d`the text of ${ element }`;
151
- }
152
-
153
- of(parent: QuestionAdapter<PageElement> | PageElement): Question<Promise<string>> {
154
- return new TextOfSingleElement(PageElement.of(this.element, parent));
155
- }
156
-
157
- /**
158
- * @inheritDoc
159
- */
160
- async answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<string> {
161
- const element = await actor.answer(this.element);
162
-
163
- return element.text();
164
- }
165
-
166
- /**
167
- * @inheritDoc
168
- */
169
- describedAs(subject: string): this {
170
- this.subject = subject;
171
- return this;
172
- }
173
-
174
- /**
175
- * @inheritDoc
176
- */
177
- toString(): string {
178
- return this.subject;
140
+ return Question.about(d`the text of ${ pageElements }`, textOfMultiple(pageElements));
179
141
  }
180
142
  }
181
143
 
182
- class TextOfMultipleElements
183
- extends Question<Promise<string[]>>
184
- implements MetaQuestion<PageElement, string[]>
185
- {
186
- /**
187
- * @private
188
- */
189
- private subject: string;
190
-
191
- static of(elements: PageElements): MetaQuestionAdapter<PageElement, string[]> {
192
- return Question.createAdapter(new TextOfMultipleElements(elements)) as MetaQuestionAdapter<PageElement, string[]>;
193
- }
194
-
195
- protected constructor(private readonly elements: PageElements) {
196
- super();
197
- this.subject = d`the text of ${ elements }`;
198
- }
199
-
200
- of(parent: Answerable<PageElement>): Question<Promise<string[]>> {
201
- return new TextOfMultipleElements(this.elements.of(parent));
202
- }
203
-
204
- /**
205
- * @inheritDoc
206
- */
207
- async answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<string[]> {
208
- const elements: PageElement[] = await actor.answer(this.elements);
209
-
144
+ function textOfMultiple(pageElements: Answerable<PageElement[]>) {
145
+ return async (actor: AnswersQuestions) => {
146
+ const elements: PageElement[] = await actor.answer(pageElements);
210
147
  return asyncMap(elements, element => element.text());
211
148
  }
212
-
213
- /**
214
- * @inheritDoc
215
- */
216
- describedAs(subject: string): this {
217
- this.subject = subject;
218
- return this;
219
- }
220
-
221
- /**
222
- * @inheritDoc
223
- */
224
- toString(): string {
225
- return this.subject;
226
- }
227
149
  }