This commit is contained in:
dap 2024-09-23 07:53:24 +08:00
commit 69657a2127
23 changed files with 386 additions and 251 deletions

View File

@ -3,4 +3,4 @@ ports:
onOpen: open-preview onOpen: open-preview
tasks: tasks:
- init: corepack enable && pnpm install - init: corepack enable && pnpm install
command: pnpm run dev command: pnpm run dev:play

View File

@ -133,6 +133,10 @@ pnpm build
<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> <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>
## スター歴史
[![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)
## 貢献者 ## 貢献者
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors"> <a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">

View File

@ -132,6 +132,10 @@ If you think this project is helpful to you, you can help the author buy a cup o
<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> <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>
## Star History
[![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)
## Contributor ## Contributor
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors"> <a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">

View File

@ -150,6 +150,10 @@ pnpm build
[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases) [CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases)
## Star History
[![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)
## Contributor ## Contributor
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors"> <a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">

View File

@ -46,8 +46,6 @@ The execution command is: `pnpm run [script]` or `npm run [script]`.
```json ```json
{ {
"scripts": { "scripts": {
// Install dependencies
"bootstrap": "pnpm install",
// Build the project // Build the project
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build", "build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
// Build the project with analysis // Build the project with analysis
@ -107,9 +105,9 @@ The execution command is: `pnpm run [script]` or `npm run [script]`.
// Package specification check // Package specification check
"publint": "vsh publint", "publint": "vsh publint",
// Delete all node_modules, yarn.lock, package.lock.json, and reinstall dependencies // Delete all node_modules, yarn.lock, package.lock.json, and reinstall dependencies
"reinstall": "pnpm clean --del-lock && pnpm bootstrap", "reinstall": "pnpm clean --del-lock && pnpm install",
// Run vitest unit tests // Run vitest unit tests
"test:unit": "vitest", "test:unit": "vitest run --dom",
// Update project dependencies // Update project dependencies
"update:deps": " pnpm update --latest --recursive", "update:deps": " pnpm update --latest --recursive",
// Changeset generation and versioning // Changeset generation and versioning

View File

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

View File

@ -24,6 +24,7 @@ export async function node(): Promise<Linter.Config[]> {
'vite', 'vite',
'@vue/test-utils', '@vue/test-utils',
'@vben/tailwind-config', '@vben/tailwind-config',
'@playwright/test',
], ],
}, },
], ],

View File

@ -138,6 +138,14 @@ const customConfig: Linter.Config[] = [
'unicorn/prefer-module': 'off', 'unicorn/prefer-module': 'off',
}, },
}, },
{
files: ['**/**/playwright.config.ts'],
rules: {
'n/prefer-global/buffer': 'off',
'n/prefer-global/process': 'off',
'no-console': 'off',
},
},
{ {
files: ['internal/**/**'], files: ['internal/**/**'],
rules: { rules: {

View File

@ -25,11 +25,10 @@
}, },
"type": "module", "type": "module",
"scripts": { "scripts": {
"bootstrap": "pnpm install",
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build", "build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
"build:analyze": "turbo build:analyze", "build:analyze": "turbo build:analyze",
"build:docker": "./build-local-docker-image.sh",
"build:antd": "pnpm run build --filter=@vben/web-antd", "build:antd": "pnpm run build --filter=@vben/web-antd",
"build:docker": "./build-local-docker-image.sh",
"build:docs": "pnpm run build --filter=@vben/docs", "build:docs": "pnpm run build --filter=@vben/docs",
"build:ele": "pnpm run build --filter=@vben/web-ele", "build:ele": "pnpm run build --filter=@vben/web-ele",
"build:naive": "pnpm run build --filter=@vben/web-naive", "build:naive": "pnpm run build --filter=@vben/web-naive",
@ -55,15 +54,16 @@
"prepare": "is-ci || husky", "prepare": "is-ci || husky",
"preview": "turbo-run preview", "preview": "turbo-run preview",
"publint": "vsh publint", "publint": "vsh publint",
"reinstall": "pnpm clean --del-lock && pnpm bootstrap", "reinstall": "pnpm clean --del-lock && pnpm install",
"test:unit": "vitest", "test:unit": "vitest run --dom",
"test:e2e": "turbo run test:e2e",
"update:deps": "pnpm update --latest --recursive", "update:deps": "pnpm update --latest --recursive",
"version": "pnpm exec changeset version && pnpm install --no-frozen-lockfile" "version": "pnpm exec changeset version && pnpm install --no-frozen-lockfile"
}, },
"devDependencies": { "devDependencies": {
"@changesets/changelog-github": "catalog:", "@changesets/changelog-github": "catalog:",
"@changesets/cli": "catalog:", "@changesets/cli": "catalog:",
"@types/jsdom": "catalog:", "@playwright/test": "catalog:",
"@types/node": "catalog:", "@types/node": "catalog:",
"@vben/commitlint-config": "workspace:*", "@vben/commitlint-config": "workspace:*",
"@vben/eslint-config": "workspace:*", "@vben/eslint-config": "workspace:*",
@ -80,10 +80,11 @@
"autoprefixer": "catalog:", "autoprefixer": "catalog:",
"cross-env": "catalog:", "cross-env": "catalog:",
"cspell": "catalog:", "cspell": "catalog:",
"happy-dom": "catalog:",
"husky": "catalog:", "husky": "catalog:",
"is-ci": "catalog:", "is-ci": "catalog:",
"jsdom": "catalog:",
"lint-staged": "catalog:", "lint-staged": "catalog:",
"playwright": "catalog:",
"rimraf": "catalog:", "rimraf": "catalog:",
"tailwindcss": "catalog:", "tailwindcss": "catalog:",
"turbo": "catalog:", "turbo": "catalog:",
@ -113,8 +114,7 @@
}, },
"neverBuiltDependencies": [ "neverBuiltDependencies": [
"canvas", "canvas",
"node-gyp", "node-gyp"
"playwright"
] ]
} }
} }

View File

@ -1,6 +1,8 @@
{ {
"name": "@vben-core/shadcn-ui", "name": "@vben-core/shadcn-ui",
"version": "5.3.0", "version": "5.3.0",
"#main": "./dist/index.mjs",
"#module": "./dist/index.mjs",
"homepage": "https://github.com/vbenjs/vue-vben-admin", "homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": { "repository": {
@ -20,16 +22,14 @@
"sideEffects": [ "sideEffects": [
"**/*.css" "**/*.css"
], ],
"#main": "./dist/index.mjs",
"main": "./src/index.ts", "main": "./src/index.ts",
"#module": "./dist/index.mjs",
"module": "./src/index.ts", "module": "./src/index.ts",
"exports": { "exports": {
".": { ".": {
"types": "./src/index.ts", "types": "./src/index.ts",
"development": "./src/index.ts", "development": "./src/index.ts",
"//default": "./dist/index.mjs", "default": "./src/index.ts",
"default": "./src/index.ts" "//default": "./dist/index.mjs"
} }
}, },
"publishConfig": { "publishConfig": {

View File

@ -51,6 +51,7 @@ defineExpose({
}" }"
:style="style" :style="style"
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" 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"
name="captcha-action"
> >
<Slot :is-passing="isPassing" class="text-foreground/60 size-4"> <Slot :is-passing="isPassing" class="text-foreground/60 size-4">
<slot name="icon"> <slot name="icon">

View File

@ -95,6 +95,7 @@ function goToLogin() {
:class="{ :class="{
'cursor-wait': loading, 'cursor-wait': loading,
}" }"
aria-label="submit"
class="mt-2 w-full" class="mt-2 w-full"
@click="handleSubmit" @click="handleSubmit"
> >

View File

@ -137,6 +137,7 @@ defineExpose({ setFieldValue });
'cursor-wait': loading, 'cursor-wait': loading,
}" }"
:loading="loading" :loading="loading"
aria-label="login"
class="w-full" class="w-full"
@click="handleSubmit" @click="handleSubmit"
> >

View File

@ -97,6 +97,7 @@ function goToLogin() {
'cursor-wait': loading, 'cursor-wait': loading,
}" }"
:loading="loading" :loading="loading"
aria-label="register"
class="mt-2 w-full" class="mt-2 w-full"
@click="handleSubmit" @click="handleSubmit"
> >

View File

@ -30,16 +30,21 @@ const props = withDefaults(
}, },
); );
const keyword = ref('');
const searchInputRef = ref<HTMLInputElement>();
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
onCancel() { onCancel() {
modalApi.close(); modalApi.close();
}, },
onOpenChange(isOpen: boolean) {
if (!isOpen) {
keyword.value = '';
}
},
}); });
const open = modalApi.useStore((state) => state.isOpen); const open = modalApi.useStore((state) => state.isOpen);
const keyword = ref('');
const searchInputRef = ref<HTMLInputElement>();
function handleClose() { function handleClose() {
modalApi.close(); modalApi.close();
keyword.value = ''; keyword.value = '';

View File

@ -0,0 +1,20 @@
import { expect, test } from '@playwright/test';
import { authLogin } from './common/auth';
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test.describe('Auth Login Page Tests', () => {
test('check title and page elements', async ({ page }) => {
// 获取页面标题并断言标题包含 'Vben Admin'
const title = await page.title();
expect(title).toContain('Vben Admin');
});
// 测试用例: 成功登录
test('should successfully login with valid credentials', async ({ page }) => {
await authLogin(page);
});
});

View File

@ -0,0 +1,46 @@
import type { Page } from '@playwright/test';
import { expect } from '@playwright/test';
export async function authLogin(page: Page) {
// 确保登录表单正常
const usernameInput = await page.locator(`input[name='username']`);
await expect(usernameInput).toBeVisible();
const passwordInput = await page.locator(`input[name='password']`);
await expect(passwordInput).toBeVisible();
const sliderCaptcha = await page.locator(`div[name='captcha']`);
const sliderCaptchaAction = await page.locator(`div[name='captcha-action']`);
await expect(sliderCaptcha).toBeVisible();
await expect(sliderCaptchaAction).toBeVisible();
// 拖动验证码滑块
// 获取拖动按钮的位置
const sliderCaptchaBox = await sliderCaptcha.boundingBox();
if (!sliderCaptchaBox) throw new Error('滑块未找到');
const actionBoundingBox = await sliderCaptchaAction.boundingBox();
if (!actionBoundingBox) throw new Error('要拖动的按钮未找到');
// 计算起始位置和目标位置
const startX = actionBoundingBox.x + actionBoundingBox.width / 2; // div 中心的 x 坐标
const startY = actionBoundingBox.y + actionBoundingBox.height / 2; // div 中心的 y 坐标
const targetX = startX + sliderCaptchaBox.width + actionBoundingBox.width; // 向右拖动容器的宽度
const targetY = startY; // y 坐标保持不变
// 模拟鼠标拖动
await page.mouse.move(startX, startY); // 移动到 action 的中心
await page.mouse.down(); // 按下鼠标
await page.mouse.move(targetX, targetY, { steps: 20 }); // 拖动到目标位置
await page.mouse.up(); // 松开鼠标
// 在拖动后进行断言检查action是否在预期位置,
const newActionBoundingBox = await sliderCaptchaAction.boundingBox();
expect(newActionBoundingBox?.x).toBeGreaterThan(actionBoundingBox.x);
// 到这里已经校验成功,点击进行登录
await page.waitForTimeout(300);
await page.getByRole('button', { name: 'login' }).click();
}

View File

@ -20,7 +20,10 @@
"build:analyze": "pnpm vite build --mode analyze", "build:analyze": "pnpm vite build --mode analyze",
"dev": "pnpm vite --mode development", "dev": "pnpm vite --mode development",
"preview": "vite preview", "preview": "vite preview",
"typecheck": "vue-tsc --noEmit --skipLibCheck" "typecheck": "vue-tsc --noEmit --skipLibCheck",
"test:e2e": "playwright test",
"test:e2e-ui": "playwright test --ui",
"test:e2e-codegen": "playwright codegen"
}, },
"imports": { "imports": {
"#/*": "./src/*" "#/*": "./src/*"

View File

@ -0,0 +1,108 @@
import type { PlaywrightTestConfig } from '@playwright/test';
import { devices } from '@playwright/test';
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();
/**
* See https://playwright.dev/docs/test-configuration.
*/
const config: PlaywrightTestConfig = {
expect: {
/**
* Maximum time expect() should wait for the condition to be met.
* For example in `await expect(locator).toHaveText();`
*/
timeout: 5000,
},
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
outputDir: 'node_modules/.e2e/test-results/',
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
},
// {
// name: 'firefox',
// use: {
// ...devices['Desktop Firefox'],
// },
// },
// {
// name: 'webkit',
// use: {
// ...devices['Desktop Safari'],
// },
// },
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: {
// ...devices['Pixel 5'],
// },
// },
// {
// name: 'Mobile Safari',
// use: {
// ...devices['iPhone 12'],
// },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: {
// channel: 'msedge',
// },
// },
// {
// name: 'Google Chrome',
// use: {
// channel: 'chrome',
// },
// },
],
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: [
['list'],
['html', { outputFolder: 'node_modules/.e2e/test-results' }],
],
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
testDir: './__tests__/e2e',
/* Maximum time one test can run for. */
timeout: 30 * 1000,
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
actionTimeout: 0,
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: 'http://localhost:5555',
/* Only on CI systems run the tests headless */
headless: !!process.env.CI,
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'retain-on-failure',
},
/* Run your local dev server before starting the tests */
webServer: {
command: process.env.CI ? 'pnpm preview --port 5555' : 'pnpm dev',
port: 5555,
reuseExistingServer: !process.env.CI,
},
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
};
export default config;

File diff suppressed because it is too large Load Diff

View File

@ -29,6 +29,7 @@ catalog:
'@jspm/generator': ^2.3.1 '@jspm/generator': ^2.3.1
'@manypkg/get-packages': ^2.2.2 '@manypkg/get-packages': ^2.2.2
'@nolebase/vitepress-plugin-git-changelog': ^2.5.0 '@nolebase/vitepress-plugin-git-changelog': ^2.5.0
'@playwright/test': ^1.47.2
'@radix-icons/vue': ^1.0.0 '@radix-icons/vue': ^1.0.0
'@stylistic/stylelint-plugin': ^3.0.1 '@stylistic/stylelint-plugin': ^3.0.1
'@tailwindcss/nesting': 0.0.0-insiders.565cd3e '@tailwindcss/nesting': 0.0.0-insiders.565cd3e
@ -39,7 +40,6 @@ catalog:
'@types/chalk': ^2.2.0 '@types/chalk': ^2.2.0
'@types/eslint': ^9.6.1 '@types/eslint': ^9.6.1
'@types/html-minifier-terser': ^7.0.2 '@types/html-minifier-terser': ^7.0.2
'@types/jsdom': ^21.1.7
'@types/jsonwebtoken': ^9.0.7 '@types/jsonwebtoken': ^9.0.7
'@types/lodash.clonedeep': ^4.5.9 '@types/lodash.clonedeep': ^4.5.9
'@types/node': ^22.5.5 '@types/node': ^22.5.5
@ -103,10 +103,10 @@ catalog:
get-port: ^7.1.0 get-port: ^7.1.0
globals: ^15.9.0 globals: ^15.9.0
h3: ^1.12.0 h3: ^1.12.0
happy-dom: ^15.7.4
html-minifier-terser: ^7.2.0 html-minifier-terser: ^7.2.0
husky: ^9.1.6 husky: ^9.1.6
is-ci: ^3.0.1 is-ci: ^3.0.1
jsdom: ^25.0.1
jsonc-eslint-parser: ^2.4.0 jsonc-eslint-parser: ^2.4.0
jsonwebtoken: ^9.0.2 jsonwebtoken: ^9.0.2
lint-staged: ^15.2.10 lint-staged: ^15.2.10
@ -121,6 +121,7 @@ catalog:
pinia: 2.2.2 pinia: 2.2.2
pinia-plugin-persistedstate: ^4.0.2 pinia-plugin-persistedstate: ^4.0.2
pkg-types: ^1.2.0 pkg-types: ^1.2.0
playwright: ^1.47.2
postcss: ^8.4.47 postcss: ^8.4.47
postcss-antd-fixes: ^0.2.0 postcss-antd-fixes: ^0.2.0
postcss-html: ^1.7.0 postcss-html: ^1.7.0

View File

@ -33,6 +33,7 @@
"stub": { "stub": {
"cache": false "cache": false
}, },
"test:e2e": {},
"dev": { "dev": {
"dependsOn": [], "dependsOn": [],
"outputs": [], "outputs": [],

View File

@ -1,10 +1,11 @@
import Vue from '@vitejs/plugin-vue'; import Vue from '@vitejs/plugin-vue';
import VueJsx from '@vitejs/plugin-vue-jsx'; import VueJsx from '@vitejs/plugin-vue-jsx';
import { defineConfig } from 'vitest/config'; import { configDefaults, defineConfig } from 'vitest/config';
export default defineConfig({ export default defineConfig({
plugins: [Vue(), VueJsx()], plugins: [Vue(), VueJsx()],
test: { test: {
environment: 'jsdom', environment: 'happy-dom',
exclude: [...configDefaults.exclude, '**/e2e/**'],
}, },
}); });