Skip to content

Commit 2a83f1d

Browse files
authored
feat: add playwright e2e testing framework (vbenjs#4468)
* feat: add playwright e2e testing framework
1 parent 4b3d2d2 commit 2a83f1d

File tree

22 files changed

+345
-300
lines changed

22 files changed

+345
-300
lines changed

.gitpod.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ ports:
33
onOpen: open-preview
44
tasks:
55
- init: corepack enable && pnpm install
6-
command: pnpm run dev
6+
command: pnpm run dev:play

README.ja-JP.md

+4
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ pnpm build
133133

134134
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
135135

136+
## スター歴史
137+
138+
[![Star History Chart](https://api.star-history.com/svg?repos=vbenjs/vue-vben-admin&type=Date)](https://star-history.com/#vbenjs/vue-vben-admin&Date)
139+
136140
## 貢献者
137141

138142
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ If you think this project is helpful to you, you can help the author buy a cup o
132132

133133
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
134134

135+
## Star History
136+
137+
[![Star History Chart](https://api.star-history.com/svg?repos=vbenjs/vue-vben-admin&type=Date)](https://star-history.com/#vbenjs/vue-vben-admin&Date)
138+
135139
## Contributor
136140

137141
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">

README.zh-CN.md

+4
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ pnpm build
132132

133133
[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases)
134134

135+
## Star History
136+
137+
[![Star History Chart](https://api.star-history.com/svg?repos=vbenjs/vue-vben-admin&type=Date)](https://star-history.com/#vbenjs/vue-vben-admin&Date)
138+
135139
## Contributor
136140

137141
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">

docs/src/en/guide/essentials/development.md

+2-4
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ The execution command is: `pnpm run [script]` or `npm run [script]`.
4646
```json
4747
{
4848
"scripts": {
49-
// Install dependencies
50-
"bootstrap": "pnpm install",
5149
// Build the project
5250
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
5351
// Build the project with analysis
@@ -107,9 +105,9 @@ The execution command is: `pnpm run [script]` or `npm run [script]`.
107105
// Package specification check
108106
"publint": "vsh publint",
109107
// Delete all node_modules, yarn.lock, package.lock.json, and reinstall dependencies
110-
"reinstall": "pnpm clean --del-lock && pnpm bootstrap",
108+
"reinstall": "pnpm clean --del-lock && pnpm install",
111109
// Run vitest unit tests
112-
"test:unit": "vitest",
110+
"test:unit": "vitest run --dom",
113111
// Update project dependencies
114112
"update:deps": " pnpm update --latest --recursive",
115113
// Changeset generation and versioning

docs/src/guide/essentials/development.md

+2-4
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ npm 脚本是项目常见的配置,用于执行一些常见的任务,比如
4646
```json
4747
{
4848
"scripts": {
49-
// 安装依赖
50-
"bootstrap": "pnpm install",
5149
// 构建项目
5250
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
5351
// 构建项目并分析
@@ -107,9 +105,9 @@ npm 脚本是项目常见的配置,用于执行一些常见的任务,比如
107105
// 包规范检查
108106
"publint": "vsh publint",
109107
// 删除所有的node_modules、yarn.lock、package.lock.json,重新安装依赖
110-
"reinstall": "pnpm clean --del-lock && pnpm bootstrap",
108+
"reinstall": "pnpm clean --del-lock && pnpm install",
111109
// 运行 vitest 单元测试
112-
"test:unit": "vitest",
110+
"test:unit": "vitest run --dom",
113111
// 更新项目依赖
114112
"update:deps": " pnpm update --latest --recursive",
115113
// changeset生成提交集

internal/lint-configs/eslint-config/src/configs/node.ts

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export async function node(): Promise<Linter.Config[]> {
2424
'vite',
2525
'@vue/test-utils',
2626
'@vben/tailwind-config',
27+
'@playwright/test',
2728
],
2829
},
2930
],

internal/lint-configs/eslint-config/src/custom-config.ts

+8
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ const customConfig: Linter.Config[] = [
134134
'unicorn/prefer-module': 'off',
135135
},
136136
},
137+
{
138+
files: ['**/**/playwright.config.ts'],
139+
rules: {
140+
'n/prefer-global/buffer': 'off',
141+
'n/prefer-global/process': 'off',
142+
'no-console': 'off',
143+
},
144+
},
137145
{
138146
files: ['internal/**/**'],
139147
rules: {

package.json

+8-8
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,10 @@
2525
},
2626
"type": "module",
2727
"scripts": {
28-
"bootstrap": "pnpm install",
2928
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
3029
"build:analyze": "turbo build:analyze",
31-
"build:docker": "./build-local-docker-image.sh",
3230
"build:antd": "pnpm run build --filter=@vben/web-antd",
31+
"build:docker": "./build-local-docker-image.sh",
3332
"build:docs": "pnpm run build --filter=@vben/docs",
3433
"build:ele": "pnpm run build --filter=@vben/web-ele",
3534
"build:naive": "pnpm run build --filter=@vben/web-naive",
@@ -55,15 +54,16 @@
5554
"prepare": "is-ci || husky",
5655
"preview": "turbo-run preview",
5756
"publint": "vsh publint",
58-
"reinstall": "pnpm clean --del-lock && pnpm bootstrap",
59-
"test:unit": "vitest",
57+
"reinstall": "pnpm clean --del-lock && pnpm install",
58+
"test:unit": "vitest run --dom",
59+
"test:e2e": "turbo run test:e2e",
6060
"update:deps": "pnpm update --latest --recursive",
6161
"version": "pnpm exec changeset version && pnpm install --no-frozen-lockfile"
6262
},
6363
"devDependencies": {
6464
"@changesets/changelog-github": "catalog:",
6565
"@changesets/cli": "catalog:",
66-
"@types/jsdom": "catalog:",
66+
"@playwright/test": "catalog:",
6767
"@types/node": "catalog:",
6868
"@vben/commitlint-config": "workspace:*",
6969
"@vben/eslint-config": "workspace:*",
@@ -80,10 +80,11 @@
8080
"autoprefixer": "catalog:",
8181
"cross-env": "catalog:",
8282
"cspell": "catalog:",
83+
"happy-dom": "catalog:",
8384
"husky": "catalog:",
8485
"is-ci": "catalog:",
85-
"jsdom": "catalog:",
8686
"lint-staged": "catalog:",
87+
"playwright": "catalog:",
8788
"rimraf": "catalog:",
8889
"tailwindcss": "catalog:",
8990
"turbo": "catalog:",
@@ -113,8 +114,7 @@
113114
},
114115
"neverBuiltDependencies": [
115116
"canvas",
116-
"node-gyp",
117-
"playwright"
117+
"node-gyp"
118118
]
119119
}
120120
}

packages/@core/ui-kit/shadcn-ui/package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
{
22
"name": "@vben-core/shadcn-ui",
33
"version": "5.3.0",
4+
"#main": "./dist/index.mjs",
5+
"#module": "./dist/index.mjs",
46
"homepage": "https://github.com/vbenjs/vue-vben-admin",
57
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
68
"repository": {
@@ -20,16 +22,14 @@
2022
"sideEffects": [
2123
"**/*.css"
2224
],
23-
"#main": "./dist/index.mjs",
2425
"main": "./src/index.ts",
25-
"#module": "./dist/index.mjs",
2626
"module": "./src/index.ts",
2727
"exports": {
2828
".": {
2929
"types": "./src/index.ts",
3030
"development": "./src/index.ts",
31-
"//default": "./dist/index.mjs",
32-
"default": "./src/index.ts"
31+
"default": "./src/index.ts",
32+
"//default": "./dist/index.mjs"
3333
}
3434
},
3535
"publishConfig": {

packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-action.vue

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ defineExpose({
5151
}"
5252
:style="style"
5353
class="bg-background dark:bg-accent absolute left-0 top-0 flex h-full cursor-move items-center justify-center px-3.5 shadow-md"
54+
name="captcha-action"
5455
>
5556
<Slot :is-passing="isPassing" class="text-foreground/60 size-4">
5657
<slot name="icon">

packages/effects/common-ui/src/ui/authentication/forget-password.vue

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ function goToLogin() {
9595
:class="{
9696
'cursor-wait': loading,
9797
}"
98+
aria-label="submit"
9899
class="mt-2 w-full"
99100
@click="handleSubmit"
100101
>

packages/effects/common-ui/src/ui/authentication/login.vue

+1
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ onMounted(() => {
129129
'cursor-wait': loading,
130130
}"
131131
:loading="loading"
132+
aria-label="login"
132133
class="w-full"
133134
@click="handleSubmit"
134135
>

packages/effects/common-ui/src/ui/authentication/register.vue

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ function goToLogin() {
9797
'cursor-wait': loading,
9898
}"
9999
:loading="loading"
100+
aria-label="register"
100101
class="mt-2 w-full"
101102
@click="handleSubmit"
102103
>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { expect, test } from '@playwright/test';
2+
3+
import { authLogin } from './common/auth';
4+
5+
test.beforeEach(async ({ page }) => {
6+
await page.goto('/');
7+
});
8+
9+
test.describe('Auth Login Page Tests', () => {
10+
test('check title and page elements', async ({ page }) => {
11+
// 获取页面标题并断言标题包含 'Vben Admin'
12+
const title = await page.title();
13+
expect(title).toContain('Vben Admin');
14+
});
15+
16+
// 测试用例: 成功登录
17+
test('should successfully login with valid credentials', async ({ page }) => {
18+
await authLogin(page);
19+
});
20+
});
+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type { Page } from '@playwright/test';
2+
3+
import { expect } from '@playwright/test';
4+
5+
export async function authLogin(page: Page) {
6+
// 确保登录表单正常
7+
const usernameInput = await page.locator(`input[name='username']`);
8+
await expect(usernameInput).toBeVisible();
9+
10+
const passwordInput = await page.locator(`input[name='password']`);
11+
await expect(passwordInput).toBeVisible();
12+
13+
const sliderCaptcha = await page.locator(`div[name='captcha']`);
14+
const sliderCaptchaAction = await page.locator(`div[name='captcha-action']`);
15+
await expect(sliderCaptcha).toBeVisible();
16+
await expect(sliderCaptchaAction).toBeVisible();
17+
18+
// 拖动验证码滑块
19+
// 获取拖动按钮的位置
20+
const sliderCaptchaBox = await sliderCaptcha.boundingBox();
21+
if (!sliderCaptchaBox) throw new Error('滑块未找到');
22+
23+
const actionBoundingBox = await sliderCaptchaAction.boundingBox();
24+
if (!actionBoundingBox) throw new Error('要拖动的按钮未找到');
25+
26+
// 计算起始位置和目标位置
27+
const startX = actionBoundingBox.x + actionBoundingBox.width / 2; // div 中心的 x 坐标
28+
const startY = actionBoundingBox.y + actionBoundingBox.height / 2; // div 中心的 y 坐标
29+
30+
const targetX = startX + sliderCaptchaBox.width + actionBoundingBox.width; // 向右拖动容器的宽度
31+
const targetY = startY; // y 坐标保持不变
32+
33+
// 模拟鼠标拖动
34+
await page.mouse.move(startX, startY); // 移动到 action 的中心
35+
await page.mouse.down(); // 按下鼠标
36+
await page.mouse.move(targetX, targetY, { steps: 20 }); // 拖动到目标位置
37+
await page.mouse.up(); // 松开鼠标
38+
39+
// 在拖动后进行断言,检查action是否在预期位置,
40+
const newActionBoundingBox = await sliderCaptchaAction.boundingBox();
41+
expect(newActionBoundingBox?.x).toBeGreaterThan(actionBoundingBox.x);
42+
43+
// 到这里已经校验成功,点击进行登录
44+
await page.waitForTimeout(300);
45+
await page.getByRole('button', { name: 'login' }).click();
46+
}

playground/package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
"build:analyze": "pnpm vite build --mode analyze",
2121
"dev": "pnpm vite --mode development",
2222
"preview": "vite preview",
23-
"typecheck": "vue-tsc --noEmit --skipLibCheck"
23+
"typecheck": "vue-tsc --noEmit --skipLibCheck",
24+
"test:e2e": "playwright test",
25+
"test:e2e-ui": "playwright test --ui",
26+
"test:e2e-codegen": "playwright codegen"
2427
},
2528
"imports": {
2629
"#/*": "./src/*"

0 commit comments

Comments
 (0)