Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: improve the test reporter's messaging #5724

Merged
merged 27 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8dbbcb1
chore: improve the test cancelled message
galargh Sep 5, 2024
d9bca64
chore: report on errors in before and describe during the test run
galargh Sep 5, 2024
3957b42
chore: do not display passing top-level files
galargh Sep 5, 2024
15a5eab
chore: remove suggestion that the user forgot to await a promise
galargh Sep 5, 2024
e1b17cf
chore: force unawaited nested it to go through event loop
galargh Sep 5, 2024
270f889
chore: make the unused diagnostics messages hardhat specific
galargh Sep 5, 2024
bfb1415
chore: remove ERR_TEST_FAILURE from the formatted error
galargh Sep 6, 2024
54b7df4
test: add an async describe test
galargh Sep 7, 2024
fee6fb5
chore: do not use async describe
galargh Sep 6, 2024
a698e68
feat: do not show cancelled by parent errors
galargh Sep 6, 2024
6127086
chore: regenerate async describe test
galargh Sep 7, 2024
bc0a738
chore: show test cancellations in grey
galargh Sep 7, 2024
4524cd2
Merge branch 'galargh/test-reporter-stack-cleanup' into galargh/test-…
galargh Sep 8, 2024
0f9b331
Merge remote-tracking branch 'origin/galargh/test-reporter-stack-clea…
galargh Sep 9, 2024
014748e
feat: allow customising test only message programatically
galargh Sep 9, 2024
ed509a9
feat: do not display tests failing with parent already finished in on…
galargh Sep 9, 2024
adf3818
Revert "chore: do not use async describe"
galargh Sep 9, 2024
8bb1bce
Merge remote-tracking branch 'origin/galargh/test-reporter-stack-clea…
galargh Sep 10, 2024
20f9b4f
chore: use nodeTestOptions in node-test-runner
galargh Sep 10, 2024
7807f9f
chore: revert custom handling applied to its cancelled by parent
galargh Sep 10, 2024
d43bf39
test: add a comprehensive test for async describe behaviour
galargh Sep 10, 2024
0903307
test: regenerate async-describe-test fixture with node v22.7.0
galargh Sep 10, 2024
5599b92
test: regenerate async-describe-test fixture with node v22.8.0
galargh Sep 10, 2024
6e4bf3a
Merge branch 'v-next' into galargh/test-reporter-ui
galargh Sep 11, 2024
c61f721
chore: remove extra dot at the end of a name of a test case
galargh Sep 11, 2024
8332321
chore: move global diagnostics modifications
galargh Sep 11, 2024
b415497
chore: move helper functions to a separate file
galargh Sep 11, 2024
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
8 changes: 4 additions & 4 deletions v-next/example-project/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,15 @@ const config: HardhatUserConfig = {
],
plugins: [
pluginExample,
HardhatMochaTestRunner,
// HardhatMochaTestRunner,
// if testing node plugin, use the following line instead
// HardhatNodeTestRunner,
HardhatNodeTestRunner,
viemScketchPlugin,
],
paths: {
tests: "test/mocha",
// tests: "test/mocha",
// if testing node plugin, use the following line instead
// tests: "test/node",
tests: "test/node",
},
};

Expand Down
8 changes: 6 additions & 2 deletions v-next/example-project/test/node/other-example-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ import { describe, it } from "node:test";

import hre from "@ignored/hardhat-vnext";

describe("Other example test", () => {
describe("Other example test", async () => {
const taskName = await new Promise((resolve) =>
setTimeout(resolve, "example"),
);

it("should have the example task", () => {
assert.ok(hre.tasks.getTask("example"));
assert.ok(hre.tasks.getTask(taskName));
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,42 +1,33 @@

1) should pass 1

nested async describe with promise 1
2) should pass 2
1) should pass 2

2) nested async describe with promise 1
1) nested async describe with promise 1

nested async describe 1
3) should pass 3
2) should pass 3

3) nested async describe 1
2) nested async describe 1

nested describe 1
4) should pass 4
3) should pass 4

4) nested describe 1
3) nested describe 1


0 passing (128ms)
1 failing
0 passing (131ms)
3 cancelled

1) should pass 1:

Error: test could not be started because its parent finished
 at SuiteContext.<anonymous> (integration-tests/fixture-tests/async-describe-test/test.ts:1:193)

2) nested async describe with promise 1:
1) nested async describe with promise 1:

Error: test could not be started because its parent finished
 at SuiteContext.<anonymous> (integration-tests/fixture-tests/async-describe-test/test.ts:1:244)

3) nested async describe 1:
2) nested async describe 1:

Error: test could not be started because its parent finished
 at SuiteContext.<anonymous> (integration-tests/fixture-tests/async-describe-test/test.ts:1:422)

4) nested describe 1:
3) nested describe 1:

Error: test could not be started because its parent finished
 at SuiteContext.<anonymous> (integration-tests/fixture-tests/async-describe-test/test.ts:1:520)
Expand Down
11 changes: 6 additions & 5 deletions v-next/hardhat-node-test-reporter/integration-tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { run } from "node:test";

import { diff } from "jest-diff";

import reporter from "../src/reporter.js";
import { hardhatTestReporter } from "../src/reporter.js";

let SHOW_OUTPUT = false;
const testOnly: string[] = [];
Expand Down Expand Up @@ -72,13 +72,14 @@ for (const entry of entries) {
options = JSON.parse(readFileSync(optionsPath, "utf8"));
}

options = { ...options, files: testFiles };

const reporter = hardhatTestReporter(options);

// We disable github actions annotations, as they are misleading on PRs
// otherwise.
process.env.NO_GITHUB_ACTIONS_ANNOTATIONS = "true";
const reporterStream = run({
...options,
files: testFiles,
}).compose(reporter);
const reporterStream = run(options).compose(reporter);

for await (const chunk of reporterStream) {
outputChunks.push(chunk);
Expand Down
17 changes: 17 additions & 0 deletions v-next/hardhat-node-test-reporter/src/node-test-error-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ export function isSubtestFailedError(error: Error): boolean {
);
}

/**
* Returns true if the error represents that a test/suite was cancelled because
* the parent already finished.
*
* This typically happens if you are running in `only` and the parent is async,
* as the parent gets cancelled but continues running an async operation, and
* try to create a subtests after it.
*/
export function isParentAlreadyFinishedError(error: Error): boolean {
return (
"code" in error &&
"failureType" in error &&
error.code === "ERR_TEST_FAILURE" &&
error.failureType === "parentAlreadyFinished"
);
}

/**
* Returns true if the error represents that a test was cancelled because its
* parent failed.
Expand Down
51 changes: 43 additions & 8 deletions v-next/hardhat-node-test-reporter/src/reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
TestEventSource,
TestReporter,
TestReporterResult,
TestRunOptions,
} from "./types.js";

import chalk from "chalk";
Expand All @@ -22,6 +23,7 @@ import {
import { annotatePR } from "./github-actions.js";
import {
isCancelledByParentError,
isParentAlreadyFinishedError,
isSubtestFailedError,
} from "./node-test-error-utils.js";

Expand All @@ -47,11 +49,16 @@ export interface HardhatTestReporterConfig {
* @param source
*/

const customReporter: TestReporter = hardhatTestReporter();

const customReporter: TestReporter = hardhatTestReporter(getTestRunOptions());
export default customReporter;

function getTestRunOptions(): TestRunOptions {
const only = process.execArgv.includes("--test-only");
return { only };
}

export function hardhatTestReporter(
options: TestRunOptions,
config: HardhatTestReporterConfig = {},
): TestReporter {
return async function* (source: TestEventSource): TestReporterResult {
Expand Down Expand Up @@ -102,20 +109,32 @@ export function hardhatTestReporter(
const preFormattedFailureReasons: string[] = [];

let topLevelFilePassCount = 0;
let parentAlreadyFinishedFailureCount = 0;

for await (const event of source) {
switch (event.type) {
case "test:diagnostic": {
// We need to subtract the number of top-level file passes from the
// total number of tests/passes, as we are not printing them.
if (
event.data.message.startsWith("tests") ||
event.data.message.startsWith("pass")
) {
// We need to subtract the number of tests/suites that we chose not to
// display from the summary
if (event.data.message.startsWith("tests")) {
const parts = event.data.message.split(" ");
const count =
parseInt(parts[1], 10) -
topLevelFilePassCount -
parentAlreadyFinishedFailureCount;
event.data.message = `${parts[0]} ${count}`;
}
if (event.data.message.startsWith("pass")) {
const parts = event.data.message.split(" ");
const count = parseInt(parts[1], 10) - topLevelFilePassCount;
event.data.message = `${parts[0]} ${count}`;
}
if (event.data.message.startsWith("fail")) {
const parts = event.data.message.split(" ");
const count =
parseInt(parts[1], 10) - parentAlreadyFinishedFailureCount;
event.data.message = `${parts[0]} ${count}`;
}
Copy link
Member

Choose a reason for hiding this comment

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

Maybe all this logic should go to processGlobalDiagnostics

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a good catch. I now moved it to after processGlobalDiagnostics which I think is much cleaner, see 8332321 (#5724)

diagnostics.push(event.data);
break;
}
Expand All @@ -138,6 +157,22 @@ export function hardhatTestReporter(
break;
}

// We do not want to display failures caused by the parent already
// having finished when running with the --test-only flag because
// they are false negatives.
// We still display the failed suites, because failures for the nested
// tests (cancelled by parent errors) were already displayed.
Copy link
Member

@alcuadrado alcuadrado Sep 10, 2024

Choose a reason for hiding this comment

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

This doesn't work well, as it looks like an error. I think a good trade-off would be to detect that situation and maybe display those canceled tests/suites as a particular case in the summary at the end of the test run if needed.

This is a complex case, so we can continue addressing it in a follow up PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

I replied in a longer form in #5724 (comment) TLDR I removed any attempt to handle that case from this PR + the newest node makes the output for these cases a lot better (e.g. in hardhat package, test:only works as expected now).

if (
options.only === true &&
event.data.details.type !== "suite" &&
event.type === "test:fail" &&
isParentAlreadyFinishedError(event.data.details.error)
) {
parentAlreadyFinishedFailureCount++;
stack.pop();
break;
}

if (event.data.details.type === "suite") {
// If a suite failed for a reason other than a subtest failing, we
// want to print its failure.
Expand Down
3 changes: 3 additions & 0 deletions v-next/hardhat-node-test-reporter/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { run } from "node:test";
import type { TestEvent } from "node:test/reporters";

export type TestRunOptions = NonNullable<Parameters<typeof run>[0]>;

/**
* A map from event type to its data type.
*/
Expand Down
4 changes: 3 additions & 1 deletion v-next/hardhat-node-test-runner/src/task-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ const testWithHardhat: NewTaskActionFunction<TestActionArguments> = async (

const testOnlyMessage =
"'only' and 'runOnly' require the --only command-line option.";
const customReporter = hardhatTestReporter({ testOnlyMessage });
const customReporter = hardhatTestReporter(nodeTestOptions, {
testOnlyMessage,
});

const reporterStream = run({ files, only })
.on("test:fail", (event) => {
Expand Down