Reputation: 13
this is TestCafe-specific question. I want to be able to select elements based on the attribute value of the element itself or anywhere in descendants. And the main requirement is to be able to do this without async calls.
Basically, i'm trying to write something like the following:
const findByTestName = (context: Selector, testName: string): Selector => {
// if context itself matches [data-test~='${testName}'] return context;
// return context.find('[data-test~='${testName}']');
};
Is it possible?
EDIT:
To give a bit more context: I could've tried to solve it using css query like "#someid[data-test='testname'], #someid [data-test='testname']"
, but i'm trying to create nested page models like so:
class Page {
constructor() {
this.root = new Selector('[data-test="page"]');
this.button = new Button(findByTestName(this.root, 'page__button'));
}
}
class Button {
constructor(context: Selector) {
this.root = findByTestName(context, 'button');
this.label = findByTestName(this.root, 'button__label');
}
}
where DOM configuration could be like this:
<div id="page">
<div data-test="page__button button">
<span data-test="button__label" />
</div>
</div>
or this. depending on the actual layout
<div id="page">
<div data-test="page__button">
<div data-test="someotherstuff">
<div data-test="button>
<span data-test="button__label" />
</div>
</div>
</div>
</div>
this way Button pageModel would be a reusable thing that can be applied to any button.
Upvotes: 1
Views: 340
Reputation: 2903
You can pass selectors (even produced by filter functions like Selector.find
) as dependencies to create complex selectors synchronously:
import { Selector } from 'testcafe';
const findByTestName = (root, testName) => {
const children = root.find(`[data-test=${testName}]`);
const context = { root: root, children: children, testName: testName };
return Selector(() => {
const element = root();
if (element.getAttribute('data-test') === testName)
return el;
return children();
}, { dependencies: context });
}
fixture`Selector`.page`./test.html`;
test(`Dependencies`, async t => {
const root = Selector('[data-test=page__button]');
console.log(await findByTestName(root, "button__label").tagName);
});
Please note that you can get rid of all constants inside the findByTestName
function, but shorthand properties cannot be used to specify dependencies:
const findByTestName = (root, testName) => Selector(() => {
//...
}, {
dependencies: {
root: root,
children: root.find(...)
//...
}
);
Upvotes: 3
Reputation: 5227
You need to use the Selector.with method. See an example:
import { Selector } from 'testcafe';
fixture `Fixture`
.page('./index.html');
const findByTestName = Selector(testName => {
var checkElement = function (el, checkedAttrValue, result) {
var testNameAttributeVale = el.getAttribute('data-test');
if (testNameAttributeVale === checkedAttrValue)
result.push(el);
return result;
};
var rootElement = context();
var result = [];
do {
checkElement(rootElement);
rootElement = rootElement.parentElement;
}
while(rootElement);
return result;
}, { dependencies: { context: Selector('body') }});
test('test', async t => {
const elms1 = await findByTestName('button');
const elms2 = await findByTestName.with({
dependencies: { context: Selector('div[data-test=button]')}
})('someotherstuff');
});
Upvotes: 2