Node事件概述
Node事件钩子使您能够接入、修改或扩展Cypress的内部行为。
通常情况下,作为用户,您的所有测试代码、应用程序和Cypress命令都在浏览器中执行。但Cypress也是一个Node进程,可用于改变Cypress的行为或暴露插件供他人使用。
setupNodeEvents
使您能够接入运行在浏览器外部的Node进程。
Node事件钩子是您编写自定义代码的"接缝",这些代码在Cypress生命周期的特定阶段执行。
setupNodeEvents
setupNodeEvents
函数接收2个参数:on
和config
。它可以返回一个同步值,也可以返回一个Promise,该Promise将被等待直到解析完成。这使您能够执行异步操作,例如从文件系统读取文件。
如果您返回或解析一个对象,Cypress会将该对象合并到config
中,从而使您能够覆盖配置或环境变量。
- cypress.config.js 文件
- cypress.config.ts 文件
const { defineConfig } = require('cypress')
module.exports = defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
// 在此配置插件
},
},
})
import { defineConfig } from 'cypress'
export default defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
// 在此配置插件
},
},
})
on
on
是一个函数,用于注册监听Cypress暴露的各种事件。
注册监听事件如下所示:
- cypress.config.js 文件
- cypress.config.ts 文件
const { defineConfig } = require('cypress')
module.exports = defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
on('<event>', (arg1, arg2) => {
// 插件代码在此
})
},
},
})
import { defineConfig } from 'cypress'
export default defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
on('<event>', (arg1, arg2) => {
// 插件代码在此
})
},
},
})
事件列表
每个事件都记录了自己的参数签名。
事件 | 描述 |
---|---|
after:run | 运行结束后触发。 |
after:screenshot | 截图完成后触发。 |
after:spec | 单个spec运行完成后触发。 |
before:browser:launch | 浏览器启动前立即触发。 |
before:run | 运行开始前触发。 |
before:spec | 当spec即将运行时触发。 |
file:preprocessor | 当spec或相关文件需要为浏览器转译时触发。 |
task | 与cy.task 命令结合使用。 |
config
config
是已解析的Cypress配置对象,用于打开的项目。
此配置包含所有传递给浏览器的项目值。
一些插件可能会利用或需要这些值,以便根据配置采取特定操作。如果这些值被编程修改,Cypress将使用新值。
config
对象还包括以下不属于标准配置的额外值。这些值是只读的,无法从插件文件中修改。
configFile
: Cypress配置文件的绝对路径。有关此值的更多信息,请参阅--config-file和configFile文档。projectRoot
: 项目根目录的绝对路径(例如/Users/me/dev/my-project
)version
: Cypress的版本号。可用 于处理重大变更。
使用场景
配置
接入node事件允许您以编程方式更改来自Cypress配置文件、cypress.env.json
、命令行或系统环境变量的已解析配置和环境变量。
这使您能够:
- 使用具有各自配置的多个环境
- 根据环境交换环境变量
- 使用内置的
fs
库读取配置文件 - 更改用于测试的浏览器列表
- 用
yml
编写配置
查看我们的配置API文档,了解如何使用此事件。
预处理器
file:preprocessor
事件用于自定义测试代码如何被转译并发送到浏览器。默认情况下,Cypress处理ES2015+、TypeScript和CoffeeScript,使用webpack打包到浏览器。
您可以使用file:preprocessor
事件来:
- 添加最新的ES*支持
- 用ClojureScript编写测试代码
- 自定义Babel设置以添加自己的插件
- 自定义TypeScript编译选项
- 用Vite或其他工具替换Webpack
查看我们的文件预处理器API文档,了解如何使用此事件。
运行生命周期
before:run
和after:run
事件分别在运行前后触发。
您可以使用before:run
来:
- 设置运行报告
- 启动计时器以计算运行时间
您可以使用after:run
来:
- 完成在
before:run
中设置的运行报告 - 停止在
before:run
中设置的计时器
Spec生命周期
before:spec
和after:spec
事件分别在单个spec运行前后触发。
您可以使用before:spec
来:
- 设置spec运行报告
- 启动计时器以计算spec运行时间
您可以使用after:spec
来:
- 完成在
before:spec
中设置的报告 - 停止在
before:spec
中设置的计时器 - 删除为spec录制的视频。这可以避免花费时间和计算资源压缩和上传视频。您可以根据spec的结果有条件地执行此操作,例如如果它通过(因此保留失败测试的视频用于调试目的)。
查看Before Spec API文档和After Spec API文档,了解如何使用这些事件。
浏览器启动
before:browser:launch
事件可用于修改每个特定浏览器的启动参数。
您可以使用before:browser:launch
事件来:
- 加载Chrome扩展
- 启用或禁用实验性Chrome功能
- 控制加载哪些Chrome组件
查看我们的浏览器启动API文档,了解如何使用此事件。
截图处理
after:screenshot
事件在截图保存到磁盘后调用。
您可以使用after:screenshot
事件来:
- 保存有关截图的详细信息
- 重命名截图
- 通过调整大小或裁剪来操作截图图像
查看我们的After Screenshot API文档,了解如何使用此事件。
cy.task
task
事件与cy.task()
命令结合使用。它允许您在Node中编写任意代码,以完成浏览器中无法完成的任务。
您可以使用task
事件来:
- 操作数据库(种子、读取、写入等)
- 在Node中存储您希望持久化的状态(因为驱动程序在访问时会完全刷新)
- 执行并行任务(如在Cypress之外发出多个http请求)
- 运行外部进程(如启动另一个浏览器如Safari或puppeteer的Webdriver实例)
执行上下文
setupNodeEvents
函数在Cypress打开项目时调用。
Cypress通过生成一个独立的child_process
来实现这一点,该进程然后require
Cypress配置文件。这与Visual Studio Code或Atom的工作方式类似。
此代码将使用启动Cypress的Node版本执行。
npm模块
当Cypress执行setupNodeEvents
函数时,process.cwd()
将设置为您的项目路径。此外,您将能够require
任何已安装的node模块,包括项目中的本地文件。
例如,如果您的package.json
如下所示:
{
"name": "My Project",
"dependencies": {
"debug": "x.x.x"
},
"devDependencies": {
"lodash": "x.x.x"
}
}
那么您可以在setupNodeEvents
函数中执行以下任何操作:
- cypress.config.js 文件
- cypress.config.ts 文件
const { defineConfig } = require('cypress')
module.exports = defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
const _ = require('lodash') // 是的,dev依赖
const path = require('path') // 是的,核心node库
const debug = require('debug') // 是的,依赖
const User = require('./lib/models/user') // 是的,相对本地模块
console.log(__dirname) // /Users/janelane/Dev/my-project
console.log(process.cwd()) // /Users/janelane/Dev/my-project
},
},
})
import { defineConfig } from 'cypress'
export default defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
const _ = require('lodash') // 是的,dev依赖
const path = require('path') // 是的,核心node库
const debug = require('debug') // 是的,依赖
const User = require('./lib/models/user') // 是的,相对本地 模块
console.log(__dirname) // /Users/janelane/Dev/my-project
console.log(process.cwd()) // /Users/janelane/Dev/my-project
},
},
})
错误处理
Cypress配置文件在自己的子进程中加载,因此与Cypress本身运行的上下文隔离。这意味着您无法意外修改或更改Cypress自身的执行方式。
如果您的setupNodeEvents
函数有未捕获的异常、Promise的未处理拒绝或语法错误,Cypress会自动捕获这些错误并在控制台甚至Cypress本身中显示给您。
setupNodeEvents
函数中的错误_不会崩溃_Cypress。
文件更改
通常在Node中编写代码时,更改任何文件后通常需要重新启动进程。
Cypress自动监视您的Cypress配置文件,任何更改都会立即生效。我们将读取文件并再次执行导出的函数。
这使您即使在Cypress已经运行时也能迭代插件代码。
真实世界示例
真实世界应用(RWA) 使用任务重新种子数据库,并为各种测试场景过滤/查找测试数据。
⚠️ 这段代码属于
setupNodeEvents 函数的一部分,
因此会在 Node 环境中执行。你不能在此函数中调用 Cypress
或 cy
命令,
但可以直接访问文件系统和操作系统的其他部分。
- cypress.config.js 文件
- cypress.config.ts 文件
const { defineConfig } = require('cypress')
module.exports = defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
on('task', {
async 'db:seed'() {
// 用测试数据种子数据库
const { data } = await axios.post(`${testDataApiEndpoint}/seed`)
return data
},
// 从数据库(MySQL、PostgreSQL等)获取测试数据
'filter:database'(queryPayload) {
return queryDatabase(queryPayload, (data, attrs) =>
_.filter(data.results, attrs)
)
},
'find:database'(queryPayload) {
return queryDatabase(queryPayload, (data, attrs) =>
_.find(data.results, attrs)
)
},
})
},
},
})
import { defineConfig } from 'cypress'
export default defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
on('task', {
async 'db:seed'() {
// 用测试数据种子数据库
const { data } = await axios.post(`${testDataApiEndpoint}/seed`)
return data
},
// 从数据库(MySQL、PostgreSQL等)获取测试数据
'filter:database'(queryPayload) {
return queryDatabase(queryPayload, (data, attrs) =>
_.filter(data.results, attrs)
)
},
'find:database'(queryPayload) {
return queryDatabase(queryPayload, (data, attrs) =>
_.find(data.results, attrs)
)
},
})
},
},
})
查看Real World App测试套件,了解这些任务的实际应用。