ci: 完善项目持续集成配置并添加 Playwright 测试
This commit is contained in:
23
.github/workflows/playwright.yaml
vendored
Normal file
23
.github/workflows/playwright.yaml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
env:
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
playwright:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: mcr.microsoft.com/playwright:v1.55.0-noble
|
||||||
|
steps:
|
||||||
|
- name: ⚙️ 设置 Node 环境
|
||||||
|
uses: yanhao98/composite-actions/setup-node-environment@25eb4dc0c134cc9df2b7c569aa54140a366b45a8
|
||||||
|
# - name: 📥 安装 Playwright 浏览器
|
||||||
|
# run: pnpm exec playwright install --with-deps
|
||||||
|
- name: ▶️ 运行 Playwright 测试
|
||||||
|
run: |
|
||||||
|
npx playwright test
|
20
.husky/README.md
Normal file
20
.husky/README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
### Husky 遇到 command not found: husky
|
||||||
|
|
||||||
|
- https://typicode.github.io/husky/zh/troubleshoot.html#找不到命令-command-not-found
|
||||||
|
- https://typicode.github.io/husky/zh/how-to.html#node-版本管理器和-gui
|
||||||
|
|
||||||
|
```shell
|
||||||
|
ln -s $(which pnpm) $HOME/.local/bin/pnpm
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
# if command -v pnpm >/dev/null 2>&1; then
|
||||||
|
# # 如果 pnpm 可用,直接使用它
|
||||||
|
# pnpm exec lint-staged
|
||||||
|
# else
|
||||||
|
# # 如果 pnpm 不可用,使用 $HOME/.local/bin/pnpm
|
||||||
|
# # ln -s $(which pnpm) $HOME/.local/bin/pnpm
|
||||||
|
# echo "找不到 pnpm,使用 $HOME/.local/bin/pnpm"
|
||||||
|
# "$HOME"/.local/bin/pnpm exec lint-staged
|
||||||
|
# fi
|
||||||
|
```
|
5
.husky/commit-msg
Normal file
5
.husky/commit-msg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# 此钩子在 pre-commit 钩子成功完成后,用于检查提交消息。
|
||||||
|
echo "📝 [Commit-msg] 正在运行 commit-msg 钩子..."
|
||||||
|
echo "检查提交消息:$1"
|
||||||
|
pnpm exec commitlint --edit $1
|
||||||
|
echo "✅ [Commit-msg] commit-msg 钩子完成!"
|
4
.husky/post-merge
Normal file
4
.husky/post-merge
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# 此钩子在 git merge 或 git pull 成功完成后运行。
|
||||||
|
echo "🔗 [Post-merge] 正在安装依赖..."
|
||||||
|
pnpm install
|
||||||
|
echo "✅ [Post-merge] 依赖安装完成!"
|
4
.husky/pre-commit
Normal file
4
.husky/pre-commit
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# 此钩子在执行 git commit 命令时,在创建提交之前运行。
|
||||||
|
echo "🧹 [Pre-commit] 正在运行 lint-staged..."
|
||||||
|
pnpm exec lint-staged
|
||||||
|
echo "✅ [Pre-commit] lint-staged 完成!"
|
9
commitlint.config.ts
Normal file
9
commitlint.config.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import type { UserConfig } from '@commitlint/types';
|
||||||
|
|
||||||
|
|
||||||
|
const Configuration: UserConfig = {
|
||||||
|
extends: ['@commitlint/config-conventional'],
|
||||||
|
formatter: '@commitlint/format',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Configuration;
|
53
e2e/playwright/vue.spec.ts
Normal file
53
e2e/playwright/vue.spec.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
|
test.describe('Vue App', () => {
|
||||||
|
test('visits the app root url', async ({ page }) => {
|
||||||
|
await page.goto('/')
|
||||||
|
await expect(page.locator('h1')).toHaveText('You did it!')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('displays Vue documentation link', async ({ page }) => {
|
||||||
|
await page.goto('/')
|
||||||
|
const link = page.locator('a[href="https://vuejs.org/"]')
|
||||||
|
await expect(link).toBeVisible()
|
||||||
|
await expect(link).toHaveText('vuejs.org')
|
||||||
|
await expect(link).toHaveAttribute('target', '_blank')
|
||||||
|
await expect(link).toHaveAttribute('rel', 'noopener')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('displays button with initial name state', async ({ page }) => {
|
||||||
|
await page.goto('/')
|
||||||
|
const button = page.locator('button[aria-label="get name"]')
|
||||||
|
await expect(button).toBeVisible()
|
||||||
|
await expect(button).toHaveText('Name from API is: Unknown')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('button click triggers API call', async ({ page }) => {
|
||||||
|
await page.goto('/')
|
||||||
|
|
||||||
|
await page.route('/api/', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({ name: 'Test User' }),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const button = page.locator('button[aria-label="get name"]')
|
||||||
|
await button.click()
|
||||||
|
|
||||||
|
await expect(button).toHaveText('Name from API is: Test User')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('handles API error gracefully', async ({ page }) => {
|
||||||
|
await page.goto('/')
|
||||||
|
|
||||||
|
await page.route('/api/', async (route) => {
|
||||||
|
await route.abort('failed')
|
||||||
|
})
|
||||||
|
|
||||||
|
const button = page.locator('button[aria-label="get name"]')
|
||||||
|
await button.click()
|
||||||
|
|
||||||
|
await expect(button).toHaveText('Name from API is: Unknown')
|
||||||
|
})
|
||||||
|
})
|
@@ -1,8 +0,0 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
|
||||||
|
|
||||||
// See here how to get started:
|
|
||||||
// https://playwright.dev/docs/intro
|
|
||||||
test('visits the app root url', async ({ page }) => {
|
|
||||||
await page.goto('/')
|
|
||||||
await expect(page.locator('h1')).toHaveText('You did it!')
|
|
||||||
})
|
|
72
package.json
72
package.json
@@ -8,16 +8,22 @@
|
|||||||
"node": "^20.19.0 || >=22.12.0"
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"_all": "run-p build-only format type-check lint",
|
||||||
"dev": "vite --port 4730 --host --strictPort",
|
"dev": "vite --port 4730 --host --strictPort",
|
||||||
"build": "run-p type-check \"build-only {@}\" --",
|
"build": "run-p type-check \"build-only {@}\" --",
|
||||||
"preview:vite": "vite preview",
|
|
||||||
"preview": "pnpm run build && wrangler dev",
|
|
||||||
"test:unit": "vitest",
|
|
||||||
"test:e2e": "playwright test",
|
|
||||||
"build-only": "vite build",
|
"build-only": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"preview:wrangler": "pnpm run build && wrangler dev",
|
||||||
|
"test:unit": "vitest",
|
||||||
|
"test:playwright": "playwright test",
|
||||||
|
"test:playwright:headless": "HEADLESS=true playwright test",
|
||||||
|
"test:playwright:ui": "playwright test --ui",
|
||||||
|
"test:playwright:chromium": "playwright test --project=chromium",
|
||||||
"type-check": "vue-tsc --build",
|
"type-check": "vue-tsc --build",
|
||||||
"lint:oxlint": "oxlint . --fix -D correctness --ignore-path .gitignore",
|
"lint:oxlint": "oxlint . --fix -D correctness --ignore-path .gitignore",
|
||||||
"lint:eslint": "eslint . --fix",
|
"lint:eslint": "eslint . --fix",
|
||||||
|
"_oxlint_cfg": "oxlint . --fix --ignore-path=.gitignore --print-config",
|
||||||
|
"__oxlint_-D": "oxlint . --fix --deny=correctness",
|
||||||
"lint": "run-s lint:*",
|
"lint": "run-s lint:*",
|
||||||
"format": "prettier --write src/",
|
"format": "prettier --write src/",
|
||||||
"-wrangler:pages:deploy:preview": "wrangler pages deploy dist --project-name=vue-ts-example-2025 --branch=preview",
|
"-wrangler:pages:deploy:preview": "wrangler pages deploy dist --project-name=vue-ts-example-2025 --branch=preview",
|
||||||
@@ -26,41 +32,65 @@
|
|||||||
"-deploy:prod": "run-s build-only wrangler:pages:deploy:prod",
|
"-deploy:prod": "run-s build-only wrangler:pages:deploy:prod",
|
||||||
"wrangler:deploy": "pnpm run build && wrangler deploy",
|
"wrangler:deploy": "pnpm run build && wrangler deploy",
|
||||||
"wrangler:versions:upload": "pnpm run build && wrangler versions upload",
|
"wrangler:versions:upload": "pnpm run build && wrangler versions upload",
|
||||||
"cf-typegen": "wrangler types"
|
"cf-typegen": "wrangler types",
|
||||||
|
"_dep:dedupe": "pnpm dedupe",
|
||||||
|
"_dep:update": "pnpm dlx taze major --interactive",
|
||||||
|
"_sizecheck:Treemap": "pnpm dlx vite-bundle-visualizer -t treemap",
|
||||||
|
"_sizecheck:Sunburst": "pnpm dlx vite-bundle-visualizer -t sunburst",
|
||||||
|
"_sizecheck:Network": "pnpm dlx vite-bundle-visualizer -t network",
|
||||||
|
"_knip": "pnpm dlx knip",
|
||||||
|
"prepare": "husky"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"{src,e2e}/**/*.{js,jsx,ts,tsx,vue}": [
|
||||||
|
"prettier --write",
|
||||||
|
"eslint --fix",
|
||||||
|
"oxlint --fix"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"overrides": {
|
||||||
|
"vite": "$vite"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@commitlint/cli": "^19.8.1",
|
||||||
|
"@commitlint/config-conventional": "^19.8.1",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"vue": "^3.5.18",
|
"vue": "^3.5.21",
|
||||||
"vue-router": "^4.5.1"
|
"vue-router": "^4.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/vite-plugin": "^1.12.3",
|
"@cloudflare/vite-plugin": "^1.12.3",
|
||||||
"@playwright/test": "^1.54.1",
|
"@commitlint/types": "^19.8.1",
|
||||||
|
"@playwright/test": "^1.55.0",
|
||||||
"@prettier/plugin-oxc": "^0.0.4",
|
"@prettier/plugin-oxc": "^0.0.4",
|
||||||
"@tsconfig/node22": "^22.0.2",
|
"@tsconfig/node22": "^22.0.2",
|
||||||
"@types/jsdom": "^21.1.7",
|
"@types/jsdom": "^21.1.7",
|
||||||
"@types/node": "^22.16.5",
|
"@types/node": "^22.18.1",
|
||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
"@vitejs/plugin-vue-jsx": "^5.0.1",
|
"@vitejs/plugin-vue-jsx": "^5.1.1",
|
||||||
"@vitest/eslint-plugin": "^1.3.4",
|
"@vitest/eslint-plugin": "^1.3.9",
|
||||||
"@vue/eslint-config-prettier": "^10.2.0",
|
"@vue/eslint-config-prettier": "^10.2.0",
|
||||||
"@vue/eslint-config-typescript": "^14.6.0",
|
"@vue/eslint-config-typescript": "^14.6.0",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"@vue/tsconfig": "^0.7.0",
|
"@vue/tsconfig": "^0.8.1",
|
||||||
"eslint": "^9.31.0",
|
"eslint": "^9.35.0",
|
||||||
"eslint-plugin-oxlint": "~1.8.0",
|
"eslint-plugin-oxlint": "~1.14.0",
|
||||||
"eslint-plugin-playwright": "^2.2.0",
|
"eslint-plugin-playwright": "^2.2.2",
|
||||||
"eslint-plugin-vue": "~10.3.0",
|
"eslint-plugin-vue": "~10.4.0",
|
||||||
"jiti": "^2.4.2",
|
"husky": "^9.1.7",
|
||||||
|
"jiti": "^2.5.1",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
|
"lint-staged": "^16.1.6",
|
||||||
"npm-run-all2": "^8.0.4",
|
"npm-run-all2": "^8.0.4",
|
||||||
"oxlint": "~1.8.0",
|
"oxlint": "~1.14.0",
|
||||||
"prettier": "3.6.2",
|
"prettier": "3.6.2",
|
||||||
"typescript": "~5.8.0",
|
"typescript": "~5.9.2",
|
||||||
"vite": "npm:rolldown-vite@latest",
|
"vite": "npm:rolldown-vite@^7.1.8",
|
||||||
"vite-plugin-vue-devtools": "^8.0.0",
|
"vite-plugin-vue-devtools": "^8.0.1",
|
||||||
"vitest": "^3.2.4",
|
"vitest": "^3.2.4",
|
||||||
"vue-tsc": "^3.0.4",
|
"vue-tsc": "^3.0.6",
|
||||||
"wrangler": "^4.34.0"
|
"wrangler": "^4.34.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
import process from 'node:process'
|
|
||||||
import { defineConfig, devices } from '@playwright/test'
|
import { defineConfig, devices } from '@playwright/test'
|
||||||
|
import process from 'node:process'
|
||||||
|
|
||||||
|
// const runningInVSCode = process.env.TERM_PROGRAM === 'vscode'
|
||||||
|
const baseURL = process.env.CI ? 'http://localhost:4173' : 'http://localhost:4730'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read environment variables from file.
|
* Read environment variables from file.
|
||||||
@@ -11,7 +14,7 @@ import { defineConfig, devices } from '@playwright/test'
|
|||||||
* See https://playwright.dev/docs/test-configuration.
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
*/
|
*/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
testDir: './e2e',
|
testDir: './e2e/playwright',
|
||||||
/* Maximum time one test can run for. */
|
/* Maximum time one test can run for. */
|
||||||
timeout: 30 * 1000,
|
timeout: 30 * 1000,
|
||||||
expect: {
|
expect: {
|
||||||
@@ -34,13 +37,13 @@ export default defineConfig({
|
|||||||
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||||
actionTimeout: 0,
|
actionTimeout: 0,
|
||||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||||
baseURL: process.env.CI ? 'http://localhost:4173' : 'http://localhost:5173',
|
baseURL,
|
||||||
|
|
||||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
trace: 'on-first-retry',
|
trace: 'on-first-retry',
|
||||||
|
|
||||||
/* Only on CI systems run the tests headless */
|
/* Only on CI systems run the tests headless */
|
||||||
headless: !!process.env.CI,
|
headless: !!process.env.CI || process.env.HEADLESS === 'true',
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Configure projects for major browsers */
|
/* Configure projects for major browsers */
|
||||||
@@ -104,7 +107,7 @@ export default defineConfig({
|
|||||||
* Playwright will re-use the local server if there is already a dev-server running.
|
* Playwright will re-use the local server if there is already a dev-server running.
|
||||||
*/
|
*/
|
||||||
command: process.env.CI ? 'npm run preview' : 'npm run dev',
|
command: process.env.CI ? 'npm run preview' : 'npm run dev',
|
||||||
port: process.env.CI ? 4173 : 5173,
|
port: Number(new URL(baseURL).port),
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: !process.env.CI,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
1174
pnpm-lock.yaml
generated
1174
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user