feat: v0.8.2 安全纵深补全 · 协议保真 · 断链修复 — 图片SSRF/根MEMORY.md保护根治 · Anthropic thinking回传+pause_turn续传 · 2523 用例全量回归 + E2E 扩充
CI / 类型检查 + Lint + 单元测试 (push) Failing after 9m45s
CI / 全量测试 (Electron ABI) (push) Failing after 6m28s
CI / 产物编译验证 (push) Successful in 11m18s

This commit is contained in:
2026-09-08 14:30:27 +08:00
parent 69776e447f
commit 4cd6e997b5
86 changed files with 4303 additions and 956 deletions
+70
View File
@@ -0,0 +1,70 @@
/**
* E2E 测试公共设施(v0.8.2 P3-3
*
* 封装"启动应用(隔离 userData+ 本地 mock LLM + 清理"的完整生命周期。
* 每个测试文件独立调用 launchApp() —— 应用实例与 mock 互相隔离,杜绝跨用例
* 的运行态竞态(中断链路对"上一 run 的收尾兜底定时器"这类跨用例污染敏感)。
*/
import type { ElectronApplication, Page } from '@playwright/test';
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { _electron as electron } from 'playwright';
import { startMockLLM, type MockLLMHandle } from './mock-llm';
export interface AppHarness {
electronApp: ElectronApplication;
page: Page;
mock: MockLLMHandle;
userDataDir: string;
workspaceDir: string;
/** 关闭应用 + mock + 清理临时目录 */
cleanup: () => Promise<void>;
}
export async function launchApp(): Promise<AppHarness> {
// 1. 本地 mock Provider(随机端口,仅监听 127.0.0.1
const mock = await startMockLLM();
// 2. 隔离的用户数据与工作空间目录
const userDataDir = mkdtempSync(join(tmpdir(), 'metona-e2e-user-'));
const workspaceDir = join(userDataDir, 'workspace');
mkdirSync(workspaceDir, { recursive: true });
// workspace-config.json 指向隔离工作空间(main.ts 启动时优先读取)
writeFileSync(
join(userDataDir, 'workspace-config.json'),
JSON.stringify({ workspacePath: workspaceDir }),
'utf-8',
);
// 3. 启动 Electron(产物构建由 npm run test:e2e 的 build 步骤保证)
const electronApp = await electron.launch({
args: ['.'],
env: {
...process.env,
METONA_USER_DATA_DIR: userDataDir,
METONA_E2E_SEED_CONFIG: '1',
METONA_E2E_LLM_URL: mock.url,
// 隔离冒烟环境无更新源(生产环境 updater 由 feedUrl 空值禁用,双保险)
METONA_E2E_DISABLE_UPDATE: '1',
} as Record<string, string>,
});
const page = await electronApp.firstWindow();
await page.waitForLoadState('domcontentloaded');
const cleanup = async (): Promise<void> => {
await electronApp.close();
await mock.close();
// 调试期保留 userDatamain.log 排查用);稳定后恢复清理
if (!process.env['METONA_E2E_KEEP_USER_DATA']) {
try {
rmSync(userDataDir, { recursive: true, force: true });
} catch {
/* Windows 文件句柄释放延迟 — 清理失败不影响断言 */
}
}
};
return { electronApp, page, mock, userDataDir, workspaceDir, cleanup };
}