简介
testcafe (官网) 是一个基于 nodejs 的自动化测试框架,优点就先不多说了,我们直接进入正题!
安装
testcafe 是一个 npm 包,它可以使用 npm 或 yarn 包管理器进行安装,这里我们使用 yarn 进行安装(因为它很快)
如果你的机器上没有安装 yarn,那么你可以使用 npm 来安装 yarn 😊
npm install -g yarn
在命令行中运行以下命令
yarn global add testcafe
这样, testcafe 就安装在你本机上啦
小试牛刀
创建一个测试项目
我们新建一个目录,用于练习我们的自动化测试框架 testcafe
# 首先切换到你用于管理 web 项目的根目录,我本机是 "~/www"
cd ~/www
mkdir testcafe && cd testcafe
编写第一个测试脚本
然后新建一个名为 myFirstTestcase.js 文件,用于编写我们的自动化测试脚本
vim myFirstTestcase.js # 用 vim 打开这个文件(如果文件不存在就创建它)
如果你安装了 vscode,你还可以使用
code myFirstTestcase.js来创建并编辑它 需要在 vscode 的控制它中输入install command来启用code命令
将以下内容粘贴到 myFirstTestcase.js 中
// ES6 导包语法
import { Selector } from 'testcafe';
// 声明一个 fixture 测试项目
fixture('Getting Started')
  // 打开一个 web 页面用于接下来的测试
  .page('http://devexpress.github.io/testcafe/example')
// 创建一个测试用例
test('My first test', async t => {
  // Test code
});
到这里,我们的第一个 test script 就写好了,接下来我们来运行它看看
运行测试脚本
我们回到命令行中,执行以下命令来运行一个测试脚本
testcafe chrome myFirstTestcase.js
这个命令中,
testcafe是我们使用包管理工具全局安装的依赖,testcafe 是它的可执行程序
chrome是我们的测试平台,安装在本机的浏览器,也可以是safarifirefox等
myFirstTestcase.js是我们编写的测试脚本
稍等片刻,我们的浏览器就会被自动打开然后运行测试脚本,最后浏览器被自动关闭,在终端上留下了测试结果。

编写测试代码
刚才我们的测试脚本只是简单的打开了一个页面,它并没有执行任何动作。
在接下来的几个例子中,你可以打开测试目标网站 http://devexpress.github.io/testcafe/example 这个页面,然后打开控制台,观察其 DOM 结构,并试着模拟脚本的操作,以便方便的理解脚本的内容。
在页面上执行操作
接下来我们简单的写两个动作,用来对页面进行操作。
打开 myFirstTestcase.js,在 test 方法的回调函数中添加以下内容
  test('My first test', async t => {
-   // Test code
+   await t
+     .typeText('#developer-name', 'John Smith')
+     .click('#submit-button');
  });
t 是我们的测试用例的控制器,它又很多方法,在上面的例子中,我们调用了它的 typeText 和 click 两个方法,其中
- typeText方法用来键入文本,它接受两个参数:第一个参数是 selector 选择器(它的语法类似 jQuery 选择器的语法);第二个参数是你要键入的文本;
- click方法用来模拟鼠标点击,它接受一个参数,是一个 selector 选择器
有关这个控制器的更多方法,请先参考 TestCafe 官方 API 手册 (它是一个英文文档,稍后我会整理出这个文档的中文手册在本页下方)
这段代码的作用是:
- 首先找到 id 为 developer-name的标签,输入值 'John Smith'
- 然后点击 id 为 submit-button的按钮
观察页面的变化
上面一小节我们对页面进行了交互,接下来我们想知道页面进行了什么反应,也就是观察的页面变化。
我们对测试脚本进行修改
  test('My first test', async t => {
    await t
      .typeText('#developer-name', 'John Smith')
      .click('#submit-button');
+
+   const articleHeader = await Selector('.result-content').find('h1');
+
+   // 获取 article header 的内容文本
+   let headerText = await articleHeader.innerText;
  });
在我们点击页面上的“提交”按钮后,会打开一个“谢谢”页面。
如果我们要访问页面上的 DOM 元素,可以使用测试脚本顶部导入的 Selector 方法
- L6,我们声明了一个 articleHeader变量,这个变量的值是根据选择器.result-content > h1找到的 DOM 元素
- L9,我们又声明了一个 headerText变量,它的值是我们获取到的 DOM 元素的innerText属性(这个属性的值是 DOM 元素的内容文本)
断言
我们拿到需要判断的值后,就可以对测试用例进行断言了。
它到底能否正确执行测试用例并输出我们期望的结果?
我们使用测试用例控制器的 t.expect() 方法来进行断言,
将测试脚本改写为以下内容
  test('My first test', async t => {
    await t
      .typeText('#developer-name', 'John Smith')
      .click('#submit-button');
-   const articleHeader = await Selector('.result-content').find('h1');
-
-   // 获取 article header 的内容文本
-   let headerText = await articleHeader.innerText;
+   // 使用断言方法来判断我们获取到的值与我们期望的值是否相等
+   .expect(Selector('#article-header').innerText).eql('Thank you, John Smith!');
  });
- L11 - expect()是一个 BDD(行为驱动开发)风格的断言方法, 它接受一个参数:需要进行断言的变量;
 它返回一个断言类实例对象,后跟一个断言方法。
- eql()是一个断言方法,用于判断断言实例与期望值是否严格等于,它接受一个参数:期望值;
 该类断言方法会在打印出相应的测试报告,如果相等则返回 pass,否则抛出一个 AssertionError 
小结
到这里,我们的第一个自动化测试脚本就完成了,如果你没有跑通的话,请检查一下你的测试脚本是否与以下内容一致
// ES6 导包语法
import { Selector } from 'testcafe'
// 声明一个 fixture 测试项目
fixture('Getting Started')
  // 打开一个 web 页面用于接下来的测试
  .page('http://devexpress.github.io/testcafe/example')
// 创建一个测试用例
test('My first test', async t => {
  await t
    .typeText('#developer-name', 'John Smith')
    .click('#submit-button')
    // 使用断言方法来判断我们获取到的值与我们期望的值是否相等
    .expect(Selector('#article-header').innerText).eql('Thank you, John Smith!1')
})
API 文档 (unfinished)
测试代码结构
测试组 Fixtures
TestCafe 测试必须将一些测试组织起来,测试组 (Fixtures) 就像是一个文件夹,将同一类的测试包裹起来。 一个测试文件可以包含很多测试组。
要声明一个测试组,使用 fixture 方法
fixture( fixtrueName )
// 或下面这种用法
fixture `fixtureName`
| 参数 | 类型 | 描述 | 
|---|---|---|
| fixtureName | string | 测试组的名称 | 
它返回一个测试组对象,可以接测试组方法,有关这些方法,请参考下方相关 API。
请注意,测试方法
test必须放在测试组声明后面。
测试用例 Tests
你可以使用 test 方法声明一个测试用例。
test( testName, fn(t) )
| 参数 | 类型 | 描述 | 
|---|---|---|
| testName | string | 测试用例的名称 | 
| fn | Function | 包含测试代码的异步函数 | 
| t | Object | 测试用例的测试控制器 | 
fixture('MyFixture')
test('Test1', async t => {
  /* Test 1 Code */
})
test('Test2', async t => {
  /* Test 2 Code */
})
TestCafe 测试在服务器端执行。 你可以使用测试动作来操纵测试的网页。 要确定页面元素的状态或从客户端获取任何其他数据,请使用选择器和客户端方法。
要检查页面状态是否与预期页面状态匹配,请使用断言。
测试控制器 Test Controller
如果要使用测试动作、断言、或者等待, 必须挂在在测试控制器上.
指定起始页面
您可以指定 fixture 中所有测试启动时的 web 页面。
fixture.page( url )
fixture.page `url`
类似的,你也可以为特定的测试用例置顶一个起始的 web 页面。
test.page( url )
test.page `url`
| 参数 | 类型 | 描述 | 
|---|---|---|
| url | string | 指定起始页面的 URL | 
fixture('MyFixture')
  .page('http://devexpress.github.io/testcafe/example')
test('Test1', async t => {
  // Starts at http://devexpress.github.io/testcafe/example
})
test.page('http://devexpress.github.io/testcafe/blog/')
  ('Test2', async t => {
      // Starts at http://devexpress.github.io/testcafe/blog/
  })
如果没有指定起始页面,则打开 about:blank 页面。
你也可以使用 file:// 协议或相对路径
fixture('MyFixture')
  .page('file:///user/my-website/index.html')
fixture('MyFixture')
  .page('../my-project/index.html')
测试元数据
你可以使用键值对的方式为测试指定元数据,并在测试报告中展示这些元数据。
要定义元数据,请使用 meta 方法。
fixture
  .meta('fixtureID', 'f-0001')
  .meta({ author: 'John', creationDate: '05/03/2018' })
  
test
  .meta('testID', 't-0001')
  .meta({ severity: 'critical', testedAPIVersion: '1.0' })
  ('MyTest', async t => { /* ... */})
你可以使用 custom reporter 来访问测试元数据。
报告器的 reportFixtureStart 和 reportTestDone 方法来访问测试元数据
钩子
测试组钩子
在每个测试组开始前、结束后,都可以执行特定的方法,它叫做钩子。如果一个测试在多个浏览器中运行,则在每个浏览器中都会执行指定钩子。
你可以指定测试组开始前、结束后执行的钩子
fixture `My fixture`
  .page `http://example.com`
  .before( async ctx => {
    /* fixture initialization code */
  })
  .after( async ctx => {
    /* fixture finalization code */
  });
测试用例钩子
在每次测试的开始前、结束后,也有相应的钩子。
fixture.beforeEach( fn(t) )
fixture.afterEach( fn(t) )
test.before( fn(t) )
test.after( fn(t) )
如果指定了
test.before()或test.after(), 那么它会覆盖fixture.beforeEach()或fixture.afterEach()的钩子。
fixture `My fixture`
  .page `http://example.com`
  .beforeEach( async t => {
      /* test initialization code */
  })
  .afterEach( async t => {
      /* test finalization code */
  });
test
  .before( async t => {
      /* test initialization code */
  })
  ('MyTest', async t => { /* ... */ })
  .after( async t => {
      /* test finalization code */
  });
在钩子和测试代码之间共享变量
通过使用测试的上下文对象,来共享在钩子和测试代码之间的变量。
测试的上下文对象为 t.ctx,使用它来代替全局变量。
fixture `Fixture1`
  .beforeEach(async t  => {
    t.ctx.someProp = 123;
  });
test
  ('Test1', async t => {
    console.log(t.ctx.someProp); // > 123
  })
  .after(async t => {
    console.log(t.ctx.someProp); // > 123
  });
需要注意的是,每个测试都有自己的测试上下文对象。每次测试开始时,
t.ctx都是一个空的对象。
在测试组的钩子中,回调函数的参数为 ctx,即为测试的上下文对象,在测试代码中可以使用 t.fixtureCtx 来访问它
fixture `Fixture1`
  .before(async ctx  => {
    ctx.someProp = 123;
  })
  .after(async ctx  => {
    console.log(ctx.newProp); // > abc
  });
test('Test1', async t => {
  console.log(t.fixtureCtx.someProp); // > 123
});
test('Test2', async t => {
  t.fixtureCtx.newProp = 'abc';
});
跳过测试
你在写测试用例时,可以跳过某个测试用例或者只执行某个测试用例
fixture.skip `Fixture1`; // 所有该测试组的用例都会被跳过
test('Fixture1Test1', () => {});
test('Fixture1Test2', () => {});
fixture `Fixture2`;
test('Fixture2Test1', () => {});
test.skip('Fixture2Test2', () => {}); // 这个测试用例会被跳过
test('Fixture2Test3', () => {});
fixture.only `Fixture1`;
test('Fixture1Test1', () => {});
test('Fixture1Test2', () => {});
fixture `Fixture2`;
test('Fixture2Test1', () => {});
test.only('Fixture2Test2', () => {});
test('Fixture2Test3', () => {});
// 只有 `Fixture1` 测试组和 `Fixture2Test2` 测试用例会被执行
页面元素选择
选择器 selector
选择器是标识测试中的网页元素的方法。选择器 API 提供了选择页面上的元素并获取其状态的方法和属性。
要从 testcafe 模块导入 Selector 构造函数,调用此构造函数并将 CSS 选择器字符串作为参数传递。
import { Selector } from 'testcafe';
const article = Selector('.article-content');
Selector 参数语法类似于 jQuery 选择器语法,他们都是使用的 CSS选择器 语法。
在上面的例子中,我们选择了一个 class 为 atricle-content 的元素。
然后我们就可以使用这个选择器对对元素进行操作了。
await t.click(article)
或者在断言方法中使用它
await t.expect(article.scrollHeight).eql(1800)
甚至还可以编写一个匹配多个页面元素的选择器,然后按文本、属性等对它们进行过滤。
下面这两个例子首先选择了一个 class 为 radio-button 的元素,并且其中的文本为 "Windows",第二个是含有属性为 selected 的元素。
const windowsRadioButton  = Selector('.radio-button').withText('Windows');
const selectedRadioButton = Selector('.radio-button').withAttribute('selected');
如果需要在 DOM 树中查找特定元素,可以使用选择器 API 的[搜索方法]查找它。
const buttonWrapper = Selector('.article-content').find('#share-button').parent();
创建选择器
Selector( init [, options] )
| 参数 | 类型 | 描述 | 
|---|---|---|
| init | Function | string | Selector | Snapshot | Promise | 标识要选择的 DOM 节点 | 
| options(可选的) | Object | 选项, 有关[选择器选项] | 
- 使用一个 CSS 选择器 - import { Selector } from 'testcafe';
 const usernameInput = Selector('#username');
- 使用一个在客户端执行的函数,必须返回一个 - DOM node,一组- DOM nodes,- NodeList,- HTMLCollection,- null或者- undefined,或者 Promise.resolve() 为以上内容的方法- 请注意,它不能使用一些外部变量,因为该方法是在浏览器中运行的 - import { Selector } from 'testcafe';
 const element = Selector(() => {
 const storedElementId = window.localStorage.storedElementId;
 return document.querySelector(storedElementId);
 });
- 一个 - Selector构造器- import { Selector } from 'testcafe';
 const ctaButton = Selector('.cta-button');
 Selector(ctaButton, { visibilityCheck: true });
使用选择器
本主题描述如何标识DOM元素并使用选择器获取关于它们的信息。
检查元素是否存在
选择器可能返回一个、多个或者不存在匹配的元素。您可以使用一下属性来检查元素是否存在,或者确定匹配元素的数量。
| 属性 | 类型 | 描述 | 
|---|---|---|
| exists | boolean | 如果匹配到元素则返回 true | 
| count | number | 选择器匹配的节点数量 | 
import { Selector } from 'testcafe';
fixture `Example page`
  .page `http://devexpress.github.io/testcafe/example/`;
test('My test', async t => {
  const osCount            = Selector('.column.col-2 label').count;
  const submitButtonExists = Selector('#submit-button').exists;
  await t
    .expect(osCount).eql(3)
    .expect(submitButtonExists).ok();
});
注意,选择器的 getter 是异步的
获取元素的状态
你也可以获取选择器匹配元素的状态(size、position、classes 等)。有关元素的状态
import { Selector } from 'testcafe';
fixture `My fixture`
  .page('http://devexpress.github.io/testcafe/example/');
const windowsInput = Selector('#windows');
test('Obtain Element State', async t => {
  await t.click(windowsInput);
  const windowsInputChecked = await windowsInput.checked; // returns true
});
DOM 节点快照
如果你需要获取整个 DOM 元素实例的状态,需要使用 await 来匹配
import { Selector } from 'testcafe';
fixture `My fixture`
  .page `http://devexpress.github.io/testcafe/example/`;
test('DOM Node Snapshot', async t => {
  const sliderHandle = Selector('#slider').child('span');
  const sliderHandleSnapshot = await sliderHandle();
  console.log(sliderHandleSnapshot.hasClass('ui-slider-handle'));    // => true
  console.log(sliderHandleSnapshot.childElementCount);               // => 0
});
定义动作的目标
你还可以将选择器的返回值作为测试控制器操作的目标。DOM 节点快照同样可以。
import { Selector } from 'testcafe';
fixture `My fixture`
    .page `http://devexpress.github.io/testcafe/example/`;
const label = Selector('#tried-section').child('label');
test('My Test', async t => {
  const labelSnapshot = await label();
  await t.click(labelSnapshot);
});
如果匹配到多个目标元素,那么只有匹配到的第一个元素将会被操作。
定义断言的目标
你可以将选择器的返回值作为断言的目标。
import { Selector } from 'testcafe';
fixture `My fixture`
  .page `http://devexpress.github.io/testcafe/example/`;
test('Assertion with Selector', async t => {
  const developerNameInput = Selector('#developer-name');
  await t
    .expect(developerNameInput.value).eql('')
    .typeText(developerNameInput, 'Peter')
    .expect(developerNameInput.value).eql('Peter');
});
选择器超时
在测试期间,testcafe 会一次又一次的检查,等待目标元素变得可见,如果超时还不可见则不会通过测试用例。
如何设置超时
- 你可以在选择器构造函数的选项中指定超时时间。
- 如果使用 API 进行测试,则需要在 runner.run方法中指定。
- 如果使用命令行启动,则需要指定选择器超时 选项。
调试选择器
Testcafe 会输出有关于测试报告运行失败时的详细信息。
如果你尝试使用不匹配任何DOM元素的选择器时,测试失败并抛出一个错误。错误消息指示哪个选择器失败。

选择器的方法
过滤 DOM 节点
如果选择器返回多个DOM节点,你可以对它们进行筛选,以选择一个最终将由选择器返回的节点。选择器提供了根据索引、文本、属性等过滤DOM节点的方法。
根据索引 (nth)
| 参数 | 返回值类型 | 描述 | 
|---|---|---|
| nth(index) | Selector | 根据匹配集中的索引查找元素。 index参数为从0开始的索引,如果指定为负数,则从末尾开始计算索引。 | 
// 返回第三个 ul 元素
Selector('ul').nth(2)
// 返回最后一个 div 元素
Selector('div').nth(-1)
根据文本 (withText / withExactText)
| 参数 | 返回值类型 | 描述 | 
|---|---|---|
| withText(text) | Selector | 创建一个选择器,该选择器过滤指定文本的匹配项,选择包含此文本的元素。 text参数区分大小写。 | 
| withExactText(text) | Selector | 创建一个选择器,该选择器过滤指定文本的匹配项,将会严格匹配等于该文本的元素。 text参数区分大小写。 | 
| withText(regexp) | Selector | 创建一个选择器,该选择器过滤指定正则表达式的匹配项。 | 
// 返回内容含有 'foo' 的 label 元素
// 不会返回含有 'foobar' 或 'Foo' 的元素
Selector('label').withText('foo')
// 返回匹配 /a[b-e]/ 文本的 div 元素。
// 会匹配 'ab', 'ac'。 不会匹配 'bb', 'aa'
Selector('div').withText(/a[b-e]/)
需要注意, withText 不仅会留下立即包含指定文本的元素,还会留下它的祖先元素。比如
<div class="container">
  <div class="child">some text</div>
</div>
// 这个选择器会匹配 '.container' 和 '.child'
Selector('div').withText('some text')
根据属性 (withAttribute)
| 参数 | 返回值类型 | 描述 | 
|---|---|---|
| withAttribute(attrName [, attrValue]) | Selector | 创建一个选择器,选择包含指定属性(或属性值)的元素。 | 
| 参数 | 类型 | 描述 | 
|---|---|---|
| attrName | string | RegExp | 属性名,区分大小写。 | 
| attrValue(可选的) | string | RegExp | 属性值,区分大小写。你可以省略它来匹配任何值。 | 
// 匹配任何含有 href 属性的 a 标签
Selector('a').withAttribute('href')
// 匹配 title 属性为‘图片’的 img 元素
Selector('img').withAttribute('title', '图片')
// 匹配任何 src 属性以 'https:https://static.mutoe.com' 开头的 img 元素
Selector('img').withAttribute('src', /^https?:\/\/static\.mutoe\.com/)
根据可见性 (filterVisible / filterHidden)
| 参数 | 返回值类型 | 描述 | 
|---|---|---|
| filterVisible() | Selector | 创建一个选择器,只留下可见的元素。这些元素没有 display: none;,visibility: hidden;,width: 0;或height: 0;这些属性 | 
| filterHidden() | Selector | 创建一个选择器,只留下不可见的元素。这些元素含有 display: none;,visibility: hidden;,width: 0;或height: 0;这些属性 | 
// 选择所有可见的 div 元素
Selector('div').filterVisible()
// 选择所有隐藏的 input 元素
Selector('input').filterHidden()
根据过滤器 (filter)
| 参数 | 返回值类型 | 描述 | 
|---|---|---|
| filter(cssSelector) | Selector | 创建一个选择器,根据 css 选择器的语法过滤。 | 
| filter(filterFn(node, idx), dependencies) | Selector | 创建一个选择器,根据节点的状态过滤。 | 
| 参数 | 类型 | 描述 | 
|---|---|---|
| cssSelector | string | css 选择器 | 
| filterFn(node, idx) | Function | 该方法将会在浏览器环境中执行 | 
| dependencies | Function | 需要传递到客户端的方法,否则调用时不会出现在浏览器上下文环境中 | 
| node | Element | 当前元素的 DOM node | 
| idx | number | 当前 DOM node 所处的索引 | 
// 从所有 li 标签中挑选含有 'active' class 的元素
Selector('li').filter('.active')
import { Selector } from 'testcafe'
fixture `Example page`
  .page `http://devexpress.github.io/testcafe/example/`
test('My test', async t => {
  const secondCheckBox = Selector('input')
    .withAttribute('type', 'checkbox')
    .nth(1)
  const checkedInputs = Selector('input')
    .withAttribute('type', 'checkbox')
    .filter(node => node.checked)
  const windowsLabel = Selector('label')
    .withText('Windows')
  await t
    .click(secondCheckBox)
    .expect(checkedInputs.count).eql(1)
    .click(windowsLabel)
})
如果所有的 DOM 都被过滤掉,filter() 选择器会返回 null
根据关系 (find / parent / child / sibling)
| 参数 | 返回值类型 | 描述 | 
|---|---|---|
| find(cssSelector) | Selector | 返回当前元素匹配的所有子节点,并通过 css 选择器筛选它们。 | 
| find(filterFn, dependencies) | Selector | 返回当前元素匹配的所有子节点,根据节点的状态过滤。 | 
| parent() | Selector | 返回当前元素匹配的父节点。(第一个元素将会是最近的父元素) | 
| parent(index) | Selector | 返回当前元素匹配的父节点,并通过索引筛选它们。 0是最近的父元素,负数将会从根结点开始计算。 | 
| parent(cssSelector) | Selector | 返回当前元素匹配的父节点,并通过 css 选择器筛选。 | 
| parent(filterFn, dependencies) | Selector | 返回当前元素匹配的父节点,根据节点的状态过滤。 | 
| child() | Selector | 返回当前元素匹配的子节点。(第一个元素将会是最近的子元素) | 
| child(index) | Selector | 返回当前元素匹配的子节点,并通过索引筛选它们。 0是最近的子元素,负数将会从根结点开始计算。 | 
| child(cssSelector) | Selector | 返回当前元素匹配的子节点,并通过 css 选择器筛选。 | 
| child(filterFn, dependencies) | Selector | 返回当前元素匹配的子节点,根据节点的状态过滤。 | 
| sibling() | Selector | 返回当前元素匹配的兄弟元素。(第一个元素将会是最近的兄弟元素) | 
| sibling(index) | Selector | 返回当前元素匹配的兄弟元素,并通过索引筛选它们。以 0开始,负数将会从末尾开始计算。 | 
| sibling(cssSelector) | Selector | 返回当前元素匹配的兄弟元素,并通过 css 选择器筛选。 | 
| sibling(filterFn, dependencies) | Selector | 返回当前元素匹配的兄弟元素,根据节点的状态过滤。 | 
| nextSibling() | Selector | 返回当前元素之后匹配的兄弟元素。(第一个元素将会是之后最近的兄弟元素) | 
| nextSibling(index) | Selector | 返回当前元素之后匹配的兄弟元素,并通过索引筛选它们。以 0开始,负数将会从末尾开始计算。 | 
| nextSibling(cssSelector) | Selector | 返回当前元素之后匹配的兄弟元素,并通过 css 选择器筛选。 | 
| nextSibling(filterFn, dependencies) | Selector | 返回当前元素之后匹配的兄弟元素,根据节点的状态过滤。 | 
| prevSibling() | Selector | 返回当前元素之前匹配的兄弟元素。(第一个元素将会是之前最近的兄弟元素) | 
| prevSibling(index) | Selector | 返回当前元素之前匹配的兄弟元素,并通过索引筛选它们。以 0开始,负数将会从末尾开始计算。 | 
| prevSibling(cssSelector) | Selector | 返回当前元素之前匹配的兄弟元素,并通过 css 选择器筛选。 | 
| prevSibling(filterFn, dependencies) | Selector | 返回当前元素之前匹配的兄弟元素,根据节点的状态过滤。 | 
例子
Selector('ul').find('label').parent('div.someClass')
找到页面上的所有ul元素。然后,在每个已找到的ul元素中找到label元素。然后,为每个label元素找到一个匹配div.someClass选择器的父元素。
Selector('.container').parent(1).nth(0).find('.content').withText('yo!').child('span')
这个例子做了以下事情:
- 找到 .container元素的第二个父元素(父元素的父元素)
- 选择匹配集中的第 1 个元素
- 在该元素中,查找与 .content选择器匹配的元素
- 找到文本包含 'yo!' 的元素
- 在每个已过滤的元素中,搜索标记名为 span的子项。
选择器选项 (no)
暂未更新
选择器拓展 (no)
暂未更新
边缘情况 (no)
暂未更新
DOM 节点状态
暂未更新
特定框架的选择器 (no)
暂未更新
一些例子 (no)
暂未更新
动作
单击 click
单击页面上的元素
t.click( selector [, options] )
| 参数 | 类型 | 描述 | 
|---|---|---|
| selector | Function | string | Selector | Snapshot | Promise | selector 选择器,有关选择器 | 
| options(可选的) | Object | 选项, 有关点击动作选项 | 
下面用一个例子来展示如何使用 t.click 动作来选择一个复选框元素。
test('Click a check box and check its state', async t => {
  const checkbox = Selector('#testing-on-remote-devices');
  await t
    .click(checkbox)
    .expect(checkbox.checked).ok();
});
下面一个例子使用 options 参数在输入框中设置光标位置
test('Click Input', async t => {
  const nameInput = Selector('#developer-name');
  await t
    .typeText(nameInput, 'Peter Parker')
    .click(nameInput, { caretPos: 5 })
    .pressKey('backspace')
    .expect(nameInput.value).eql('Pete Parker');
});
双击 doubleClick
双击页面上的元素
t.doubleClick( selector [, options] )
| 参数 | 类型 | 描述 | 
|---|---|---|
| selector | Function | string | Selector | Snapshot | Promise | selector 选择器,有关选择器 | 
| options(可选的) | Object | 选项, 有关点击动作选项 | 
右击 rightClick
右击页面上的元素
t.rightClick( selector [, options] )
| 参数 | 类型 | 描述 | 
|---|---|---|
| selector | Function | string | Selector | Snapshot | Promise | selector 选择器,有关选择器 | 
| options(可选的) | Object | 选项, 有关点击动作选项 | 
拖拽 drag
拖拽一定距离 drag
t.drag( selector, dragOffsetX, dragOffsetY [, options] )
| 参数 | 类型 | 描述 | 
|---|---|---|
| selector | Function | string | Selector | Snapshot | Promise | selector 选择器,有关选择器 | 
| dragOffsetX | Number | 鼠标在 x 轴上需要拖拽的距离 | 
| dragOffsetY | Number | 鼠标在 y 轴上需要拖拽的距离 | 
| options(可选的) | Object | 选项, 有关鼠标动作选项 | 
下面一个例子来演示如何使用 t.drag 动作来拖拽元素
test('Drag slider', async t => {
  const slider = Selector('#developer-rating');
  await t
    .click('#i-tried-testcafe');
    .expect(slider.value).eql(1)
    .drag('.ui-slider-handle', 360, 0, { offsetX: 10, offsetY: 10 })
    .expect(slider.value).eql(7);
});
拖拽到另一个元素上 dragToElement
t.dragToElement( selector, destinationSelector [, options] )
| 参数 | 类型 | 描述 | 
|---|---|---|
| selector | Function | string | Selector | Snapshot | Promise | selector 选择器,有关选择器 | 
| destinationSelector | Function | string | Selector | Snapshot | Promise | selector 选择器, 拖拽目标元素,有关选择器 | 
| options(可选的) | Object | 选项, 有关拖拽到元素动作选项 | 
下面这个例子演示了如何使用 t.dragToElement 将元素拖放到特定区域
test('Drag an item from the toolbox', async t => {
  const designSurfaceItems = Selector('.design-surface').find('.items');
  await t
    .dragToElement('.toolbox-item.text-input', '.design-surface')
    .expect(designSurfaceItems.count).gt(0);
});
悬停 hover
将鼠标指针悬停在网页元素上
t.hover( selector [, options] )
| 参数 | 类型 | 描述 | 
|---|---|---|
| selector | Function | string | Selector | Snapshot | Promise | selector 选择器,有关选择器 | 
| options(可选的) | Object | 选项, 有关鼠标动作选项 | 
使用此操作可以调用弹出元素,例如悬停在其他元素上时出现的提示窗口、弹出菜单或下拉列表。
下面这例子演示如何将鼠标指针移动到组合框上显示下拉列表,然后选择一个项目并检查组合框的值。
test('Select combo box value', async t => {
  const comboBox = Selector('.combo-box');
  await t
    .hover(comboBox)
    .click('#i-prefer-both')
    .expect(comboBox.value).eql('Both');
});
选择文本 selectText
在 input 元素中
t.selectText( selector [, startPos] [, endPos] [, options] )
| 参数 | 类型 | 描述 | 默认值 | 
|---|---|---|---|
| selector | Function | string | Selector | Snapshot | Promise | selector 选择器,有关选择器 | |
| startPos(可选的) | Number | 选择的起始位置,从 0 开始的整数 | 0 | 
| endPos(可选的) | Number | 选择的结束位置,从 0 开始的整数 | 可见文本的长度 | 
| options(可选的) | Object | 选项, 有关基本动作选项 | 
const developerNameInput = Selector('#developer-name');
const getElementSelectionStart = ClientFunction(selector => selector().selectionStart);
const getElementSelectionEnd = ClientFunction(selector => selector().selectionEnd);
test('Select text within input', async t => {
  await t
    .typeText(developerNameInput, 'Test Cafe', { caretPos: 0 })
    .selectText(developerNameInput, 7, 1);
  await t
    .expect(await getElementSelectionStart(developerNameInput)).eql(1)
    .expect(await getElementSelectionEnd(developerNameInput)).eql(7);
});
如果
startPos的值大于endPos的值,则动作将执行向前选择。
在 textarea 元素中
t.selectTextAreaContent( selector [, startLine] [, startPos] [, endLine] [, endPos] [, options] )
| 参数 | 类型 | 描述 | 默认值 | 
|---|---|---|---|
| selector | Function | string | Selector | Snapshot | Promise | selector 选择器,有关选择器 | |
| startLine(可选的) | Number | 选择开始的行号,从 0 开始的整数 | 0 | 
| startPos(可选的) | Number | 选择的起始位置,从 0 开始的整数 | 0 | 
| endLine(可选的) | Number | 选择结束的行号,从 0 开始的整数 | 最后一行的索引 | 
| endPos(可选的) | Number | 选择的结束位置(基于 endline),从 0 开始的整数 | endLine的最后一个字符 | 
| options(可选的) | Object | 选项, 有关基本动作选项 | 
const commentTextArea = Selector('#comments');
const getElementSelectionStart = ClientFunction(selector => selector().selectionStart);
const getElementSelectionEnd   = ClientFunction(selector => selector().selectionEnd);
test('Select text within textarea', async t => {
  await t
    .click('#tried-test-cafe')
    .typeText(commentTextArea, [
      'Lorem ipsum dolor sit amet',
      'consectetur adipiscing elit',
      'sed do eiusmod tempor'
    ].join(',\n'))
    .selectTextAreaContent(commentTextArea, 0, 5, 2, 10);
  await t
    .expect(await getElementSelectionStart(commentTextArea)).eql(5)
    .expect(await getElementSelectionEnd(commentTextArea)).eql(67);
});
如果
startLine比endLine的值大,则执行向前选择。
在 contentEditable 元素中
t.selectEditableContent( startSelector, endSelector [, options] )
| 参数 | 类型 | 描述 | 默认值 | 
|---|---|---|---|
| startSelector | Function | string | Selector | Snapshot | Promise | 标识开始选择的元素 selector 选择器,有关选择器 | |
| endSelector | Function | string | Selector | Snapshot | Promise | 标识结束选择的元素 selector 选择器,有关选择器 | |
| options(可选的) | Object | 选项, 有关基本动作选项 | 
此方法适用于启用了 contentEditable 属性的 HTML 元素。
test('Delete text within a contentEditable element', async t => {
  await t
    .selectEditableContent('#foreword', '#chapter-3')
    .pressKey('delete')
    .expect(Selector('#chapter-2').exists).notOk()
    .expect(Selector('#chapter-4').exists).ok();
});
键入文本 typeText
t.typeText( selector, text [, options] )
| 参数 | 类型 | 描述 | 
|---|---|---|
| selector | Function | string | Selector | Snapshot | Promise | selector 选择器,有关选择器 | 
| text | string | 要在指定元素中输入的文本 | 
| options(可选的) | Object | 选项, 有关输入动作选项 | 
如果要删除文本,请使用
t.selectText和t.pressKey来实现
test('Type and Replace', async t => {
  const nameInput = Selector('#developer-name');
  await t
    .typeText(nameInput, 'Peter')
    .typeText(nameInput, 'Paker', { replace: true })
    .typeText(nameInput, 'r', { caretPos: 2 })
    .expect(nameInput.value).eql('Parker');
});
注意
某些类型的HTML5输入(如 DateTime , Color 或 Range)需要以特定格式输入值。
The following table lists value formats expected by these inputs.
输入类型 模式 例子 Date yyyy-MM-dd2018-10-25Week yyyy-Www2018-W03Month yyyy-MM2018-10DateTime yyyy-MM-ddThh:mm2018-10-25T13:22Time hh:mm13:22:28Color #rrggbb#003500Range n45
按键 pressKey
t.pressKey( keys [, options] )
| 参数 | 类型 | 描述 | 
|---|---|---|
| keys | string | 要在指定元素中输入的文本 | 
| options(可选的) | Object | 选项, 有关基本动作选项 | 
下表显示了如何指定不同类型,键序列和组合的键。
| 按键类型 | 例子 | 
|---|---|
| 字母和数字 | a,A,1 | 
| 修饰键 | shift,alt(⌥),ctrl,meta(⌘) | 
| 导航和操作键 | backspace,tab,enter,capslock,esc,space,pageup,pagedown,end,home,left,right,up,down,ins,delete | 
| 组合键 | shift+a,ctrl+d | 
| 一番操作 | ctrl+a del,ctrl+a ctrl+c(自由组合按键,并使用空格分隔它们) | 
test('Key Presses', async t => {
  const nameInput = Selector('#developer-name');
  await t
    .typeText(nameInput, 'Peter Parker')
    .pressKey('home right . delete delete delete delete')
    .expect(nameInput.value).eql('P. Parker');
});
页面跳转 navigateTo
t.navigateTo( url )
| 参数 | 类型 | 描述 | 
|---|---|---|
| url | string | 要导航到的 URL 地址。可以是绝对地址或相对地址 | 
下面的例子来演示如何导航到一个绝对地址
test('Navigate to the main page', async t => {
  await t
    .click('#submit-button')
    .navigateTo('http://devexpress.github.io/testcafe');
});
在重定向发生后,TestCafe 会自动等待服务器响应。如果服务器在 15s 内没有响应,测试将会被恢复(?)。
截图 takeScreenshot
获取整页的屏幕截图 takeScreenshot
t.takeScreenshot( [path] )
| 参数 | 类型 | 描述 | 
|---|---|---|
| path(可选的) | string | 截图文件保存的相对路径和名称,该相对路径是基于命令行制定的基本目录。 | 
test('Take a screenshot of a fieldset', async t => {
  await t
    .typeText('#developer-name', 'Peter Parker')
    .click('#submit-button')
    .takeScreenshot('my-fixture/thank-you-page.png');
});
获取页面元素的屏幕截图 takeElementScreenshot
t.takeScreenshot( [path] )
| 参数 | 类型 | 描述 | 
|---|---|---|
| selector | Function | string | Selector | Snapshot | Promise | selector 选择器,有关[选择目标元素] | 
| path(可选的) | string | 截图文件保存的相对路径和名称,该相对路径是基于命令行制定的基本目录。 | 
| options(可选的) | Object | 用于定义屏幕截图截取方式的选项。详情见下文。 | 
test('Take a screenshot of a fieldset', async t => {
  await t
    .click('#reusing-js-code')
    .click('#continuous-integration-embedding')
    .takeElementScreenshot(Selector('fieldset').nth(1), 'my-fixture/important-features.png');
});
options 对象包含以下属性
| 属性 | 类型 | 描述 | 默认值 | 
|---|---|---|---|
| scrollTargetX,scrollTargetY | number | 该点的坐标,是相对于目标元素计算的。 | 元素的中心。 | 
| includeMargins | boolean | 是否包含外边距 (margin) | false | 
| includeBorders | boolean | 是否包含边框 (border) | true | 
| includePaddings | boolean | 是否包含内边距 (padding) | true | 
| crop | Object | 是否允许裁剪屏幕截图上的目标元素。 | 裁剪到整个元素。如果它不适合窗口大小,则为其可见部分。 | 
crop 对象具有以下字段
| 字段 | 类型 | 描述 | 
|---|---|---|
| top | number | 裁剪矩形的上边缘。如果传递负数,则从元素的下边缘计算坐标。 | 
| left | number | 裁剪矩形的左边缘。如果传递负数,则从元素的右边缘计算坐标。 | 
| bottom | number | 裁剪矩形的下边缘。如果传递负数,则从元素的上边缘计算坐标。 | 
| right | number | 裁剪矩形的右边缘。如果传递负数,则从元素的左边缘计算坐标。 | 

test('Take a screenshot of my new avatar', async t => {
  await t
    .click('#change-avatar')
    .setFilesToUpload('#upload-input', 'img/portrait.jpg')
    .click('#submit')
    .takeElementScreenshot('#avatar', {
      includeMargins: true,
      crop: {
        top: -100,
        left: 10,
        bottom: 30,
        right: 200
      }
    });
});
上传 setFilesToUpload (no)
暂未更新
调整窗口尺寸 resize (no)
暂未更新
动作选项 options
基本动作选项 basic action
{
  speed: Number
}
| 参数 | 类型 | 描述 | 默认值 | 
|---|---|---|---|
| speed | number | 动作的速度,在 1(最大速度) 和0.01(最小速度) 之间 | 1 | 
基本动作选项用于 t.pressKey,t.selectText,t.selectTextAreaContent 和 t.selectEditableContent 动作。
test('My Test', async t => {
  const nameInput = Selector('#developer-name');
  await t
    .typeText(nameInput, 'Peter')
    .typeText(nameInput, ' Parker', { speed: 0.1 });
});
鼠标动作选项 mouse action
{
  modifiers: {
    ctrl: Boolean,
    alt: Boolean,
    shift: Boolean,
    meta: Boolean
  },
  offsetX: Number,
  offsetY: Number,
  speed: Number
}
| 参数 | 类型 | 描述 | 默认值 | 
|---|---|---|---|
| ctrlaltshiftmeta | boolean | 在鼠标操作期间要按下的修饰键 | false | 
| offsetXoffsetY | number | 鼠标指针坐标,正整数从左上角计算,负整数从右下角计算 | 目标元素的中心 | 
| speed | number | 动作的速度,在 1(最大速度) 和0.01(最小速度) 之间 | 1 | 
鼠标动作选项用于 t.drag 和 t.hover 动作。
test('My Test', async t => {
  await t
    .drag(sliderHandle, 360, 0, {
      offsetX: 10,
      offsetY: 10,
      modifiers: {
        shift: true
      }
    });
});
拖拽到元素动作选项 dragToElement action
{
  modifiers: {
    ctrl: Boolean,
    alt: Boolean,
    shift: Boolean,
    meta: Boolean
  },
  offsetX: Number,
  offsetY: Number,
  destinationOffsetX: Number,
  destinationOffsetY: Number,
  speed: Number
}
| 参数 | 类型 | 描述 | 默认值 | 
|---|---|---|---|
| ctrlaltshiftmeta | boolean | 在鼠标操作期间要按下的修饰键 | false | 
| offsetXoffsetY | number | 鼠标指针坐标,正整数从左上角计算,负整数从右下角计算 | 目标元素的中心 | 
| destinationOffsetXdestinationOffsetY | number | 鼠标拖拽完成时的指针坐标,正整数从左上角计算,负整数从右下角计算 | 目标元素的中心 | 
| speed | number | 动作的速度,在 1(最大速度) 和0.01(最小速度) 之间 | 1 | 
拖拽到元素动作选项用于 t.dragToElement 动作。
test('My Test', async t => {
  const fileIcon      = Selector('.file-icon');
  const directoryPane = Selector('.directory');
  await t
    .dragToElement(fileIcon, directoryPane, {
      offsetX: 10,
      offsetY: 10,
      destinationOffsetX: 100,
      destinationOffsetY: 50,
      modifiers: {
        shift: true
      }
    });
});
点击动作选项 click action
{
  modifiers: {
    ctrl: Boolean,
    alt: Boolean,
    shift: Boolean,
    meta: Boolean
  },
  offsetX: Number,
  offsetY: Number,
  caretPos: Number,
  speed: Number
}
| 参数 | 类型 | 描述 | 默认值 | 
|---|---|---|---|
| ctrlaltshiftmeta | boolean | 在鼠标操作期间要按下的修饰键 | false | 
| offsetXoffsetY | number | 鼠标指针坐标,正整数从左上角计算,负整数从右下角计算 | 目标元素的中心 | 
| caretPos | number | 如果在输入元素上执行动作,则为初始插入符号位置,从零开始的整数。 | 文本长度 | 
| speed | number | 动作的速度,在 1(最大速度) 和0.01(最小速度) 之间 | 1 | 
点击操作选项用于 t.click,t.doubleClick 和 t.rightClick 动作。
test('My Test', async t => {
  const nameInput = Selector('#developer-name');
  await t
    .typeText(nameInput, 'Pete Parker')
    .click(nameInput, { caretPos: 4 })
    .pressKey('r');
});
输入动作选项 typing action
{
  modifiers: {
    ctrl: Boolean,
    alt: Boolean,
    shift: Boolean,
    meta: Boolean
  },
  offsetX: Number,
  offsetY: Number,
  caretPos: Number,
  replace: Boolean,
  paste: Boolean,
  speed: Number
}
| 参数 | 类型 | 描述 | 默认值 | 
|---|---|---|---|
| ctrlaltshiftmeta | boolean | 在鼠标操作期间要按下的修饰键 | false | 
| offsetXoffsetY | number | 鼠标指针坐标,正整数从左上角计算,负整数从右下角计算 | 目标元素的中心 | 
| caretPos | number | 如果在输入元素上执行动作,则为初始插入符号位置,从零开始的整数。 | 文本长度 | 
| replace | boolean | 是否替换原有文本 | false | 
| paste | boolean | 是否使用粘贴的方式一次性键入文本 | false | 
| speed | number | 动作的速度,在 1(最大速度) 和0.01(最小速度) 之间 | 1 | 
输入动作选项用于 t.typeText 动作。
test('My Test', async t => {
  await t
    .typeText(nameInput, 'Peter')
    .typeText(nameInput, 'Parker', { replace: true });
});
断言
你可以使用断言来检查测试的网页状态是否与预期状态匹配。
TestCafe 提供了一组基于行为驱动开发风格(BDD风格)的断言方法。
构造断言
要构造一个断言,可以使用测试控制器 (t) 的 expect 方法。
await t.expect( actual )
这个构造方法接受一个实际值,可以是 selector 的 DOM 节点状态属性或者是一个从页面中侦听到的 promise 对象。 TestCafe 会自动等待节点状态值的变动。
接下来跟一个断言方法,他接受期望值和一些其他的可选参数。
断言方法
等于 eql
await t.expect( actual ).eql( expected [, message] [, options ])
| 参数 | 类型 | 描述 | 
|---|---|---|
| actual | Any | 比较值,如果是一个 promise 对象,TestCafe 会自动等待值的变化 | 
| expected | Any | 期望值 | 
| message(optional) | string | 如果测试失败,需要在测试报告中输出的字符串 | 
| options(optional) | Object | 参见断言选项 | 
await t
  .expect({ a: 'bar' }).eql({ a: 'bar' }, '这个断言将会通过')
  .expect({ a: 'bar' }).eql({ a: 'foo' }, '这个断言将会失败,并且这句话会被打印出来');
test('My test', async t => {
  await t.expect(Selector('.className').count).eql(3);
});
不等于 notEql
await t.expect( actual ).notEql( unexpected [, message] [, options ])
| 参数 | 类型 | 描述 | 
|---|---|---|
| actual | Any | 比较值,如果是一个 promise 对象,TestCafe 会自动等待值的变化 | 
| unexpected | Any | 不期望的值 | 
| message(optional) | string | 如果测试失败,需要在测试报告中输出的字符串 | 
| options(optional) | Object | 参见断言选项 | 
await t
  .expect({ a: 'bar' }).notEql({ a: 'bar' }, '这个断言将会失败,并且这句话会被打印出来')
  .expect({ a: 'bar' }).notEql({ a: 'foo' }, '这个断言将会通过');
test('My test', async t => {
  await t.expect(Selector('.className').count).notEql(2);
});
真值 ok
await t.expect( actual ).ok( [ message ] [, options ])
| 参数 | 类型 | 描述 | 
|---|---|---|
| actual | Any | 比较值,如果是一个 promise 对象,TestCafe 会自动等待值的变化 | 
| message(optional) | string | 如果测试失败,需要在测试报告中输出的字符串 | 
| options(optional) | Object | 参见断言选项 | 
await t
  .expect('ok').ok('这个断言将会通过')
  .expect(false).ok('这个断言将会失败,并且这句话会被打印出来');
test('My test', async t => {
  await t.expect(Selector('#element').exists).ok();
});
假值 notOk
await t.expect( actual ).notOk( [ message ] [, options ])
| 参数 | 类型 | 描述 | 
|---|---|---|
| actual | Any | 比较值,如果是一个 promise 对象,TestCafe 会自动等待值的变化 | 
| message(optional) | string | 如果测试失败,需要在测试报告中输出的字符串 | 
| options(optional) | Object | 参见断言选项 | 
await t
  .expect('ok').notOk('这个断言将会失败,并且这句话会被打印出来')
  .expect(false).notOk('这个断言将会通过');
test('My test', async t => {
  await t.expect(Selector('#element').exists).notOk();
});
包含 contains
await t.expect( actual ).contains( expected [, message ] [, options ])
| 参数 | 类型 | 描述 | 
|---|---|---|
| actual | Any | 比较值,如果是一个 promise 对象,TestCafe 会自动等待值的变化 | 
| expected | Any | 期望值 | 
| message(optional) | string | 如果测试失败,需要在测试报告中输出的字符串 | 
| options(optional) | Object | 参见断言选项 | 
await t
  .expect('foo bar').contains('bar', '用例未通过:字符串 "foo bar" 里面不含有期望的 "bar" 子串')
  .expect([1, 2, 3]).contains(2, '用例未通过:数组中不含有期望的值')
  .expect({ foo: 'bar', hello: 'universe' }).contains({ foo: 'bar' }, '用例未通过:对象中不含有期望的属性')
test('My test', async t => {
  const getLocation = ClientFunction(() => document.location.href.toString())
  await t.expect(getLocation()).contains('example.com', '用例未通过:网址不包含期望的值');
});
不包含 notContains
await t.expect( actual ).notContains( unexpected [, message ] [, options ])
| 参数 | 类型 | 描述 | 
|---|---|---|
| actual | Any | 比较值,如果是一个 promise 对象,TestCafe 会自动等待值的变化 | 
| unexpected | Any | 不期望的值 | 
| message(optional) | string | 如果测试失败,需要在测试报告中输出的字符串 | 
| options(optional) | Object | 参见断言选项 | 
await t
  .expect('foo bar').notContains('bar', '用例未通过:字符串中含有不期望的子串')
  .expect([1, 2, 3]).notContains(2, '用例未通过:数组中含有不期望的值')
  .expect({ foo: 'bar', hello: 'universe' }).notContains({ buzz: 'abc' }, '用例未通过:对象中含有不期望的属性')
test('My test', async t => {
  const getLocation = ClientFunction(() => document.location.href.toString())
  await t.expect(getLocation()).notContains('example.com', '用例未通过:网址包含了不被期望的值');
});
类型等于 tyoeOf
await t.expect( actual ).typeOf( typeName [, message ] [, options ])
| 参数 | 类型 | 描述 | 
|---|---|---|
| actual | Any | 比较值,如果是一个 promise 对象,TestCafe 会自动等待值的变化 | 
| typeName | string | 期望的 actual的类型 | 
| message(optional) | string | 如果测试失败,需要在测试报告中输出的字符串 | 
| options(optional) | Object | 参见断言选项 | 
await t
  .expect({ a: 'bar' }).typeOf('object', '用例未通过:比较值不是对象')
  .expect(/bar/).typeOf('regexp', '用例未通过:比较值不是正则表达式')
  .expect(null).typeOf('null', '用例未通过:比较值不为null')
test('My test', async t => {
  await t.expect(Selector('#element').getAttribute('attr')).typeOf('string');
});
类型不等于 notTypeOf
await t.expect( actual ).notTypeOf( typeName [, message ] [, options ])
| 参数 | 类型 | 描述 | 
|---|---|---|
| actual | Any | 比较值,如果是一个 promise 对象,TestCafe 会自动等待值的变化 | 
| typeName | string | 不期望的 actual的类型 | 
| message(optional) | string | 如果测试失败,需要在测试报告中输出的字符串 | 
| options(optional) | Object | 参见断言选项 | 
await t
  .expect('bar').notTypeOf('number', '用例未通过:比较值不是数字类型')
test('My test', async t => {
  await t.expect(Selector('#element').getAttribute('attr')).notTypeOf('null');
});
大于 gt
await t.expect( actual ).gt( expected [, message ] [, options ])
| 参数 | 类型 | 描述 | 
|---|---|---|
| actual | Any | 比较值,如果是一个 promise 对象,TestCafe 会自动等待值的变化 | 
| expected | Any | 期望的值 | 
| message(optional) | string | 如果测试失败,需要在测试报告中输出的字符串 | 
| options(optional) | Object | 参见断言选项 | 
await t.expect(5).gt(2, '用例未通过:比较值应该比 2 大')
test('My test', async t => {
  await t.expect(Selector('#element').clientWidth).gt(300);
});
大于等于 gte
await t.expect( actual ).gte( expected [, message ] [, options ])
用法同 gt
小于 lt
await t.expect( actual ).lt( expected [, message ] [, options ])
用法同 gt
小于等于 lte
await t.expect( actual ).lte( expected [, message ] [, options ])
用法同 gt
在某个范围 within
await t.expect( actual ).within( start, finish [, message ] [, options ])
| 参数 | 类型 | 描述 | 
|---|---|---|
| actual | number | 比较值,如果是一个 promise 对象,TestCafe 会自动等待值的变化 | 
| start | number | 范围下限(包含) | 
| finish | number | 范围上限(包含) | 
| message(optional) | string | 如果测试失败,需要在测试报告中输出的字符串 | 
| options(optional) | Object | 参见断言选项 | 
await t.expect(5).within(3, 10, '这个断言将会通过');
test('My test', async t => {
  await t.expect(Selector('#element').scrollTop).within(300, 400);
});
不在某个范围 notWithin
await t.expect( actual ).notWithin( start, finish [, message ] [, options ])
用法同 within
正则匹配 match
await t.expect( actual ).match( regexp [, message ] [, options ])
| 参数 | 类型 | 描述 | 
|---|---|---|
| actual | string | 比较值,如果是一个 promise 对象,TestCafe 会自动等待值的变化 | 
| regexp | RegExp | 用来匹配 actual的正则表达式 | 
| message(optional) | string | 如果测试失败,需要在测试报告中输出的字符串 | 
| options(optional) | Object | 参见断言选项 | 
await t.expect('foobar').match(/^f/, '这个断言将会通过');
test('My test', async t => {
  const getLocation = ClientFunction(() => document.location.href.toString());
  await t.expect(getLocation()).match(/\.com/);
});
非正则匹配 notMatch
await t.expect( actual ).notMatch( regexp [, message ] [, options ])
用法同 match
断言选项
{
  timeout: Number,
  allowUnawaitedPromise: Boolean
}
| 参数 | 类型 | 描述 | 默认值 | 
|---|---|---|---|
| timeout | number | 如果在断言中使用了属性选择器或客户端函数,可等待的最大时间 (单位:ms) | |
| allowUnawaitedPromise | boolean | 如果你要断言一个常规的 promise 方法,请将该选项设置为 true | 
await t.expect(Selector('#elementId').innerText).eql('text', '在 500ms 内检查元素的文本', { timeout: 500 });
await t.expect(doSomethingAsync()).ok('检查异步函数是否含回了 promise 对象', { allowUnawaitedPromise: true });
客户端方法 (no)
暂未更新
等待
无论出于什么原因,让测试暂停一小会
t.wait( timeout )
| 参数 | 类型 | 描述 | 
|---|---|---|
| timeout | number | 暂停的持续时间,单位 ms | 
await t
  .click('#play-1-sec-animation')
  .wait(1000)
  .expect(header.getStyleProperty('opacity')).eql(0);