Skip to content

Commit

Permalink
Added returnDOMNodes option in addCustomMethods(closes DevExpress#1919)…
Browse files Browse the repository at this point in the history
  • Loading branch information
kirovboris committed Dec 18, 2019
1 parent 37145f7 commit 5c8731a
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 23 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ node_modules
.publish
/___test-screenshots___
yarn.lock
package-lock.json
68 changes: 50 additions & 18 deletions src/client-functions/selectors/add-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,12 @@ function assertAddCustomDOMPropertiesOptions (properties) {
});
}

function assertAddCustomMethods (properties) {
function assertAddCustomMethods (properties, opts) {
assertType(is.nonNullObject, 'addCustomMethods', '"addCustomMethods" option', properties);

if (opts !== void 0)
assertType(is.nonNullObject, 'addCustomMethods', '"addCustomMethods" option', opts);

Object.keys(properties).forEach(prop => {
assertType(is.function, 'addCustomMethods', `Custom method '${prop}'`, properties[prop]);
});
Expand All @@ -120,35 +123,55 @@ function addSnapshotProperties (obj, getSelector, properties) {
});
}

export function addCustomMethods (obj, getSelector, customMethods) {
var customMethodProps = customMethods ? Object.keys(customMethods) : [];
export function addCustomMethods (obj, getSelector, SelectorBuilder, customMethods) {
const customMethodProps = customMethods ? Object.keys(customMethods) : [];

customMethodProps.forEach(prop => {
var dependencies = {
customMethod: customMethods[prop],
const { returnDOMNodes = false, method } = customMethods[prop];

const dependencies = {
customMethod: method,
selector: getSelector()
};

var callsiteNames = { instantiation: prop };
const callsiteNames = { instantiation: prop };

obj[prop] = (new ClientFunctionBuilder((...args) => {
/* eslint-disable no-undef */
var node = selector();
if (returnDOMNodes) {
obj[prop] = (...args) => {
const selectorFn = () => {
/* eslint-disable no-undef */
const nodes = selector();

return customMethod.apply(customMethod, [node].concat(args));
/* eslint-enable no-undef */
}, { dependencies }, callsiteNames)).getFunction();
return customMethod.apply(customMethod, [nodes].concat(args));
/* eslint-enable no-undef */
};

return createDerivativeSelectorWithFilter(getSelector, SelectorBuilder, selectorFn, () => true, {
args,
customMethod: method
});
};
}
else {
obj[prop] = (new ClientFunctionBuilder((...args) => {
/* eslint-disable no-undef */
const node = selector();

return customMethod.apply(customMethod, [node].concat(args));
/* eslint-enable no-undef */
}, { dependencies }, callsiteNames)).getFunction();
}
});
}

function addSnapshotPropertyShorthands (obj, getSelector, customDOMProperties, customMethods) {
function addSnapshotPropertyShorthands (obj, getSelector, SelectorBuilder, customDOMProperties, customMethods) {
var properties = SNAPSHOT_PROPERTIES;

if (customDOMProperties)
properties = properties.concat(Object.keys(customDOMProperties));

addSnapshotProperties(obj, getSelector, properties);
addCustomMethods(obj, getSelector, customMethods);
addCustomMethods(obj, getSelector, SelectorBuilder, customMethods);

obj.getStyleProperty = prop => {
var callsite = getCallsiteForMethod('getStyleProperty');
Expand Down Expand Up @@ -406,10 +429,19 @@ function addCustomDOMPropertiesMethod (obj, getSelector, SelectorBuilder) {
}

function addCustomMethodsMethod (obj, getSelector, SelectorBuilder) {
obj.addCustomMethods = customMethods => {
assertAddCustomMethods(customMethods);
obj.addCustomMethods = function (methods, opts) {
assertAddCustomMethods(methods, opts);

const customMethods = {};

Object.keys(methods).forEach(methodName => {
customMethods[methodName] = {
method: methods[methodName],
returnDOMNodes: opts && !!opts.returnDOMNodes
};
});

var builder = new SelectorBuilder(getSelector(), { customMethods }, { instantiation: 'Selector' });
const builder = new SelectorBuilder(getSelector(), { customMethods }, { instantiation: 'Selector' });

return builder.getFunction();
};
Expand Down Expand Up @@ -615,7 +647,7 @@ function addHierarchicalSelectors (obj, getSelector, SelectorBuilder) {
}

export function addAPI (obj, getSelector, SelectorBuilder, customDOMProperties, customMethods) {
addSnapshotPropertyShorthands(obj, getSelector, customDOMProperties, customMethods);
addSnapshotPropertyShorthands(obj, getSelector, SelectorBuilder, customDOMProperties, customMethods);
addCustomDOMPropertiesMethod(obj, getSelector, SelectorBuilder);
addCustomMethodsMethod(obj, getSelector, SelectorBuilder);
addFilterMethods(obj, getSelector, SelectorBuilder);
Expand Down
2 changes: 1 addition & 1 deletion src/client-functions/selectors/selector-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export default class SelectorBuilder extends ClientFunctionBuilder {
createSnapshotMethods(snapshot);

if (this.options.customMethods)
addCustomMethods(snapshot, () => snapshot.selector, this.options.customMethods);
addCustomMethods(snapshot, () => snapshot.selector, SelectorBuilder, this.options.customMethods);
}

return snapshot;
Expand Down
25 changes: 22 additions & 3 deletions test/functional/fixtures/api/es-next/selector/test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
var expect = require('chai').expect;

var DEFAULT_SELECTOR_TIMEOUT = 3000;
var DEFAULT_RUN_OPTIONS = { selectorTimeout: DEFAULT_SELECTOR_TIMEOUT };
var DEFAULT_CHROME_RUN_OPTIONS = { only: 'chrome', selectorTimeout: 3000 };
var DEFAULT_SELECTOR_TIMEOUT = 3000;
var DEFAULT_RUN_OPTIONS = { selectorTimeout: DEFAULT_SELECTOR_TIMEOUT };
var DEFAULT_CHROME_RUN_OPTIONS = { only: 'chrome', selectorTimeout: DEFAULT_SELECTOR_TIMEOUT };

describe('[API] Selector', function () {
it('Should provide basic properties in HTMLElement snapshots', function () {
Expand Down Expand Up @@ -61,6 +61,10 @@ describe('[API] Selector', function () {
return runTests('./testcafe-fixtures/selector-test.js', 'Selector `addCustomMethods` method', DEFAULT_RUN_OPTIONS);
});

it('Selector `addCustomMethods` method - Selector mode', function () {
return runTests('./testcafe-fixtures/selector-test.js', 'Selector `addCustomMethods` method - Selector mode', DEFAULT_RUN_OPTIONS);
});

it('Should wait for element to appear on new page', function () {
return runTests('./testcafe-fixtures/selector-test.js', 'Element on new page', DEFAULT_RUN_OPTIONS);
});
Expand Down Expand Up @@ -284,6 +288,21 @@ describe('[API] Selector', function () {
});
}
);

it('Should raise error if custom method throws an error - Selector mode',
function () {
return runTests('./testcafe-fixtures/selector-error-test.js', 'Add custom method - method throws an error - Selector mode', {
shouldFail: true,
only: 'chrome'
})
.catch(function (errs) {
expect(errs[0]).contains(
'An error occurred in Selector code: Error: test'
);
expect(errs[0]).contains('> 73 | await el.customMethod()();');
});
}
);
});

describe('Regression', function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,13 @@ test('Add custom method - method throws an error', async () => {

await el.customMethod();
});

test('Add custom method - method throws an error - Selector mode', async () => {
const el = Selector('rect').addCustomMethods({
customMethod: () => {
throw new Error('test');
}
}, { returnDOMNodes: true });

await el.customMethod()();
});
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ test('Selector "withText" method', async t => {
});

test('Selector "withExactText" method', async t => {
let selector = Selector('#withExactText div');
let selector = Selector('#withExactText div');

await t
.expect(selector.withText('Element with text').count).eql(6);
Expand Down Expand Up @@ -1034,3 +1034,54 @@ test('hasAttribute method', async t => {
.expect(sel.hasAttribute('id')).notOk()
.expect(el.hasAttribute).eql(void 0);
});

test('Selector `addCustomMethods` method - Selector mode', async t => {
const sectionDiv = Selector('section div').addCustomMethods({
customFilter: nodes => nodes.filter(node => node.id === 'el2' || node.id === 'el3'),
customFilterByParam: (nodes, id) => nodes.filter(node => node.id === id)
}, { returnDOMNodes: true });

const form = Selector('form').addCustomMethods({
customFind: (nodes) => nodes[0].querySelectorAll('input'),
customFindByType: (nodes, type) => nodes[0].querySelectorAll(`input[type=${type}]`)
}, { returnDOMNodes: true });

let filteredDivs = sectionDiv.customFilter();
let divsById = sectionDiv.customFilterByParam('el4');

await t
.expect(filteredDivs.count).eql(2)
.expect(filteredDivs.nth(0).id).eql('el2')
.expect(filteredDivs.nth(1).id).eql('el3')

.expect(divsById.id).eql('el4');

const inputs = form.customFind();
const inputByType = form.customFindByType('checkbox');

await t
.expect(inputs.count).eql(2)
.expect(inputs.nth(0).id).eql('textInput')
.expect(inputs.nth(1).id).eql('checkInput')

.expect(inputByType.id).eql('checkInput');

const snapshot = await sectionDiv();

filteredDivs = snapshot.customFilter();
divsById = snapshot.customFilterByParam('el4');

await t
.expect(filteredDivs.count).eql(2)
.expect(filteredDivs.nth(0).id).eql('el2')
.expect(filteredDivs.nth(1).id).eql('el3')

.expect(divsById.id).eql('el4');

const nonExistingElement = Selector('nonExistingElement').addCustomMethods({
prop: () => 'value'
}, { returnDOMNodes: true });

await t.expect(await nonExistingElement()).eql(null);
});

0 comments on commit 5c8731a

Please sign in to comment.