@serenity-js/web 3.6.1 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [3.7.1](https://github.com/serenity-js/serenity-js/compare/v3.7.0...v3.7.1) (2023-07-22)
7
+
8
+ **Note:** Version bump only for package @serenity-js/web
9
+
10
+
11
+
12
+
13
+
14
+ # [3.7.0](https://github.com/serenity-js/serenity-js/compare/v3.6.1...v3.7.0) (2023-07-20)
15
+
16
+
17
+ ### Features
18
+
19
+ * **web:** new PageElement Query Language API - parentElement.closestTo(childElement) ([7d48fd8](https://github.com/serenity-js/serenity-js/commit/7d48fd8c1dcda6cbd5f8d0579e4cce129b24618f))
20
+
21
+
22
+
23
+
24
+
6
25
  ## [3.6.1](https://github.com/serenity-js/serenity-js/compare/v3.6.0...v3.6.1) (2023-07-11)
7
26
 
8
27
  **Note:** Version bump only for package @serenity-js/web
@@ -2,7 +2,7 @@
2
2
  import * as util from 'util';
3
3
  import type { PageElement } from './PageElement';
4
4
  import { RootLocator } from './RootLocator';
5
- import type { Selector } from './selectors';
5
+ import { ByCss, type Selector } from './selectors';
6
6
  /**
7
7
  * {@apilink Locator} uses a {@apilink Selector} to locate a {@apilink PageElement} or {@apilink PageElements}
8
8
  * within the {@apilink Page}.
@@ -26,7 +26,18 @@ export declare abstract class Locator<Native_Element_Type, Native_Selector_Type
26
26
  switchToMainFrame(): Promise<void>;
27
27
  protected abstract nativeSelector(): Native_Selector_Type;
28
28
  abstract of(parent: RootLocator<Native_Element_Type>): Locator<Native_Element_Type>;
29
+ abstract closestTo(child: Locator<Native_Element_Type>): Locator<Native_Element_Type>;
29
30
  abstract locate(child: Locator<Native_Element_Type>): Locator<Native_Element_Type>;
31
+ /**
32
+ * Expresses {@apilink ByCss}, {@apilink ById}, or {@apilink ByTagName} as a {@apilink ByCss} selector.
33
+ *
34
+ * @throws LogicError
35
+ * if the `selector` can't be expressed as {@apilink ByCss}
36
+ *
37
+ * @param selector
38
+ * @protected
39
+ */
40
+ protected asCssSelector(selector: Selector): ByCss;
30
41
  abstract element(): PageElement<Native_Element_Type>;
31
42
  abstract allElements(): Promise<Array<PageElement<Native_Element_Type>>>;
32
43
  toString(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"Locator.d.ts","sourceRoot":"","sources":["../../../src/screenplay/models/Locator.ts"],"names":[],"mappings":";AACA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;;;;;GAWG;AACH,8BAAsB,OAAO,CAAC,mBAAmB,EAAE,oBAAoB,GAAG,GAAG,CACzE,SAAQ,WAAW,CAAC,mBAAmB,CAAC;IAGpC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,mBAAmB,CAAC;aAC3C,QAAQ,EAAE,QAAQ;IAFtC,SAAS,aACc,MAAM,EAAE,WAAW,CAAC,mBAAmB,CAAC,EAC3C,QAAQ,EAAE,QAAQ;aAKtB,aAAa,IAAI,OAAO,CAAC,mBAAmB,CAAC;aAC7C,iBAAiB,IAAI,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAElE,aAAa,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1D,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIpC,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxC,SAAS,CAAC,QAAQ,CAAC,cAAc,IAAI,oBAAoB;IAEzD,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,mBAAmB,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC;IACnF,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAElF,QAAQ,CAAC,OAAO,IAAI,WAAW,CAAC,mBAAmB,CAAC;IAEpD,QAAQ,CAAC,WAAW,IAAI,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAExE,QAAQ,IAAI,MAAM;IAIlB;;OAEG;IACH,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,gGAAiE;CACzF"}
1
+ {"version":3,"file":"Locator.d.ts","sourceRoot":"","sources":["../../../src/screenplay/models/Locator.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAmB,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEpE;;;;;;;;;;;GAWG;AACH,8BAAsB,OAAO,CAAC,mBAAmB,EAAE,oBAAoB,GAAG,GAAG,CACzE,SAAQ,WAAW,CAAC,mBAAmB,CAAC;IAGpC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,mBAAmB,CAAC;aAC3C,QAAQ,EAAE,QAAQ;IAFtC,SAAS,aACc,MAAM,EAAE,WAAW,CAAC,mBAAmB,CAAC,EAC3C,QAAQ,EAAE,QAAQ;aAKtB,aAAa,IAAI,OAAO,CAAC,mBAAmB,CAAC;aAC7C,iBAAiB,IAAI,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAElE,aAAa,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1D,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIpC,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxC,SAAS,CAAC,QAAQ,CAAC,cAAc,IAAI,oBAAoB;IAEzD,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,mBAAmB,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC;IACnF,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC;IACrF,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAElF;;;;;;;;OAQG;IACH,SAAS,CAAC,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK;IAgBlD,QAAQ,CAAC,OAAO,IAAI,WAAW,CAAC,mBAAmB,CAAC;IAEpD,QAAQ,CAAC,WAAW,IAAI,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAExE,QAAQ,IAAI,MAAM;IAIlB;;OAEG;IACH,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,gGAAiE;CACzF"}
@@ -25,9 +25,11 @@ var __importStar = (this && this.__importStar) || function (mod) {
25
25
  var _a;
26
26
  Object.defineProperty(exports, "__esModule", { value: true });
27
27
  exports.Locator = void 0;
28
+ const core_1 = require("@serenity-js/core");
28
29
  const io_1 = require("@serenity-js/core/lib/io");
29
30
  const util = __importStar(require("util")); // eslint-disable-line unicorn/import-style
30
31
  const RootLocator_1 = require("./RootLocator");
32
+ const selectors_1 = require("./selectors");
31
33
  /**
32
34
  * {@apilink Locator} uses a {@apilink Selector} to locate a {@apilink PageElement} or {@apilink PageElements}
33
35
  * within the {@apilink Page}.
@@ -59,6 +61,27 @@ class Locator extends RootLocator_1.RootLocator {
59
61
  async switchToMainFrame() {
60
62
  await this.parent.switchToMainFrame();
61
63
  }
64
+ /**
65
+ * Expresses {@apilink ByCss}, {@apilink ById}, or {@apilink ByTagName} as a {@apilink ByCss} selector.
66
+ *
67
+ * @throws LogicError
68
+ * if the `selector` can't be expressed as {@apilink ByCss}
69
+ *
70
+ * @param selector
71
+ * @protected
72
+ */
73
+ asCssSelector(selector) {
74
+ if (selector instanceof selectors_1.ByCss) {
75
+ return selector;
76
+ }
77
+ if (selector instanceof selectors_1.ById) {
78
+ return new selectors_1.ByCss(`#${selector.value}`);
79
+ }
80
+ if (selector instanceof selectors_1.ByTagName) {
81
+ return new selectors_1.ByCss(`${selector.value}`);
82
+ }
83
+ throw new core_1.LogicError(`${selector} can't be expressed as a CSS selector`);
84
+ }
62
85
  toString() {
63
86
  return this.selector.toString();
64
87
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Locator.js","sourceRoot":"","sources":["../../../src/screenplay/models/Locator.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iDAA2D;AAC3D,2CAA6B,CAAC,2CAA2C;AAGzE,+CAA4C;AAG5C;;;;;;;;;;;GAWG;AACH,MAAsB,OAClB,SAAQ,yBAAgC;IAExC,YACuB,MAAwC,EAC3C,QAAkB;QAElC,KAAK,EAAE,CAAC;QAHW,WAAM,GAAN,MAAM,CAAkC;QAC3C,aAAQ,GAAR,QAAQ,CAAU;QAiCtC;;WAEG;QACH,QAAqB,GAAG,IAAA,oBAAe,EAAC,IAAI,EAAE,CAAE,QAAsB,EAAE,UAAU,CAAE,CAAC,CAAC;IAjCtF,CAAC;IAKD,KAAK,CAAC,aAAa,CAAC,OAA4B;QAC5C,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,mBAAmB;QACrB,MAAM,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,iBAAiB;QACnB,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;IAC1C,CAAC;IAWD,QAAQ;QACJ,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;IACpC,CAAC;CAMJ;AA1CD,0BA0CC;KADI,IAAI,CAAC,OAAO,CAAC,MAAM"}
1
+ {"version":3,"file":"Locator.js","sourceRoot":"","sources":["../../../src/screenplay/models/Locator.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,4CAA+C;AAC/C,iDAA2D;AAC3D,2CAA6B,CAAC,2CAA2C;AAGzE,+CAA4C;AAC5C,2CAAoE;AAEpE;;;;;;;;;;;GAWG;AACH,MAAsB,OAClB,SAAQ,yBAAgC;IAExC,YACuB,MAAwC,EAC3C,QAAkB;QAElC,KAAK,EAAE,CAAC;QAHW,WAAM,GAAN,MAAM,CAAkC;QAC3C,aAAQ,GAAR,QAAQ,CAAU;QA2DtC;;WAEG;QACH,QAAqB,GAAG,IAAA,oBAAe,EAAC,IAAI,EAAE,CAAE,QAAsB,EAAE,UAAU,CAAE,CAAC,CAAC;IA3DtF,CAAC;IAKD,KAAK,CAAC,aAAa,CAAC,OAA4B;QAC5C,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,mBAAmB;QACrB,MAAM,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,iBAAiB;QACnB,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;IAC1C,CAAC;IAQD;;;;;;;;OAQG;IACO,aAAa,CAAC,QAAkB;QACtC,IAAI,QAAQ,YAAY,iBAAK,EAAE;YAC3B,OAAO,QAAQ,CAAC;SACnB;QAED,IAAI,QAAQ,YAAY,gBAAI,EAAE;YAC1B,OAAO,IAAI,iBAAK,CAAC,IAAK,QAAQ,CAAC,KAAM,EAAE,CAAC,CAAC;SAC5C;QAED,IAAI,QAAQ,YAAY,qBAAS,EAAE;YAC/B,OAAO,IAAI,iBAAK,CAAC,GAAI,QAAQ,CAAC,KAAM,EAAE,CAAC,CAAC;SAC3C;QAED,MAAM,IAAI,iBAAU,CAAC,GAAI,QAAS,uCAAuC,CAAC,CAAA;IAC9E,CAAC;IAMD,QAAQ;QACJ,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;IACpC,CAAC;CAMJ;AApED,0BAoEC;KADI,IAAI,CAAC,OAAO,CAAC,MAAM"}
@@ -21,7 +21,63 @@ export declare abstract class PageElement<Native_Element_Type = any> implements
21
21
  static located<NET>(selector: Answerable<Selector>): QuestionAdapter<PageElement<NET>> & MetaQuestion<PageElement, PageElement>;
22
22
  static of<NET>(childElement: Answerable<PageElement<NET>>, parentElement: Answerable<PageElement<NET>>): QuestionAdapter<PageElement<NET>> & MetaQuestion<PageElement, PageElement>;
23
23
  constructor(locator: Locator<Native_Element_Type>);
24
+ /**
25
+ * Locates a child element that:
26
+ * - matches the given selector
27
+ * - is located within the `parentElement`
28
+ *
29
+ * @param parentElement
30
+ */
24
31
  abstract of(parentElement: PageElement<Native_Element_Type>): PageElement<Native_Element_Type>;
32
+ /**
33
+ * Traverses the element and its parents, heading toward the document root,
34
+ * until it finds a parent {@apilink PageElement} that matches its associated CSS selector.
35
+ *
36
+ * #### Example
37
+ *
38
+ * ```html
39
+ * <div class="form-entry">
40
+ * <input id="username" />
41
+ * <ul class="warnings">
42
+ * <li>Username should be an email address</li>
43
+ * </ul>
44
+ * </div>
45
+ * ```
46
+ *
47
+ * ```typescript
48
+ * class Username {
49
+ * static field = () =>
50
+ * PageElement.located(By.id('username'))
51
+ * .describedAs('username field')
52
+ *
53
+ * private static container = () =>
54
+ * PageElement.located(By.css('.form-entry'))
55
+ * .describedAs('form entry container')
56
+ *
57
+ * static warnings = () =>
58
+ * PageElements.located(By.css('ul.warnings li'))
59
+ * .describedAs('warnings')
60
+ * .of(
61
+ * Username.container().closestTo(Username.field())
62
+ * )
63
+ * }
64
+ * ```
65
+ *
66
+ * :::info
67
+ * This method relies on [Element: closest() API](https://developer.mozilla.org/en-US/docs/Web/API/Element/closest),
68
+ * and so is only compatible with locating parent elements specified using the following CSS selectors:
69
+ * - {@apilink ByCss}
70
+ * - {@apilink ById}
71
+ * - {@apilink ByTagName}
72
+ * :::
73
+ *
74
+ * @param childElement
75
+ * @returns
76
+ *
77
+ * #### Learn more
78
+ * - [Element: closest() method](https://developer.mozilla.org/en-US/docs/Web/API/Element/closest)
79
+ */
80
+ abstract closestTo(childElement: PageElement<Native_Element_Type>): PageElement<Native_Element_Type>;
25
81
  /**
26
82
  * An "escape hatch" providing access to the integration tool-specific implementation of a Web element.
27
83
  */
@@ -1 +1 @@
1
- {"version":3,"file":"PageElement.d.ts","sourceRoot":"","sources":["../../../src/screenplay/models/PageElement.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PageElement.d.ts","sourceRoot":"","sources":["../../../src/screenplay/models/PageElement.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"PageElement.js","sourceRoot":"","sources":["../../../src/screenplay/models/PageElement.ts"],"names":[],"mappings":";;;AACA,4CAAgD;AAChD,2CAA+C;AAE/C,4CAA4C;AAO5C;;;;;;;;;;GAUG;AACH,MAAsB,WAAW;IAE7B,MAAM,CAAC,IAAI,CAAM,aAAkB;QAC/B,OAAO,eAAQ,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YACvD,MAAM,WAAW,GAAG,MAAM,wBAAY,CAAC,EAAE,CAAoB,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;YAElF,OAAO,WAAW,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;QACxD,CAAC,CAA+E,CAAC;IACrF,CAAC;IAED,MAAM,CAAC,OAAO,CAAM,QAA8B;QAC9C,OAAO,eAAQ,CAAC,KAAK,CAAC,IAAA,QAAC,EAAA,wBAAyB,QAAS,EAAE,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YACvE,MAAM,UAAU,GAAI,MAAM,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACjD,MAAM,WAAW,GAAG,MAAM,wBAAY,CAAC,EAAE,CAAoB,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;YAElF,OAAO,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC1C,CAAC,CAA+E,CAAC;IACrF,CAAC;IAED,MAAM,CAAC,EAAE,CAAM,YAA0C,EAAE,aAA2C;QAClG,OAAO,eAAQ,CAAC,KAAK,CAAC,IAAA,QAAC,EAAA,GAAI,YAAa,OAAQ,aAAc,EAAE,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YAC5E,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,CAA+E,CAAC;IACrF,CAAC;IAED,YAA4B,OAAqC;QAArC,YAAO,GAAP,OAAO,CAA8B;QAC7D,IAAA,mBAAM,EAAC,wBAAwB,EAAE,OAAO,EAAE,IAAA,sBAAS,GAAE,CAAC,CAAC;IAC3D,CAAC;IAID;;OAEG;IACH,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;IAuDD;;;;OAIG;IACH,KAAK,CAAC,SAAS;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;IACpC,CAAC;CAoBJ;AA7HD,kCA6HC"}
1
+ {"version":3,"file":"PageElement.js","sourceRoot":"","sources":["../../../src/screenplay/models/PageElement.ts"],"names":[],"mappings":";;;AACA,4CAAgD;AAChD,2CAA+C;AAE/C,4CAA4C;AAO5C;;;;;;;;;;GAUG;AACH,MAAsB,WAAW;IAE7B,MAAM,CAAC,IAAI,CAAM,aAAkB;QAC/B,OAAO,eAAQ,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YACvD,MAAM,WAAW,GAAG,MAAM,wBAAY,CAAC,EAAE,CAAoB,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;YAElF,OAAO,WAAW,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;QACxD,CAAC,CAA+E,CAAC;IACrF,CAAC;IAED,MAAM,CAAC,OAAO,CAAM,QAA8B;QAC9C,OAAO,eAAQ,CAAC,KAAK,CAAC,IAAA,QAAC,EAAA,wBAAyB,QAAS,EAAE,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YACvE,MAAM,UAAU,GAAI,MAAM,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACjD,MAAM,WAAW,GAAG,MAAM,wBAAY,CAAC,EAAE,CAAoB,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;YAElF,OAAO,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC1C,CAAC,CAA+E,CAAC;IACrF,CAAC;IAED,MAAM,CAAC,EAAE,CAAM,YAA0C,EAAE,aAA2C;QAClG,OAAO,eAAQ,CAAC,KAAK,CAAC,IAAA,QAAC,EAAA,GAAI,YAAa,OAAQ,aAAc,EAAE,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YAC5E,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,CAA+E,CAAC;IACrF,CAAC;IAED,YAA4B,OAAqC;QAArC,YAAO,GAAP,OAAO,CAA8B;QAC7D,IAAA,mBAAM,EAAC,wBAAwB,EAAE,OAAO,EAAE,IAAA,sBAAS,GAAE,CAAC,CAAC;IAC3D,CAAC;IA6DD;;OAEG;IACH,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;IAuDD;;;;OAIG;IACH,KAAK,CAAC,SAAS;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;IACpC,CAAC;CAoBJ;AAtLD,kCAsLC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serenity-js/web",
3
- "version": "3.6.1",
3
+ "version": "3.7.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.6.1",
48
- "@serenity-js/core": "3.6.1",
47
+ "@serenity-js/assertions": "3.7.1",
48
+ "@serenity-js/core": "3.7.1",
49
49
  "tiny-types": "^1.20.0"
50
50
  },
51
51
  "devDependencies": {
@@ -58,5 +58,5 @@
58
58
  "ts-node": "^10.9.1",
59
59
  "typescript": "^5.1.6"
60
60
  },
61
- "gitHead": "3e7242840af96081c63d12b116e3f95f74b97160"
61
+ "gitHead": "6a67f2edc2872be27b849402a1109f0944e019bb"
62
62
  }
@@ -1,9 +1,10 @@
1
+ import { LogicError } from '@serenity-js/core';
1
2
  import { inspectedObject } from '@serenity-js/core/lib/io';
2
3
  import * as util from 'util'; // eslint-disable-line unicorn/import-style
3
4
 
4
5
  import type { PageElement } from './PageElement';
5
6
  import { RootLocator } from './RootLocator';
6
- import type { Selector } from './selectors';
7
+ import { ByCss, ById, ByTagName, type Selector } from './selectors';
7
8
 
8
9
  /**
9
10
  * {@apilink Locator} uses a {@apilink Selector} to locate a {@apilink PageElement} or {@apilink PageElements}
@@ -45,8 +46,34 @@ export abstract class Locator<Native_Element_Type, Native_Selector_Type = any>
45
46
  protected abstract nativeSelector(): Native_Selector_Type;
46
47
 
47
48
  abstract of(parent: RootLocator<Native_Element_Type>): Locator<Native_Element_Type>;
49
+ abstract closestTo(child: Locator<Native_Element_Type>): Locator<Native_Element_Type>;
48
50
  abstract locate(child: Locator<Native_Element_Type>): Locator<Native_Element_Type>;
49
51
 
52
+ /**
53
+ * Expresses {@apilink ByCss}, {@apilink ById}, or {@apilink ByTagName} as a {@apilink ByCss} selector.
54
+ *
55
+ * @throws LogicError
56
+ * if the `selector` can't be expressed as {@apilink ByCss}
57
+ *
58
+ * @param selector
59
+ * @protected
60
+ */
61
+ protected asCssSelector(selector: Selector): ByCss {
62
+ if (selector instanceof ByCss) {
63
+ return selector;
64
+ }
65
+
66
+ if (selector instanceof ById) {
67
+ return new ByCss(`#${ selector.value }`);
68
+ }
69
+
70
+ if (selector instanceof ByTagName) {
71
+ return new ByCss(`${ selector.value }`);
72
+ }
73
+
74
+ throw new LogicError(`${ selector } can't be expressed as a CSS selector`)
75
+ }
76
+
50
77
  abstract element(): PageElement<Native_Element_Type>;
51
78
 
52
79
  abstract allElements(): Promise<Array<PageElement<Native_Element_Type>>>;
@@ -52,8 +52,65 @@ export abstract class PageElement<Native_Element_Type = any> implements Optional
52
52
  ensure('native element locator', locator, isDefined());
53
53
  }
54
54
 
55
+ /**
56
+ * Locates a child element that:
57
+ * - matches the given selector
58
+ * - is located within the `parentElement`
59
+ *
60
+ * @param parentElement
61
+ */
55
62
  abstract of(parentElement: PageElement<Native_Element_Type>): PageElement<Native_Element_Type>;
56
63
 
64
+ /**
65
+ * Traverses the element and its parents, heading toward the document root,
66
+ * until it finds a parent {@apilink PageElement} that matches its associated CSS selector.
67
+ *
68
+ * #### Example
69
+ *
70
+ * ```html
71
+ * <div class="form-entry">
72
+ * <input id="username" />
73
+ * <ul class="warnings">
74
+ * <li>Username should be an email address</li>
75
+ * </ul>
76
+ * </div>
77
+ * ```
78
+ *
79
+ * ```typescript
80
+ * class Username {
81
+ * static field = () =>
82
+ * PageElement.located(By.id('username'))
83
+ * .describedAs('username field')
84
+ *
85
+ * private static container = () =>
86
+ * PageElement.located(By.css('.form-entry'))
87
+ * .describedAs('form entry container')
88
+ *
89
+ * static warnings = () =>
90
+ * PageElements.located(By.css('ul.warnings li'))
91
+ * .describedAs('warnings')
92
+ * .of(
93
+ * Username.container().closestTo(Username.field())
94
+ * )
95
+ * }
96
+ * ```
97
+ *
98
+ * :::info
99
+ * This method relies on [Element: closest() API](https://developer.mozilla.org/en-US/docs/Web/API/Element/closest),
100
+ * and so is only compatible with locating parent elements specified using the following CSS selectors:
101
+ * - {@apilink ByCss}
102
+ * - {@apilink ById}
103
+ * - {@apilink ByTagName}
104
+ * :::
105
+ *
106
+ * @param childElement
107
+ * @returns
108
+ *
109
+ * #### Learn more
110
+ * - [Element: closest() method](https://developer.mozilla.org/en-US/docs/Web/API/Element/closest)
111
+ */
112
+ abstract closestTo(childElement: PageElement<Native_Element_Type>): PageElement<Native_Element_Type>;
113
+
57
114
  /**
58
115
  * An "escape hatch" providing access to the integration tool-specific implementation of a Web element.
59
116
  */