@@ -13,97 +13,100 @@ let apiError: string | null = "Authentication required";
1313
1414const passthroughRef = < T , > ( value : T ) : T => value ;
1515
16- void mock . module ( "react-dnd" , ( ) => ( {
17- DndProvider : ( props : { children : React . ReactNode } ) => props . children ,
18- useDrag : ( ) => [ { isDragging : false } , passthroughRef , ( ) => undefined ] as const ,
19- useDrop : ( ) => [ { isOver : false } , passthroughRef ] as const ,
20- useDragLayer : ( ) => ( { isDragging : false , item : null , currentOffset : null } ) ,
21- } ) ) ;
22-
23- void mock . module ( "react-dnd-html5-backend" , ( ) => ( {
24- HTML5Backend : { } ,
25- getEmptyImage : ( ) => null ,
26- } ) ) ;
27-
28- // AppLoader imports App, which pulls in Lottie-based components. In happy-dom,
29- // lottie-web's canvas bootstrap can throw during module evaluation.
30- void mock . module ( "lottie-react" , ( ) => ( {
31- __esModule : true ,
32- default : ( ) => < div data-testid = "LottieMock" /> ,
33- } ) ) ;
34-
35- void mock . module ( "@/browser/contexts/API" , ( ) => ( {
36- APIProvider : ( props : { children : React . ReactNode } ) => props . children ,
37- useAPI : ( ) => {
38- if ( apiStatus === "auth_required" ) {
39- return {
40- api : null ,
41- status : "auth_required" as const ,
42- error : apiError ,
43- authenticate : ( ) => undefined ,
44- retry : ( ) => undefined ,
45- } ;
46- }
16+ function installAppLoaderModuleMocks ( ) {
17+ void mock . module ( "react-dnd" , ( ) => ( {
18+ DndProvider : ( props : { children : React . ReactNode } ) => props . children ,
19+ useDrag : ( ) => [ { isDragging : false } , passthroughRef , ( ) => undefined ] as const ,
20+ useDrop : ( ) => [ { isOver : false } , passthroughRef ] as const ,
21+ useDragLayer : ( ) => ( { isDragging : false , item : null , currentOffset : null } ) ,
22+ } ) ) ;
23+
24+ void mock . module ( "react-dnd-html5-backend" , ( ) => ( {
25+ HTML5Backend : { } ,
26+ getEmptyImage : ( ) => null ,
27+ } ) ) ;
28+
29+ // AppLoader imports App, which pulls in Lottie-based components. In happy-dom,
30+ // lottie-web's canvas bootstrap can throw during module evaluation.
31+ void mock . module ( "lottie-react" , ( ) => ( {
32+ __esModule : true ,
33+ default : ( ) => < div data-testid = "LottieMock" /> ,
34+ } ) ) ;
35+
36+ void mock . module ( "@/browser/contexts/API" , ( ) => ( {
37+ APIProvider : ( props : { children : React . ReactNode } ) => props . children ,
38+ useAPI : ( ) => {
39+ if ( apiStatus === "auth_required" ) {
40+ return {
41+ api : null ,
42+ status : "auth_required" as const ,
43+ error : apiError ,
44+ authenticate : ( ) => undefined ,
45+ retry : ( ) => undefined ,
46+ } ;
47+ }
48+
49+ if ( apiStatus === "error" ) {
50+ return {
51+ api : null ,
52+ status : "error" as const ,
53+ error : apiError ?? "Connection error" ,
54+ authenticate : ( ) => undefined ,
55+ retry : ( ) => undefined ,
56+ } ;
57+ }
4758
48- if ( apiStatus === "error" ) {
4959 return {
5060 api : null ,
51- status : "error " as const ,
52- error : apiError ?? "Connection error" ,
61+ status : "connecting " as const ,
62+ error : null ,
5363 authenticate : ( ) => undefined ,
5464 retry : ( ) => undefined ,
5565 } ;
56- }
57-
58- return {
59- api : null ,
60- status : "connecting" as const ,
61- error : null ,
62- authenticate : ( ) => undefined ,
63- retry : ( ) => undefined ,
64- } ;
65- } ,
66- } ) ) ;
67-
68- void mock . module ( "../../App" , ( ) => ( {
69- __esModule : true ,
70- // App imports the full sidebar tree (including react-dnd) even though these auth-path tests only
71- // need AppLoader's pre-App branching. Keep the unit focused so cross-file DOM teardown cannot
72- // trip react-dnd's MutationObserver bootstrap between test files.
73- default : ( ) => < div data-testid = "AppMock" /> ,
74- } ) ) ;
75-
76- void mock . module ( "@/browser/components/LoadingScreen/LoadingScreen" , ( ) => ( {
77- LoadingScreen : ( ) => {
78- const { theme } = useTheme ( ) ;
79- return < div data-testid = "LoadingScreenMock" > { theme } </ div > ;
80- } ,
81- } ) ) ;
82-
83- void mock . module ( "@/browser/components/StartupConnectionError/StartupConnectionError" , ( ) => ( {
84- StartupConnectionError : ( props : { error : string } ) => (
85- < div data-testid = "StartupConnectionErrorMock" > { props . error } </ div >
86- ) ,
87- } ) ) ;
88-
89- void mock . module ( "@/browser/components/AuthTokenModal/AuthTokenModal" , ( ) => ( {
90- // Note: Module mocks leak between bun test files.
91- // Export all commonly-used symbols to avoid cross-test import errors.
92- AuthTokenModal : ( props : { error ?: string | null } ) => (
93- < div data-testid = "AuthTokenModalMock" > { props . error ?? "no-error" } </ div >
94- ) ,
95- getStoredAuthToken : ( ) => null ,
96- // eslint-disable-next-line @typescript-eslint/no-empty-function
97- setStoredAuthToken : ( ) => { } ,
98- // eslint-disable-next-line @typescript-eslint/no-empty-function
99- clearStoredAuthToken : ( ) => { } ,
100- } ) ) ;
66+ } ,
67+ } ) ) ;
68+
69+ void mock . module ( "../../App" , ( ) => ( {
70+ __esModule : true ,
71+ // App imports the full sidebar tree (including react-dnd) even though these auth-path tests
72+ // only need AppLoader's pre-App branching. Keep the unit focused so cross-file DOM teardown
73+ // cannot trip react-dnd's MutationObserver bootstrap between test files.
74+ default : ( ) => < div data-testid = "AppMock" /> ,
75+ } ) ) ;
76+
77+ void mock . module ( "@/browser/components/LoadingScreen/LoadingScreen" , ( ) => ( {
78+ LoadingScreen : ( ) => {
79+ const { theme } = useTheme ( ) ;
80+ return < div data-testid = "LoadingScreenMock" > { theme } </ div > ;
81+ } ,
82+ } ) ) ;
83+
84+ void mock . module ( "@/browser/components/StartupConnectionError/StartupConnectionError" , ( ) => ( {
85+ StartupConnectionError : ( props : { error : string } ) => (
86+ < div data-testid = "StartupConnectionErrorMock" > { props . error } </ div >
87+ ) ,
88+ } ) ) ;
89+
90+ void mock . module ( "@/browser/components/AuthTokenModal/AuthTokenModal" , ( ) => ( {
91+ // Note: Module mocks leak between bun test files.
92+ // Export all commonly-used symbols to avoid cross-test import errors.
93+ AuthTokenModal : ( props : { error ?: string | null } ) => (
94+ < div data-testid = "AuthTokenModalMock" > { props . error ?? "no-error" } </ div >
95+ ) ,
96+ getStoredAuthToken : ( ) => null ,
97+ // eslint-disable-next-line @typescript-eslint/no-empty-function
98+ setStoredAuthToken : ( ) => { } ,
99+ // eslint-disable-next-line @typescript-eslint/no-empty-function
100+ clearStoredAuthToken : ( ) => { } ,
101+ } ) ) ;
102+ }
101103
102104let AppLoader : ( typeof import ( "../AppLoader/AppLoader" ) ) [ "AppLoader" ] ;
103105
104106describe ( "AppLoader" , ( ) => {
105107 beforeEach ( async ( ) => {
106108 cleanupDom = installDom ( ) ;
109+ installAppLoaderModuleMocks ( ) ;
107110 ( { AppLoader } = await import ( "../AppLoader/AppLoader" ) ) ;
108111 } ) ;
109112
0 commit comments