Skip to content

Commit b77a723

Browse files
committed
Improvements to driver cleanup
- Better synchronization: The driver now waits for Vue's reactivity system at critical points - Race condition prevention: Proper cleanup prevents multiple apps from interfering with each other - Robust error handling: Tests clean up properly even when they fail - Vue-aware timing: The driver now respects Vue's async nature and component lifecycle
1 parent c1a217a commit b77a723

File tree

2 files changed

+58
-12
lines changed

2 files changed

+58
-12
lines changed

src/Frontend/test/driver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { DefaultBodyType, StrictRequest, type PathParams } from "msw";
22

33
type GoTo = (path: string) => Promise<void>;
4-
type DisposeApp = () => void;
4+
type DisposeApp = () => Promise<void>;
55

66
export type Method = "get" | "post" | "patch" | "put" | "delete" | "options";
77

src/Frontend/test/drivers/vitest/driver.ts

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,74 @@ import { mount } from "@/mount";
44
import makeRouter from "../../../src/router";
55
import { mockEndpoint, mockEndpointDynamic } from "../../utils";
66
import { mockServer } from "../../mock-server";
7-
import { App } from "vue";
7+
import { App, nextTick } from "vue";
8+
9+
// Utility function to wait for Vue's reactivity system to settle
10+
async function waitForVue() {
11+
// Wait for Vue's nextTick to ensure reactive updates are complete
12+
await nextTick();
13+
// Additional wait for any pending async operations
14+
await new Promise((resolve) => setTimeout(resolve, 0));
15+
}
816

917
function makeDriver() {
10-
let app: App<Element>;
18+
let app: App<Element> | null = null;
1119
const driver = <Driver>{
1220
async goTo(path) {
1321
const router = makeRouter();
22+
23+
// First, clean up any existing app to prevent race conditions
24+
if (app) {
25+
try {
26+
app.unmount();
27+
await waitForVue();
28+
} catch {
29+
// Ignore unmount errors
30+
}
31+
}
32+
1433
try {
1534
await router.push(path);
1635
} catch (error) {
1736
// Ignore redirection error.
1837
if (error instanceof Error && error.message.includes("Redirected when going from")) {
19-
return;
38+
// Still need to continue with mounting even after redirect
39+
} else {
40+
throw error;
2041
}
21-
22-
throw error;
2342
}
2443

44+
// Clean and prepare DOM
2545
document.body.innerHTML = '<div id="app"></div><div id="modalDisplay"></div>';
46+
47+
// Mount the Vue app
2648
app = mount({ router });
49+
50+
// Wait for Vue to fully initialize and mount
51+
await waitForVue();
52+
53+
// Wait for router to be ready and any initial navigation to complete
54+
await router.isReady();
55+
56+
// Additional wait for component mounting and initial renders
57+
await waitForVue();
2758
},
2859
mockEndpoint,
2960
mockEndpointDynamic: mockEndpointDynamic,
3061
setUp(factory) {
3162
return factory({ driver: this });
3263
},
33-
disposeApp() {
34-
app.unmount();
64+
async disposeApp() {
65+
if (app) {
66+
try {
67+
app.unmount();
68+
await waitForVue();
69+
} catch (error) {
70+
// Ignore unmount errors
71+
console.warn("Error during app disposal:", error);
72+
}
73+
app = null;
74+
}
3575
},
3676
};
3777
return driver;
@@ -44,11 +84,17 @@ const test = itVitest.extend<{ driver: Driver }>({
4484
mockServer.resetHandlers();
4585

4686
const driver = makeDriver();
47-
//run the test
48-
await use(driver);
4987

50-
//unmount the app after the test runs
51-
driver.disposeApp();
88+
try {
89+
//run the test
90+
await use(driver);
91+
} finally {
92+
//unmount the app after the test runs (even if test fails)
93+
await driver.disposeApp();
94+
95+
// Additional cleanup: wait for any pending Vue operations
96+
await waitForVue();
97+
}
5298
},
5399
});
54100

0 commit comments

Comments
 (0)