Skip to content

Commit 2136b4f

Browse files
committed
Add docs about the reporter and the code design
1 parent ca10b42 commit 2136b4f

File tree

1 file changed

+62
-17
lines changed

1 file changed

+62
-17
lines changed

v-next/hardhat-node-test-reporter/src/reporter.ts

+62-17
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,66 @@ import {
2020

2121
export const SLOW_TEST_THRESHOLD = 75;
2222

23+
/**
24+
* This is a node:test reporter that tries to mimic Mocha's default reporter, as
25+
* close as possible.
26+
*
27+
* It is designed to output information about the test runs as soon as possible
28+
* and in test defintion order.
29+
*
30+
* Once the test run ends, it will output global information about it, based on
31+
* the diagnostics emitted by node:test, and any custom or unrecognized
32+
* diagnostics message.
33+
*
34+
* Finally, it will output the failure reasons for all the failed tests.
35+
*
36+
* @param source
37+
*/
2338
export default async function* customReporter(
2439
source: TestEventSource,
2540
): TestReporterResult {
26-
// This reporter prints the tests in definition order, imitating Mocha's
27-
// default reporter as close as possible.
28-
//
29-
// We use this stack to keep track of the current test hierarchy.
30-
//
31-
// The output of each test (its context - the describe they belong to -
32-
// and if they passed/failed) is printed as soon as the test finishes.
41+
/**
42+
* The test reporter works by keeping a stack of the currently executing[1]
43+
* tests and suites. We use it to keep track of the context of a test, so that
44+
* when it passes or fails, we can print that context (if necessary), before
45+
* its results.
46+
*
47+
* Printing the context of a test will normally imply printing all the
48+
* describes where it is nested.
49+
*
50+
* As the context may be shared by more than one test, and we don't want to
51+
* repeate it, we keep track of the last printed context element. We do this
52+
* by keeping track of its index in the stack.
53+
*
54+
* We also keep track of any diagnostic message that its reported by node:test
55+
* and at the end we try to parse the global diagnostics to gather information
56+
* about the test run. If during this parsing we don't recognize or can't
57+
* properly parse one of this diagnostics, we will print it at the end.
58+
*
59+
* Whenever a test fails, we pre-format its failure reason, so that don't need
60+
* to keep the failure event in memory, and we can still print the failure
61+
* reason at the end.
62+
*
63+
* This code is structed in the following way:
64+
* - We use an async generator to process the events as they come, printing
65+
* the information as soon as possible.
66+
* - Instead of printing, we yield string.
67+
* - Any formatting that needs to be done is done in the formatting module.
68+
* - The formatting module exports functions that generate strings for the
69+
* different parts of the test run's output. They do not print anything, and
70+
* they never end in a newline. Any newline between different parts of the
71+
* output is added by the generator.
72+
* - The generaor drives the high-level format of the output, and only uses
73+
* the formatting functions to generate repetitive parts of it.
74+
*
75+
* [1] As reporter by node:test, in defintion order, which may differ from
76+
* actual execution order.
77+
*/
78+
3379
const stack: Array<TestEventData["test:start"]> = [];
3480

35-
// We use this number to keep track of which elements from the stack have
36-
// already been printed (e.g. a describe).
3781
let lastPrintedIndex: number | undefined;
3882

39-
// Diagnostics are processed at the end, so we collect them all here
4083
const diagnostics: Array<TestEventData["test:diagnostic"]> = [];
4184

4285
const preFormattedFailureReasons: string[] = [];
@@ -55,7 +98,7 @@ export default async function* customReporter(
5598
case "test:fail": {
5699
if (event.data.details.type === "suite") {
57100
// If a suite failed for a reason other than a subtest failing, we
58-
// want to print its failure, so we push it to the failures array.
101+
// want to print its failure.
59102
if (event.type === "test:fail") {
60103
if (!isSubtestFailedError(event.data.details.error)) {
61104
preFormattedFailureReasons.push(
@@ -68,14 +111,11 @@ export default async function* customReporter(
68111
}
69112
}
70113

71-
stack.pop();
72-
73114
// If a suite/describe was already printed, we need to descrease
74115
// the lastPrintedIndex, as we are removing it from the stack.
75116
//
76117
// If its nesting was 0, we print an empty line to separate top-level
77118
// describes.
78-
79119
if (event.data.nesting === 0) {
80120
lastPrintedIndex = undefined;
81121
yield "\n";
@@ -89,11 +129,13 @@ export default async function* customReporter(
89129
}
90130
}
91131

132+
// Remove the current test from the stack, as it was just processed
133+
stack.pop();
92134
continue;
93135
}
94136

95137
// If we have printed everything except the current element in the stack
96-
// all of it's context/hierarchy has been printed (e.g. the describes).\
138+
// all of it's context/hierarchy has been printed (e.g. its describes).
97139
//
98140
// Otherwise, we print all the unprinted elements in the stack, except
99141
// for the last one, which is the current test.
@@ -117,24 +159,27 @@ export default async function* customReporter(
117159
contextStack: stack,
118160
};
119161

162+
// We format the failure reason and store it in an array, so that we
163+
// can output it at the end.
120164
preFormattedFailureReasons.push(formatFailureReason(failure));
121165

122166
yield formatTestFailure(failure);
123167
}
124168

169+
// If the test was slow, we print a message about it
125170
if (event.data.details.duration_ms > SLOW_TEST_THRESHOLD) {
126171
yield formatSlowTestInfo(event.data.details.duration_ms);
127172
}
128173

129174
yield "\n";
130175

131-
stack.pop();
132-
133176
// Top-level tests are separated by an empty line
134177
if (event.data.nesting === 0) {
135178
yield "\n";
136179
}
137180

181+
// Remove the current test from the stack, as it was just processed it
182+
stack.pop();
138183
break;
139184
}
140185
case "test:stderr": {

0 commit comments

Comments
 (0)