Skip to content
This repository has been archived by the owner on Mar 15, 2024. It is now read-only.

Commit

Permalink
feat: Record test failure details in mocha
Browse files Browse the repository at this point in the history
Fixes #231
  • Loading branch information
dividedmind authored and brikelly committed Aug 16, 2023
1 parent f5b5567 commit 1052eb9
Show file tree
Hide file tree
Showing 8 changed files with 347 additions and 222 deletions.
27 changes: 24 additions & 3 deletions components/recorder-cli/mocha/index.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import process from "node:process";
import { hooks } from "../../../lib/node/mocha-hook.mjs";
import { ExternalAppmapError } from "../../error/index.mjs";
import {
ExternalAppmapError,
parseExceptionStack,
} from "../../error/index.mjs";
import { logInfo, logErrorWhen } from "../../log/index.mjs";
import { getUuid } from "../../uuid/index.mjs";
import { extendConfiguration } from "../../configuration/index.mjs";
Expand All @@ -26,8 +29,24 @@ import {
closeSocket,
addSocketListener,
} from "../../socket/index.mjs";
import { stringifyLocation } from "../../location/index.mjs";

const { undefined, parseInt, Boolean, Promise } = globalThis;

const { undefined, parseInt, Promise } = globalThis;
const buildTestFailure = (error) => {
if (!error?.name) {
return undefined;
}
const message = [error.name, error.message].filter(Boolean).join(": ");
const frame = parseExceptionStack(error)?.at(0);
if (!frame) {
return { message };
}
return {
message,
location: stringifyLocation({ position: frame, url: frame.fileName }),
};
};

// Accessing mocha version via the prototype is not documented but it seems stable enough.
// Added in https://github.com/mochajs/mocha/pull/3535
Expand Down Expand Up @@ -140,9 +159,11 @@ export const recordAsync = (configuration) => {
"No running mocha test case",
ExternalAppmapError,
);
const passed = context.currentTest.state === "passed";
recordStopTrack(frontend, running.track, {
type: "test",
passed: context.currentTest.state === "passed",
passed,
failure: passed ? undefined : buildTestFailure(context.currentTest.err),
});
flush();
running = null;
Expand Down
42 changes: 36 additions & 6 deletions components/recorder-cli/mocha/index.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from "../../configuration/index.mjs";
import { recordAsync } from "./index.mjs";

const { setImmediate } = globalThis;
const { Error, setImmediate, undefined } = globalThis;

const getLastMockSocket = readGlobal("GET_LAST_MOCK_SOCKET");
const receiveMockSocket = readGlobal("RECEIVE_MOCK_SOCKET");
Expand Down Expand Up @@ -45,18 +45,48 @@ setImmediate(() => {
receiveMockSocket(getLastMockSocket(), "0");
});

await beforeEach.call({
currentTest: {
parent: {
fullTitle: () => "full-title-1",
const startTest = () =>
beforeEach.call({
currentTest: {
parent: {
fullTitle: () => "full-title-1",
},
},
});

await startTest();

await afterEach.call({
currentTest: {
state: "passed",
},
});

await startTest();

await afterEach.call({
currentTest: {
state: "passed",
state: "failed",
err: new Error("test error"),
},
});

await afterAll();

const testFailure = async (error) => {
await startTest();
return afterEach.call({
currentTest: {
state: "failed",
err: error,
},
});
};

await testFailure(undefined);
await testFailure({ name: "TestError", message: "test error" });
await testFailure({
name: "TestError",
message: "test error",
stack: "TestError: test error\n at /app/foo-bar.js:4:6",
});
21 changes: 21 additions & 0 deletions components/trace/appmap/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
logInfo,
logInfoWhen,
} from "../../log/index.mjs";
import { toRelativeUrl } from "../../url/index.mjs";
import { validateAppmap } from "../../validate-appmap/index.mjs";
import { parseLocation } from "../../location/index.mjs";
import {
Expand All @@ -22,11 +23,13 @@ import { orderEventArray } from "./ordering/index.mjs";
import { getOutputUrl } from "./output.mjs";

const {
Boolean,
Map,
Array: { from: toArray },
String,
Math: { round },
RangeError,
URL,
} = globalThis;

const VERSION = "1.8.0";
Expand Down Expand Up @@ -181,6 +184,18 @@ const printApplyEventDistribution = (events, codebase) => {
.join("");
};

const makeFailure = ({ message, location }, { base }) => {
if (!location) {
return { message };
}
const {
url,
position: { line },
} = parseLocation(location);
const relative = toRelativeUrl(new URL(url, base), base);
return { message, location: [relative, line].filter(Boolean).join(":") };
};

export const compileTrace = (configuration, files, messages, termination) => {
logDebug("Trace: %j", { configuration, files, messages, termination });
const errors = [];
Expand Down Expand Up @@ -290,12 +305,18 @@ export const compileTrace = (configuration, files, messages, termination) => {
}
}
/* c8 ignore stop */

const appmap = {
version: VERSION,
metadata: compileMetadata(configuration, errors, termination),
classMap: exportClassmap(codebase),
events: digested_events,
};

if (termination.failure) {
appmap.metadata.test_failure = makeFailure(termination.failure, codebase);
}

if (configuration.validate.appmap) {
validateAppmap(appmap);
}
Expand Down
Loading

0 comments on commit 1052eb9

Please sign in to comment.