test(qa): e2e 装配修正 + 集成/门禁测试与工具链重构
- e2e:修死选择器与蜜罐误命中、补断言、GA4/security-headers 用例诚实标注 skip 边界;新增 assert-helpers/fixtures/hydrated/primary-nav/touch-targets 等复用桩。 - 集成层:config/test/jest.integration.config.js + tests-integration/ 真库 一次性 SQLite 用例,teardown 守卫开发库指纹。 - 门禁工具链:jest.setup 归位到根、scripts/accessibility 全站 axe 节点计数、 check-brand-text-token/check-motion-constraints 机械守卫、stryker/lighthouse 配置收敛。
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 在**进程内**用真实 Prisma 客户端把 schema 应用到一次性临时 SQLite 文件。
|
||||
*
|
||||
* 由 config/test/itest/global-setup.js 通过 `npx tsx` 调用(与项目既有约定一致:
|
||||
* package.json 的 db:seed / check:contrast 同样用 `npx tsx` 跑 TS 脚本)。
|
||||
*
|
||||
* 为什么不用 `prisma db push`:db push 会自行解析数据源并连接,无法在「连接之前」证明它
|
||||
* 打开的是哪个文件。本脚本显式传入 `datasourceUrl`,并在建表后用
|
||||
* `PRAGMA database_list` 让 **SQLite 自己回答**它打开了哪个文件,再断言该路径位于
|
||||
* realpath(os.tmpdir()) 之下 —— 这是比「事后检查」更强的保证。
|
||||
*/
|
||||
import { createRequire } from 'node:module';
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const guard = require('./env.js');
|
||||
// 真实(非 mock)Prisma 客户端:与 src/lib/db.ts 走同一个生成产物
|
||||
const { PrismaClient } = require(path.join(guard.REPO_ROOT, 'src', 'generated', 'prisma', 'client.ts'));
|
||||
|
||||
const url = process.env.DATABASE_URL;
|
||||
const ddlFile = process.env.ITEST_DDL_FILE;
|
||||
const resultFile = process.env.ITEST_RESULT_FILE;
|
||||
|
||||
/** 与 src/lib/db.ts 完全一致的构造方式(无参构造 → 读 env DATABASE_URL),
|
||||
* 唯一区别是这里额外显式传 datasourceUrl,让「指向哪里」在代码里可读可审。 */
|
||||
const client = new PrismaClient({ datasourceUrl: url });
|
||||
|
||||
const resolvedUrl = guard.assertSafeDatasourceUrl(url, 'apply-schema');
|
||||
|
||||
const statements = guard.splitStatements(fs.readFileSync(ddlFile, 'utf8'));
|
||||
const applied = [];
|
||||
for (const statement of statements) {
|
||||
await client.$executeRawUnsafe(statement);
|
||||
applied.push(statement.split('\n')[0].slice(0, 60));
|
||||
}
|
||||
|
||||
// SQLite 自报打开的文件 —— 断言它确实是临时库,而不是 prisma/dev.db。
|
||||
const dbList = await client.$queryRawUnsafe('PRAGMA database_list');
|
||||
const openedFile = dbList?.[0]?.file;
|
||||
if (typeof openedFile !== 'string') {
|
||||
throw new Error(`[itest-guard] PRAGMA database_list 未返回文件路径:${JSON.stringify(dbList)}`);
|
||||
}
|
||||
const verifiedOpen = guard.assertSafeDatasourceUrl(`file:${openedFile}`, 'database_list');
|
||||
|
||||
const tables = await client.$queryRawUnsafe(
|
||||
`SELECT name FROM sqlite_master WHERE type='table' ORDER BY name`,
|
||||
);
|
||||
|
||||
// 建表后复查项目库指纹:db push/diff 期间不得改动 prisma/dev.db。
|
||||
const after = guard.devDbBaselines();
|
||||
const baseline = JSON.parse(fs.readFileSync(process.env.ITEST_BASELINE_FILE, 'utf8'));
|
||||
const drift = Object.keys(baseline).filter((key) => !guard.sameFingerprint(baseline[key], after[key]));
|
||||
if (drift.length > 0) {
|
||||
throw new Error(`[itest-guard] 项目数据源被改动:${drift.join(', ')}`);
|
||||
}
|
||||
|
||||
await client.$disconnect();
|
||||
|
||||
fs.writeFileSync(
|
||||
resultFile,
|
||||
JSON.stringify({
|
||||
ok: true,
|
||||
datasourceUrl: url,
|
||||
resolvedUrl,
|
||||
openedFile: verifiedOpen,
|
||||
statementCount: statements.length,
|
||||
applied,
|
||||
tables: tables.map((t) => t.name),
|
||||
devDb: after,
|
||||
}, null, 2),
|
||||
);
|
||||
|
||||
console.log('[itest] schema applied');
|
||||
console.log('[itest] DATABASE_URL =', url);
|
||||
console.log('[itest] sqlite opened', openedFile);
|
||||
console.log('[itest] tables =', tables.map((t) => t.name).join(','));
|
||||
Reference in New Issue
Block a user