Skip to content

Commit d782944

Browse files
fix: handle concurrent runTest calls
1 parent cb5651e commit d782944

File tree

8 files changed

+99
-39
lines changed

8 files changed

+99
-39
lines changed

packages/dom-evaluator/src/dom-test-evaluator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ export class DOMTestEvaluator implements TestEvaluator {
173173
if (e.data.type === "test") {
174174
const result = await this.#runTest!(e.data.value);
175175
const msg = { type: "result" as const, value: result };
176-
postCloneableMessage((msg) => self.parent.postMessage(msg, "*"), msg);
176+
postCloneableMessage((msg) => e.ports[0].postMessage(msg), msg);
177177
} else if (e.data.type === "init") {
178178
await this.init(e.data.value);
179179
self.parent.postMessage(READY_MESSAGE, "*");

packages/javascript-evaluator/src/javascript-test-evaluator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ ${test};`);
106106
if (e.data.type === "test") {
107107
const result = await this.#runTest!(e.data.value);
108108
const msg = { type: "result" as const, value: result };
109-
postCloneableMessage(postMessage, msg);
109+
postCloneableMessage((msg) => e.ports[0].postMessage(msg), msg);
110110
} else if (e.data.type === "init") {
111111
this.init(e.data.value);
112112
postMessage(READY_MESSAGE);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { post } from "./awaitable-post";
2+
3+
// Echos the post data back
4+
class MockWorker {
5+
// Simulate posting a message to the worker
6+
postMessage<T>(msg: T, opts: WindowPostMessageOptions) {
7+
opts.transfer?.forEach((transferable) => {
8+
if (transferable instanceof MessagePort) {
9+
transferable.postMessage(msg);
10+
}
11+
});
12+
}
13+
}
14+
15+
describe("post", () => {
16+
it("should resolve with the post data", async () => {
17+
const postData = { id: 1, title: "Test Post" };
18+
const mockWorker = new MockWorker();
19+
const result = await post({ messenger: mockWorker, message: postData });
20+
expect(result).toEqual(postData);
21+
});
22+
23+
it("should handle multiple messages", async () => {
24+
const mockWorker = new MockWorker();
25+
const postData1 = { id: 1, title: "First Post" };
26+
const postData2 = { id: 2, title: "Second Post" };
27+
const result1 = post({ messenger: mockWorker, message: postData1 });
28+
const result2 = post({ messenger: mockWorker, message: postData2 });
29+
const [res1, res2] = await Promise.all([result1, result2]);
30+
expect(res1).toEqual(postData1);
31+
expect(res2).toEqual(postData2);
32+
});
33+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export type Messenger<Message> = {
2+
postMessage: (message: Message, options: WindowPostMessageOptions) => void;
3+
};
4+
5+
export function post<Returned>({
6+
messenger,
7+
message,
8+
}: {
9+
messenger: Messenger<unknown>;
10+
message: unknown;
11+
}): Promise<Returned> {
12+
return new Promise((resolve) => {
13+
const channel = new MessageChannel();
14+
channel.port1.onmessage = (event) => {
15+
channel.port1.close();
16+
resolve(event.data as Returned);
17+
};
18+
19+
messenger.postMessage(message, {
20+
targetOrigin: "*",
21+
transfer: [channel.port2],
22+
});
23+
});
24+
}

packages/main/src/index.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,31 @@ describe("Test Runner", () => {
222222
text: "JSHandle@error",
223223
});
224224
});
225+
226+
it("should handle concurrent tests", async () => {
227+
const results = await page.evaluate(async (type) => {
228+
const runner = await window.FCCTestRunner.createTestRunner({
229+
type,
230+
});
231+
232+
const testOne = runner.runTest("assert.equal(1, 2)");
233+
const testTwo = runner.runTest("assert.equal(2, 3)");
234+
return Promise.all([testOne, testTwo]);
235+
}, type);
236+
237+
expect(results[0]).toMatchObject({
238+
err: {
239+
actual: 1,
240+
expected: 2,
241+
},
242+
});
243+
expect(results[1]).toMatchObject({
244+
err: {
245+
actual: 2,
246+
expected: 3,
247+
},
248+
});
249+
});
225250
});
226251

227252
describe("DOM evaluator", () => {

packages/main/src/test-runner.ts

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import type {
2-
ReadyEvent,
3-
ResultEvent,
4-
} from "../../shared/src/interfaces/test-runner";
1+
import type { ReadyEvent } from "../../shared/src/interfaces/test-runner";
52
import type {
63
InitEvent,
74
TestEvent,
@@ -15,6 +12,7 @@ import {
1512
TEST_EVALUATOR_SCRIPT_ID,
1613
TEST_EVALUATOR_HOOKS_ID,
1714
} from "../../shared/src/ids";
15+
import { post } from "./awaitable-post";
1816

1917
interface Runner {
2018
init(opts?: InitOptions): Promise<void>;
@@ -143,22 +141,13 @@ ${opts.source}`;
143141
}
144142

145143
runTest(test: string) {
146-
const result = new Promise<Pass | Fail>((resolve) => {
147-
const listener = (event: ResultEvent) => {
148-
if (
149-
event.origin === "null" &&
150-
event.source === this.#testEvaluator.contentWindow
151-
) {
152-
window.removeEventListener("message", listener);
153-
resolve(event.data.value);
154-
}
155-
};
156-
157-
window.addEventListener("message", listener);
158-
});
159-
160-
const msg: TestEvent["data"] = { type: "test", value: test };
161-
this.#testEvaluator.contentWindow?.postMessage(msg, "*");
144+
const result = post<{ value: Pass | Fail }>({
145+
messenger: this.#testEvaluator.contentWindow!,
146+
message: {
147+
type: "test",
148+
value: test,
149+
} as TestEvent["data"],
150+
}).then(({ value }) => value);
162151

163152
return result;
164153
}
@@ -222,21 +211,16 @@ export class WorkerTestRunner implements Runner {
222211
});
223212
}, timeout);
224213
});
225-
const result = new Promise<Pass | Fail>((resolve) => {
226-
const listener = (event: ResultEvent) => {
227-
this.#testEvaluator.removeEventListener("message", listener);
228-
// TODO: differentiate between messages
229-
resolve(event.data.value);
230-
};
231-
232-
this.#testEvaluator.addEventListener("message", listener);
233-
});
234214

235215
const msg: TestEvent["data"] = {
236216
type: "test",
237217
value: test,
238218
};
239-
this.#testEvaluator.postMessage(msg);
219+
220+
const result = post<{ value: Pass | Fail }>({
221+
messenger: this.#testEvaluator,
222+
message: msg,
223+
}).then(({ value }) => value);
240224

241225
try {
242226
return await Promise.race([result, terminate]);

packages/python-evaluator/src/python-test-evaluator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ class PythonTestEvaluator implements TestEvaluator {
198198
if (e.data.type === "test") {
199199
const result = await this.#runTest!(e.data.value);
200200
const msg = { type: "result" as const, value: result };
201-
postCloneableMessage(postMessage, msg);
201+
postCloneableMessage((msg) => e.ports[0].postMessage(msg), msg);
202202
} else if (e.data.type === "init") {
203203
await this.init(e.data.value);
204204
postMessage(READY_MESSAGE);
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1 @@
1-
import { Fail, Pass } from "./test-evaluator";
2-
31
export type ReadyEvent = MessageEvent<{ type: "ready" }>;
4-
export type ResultEvent = MessageEvent<{
5-
type: "result";
6-
value: Pass | Fail;
7-
}>;

0 commit comments

Comments
 (0)