Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions packages/interfacectl-cli/test/prepare-runtime.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,41 @@ async function runCli(args, cwd = __dirname) {
};
}

function parseJsonFromOutput(raw) {
const trimmed = raw.trim();
if (!trimmed) {
throw new Error("Expected JSON output but received an empty string");
}

try {
return JSON.parse(trimmed);
} catch {
const lines = trimmed.split(/\r?\n/).filter(Boolean);
for (let index = lines.length - 1; index >= 0; index -= 1) {
try {
return JSON.parse(lines[index]);
Comment on lines +49 to +52
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Parse trailing JSON block instead of individual lines

The fallback in parseJsonFromOutput only tries JSON.parse on each single line, which cannot parse the pretty-printed error object emitted by runPrepareRuntimeCommand (JSON.stringify(..., null, 2)), so the new logic still fails whenever a Node 18 experimental warning is prepended to stderr. In that scenario JSON.parse(trimmed) fails because of the warning prefix, each individual JSON line also fails, and the test throws Unable to parse JSON output instead of asserting on payload.code.

Useful? React with 👍 / 👎.

} catch {
// Keep scanning until we find the trailing JSON payload.
}
}

for (let index = 0; index < trimmed.length; index += 1) {
const char = trimmed[index];
if (char !== "{" && char !== "[") {
continue;
}

try {
return JSON.parse(trimmed.slice(index));
} catch {
// Keep scanning until we find a complete JSON payload.
}
}
}

throw new Error(`Unable to parse JSON output: ${trimmed}`);
}

async function writeJson(filePath, value) {
await fsp.mkdir(path.dirname(filePath), { recursive: true });
await fsp.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
Expand Down Expand Up @@ -153,7 +188,7 @@ test("prepare-runtime: emits resolved runtime payload with governance and enforc
);

assert.equal(result.exitCode, 0, result.stderr);
const payload = JSON.parse(result.stdout);
const payload = parseJsonFromOutput(result.stdout);

assert.equal(payload.surface.surfaceId, "demo-surface");
assert.equal(payload.bundle.sourcePaths.runtime, path.join(bundleRoot, "surfaces", "demo-surface", "runtime.json"));
Expand Down Expand Up @@ -192,7 +227,7 @@ test("prepare-runtime: fails with adapter error when runtime bundle is missing",
);

assert.equal(result.exitCode, 10, result.stderr);
const payload = JSON.parse(result.stderr);
const payload = parseJsonFromOutput(result.stderr);
assert.equal(payload.code, "adapter.bundle.runtime-missing");
} finally {
await fsp.rm(tempDir, { recursive: true, force: true });
Expand Down
Loading