
























@@ -96,6 +96,14 @@ function effectiveSpawnCommand(call: unknown[] | undefined): unknown {
9696return command;
9797}
989899+function mockExpiredLaunchPollingClock(): void {
100+let now = 1_000_000;
101+vi.spyOn(Date, "now").mockImplementation(() => {
102+now += 1_000;
103+return now;
104+});
105+}
106+99107async function withMockChromeCdpServer(params: {
100108wsPath: string;
101109onConnection?: (wss: WebSocketServer) => void;
@@ -507,15 +515,16 @@ describe("chrome.ts internal", () => {
507515let spawnCalls = 0;
508516const firstProc = makeFakeProc();
509517const secondProc = makeFakeProc();
518+mockExpiredLaunchPollingClock();
510519spawnMock.mockImplementation(() => {
511520spawnCalls += 1;
512521if (spawnCalls === 1) {
513-setTimeout(() => {
522+void Promise.resolve().then(() => {
514523firstProc.stderr.emit(
515524"data",
516525Buffer.from("The profile appears to be in use by another Chromium process"),
517526);
518-}, 0);
527+});
519528return firstProc;
520529}
521530cdpReachable = true;
@@ -566,7 +575,10 @@ describe("chrome.ts internal", () => {
566575const fakeProc = makeFakeProc();
567576spawnMock.mockReturnValue(fakeProc);
568577// Leak some stderr into the buffer so the hint renders.
569-setTimeout(() => fakeProc.stderr.emit("data", Buffer.from("crash dump\n")), 10);
578+void Promise.resolve().then(() =>
579+fakeProc.stderr.emit("data", Buffer.from("crash dump\n")),
580+);
581+mockExpiredLaunchPollingClock();
570582571583// fetch always fails → isChromeReachable returns false every poll.
572584vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("ECONNREFUSED")));
@@ -587,38 +599,32 @@ describe("chrome.ts internal", () => {
587599});
588600589601it("uses the configured local launch timeout while waiting for CDP discovery", async () => {
590-vi.useFakeTimers();
591-try {
592-const executablePath = path.join(tmpDir, "chrome");
593-await fsp.writeFile(executablePath, "");
594-const existsSync = fs.existsSync.bind(fs);
595-vi.spyOn(fs, "existsSync").mockImplementation((p) => {
596-const s = String(p);
597-if (s.endsWith("Local State") || s.endsWith("Preferences")) {
598-return true;
599-}
600-return existsSync(p);
601-});
602-const fakeProc = makeFakeProc();
603-spawnMock.mockReturnValue(fakeProc);
604-vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("ECONNREFUSED")));
605-606-const resolved = {
607- ...makeResolved(),
608- executablePath,
609-localLaunchTimeoutMs: 1,
610-};
611-const profile = makeProfile(55556);
612-const rejection = expect(launchOpenClawChrome(resolved, profile)).rejects.toThrow(
613-/Failed to start Chrome CDP/,
614-);
602+const executablePath = path.join(tmpDir, "chrome");
603+await fsp.writeFile(executablePath, "");
604+const existsSync = fs.existsSync.bind(fs);
605+vi.spyOn(fs, "existsSync").mockImplementation((p) => {
606+const s = String(p);
607+if (s.endsWith("Local State") || s.endsWith("Preferences")) {
608+return true;
609+}
610+return existsSync(p);
611+});
612+const fakeProc = makeFakeProc();
613+spawnMock.mockReturnValue(fakeProc);
614+mockExpiredLaunchPollingClock();
615+vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("ECONNREFUSED")));
615616616-await vi.advanceTimersByTimeAsync(10);
617-await rejection;
618-expect(fakeProc.kill).toHaveBeenCalledWith("SIGKILL");
619-} finally {
620-vi.useRealTimers();
621-}
617+const resolved = {
618+ ...makeResolved(),
619+ executablePath,
620+localLaunchTimeoutMs: 1,
621+};
622+const profile = makeProfile(55556);
623+624+await expect(launchOpenClawChrome(resolved, profile)).rejects.toThrow(
625+/Failed to start Chrome CDP/,
626+);
627+expect(fakeProc.kill).toHaveBeenCalledWith("SIGKILL");
622628});
623629});
624630@@ -997,9 +1003,12 @@ describe("chrome.ts internal", () => {
9971003const fakeProc = makeFakeProc();
9981004spawnMock.mockImplementation(() => {
9991005// Synthesize stderr data shortly after spawn.
1000-setTimeout(() => fakeProc.stderr.emit("data", Buffer.from("chrome crash log\n")), 5);
1006+void Promise.resolve().then(() =>
1007+fakeProc.stderr.emit("data", Buffer.from("chrome crash log\n")),
1008+);
10011009return fakeProc;
10021010});
1011+mockExpiredLaunchPollingClock();
10031012vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("ECONNREFUSED")));
10041013const profile = {
10051014name: "openclaw-stderr",
@@ -1036,6 +1045,7 @@ describe("chrome.ts internal", () => {
10361045return false;
10371046});
10381047spawnMock.mockImplementation(() => makeFakeProc());
1048+mockExpiredLaunchPollingClock();
10391049vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("ECONNREFUSED")));
10401050const profile = {
10411051name: "openclaw-mac",
@@ -1064,13 +1074,9 @@ describe("chrome.ts internal", () => {
1064107410651075it("breaks out of the bootstrap prefs-wait loop as soon as both files exist", async () => {
10661076// Covers the `if (exists(localStatePath) && exists(preferencesPath)) break;` branch.
1067-// Use a wallclock flag that the mock checks each call so the loop
1068-// iterates (awaiting its 100ms setTimeout) once with prefs-absent,
1069-// then the flag flips and the next iteration hits the break.
1070-let prefsVisible = false;
1071-setTimeout(() => {
1072-prefsVisible = true;
1073-}, 50);
1077+// The first prefs probe makes bootstrap necessary; subsequent probes
1078+// make both prefs files visible so the polling loop breaks immediately.
1079+let prefsProbeCount = 0;
10741080vi.spyOn(fs, "existsSync").mockImplementation((p) => {
10751081const s = String(p);
10761082if (
@@ -1081,7 +1087,8 @@ describe("chrome.ts internal", () => {
10811087return true;
10821088}
10831089if (s.endsWith("Local State") || s.endsWith("Preferences")) {
1084-return prefsVisible;
1090+prefsProbeCount += 1;
1091+return prefsProbeCount > 1;
10851092}
10861093return false;
10871094});
@@ -1136,17 +1143,15 @@ describe("chrome.ts internal", () => {
11361143});
11371144const bootstrapProc = makeFakeProc();
11381145const runtimeProc = makeFakeProc();
1146+bootstrapProc.kill = vi.fn((_sig?: string) => {
1147+bootstrapProc.killed = true;
1148+bootstrapProc.exitCode = 0;
1149+return true;
1150+});
11391151let callCount = 0;
11401152spawnMock.mockImplementation(() => {
11411153callCount += 1;
1142-if (callCount === 1) {
1143-// Set exitCode shortly after spawn so the exit-wait loop breaks.
1144-setTimeout(() => {
1145-bootstrapProc.exitCode = 0;
1146-}, 25);
1147-return bootstrapProc;
1148-}
1149-return runtimeProc;
1154+return callCount === 1 ? bootstrapProc : runtimeProc;
11501155});
11511156await withMockChromeCdpServer({
11521157wsPath: "/devtools/browser/EXIT_BREAK",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。