Files
openclaw/scripts/e2e/qa-plugin-install-sources.mjs
Vincent Koc 06b5f0b2ff test(qa): cover CLI onboarding and plugin install sources (#118882)
* test(qa): cover CLI onboarding and plugin sources

* test(qa): assert npm file plugin source

* test(qa): redact onboarding password assertion

* test(qa): retain onboarding evidence log
2026-08-04 06:54:42 +08:00

105 lines
3.4 KiB
JavaScript

import { spawn } from "node:child_process";
import { createQaScriptEvidenceWriter } from "../../test/e2e/qa-lab/runtime/script-evidence.js";
const args = process.argv.slice(2);
const artifactBaseIndex = args.indexOf("--artifact-base");
const artifactBase = args[artifactBaseIndex + 1];
if (artifactBaseIndex === -1 || !artifactBase || artifactBase.startsWith("-")) {
throw new Error("--artifact-base is required");
}
const writer = createQaScriptEvidenceWriter({
artifactBase,
logFileName: "cli-plugin-install-sources.log",
primaryModel: "openai/gpt-5.6-luna",
providerMode: "mock-openai",
repoRoot: process.cwd(),
target: {
id: "cli-plugin-install-sources",
title: "CLI plugin install sources",
sourcePath: "scripts/e2e/qa-plugin-install-sources.mjs",
docsRefs: ["docs/cli/plugins.md"],
codeRefs: [
"src/cli/plugins-cli.ts",
"src/plugins/install.ts",
"src/plugins/install-source-info.ts",
"scripts/e2e/lib/plugins/sweep.sh",
"scripts/e2e/lib/bundled-plugin-install-uninstall/sweep.sh",
],
},
});
const requiredMarkers = [
"Testing tgz install flow...",
"Testing install from local folder (plugins.load.paths)...",
"Testing install from npm spec (file:)...",
"Testing install and update from npm registry...",
"Testing install from git repo and plugin CLI execution...",
"Testing marketplace install and update flows...",
"Testing ClawHub plugin install and uninstall...",
];
const seenMarkers = new Set();
let markerCarry = "";
let receivedSignal = null;
let bundledSelectionProven = false;
const collectMarkers = (chunk) => {
const text = `${markerCarry}${chunk}`;
for (const marker of requiredMarkers) {
if (text.includes(marker)) {
seenMarkers.add(marker);
}
}
if (/bundled plugin install\/uninstall sweep passed \(([1-9]\d*) plugin\(s\)\)/u.test(text)) {
bundledSelectionProven = true;
}
markerCarry = text.slice(-256);
};
const runScript = (script, env = {}) =>
new Promise((resolve, reject) => {
const child = spawn("bash", [script], {
env: { ...process.env, ...env },
stdio: ["inherit", "pipe", "pipe"],
});
const onChunk = (stream, chunk) => {
writer.appendLog(chunk);
collectMarkers(chunk);
stream.write(chunk);
};
child.stdout.on("data", (chunk) => onChunk(process.stdout, chunk));
child.stderr.on("data", (chunk) => onChunk(process.stderr, chunk));
child.once("error", reject);
child.once("close", (code, signal) => {
receivedSignal ??= signal;
resolve(code ?? 1);
});
});
const startedAt = Date.now();
let status = await runScript("scripts/e2e/plugins-docker.sh", {
OPENCLAW_PLUGINS_E2E_CLAWHUB: "1",
OPENCLAW_PLUGINS_E2E_LIVE_CLAWHUB: "0",
});
if (status === 0) {
status = await runScript("scripts/e2e/bundled-plugin-install-uninstall-docker.sh");
}
const missingMarkers = requiredMarkers.filter((marker) => !seenMarkers.has(marker));
if (!bundledSelectionProven) {
missingMarkers.push("bundled plugin install/uninstall sweep passed (>0 plugin(s))");
}
if (missingMarkers.length > 0) {
status = 1;
}
await writer.write({
status: status === 0 ? "pass" : "fail",
durationMs: Date.now() - startedAt,
...(missingMarkers.length > 0
? { details: `missing executable assertion marker(s): ${missingMarkers.join(", ")}` }
: {}),
});
if (receivedSignal) {
process.kill(process.pid, receivedSignal);
}
process.exit(status);