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
tasks:
- 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>
## スター歴史
[![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">

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>
## 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
<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)
## 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
<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
{
"scripts": {
// Install dependencies
"bootstrap": "pnpm install",
// Build the project
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
// Build the project with analysis
@ -107,9 +105,9 @@ The execution command is: `pnpm run [script]` or `npm run [script]`.
// Package specification check
"publint": "vsh publint",
// 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
"test:unit": "vitest",
"test:unit": "vitest run --dom",
// Update project dependencies
"update:deps": " pnpm update --latest --recursive",
// Changeset generation and versioning

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,16 +30,21 @@ const props = withDefaults(
},
);
const keyword = ref('');
const searchInputRef = ref<HTMLInputElement>();
const [Modal, modalApi] = useVbenModal({
onCancel() {
modalApi.close();
},
onOpenChange(isOpen: boolean) {
if (!isOpen) {
keyword.value = '';
}
},
});
const open = modalApi.useStore((state) => state.isOpen);
const keyword = ref('');
const searchInputRef = ref<HTMLInputElement>();
function handleClose() {
modalApi.close();
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",
"dev": "pnpm vite --mode development",
"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": {
"#/*": "./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
'@manypkg/get-packages': ^2.2.2
'@nolebase/vitepress-plugin-git-changelog': ^2.5.0
'@playwright/test': ^1.47.2
'@radix-icons/vue': ^1.0.0
'@stylistic/stylelint-plugin': ^3.0.1
'@tailwindcss/nesting': 0.0.0-insiders.565cd3e
@ -39,7 +40,6 @@ catalog:
'@types/chalk': ^2.2.0
'@types/eslint': ^9.6.1
'@types/html-minifier-terser': ^7.0.2
'@types/jsdom': ^21.1.7
'@types/jsonwebtoken': ^9.0.7
'@types/lodash.clonedeep': ^4.5.9
'@types/node': ^22.5.5
@ -103,10 +103,10 @@ catalog:
get-port: ^7.1.0
globals: ^15.9.0
h3: ^1.12.0
happy-dom: ^15.7.4
html-minifier-terser: ^7.2.0
husky: ^9.1.6
is-ci: ^3.0.1
jsdom: ^25.0.1
jsonc-eslint-parser: ^2.4.0
jsonwebtoken: ^9.0.2
lint-staged: ^15.2.10
@ -121,6 +121,7 @@ catalog:
pinia: 2.2.2
pinia-plugin-persistedstate: ^4.0.2
pkg-types: ^1.2.0
playwright: ^1.47.2
postcss: ^8.4.47
postcss-antd-fixes: ^0.2.0
postcss-html: ^1.7.0

View File

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

View File

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