Skip to main content

事件目录

Cypress在浏览器中运行时会产生一系列事件。

这些事件不仅对控制应用程序行为有用,也对调试目的非常实用。

以下是你可以利用这些事件实现的一些示例:

  • 监听uncaught exceptions并阻止Cypress使测试失败
  • 监听alertconfirm调用并改变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)
描述:在测试及所有beforebeforeEach钩子运行前触发。
事件详情
名称:test:after:run
返回:测试属性 (Object), 可运行实例 (Object)
描述:在测试及所有afterEachafter钩子运行后触发。

其他事件

Cypress还触发大量其他事件以与Node服务器进程、自动化服务器、mocha、应用和报告器通信。这些事件严格内部于Cypress工作方式,对用户无用。

绑定事件

全局Cypresscy对象都是标准的Node事件发射器。这意味着你可以使用以下方法绑定和解绑事件。

理解为何要绑定到Cypresscy很重要。

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:*'

重新加载浏览器并打开"详细"日志以在开发者工具控制台中查看调试消息。

console log events for debugging