Compare commits
2
Commits
b489cb3152
...
cda4b1825b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cda4b1825b | ||
|
|
087f27936e |
No files matched your search
@@ -113,9 +113,9 @@ pnpm dev # 并行启动 api(3000) / web(5173) / worker
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm lint && pnpm typecheck && pnpm test # 本地门禁
|
pnpm lint && pnpm typecheck && pnpm test # 本地门禁
|
||||||
pnpm build && pnpm test:e2e # 构建与端到端冒烟
|
VITE_PUBLIC_DEMO_SESSION=true pnpm test:e2e # 构建 + 端到端冒烟(demo 会话)
|
||||||
pnpm openapi:export && pnpm openapi:diff # 契约快照与破坏性变更检查
|
pnpm openapi:export && pnpm openapi:diff # 契约快照与破坏性变更检查
|
||||||
pnpm ci:local # 本地跑完整 CI 门禁
|
pnpm ci:local # 本地跑完整 CI 门禁(已内置 e2e 所需 env)
|
||||||
```
|
```
|
||||||
|
|
||||||
> Gitea 暂未部署 Actions runner,推送后远程 CI 会停留在 queued;当前以 `pnpm ci:local` 为准,runner 就绪后 `.gitea/workflows/ci.yml` 会自动接管。
|
> Gitea 暂未部署 Actions runner,推送后远程 CI 会停留在 queued;当前以 `pnpm ci:local` 为准,runner 就绪后 `.gitea/workflows/ci.yml` 会自动接管。
|
||||||
|
|||||||
+88
-6
@@ -204,7 +204,10 @@ export interface paths {
|
|||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
/** 部门树(两级,含成员数与负责人) */
|
/**
|
||||||
|
* 部门树(两级)
|
||||||
|
* @description memberCount 含下级成员、directMemberCount 仅本部门直属成员;默认只返回启用部门,管理页传 include_disabled=true
|
||||||
|
*/
|
||||||
get: operations['DepartmentsController_tree'];
|
get: operations['DepartmentsController_tree'];
|
||||||
put?: never;
|
put?: never;
|
||||||
post?: never;
|
post?: never;
|
||||||
@@ -225,7 +228,7 @@ export interface paths {
|
|||||||
put?: never;
|
put?: never;
|
||||||
/**
|
/**
|
||||||
* 新建部门 / 小组
|
* 新建部门 / 小组
|
||||||
* @description 最多两级,在小组下新建返回 42201
|
* @description 最多两级;父级必须启用;负责人必须是启用用户;同级重名返回 40903
|
||||||
*/
|
*/
|
||||||
post: operations['DepartmentsController_create'];
|
post: operations['DepartmentsController_create'];
|
||||||
delete?: never;
|
delete?: never;
|
||||||
@@ -247,8 +250,51 @@ export interface paths {
|
|||||||
delete?: never;
|
delete?: never;
|
||||||
options?: never;
|
options?: never;
|
||||||
head?: never;
|
head?: never;
|
||||||
/** 部门改名 */
|
/**
|
||||||
patch: operations['DepartmentsController_rename'];
|
* 更新部门(名称 / 负责人 / 排序)
|
||||||
|
* @description 上级关系创建后不可移动;负责人必须是启用用户
|
||||||
|
*/
|
||||||
|
patch: operations['DepartmentsController_update'];
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
'/api/v1/departments/{id}/disable': {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
/**
|
||||||
|
* 停用部门
|
||||||
|
* @description 本部门仍有在职成员、或一级部门存在启用中的下级小组 / 下级在职成员时返回 42201;已停用返回 40903
|
||||||
|
*/
|
||||||
|
post: operations['DepartmentsController_disable'];
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
'/api/v1/departments/{id}/enable': {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
/**
|
||||||
|
* 启用部门
|
||||||
|
* @description 上级必须启用;已启用返回 40903
|
||||||
|
*/
|
||||||
|
post: operations['DepartmentsController_enable'];
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
'/api/v1/roles': {
|
'/api/v1/roles': {
|
||||||
@@ -657,7 +703,9 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
DepartmentsController_tree: {
|
DepartmentsController_tree: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: {
|
||||||
|
include_disabled?: 'true' | 'false';
|
||||||
|
};
|
||||||
header?: never;
|
header?: never;
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
@@ -689,7 +737,41 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
DepartmentsController_rename: {
|
DepartmentsController_update: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content?: never;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
DepartmentsController_disable: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content?: never;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
DepartmentsController_enable: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header?: never;
|
||||||
|
|||||||
+4
-1
@@ -2,7 +2,7 @@ import { expect, type Page, test } from '@playwright/test';
|
|||||||
|
|
||||||
async function signIn(page: Page): Promise<void> {
|
async function signIn(page: Page): Promise<void> {
|
||||||
await page.goto('/login');
|
await page.goto('/login');
|
||||||
await page.getByPlaceholder('请输入账号或邮箱').fill('admin');
|
await page.getByPlaceholder('请输入账号或手机号').fill('admin');
|
||||||
await page.getByPlaceholder('请输入密码').fill('demo1234');
|
await page.getByPlaceholder('请输入密码').fill('demo1234');
|
||||||
await page.getByTestId('login-submit').click();
|
await page.getByTestId('login-submit').click();
|
||||||
await expect(page.getByTestId('app-shell')).toBeVisible();
|
await expect(page.getByTestId('app-shell')).toBeVisible();
|
||||||
@@ -12,6 +12,9 @@ test('登录页打开并进入工作台', async ({ page }) => {
|
|||||||
await page.goto('/login');
|
await page.goto('/login');
|
||||||
await expect(page.getByRole('heading', { name: '登录灵犀' })).toBeVisible();
|
await expect(page.getByRole('heading', { name: '登录灵犀' })).toBeVisible();
|
||||||
await signIn(page);
|
await signIn(page);
|
||||||
|
// A1 起登录默认落地 /system/users,再从顶部模块导航进入工作台
|
||||||
|
await expect(page).toHaveURL(/\/system\/users/);
|
||||||
|
await page.getByRole('button', { name: '工作台', exact: true }).click();
|
||||||
await expect(page).toHaveURL('/');
|
await expect(page).toHaveURL('/');
|
||||||
await expect(page.getByRole('heading', { name: '工作台', exact: true })).toBeVisible();
|
await expect(page.getByRole('heading', { name: '工作台', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('navigation', { name: '模块导航' })).toBeVisible();
|
await expect(page.getByRole('navigation', { name: '模块导航' })).toBeVisible();
|
||||||
|
|||||||
+15
-6
@@ -7,26 +7,35 @@ import { spawnSync } from 'node:child_process';
|
|||||||
|
|
||||||
const pnpm = process.env.npm_execpath ? [process.execPath, process.env.npm_execpath] : ['pnpm'];
|
const pnpm = process.env.npm_execpath ? [process.execPath, process.env.npm_execpath] : ['pnpm'];
|
||||||
|
|
||||||
/** 每一步可包含多条 pnpm 命令,任一失败立即退出。 */
|
/**
|
||||||
|
* 端到端冒烟走 vite preview + demo 会话,需与 .gitea/workflows/ci.yml 的 job env 对齐;
|
||||||
|
* 只加在构建与 e2e 步骤,避免影响单测里对真实登录流程的断言。
|
||||||
|
*/
|
||||||
|
const e2eEnv = {
|
||||||
|
VITE_PUBLIC_API_BASE_URL: process.env.VITE_PUBLIC_API_BASE_URL ?? 'http://127.0.0.1:3000/api/v1',
|
||||||
|
VITE_PUBLIC_DEMO_SESSION: process.env.VITE_PUBLIC_DEMO_SESSION ?? 'true',
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 每一步可包含多条 pnpm 命令,任一失败立即退出;env 为该步骤追加的环境变量。 */
|
||||||
const steps = [
|
const steps = [
|
||||||
{ label: '检查本地依赖(PG / Redis)', commands: [['check:dev']] },
|
{ label: '检查本地依赖(PG / Redis)', commands: [['check:dev']] },
|
||||||
{ label: '格式化检查', commands: [['format:check']] },
|
{ label: '格式化检查', commands: [['format:check']] },
|
||||||
{ label: 'Lint', commands: [['lint']] },
|
{ label: 'Lint', commands: [['lint']] },
|
||||||
{ label: '类型检查', commands: [['typecheck']] },
|
{ label: '类型检查', commands: [['typecheck']] },
|
||||||
{ label: '单元测试', commands: [['test']] },
|
{ label: '单元测试', commands: [['test']] },
|
||||||
{ label: '构建', commands: [['build']] },
|
{ label: '构建', commands: [['build']], env: e2eEnv },
|
||||||
{
|
{
|
||||||
label: '数据库迁移与种子',
|
label: '数据库迁移与种子',
|
||||||
commands: [['exec', 'prisma', 'migrate', 'deploy'], ['db:seed']],
|
commands: [['exec', 'prisma', 'migrate', 'deploy'], ['db:seed']],
|
||||||
},
|
},
|
||||||
{ label: '集成测试', commands: [['test:int']] },
|
{ label: '集成测试', commands: [['test:int']] },
|
||||||
{ label: 'OpenAPI 导出与契约检查', commands: [['openapi:export'], ['openapi:diff']] },
|
{ label: 'OpenAPI 导出与契约检查', commands: [['openapi:export'], ['openapi:diff']] },
|
||||||
{ label: '端到端测试', commands: [['test:e2e']] },
|
{ label: '端到端测试', commands: [['test:e2e']], env: e2eEnv },
|
||||||
];
|
];
|
||||||
|
|
||||||
function runPnpm(args) {
|
function runPnpm(args, env = {}) {
|
||||||
const result = spawnSync(pnpm[0], [...pnpm.slice(1), ...args], {
|
const result = spawnSync(pnpm[0], [...pnpm.slice(1), ...args], {
|
||||||
env: process.env,
|
env: { ...process.env, ...env },
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
});
|
});
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
@@ -43,7 +52,7 @@ const startedAt = Date.now();
|
|||||||
for (const [index, step] of steps.entries()) {
|
for (const [index, step] of steps.entries()) {
|
||||||
console.log(`\n[${index + 1}/${steps.length}] ${step.label}`);
|
console.log(`\n[${index + 1}/${steps.length}] ${step.label}`);
|
||||||
for (const args of step.commands) {
|
for (const args of step.commands) {
|
||||||
runPnpm(args);
|
runPnpm(args, step.env);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const seconds = ((Date.now() - startedAt) / 1000).toFixed(1);
|
const seconds = ((Date.now() - startedAt) / 1000).toFixed(1);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://turbo.build/schema.json",
|
"$schema": "https://turbo.build/schema.json",
|
||||||
"ui": "stream",
|
"ui": "stream",
|
||||||
|
"globalEnv": ["VITE_PUBLIC_API_BASE_URL", "VITE_PUBLIC_DEMO_SESSION"],
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"build": {
|
"build": {
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": ["^build"],
|
||||||
|
|||||||
Reference in new issue
Block a user