Skip to content

Commit

Permalink
Merge branch 'main'
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshuaKGoldberg committed Oct 2, 2023
1 parent b05283e commit 6062dca
Show file tree
Hide file tree
Showing 26 changed files with 705 additions and 0 deletions.
54 changes: 54 additions & 0 deletions src/cft.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cft = void 0;
const complaining_1 = require("./complaining");

Check failure on line 4 in src/cft.js

View workflow job for this annotation

GitHub Actions / lint

Missing file extension "js" for "./complaining"
const console_1 = require("./console");

Check failure on line 5 in src/cft.js

View workflow job for this annotation

GitHub Actions / lint

Missing file extension "js" for "./console"
const defaults_1 = require("./defaults");

Check failure on line 6 in src/cft.js

View workflow job for this annotation

GitHub Actions / lint

Missing file extension "js" for "./defaults"
const selectTestFramework_1 = require("./environments/selectTestFramework");

Check failure on line 7 in src/cft.js

View workflow job for this annotation

GitHub Actions / lint

Missing file extension "js" for "./environments/selectTestFramework"
const selectSpyFactory_1 = require("./spies/selectSpyFactory");

Check failure on line 8 in src/cft.js

View workflow job for this annotation

GitHub Actions / lint

Missing file extension "js" for "./spies/selectSpyFactory"
const defaultReportComplaint = ({ error }) => {
throw error;
};
const cft = (rawRequest) => {

Check failure on line 12 in src/cft.js

View workflow job for this annotation

GitHub Actions / lint

Expected blank line before this statement
const request = (0, defaults_1.setDefaults)(rawRequest);
const spyFactory = (0, selectSpyFactory_1.selectSpyFactory)(request);
const testFramework = (0, selectTestFramework_1.selectTestFramework)(request);
const methodSpies = {};
const relevantMethodNames = console_1.consoleMethodNames.filter(
(name) => !request.console[name]
);
// Before each test, we spy on the console's methods
testFramework.beforeEach(() => {
for (const methodName of relevantMethodNames) {
methodSpies[methodName] = spyFactory(console, methodName);
}
});
// After each test, we collect the spied method calls, reporting on any found
testFramework.afterEach(
({ reportComplaint = defaultReportComplaint } = {}) => {
var _a, _b;
const methodsWithCalls = [];
for (const methodName of relevantMethodNames) {
const spy = methodSpies[methodName];
const methodCalls = spy.getCalls();
const filteredCalls =
(_b =
(_a = testFramework.mapSpyCalls) === null || _a === void 0
? void 0
: _a.call(testFramework, { methodCalls, methodName })) !== null &&
_b !== void 0
? _b
: methodCalls;
spy.restore();
if (filteredCalls.length !== 0) {
methodsWithCalls.push([methodName, filteredCalls]);
}
}
if (methodsWithCalls.length === 0) {

Check failure on line 47 in src/cft.js

View workflow job for this annotation

GitHub Actions / lint

Expected blank line before this statement
return;
}
reportComplaint((0, complaining_1.createComplaint)(methodsWithCalls));

Check failure on line 50 in src/cft.js

View workflow job for this annotation

GitHub Actions / lint

Expected blank line before this statement
}
);
};
exports.cft = cft;

Check failure on line 54 in src/cft.js

View workflow job for this annotation

GitHub Actions / lint

Expected blank line before this statement
23 changes: 23 additions & 0 deletions src/complaining/createComplaint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createComplaint = void 0;
const formatMethodComplaint_1 = require("./formatMethodComplaint");

Check failure on line 4 in src/complaining/createComplaint.js

View workflow job for this annotation

GitHub Actions / lint

Missing file extension "js" for "./formatMethodComplaint"
const createComplaint = (methodsWithCalls) => {
const methodComplaints = methodsWithCalls
.map(formatMethodComplaint_1.formatMethodComplaint)
.join("\n");
const s = methodsWithCalls.length === 1 ? "" : "s";
// It looks like something wrote to the console during your test!
// Put a breakpoint on this line and check the methodsWithCalls variable to see details.
const error = new Error(
`Oh no! Your test called the following console method${s}:\n${methodComplaints}`
);
return {
error,
methodComplaints: methodsWithCalls.map(([methodName, methodCalls]) => ({
methodCalls,
methodName,
})),
};
};
exports.createComplaint = createComplaint;
13 changes: 13 additions & 0 deletions src/complaining/formatComplaintCall.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatComplaintCall = void 0;
const formatComplaintLineArg = (arg) => {
try {
return JSON.stringify(arg) || JSON.stringify(`${arg}`);
} catch (_a) {
return `A recursive object with keys: ${Object.keys(arg).join(", ")}`;
}
};
const formatComplaintCall = (call) =>
call.map(formatComplaintLineArg).join(", ");
exports.formatComplaintCall = formatComplaintCall;
24 changes: 24 additions & 0 deletions src/complaining/formatComplaintCall.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
const formatComplaintCall_1 = require("./formatComplaintCall");
(0, vitest_1.describe)("formatComplaintCall", () => {
vitest_1.it.each([
[[false], "false"],
[[true], "true"],
[[{}], "{}"],
[[{ inner: {} }], '{"inner":{}}'],
[
(() => {
const x = { prop: {} };
x.prop = x;
return [x];
})(),
"A recursive object with keys: prop",
],
[[1, "two", [3]], '1, "two", [3]'],
])(`formats %p as %p`, (args, expected) => {
const actual = (0, formatComplaintCall_1.formatComplaintCall)(args);
(0, vitest_1.expect)(actual).toBe(expected);
});
});
20 changes: 20 additions & 0 deletions src/complaining/formatMethodComplaint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatMethodComplaint = void 0;
const formatComplaintCall_1 = require("./formatComplaintCall");
const lineThreshold = 3;
const formatMethodComplaint = ([methodName, calls]) => {
const summary = ` * ${methodName} (${calls.length} call${
calls.length === 1 ? "" : "s"
})`;
const lines = calls
.slice(0, Math.min(calls.length, lineThreshold))
.map(formatComplaintLineWithIndex);
if (calls.length > lineThreshold) {
lines.push(`...${calls.length - lineThreshold} more`);
}
return `${summary}\n${lines.map((line) => ` > Call ${line}`).join("\n")}`;
};
exports.formatMethodComplaint = formatMethodComplaint;
const formatComplaintLineWithIndex = (call, i) =>
`${i}: ${(0, formatComplaintCall_1.formatComplaintCall)(call)}`;
27 changes: 27 additions & 0 deletions src/complaining/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatMethodComplaint =
exports.formatComplaintCall =
exports.createComplaint =
void 0;
var createComplaint_1 = require("./createComplaint");
Object.defineProperty(exports, "createComplaint", {
enumerable: true,
get: function () {
return createComplaint_1.createComplaint;
},
});
var formatComplaintCall_1 = require("./formatComplaintCall");
Object.defineProperty(exports, "formatComplaintCall", {
enumerable: true,
get: function () {
return formatComplaintCall_1.formatComplaintCall;
},
});
var formatMethodComplaint_1 = require("./formatMethodComplaint");
Object.defineProperty(exports, "formatMethodComplaint", {
enumerable: true,
get: function () {
return formatMethodComplaint_1.formatMethodComplaint;
},
});
11 changes: 11 additions & 0 deletions src/console.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.consoleMethodNames = void 0;
const isValidMemberName = (methodName) =>
!methodName.startsWith("_") && methodName[0].toLowerCase() === methodName[0];
exports.consoleMethodNames = Object.keys(console)
.filter(
(methodName) =>
isValidMemberName(methodName) && typeof console[methodName] === "function"
)
.sort();
10 changes: 10 additions & 0 deletions src/defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setDefaults = void 0;
const defaults = {
console: {},
};
const setDefaults = (request = {}) => {
return Object.assign(Object.assign({}, defaults), request);
};
exports.setDefaults = setDefaults;
29 changes: 29 additions & 0 deletions src/environments/ava.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.selectAvaEnvironment = void 0;
const isAva = (testFramework) => {
return (
typeof testFramework !== "undefined" &&
typeof testFramework.afterEach !== "undefined" &&
typeof testFramework.beforeEach !== "undefined" &&
typeof testFramework.failing !== "undefined" &&
typeof testFramework.meta === "object" &&
typeof testFramework.meta.file === "string" &&
typeof testFramework.serial !== "undefined" &&
typeof testFramework.serial.cb !== "undefined"
);
};
const selectAvaEnvironment = ({ testFramework }) => {
if (!isAva(testFramework)) {
return undefined;
}
return {
afterEach: (callback) => {
testFramework.afterEach(() => {
callback();
});
},
beforeEach: testFramework.beforeEach,
};
};
exports.selectAvaEnvironment = selectAvaEnvironment;
30 changes: 30 additions & 0 deletions src/environments/jasmine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.selectJasmineEnvironment = void 0;
const selectJasmineEnvironment = () => {
if (
typeof afterEach === "undefined" ||
typeof beforeEach === "undefined" ||
typeof jasmine === "undefined" ||
typeof jasmine.Spec === "undefined"
) {
return undefined;
}
return {
afterEach: (callback) => {
afterEach(() => {
callback({
reportComplaint({ error }) {
// Jasmine prints the error stack along with its message, resulting in a duplicate message
error.stack = error.stack.substring(error.message.length);
throw error;
},
});
});
},
beforeEach: (callback) => {
beforeEach(callback);
},
};
};
exports.selectJasmineEnvironment = selectJasmineEnvironment;
33 changes: 33 additions & 0 deletions src/environments/jest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.selectJestEnvironment = void 0;
const selectJestEnvironment = () => {
if (
typeof afterEach === "undefined" ||
typeof beforeEach === "undefined" ||
typeof jest === "undefined"
) {
return undefined;
}
let afterEachCallback;
let beforeEachCallback;
afterEach(() => {
afterEachCallback === null || afterEachCallback === void 0
? void 0
: afterEachCallback();
});
beforeEach(() => {
beforeEachCallback === null || beforeEachCallback === void 0
? void 0
: beforeEachCallback();
});
return {
afterEach: (callback) => {
afterEachCallback = callback;
},
beforeEach: (callback) => {
beforeEachCallback = callback;
},
};
};
exports.selectJestEnvironment = selectJestEnvironment;
31 changes: 31 additions & 0 deletions src/environments/lab.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.selectLabEnvironment = void 0;
const isLab = (testFramework) => {
return (
typeof testFramework !== "undefined" &&
typeof testFramework.afterEach !== "undefined" &&
typeof testFramework.beforeEach !== "undefined" &&
typeof testFramework.setOnly !== "undefined" &&
typeof testFramework._current === "object" &&
typeof testFramework._current.tests === "object"
);
};
const selectLabEnvironment = ({ testFramework }) => {
if (!isLab(testFramework)) {
return undefined;
}
return {
afterEach: (callback) => {
testFramework.afterEach(() => {
callback({
reportComplaint({ error }) {
throw error;
},
});
});
},
beforeEach: testFramework.beforeEach,
};
};
exports.selectLabEnvironment = selectLabEnvironment;
49 changes: 49 additions & 0 deletions src/environments/mocha.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.selectMochaEnvironment = void 0;
const selectMochaEnvironment = () => {
// Until there is some kind of global `mocha` variable that can be referenced,
// we check the stringified versions of its used hook methods
// See https://github.com/JoshuaKGoldberg/console-fail-test/issues/10
if (
typeof afterEach === "undefined" ||
typeof beforeEach === "undefined" ||
`${afterEach}`.replace(/\s/g, "") !==
"function(name,fn){suites[0].afterEach(name,fn);}" ||
`${beforeEach}`.replace(/\s/g, "") !==
"function(name,fn){suites[0].beforeEach(name,fn);}"
) {
return undefined;
}
return {
afterEach: (callback) => {
afterEach(function () {
if (this.currentTest.state !== "passed") {
return;
}
callback({
reportComplaint: ({ error }) => {
error.message = error.message.replace(/\n/g, "\n ");
this.test.error(error);
},
});
});
},
beforeEach,
mapSpyCalls: ({ methodCalls, methodName }) => {
if (methodCalls.length === 0 || methodName !== "log") {
return methodCalls;
}
// Mocha logs test names and status just before the after hook
// The last log, if it exists, might be the spec reporter
// It'd be nice to have more info on what the reporter has logged...
const lastCall = methodCalls[methodCalls.length - 1];
const first = lastCall[0];
if (typeof first === "string" && first.startsWith(" ")) {
methodCalls = methodCalls.slice(0, methodCalls.length - 1);
}
return methodCalls;
},
};
};
exports.selectMochaEnvironment = selectMochaEnvironment;
Loading

0 comments on commit 6062dca

Please sign in to comment.