Skip to content

Commit cfbc6d9

Browse files
authored
fix(core): handle stream write callbacks in status renderer (#1312)
1 parent eaf1d2d commit cfbc6d9

2 files changed

Lines changed: 45 additions & 2 deletions

File tree

packages/core/src/reporter/windowedRenderer.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,15 +206,19 @@ export class WindowRenderer {
206206
const original = stream.write.bind(stream);
207207

208208
// @ts-expect-error -- not sure how 2 overloads should be typed
209-
stream.write = (chunk, _, callback) => {
209+
stream.write = (chunk, encoding, callback) => {
210+
const writeCallback =
211+
typeof encoding === 'function' ? encoding : callback;
212+
210213
if (chunk) {
211214
if (this.finished || !this.started || this.hiddenForOutputCount > 0) {
212215
this.write(chunk.toString(), type);
213216
} else {
214217
this.buffer.push({ type, message: chunk.toString() });
215218
}
216219
}
217-
callback?.();
220+
writeCallback?.();
221+
return true;
218222
};
219223

220224
return function restore() {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Writable } from 'node:stream';
2+
import { describe, expect, it, onTestFinished } from '@rstest/core';
3+
import { WindowRenderer } from '../../src/reporter/windowedRenderer';
4+
import { flushOutputStreams } from '../../src/utils/logger';
5+
6+
const createWritable = () =>
7+
new Writable({
8+
write(_chunk, _encoding, callback) {
9+
callback();
10+
},
11+
});
12+
13+
describe('WindowRenderer', () => {
14+
it('does not block stream flushing when write callback is the second argument', async () => {
15+
const renderer = new WindowRenderer({
16+
getWindow: () => [],
17+
logger: {
18+
outputStream: createWritable(),
19+
errorStream: createWritable(),
20+
getColumns: () => 80,
21+
},
22+
});
23+
24+
onTestFinished(() => {
25+
renderer.stop();
26+
});
27+
28+
renderer.start();
29+
30+
const flushed = await Promise.race([
31+
flushOutputStreams().then(() => true),
32+
new Promise<boolean>((resolve) => {
33+
setTimeout(() => resolve(false), 100);
34+
}),
35+
]);
36+
37+
expect(flushed).toBe(true);
38+
});
39+
});

0 commit comments

Comments
 (0)