@serenity-js/web 3.0.0-rc.4 → 3.0.0-rc.5

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 (61) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/lib/screenplay/abilities/BrowseTheWeb.d.ts +6 -6
  3. package/lib/screenplay/abilities/BrowseTheWeb.js +13 -0
  4. package/lib/screenplay/abilities/BrowseTheWeb.js.map +1 -1
  5. package/lib/screenplay/interactions/Press.js +0 -1
  6. package/lib/screenplay/interactions/Press.js.map +1 -1
  7. package/lib/screenplay/models/Locator.d.ts +14 -0
  8. package/lib/screenplay/models/Locator.js +19 -0
  9. package/lib/screenplay/models/Locator.js.map +1 -0
  10. package/lib/screenplay/models/PageElement.d.ts +5 -7
  11. package/lib/screenplay/models/PageElement.js +7 -13
  12. package/lib/screenplay/models/PageElement.js.map +1 -1
  13. package/lib/screenplay/models/PageElements.d.ts +7 -9
  14. package/lib/screenplay/models/PageElements.js +30 -28
  15. package/lib/screenplay/models/PageElements.js.map +1 -1
  16. package/lib/screenplay/models/index.d.ts +1 -2
  17. package/lib/screenplay/models/index.js +1 -2
  18. package/lib/screenplay/models/index.js.map +1 -1
  19. package/lib/screenplay/models/selectors/ByCss.d.ts +3 -1
  20. package/lib/screenplay/models/selectors/ByCss.js +4 -0
  21. package/lib/screenplay/models/selectors/ByCss.js.map +1 -1
  22. package/lib/screenplay/models/selectors/ByCssContainingText.d.ts +3 -2
  23. package/lib/screenplay/models/selectors/ByCssContainingText.js +3 -2
  24. package/lib/screenplay/models/selectors/ByCssContainingText.js.map +1 -1
  25. package/lib/screenplay/models/selectors/ById.d.ts +3 -1
  26. package/lib/screenplay/models/selectors/ById.js +4 -0
  27. package/lib/screenplay/models/selectors/ById.js.map +1 -1
  28. package/lib/screenplay/models/selectors/ByTagName.d.ts +3 -1
  29. package/lib/screenplay/models/selectors/ByTagName.js +4 -0
  30. package/lib/screenplay/models/selectors/ByTagName.js.map +1 -1
  31. package/lib/screenplay/models/selectors/ByXPath.d.ts +3 -1
  32. package/lib/screenplay/models/selectors/ByXPath.js +4 -0
  33. package/lib/screenplay/models/selectors/ByXPath.js.map +1 -1
  34. package/lib/screenplay/models/selectors/Selector.d.ts +1 -3
  35. package/lib/screenplay/models/selectors/Selector.js +1 -3
  36. package/lib/screenplay/models/selectors/Selector.js.map +1 -1
  37. package/lib/screenplay/questions/Text.d.ts +2 -1
  38. package/lib/screenplay/questions/Text.js +16 -10
  39. package/lib/screenplay/questions/Text.js.map +1 -1
  40. package/package.json +4 -4
  41. package/src/screenplay/abilities/BrowseTheWeb.ts +25 -8
  42. package/src/screenplay/interactions/Press.ts +0 -1
  43. package/src/screenplay/models/Locator.ts +25 -0
  44. package/src/screenplay/models/PageElement.ts +11 -20
  45. package/src/screenplay/models/PageElements.ts +33 -36
  46. package/src/screenplay/models/index.ts +1 -2
  47. package/src/screenplay/models/selectors/ByCss.ts +4 -1
  48. package/src/screenplay/models/selectors/ByCssContainingText.ts +3 -3
  49. package/src/screenplay/models/selectors/ById.ts +4 -1
  50. package/src/screenplay/models/selectors/ByTagName.ts +4 -1
  51. package/src/screenplay/models/selectors/ByXPath.ts +4 -1
  52. package/src/screenplay/models/selectors/Selector.ts +2 -4
  53. package/src/screenplay/questions/Text.ts +30 -4
  54. package/lib/screenplay/models/NativeElementLocator.d.ts +0 -5
  55. package/lib/screenplay/models/NativeElementLocator.js +0 -3
  56. package/lib/screenplay/models/NativeElementLocator.js.map +0 -1
  57. package/lib/screenplay/models/PassThroughNativeElementLocator.d.ts +0 -9
  58. package/lib/screenplay/models/PassThroughNativeElementLocator.js +0 -17
  59. package/lib/screenplay/models/PassThroughNativeElementLocator.js.map +0 -1
  60. package/src/screenplay/models/NativeElementLocator.ts +0 -6
  61. package/src/screenplay/models/PassThroughNativeElementLocator.ts +0 -18
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
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.0.0-rc.5](https://github.com/serenity-js/serenity-js/compare/v3.0.0-rc.4...v3.0.0-rc.5) (2022-01-07)
7
+
8
+
9
+ ### Features
10
+
11
+ * **web:** support for advanced PageElement locator patterns ([c1ff8b7](https://github.com/serenity-js/serenity-js/commit/c1ff8b7539ebc1da8f79ea2b6d17bb01c42f443d)), closes [#1084](https://github.com/serenity-js/serenity-js/issues/1084)
12
+
13
+
14
+
15
+
16
+
6
17
  # [3.0.0-rc.4](https://github.com/serenity-js/serenity-js/compare/v3.0.0-rc.3...v3.0.0-rc.4) (2021-12-30)
7
18
 
8
19
 
@@ -1,8 +1,9 @@
1
1
  import { Ability, Duration, UsesAbilities } from '@serenity-js/core';
2
2
  import { Key } from '../../input';
3
- import { Cookie, CookieData, ModalDialog, NativeElementLocator, Page, PageElement, Selector } from '../models';
3
+ import { Cookie, CookieData, Locator, ModalDialog, Page, PageElement, Selector } from '../models';
4
4
  import { BrowserCapabilities } from './BrowserCapabilities';
5
- export declare abstract class BrowseTheWeb<Native_Element_Type = any> implements Ability {
5
+ export declare abstract class BrowseTheWeb<Native_Element_Type = any, Native_Root_Element_Type = unknown> implements Ability {
6
+ protected locators: Map<new (...args: unknown[]) => Selector, (selector: Selector) => Locator<Native_Element_Type, Native_Root_Element_Type>>;
6
7
  /**
7
8
  * @desc
8
9
  * Used to access the Actor's ability to {@link BrowseTheWeb}
@@ -12,16 +13,15 @@ export declare abstract class BrowseTheWeb<Native_Element_Type = any> implements
12
13
  * @param {@serenity-js/core/lib/screenplay/actor~UsesAbilities} actor
13
14
  * @return {BrowseTheWeb}
14
15
  */
15
- static as(actor: UsesAbilities): BrowseTheWeb;
16
+ static as<NET = any, NRET = unknown>(actor: UsesAbilities): BrowseTheWeb<NET, NRET>;
17
+ constructor(locators: Map<new (...args: unknown[]) => Selector, (selector: Selector) => Locator<Native_Element_Type, Native_Root_Element_Type>>);
16
18
  abstract navigateTo(destination: string): Promise<void>;
17
19
  abstract navigateBack(): Promise<void>;
18
20
  abstract navigateForward(): Promise<void>;
19
21
  abstract reloadPage(): Promise<void>;
20
22
  abstract waitFor(duration: Duration): Promise<void>;
21
23
  abstract waitUntil(condition: () => boolean | Promise<boolean>, timeout: Duration): Promise<void>;
22
- abstract locate<T>(selector: Selector<T>, locator?: NativeElementLocator<Native_Element_Type>): PageElement<Native_Element_Type>;
23
- abstract locateAll<T>(selector: Selector<T>, locator?: NativeElementLocator<Native_Element_Type>): Promise<Array<PageElement<Native_Element_Type>>>;
24
- abstract nativeElementLocator(): NativeElementLocator<Native_Element_Type>;
24
+ locate(selector: Selector): Locator<Native_Element_Type, Native_Root_Element_Type>;
25
25
  abstract browserCapabilities(): Promise<BrowserCapabilities>;
26
26
  abstract sendKeys(keys: Array<Key | string>): Promise<void>;
27
27
  abstract executeScript<Result, InnerArguments extends any[]>(script: string | ((...parameters: InnerArguments) => Result), ...args: InnerArguments): Promise<Result>;
@@ -1,7 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BrowseTheWeb = void 0;
4
+ const core_1 = require("@serenity-js/core");
5
+ const f = (0, core_1.format)({ markQuestions: true });
4
6
  class BrowseTheWeb {
7
+ constructor(locators) {
8
+ this.locators = locators;
9
+ }
5
10
  /**
6
11
  * @desc
7
12
  * Used to access the Actor's ability to {@link BrowseTheWeb}
@@ -14,6 +19,14 @@ class BrowseTheWeb {
14
19
  static as(actor) {
15
20
  return actor.abilityTo(BrowseTheWeb);
16
21
  }
22
+ locate(selector) {
23
+ for (const [type, locatorFactory] of this.locators) {
24
+ if (selector instanceof type) {
25
+ return locatorFactory(selector);
26
+ }
27
+ }
28
+ throw new core_1.LogicError(f `${selector} is not supported by ${this.constructor.name}`);
29
+ }
17
30
  }
18
31
  exports.BrowseTheWeb = BrowseTheWeb;
19
32
  //# sourceMappingURL=BrowseTheWeb.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BrowseTheWeb.js","sourceRoot":"","sources":["../../../src/screenplay/abilities/BrowseTheWeb.ts"],"names":[],"mappings":";;;AAMA,MAAsB,YAAY;IAC9B;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,CAAC,KAAoB;QAC1B,OAAO,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACzC,CAAC;CA+DJ;AA3ED,oCA2EC"}
1
+ {"version":3,"file":"BrowseTheWeb.js","sourceRoot":"","sources":["../../../src/screenplay/abilities/BrowseTheWeb.ts"],"names":[],"mappings":";;;AAAA,4CAAyF;AAMzF,MAAM,CAAC,GAAG,IAAA,aAAM,EAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;AAE1C,MAAsB,YAAY;IAc9B,YACc,QAIT;QAJS,aAAQ,GAAR,QAAQ,CAIjB;IAEL,CAAC;IApBD;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,CAA4B,KAAoB;QACrD,OAAO,KAAK,CAAC,SAAS,CAAC,YAAY,CAA4B,CAAC;IACpE,CAAC;IAuBD,MAAM,CAAC,QAAkB;QACrB,KAAK,MAAM,CAAE,IAAI,EAAE,cAAc,CAAE,IAAI,IAAI,CAAC,QAAQ,EAAE;YAClD,IAAI,QAAQ,YAAY,IAAI,EAAE;gBAC1B,OAAO,cAAc,CAAC,QAAQ,CAAC,CAAC;aACnC;SACJ;QAED,MAAM,IAAI,iBAAU,CAAC,CAAC,CAAC,GAAI,QAAS,wBAAyB,IAAI,CAAC,WAAW,CAAC,IAAK,EAAE,CAAC,CAAC;IAC3F,CAAC;CA+CJ;AA1FD,oCA0FC"}
@@ -112,7 +112,6 @@ class PressKeyInField extends PageElementInteraction_1.PageElementInteraction {
112
112
  const field = await this.resolve(actor, this.field);
113
113
  const keys = await actor.answer(this.keys);
114
114
  // fix for protractor
115
- // todo: should this wait on focus to occur?
116
115
  await abilities_1.BrowseTheWeb.as(actor).executeScript(
117
116
  /* istanbul ignore next */
118
117
  function focus(element) {
@@ -1 +1 @@
1
- {"version":3,"file":"Press.js","sourceRoot":"","sources":["../../../src/screenplay/interactions/Press.ts"],"names":[],"mappings":";;;AAAA,4CAAiH;AACjH,iDAA+D;AAE/D,uCAAkC;AAClC,4CAA4C;AAE5C,qEAAkE;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAa,KAAM,SAAQ,+CAAsB;IAmB7C;;;OAGG;IACH,YACqB,IAAqC;QAEtD,KAAK,CAAC,IAAA,cAAS,EAAC,kBAAmB,IAAK,EAAE,CAAC,CAAC;QAF3B,SAAI,GAAJ,IAAI,CAAiC;IAG1D,CAAC;IAzBD;;;;;;;;OAQG;IACH,MAAM,CAAC,GAAG,CAAC,GAAG,IAAwD;QAClE,OAAO,IAAI,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,EAAE,CAAC,KAA8B,CAAC,6CAA6C;QAC3E,OAAO,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAChD,CAAC;IAYD;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,SAAS,CAAC,KAAuC;QACnD,MAAM,IAAI,GAAI,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,OAAO,wBAAY,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC;CACJ;AA/CD,sBA+CC;AAED,MAAM,eAAgB,SAAQ,+CAAsB;IAChD;;;;;;OAMG;IACH,YACqB,IAAqC,EACrC,KAA8B,CAAC,kDAAkD;QAElG,KAAK,CAAC,IAAA,cAAS,EAAC,kBAAmB,IAAK,OAAQ,KAAM,EAAE,CAAC,CAAC;QAHzC,SAAI,GAAJ,IAAI,CAAiC;QACrC,UAAK,GAAL,KAAK,CAAyB;IAGnD,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,KAAuC;QACnD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACpD,MAAM,IAAI,GAAI,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE5C,qBAAqB;QACrB,4CAA4C;QAC5C,MAAM,wBAAY,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,aAAa;QACtC,0BAA0B;QAC1B,SAAS,KAAK,CAAC,OAAY;YACvB,OAAO,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC,EACD,MAAM,KAAK,CAAC,aAAa,EAAE,CAC9B,CAAC;QAEF,OAAO,wBAAY,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,WAAY,SAAQ,eAAsC;IAO5D,YAA6B,IAAwD;QACjF,KAAK,EAAE,CAAC;QADiB,SAAI,GAAJ,IAAI,CAAoD;QAEjF,IAAI,CAAC,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC;IAPD,MAAM,CAAC,EAAE,CAAC,IAAwD;QAC9D,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAOD,KAAK,CAAC,UAAU,CAAC,KAAuC;QACpD,MAAM,IAAI,GAAG,MAAM,IAAA,aAAQ,EAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAEjE,OAAO,IAAI;aACN,IAAI,EAAE;aACN,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED;;;;;;OAMG;IACH,WAAW,CAAC,OAAe;QACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,QAAQ;QACJ,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;IAEO,MAAM,CAAC,QAAQ,CAAC,IAAwD;QAC5E,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QAElD,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;YAChD,MAAM,SAAS,GAAG,WAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,UAAU;gBAC9C,CAAC,CAAC,GAAG;gBACL,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC;YAEpB,OAAO;gBACH,WAAW,EAAE,KAAK,KAAK,CAAC;oBACpB,CAAC,CAAC,GAAI,GAAI,EAAE;oBACZ,CAAC,CAAC,GAAI,GAAG,CAAC,WAAY,GAAG,GAAG,CAAC,SAAS,GAAI,GAAI,EAAE;gBACpD,SAAS;aACZ,CAAA;QACL,CAAC,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC;QAErD,OAAO,GAAI,MAAO,IAAK,WAAY,EAAE,CAAC;IAC1C,CAAC;CACJ"}
1
+ {"version":3,"file":"Press.js","sourceRoot":"","sources":["../../../src/screenplay/interactions/Press.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { PageElement } from './PageElement';
2
+ import { Selector } from './selectors';
3
+ export declare abstract class Locator<Native_Element_Type, Native_Root_Element_Type = any, Selector_Type extends Selector = Selector> {
4
+ protected readonly parentRoot: () => Promise<Native_Root_Element_Type> | Native_Root_Element_Type;
5
+ protected readonly selector: Selector_Type;
6
+ protected readonly locateElement: (root: Native_Root_Element_Type) => Promise<Native_Element_Type> | Native_Element_Type;
7
+ protected readonly locateAllElements: (root: Native_Root_Element_Type) => Promise<Array<Native_Element_Type>> | Array<Native_Element_Type>;
8
+ constructor(parentRoot: () => Promise<Native_Root_Element_Type> | Native_Root_Element_Type, selector: Selector_Type, locateElement: (root: Native_Root_Element_Type) => Promise<Native_Element_Type> | Native_Element_Type, locateAllElements: (root: Native_Root_Element_Type) => Promise<Array<Native_Element_Type>> | Array<Native_Element_Type>);
9
+ nativeElement(): Promise<Native_Element_Type>;
10
+ abstract of(parent: Locator<Native_Element_Type, Native_Root_Element_Type>): Locator<Native_Element_Type, Native_Root_Element_Type>;
11
+ abstract element(): PageElement<Native_Element_Type>;
12
+ abstract allElements(): Promise<Array<PageElement<Native_Element_Type>>>;
13
+ toString(): string;
14
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Locator = void 0;
4
+ class Locator {
5
+ constructor(parentRoot, selector, locateElement, locateAllElements) {
6
+ this.parentRoot = parentRoot;
7
+ this.selector = selector;
8
+ this.locateElement = locateElement;
9
+ this.locateAllElements = locateAllElements;
10
+ }
11
+ async nativeElement() {
12
+ return this.locateElement(await this.parentRoot());
13
+ }
14
+ toString() {
15
+ return this.selector.toString();
16
+ }
17
+ }
18
+ exports.Locator = Locator;
19
+ //# sourceMappingURL=Locator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Locator.js","sourceRoot":"","sources":["../../../src/screenplay/models/Locator.ts"],"names":[],"mappings":";;;AAGA,MAAsB,OAAO;IACzB,YACuB,UAA8E,EAC9E,QAAuB,EACvB,aAAqG,EACrG,iBAAuH;QAHvH,eAAU,GAAV,UAAU,CAAoE;QAC9E,aAAQ,GAAR,QAAQ,CAAe;QACvB,kBAAa,GAAb,aAAa,CAAwF;QACrG,sBAAiB,GAAjB,iBAAiB,CAAsG;IAE9I,CAAC;IAEM,KAAK,CAAC,aAAa;QACtB,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IACvD,CAAC;IAOD,QAAQ;QACJ,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;IACpC,CAAC;CACJ;AArBD,0BAqBC"}
@@ -1,15 +1,13 @@
1
1
  import { Adapter, Answerable, Question } from '@serenity-js/core';
2
- import { NativeElementLocator } from './NativeElementLocator';
2
+ import { Locator } from './Locator';
3
3
  import { Selector } from './selectors';
4
4
  export declare abstract class PageElement<Native_Element_Type = any> {
5
- protected readonly selector: Selector<unknown>;
6
- private readonly locator;
7
- static located<NET, ST>(selector: Answerable<Selector<ST>>): Question<Promise<PageElement<NET>>> & Adapter<PageElement<NET>>;
5
+ readonly locator: Locator<Native_Element_Type>;
6
+ static located<NET>(selector: Answerable<Selector>): Question<Promise<PageElement<NET>>> & Adapter<PageElement<NET>>;
8
7
  static of<NET>(childElement: Answerable<PageElement<NET>>, parentElement: Answerable<PageElement<NET>>): Question<Promise<PageElement<NET>>> & Adapter<PageElement<NET>>;
9
- constructor(selector: Selector<unknown>, locator: NativeElementLocator<Native_Element_Type>);
10
- abstract of(parent: PageElement<Native_Element_Type>): PageElement<Native_Element_Type>;
8
+ constructor(locator: Locator<Native_Element_Type>);
9
+ abstract of(parentElement: PageElement<Native_Element_Type>): PageElement<Native_Element_Type>;
11
10
  nativeElement(): Promise<Native_Element_Type>;
12
- nativeElementLocator(): NativeElementLocator<Native_Element_Type>;
13
11
  toString(): string;
14
12
  abstract enterValue(value: string | number | Array<string | number>): Promise<void>;
15
13
  abstract clearValue(): Promise<void>;
@@ -2,19 +2,21 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PageElement = void 0;
4
4
  const core_1 = require("@serenity-js/core");
5
+ const tiny_types_1 = require("tiny-types");
5
6
  const abilities_1 = require("../abilities");
6
7
  const d = (0, core_1.format)({ markQuestions: false });
7
8
  class PageElement {
8
- constructor(selector, locator) {
9
- this.selector = selector;
9
+ constructor(locator) {
10
10
  this.locator = locator;
11
+ (0, tiny_types_1.ensure)('native element locator', locator, (0, tiny_types_1.isDefined)());
11
12
  }
12
13
  static located(selector) {
13
14
  return core_1.Question.about(d `page element located ${selector}`, async (actor) => {
14
15
  const bySelector = await actor.answer(selector);
15
- return abilities_1.BrowseTheWeb.as(actor).locate(bySelector);
16
+ return abilities_1.BrowseTheWeb.as(actor).locate(bySelector).element();
16
17
  });
17
18
  }
19
+ // todo: review usages and consider removing if not used
18
20
  static of(childElement, parentElement) {
19
21
  return core_1.Question.about(d `${childElement} of ${parentElement})`, async (actor) => {
20
22
  const child = await actor.answer(childElement);
@@ -23,18 +25,10 @@ class PageElement {
23
25
  });
24
26
  }
25
27
  async nativeElement() {
26
- try {
27
- return this.locator.locate(this.selector);
28
- }
29
- catch (error) {
30
- throw new core_1.LogicError(`Couldn't find element`, error);
31
- }
32
- }
33
- nativeElementLocator() {
34
- return this.locator;
28
+ return this.locator.nativeElement();
35
29
  }
36
30
  toString() {
37
- return `PageElement located ${this.selector}`;
31
+ return `PageElement located ${this.locator.toString()}`;
38
32
  }
39
33
  }
40
34
  exports.PageElement = PageElement;
@@ -1 +1 @@
1
- {"version":3,"file":"PageElement.js","sourceRoot":"","sources":["../../../src/screenplay/models/PageElement.ts"],"names":[],"mappings":";;;AAAA,4CAAsF;AAEtF,4CAA4C;AAI5C,MAAM,CAAC,GAAG,IAAA,aAAM,EAAC,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC;AAE3C,MAAsB,WAAW;IAkB7B,YACuB,QAA2B,EAC7B,OAAkD;QADhD,aAAQ,GAAR,QAAQ,CAAmB;QAC7B,YAAO,GAAP,OAAO,CAA2C;IAEvE,CAAC;IApBD,MAAM,CAAC,OAAO,CAAU,QAAkC;QACtD,OAAO,eAAQ,CAAC,KAAK,CAAC,CAAC,CAAA,wBAAyB,QAAS,EAAE,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YACvE,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAChD,OAAO,wBAAY,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,MAAM,CAAK,UAAU,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;IACP,CAAC;IAED,MAAM,CAAC,EAAE,CAAM,YAA0C,EAAE,aAA2C;QAClG,OAAO,eAAQ,CAAC,KAAK,CAAC,CAAC,CAAA,GAAI,YAAa,OAAQ,aAAc,GAAG,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YAC7E,MAAM,KAAK,GAAO,MAAM,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YACnD,MAAM,MAAM,GAAM,MAAM,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YAEpD,OAAO,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACP,CAAC;IAUD,KAAK,CAAC,aAAa;QACf,IAAI;YACA,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;SAC7C;QACD,OAAO,KAAK,EAAE;YACV,MAAM,IAAI,iBAAU,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;SACxD;IACL,CAAC;IAED,oBAAoB;QAChB,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;IAED,QAAQ;QACJ,OAAO,uBAAwB,IAAI,CAAC,QAAS,EAAE,CAAA;IACnD,CAAC;CAoBJ;AA7DD,kCA6DC"}
1
+ {"version":3,"file":"PageElement.js","sourceRoot":"","sources":["../../../src/screenplay/models/PageElement.ts"],"names":[],"mappings":";;;AAAA,4CAA0E;AAC1E,2CAA+C;AAE/C,4CAA4C;AAI5C,MAAM,CAAC,GAAG,IAAA,aAAM,EAAC,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC;AAE3C,MAAsB,WAAW;IAmB7B,YAA4B,OAAqC;QAArC,YAAO,GAAP,OAAO,CAA8B;QAC7D,IAAA,mBAAM,EAAC,wBAAwB,EAAE,OAAO,EAAE,IAAA,sBAAS,GAAE,CAAC,CAAC;IAC3D,CAAC;IAnBD,MAAM,CAAC,OAAO,CAAM,QAA8B;QAC9C,OAAO,eAAQ,CAAC,KAAK,CAAC,CAAC,CAAA,wBAAyB,QAAS,EAAE,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YACvE,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAChD,OAAO,wBAAY,CAAC,EAAE,CAAM,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;QACpE,CAAC,CAAC,CAAC;IACP,CAAC;IAED,wDAAwD;IACxD,MAAM,CAAC,EAAE,CAAM,YAA0C,EAAE,aAA2C;QAClG,OAAO,eAAQ,CAAC,KAAK,CAAC,CAAC,CAAA,GAAI,YAAa,OAAQ,aAAc,GAAG,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YAC7E,MAAM,KAAK,GAAO,MAAM,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YACnD,MAAM,MAAM,GAAM,MAAM,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YAEpD,OAAO,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACP,CAAC;IAQD,KAAK,CAAC,aAAa;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;IACxC,CAAC;IAED,QAAQ;QACJ,OAAO,uBAAwB,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAG,EAAE,CAAC;IAC9D,CAAC;CAoBJ;AAnDD,kCAmDC"}
@@ -1,15 +1,13 @@
1
- import { Answerable, List } from '@serenity-js/core';
1
+ import { Answerable, List, MetaQuestion, Question } from '@serenity-js/core';
2
+ import { Locator } from './Locator';
2
3
  import { PageElement } from './PageElement';
3
4
  import { Selector } from './selectors';
4
- export declare class PageElements<Native_Element_Type = any> extends List<PageElement<Native_Element_Type>> {
5
- protected readonly selector: Answerable<Selector<unknown>>;
6
- private readonly parent?;
7
- static located<NET, ST>(selector: Answerable<Selector<ST>>): PageElements<NET>;
5
+ export declare class PageElements<Native_Element_Type = any> extends List<PageElement<Native_Element_Type>> implements MetaQuestion<Answerable<PageElement<Native_Element_Type>>, Promise<Array<PageElement<Native_Element_Type>>>> {
6
+ protected readonly locator: Question<Promise<Locator<Native_Element_Type>>>;
7
+ static located<NET>(selector: Answerable<Selector>): PageElements<NET>;
8
8
  /**
9
- * @param {Answerable<Selector<unknown>>} selector
10
- * @param {Answerable<PageElement>} [parent]
11
- * if not specified, browser root selector is used
9
+ * @param locator
12
10
  */
13
- constructor(selector: Answerable<Selector<unknown>>, parent?: Answerable<PageElement>);
11
+ constructor(locator: Question<Promise<Locator<Native_Element_Type>>>);
14
12
  of(parent: Answerable<PageElement<Native_Element_Type>>): PageElements<Native_Element_Type>;
15
13
  }
@@ -6,20 +6,17 @@ const abilities_1 = require("../abilities");
6
6
  const f = (0, core_1.format)({ markQuestions: true });
7
7
  class PageElements extends core_1.List {
8
8
  /**
9
- * @param {Answerable<Selector<unknown>>} selector
10
- * @param {Answerable<PageElement>} [parent]
11
- * if not specified, browser root selector is used
9
+ * @param locator
12
10
  */
13
- constructor(selector, parent) {
14
- super(new PageElementCollection(selector, parent));
15
- this.selector = selector;
16
- this.parent = parent;
11
+ constructor(locator) {
12
+ super(allElementsOf(locator));
13
+ this.locator = locator;
17
14
  }
18
15
  static located(selector) {
19
- return new PageElements(selector);
16
+ return new PageElements(relativeToDocumentRoot(selector));
20
17
  }
21
18
  of(parent) {
22
- return new PageElements(this.selector, parent)
19
+ return new PageElements(relativeToParent(this.locator, parent))
23
20
  .describedAs(`<<${this.toString()}>>` + f `.of(${parent})`);
24
21
  }
25
22
  }
@@ -27,24 +24,29 @@ exports.PageElements = PageElements;
27
24
  /**
28
25
  * @package
29
26
  */
30
- class PageElementCollection extends core_1.Question {
31
- constructor(selector, parent) {
32
- super();
33
- this.selector = selector;
34
- this.parent = parent;
35
- this.subject = `page elements located ${selector.toString()}`;
36
- }
37
- async answeredBy(actor) {
38
- const bySelector = await actor.answer(this.selector);
39
- const parent = await actor.answer(this.parent);
40
- return abilities_1.BrowseTheWeb.as(actor).locateAll(bySelector, parent ? parent.nativeElementLocator() : undefined);
41
- }
42
- describedAs(subject) {
43
- this.subject = subject;
44
- return this;
45
- }
46
- toString() {
47
- return this.subject;
48
- }
27
+ function relativeToDocumentRoot(selector) {
28
+ return core_1.Question.about(selector.toString(), async (actor) => {
29
+ const bySelector = await actor.answer(selector);
30
+ return abilities_1.BrowseTheWeb.as(actor).locate(bySelector);
31
+ });
32
+ }
33
+ /**
34
+ * @package
35
+ */
36
+ function relativeToParent(relativeLocator, parent) {
37
+ return core_1.Question.about(relativeLocator.toString() + f `.of${parent}`, async (actor) => {
38
+ const locator = await actor.answer(relativeLocator);
39
+ const parentElement = await actor.answer(parent);
40
+ return locator.of(parentElement.locator);
41
+ });
42
+ }
43
+ /**
44
+ * @package
45
+ */
46
+ function allElementsOf(locator) {
47
+ return core_1.Question.about(`page elements located ${locator.toString()}`, async (actor) => {
48
+ const resolved = await actor.answer(locator);
49
+ return resolved.allElements();
50
+ });
49
51
  }
50
52
  //# sourceMappingURL=PageElements.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PageElements.js","sourceRoot":"","sources":["../../../src/screenplay/models/PageElements.ts"],"names":[],"mappings":";;;AAAA,4CAAwG;AAExG,4CAA4C;AAI5C,MAAM,CAAC,GAAG,IAAA,aAAM,EAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;AAE1C,MAAa,YACT,SAAQ,WAAsC;IAM9C;;;;OAIG;IACH,YACuB,QAAuC,EACzC,MAAgC;QAEjD,KAAK,CAAC,IAAI,qBAAqB,CAAsB,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;QAHrD,aAAQ,GAAR,QAAQ,CAA+B;QACzC,WAAM,GAAN,MAAM,CAA0B;IAGrD,CAAC;IAdD,MAAM,CAAC,OAAO,CAAU,QAAkC;QACtD,OAAO,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IAcD,EAAE,CAAC,MAAoD;QACnD,OAAO,IAAI,YAAY,CAAsB,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC;aAC9D,WAAW,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,IAAI,GAAG,CAAC,CAAA,OAAQ,MAAO,GAAG,CAAC,CAAC;IACrE,CAAC;CACJ;AAvBD,oCAuBC;AAED;;GAEG;AACH,MAAM,qBACF,SAAQ,eAA0D;IAGlE,YACqB,QAAuC,EACvC,MAAgC;QAEjD,KAAK,EAAE,CAAC;QAHS,aAAQ,GAAR,QAAQ,CAA+B;QACvC,WAAM,GAAN,MAAM,CAA0B;QAGjD,IAAI,CAAC,OAAO,GAAG,yBAA0B,QAAQ,CAAC,QAAQ,EAAG,EAAE,CAAC;IACpE,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAAuC;QACpD,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrD,MAAM,MAAM,GAAO,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEnD,OAAO,wBAAY,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC5G,CAAC;IAED,WAAW,CAAC,OAAe;QACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,QAAQ;QACJ,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;CACJ"}
1
+ {"version":3,"file":"PageElements.js","sourceRoot":"","sources":["../../../src/screenplay/models/PageElements.ts"],"names":[],"mappings":";;;AAAA,4CAAqF;AAErF,4CAA4C;AAK5C,MAAM,CAAC,GAAG,IAAA,aAAM,EAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;AAE1C,MAAa,YACT,SAAQ,WAAsC;IAO9C;;OAEG;IACH,YAA+B,OAAwD;QACnF,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;QADH,YAAO,GAAP,OAAO,CAAiD;IAEvF,CAAC;IATD,MAAM,CAAC,OAAO,CAAM,QAA8B;QAC9C,OAAO,IAAI,YAAY,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC9D,CAAC;IASD,EAAE,CAAC,MAAoD;QACnD,OAAO,IAAI,YAAY,CAAsB,gBAAgB,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;aAC/E,WAAW,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,IAAI,GAAG,CAAC,CAAA,OAAQ,MAAO,GAAG,CAAC,CAAC;IACrE,CAAC;CACJ;AAnBD,oCAmBC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAAsB,QAA8B;IAC/E,OAAO,eAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;QACrD,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChD,OAAO,wBAAY,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACP,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAsB,eAAyD,EAAE,MAAoD;IAC1J,OAAO,eAAQ,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA,MAAO,MAAO,EAAE,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;QAChF,MAAM,OAAO,GAA6C,MAAM,KAAK,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QAC9F,MAAM,aAAa,GAAuC,MAAM,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAErF,OAAO,OAAO,CAAC,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACP,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAsB,OAAwD;IAChG,OAAO,eAAQ,CAAC,KAAK,CAAC,yBAA0B,OAAO,CAAC,QAAQ,EAAG,EAAE,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;QACjF,MAAM,QAAQ,GAAiC,MAAM,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3E,OAAO,QAAQ,CAAC,WAAW,EAAE,CAAC;IAClC,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -1,9 +1,8 @@
1
1
  export * from './Cookie';
2
2
  export * from './CookieData';
3
+ export * from './Locator';
3
4
  export * from './ModalDialog';
4
- export * from './NativeElementLocator';
5
5
  export * from './Page';
6
6
  export * from './PageElement';
7
7
  export * from './PageElements';
8
- export * from './PassThroughNativeElementLocator';
9
8
  export * from './selectors';
@@ -12,11 +12,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  __exportStar(require("./Cookie"), exports);
14
14
  __exportStar(require("./CookieData"), exports);
15
+ __exportStar(require("./Locator"), exports);
15
16
  __exportStar(require("./ModalDialog"), exports);
16
- __exportStar(require("./NativeElementLocator"), exports);
17
17
  __exportStar(require("./Page"), exports);
18
18
  __exportStar(require("./PageElement"), exports);
19
19
  __exportStar(require("./PageElements"), exports);
20
- __exportStar(require("./PassThroughNativeElementLocator"), exports);
21
20
  __exportStar(require("./selectors"), exports);
22
21
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/screenplay/models/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAyB;AACzB,+CAA6B;AAC7B,gDAA8B;AAC9B,yDAAuC;AACvC,yCAAuB;AACvB,gDAA8B;AAC9B,iDAA+B;AAC/B,oEAAkD;AAClD,8CAA4B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/screenplay/models/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAyB;AACzB,+CAA6B;AAC7B,4CAA0B;AAC1B,gDAA8B;AAC9B,yCAAuB;AACvB,gDAA8B;AAC9B,iDAA+B;AAC/B,8CAA4B"}
@@ -1,3 +1,5 @@
1
1
  import { Selector } from './Selector';
2
- export declare class ByCss extends Selector<string> {
2
+ export declare class ByCss extends Selector {
3
+ readonly value: string;
4
+ constructor(value: string);
3
5
  }
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ByCss = void 0;
4
4
  const Selector_1 = require("./Selector");
5
5
  class ByCss extends Selector_1.Selector {
6
+ constructor(value) {
7
+ super();
8
+ this.value = value;
9
+ }
6
10
  }
7
11
  exports.ByCss = ByCss;
8
12
  //# sourceMappingURL=ByCss.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ByCss.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/ByCss.ts"],"names":[],"mappings":";;;AAAA,yCAAsC;AAEtC,MAAa,KAAM,SAAQ,mBAAgB;CAC1C;AADD,sBACC"}
1
+ {"version":3,"file":"ByCss.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/ByCss.ts"],"names":[],"mappings":";;;AAAA,yCAAsC;AAEtC,MAAa,KAAM,SAAQ,mBAAQ;IAC/B,YAA4B,KAAa;QACrC,KAAK,EAAE,CAAC;QADgB,UAAK,GAAL,KAAK,CAAQ;IAEzC,CAAC;CACJ;AAJD,sBAIC"}
@@ -1,5 +1,6 @@
1
1
  import { Selector } from './Selector';
2
- export declare class ByCssContainingText extends Selector<string> {
2
+ export declare class ByCssContainingText extends Selector {
3
+ readonly value: string;
3
4
  readonly text: string;
4
- constructor(selector: string, text: string);
5
+ constructor(value: string, text: string);
5
6
  }
@@ -3,8 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ByCssContainingText = void 0;
4
4
  const Selector_1 = require("./Selector");
5
5
  class ByCssContainingText extends Selector_1.Selector {
6
- constructor(selector, text) {
7
- super(selector);
6
+ constructor(value, text) {
7
+ super();
8
+ this.value = value;
8
9
  this.text = text;
9
10
  }
10
11
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ByCssContainingText.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/ByCssContainingText.ts"],"names":[],"mappings":";;;AAAA,yCAAsC;AAEtC,MAAa,mBAAoB,SAAQ,mBAAgB;IACrD,YAAY,QAAgB,EAAkB,IAAY;QACtD,KAAK,CAAC,QAAQ,CAAC,CAAC;QAD0B,SAAI,GAAJ,IAAI,CAAQ;IAE1D,CAAC;CACJ;AAJD,kDAIC"}
1
+ {"version":3,"file":"ByCssContainingText.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/ByCssContainingText.ts"],"names":[],"mappings":";;;AAAA,yCAAsC;AAEtC,MAAa,mBAAoB,SAAQ,mBAAQ;IAC7C,YAA4B,KAAa,EAAkB,IAAY;QACnE,KAAK,EAAE,CAAC;QADgB,UAAK,GAAL,KAAK,CAAQ;QAAkB,SAAI,GAAJ,IAAI,CAAQ;IAEvE,CAAC;CACJ;AAJD,kDAIC"}
@@ -1,3 +1,5 @@
1
1
  import { Selector } from './Selector';
2
- export declare class ById extends Selector<string> {
2
+ export declare class ById extends Selector {
3
+ readonly value: string;
4
+ constructor(value: string);
3
5
  }
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ById = void 0;
4
4
  const Selector_1 = require("./Selector");
5
5
  class ById extends Selector_1.Selector {
6
+ constructor(value) {
7
+ super();
8
+ this.value = value;
9
+ }
6
10
  }
7
11
  exports.ById = ById;
8
12
  //# sourceMappingURL=ById.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ById.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/ById.ts"],"names":[],"mappings":";;;AAAA,yCAAsC;AAEtC,MAAa,IAAK,SAAQ,mBAAgB;CACzC;AADD,oBACC"}
1
+ {"version":3,"file":"ById.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/ById.ts"],"names":[],"mappings":";;;AAAA,yCAAsC;AAEtC,MAAa,IAAK,SAAQ,mBAAQ;IAC9B,YAA4B,KAAa;QACrC,KAAK,EAAE,CAAC;QADgB,UAAK,GAAL,KAAK,CAAQ;IAEzC,CAAC;CACJ;AAJD,oBAIC"}
@@ -1,3 +1,5 @@
1
1
  import { Selector } from './Selector';
2
- export declare class ByTagName extends Selector<string> {
2
+ export declare class ByTagName extends Selector {
3
+ readonly value: string;
4
+ constructor(value: string);
3
5
  }
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ByTagName = void 0;
4
4
  const Selector_1 = require("./Selector");
5
5
  class ByTagName extends Selector_1.Selector {
6
+ constructor(value) {
7
+ super();
8
+ this.value = value;
9
+ }
6
10
  }
7
11
  exports.ByTagName = ByTagName;
8
12
  //# sourceMappingURL=ByTagName.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ByTagName.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/ByTagName.ts"],"names":[],"mappings":";;;AAAA,yCAAsC;AAEtC,MAAa,SAAU,SAAQ,mBAAgB;CAC9C;AADD,8BACC"}
1
+ {"version":3,"file":"ByTagName.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/ByTagName.ts"],"names":[],"mappings":";;;AAAA,yCAAsC;AAEtC,MAAa,SAAU,SAAQ,mBAAQ;IACnC,YAA4B,KAAa;QACrC,KAAK,EAAE,CAAC;QADgB,UAAK,GAAL,KAAK,CAAQ;IAEzC,CAAC;CACJ;AAJD,8BAIC"}
@@ -1,3 +1,5 @@
1
1
  import { Selector } from './Selector';
2
- export declare class ByXPath extends Selector<string> {
2
+ export declare class ByXPath extends Selector {
3
+ readonly value: string;
4
+ constructor(value: string);
3
5
  }
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ByXPath = void 0;
4
4
  const Selector_1 = require("./Selector");
5
5
  class ByXPath extends Selector_1.Selector {
6
+ constructor(value) {
7
+ super();
8
+ this.value = value;
9
+ }
6
10
  }
7
11
  exports.ByXPath = ByXPath;
8
12
  //# sourceMappingURL=ByXPath.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ByXPath.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/ByXPath.ts"],"names":[],"mappings":";;;AAAA,yCAAsC;AAEtC,MAAa,OAAQ,SAAQ,mBAAgB;CAC5C;AADD,0BACC"}
1
+ {"version":3,"file":"ByXPath.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/ByXPath.ts"],"names":[],"mappings":";;;AAAA,yCAAsC;AAEtC,MAAa,OAAQ,SAAQ,mBAAQ;IACjC,YAA4B,KAAa;QACrC,KAAK,EAAE,CAAC;QADgB,UAAK,GAAL,KAAK,CAAQ;IAEzC,CAAC;CACJ;AAJD,0BAIC"}
@@ -1,5 +1,3 @@
1
- export declare abstract class Selector<T> {
2
- readonly value: T;
3
- constructor(value: T);
1
+ export declare abstract class Selector {
4
2
  toString(): string;
5
3
  }
@@ -4,10 +4,8 @@ exports.Selector = void 0;
4
4
  const core_1 = require("@serenity-js/core");
5
5
  const f = (0, core_1.format)({ markQuestions: true });
6
6
  class Selector {
7
- constructor(value) {
8
- this.value = value;
9
- }
10
7
  toString() {
8
+ // todo: strip anything preceding "By", like "WebdriverIOByCss|
11
9
  const selectorDescription = this.constructor.name.replace(/([a-z]+)([A-Z])/g, '$1 $2').toLowerCase();
12
10
  const parametersDescription = Object.keys(this).map(field => f `${this[field]}`).join(', ');
13
11
  return `${selectorDescription} (${parametersDescription})`;
@@ -1 +1 @@
1
- {"version":3,"file":"Selector.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/Selector.ts"],"names":[],"mappings":";;;AAAA,4CAA2C;AAE3C,MAAM,CAAC,GAAG,IAAA,aAAM,EAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;AAE1C,MAAsB,QAAQ;IAE1B,YAA4B,KAAQ;QAAR,UAAK,GAAL,KAAK,CAAG;IACpC,CAAC;IAED,QAAQ;QACJ,MAAM,mBAAmB,GAAK,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QACvG,MAAM,qBAAqB,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE3F,OAAO,GAAI,mBAAoB,KAAM,qBAAsB,GAAG,CAAC;IACnE,CAAC;CACJ;AAXD,4BAWC"}
1
+ {"version":3,"file":"Selector.js","sourceRoot":"","sources":["../../../../src/screenplay/models/selectors/Selector.ts"],"names":[],"mappings":";;;AAAA,4CAA2C;AAE3C,MAAM,CAAC,GAAG,IAAA,aAAM,EAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;AAE1C,MAAsB,QAAQ;IAE1B,QAAQ;QACJ,+DAA+D;QAC/D,MAAM,mBAAmB,GAAK,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QACvG,MAAM,qBAAqB,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE3F,OAAO,GAAI,mBAAoB,KAAM,qBAAsB,GAAG,CAAC;IACnE,CAAC;CACJ;AATD,4BASC"}
@@ -1,5 +1,5 @@
1
1
  import { Adapter, Answerable, MetaQuestion, Question } from '@serenity-js/core';
2
- import { PageElement } from '../models';
2
+ import { PageElement, PageElements } from '../models';
3
3
  /**
4
4
  * @desc
5
5
  * Resolves to the visible (i.e. not hidden by CSS) `innerText` of:
@@ -93,5 +93,6 @@ export declare class Text {
93
93
  *
94
94
  * @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
95
95
  */
96
+ static ofAll(elements: PageElements): Question<Promise<string[]>> & MetaQuestion<Answerable<PageElement>, Promise<string[]>> & Adapter<string[]>;
96
97
  static ofAll(elements: Answerable<PageElement[]>): Question<Promise<string[]>> & Adapter<string[]>;
97
98
  }
@@ -89,17 +89,10 @@ class Text {
89
89
  static of(element) {
90
90
  return (0, core_1.createAdapter)(new TextOfSingleElement(element));
91
91
  }
92
- /**
93
- * @desc
94
- * Retrieves text of a group of {@link WebElement}s,
95
- * represented by Answerable<{@link @wdio/types~ElementList}>
96
- *
97
- * @param {Answerable<PageElement[]>} elements
98
- * @returns {Question<Promise<string[]>> & MetaQuestion<Answerable<PageElement>, Promise<string[]>>}
99
- *
100
- * @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
101
- */
102
92
  static ofAll(elements) {
93
+ if (elements instanceof models_1.PageElements) {
94
+ return (0, core_1.createAdapter)(new TextOfMultipleElements(elements));
95
+ }
103
96
  return core_1.Question.about(f `the text of ${elements}`, async (actor) => {
104
97
  const pageElements = await actor.answer(elements);
105
98
  return (0, io_1.asyncMap)(pageElements, element => element.text());
@@ -120,4 +113,17 @@ class TextOfSingleElement extends ElementQuestion_1.ElementQuestion {
120
113
  return element.text();
121
114
  }
122
115
  }
116
+ class TextOfMultipleElements extends ElementQuestion_1.ElementQuestion {
117
+ constructor(elements) {
118
+ super(f `the text of ${elements}`);
119
+ this.elements = elements;
120
+ }
121
+ of(parent) {
122
+ return new TextOfMultipleElements(this.elements.of(parent));
123
+ }
124
+ async answeredBy(actor) {
125
+ const elements = await actor.answer(this.elements);
126
+ return (0, io_1.asyncMap)(elements, element => element.text());
127
+ }
128
+ }
123
129
  //# sourceMappingURL=Text.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Text.js","sourceRoot":"","sources":["../../../src/screenplay/questions/Text.ts"],"names":[],"mappings":";;;AAAA,4CAAwI;AACxI,iDAAoD;AAEpD,sCAAwC;AACxC,uDAAoD;AAEpD,MAAM,CAAC,GAAG,IAAA,aAAM,EAAC,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,MAAa,IAAI;IAEb;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,CAAC,OAAgC;QAKtC,OAAO,IAAA,oBAAa,EAChB,IAAI,mBAAmB,CAAC,OAAO,CAAC,CACnC,CAAC;IACN,CAAC;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,KAAK,CAAC,QAAmC;QAC5C,OAAO,eAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,eAAgB,QAAS,EAAE,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YAE/D,MAAM,YAAY,GAAkB,MAAM,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAEjE,OAAO,IAAA,aAAQ,EAAC,YAAY,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAA;IACN,CAAC;CACJ;AAxCD,oBAwCC;AAED,MAAM,mBACF,SAAQ,iCAAgC;IAGxC,YAA6B,OAAgC;QACzD,KAAK,CAAC,eAAgB,OAAQ,EAAE,CAAC,CAAC;QADT,YAAO,GAAP,OAAO,CAAyB;IAE7D,CAAC;IAED,EAAE,CAAC,MAA+B;QAC9B,OAAO,IAAI,mBAAmB,CAAC,oBAAW,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAAuC;QACpD,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEjD,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;CACJ"}
1
+ {"version":3,"file":"Text.js","sourceRoot":"","sources":["../../../src/screenplay/questions/Text.ts"],"names":[],"mappings":";;;AAAA,4CAAwI;AACxI,iDAAoD;AAEpD,sCAAsD;AACtD,uDAAoD;AAEpD,MAAM,CAAC,GAAG,IAAA,aAAM,EAAC,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,MAAa,IAAI;IAEb;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,CAAC,OAAgC;QAKtC,OAAO,IAAA,oBAAa,EAChB,IAAI,mBAAmB,CAAC,OAAO,CAAC,CACnC,CAAC;IACN,CAAC;IAcD,MAAM,CAAC,KAAK,CAAC,QAAkD;QAC3D,IAAI,QAAQ,YAAY,qBAAY,EAAE;YAClC,OAAO,IAAA,oBAAa,EAChB,IAAI,sBAAsB,CAAC,QAAQ,CAAC,CACvC,CAAC;SACL;QAED,OAAO,eAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,eAAgB,QAAS,EAAE,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YAC/D,MAAM,YAAY,GAAkB,MAAM,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAEjE,OAAO,IAAA,aAAQ,EAAC,YAAY,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;IACP,CAAC;CACJ;AA/CD,oBA+CC;AAED,MAAM,mBACF,SAAQ,iCAAgC;IAGxC,YAA6B,OAAgC;QACzD,KAAK,CAAC,eAAgB,OAAQ,EAAE,CAAC,CAAC;QADT,YAAO,GAAP,OAAO,CAAyB;IAE7D,CAAC;IAED,EAAE,CAAC,MAA+B;QAC9B,OAAO,IAAI,mBAAmB,CAAC,oBAAW,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAAuC;QACpD,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEjD,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;CACJ;AAED,MAAM,sBACF,SAAQ,iCAAkC;IAG1C,YAA6B,QAAsB;QAC/C,KAAK,CAAC,CAAC,CAAC,eAAgB,QAAS,EAAE,CAAC,CAAC;QADZ,aAAQ,GAAR,QAAQ,CAAc;IAEnD,CAAC;IAED,EAAE,CAAC,MAA+B;QAC9B,OAAO,IAAI,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAAuC;QACpD,MAAM,QAAQ,GAAkB,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAElE,OAAO,IAAA,aAAQ,EAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;CACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serenity-js/web",
3
- "version": "3.0.0-rc.4",
3
+ "version": "3.0.0-rc.5",
4
4
  "description": "Serenity/JS Screenplay Pattern APIs for the Web",
5
5
  "author": {
6
6
  "name": "Jan Molak",
@@ -46,8 +46,8 @@
46
46
  "npm": "^6 || ^7"
47
47
  },
48
48
  "dependencies": {
49
- "@serenity-js/assertions": "3.0.0-rc.4",
50
- "@serenity-js/core": "3.0.0-rc.4",
49
+ "@serenity-js/assertions": "3.0.0-rc.5",
50
+ "@serenity-js/core": "3.0.0-rc.5",
51
51
  "tiny-types": "^1.17.0"
52
52
  },
53
53
  "devDependencies": {
@@ -81,5 +81,5 @@
81
81
  "cache": true,
82
82
  "all": true
83
83
  },
84
- "gitHead": "126a1cad42bcb1416ca68e62f54bf0d3eb3ea157"
84
+ "gitHead": "b63bb1465731d922fde0ade3b77ddebb96dbf990"
85
85
  }
@@ -1,10 +1,12 @@
1
- import { Ability, Duration, UsesAbilities } from '@serenity-js/core';
1
+ import { Ability, Duration, format, LogicError, UsesAbilities } from '@serenity-js/core';
2
2
 
3
3
  import { Key } from '../../input';
4
- import { Cookie, CookieData, ModalDialog, NativeElementLocator, Page, PageElement, Selector } from '../models';
4
+ import { Cookie, CookieData, Locator, ModalDialog, Page, PageElement, Selector } from '../models';
5
5
  import { BrowserCapabilities } from './BrowserCapabilities';
6
6
 
7
- export abstract class BrowseTheWeb<Native_Element_Type = any> implements Ability {
7
+ const f = format({ markQuestions: true });
8
+
9
+ export abstract class BrowseTheWeb<Native_Element_Type = any, Native_Root_Element_Type = unknown> implements Ability {
8
10
  /**
9
11
  * @desc
10
12
  * Used to access the Actor's ability to {@link BrowseTheWeb}
@@ -14,8 +16,17 @@ export abstract class BrowseTheWeb<Native_Element_Type = any> implements Ability
14
16
  * @param {@serenity-js/core/lib/screenplay/actor~UsesAbilities} actor
15
17
  * @return {BrowseTheWeb}
16
18
  */
17
- static as(actor: UsesAbilities): BrowseTheWeb {
18
- return actor.abilityTo(BrowseTheWeb);
19
+ static as<NET = any, NRET = unknown>(actor: UsesAbilities): BrowseTheWeb<NET, NRET> {
20
+ return actor.abilityTo(BrowseTheWeb) as BrowseTheWeb<NET, NRET>;
21
+ }
22
+
23
+ constructor(
24
+ protected locators: Map<
25
+ new (...args: unknown[]) => Selector,
26
+ (selector: Selector) =>
27
+ Locator<Native_Element_Type, Native_Root_Element_Type>
28
+ >
29
+ ) {
19
30
  }
20
31
 
21
32
  abstract navigateTo(destination: string): Promise<void>;
@@ -30,9 +41,15 @@ export abstract class BrowseTheWeb<Native_Element_Type = any> implements Ability
30
41
 
31
42
  abstract waitUntil(condition: () => boolean | Promise<boolean>, timeout: Duration): Promise<void>;
32
43
 
33
- abstract locate<T>(selector: Selector<T>, locator?: NativeElementLocator<Native_Element_Type>): PageElement<Native_Element_Type>;
34
- abstract locateAll<T>(selector: Selector<T>, locator?: NativeElementLocator<Native_Element_Type>): Promise<Array<PageElement<Native_Element_Type>>>;
35
- abstract nativeElementLocator(): NativeElementLocator<Native_Element_Type>;
44
+ locate(selector: Selector): Locator<Native_Element_Type, Native_Root_Element_Type> {
45
+ for (const [ type, locatorFactory ] of this.locators) {
46
+ if (selector instanceof type) {
47
+ return locatorFactory(selector);
48
+ }
49
+ }
50
+
51
+ throw new LogicError(f `${ selector } is not supported by ${ this.constructor.name }`);
52
+ }
36
53
 
37
54
  abstract browserCapabilities(): Promise<BrowserCapabilities>;
38
55
 
@@ -121,7 +121,6 @@ class PressKeyInField extends PageElementInteraction {
121
121
  const keys = await actor.answer(this.keys);
122
122
 
123
123
  // fix for protractor
124
- // todo: should this wait on focus to occur?
125
124
  await BrowseTheWeb.as(actor).executeScript(
126
125
  /* istanbul ignore next */
127
126
  function focus(element: any) {
@@ -0,0 +1,25 @@
1
+ import { PageElement } from './PageElement';
2
+ import { Selector } from './selectors';
3
+
4
+ export abstract class Locator<Native_Element_Type, Native_Root_Element_Type = any, Selector_Type extends Selector = Selector> {
5
+ constructor(
6
+ protected readonly parentRoot: () => Promise<Native_Root_Element_Type> | Native_Root_Element_Type,
7
+ protected readonly selector: Selector_Type,
8
+ protected readonly locateElement: (root: Native_Root_Element_Type) => Promise<Native_Element_Type> | Native_Element_Type,
9
+ protected readonly locateAllElements: (root: Native_Root_Element_Type) => Promise<Array<Native_Element_Type>> | Array<Native_Element_Type>,
10
+ ) {
11
+ }
12
+
13
+ public async nativeElement(): Promise<Native_Element_Type> {
14
+ return this.locateElement(await this.parentRoot());
15
+ }
16
+
17
+ abstract of(parent: Locator<Native_Element_Type, Native_Root_Element_Type>): Locator<Native_Element_Type, Native_Root_Element_Type>;
18
+
19
+ abstract element(): PageElement<Native_Element_Type>;
20
+ abstract allElements(): Promise<Array<PageElement<Native_Element_Type>>>;
21
+
22
+ toString(): string {
23
+ return this.selector.toString();
24
+ }
25
+ }
@@ -1,20 +1,22 @@
1
- import { Adapter, Answerable, format, LogicError, Question } from '@serenity-js/core';
1
+ import { Adapter, Answerable, format, Question } from '@serenity-js/core';
2
+ import { ensure, isDefined } from 'tiny-types';
2
3
 
3
4
  import { BrowseTheWeb } from '../abilities';
4
- import { NativeElementLocator } from './NativeElementLocator';
5
+ import { Locator } from './Locator';
5
6
  import { Selector } from './selectors';
6
7
 
7
8
  const d = format({ markQuestions: false });
8
9
 
9
10
  export abstract class PageElement<Native_Element_Type = any> {
10
11
 
11
- static located<NET, ST>(selector: Answerable<Selector<ST>>): Question<Promise<PageElement<NET>>> & Adapter<PageElement<NET>> {
12
+ static located<NET>(selector: Answerable<Selector>): Question<Promise<PageElement<NET>>> & Adapter<PageElement<NET>> {
12
13
  return Question.about(d`page element located ${ selector }`, async actor => {
13
14
  const bySelector = await actor.answer(selector);
14
- return BrowseTheWeb.as(actor).locate<ST>(bySelector);
15
+ return BrowseTheWeb.as<NET>(actor).locate(bySelector).element();
15
16
  });
16
17
  }
17
18
 
19
+ // todo: review usages and consider removing if not used
18
20
  static of<NET>(childElement: Answerable<PageElement<NET>>, parentElement: Answerable<PageElement<NET>>): Question<Promise<PageElement<NET>>> & Adapter<PageElement<NET>> {
19
21
  return Question.about(d`${ childElement } of ${ parentElement })`, async actor => {
20
22
  const child = await actor.answer(childElement);
@@ -24,29 +26,18 @@ export abstract class PageElement<Native_Element_Type = any> {
24
26
  });
25
27
  }
26
28
 
27
- constructor(
28
- protected readonly selector: Selector<unknown>,
29
- private readonly locator: NativeElementLocator<Native_Element_Type>,
30
- ) {
29
+ constructor(public readonly locator: Locator<Native_Element_Type>) {
30
+ ensure('native element locator', locator, isDefined());
31
31
  }
32
32
 
33
- abstract of(parent: PageElement<Native_Element_Type>): PageElement<Native_Element_Type>;
33
+ abstract of(parentElement: PageElement<Native_Element_Type>): PageElement<Native_Element_Type>;
34
34
 
35
35
  async nativeElement(): Promise<Native_Element_Type> {
36
- try {
37
- return this.locator.locate(this.selector);
38
- }
39
- catch (error) {
40
- throw new LogicError(`Couldn't find element`, error);
41
- }
42
- }
43
-
44
- nativeElementLocator(): NativeElementLocator<Native_Element_Type> {
45
- return this.locator;
36
+ return this.locator.nativeElement();
46
37
  }
47
38
 
48
39
  toString(): string {
49
- return `PageElement located ${ this.selector }`
40
+ return `PageElement located ${ this.locator.toString() }`;
50
41
  }
51
42
 
52
43
  abstract enterValue(value: string | number | Array<string | number>): Promise<void>;
@@ -1,6 +1,7 @@
1
- import { Answerable, AnswersQuestions, format, List, Question, UsesAbilities } from '@serenity-js/core';
1
+ import { Answerable, format, List, MetaQuestion, Question } from '@serenity-js/core';
2
2
 
3
3
  import { BrowseTheWeb } from '../abilities';
4
+ import { Locator } from './Locator';
4
5
  import { PageElement } from './PageElement';
5
6
  import { Selector } from './selectors';
6
7
 
@@ -8,25 +9,21 @@ const f = format({ markQuestions: true });
8
9
 
9
10
  export class PageElements<Native_Element_Type = any>
10
11
  extends List<PageElement<Native_Element_Type>>
12
+ implements MetaQuestion<Answerable<PageElement<Native_Element_Type>>, Promise<Array<PageElement<Native_Element_Type>>>>
11
13
  {
12
- static located<NET, ST>(selector: Answerable<Selector<ST>>): PageElements<NET> {
13
- return new PageElements(selector);
14
+ static located<NET>(selector: Answerable<Selector>): PageElements<NET> {
15
+ return new PageElements(relativeToDocumentRoot(selector));
14
16
  }
15
17
 
16
18
  /**
17
- * @param {Answerable<Selector<unknown>>} selector
18
- * @param {Answerable<PageElement>} [parent]
19
- * if not specified, browser root selector is used
19
+ * @param locator
20
20
  */
21
- constructor(
22
- protected readonly selector: Answerable<Selector<unknown>>,
23
- private readonly parent?: Answerable<PageElement>
24
- ) {
25
- super(new PageElementCollection<Native_Element_Type>(selector, parent));
21
+ constructor(protected readonly locator: Question<Promise<Locator<Native_Element_Type>>>) {
22
+ super(allElementsOf(locator));
26
23
  }
27
24
 
28
25
  of(parent: Answerable<PageElement<Native_Element_Type>>): PageElements<Native_Element_Type> {
29
- return new PageElements<Native_Element_Type>(this.selector, parent)
26
+ return new PageElements<Native_Element_Type>(relativeToParent(this.locator, parent))
30
27
  .describedAs(`<<${this.toString()}>>` + f`.of(${ parent })`);
31
28
  }
32
29
  }
@@ -34,31 +31,31 @@ export class PageElements<Native_Element_Type = any>
34
31
  /**
35
32
  * @package
36
33
  */
37
- class PageElementCollection<Native_Element_Type = any>
38
- extends Question<Promise<Array<PageElement<Native_Element_Type>>>>
39
- {
40
- private subject: string;
41
- constructor(
42
- private readonly selector: Answerable<Selector<unknown>>,
43
- private readonly parent?: Answerable<PageElement>,
44
- ) {
45
- super();
46
- this.subject = `page elements located ${ selector.toString() }`;
47
- }
48
-
49
- async answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<Array<PageElement<Native_Element_Type>>> {
50
- const bySelector = await actor.answer(this.selector);
51
- const parent = await actor.answer(this.parent);
34
+ function relativeToDocumentRoot<Native_Element_Type>(selector: Answerable<Selector>): Question<Promise<Locator<Native_Element_Type>>> {
35
+ return Question.about(selector.toString(), async actor => {
36
+ const bySelector = await actor.answer(selector);
37
+ return BrowseTheWeb.as(actor).locate(bySelector);
38
+ });
39
+ }
52
40
 
53
- return BrowseTheWeb.as(actor).locateAll(bySelector, parent ? parent.nativeElementLocator() : undefined);
54
- }
41
+ /**
42
+ * @package
43
+ */
44
+ function relativeToParent<Native_Element_Type>(relativeLocator: Answerable<Locator<Native_Element_Type>>, parent: Answerable<PageElement<Native_Element_Type>>): Question<Promise<Locator<Native_Element_Type>>> {
45
+ return Question.about(relativeLocator.toString() + f`.of${ parent }`, async actor => {
46
+ const locator: Locator<Native_Element_Type> = await actor.answer(relativeLocator);
47
+ const parentElement: PageElement<Native_Element_Type> = await actor.answer(parent);
55
48
 
56
- describedAs(subject: string): this {
57
- this.subject = subject;
58
- return this;
59
- }
49
+ return locator.of(parentElement.locator);
50
+ });
51
+ }
60
52
 
61
- toString(): string {
62
- return this.subject;
63
- }
53
+ /**
54
+ * @package
55
+ */
56
+ function allElementsOf<Native_Element_Type>(locator: Question<Promise<Locator<Native_Element_Type>>>): Question<Promise<Array<PageElement<Native_Element_Type>>>> {
57
+ return Question.about(`page elements located ${ locator.toString() }`, async actor => {
58
+ const resolved: Locator<Native_Element_Type> = await actor.answer(locator);
59
+ return resolved.allElements();
60
+ });
64
61
  }
@@ -1,9 +1,8 @@
1
1
  export * from './Cookie';
2
2
  export * from './CookieData';
3
+ export * from './Locator';
3
4
  export * from './ModalDialog';
4
- export * from './NativeElementLocator';
5
5
  export * from './Page';
6
6
  export * from './PageElement';
7
7
  export * from './PageElements';
8
- export * from './PassThroughNativeElementLocator';
9
8
  export * from './selectors';
@@ -1,4 +1,7 @@
1
1
  import { Selector } from './Selector';
2
2
 
3
- export class ByCss extends Selector<string> {
3
+ export class ByCss extends Selector {
4
+ constructor(public readonly value: string) {
5
+ super();
6
+ }
4
7
  }
@@ -1,7 +1,7 @@
1
1
  import { Selector } from './Selector';
2
2
 
3
- export class ByCssContainingText extends Selector<string> {
4
- constructor(selector: string, public readonly text: string) {
5
- super(selector);
3
+ export class ByCssContainingText extends Selector {
4
+ constructor(public readonly value: string, public readonly text: string) {
5
+ super();
6
6
  }
7
7
  }
@@ -1,4 +1,7 @@
1
1
  import { Selector } from './Selector';
2
2
 
3
- export class ById extends Selector<string> {
3
+ export class ById extends Selector {
4
+ constructor(public readonly value: string) {
5
+ super();
6
+ }
4
7
  }
@@ -1,4 +1,7 @@
1
1
  import { Selector } from './Selector';
2
2
 
3
- export class ByTagName extends Selector<string> {
3
+ export class ByTagName extends Selector {
4
+ constructor(public readonly value: string) {
5
+ super();
6
+ }
4
7
  }
@@ -1,4 +1,7 @@
1
1
  import { Selector } from './Selector';
2
2
 
3
- export class ByXPath extends Selector<string> {
3
+ export class ByXPath extends Selector {
4
+ constructor(public readonly value: string) {
5
+ super();
6
+ }
4
7
  }
@@ -2,12 +2,10 @@ import { format } from '@serenity-js/core';
2
2
 
3
3
  const f = format({ markQuestions: true });
4
4
 
5
- export abstract class Selector<T> {
6
-
7
- constructor(public readonly value: T) {
8
- }
5
+ export abstract class Selector {
9
6
 
10
7
  toString(): string {
8
+ // todo: strip anything preceding "By", like "WebdriverIOByCss|
11
9
  const selectorDescription = this.constructor.name.replace(/([a-z]+)([A-Z])/g, '$1 $2').toLowerCase();
12
10
  const parametersDescription = Object.keys(this).map(field => f`${this[field]}`).join(', ');
13
11
 
@@ -1,7 +1,7 @@
1
1
  import { Adapter, Answerable, AnswersQuestions, createAdapter, format, MetaQuestion, Question, UsesAbilities } from '@serenity-js/core';
2
2
  import { asyncMap } from '@serenity-js/core/lib/io';
3
3
 
4
- import { PageElement } from '../models';
4
+ import { PageElement, PageElements } from '../models';
5
5
  import { ElementQuestion } from './ElementQuestion';
6
6
 
7
7
  const f = format({ markQuestions: false });
@@ -107,13 +107,20 @@ export class Text {
107
107
  *
108
108
  * @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
109
109
  */
110
- static ofAll(elements: Answerable<PageElement[]>): Question<Promise<string[]>> & Adapter<string[]> {
111
- return Question.about(f `the text of ${ elements }`, async actor => {
110
+ static ofAll(elements: PageElements): Question<Promise<string[]>> & MetaQuestion<Answerable<PageElement>, Promise<string[]>> & Adapter<string[]>
111
+ static ofAll(elements: Answerable<PageElement[]>): Question<Promise<string[]>> & Adapter<string[]>
112
+ static ofAll(elements: PageElements | Answerable<PageElement[]>): Question<Promise<string[]>> & Adapter<string[]> {
113
+ if (elements instanceof PageElements) {
114
+ return createAdapter<Promise<string[]>, ElementQuestion<Promise<string[]>> & MetaQuestion<Answerable<PageElement>, Promise<string[]>>>(
115
+ new TextOfMultipleElements(elements)
116
+ );
117
+ }
112
118
 
119
+ return Question.about(f `the text of ${ elements }`, async actor => {
113
120
  const pageElements: PageElement[] = await actor.answer(elements);
114
121
 
115
122
  return asyncMap(pageElements, element => element.text());
116
- })
123
+ });
117
124
  }
118
125
  }
119
126
 
@@ -135,3 +142,22 @@ class TextOfSingleElement
135
142
  return element.text();
136
143
  }
137
144
  }
145
+
146
+ class TextOfMultipleElements
147
+ extends ElementQuestion<Promise<string[]>>
148
+ implements MetaQuestion<Answerable<PageElement>, Promise<string[]>>
149
+ {
150
+ constructor(private readonly elements: PageElements) {
151
+ super(f `the text of ${ elements }`);
152
+ }
153
+
154
+ of(parent: Answerable<PageElement>): Question<Promise<string[]>> {
155
+ return new TextOfMultipleElements(this.elements.of(parent));
156
+ }
157
+
158
+ async answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<string[]> {
159
+ const elements: PageElement[] = await actor.answer(this.elements);
160
+
161
+ return asyncMap(elements, element => element.text());
162
+ }
163
+ }
@@ -1,5 +0,0 @@
1
- import { Selector } from './selectors';
2
- export interface NativeElementLocator<Native_Element_Type> {
3
- locate<T>(selector: Selector<T>): Promise<Native_Element_Type>;
4
- locateAll<T>(selector: Selector<T>): Promise<Native_Element_Type[]>;
5
- }
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=NativeElementLocator.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"NativeElementLocator.js","sourceRoot":"","sources":["../../../src/screenplay/models/NativeElementLocator.ts"],"names":[],"mappings":""}
@@ -1,9 +0,0 @@
1
- import { NativeElementLocator } from './NativeElementLocator';
2
- import { Selector } from './selectors';
3
- export declare class PassThroughNativeElementLocator<Native_Element_Type = any> implements NativeElementLocator<Native_Element_Type> {
4
- private readonly locator;
5
- private readonly element;
6
- constructor(locator: NativeElementLocator<Native_Element_Type>, element: Native_Element_Type);
7
- locate<T>(selector: Selector<T>): Promise<Native_Element_Type>;
8
- locateAll<T>(selector: Selector<T>): Promise<Native_Element_Type[]>;
9
- }
@@ -1,17 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PassThroughNativeElementLocator = void 0;
4
- class PassThroughNativeElementLocator {
5
- constructor(locator, element) {
6
- this.locator = locator;
7
- this.element = element;
8
- }
9
- async locate(selector) {
10
- return this.element;
11
- }
12
- locateAll(selector) {
13
- return this.locator.locateAll(selector);
14
- }
15
- }
16
- exports.PassThroughNativeElementLocator = PassThroughNativeElementLocator;
17
- //# sourceMappingURL=PassThroughNativeElementLocator.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PassThroughNativeElementLocator.js","sourceRoot":"","sources":["../../../src/screenplay/models/PassThroughNativeElementLocator.ts"],"names":[],"mappings":";;;AAGA,MAAa,+BAA+B;IACxC,YACqB,OAAkD,EAClD,OAA4B;QAD5B,YAAO,GAAP,OAAO,CAA2C;QAClD,YAAO,GAAP,OAAO,CAAqB;IAEjD,CAAC;IAED,KAAK,CAAC,MAAM,CAAI,QAAqB;QACjC,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;IAED,SAAS,CAAI,QAAqB;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5C,CAAC;CACJ;AAdD,0EAcC"}
@@ -1,6 +0,0 @@
1
- import { Selector } from './selectors';
2
-
3
- export interface NativeElementLocator<Native_Element_Type> {
4
- locate<T>(selector: Selector<T>): Promise<Native_Element_Type>;
5
- locateAll<T>(selector: Selector<T>): Promise<Native_Element_Type[]>;
6
- }
@@ -1,18 +0,0 @@
1
- import { NativeElementLocator } from './NativeElementLocator';
2
- import { Selector } from './selectors';
3
-
4
- export class PassThroughNativeElementLocator<Native_Element_Type = any> implements NativeElementLocator<Native_Element_Type> {
5
- constructor(
6
- private readonly locator: NativeElementLocator<Native_Element_Type>,
7
- private readonly element: Native_Element_Type
8
- ) {
9
- }
10
-
11
- async locate<T>(selector: Selector<T>): Promise<Native_Element_Type> {
12
- return this.element;
13
- }
14
-
15
- locateAll<T>(selector: Selector<T>): Promise<Native_Element_Type[]> {
16
- return this.locator.locateAll(selector);
17
- }
18
- }