-
Notifications
You must be signed in to change notification settings - Fork 99
Expand file tree
/
Copy pathAppLoader.auth.test.tsx
More file actions
144 lines (121 loc) · 4.29 KB
/
AppLoader.auth.test.tsx
File metadata and controls
144 lines (121 loc) · 4.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import "../../../../tests/ui/dom";
import React from "react";
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
import { cleanup, render } from "@testing-library/react";
import { useTheme } from "../../contexts/ThemeContext";
import { installDom } from "../../../../tests/ui/dom";
let cleanupDom: (() => void) | null = null;
let apiStatus: "auth_required" | "connecting" | "error" = "auth_required";
let apiError: string | null = "Authentication required";
const AUTH_TOKEN_STORAGE_KEY = "mux:auth-token";
// AppLoader imports App, which pulls in Lottie-based components. In happy-dom,
// lottie-web's canvas bootstrap can throw during module evaluation.
void mock.module("lottie-react", () => ({
__esModule: true,
default: () => <div data-testid="LottieMock" />,
}));
void mock.module("@/browser/contexts/API", () => ({
APIProvider: (props: { children: React.ReactNode }) => props.children,
useAPI: () => {
if (apiStatus === "auth_required") {
return {
api: null,
status: "auth_required" as const,
error: apiError,
authenticate: () => undefined,
retry: () => undefined,
};
}
if (apiStatus === "error") {
return {
api: null,
status: "error" as const,
error: apiError ?? "Connection error",
authenticate: () => undefined,
retry: () => undefined,
};
}
return {
api: null,
status: "connecting" as const,
error: null,
authenticate: () => undefined,
retry: () => undefined,
};
},
}));
void mock.module("@/browser/components/LoadingScreen/LoadingScreen", () => ({
LoadingScreen: () => {
const { theme } = useTheme();
return <div data-testid="LoadingScreenMock">{theme}</div>;
},
}));
void mock.module("@/browser/components/StartupConnectionError/StartupConnectionError", () => ({
StartupConnectionError: (props: { error: string }) => (
<div data-testid="StartupConnectionErrorMock">{props.error}</div>
),
}));
void mock.module("@/browser/components/AuthTokenModal/AuthTokenModal", () => ({
// Note: Module mocks leak between bun test files.
// Export all commonly-used symbols to avoid cross-test import errors.
// Keep token helpers behaviorally close to production so leaked mocks do not
// break tests that expect auth-token persistence.
AuthTokenModal: (props: { error?: string | null }) => (
<div data-testid="AuthTokenModalMock">{props.error ?? "no-error"}</div>
),
getStoredAuthToken: () => {
try {
return localStorage.getItem(AUTH_TOKEN_STORAGE_KEY);
} catch {
return null;
}
},
setStoredAuthToken: (token: string) => {
try {
localStorage.setItem(AUTH_TOKEN_STORAGE_KEY, token);
} catch {
// no-op in tests without storage
}
},
clearStoredAuthToken: () => {
try {
localStorage.removeItem(AUTH_TOKEN_STORAGE_KEY);
} catch {
// no-op in tests without storage
}
},
}));
import { AppLoader } from "../AppLoader/AppLoader";
describe("AppLoader", () => {
beforeEach(() => {
cleanupDom = installDom();
});
afterEach(() => {
cleanup();
mock.restore();
cleanupDom?.();
cleanupDom = null;
});
test("renders AuthTokenModal when API status is auth_required (before workspaces load)", () => {
apiStatus = "auth_required";
apiError = "Authentication required";
const { getByTestId, queryByText } = render(<AppLoader />);
expect(queryByText("Loading Mux")).toBeNull();
expect(getByTestId("AuthTokenModalMock").textContent).toContain("Authentication required");
});
test("renders StartupConnectionError when API status is error (before workspaces load)", () => {
apiStatus = "error";
apiError = "Connection error";
const { getByTestId, queryByTestId } = render(<AppLoader />);
expect(queryByTestId("LoadingScreenMock")).toBeNull();
expect(queryByTestId("AuthTokenModalMock")).toBeNull();
expect(getByTestId("StartupConnectionErrorMock").textContent).toContain("Connection error");
});
test("wraps LoadingScreen in ThemeProvider", () => {
apiStatus = "connecting";
apiError = null;
const { getByTestId } = render(<AppLoader />);
// If ThemeProvider is missing, useTheme() will throw.
expect(getByTestId("LoadingScreenMock").textContent).toBeTruthy();
});
});