Skip to content

Commit

Permalink
Provide selector methods for filtering nodes by visibility (closes De…
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexKamaev authored and kirovboris committed Dec 18, 2019
1 parent 9cdebd8 commit e12ffe7
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 17 deletions.
12 changes: 12 additions & 0 deletions src/client-functions/selectors/add-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,18 @@ function addFilterMethods (obj, getSelector, SelectorBuilder) {

return createDerivativeSelectorWithFilter(getSelector, SelectorBuilder, selectorFn, filter);
};

obj.filterVisible = () => {
const builder = new SelectorBuilder(getSelector(), { filterVisible: true }, { instantiation: 'Selector' });

return builder.getFunction();
};

obj.filterHidden = () => {
const builder = new SelectorBuilder(getSelector(), { filterHidden: true }, { instantiation: 'Selector' });

return builder.getFunction();
};
}

function addCustomDOMPropertiesMethod (obj, getSelector, SelectorBuilder) {
Expand Down
2 changes: 2 additions & 0 deletions src/client-functions/selectors/selector-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ export default class SelectorBuilder extends ClientFunctionBuilder {

return merge({}, dependencies, {
filterOptions: {
filterVisible: this.options.filterVisible,
filterHidden: this.options.filterHidden,
counterMode: this.options.counterMode,
collectionMode: this.options.collectionMode,
index: isNullOrUndefined(this.options.index) ? null : this.options.index
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { domUtils, positionUtils } from '../../deps/testcafe-core';
import { selectElement as selectElementUI } from '../../deps/testcafe-ui';

export function exists (el) {
return !!el;
}

export function visible (el) {
if (!domUtils.isDomElement(el) && !domUtils.isTextNode(el))
return false;

if (domUtils.isOptionElement(el) || domUtils.getTagName(el) === 'optgroup')
return selectElementUI.isOptionElementVisible(el);

return positionUtils.isElementVisible(el);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { InvalidSelectorResultError } from '../../../../../errors/test-run';
import { exists, visible } from '../element-utils';
import testCafeCore from '../../../deps/testcafe-core';
import hammerhead from '../../../deps/hammerhead';

// NOTE: save original ctors and methods because they may be overwritten by page code
var isArray = Array.isArray;
var Node = window.Node;
var HTMLCollection = window.HTMLCollection;
var NodeList = window.NodeList;
var arrayUtils = testCafeCore.arrayUtils;

function isArrayOfNodes (obj) {
if (!isArray(obj))
Expand Down Expand Up @@ -41,6 +44,14 @@ hammerhead.nativeMethods.objectDefineProperty.call(window, window, '%testCafeSel
else
throw new InvalidSelectorResultError();

filtered = arrayUtils.filter(filtered, n => exists(n));

if (options.filterVisible)
filtered = filtered.filter(n => visible(n));

if (options.filterHidden)
filtered = filtered.filter(n => !visible(n));

if (options.counterMode) {
if (options.index !== null)
return getNodeByIndex(filtered, options.index) ? 1 : 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,13 @@
import { Promise } from '../../../deps/hammerhead';
import { delay, positionUtils, domUtils } from '../../../deps/testcafe-core';
import { selectElement as selectElementUI } from '../../../deps/testcafe-ui';
import { delay } from '../../../deps/testcafe-core';
import ClientFunctionExecutor from '../client-function-executor';
import { exists, visible } from '../element-utils';
import { createReplicator, FunctionTransform, SelectorNodeTransform } from '../replicator';
import './filter';

const CHECK_ELEMENT_DELAY = 200;


// Utils
function exists (el) {
return !!el;
}

function visible (el) {
if (!domUtils.isDomElement(el) && !domUtils.isTextNode(el))
return false;

if (domUtils.isOptionElement(el) || domUtils.getTagName(el) === 'optgroup')
return selectElementUI.isOptionElementVisible(el);

return positionUtils.isElementVisible(el);
}

export default class SelectorExecutor extends ClientFunctionExecutor {
constructor (command, globalTimeout, startTime, createNotFoundError, createIsInvisibleError) {
super(command);
Expand Down
26 changes: 26 additions & 0 deletions test/functional/fixtures/api/es-next/selector/pages/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,31 @@
<div id="attr1" data-store="data-attr1" class="attr"></div>
<div id="attr2" data-store="data-attr2" class="attr"></div>
</div>

<div id="filterVisiblePlain">
<div style="display: none;">displayNone</div>
<div style="width: 0; height: 0;">zeroDimensions</div>
<div style="visibility: hidden;">visibilityHidden</div>
<div>visible</div>
</div>

<div id="filterVisibleHierarchical">
<div style="display: none;">
<p class="p" style="display: none;">1</p>
<p class="p" style="display: none;">2</p>
<p style="display: none;">3</p>
<p class="p">4</p>
<p>5</p>
</div>
<div>
<p class="p">1</p>
<p>2</p>
<p>3</p>
<p class="p" style="display: none;">4</p>
<p class="p">5</p>
<p>6</p>
</div>
</div>

</body>
</html>
8 changes: 8 additions & 0 deletions test/functional/fixtures/api/es-next/selector/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ describe('[API] Selector', function () {
return runTests('./testcafe-fixtures/selector-test.js', 'Combination of filter methods', DEFAULT_CHROME_RUN_OPTIONS);
});

it('Should provide methods for filtering by visibility for plain structure of HTML elements', function () {
return runTests('./testcafe-fixtures/selector-test.js', 'Selector `filterVisible/filterHidden` methods with plain structure', DEFAULT_RUN_OPTIONS);
});

it('Should provide methods for filtering by visibility for hierarchical structure of HTML elements', function () {
return runTests('./testcafe-fixtures/selector-test.js', 'Selector `filterVisible/filterHidden` methods with hierarchical structure', DEFAULT_RUN_OPTIONS);
});

it('Should provide .find() method', function () {
return runTests('./testcafe-fixtures/selector-test.js', 'Selector "find" method', DEFAULT_RUN_OPTIONS);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,33 @@ test('Combination of filter methods', async t => {
await t.expect(id).eql('el2');
});

test('Selector `filterVisible/filterHidden` methods with plain structure', async t => {
const elements = Selector('#filterVisiblePlain div');

await t.expect(elements.count).eql(4);
await t.expect(elements.filterVisible().count).eql(1);
await t.expect(elements.filterHidden().count).eql(3);
await t.expect(elements.filterVisible().filterHidden().count).eql(0);
});

test('Selector `filterVisible/filterHidden` methods with hierarchical structure', async t => {
let elements = Selector('#filterVisibleHierarchical > div');

await t.expect(elements.child('p').count).eql(11);

elements = elements.filterVisible().child('p');

await t.expect(elements.count).eql(6);
await t.expect(elements.filterVisible().count).eql(5);
await t.expect(elements.filterVisible().filter('.p').count).eql(2);
await t.expect(elements.filterHidden().count).eql(1);

elements = Selector('#filterVisibleHierarchical > div').filterHidden().child('p');

await t.expect(elements.count).eql(5);
await t.expect(elements.filterHidden().count).eql(5);
});

test('Selector "find" method', async t => {
await t
// String filter
Expand Down

0 comments on commit e12ffe7

Please sign in to comment.