Skip to content

Commit 8cd2fe4

Browse files
9aoyCopilot
andauthored
feat: support displaying slow-running tests (#706)
Co-authored-by: Copilot <[email protected]>
1 parent 8e3349b commit 8cd2fe4

File tree

20 files changed

+190
-26
lines changed

20 files changed

+190
-26
lines changed

e2e/reporter/index.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,12 @@ describe.concurrent('reporters', () => {
7171
});
7272

7373
await cli.exec;
74+
expect(cli.stdout).toContain('[custom reporter] onTestCaseStart');
75+
expect(
76+
cli.stdout.match(/\[custom reporter\] onTestCaseStart/g)?.length,
77+
).toBe(3);
7478
expect(cli.stdout).toContain('[custom reporter] onTestFileStart');
79+
7580
expect(
7681
cli.stdout.match(/\[custom reporter\] onTestCaseResult/g)?.length,
7782
).toBe(3);

e2e/reporter/rstest.customReporterConfig.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type {
22
Reporter,
3+
TestCaseInfo,
34
TestFileInfo,
45
TestFileResult,
56
TestResult,
@@ -13,6 +14,10 @@ class MyReporter implements Reporter {
1314
reporterResult.push('[custom reporter] onTestFileStart');
1415
}
1516

17+
onTestCaseStart(_test: TestCaseInfo) {
18+
reporterResult.push('[custom reporter] onTestCaseStart');
19+
}
20+
1621
onTestCaseResult(_result: TestResult) {
1722
reporterResult.push('[custom reporter] onTestCaseResult');
1823
}

examples/node/test/index.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { describe, expect, it, rs } from '@rstest/core';
1+
import { describe, expect, it } from '@rstest/core';
22
import { sayHi } from '../src/index';
33

4-
const config = rs.getConfig();
5-
console.log(config);
64
describe('Index', () => {
75
it('should add two numbers correctly', () => {
86
expect(1 + 1).toBe(2);

packages/core/rslib.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ export default defineConfig({
104104
id: 'rstest_loaders',
105105
format: 'esm',
106106
syntax: 'es2021',
107+
dts: false,
107108
source: {
108109
entry: {
109110
cssFilterLoader: './src/core/plugins/css-filter/loader.ts',
@@ -116,6 +117,9 @@ export default defineConfig({
116117
},
117118
},
118119
],
120+
performance: {
121+
printFileSize: !isBuildWatch,
122+
},
119123
tools: {
120124
rspack: {
121125
watchOptions: {

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export type {
7373
RstestCommand,
7474
RstestExpect as Expect,
7575
RstestUtilities,
76+
TestCaseInfo,
7677
TestFileInfo,
7778
TestFileResult,
7879
TestResult,

packages/core/src/pool/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
RstestContext,
99
RuntimeConfig,
1010
Test,
11+
TestCaseInfo,
1112
TestFileInfo,
1213
TestFileResult,
1314
TestResult,
@@ -200,6 +201,11 @@ export const createPool = async ({
200201
});
201202

202203
const rpcMethods = {
204+
onTestCaseStart: async (test: TestCaseInfo) => {
205+
Promise.all(
206+
reporters.map((reporter) => reporter.onTestCaseStart?.(test)),
207+
);
208+
},
203209
onTestCaseResult: async (result: TestResult) => {
204210
context.stateManager.onTestCaseResult(result);
205211
await Promise.all(
@@ -291,6 +297,7 @@ export const createPool = async ({
291297
.catch((err: unknown) => {
292298
(err as any).fullStack = true;
293299
return {
300+
testId: '0',
294301
project: projectName,
295302
testPath: entryInfo.testPath,
296303
status: 'fail',

packages/core/src/reporter/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
NormalizedConfig,
88
Reporter,
99
SnapshotSummary,
10+
TestCaseInfo,
1011
TestFileInfo,
1112
TestFileResult,
1213
TestResult,
@@ -69,6 +70,10 @@ export class DefaultReporter implements Reporter {
6970
this.statusRenderer?.onTestCaseResult(result);
7071
}
7172

73+
onTestCaseStart(test: TestCaseInfo): void {
74+
this.statusRenderer?.onTestCaseStart(test);
75+
}
76+
7277
onUserConsoleLog(log: UserConsoleLog): void {
7378
const shouldLog = this.config.onConsoleLog?.(log.content) ?? true;
7479

packages/core/src/reporter/statusRenderer.ts

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { relative } from 'pathe';
2-
import type { TestFileResult, TestResult } from '../types';
3-
import { color, prettyTestPath, prettyTime } from '../utils';
2+
import type { TestCaseInfo, TestFileResult, TestResult } from '../types';
3+
import {
4+
color,
5+
getTaskNameWithPrefix,
6+
POINTER,
7+
prettyTestPath,
8+
prettyTime,
9+
} from '../utils';
410
import {
511
DurationLabel,
612
getSummaryStatusString,
@@ -12,7 +18,10 @@ import { WindowRenderer } from './windowedRenderer';
1218
export class StatusRenderer {
1319
private rootPath: string;
1420
private renderer: WindowRenderer;
15-
private runningModules = new Map<string, TestResult[]>();
21+
private runningModules = new Map<
22+
string,
23+
{ runningTests: TestCaseInfo[]; results: TestResult[] }
24+
>();
1625
private testModules: TestFileResult[] = [];
1726
private startTime: number | undefined = undefined;
1827

@@ -32,13 +41,32 @@ export class StatusRenderer {
3241

3342
getContent(): string[] {
3443
this.startTime ??= Date.now();
44+
const now = Date.now();
3545
const summary = [];
36-
for (const module of this.runningModules.keys()) {
46+
47+
// only display running tests if they have been running for more than 2 seconds
48+
const shouldDisplayRunningTests = (runningTests: TestCaseInfo[]) => {
49+
return (
50+
runningTests[0]?.startTime && now - runningTests[0].startTime > 2000
51+
);
52+
};
53+
54+
for (const [module, { runningTests }] of this.runningModules.entries()) {
3755
const relativePath = relative(this.rootPath, module);
3856
summary.push(
3957
`${color.bgYellow(color.bold(' RUNS '))} ${prettyTestPath(relativePath)}`,
4058
);
59+
if (runningTests.length && shouldDisplayRunningTests(runningTests)) {
60+
let caseLog = ` ${color.gray(POINTER)} ${getTaskNameWithPrefix(runningTests[0]!)} ${color.magenta(prettyTime(now - runningTests[0]!.startTime!))}`;
61+
62+
if (runningTests.length > 1) {
63+
caseLog += color.gray(` and ${runningTests.length - 1} more cases`);
64+
}
65+
66+
summary.push(caseLog);
67+
}
4168
}
69+
4270
summary.push('');
4371

4472
if (this.testModules.length === 0) {
@@ -50,7 +78,7 @@ export class StatusRenderer {
5078
}
5179

5280
const testResults: TestResult[] = Array.from(this.runningModules.values())
53-
.flat()
81+
.flatMap(({ results }) => results)
5482
.concat(this.testModules.flatMap((mod) => mod.results));
5583

5684
if (testResults.length) {
@@ -67,15 +95,48 @@ export class StatusRenderer {
6795
}
6896

6997
onTestFileStart(testPath: string): void {
70-
this.runningModules.set(testPath, []);
98+
this.runningModules.set(testPath, { runningTests: [], results: [] });
7199
this.renderer?.schedule();
72100
}
73101

74102
onTestCaseResult(result: TestResult): void {
75-
this.runningModules.set(result.testPath, [
76-
...(this.runningModules.get(result.testPath) || []),
77-
result,
78-
]);
103+
const currentModule = this.runningModules.get(result.testPath);
104+
if (!currentModule) {
105+
this.runningModules.set(result.testPath, {
106+
runningTests: [],
107+
results: [result],
108+
});
109+
} else {
110+
// Find and remove the test from runningTests by matching testId
111+
const filteredRunningTests = currentModule.runningTests.filter(
112+
(t) => t.testId !== result.testId,
113+
);
114+
this.runningModules.set(result.testPath, {
115+
runningTests: filteredRunningTests,
116+
results: [...currentModule.results, result],
117+
});
118+
}
119+
120+
this.renderer?.schedule();
121+
}
122+
123+
onTestCaseStart(test: TestCaseInfo): void {
124+
const currentModule = this.runningModules.get(test.testPath);
125+
if (!currentModule) {
126+
this.runningModules.set(test.testPath, {
127+
runningTests: [test],
128+
results: [],
129+
});
130+
} else {
131+
// Remove from runningTests if it exists (for restart scenarios)
132+
const filteredRunningTests = currentModule.runningTests.filter(
133+
(t) => t.testId !== test.testId,
134+
);
135+
this.runningModules.set(test.testPath, {
136+
runningTests: [...filteredRunningTests, test],
137+
results: currentModule.results,
138+
});
139+
}
79140
}
80141

81142
onTestFileResult(test: TestFileResult): void {

packages/core/src/reporter/summary.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
formatTestPath,
2727
getTaskNameWithPrefix,
2828
logger,
29+
POINTER,
2930
prettyTestPath,
3031
prettyTime,
3132
TEST_DELIMITER,
@@ -89,7 +90,6 @@ export const printSnapshotSummaryLog = (
8990
);
9091
}
9192
}
92-
const POINTER = '➜';
9393

9494
if (snapshots.filesRemovedList?.length) {
9595
const [head, ...tail] = snapshots.filesRemovedList;

packages/core/src/runtime/api/snapshot.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ function getTestNames(test: TestCase) {
141141
return {
142142
filepath: test.testPath,
143143
name: getTaskNameWithPrefix(test),
144-
// testId: test.id,
144+
testId: test.testId,
145145
};
146146
}
147147

0 commit comments

Comments
 (0)