Skip to content

Commit 646c713

Browse files
committed
fix: add timeout handling
1 parent 1944c98 commit 646c713

File tree

3 files changed

+90
-34
lines changed

3 files changed

+90
-34
lines changed

src/fixtures/process.ts

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,38 +32,52 @@ export class ProcessWrap {
3232
};
3333
});
3434

35-
this._isReady = promise.then((webcontainerProcess) => {
36-
this._webcontainerProcess = webcontainerProcess;
37-
this._writer = webcontainerProcess.input.getWriter();
35+
this._isReady = new Promise((resolve, reject) => {
36+
const timeout = setTimeout(() => {
37+
console.error(
38+
"WebContainer process startup timed out in 30s, TODO remove this log message",
39+
);
3840

39-
webcontainerProcess.exit.then(setDone);
41+
reject(new Error("WebContainer process startup timed out in 30s"));
42+
}, 30_000);
4043

41-
const reader = this._webcontainerProcess.output.getReader();
44+
promise.then((webcontainerProcess) => {
45+
clearTimeout(timeout);
4246

43-
const read = async () => {
44-
while (true) {
45-
const { done, value } = await reader.read();
47+
this._webcontainerProcess = webcontainerProcess;
48+
this._writer = webcontainerProcess.input.getWriter();
4649

47-
if (isExitted && !done) {
48-
console.warn(
49-
`[webcontainer-test]: Closed process keeps writing to output. Closing reader forcefully. \n` +
50-
` Received: "${value}".`,
51-
);
52-
await reader.cancel();
53-
break;
54-
}
50+
webcontainerProcess.exit.then(setDone);
51+
52+
const reader = this._webcontainerProcess.output.getReader();
53+
54+
const read = async () => {
55+
while (true) {
56+
const { done, value } = await reader.read();
5557

56-
// webcontainer process never reaches here, but for future-proofing let's exit
57-
if (done) {
58-
break;
58+
if (isExitted && !done) {
59+
console.warn(
60+
`[webcontainer-test]: Closed process keeps writing to output. Closing reader forcefully. \n` +
61+
` Received: "${value}".`,
62+
);
63+
await reader.cancel();
64+
break;
65+
}
66+
67+
// webcontainer process never reaches here, but for future-proofing let's exit
68+
if (done) {
69+
break;
70+
}
71+
72+
this._output += value;
73+
this._listeners.forEach((fn) => fn(value));
5974
}
75+
};
6076

61-
this._output += value;
62-
this._listeners.forEach((fn) => fn(value));
63-
}
64-
};
77+
void read();
6578

66-
void read();
79+
resolve();
80+
});
6781
});
6882
}
6983

@@ -147,9 +161,23 @@ export class ProcessWrap {
147161
exit = async () => {
148162
await this._isReady;
149163

150-
this._webcontainerProcess.kill();
151-
this._listeners.splice(0);
164+
// eslint-disable-next-line no-async-promise-executor -- async in promise is completely fine
165+
return new Promise<void>(async (resolve, reject) => {
166+
const timeout = setTimeout(() => {
167+
console.error(
168+
"WebContainer process teardown timed out in 30s, TODO remove this log message",
169+
);
170+
171+
reject(new Error("WebContainer process teardown timed out in 30s"));
172+
}, 30_000);
152173

153-
return this.isDone;
174+
this._webcontainerProcess.kill();
175+
this._listeners.splice(0);
176+
177+
await this.isDone;
178+
179+
clearTimeout(timeout);
180+
resolve();
181+
});
154182
};
155183
}

src/fixtures/webcontainer.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,20 @@ export class WebContainer extends FileSystem {
1616
constructor() {
1717
super();
1818

19-
this._isReady = WebContainerApi.boot({}).then((instance) => {
20-
this._instancePromise = instance;
19+
this._isReady = new Promise((resolve, reject) => {
20+
const timeout = setTimeout(() => {
21+
console.error(
22+
"WebContainer boot timed out in 30s, TODO remove this log message",
23+
);
24+
25+
reject(new Error("WebContainer boot timed out in 30s"));
26+
}, 30_000);
27+
28+
WebContainerApi.boot({}).then((instance) => {
29+
clearTimeout(timeout);
30+
this._instancePromise = instance;
31+
resolve();
32+
});
2133
});
2234
}
2335

@@ -46,13 +58,27 @@ export class WebContainer extends FileSystem {
4658

4759
/** @internal */
4860
async teardown() {
49-
await Promise.all(this._onExit.map((fn) => fn()));
61+
// eslint-disable-next-line no-async-promise-executor -- async in promise is completely fine
62+
return new Promise<void>(async (resolve, reject) => {
63+
const timeout = setTimeout(() => {
64+
console.error(
65+
"WebContainer teardown timed out in 30s, TODO remove this log message",
66+
);
5067

51-
// @ts-ignore -- internal
52-
await this._instance._instance.teardown();
68+
reject(new Error("WebContainer teardown timed out in 30s"));
69+
}, 30_000);
5370

54-
this._instance.teardown();
55-
this._instancePromise = undefined;
71+
await Promise.all(this._onExit.map((fn) => fn()));
72+
73+
// @ts-ignore -- internal
74+
await this._instance._instance.teardown();
75+
76+
this._instance.teardown();
77+
this._instancePromise = undefined;
78+
79+
clearTimeout(timeout);
80+
resolve();
81+
});
5682
}
5783

5884
/**

vitest.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export default defineConfig({
55
plugins: [vitestWebcontainers()],
66

77
test: {
8+
reporters: "verbose",
9+
810
browser: {
911
enabled: true,
1012
provider: "playwright",

0 commit comments

Comments
 (0)