自定义选择器以匹配节点和所有后代
Custom selector to match a node and all descendants
这是 TestCafe 特有的问题。
我希望能够 select 元素基于元素本身或后代中任何地方的属性值。主要要求是能够做到这一点 无需异步调用 .
基本上,我正在尝试编写如下内容:
const findByTestName = (context: Selector, testName: string): Selector => {
// if context itself matches [data-test~='${testName}'] return context;
// return context.find('[data-test~='${testName}']');
};
可能吗?
编辑:
提供更多上下文:我本可以尝试使用 css 查询来解决它,例如 "#someid[data-test='testname'], #someid [data-test='testname']"
,但我正在尝试创建嵌套页面模型,如下所示:
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');
}
}
其中 DOM 配置可能是这样的:
<div id="page">
<div data-test="page__button button">
<span data-test="button__label" />
</div>
</div>
或者这个。以实际布局为准
<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>
这样 Button pageModel 将是一个可重复使用的东西,可以应用于任何按钮。
您需要使用Selector.with方法。
看一个例子:
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');
});
您可以将选择器(甚至由 Selector.find
等过滤器函数生成)作为依赖项传递,以同步创建复杂的选择器:
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);
});
请注意,您可以删除 findByTestName
函数中的所有常量,但 shorthand 属性 cannot be used to specify dependencies:
const findByTestName = (root, testName) => Selector(() => {
//...
}, {
dependencies: {
root: root,
children: root.find(...)
//...
}
);
这是 TestCafe 特有的问题。 我希望能够 select 元素基于元素本身或后代中任何地方的属性值。主要要求是能够做到这一点 无需异步调用 .
基本上,我正在尝试编写如下内容:
const findByTestName = (context: Selector, testName: string): Selector => {
// if context itself matches [data-test~='${testName}'] return context;
// return context.find('[data-test~='${testName}']');
};
可能吗?
编辑:
提供更多上下文:我本可以尝试使用 css 查询来解决它,例如 "#someid[data-test='testname'], #someid [data-test='testname']"
,但我正在尝试创建嵌套页面模型,如下所示:
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');
}
}
其中 DOM 配置可能是这样的:
<div id="page">
<div data-test="page__button button">
<span data-test="button__label" />
</div>
</div>
或者这个。以实际布局为准
<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>
这样 Button pageModel 将是一个可重复使用的东西,可以应用于任何按钮。
您需要使用Selector.with方法。 看一个例子:
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');
});
您可以将选择器(甚至由 Selector.find
等过滤器函数生成)作为依赖项传递,以同步创建复杂的选择器:
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);
});
请注意,您可以删除 findByTestName
函数中的所有常量,但 shorthand 属性 cannot be used to specify dependencies:
const findByTestName = (root, testName) => Selector(() => {
//...
}, {
dependencies: {
root: root,
children: root.find(...)
//...
}
);