优化前端 CI 测试分工并去除 AGC 重复执行
新增继承根配置的前端 CI 测试入口,排除由 AGC 专属任务执行的测试目录 增加实际测试文件集合校验,确保分组互斥且覆盖原有全量 同步更新 CI 工作流、开发运维文档和共享开发流程
This commit is contained in:
@@ -410,7 +410,7 @@ jobs:
|
||||
run: bash scripts/ci-npm-ci-with-retry.sh
|
||||
|
||||
- name: Run frontend and script tests
|
||||
run: npm run test
|
||||
run: npm run test:ci:frontend
|
||||
|
||||
- name: Run BgFilter worker smoke harness tests
|
||||
run: npm run bgfilter-worker:smoke-test
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## 标准流程
|
||||
|
||||
前端测试稳定性验证使用根目录 `npm test`(与 Frontend tests job 相同),保留 Vitest 的 8 worker 上限。涉及异步资源展示时,组件测试必须 mock 所有会触发的网络请求,每次调用创建独立 `Response`,并等待最终 DOM 状态而非仅等待 fetch 被调用。换签 Hook 的测试通过 `vitest.config.ts` 的 include 纳入全量运行;新增测试文件后需确认实际执行名单,命令参数指定文件不会绕过 include 白名单。排查顺序依赖可使用 `npm test -- --sequence.shuffle --sequence.seed=9467`,但不能以重试成功替代失败原因分析。
|
||||
前端测试稳定性验证使用根目录 `npm test` 执行全量集合,保留 Vitest 的 8 worker 上限。CI 的 `Frontend tests` 使用 `npm run test:ci:frontend`,继承根配置并排除 `apps/ai-game-creator-shell/tests/**`;该目录由 `AI game creator shell web tests` 执行,两个 job 的 Vitest 文件集合互斥且并集等于本地全量。原生壳定向检查与 Repository checks 的 AppSurface 检查仍保留。涉及异步资源展示时,组件测试必须 mock 所有会触发的网络请求,每次调用创建独立 `Response`,并等待最终 DOM 状态而非仅等待 fetch 被调用。换签 Hook 的测试通过 `vitest.config.ts` 的 include 纳入全量运行;新增测试文件后需确认实际执行名单,命令参数指定文件不会绕过 include 白名单。排查顺序依赖可使用 `npm test -- --sequence.shuffle --sequence.seed=9467`,但不能以重试成功替代失败原因分析。
|
||||
|
||||
用例隔离必须包括浏览器状态与 mock 实现:修改 `window.history` 后恢复基线路由;`spyOn(window, 'getSelection')` 等 spy 在用例结束后 restore;`clearAllMocks` 仅清调用记录,不能恢复被上一个用例替换的返回值。顺序打乱暴露的失败应修复泄漏来源,保留原有业务断言。
|
||||
|
||||
|
||||
@@ -293,7 +293,7 @@ npm run check
|
||||
所有 CI job 和 Jenkins Web Build 在根 workspace 安装前都必须确认 `npm --version` 为 `10.9.7`。Gitea job 使用预构建镜像内的固定版本;Jenkins Web Build 在每个独立 `bash -lc` 中 source `scripts/jenkins-prepare-npm-env.sh`,首次为 Jenkins 运行用户的版本隔离目录引导同版 npm,后续复用并把该 `bin` 放到 `PATH` 首位。旧固定镜像缺少版本元数据时只能报告 `npm_version=partial` 并由当前 job 的根 `npm ci` 继续校验 lock,不能把过渡状态当作工具链已闭合。
|
||||
|
||||
- `Repository checks`:调用唯一入口 `npm run check:repository-ci`,执行 `npm run lint`、AI 游戏创作壳 AppSurface 定向测试、主站与后台生产构建和提交差异空白检查。本地 master `pre-push` 复用同一入口,禁止在 workflow 与 hook 中维护两份近似命令。
|
||||
- `Frontend tests`:按唯一根 workspace lockfile 执行一次干净的 `npm ci`,再独立执行根 `npm run test`、`npm run bgfilter-worker:smoke-test`、`npm run check:production-health-patrol`、`npm run check:production-api-release` 和 `npm run check:production-api-deploy`,让 Vitest、Node test smoke harness 及不依赖真实服务的生产巡检 / 发布 / 部署行为 fixture 在 Gitea job 中持续执行;其中 `.test.mjs` 使用 Node test runner,不依赖 Vitest 的 `scripts/**/*.test.ts` 收集规则。
|
||||
- `Frontend tests`:按唯一根 workspace lockfile 执行一次干净的 `npm ci`,再独立执行根 `npm run test:ci:frontend`、`npm run bgfilter-worker:smoke-test`、`npm run check:production-health-patrol`、`npm run check:production-api-release` 和 `npm run check:production-api-deploy`,让 Vitest、Node test smoke harness 及不依赖真实服务的生产巡检 / 发布 / 部署行为 fixture 在 Gitea job 中持续执行。`test:ci:frontend` 使用 `vitest.frontend-ci.config.ts` 继承根配置,仅排除 `apps/ai-game-creator-shell/tests/**`,由 `AI game creator shell web tests` 执行该目录,避免两个 job 重复运行 AGC 前端测试;本地 `npm test` 仍执行原有全量集合。CI 契约测试使用 Vitest 实际文件发现验证两组互斥、并集等于全量;原生壳定向检查与 Repository checks 的 AppSurface 检查仍保留。其中 `.test.mjs` 使用 Node test runner,不依赖 Vitest 的 `scripts/**/*.test.ts` 收集规则。
|
||||
- `Backend tests`:先对 `server-rs/Cargo.lock` 执行带 5 次整命令级有界重试的 `cargo fetch --locked`,再执行 `npm run check:server-rs-ddd`、`cargo test --locked --workspace --exclude spacetime-module --no-fail-fast`、`cargo test --locked -p spacetime-module --no-fail-fast`、`api-server --all-targets` 编译和 `cargo check --locked -p spacetime-module`;普通 workspace host 测试排除 `spacetime-module` 以避免其 `spacetime-types` feature 统一污染领域 crate,模块自身的纯单元测试通过独立 package test 纳入门禁。`spacetime-module` 的 reducer / procedure 运行时行为仍必须通过真实 SpacetimeDB runtime/integration harness 验证,不能把 host 链接支持当作运行时替身。依赖准备必须位于会触发 Cargo build 的 DDD / 产物边界门禁之前,避免锁新增依赖未命中镜像缓存时绕过既有下载重试。runner 安装 `ffmpeg`,避免视频抽帧测试因工具缺失提前返回。依赖真实服务或密钥的测试必须显式 `ignored`,不能让普通 PR job访问现场环境。
|
||||
- `Native shell tests`:按唯一根 workspace lockfile 安装全部 App 依赖后,用 `npm run check:native-shells:contract`、`npm run check:native-shells:shells` 和 `npm run check:native-shells:release` 分别执行静态契约、H5 / 微信 / Expo / Tauri 桌面壳运行时门禁,以及依赖发布产物的构建 smoke,最后确认桌面壳与 AI 游戏创作壳的 `Cargo.lock` 都没有被构建过程改写。
|
||||
- `AI game creator shell web tests`:执行 `npm run check:native-shells:agc-web`(即 `npm run ai-game-creator-shell:check:web`:AGC 壳 typecheck 与壳内测试)。该分组不触碰 Cargo,因此不预热 Rust 依赖。
|
||||
|
||||
@@ -126,6 +126,7 @@
|
||||
"format:staged": "lint-staged",
|
||||
"check:pre-push-master": "bash scripts/pre-push-master.sh",
|
||||
"test": "vitest run",
|
||||
"test:ci:frontend": "vitest run --config vitest.frontend-ci.config.ts",
|
||||
"test:watch": "vitest",
|
||||
"container:init": "node scripts/container-compose.mjs init",
|
||||
"container:build": "node scripts/container-compose.mjs build",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createVitest } from 'vitest/node';
|
||||
|
||||
const workflow = readFileSync(
|
||||
resolve(process.cwd(), '.gitea/workflows/project-ci.yml'),
|
||||
@@ -397,7 +398,10 @@ describe('project CI workflow', () => {
|
||||
|
||||
it('keeps frontend, operations fixture, and native shell gates in dedicated jobs', () => {
|
||||
const frontendJob = jobSection('frontend-tests');
|
||||
expect(frontendJob).toContain('run: npm run test');
|
||||
expect(frontendJob).toMatch(/^ {8}run: npm run test:ci:frontend$/mu);
|
||||
expect(rootPackageJson.scripts?.['test:ci:frontend']).toBe(
|
||||
'vitest run --config vitest.frontend-ci.config.ts',
|
||||
);
|
||||
expect(frontendJob).toContain('run: npm run bgfilter-worker:smoke-test');
|
||||
expect(frontendJob).toContain(
|
||||
'run: npm run check:production-health-patrol',
|
||||
@@ -419,6 +423,39 @@ describe('project CI workflow', () => {
|
||||
expect(nativeJob).toContain('cargo fetch --locked');
|
||||
});
|
||||
|
||||
it('partitions frontend and AGC test files without gaps or duplicates', async () => {
|
||||
expect(rootPackageJson.scripts?.test).toBe('vitest run');
|
||||
const full = await createVitest('test', {
|
||||
config: resolve('vitest.config.ts'),
|
||||
watch: false,
|
||||
});
|
||||
try {
|
||||
const allFiles = (await full.globTestFiles()).map(([, file]) => file);
|
||||
const agcFiles = (
|
||||
await full.globTestFiles(['apps/ai-game-creator-shell/tests'])
|
||||
).map(([, file]) => file);
|
||||
const frontend = await createVitest('test', {
|
||||
config: resolve('vitest.frontend-ci.config.ts'),
|
||||
watch: false,
|
||||
});
|
||||
try {
|
||||
const frontendFiles = (await frontend.globTestFiles()).map(
|
||||
([, file]) => file,
|
||||
);
|
||||
expect(agcFiles.length).toBeGreaterThan(0);
|
||||
expect(frontendFiles.length).toBeGreaterThan(0);
|
||||
expect(frontendFiles.filter((file) => agcFiles.includes(file))).toEqual(
|
||||
[],
|
||||
);
|
||||
expect([...frontendFiles, ...agcFiles].sort()).toEqual(allFiles.sort());
|
||||
} finally {
|
||||
await frontend.close();
|
||||
}
|
||||
} finally {
|
||||
await full.close();
|
||||
}
|
||||
}, 30_000);
|
||||
|
||||
it('runs every native shell gate group exactly once across the split jobs', () => {
|
||||
for (const [group, script] of Object.entries(nativeShellGateGroupScripts)) {
|
||||
expect(rootPackageJson.scripts?.[`check:native-shells:${group}`]).toBe(
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { mergeConfig } from 'vitest/config';
|
||||
|
||||
import config from './vitest.config';
|
||||
|
||||
// AGC 前端测试由专属 CI job 执行;本地全量入口继续使用根配置。
|
||||
export default mergeConfig(config, {
|
||||
test: {
|
||||
exclude: ['apps/ai-game-creator-shell/tests/**'],
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user