事件目录
Cypress在浏览器中运行时会产生一系列事件。
这些事件不仅对控制应用程序行为有用,也对调试目的非常实用。
以下是你可以利用这些事件实现的一些示例:
- 监听
uncaught exceptions
并阻止Cypress使测试失败 - 监听
alert
或confirm
调用并改变confirm
行为 - 监听
window:before:load
事件并在页面跳转期间任何应用代码执行前修改window
- 监听
command:retry
事件以理解Cypress为何内部重试,用于调试目的
事件类型
应用事件
这些事件来自当前被测试的应用(你的应用)。这些是你最需要关注的事件。
事件 | 详情 |
---|---|
名称: | uncaught:exception |
返回: | 错误 (Object), Mocha可运行对象 (Object) |
描述: | 当应用中发生未捕获异常时触发。Cypress会使测试失败。从此事件返回false 可阻止Cypress使测试失败。也适用于调试,因为会提供实际的error 实例。参见我们的配方处理错误。 |
事件 | 详情 |
---|---|
名称: | window:confirm |
返回: | 确认文本 (String) |
描述: | 当应用调用全局window.confirm() 方法时触发。Cypress会自动接受确认。从此事件返回false 可取消确认。 |
事件 | 详情 |
---|---|
名称: | window:alert |
返回: | 警告文本 (String) |
描述: | 当应用调用全局window.alert() 方法时触发。Cypress会自动接受警告。你无法改变此行为。 |
事件 | 详情 |
---|---|
名称: | window:before:load 仅限端到端测试 |
返回: | 远程窗口 (Object) |
描述: | 在页面开始加载时触发,但在任何应用JavaScript执行之前。这与cy.visit() 的onBeforeLoad 回调同时触发。适用于在页面跳转时修改窗口。 |
事件 | 详情 |
---|---|
名称: | window:load |
返回: | 远程窗口 (Object) |
描述: | 在页面跳转后所有资源加载完成时触发。这与cy.visit() 的onLoad 回调同时触发。 |
事件 | 详情 |
---|---|
名称: | window:before:unload 仅限端到端测试 |
返回: | 实际的beforeunload事件 (Object) |
描述: | 当应用即将导航离开时触发。提供真实的事件对象。应用可能在事件上设置了returnValue ,这对断言很有用。 |
事件 | 详情 |
---|---|
名称: | window:unload 仅限端到端测试 |
返回: | 实际的unload事件 (Object) |
描述: | 当应用已卸载并正在导航离开时触发。提供真实的事件对象。此事件不可取消。 |
事件 | 详情 |
---|---|
名称: | url:changed |
返回: | 新URL (String) |
描述: | 当Cypress检测到应用URL变化时触发。 |
Cypress事件
这些事件来自Cypress,当它发出命令并响应其状态时产生。这些事件对调试非常有用。
事件 | 详情 |
---|---|
名称: | fail |
返回: | 错误 (Object), Mocha可运行对象 (Object) |
描述: | 当测试失败时触发。绑定此事件而不重新抛出错误会阻止测试进入"失败"状态。后续命令不会执行。但强烈不建议这样做。测试不应合法失败。此事件存在是因为它对调试极其有用。参见我们的配方处理错误。 |
事件 | 详情 |
---|---|
名称: | viewport:changed |
返回: | 新视口 (Object) |
描述: | 当通过cy.viewport() 改变视口或Cypress在测试间重置视口为默认值时触发。对调试有用。 |
事件 | 详情 |
---|---|
名称: | scrolled |
返回: | 被滚动的元素或窗口 (Object) |
描述: | 当Cypress滚动你的应用时触发。此事件在Cypress等待并计算可操作性时触发。它会滚动以"发现"当前被覆盖的元素。此事件对调试Cypress为何认为元素不可交互非常有用。 |
事件 | 详情 |
---|---|
名称: | command:enqueued |
返回: | 命令属性和参数 (Object) |
描述: | 当cy命令首次调用并排队等待稍后运行时触发。对调试命令执行顺序的困惑非常有用。 |
事件 | 详情 |
---|---|
名称: | command:start |
返回: | 命令实例 (Object) |
描述: | 当cy开始实际运行和执行你的命令时触发。对调试和理解命令队列的异步性非常有用。 |
事件 | 详情 |
---|---|
名称: | command:end |
返回: | 命令实例 (Object) |
描述: | 当cy完成运行和执行你的命令时触发。对调试和理解命令处理方式非常有用。 |
事件 | 详情 |
---|---|
名称: | command:retry |
返回: | 重试选项 (Object) |
描述: | 当命令开始其重试例程时触发。在Cypress内部等待重试间隔后在尾部边缘调用。对理解为何命令重试非常有用,通常包括导致重试的实际错误。当命令失败时,最终的错误是导致测试失败的错误。此事件本质上用于调试Cypress为何失败。 |
事件 | 详情 |
---|---|
名称: | log:added |
返回: | 日志属性 (Object), 日志实例 (Object) |
描述: | 当命令发出此事件以便在命令日志中显示时触发。对查看内部cypress命令如何使用Cypress.log() API非常有用。 |
事件 | 详情 |
---|---|
名称: | log:changed |
返回: | 日志属性 (Object), 日志实例 (Object) |
描述: | 当命令属性变化时触发。此事件被防抖以防止触发过快和过于频繁。对查看内部cypress命令如何使用Cypress.log() API非常有用。 |
事件 | 详情 |
---|---|
名称: | test:before:run |
返回: | 测试属性 (Object), 可运行实例 (Object) |
描述: | 在测试及所有before和beforeEach钩子运行前触发。 |
事件 | 详情 |
---|---|
名称: | test:after:run |
返回: | 测试属性 (Object), 可运行实例 (Object) |
描述: | 在测试及所有afterEach和after钩子运行后触发。 |
其他事件
Cypress还触发大量其他事件以与Node服务器进程、自动化服务器、mocha、应用和报告器通信。这些事件严格内部于Cypress工作方式,对用户无用。
绑定事件
全局Cypress
和cy
对象都是标准的Node事件发射器。这意味着你可以 使用以下方法绑定和解绑事件。
理解为何要绑定到Cypress
或cy
很重要。
Cypress
Cypress是一个全局对象,在所有测试期间持续存在。绑定到Cypress的任何事件将应用于所有测试,除非手动解绑,否则不会解绑。
这在调试时非常有用,当你想添加一个单一的"捕获所有"事件来追踪诸如测试失败或应用中未捕获异常等问题时。
cy
cy
对象绑定到每个单独的测试。绑定到cy
的事件将在测试结束时自动移除。你无需担心清理,事件监听器仅在单个测试期间被调用。
示例
未捕获异常
关闭所有未捕获异常处理
放置此配置的理想位置是在 supportFile中, 因为它会在任何测试文件执行前加载。
Cypress.on('uncaught:exception', (err, runnable) => {
// 返回false阻止Cypress使测试失败
return false
})
如果未捕获异常来自cy.origin()
命令内的域,则需要在cy.origin()
命令内添加未捕获异常处理程序才能生效。
cy.origin('https://example.cypress.io', () => {
Cypress.on('uncaught:exception', (err, runnable) => {
// 返回false阻止cy.origin()内的Cypress使测试失败
return false
})
})
有条件地关闭特定错误的未捕获异常处理
放置此配置的理想位置是在 supportFile中, 因为它会在任何测试文件执行前加载。
Cypress.on('uncaught:exception', (err, runnable) => {
// 我们预期第三方库错误消息为'list not defined'
// 不希望测试失败,因此返回false
if (err.message.includes('list not defined')) {
return false
}
// 仍希望确保没有其他意外错误,因此让它们使测试失败
})
有条件地关闭未处理Promise拒绝的未捕获异常处理
放置此配置的理想位置是在 supportFile中, 因为它会在任何测试文件执行前加载。
Cypress.on('uncaught:exception', (err, runnable, promise) => {
// 当异常源自未处理Promise拒绝时
// promise作为第三个参数提供
// 可以关闭此情况下的测试失败
if (promise) {
return false
}
// 仍希望确保没有其他意外错误,因此让它们使测试失败
})
捕获单个未捕获异常
it('is doing something very important', (done) => {
// 此事件将在测试结束时自动解绑
// 因为它绑定到'cy'
cy.on('uncaught:exception', (err, runnable) => {
expect(err.message).to.include('something about the error')
// 使用mocha的异步done回调完成
// 此测试以证明未捕获异常被抛出
done()
// 返回false阻止错误使测试失败
return false
})
// 假设这会导致错误
cy.get('button').click()
})
捕获测试失败
调试测试失败的时刻
如果你想调试任何测试失败,你可能希望将此放入supportFile,或单个spec文件的顶部。
Cypress.on('fail', (error, runnable) => {
debugger
// 现在可以访问err实例
// 和失败的mocha可运行对象
throw error // 抛出错误以使测试仍然失败
})
it('calls the "fail" callback when this test fails', () => {
// 当此cy.get()失败时
// 回调会以错误调用
cy.get('element-that-does-not-exist')
})
页面导航 仅限端到端测试
测试应用是否被重定向
// 应用代码
$('button').on('click', (e) => {
// 以编程方式改变页面
window.location.href = '/some/new/link'
})
// 测试代码
it('redirects to another page on click', (done) => {
// 此事件将在测试结束时自动解绑
// 因为它绑定到'cy'
cy.on('window:before:unload', (e) => {
// 事件上没有返回值
expect(e.returnValue).to.be.undefined
})
cy.on('window:unload', (e) => {
// 使用mocha的异步done回调完成
// 此测试以确保应用在导航到新页面时已卸载
done()
})
// 点击导致页面重定向的按钮
cy.get('button').click()
})
窗口加载前 仅限端到端测试
在页面跳转后应用加载前修改应用
it('can modify the window prior to page load on all pages', () => {
// 在此创建存根
const ga = cy.stub().as('ga')
// 阻止谷歌分析加载
// 并在每次页面加载前替换为存根
// 包括所有新页面导航
cy.on('window:before:load', (win) => {
Object.defineProperty(win, 'ga', {
configurable: false,
get: () => ga, // 始终返回存根
set: () => {}, // 不允许实际谷歌分析覆盖此属性
})
})
cy
// window:before:load将在此调用
.visit('/first/page')
.then((win) => {
// 和这里
win.location.href = '/second/page'
})
// 和这里
cy.get('a').click()
})
窗口确认
控制是否接受或拒绝确认
这使你能够测试应用对接受确认和拒绝确认的反应。
// 应用代码
$('button').on('click', (e) => {
const one = confirm('first confirm')
if (one) {
const two = confirm('second confirm')
if (!two) {
const three = confirm('third confirm')
confirm('third confirm was ' + three)
}
}
})
// 测试代码
it('can control application confirms', (done) => {
let count = 0
// 确保在应用调用confirm方法前绑定此事件
//
// 此事件将在测试结束时自动解绑
// 因为它绑定到'cy'
cy.on('window:confirm', (str) => {
count += 1
switch (count) {
case 1:
expect(str).to.eq('first confirm')
// 不返回任何内容会自动接受确认
case 2:
expect(str).to.eq('second confirm')
// 拒绝确认
return false
case 3:
expect(str).to.eq('third confirm')
// 不必返回true,但也可以
return true
case 4:
expect(str).to.eq('third confirm was true')
// 使用mocha的异步done回调完成
// 此测试以确保一切顺利到达此点而不抛出错误
done()
}
})
// 点击导致确认触发的按钮
cy.get('button').click()
})
it('could also use a stub instead of imperative code', () => {
const stub = cy.stub()
// 非必要,但为了清晰展示
stub.onFirstCall().returns(undefined)
stub.onSecondCall().returns(false)
stub.onThirdCall().returns(true)
cy.on('window:confirm', stub)
cy.get('button')
.click()
.then(() => {
expect(stub.getCall(0)).to.be.calledWith('first confirm')
expect(stub.getCall(1)).to.be.calledWith('second confirm')
expect(stub.getCall(2)).to.be.calledWith('third confirm')
expect(stub.getCall(3)).to.be.calledWith('third confirm was true')
})
})
窗口警告
断言警告文本内容
Cypress自动接受警告,但你仍可以断言文本内容。
// 应用代码
$('button').on('click', (e) => {
alert('hi')
alert('there')
alert('friend')
})
it('can assert on the alert text content', () => {
const stub = cy.stub()
cy.on('window:alert', stub)
cy.get('button')
.click()
.then(() => {
expect(stub.getCall(0)).to.be.calledWith('hi')
expect(stub.getCall(1)).to.be.calledWith('there')
expect(stub.getCall(2)).to.be.calledWith('friend')
})
})
备注
记录所有事件
Cypress使用debug
节点模块用于后端服务器进程和浏览器中运行的所有内容(称为驱动程序)。
如果你想查看Cypress发出的(大量)事件流,可以在开发者工具中打开控制台并写入这行代码。
localStorage.debug = 'cypress:*'
重新加载浏览器并打开"详细"日志以在开发者工具控制台中查看调试消息。
