@cepharum/contextual-gherkin 3.1.0 → 4.0.0

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/index.d.ts CHANGED
@@ -690,18 +690,43 @@ declare module "@cepharum/contextual-gherkin" {
690
690
  nth( index: number|null ): AbstractContext;
691
691
 
692
692
  /**
693
- * Reduces current context's set of matching elements to those matching
694
- * another selector for given type of element.
693
+ * Limits current set of matching elements to those also matching the
694
+ * selector configured for a given element type, intersecting the current
695
+ * set rather than searching descendants.
695
696
  *
696
- * @note In opposition to filter(), this function isn't descending into
697
- * the hierarchy of elements but inspects currently matching
698
- * elements, only.
697
+ * @param typeOfElement type of element to intersect current matches with
698
+ * @param defaultSelector selector to use if none has been configured for the named type of elements
699
+ * @returns another context representing only those matching elements of current context that also match selector of given type
700
+ */
701
+ filterByType( typeOfElement: string, defaultSelector?: SimpleSelector ): AbstractContext;
702
+
703
+ /**
704
+ * Limits current set of matching elements to those also matching a given
705
+ * selector, intersecting the current set rather than searching descendants.
706
+ *
707
+ * @param selector selector string to intersect current matches with
708
+ * @returns another context representing only those matching elements of current context that also match given selector
709
+ */
710
+ filterBySelector( selector: string ): AbstractContext;
711
+
712
+ /**
713
+ * Limits current set of matching elements to those containing a descendant
714
+ * matching the selector configured for a given element type.
699
715
  *
700
- * @param typeOfElement type of element to reduce matches to
716
+ * @param typeOfElement type of element to look for in descendants
701
717
  * @param defaultSelector selector to use if none has been configured for the named type of elements
702
- * @returns another context representing only those matching elements of current context that match selector of given name, too
718
+ * @returns another context representing only those matching elements of current context that contain a descendant of given type
719
+ */
720
+ filterBySubType( typeOfElement: string, defaultSelector?: SimpleSelector ): AbstractContext;
721
+
722
+ /**
723
+ * Limits current set of matching elements to those containing a descendant
724
+ * matching a given selector.
725
+ *
726
+ * @param selector selector string to look for in descendants
727
+ * @returns another context representing only those matching elements of current context that contain a descendant matching given selector
703
728
  */
704
- filter( typeOfElement: string, defaultSelector?: SimpleSelector ): AbstractContext;
729
+ filterBySubSelector( selector: string ): AbstractContext;
705
730
 
706
731
  /**
707
732
  * Reduces current context's set of matching elements to those marked as
@@ -171,25 +171,78 @@ export class PlaywrightContext extends AbstractContext {
171
171
  }
172
172
 
173
173
  /**
174
- * Limits current set of matches to those matching as a given type of
175
- * elements, too.
174
+ * Limits current set of matches to those also matching a given element
175
+ * type's selector, intersecting the current set rather than searching
176
+ * descendants.
176
177
  *
177
- * @param {string} typeOfElement names type of elements to focus on
178
+ * @param {string} typeOfElement names type of elements to intersect with
178
179
  * @param {SimpleSelector} defaultSelector selector to use as fallback
179
- * @returns {Context} node describing matches of filtering and their processing context
180
+ * @returns {Context} node describing the filtered set of elements
180
181
  */
181
- filter( typeOfElement, defaultSelector = undefined ) {
182
+ filterByType( typeOfElement, defaultSelector = undefined ) {
182
183
  const type = this.api.toNormalizedSingularName( typeOfElement );
183
184
  const { query, dependencies } = this.getSelector( type, defaultSelector );
184
185
 
185
186
  return new this.constructor( this, {
186
- locator: this.locator.filter( { has: this.createSubLocator( query, dependencies ) } ),
187
+ locator: this.locator.and( this.createSubLocator( query, dependencies, true ) ),
187
188
  type: this.type,
188
189
  selectors: this.selectors,
189
190
  cardinality: this.cardinality
190
191
  } );
191
192
  }
192
193
 
194
+ /**
195
+ * Limits current set of matches to those also matching a given selector,
196
+ * intersecting the current set rather than searching descendants.
197
+ *
198
+ * @param {string} selector selector string accepted by Playwright locators
199
+ * @returns {Context} node describing the filtered set of elements
200
+ */
201
+ filterBySelector( selector ) {
202
+ return new this.constructor( this, {
203
+ locator: this.locator.and( this.locator.page().locator( selector ) ),
204
+ type: this.type,
205
+ selectors: this.selectors,
206
+ cardinality: this.cardinality,
207
+ } );
208
+ }
209
+
210
+ /**
211
+ * Limits current set of matches to those containing a descendant matching
212
+ * a given element type's selector.
213
+ *
214
+ * @param {string} typeOfElement names type of elements to look for in descendants
215
+ * @param {SimpleSelector} defaultSelector selector to use as fallback
216
+ * @returns {Context} node describing the filtered set of elements
217
+ */
218
+ filterBySubType( typeOfElement, defaultSelector = undefined ) {
219
+ const type = this.api.toNormalizedSingularName( typeOfElement );
220
+ const { query, dependencies } = this.getSelector( type, defaultSelector );
221
+
222
+ return new this.constructor( this, {
223
+ locator: this.locator.filter( { has: this.createSubLocator( query, dependencies ) } ),
224
+ type: this.type,
225
+ selectors: this.selectors,
226
+ cardinality: this.cardinality,
227
+ } );
228
+ }
229
+
230
+ /**
231
+ * Limits current set of matches to those containing a descendant matching
232
+ * a given selector.
233
+ *
234
+ * @param {string} selector selector string accepted by Playwright locators
235
+ * @returns {Context} node describing the filtered set of elements
236
+ */
237
+ filterBySubSelector( selector ) {
238
+ return new this.constructor( this, {
239
+ locator: this.locator.filter( { has: this.locator.page().locator( selector ) } ),
240
+ type: this.type,
241
+ selectors: this.selectors,
242
+ cardinality: this.cardinality,
243
+ } );
244
+ }
245
+
193
246
  /**
194
247
  * Limits current set of matches to those picked by a given callback
195
248
  * function invoked in browser.
@@ -10,6 +10,8 @@ export async function registerPlaywrightSelectors() {
10
10
  registerAttributeSelector(),
11
11
  registerShadowQuerySelector(),
12
12
  registerMeSelector(),
13
+ registerWidthSelector(),
14
+ registerHeightSelector(),
13
15
  ] );
14
16
  }
15
17
 
@@ -141,6 +143,116 @@ export function registerShadowQuerySelector() {
141
143
  } ) );
142
144
  }
143
145
 
146
+ /**
147
+ * Registers custom selector engine `cg-width` for locating elements whose
148
+ * rendered width satisfies a numeric constraint.
149
+ *
150
+ * Query syntax: <value> exact match (px)
151
+ * >=<value> minimum width (px)
152
+ * <=<value> maximum width (px)
153
+ *
154
+ * When the engine root is an Element it is tested directly. When the root is
155
+ * a Document or ShadowRoot all descendant elements are searched instead.
156
+ */
157
+ export function registerWidthSelector() {
158
+ return selectors.register( "cg-width", () => ( {
159
+ // client function, runs in browser, can't reliably use shared code other than what is injected as init script
160
+ query( root, query ) {
161
+ const [ , op, value ] = /^(>=|<=)?(\d+(?:\.\d+)?)$/.exec( query ) || [];
162
+ if ( value == null ) {
163
+ throw new TypeError( "malformed query for matching elements by width" );
164
+ }
165
+
166
+ const target = parseFloat( value );
167
+ const test = node => {
168
+ const actual = node.getBoundingClientRect().width;
169
+ return op === ">=" ? actual >= target : op === "<=" ? actual <= target : actual === target;
170
+ };
171
+
172
+ if ( typeof root.getBoundingClientRect === "function" ) {
173
+ return test( root ) ? root : undefined;
174
+ }
175
+
176
+ return Array.from( root.querySelectorAll( "*" ) ).find( test );
177
+ },
178
+
179
+ // client function, runs in browser, can't reliably use shared code other than what is injected as init script
180
+ queryAll( root, query ) {
181
+ const [ , op, value ] = /^(>=|<=)?(\d+(?:\.\d+)?)$/.exec( query ) || [];
182
+ if ( value == null ) {
183
+ throw new TypeError( "malformed query for matching elements by width" );
184
+ }
185
+
186
+ const target = parseFloat( value );
187
+ const test = node => {
188
+ const actual = node.getBoundingClientRect().width;
189
+ return op === ">=" ? actual >= target : op === "<=" ? actual <= target : actual === target;
190
+ };
191
+
192
+ if ( typeof root.getBoundingClientRect === "function" ) {
193
+ return test( root ) ? [root] : [];
194
+ }
195
+
196
+ return Array.from( root.querySelectorAll( "*" ) ).filter( test );
197
+ },
198
+ } ) );
199
+ }
200
+
201
+ /**
202
+ * Registers custom selector engine `cg-height` for locating elements whose
203
+ * rendered height satisfies a numeric constraint.
204
+ *
205
+ * Query syntax: <value> exact match (px)
206
+ * >=<value> minimum height (px)
207
+ * <=<value> maximum height (px)
208
+ *
209
+ * When the engine root is an Element it is tested directly. When the root is
210
+ * a Document or ShadowRoot all descendant elements are searched instead.
211
+ */
212
+ export function registerHeightSelector() {
213
+ return selectors.register( "cg-height", () => ( {
214
+ // client function, runs in browser, can't reliably use shared code other than what is injected as init script
215
+ query( root, query ) {
216
+ const [ , op, value ] = /^(>=|<=)?(\d+(?:\.\d+)?)$/.exec( query ) || [];
217
+ if ( value == null ) {
218
+ throw new TypeError( "malformed query for matching elements by height" );
219
+ }
220
+
221
+ const target = parseFloat( value );
222
+ const test = node => {
223
+ const actual = node.getBoundingClientRect().height;
224
+ return op === ">=" ? actual >= target : op === "<=" ? actual <= target : actual === target;
225
+ };
226
+
227
+ if ( typeof root.getBoundingClientRect === "function" ) {
228
+ return test( root ) ? root : undefined;
229
+ }
230
+
231
+ return Array.from( root.querySelectorAll( "*" ) ).find( test );
232
+ },
233
+
234
+ // client function, runs in browser, can't reliably use shared code other than what is injected as init script
235
+ queryAll( root, query ) {
236
+ const [ , op, value ] = /^(>=|<=)?(\d+(?:\.\d+)?)$/.exec( query ) || [];
237
+ if ( value == null ) {
238
+ throw new TypeError( "malformed query for matching elements by height" );
239
+ }
240
+
241
+ const target = parseFloat( value );
242
+ const test = node => {
243
+ const actual = node.getBoundingClientRect().height;
244
+ return op === ">=" ? actual >= target : op === "<=" ? actual <= target : actual === target;
245
+ };
246
+
247
+ if ( typeof root.getBoundingClientRect === "function" ) {
248
+ return test( root ) ? [root] : [];
249
+ }
250
+
251
+ return Array.from( root.querySelectorAll( "*" ) ).filter( test );
252
+ },
253
+ } ) );
254
+ }
255
+
144
256
  /**
145
257
  * Registers custom selector for locating current root element itself primarily
146
258
  * to feature its combination with other locators.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cepharum/contextual-gherkin",
3
- "version": "3.1.0",
3
+ "version": "4.0.0",
4
4
  "description": "flexible step definitions for Gherkin",
5
5
  "author": "cepharum GmbH <thomas.urban@cepharum.de>",
6
6
  "homepage": "https://cepharum-foss.gitlab.io/contextual-gherkin/",