Skip to content

Commit

Permalink
query
Browse files Browse the repository at this point in the history
use Query directly in finders in order to get rid of build selector

- breaking: ignore trailing spaces in selectors

- restore comma-separated exception
  • Loading branch information
ro0gr committed Oct 15, 2023
1 parent 735c79d commit d3f5d00
Show file tree
Hide file tree
Showing 15 changed files with 1,172 additions and 651 deletions.
54 changes: 54 additions & 0 deletions addon/src/-private/build-selector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Query } from './query';

/**
* @public
*
* Builds a CSS selector from a target selector and a PageObject or a node in a PageObject, along with optional parameters.
*
* @example
*
* const component = PageObject.create({ scope: '.component'});
*
* buildSelector(component, '.my-element');
* // returns '.component .my-element'
*
* @example
*
* const page = PageObject.create({});
*
* buildSelector(page, '.my-element', { at: 0 });
* // returns '.my-element:eq(0)'
*
* @example
*
* const page = PageObject.create({});
*
* buildSelector(page, '.my-element', { contains: "Example" });
* // returns ".my-element :contains('Example')"
*
* @example
*
* const page = PageObject.create({});
*
* buildSelector(page, '.my-element', { last: true });
* // returns '.my-element:last'
*
* @param {Ceibo} node - Node of the tree
* @param {string} targetSelector - CSS selector
* @param {Object} options - Additional options
* @param {boolean} options.resetScope - Do not use inherited scope
* @param {string} options.contains - Filter by using :contains('foo') pseudo-class
* @param {number} options.at - Filter by index using :eq(x) pseudo-class
* @param {boolean} options.last - Filter by using :last pseudo-class
* @param {boolean} options.visible - Filter by using :visible pseudo-class
* @return {string} Fully qualified selector
*/

export function buildSelector(node, targetSelector, options) {
const q = new Query(node, {
...options,
selector: targetSelector,
});

return q.toString();
}
90 changes: 52 additions & 38 deletions addon/src/-private/finders.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
import { buildSelector, findClosestValue, guardMultiple } from './helpers';
import { getAdapter } from '../adapters/index';
import { guardMultiple } from './helpers';
import { $ } from './jquery';
import { throwBetterError, ELEMENT_NOT_FOUND } from './better-errors';

function getContainer(pageObjectNode, options) {
return (
options.testContainer ||
findClosestValue(pageObjectNode, 'testContainer') ||
getAdapter().testContainer
);
}
import { Query } from './query';

/**
* Finds a single element, otherwise fails
*
* @private
*/
export function findOne(pageObjectNode, targetSelector, options = {}) {
const selector = buildSelector(pageObjectNode, targetSelector, options);
const container = getContainer(pageObjectNode, options);
export function findOne(pageObjectNode, selector, options = {}) {
const query = new Query(
pageObjectNode,
normalizeOptions({
...options,
selector,
})
);

const elements = $(selector, container).toArray();
const elements = query.all();

guardMultiple(elements, selector);
guardMultiple(elements, query);

if (elements.length === 0) {
throwBetterError(pageObjectNode, options.pageObjectKey, ELEMENT_NOT_FOUND, {
selector,
selector: query.toString(),
});
}

Expand All @@ -38,49 +35,66 @@ export function findOne(pageObjectNode, targetSelector, options = {}) {
*
* @private
*/
export function findMany(pageObjectNode, targetSelector, options = {}) {
const selector = buildSelector(pageObjectNode, targetSelector, options);
const container = getContainer(pageObjectNode, options);
export function findMany(pageObjectNode, selector, options = {}) {
const query = new Query(
pageObjectNode,
normalizeOptions({
...options,
selector,
})
);

return $(selector, container).toArray();
return query.all();
}

/**
* @private
* @deprecated
*/
export function findElementWithAssert(
pageObjectNode,
targetSelector,
options = {}
) {
const selector = buildSelector(pageObjectNode, targetSelector, options);
const container = getContainer(pageObjectNode, options);
export function findElementWithAssert(pageObjectNode, selector, options = {}) {
const query = new Query(
pageObjectNode,
normalizeOptions({
...options,
selector,
})
);

let $elements = $(selector, container);
let elements = query.all();

guardMultiple($elements, selector, options.multiple);
guardMultiple(elements, query, options.multiple);

if ($elements.length === 0) {
if (elements.length === 0) {
throwBetterError(pageObjectNode, options.pageObjectKey, ELEMENT_NOT_FOUND, {
selector,
selector: query.toString(),
});
}

return $elements;
return $(elements);
}

/**
* @private
* @deprecated
*/
export function findElement(pageObjectNode, targetSelector, options = {}) {
const selector = buildSelector(pageObjectNode, targetSelector, options);
const container = getContainer(pageObjectNode, options);
export function findElement(pageObjectNode, selector, options = {}) {
const elements = findMany(pageObjectNode, selector, options);

let $elements = $(selector, container);
return $(elements);
}

function normalizeOptions(options) {
if (options && options.scope) {
// TODO: deprecate options.scope
const selector = [options.scope, options.selector]
.filter(Boolean)
.join(' ');

guardMultiple($elements, selector, options.multiple);
return {
...options,
selector,
};
}

return $elements;
return options;
}
45 changes: 3 additions & 42 deletions addon/src/-private/helpers.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import Ceibo from '@ro0gr/ceibo';
function isPresent(value) {
return typeof value !== 'undefined';
}
import { Query } from './query';

class Selector {
constructor(node, scope, selector, filters) {
Expand Down Expand Up @@ -159,21 +157,6 @@ export function getRoot(node) {
return root;
}

function getAllValuesForProperty(node, property) {
let iterator = node;
let values = [];

while (isPresent(iterator)) {
if (isPresent(iterator[property])) {
values.push(iterator[property]);
}

iterator = Ceibo.parent(iterator);
}

return values;
}

/**
* @public
*
Expand All @@ -183,29 +166,7 @@ function getAllValuesForProperty(node, property) {
* @return {string} Full scope of node
*/
export function fullScope(node) {
let scopes = getAllValuesForProperty(node, 'scope');
const q = new Query(node);

return scopes.reverse().join(' ');
}

/**
* @public
*
* Returns the value of property defined on the closest ancestor of given
* node.
*
* @param {Ceibo} node - Node of the tree
* @param {string} property - Property to look for
* @return {?Object} The value of property on closest node to the given node
*/
export function findClosestValue(node, property) {
if (isPresent(node[property])) {
return node[property];
}

let parent = Ceibo.parent(node);

if (isPresent(parent)) {
return findClosestValue(parent, property);
}
return q.toString();
}
Loading

0 comments on commit d3f5d00

Please sign in to comment.