test(frontend): garde-fou sur le transport des bundles construits (#74 Q1)

Vérifie sur les artefacts réels que `dist` résout `tauri` et `dist-web`
`http`, en lisant le marqueur `__IDEA_TRANSPORT__` des assets JS émis. La
régression de #74 était invisible aux tests unitaires : elle vivait dans la
conf de build et de packaging, pas dans le code testé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 09:09:42 +02:00
parent 5ca2ab77f3
commit 25728dee74
2 changed files with 79 additions and 0 deletions

View File

@ -8,6 +8,7 @@
"build": "tsc --noEmit && vite build", "build": "tsc --noEmit && vite build",
"build:web": "tsc --noEmit && vite build --mode web --outDir dist-web --emptyOutDir", "build:web": "tsc --noEmit && vite build --mode web --outDir dist-web --emptyOutDir",
"build:bundle": "npm run typecheck && vite build --outDir dist --emptyOutDir && vite build --mode web --outDir dist-web --emptyOutDir", "build:bundle": "npm run typecheck && vite build --outDir dist --emptyOutDir && vite build --mode web --outDir dist-web --emptyOutDir",
"test:bundle-transport": "node scripts/assert-bundle-transport.mjs",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"preview": "vite preview", "preview": "vite preview",
"test": "vitest run", "test": "vitest run",

View File

@ -0,0 +1,78 @@
import { readdir, readFile } from "node:fs/promises";
import { join } from "node:path";
const EXPECTED_TRANSPORT_BY_DIR = new Map([
["dist", "tauri"],
["dist-web", "http"],
]);
async function readJavaScriptAssets(buildDir) {
const assetsDir = join(process.cwd(), buildDir, "assets");
let entries;
try {
entries = await readdir(assetsDir, { withFileTypes: true });
} catch (error) {
throw new Error(
`Cannot read ${assetsDir}. Run npm run build:bundle before this check. ${error.message}`,
);
}
const files = entries
.filter((entry) => entry.isFile() && entry.name.endsWith(".js"))
.map((entry) => join(assetsDir, entry.name));
if (files.length === 0) {
throw new Error(`No JavaScript assets found in ${assetsDir}.`);
}
return Promise.all(
files.map(async (file) => ({
file,
source: await readFile(file, "utf8"),
})),
);
}
function transportMarkers(source) {
return [...source.matchAll(/__IDEA_TRANSPORT__="([a-z]+)"/g)].map(
(match) => match[1],
);
}
async function assertBundleTransport(buildDir, expectedTransport) {
const assets = await readJavaScriptAssets(buildDir);
const markers = assets.flatMap(({ file, source }) =>
transportMarkers(source).map((transport) => ({ file, transport })),
);
if (markers.length === 0) {
throw new Error(
`${buildDir} does not publish a __IDEA_TRANSPORT__ marker in its built JavaScript assets.`,
);
}
const unexpected = markers.filter(
({ transport }) => transport !== expectedTransport,
);
if (unexpected.length > 0) {
const details = unexpected
.map(({ file, transport }) => `${file}: ${transport}`)
.join("\n");
throw new Error(
`${buildDir} resolves the wrong transport; expected ${expectedTransport}.\n${details}`,
);
}
console.log(
`${buildDir}: __IDEA_TRANSPORT__="${expectedTransport}" (${markers.length} marker${markers.length === 1 ? "" : "s"})`,
);
}
try {
for (const [buildDir, expectedTransport] of EXPECTED_TRANSPORT_BY_DIR) {
await assertBundleTransport(buildDir, expectedTransport);
}
} catch (error) {
console.error(error instanceof Error ? error.message : error);
process.exitCode = 1;
}