













TestCafe 中的 Selector 是功能丰富的模块,封装了许多查找元素的功能,并且提供了额外的函数式查找、而且具有获取抽象动态元素和静态元素状态的功能。
如 上节 所说,测试代码运行在服务侧的 Test Controller 中。当需要在测试代码中指代客户端侧的 DOM 元素时,需要一种通信实体,连接服务侧和客户端侧的代码。Selector 就是其中的一种,另外还有 ClientFunction.
首先回顾 selenium 中寻找页面元素的方法:
#selenium
driver.find_element_by_id()
driver.find_element_by_name()
driver.find_element_by_xpath()
driver.find_element_by_link_text()
driver.find_element_by_partial_link_text()
driver.find_element_by_tag_name()
driver.find_element_by_class_name()
driver.find_element_by_css_selector()
puppeteer 中寻找页面元素的方法(使用了类似jQuery的形式):
//puppeteer
page.$()
page.$x()
page.$$()
page.waitFor()
page.waitForSelector()
page.waitForXPath()
上面两种语言里寻找元素的方法都是通过函数的方法查找,有下面一些缺点:
page.$() ),有的是
array(比如 page.$x() );在 TestCafe 中,上面这些进一步操作,都由 Test Controller 使用 Selector 实体来完成,将许多待处理的事物提供为API,成为开箱即用的功能。
基本用法如下面代码,已经简单得几乎不能再简化了:
import { Selector } from 'testcafe';
const article = Selector('.article-content');
生成了 Selector 实体后(即上述代码中的 article),可以作为参数传递给 服务端 代码(动作或者断言)
await t.click(article);
await t.expect(article.scrollHeight).eql(1800);
可以通过多种方法创建 Selector,如下:
Selector('img');
Selector('#submit-button');
Selector('.button');
Selector(id => {
return document.getElementById(id);
});
Selector(Selector('.cta-button'), { visibilityCheck: true });
const visibleTopMenu = Selector(await Selector('#top-menu')(), {
visibilityCheck: true
});
使用 Selector 获取元素信息,还有动态和静态两种,这是之前的工具 selenium 和 Puppeteer 所不具备的。
这种模式下,元素的信息是异步获得的。创建 Selector 实体时,Selector 并不是对元素的引用,而是一种在服务侧代码中的表达(representation)。创建 Selector 的代码仅仅返回一个 Promise ,当需要用到该元素时,使用 await 关键字才能得到元素的状态信息。比如:
import { Selector } from 'testcafe';
const windowsInput = Selector('#windows'); // 服务侧代码的表达,并不立即获取元素信息
test('Obtain Element State', async t => {
await t.click(windowsInput);
const windowsInputChecked = await windowsInput.checked; // 发出 click 动作后,才检查状态
});
动态的 Selector 实体可以直接传递给服务端的动作(action)或者断言(expect)代码,正如 上节 中的例子, click 和 expect 都直接接收 Selector 实体及其对外api
如果在创建 Selector 实体时就使用 await 关键字 将 Selector 返回的 Promise resolve 出结果,就能直接得到 DOM 节点的静态信息(SnapShot),这些信息通过 selector 实体的属性或者方法获得。比如下面的方法获取元素的 computed style:
import { Selector } from 'testcafe';
test('DOM Node Snapshot', async t => {
const logo = await (Selector('#logo'))();
console.log(logo.style);
});
和这篇文章 用 Puppeteer 获取页面元素的样式 的方法相比,上面的代码非常简单易读。
Selector 定位到的元素有可能是多个、一个、0 个等,可以通过 Selector 的两个属性来确定:
import { Selector } from 'testcafe';
test('properties', async t => {
const logo = Selector('#logo');
console.log(logo.count); // 数量
console.log(logo.exists); // 至少有1个时为 true
});
正如 上节 所说,Selector 能获取节点的信息,而且封装为方便使用的 api,比如:
适用于所有类型节点(节点类型列表):
node.childElementCount //子元素类型
node.childNodeCount //子节点类型
node.hasChildElements //是否有子元素
node.hasChildNodes // 是否有子节点
node.nodeType //节点类型
node.textContent //节点包含的文字
node.hasClass(className) // 是否包含某种 class 名称
对于DOM元素,除了上面普适 Node 的方法以外,还有下面 api 供使用:
elem.checked // 复选框和单选框的状态
elem.focused // 当前是否位于焦点
elem.selected // 下拉框的状态
elem.selectedIndex //下拉框所选中的选项的序号
elem.visible // 是否显示状态
elem.attributes // 全部 attribute,等效于 getAttribute
elem.classNames // 全部的 class 名称
elem.id // 元素的 id
elem.innerText // 元素包含的文字
elem.style // 元素的 computed style
elem.tagName // 元素名称,比如 img 等
elem.value // input 元素的值
elem.boundingClientRect //返回元素的宽高和位置信息
elem.clientHeight //返回 clientHeight,下同
elem.clientLeft
elem.clientTop
elem.clientWidth
elem.namespaceURI
elem.offsetHeight
elem.offsetLeft
elem.offsetTop
elem.offsetWidth
elem.scrollHeight
elem.scrollLeft
elem.scrollTop
elem.scrollWidth
上面的尺寸信息参考 元素的属性
如果需要从 selector 初步得到的元素集合中进一步筛选元素,可以使用函数式的 api,仍然返回 selector 类型。
Selector('label').nth(n) // 返回前一次筛选结果集中的第 n 个元素,n=0表示第一个,n=-1表示最后一个
Selector('label').withText('关键字') // 筛选出真包含关键字的元素
Selector('label').withText(/re/) // 筛选出符合 re 正则表达式的元素
Selector('label').withExactText('关键字') // 筛选出全等关键字的元素
Selector('label').withAttribute('attrName') // 筛选出包含attrName属性的元素
Selector('label').withAttribute('attrName', 'attrValue') // 筛选出 attrName = attrValue 的元素
Selector('label').filterVisible() // 筛选出可见元素
Selector('label').filterHidden() // 筛选出不可见的元素
Selector('label').filter('selected') // 筛选 class=selected 的元素
Selector('label').filter((node, idx)=>{}) // 筛选符合特定函数描述的元素
Selector('label').find('selected') // 从所有后代节点 node 中,筛选class=selected 的 node
Selector('label').parent('selected') // 从所有父代 node 中,筛选class=selected 的 node
Selector('label').child('selected') // 从所有后代元素 element 中,筛选class=selected 的元素
Selector('label').sibling('selected') // 从所有同级元素 element 中,筛选class=selected 的元素
Selector('label').nextSibling('selected') // 从所有同级后续元素 element 中,筛选class=selected 的元素
Selector('label').prevSibling('selected') // 从所有同级前置元素 element 中,筛选class=selected 的元素
除了页面 <body> 中的元素之外,还能获取 <head> 信息,比如页面 <title>
Selector('title').textContent
各位读后有什么想法,请在下方留言吧!如果对本文有疑问或者寻求合作,欢迎 联系邮箱 。邮箱已到剪贴板
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。