惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
aimingoo的专栏
aimingoo的专栏
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 聂微东
Engineering at Meta
Engineering at Meta
N
Netflix TechBlog - Medium
Blog — PlanetScale
Blog — PlanetScale
大猫的无限游戏
大猫的无限游戏
Vercel News
Vercel News
D
DataBreaches.Net
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
WordPress大学
WordPress大学
L
LangChain Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
F
Fortinet All Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
J
Java Code Geeks
Recent Announcements
Recent Announcements
Jina AI
Jina AI
G
Google Developers Blog
腾讯CDC
博客园_首页
博客园 - 【当耐特】

晚花行乐

马克卡尼在2026年达沃斯论坛上的讲话(阅读材料) | 晚花行乐 鸡娃如何用力才是恰到好处 | 晚花行乐 读万卷书,行万里路的辩证关系 | 晚花行乐 反对培训机构掐尖招生 | 晚花行乐 小泽和建国会谈最后10分钟全文(阅读材料) | 晚花行乐 来看看 DeepSeek 怎么鸡娃 | 晚花行乐 谈谈基本功 | 晚花行乐 惠普 ProDesk SFF PC 各系列参数对比 | 晚花行乐 解决瘦客户机上安装 Debian 12 启动失败问题 | 晚花行乐 意拾喻言:老外写的文言文 | 晚花行乐 笠翁对韵中的典故(十三元) | 晚花行乐 谈谈中考取消四小门 | 晚花行乐 亚马逊云科技产品免费试用攻略(3) - 对象存储服务 | 晚花行乐 在 Windows 10 LTSC 版本上安装 WSL2 | 晚花行乐 Debian 12 的常用配置项 | 晚花行乐 在 Debian 12 上安装 Nvidia 显卡驱动程序 | 晚花行乐 解决 Debian 12 关机失败问题 | 晚花行乐 解决 VS Code 自动更新版本后卡在连接界面 | 晚花行乐 观看巴黎奥运会有感 | 晚花行乐 在 Windows10 上安装惠普旧打印机驱动程序 | 晚花行乐 欢迎关注公众号:晚花行乐 | 晚花行乐 如何编写拼写检查器 | 晚花行乐 亚马逊云科技产品免费试用攻略(2) - 云服务器 | 晚花行乐 Pandas 中 axis 参数的理解(附实例) | 晚花行乐 我打算命个名,叫什么什么 Manager | 晚花行乐 上海武康路历史建筑一览 | 晚花行乐 Python 实现简单的数学表达式解析并处理 | 晚花行乐 观看马拉松的感悟 | 晚花行乐 Python 保存 Cookies 到文件并再次读取 | 晚花行乐 如何为 Hugo 静态网站添加评论功能 | 晚花行乐
前端测试工具 TestCafe 中的 Selector | 晚花行乐
2019-10-14 · via 晚花行乐

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()

上面两种语言里寻找元素的方法都是通过函数的方法查找,有下面一些缺点:

  • 往往一种方法不足以精确定位到意向中的元素,需要多种函数组合使用,比如:这篇文章 里讲的做法;
  • 仅能用来定位DOM元素,需要编写函数进一步获取元素的信息,比如 这篇文章 里讲的做法;
  • 函数返回类型不同,有的是单个元素(比如 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,如下:

  • jQuery 形式的字符串
Selector('img');
Selector('#submit-button');
Selector('.button');
  • 运行在浏览器端的函数片段,这个函数必须返回 DOM
Selector(id => {
    return document.getElementById(id);
});
  • 从其他 Selector 实例中派生
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)代码,正如 上节 中的例子, clickexpect 都直接接收 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 返回元素的数量

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 获取节点Node信息

正如 上节 所说,Selector 能获取节点的信息,而且封装为方便使用的 api,比如:

适用于所有类型节点(节点类型列表):

node.childElementCount  //子元素类型
node.childNodeCount  //子节点类型
node.hasChildElements  //是否有子元素
node.hasChildNodes  // 是否有子节点
node.nodeType   //节点类型
node.textContent   //节点包含的文字
node.hasClass(className)  // 是否包含某种 class 名称

Selector 获取元素Element信息

对于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)=>{})   // 筛选符合特定函数描述的元素
  • 在元素所在 DOM 树中查找
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 的元素

获取页面 title

除了页面 <body> 中的元素之外,还能获取 <head> 信息,比如页面 <title>

Selector('title').textContent

各位读后有什么想法,请在下方留言吧!如果对本文有疑问或者寻求合作,欢迎 联系邮箱邮箱已到剪贴板

精彩评论