Cucumber + Playwright framework based on javascript
Step 1: Setup new project and playwright+cucumber
Git init and Nodejs init for a new project
cd playwright-cucumber-demo
git init
npm init -y
Install playwright
npm init playwright@latest
# Do you want to use TypeScript or JavaScript? JavaScript (或 TypeScript,本项目以 JS 为例)
# Where to put your end-to-end tests? tests (这个目录之后会被 Cucumber 使用,但先保留)
# Add a GitHub Actions workflow? No (我们将自己创建更定制的流程)
# Install Playwright browsers? Yes
Install cucumber-js
npm install @cucumber/cucumber @cucumber/pretty-formatter
# 如果使用 TypeScript,还需要安装 typescript 和 ts-node
Step 2: Create file structure
playwright-cucumber-demo/
├── features/
│ ├── example.feature # 用 Gherkin 语言编写的功能文件
│ └── step_definitions/
│ └── example.steps.js # 步骤定义文件
├── support/
│ ├── world.js # 自定义 World 对象,用于共享状态
│ └── hooks.js # 钩子函数 (如 before, after)
├── playwright.config.js # Playwright 配置文件
├── package.json
└── .github/
└── workflows/
└── ci.yml # GitHub Actions 工作流文件 (稍后创建)
Step 3: Cucumber configurations
// cucumber.js
/* Refer to
https://github.com/cucumber/cucumber-js/blob/main/docs/configuration.md
for more details
*/
module.exports = {
default: {
// 显式指定 feature 文件路径
paths: ['features/**/*.feature'],
require: [
'support/world.js',
'support/hooks.js',
'support/register-allure.js', //预留文件,可对allure扩展
'features/**/*_steps.js'
],
format: [
// 仅保留 json 和 allure reporter
'json:cucumber-report.json',
'allure-cucumberjs/reporter'
],
parallel: 2 // 并行运行的进程数,可根据 CI 环境调整
}
}
`npx cucumber-js` execution would trigger below flows:
1.Load the require files in sequence
2.Call setWorldConstructor(CustomWorld) once while it loads world.js
3.Create CustomWorld instance before each scenario execution, so constructor method would be triggered
4.Then Before hook would be triggered
Step 4:Write example feature and step_definition files
# features/example.feature
Feature: 访问 Playwright 官网并搜索
Scenario: 在 Playwright 官网搜索
Given 我导航到 Playwright 官网
When 我在搜索框中输入 "CSS"
Then 搜索结果页面应该显示与 "CSS" 相关的内容
// features/step_definitions/example.steps.js
const { Given, When, Then } = require('@cucumber/cucumber');
const { expect } = require('@playwright/test');
Given('我导航到 Playwright 官网', async function () {
// this.page 来自 World 对象,在 hooks.js 中初始化
await this.page.goto('https://playwright.dev');
});
When('我在搜索框中输入 {string}', async function (searchTerm) {
await this.page.click('button.DocSearch-Button');
await this.page.fill('input.DocSearch-Input', searchTerm);
});
Then('搜索结果页面应该显示与 {string} 相关的内容', async function (searchTerm) {
// 等待搜索结果出现
const firstResult = await this.page.waitForSelector('.DocSearch-Hit a');
const text = await firstResult.textContent();
expect(text).toContain(searchTerm);
});
Step 5: Create support files to integrate Allure
Package Dependency
npm install --save-dev allure-cucumberjs allure-commandline
World Extension
Cucumber framework allows a custom World to manage per-scenario state and integrate Allure, so we should define a CustomWorld class to extend World.
Why it's needed: Only when using Cucumber, you need a central place to manage shared state and lifecycles at the scene level (e.g., browser/page and allure operations). Without it, you have to manually create it at each step or manage it through global variables, which is difficult to maintain.
// support/world.js
/* Refer to
https://github.com/cucumber/cucumber-js/blob/db0956e0725e2ef00854c0d52c4392db00bdf307/docs/support_files/world.md
for more details
*/
const { setWorldConstructor, World } = require('@cucumber/cucumber');
const { chromium } = require('playwright');
// 根据 allure-cucumberjs legacy.ts 的弃用说明,不再继承 AllureCucumberWorld,直接使用 allure-js-commons API
const {
attachment, // 添加附件
label, // 添加自定义或标准标签
link, // 添加链接 (issue / tms)
severity, // 严重级别
step // 自定义步骤包装
} = require('allure-js-commons');
class CustomWorld extends World {
constructor(options) {
super(options);
// 可在此处初始化测试上下文数据
}
async launchBrowser() {
this.browser = await chromium.launch({ headless: true });
this.context = await this.browser.newContext();
this.page = await this.context.newPage();
}
async closeBrowser() {
if (this.browser) await this.browser.close();
}
// ===== Allure 辅助方法(采用新 API,避免使用已弃用的 AllureCucumberWorld 实例方法) =====
async attachScreenshot(name = 'screenshot') {
if (!this.page) return;
const buf = await this.page.screenshot();
attachment(name, buf, { contentType: 'image/png' });
}
addSeverity(level = 'normal') {
severity(level); // e.g. blocker, critical, normal, minor, trivial
}
addLabel(name, value) {
label(name, value);
}
addIssue(name, url) {
link(url, name, 'issue');
}
addTms(name, url) {
link(url, name, 'tms');
}
async runStep(title, bodyFn) {
return step(title, bodyFn); // 在报告中嵌套展示
}
}
setWorldConstructor(CustomWorld);
Hook function
// support/hooks.js
/* Refer to
https://github.com/cucumber/cucumber-js/blob/main/docs/support_files/hooks.md
for more details
*/
const { Before, After } = require('@cucumber/cucumber');
Before(async function () {
await this.launchBrowser();
});
After(async function () {
await this.closeBrowser();
});
Step 6: Add package.json script
"scripts": {
"clean:allure": "node -e \"const fs=require('fs');const d='allure-results';if(fs.existsSync(d)) fs.rmSync(d,{recursive:true,force:true});fs.mkdirSync(d);console.log('Allure results directory reset');\"",
"test": "cucumber-js",
"test:headed": "cucumber-js --world-parameters={\"headed\": true}",
"test:report": "cucumber-js && open cucumber-report.html",
"allure:report": "allure generate allure-results -o allure-report --clean",
"allure:open": "allure open allure-report",
"tests": "npm run clean:allure && npm run test && npm run allure:report && npm run allure:open"
}
更多推荐


所有评论(0)