@@ -61,6 +61,9 @@ jest.mock("./components/Layout/MainLayout", () => {
6161 < button onClick = { onToggleTheme } data-testid = "toggle-theme" >
6262 Toggle Theme
6363 </ button >
64+ < button onClick = { ( ) => onNavigate ( "home" ) } data-testid = "nav-home" >
65+ Home
66+ </ button >
6467 < button onClick = { ( ) => onNavigate ( "config" ) } data-testid = "nav-config" >
6568 Config
6669 </ button >
@@ -188,6 +191,41 @@ jest.mock("./components/History/AttackHistory", () => {
188191 } ;
189192} ) ;
190193
194+ jest . mock ( "./components/Home/Home" , ( ) => {
195+ const MockHome = ( {
196+ activeTarget,
197+ onNavigate,
198+ onOpenAttack,
199+ labels,
200+ } : {
201+ activeTarget : unknown ;
202+ onNavigate : ( view : string ) => void ;
203+ onOpenAttack : ( attackResultId : string ) => void ;
204+ labels : Record < string , string > ;
205+ } ) => {
206+ return (
207+ < div data-testid = "home-view" >
208+ < span data-testid = "home-has-target" > { activeTarget ? "yes" : "no" } </ span >
209+ < span data-testid = "home-labels-json" > { JSON . stringify ( labels ) } </ span >
210+ < button onClick = { ( ) => onNavigate ( "config" ) } data-testid = "home-go-config" >
211+ Go to config
212+ </ button >
213+ < button
214+ onClick = { ( ) => onOpenAttack ( "ar-home-attack" ) }
215+ data-testid = "home-open-attack"
216+ >
217+ Open Home Attack
218+ </ button >
219+ </ div >
220+ ) ;
221+ } ;
222+ MockHome . displayName = "MockHome" ;
223+ return {
224+ __esModule : true ,
225+ default : MockHome ,
226+ } ;
227+ } ) ;
228+
191229describe ( "App" , ( ) => {
192230 beforeEach ( ( ) => {
193231 jest . clearAllMocks ( ) ;
@@ -197,7 +235,7 @@ describe("App", () => {
197235 it ( "renders with FluentProvider and MainLayout" , ( ) => {
198236 render ( < App /> ) ;
199237 expect ( screen . getByTestId ( "main-layout" ) ) . toBeInTheDocument ( ) ;
200- expect ( screen . getByTestId ( "chat-window " ) ) . toBeInTheDocument ( ) ;
238+ expect ( screen . getByTestId ( "home-view " ) ) . toBeInTheDocument ( ) ;
201239 } ) ;
202240
203241 it ( "starts in dark mode" , ( ) => {
@@ -229,9 +267,21 @@ describe("App", () => {
229267 ) ;
230268 } ) ;
231269
232- it ( "starts in chat view" , ( ) => {
270+ it ( "starts in home view" , ( ) => {
233271 render ( < App /> ) ;
234272
273+ expect ( screen . getByTestId ( "main-layout" ) ) . toHaveAttribute (
274+ "data-current-view" ,
275+ "home"
276+ ) ;
277+ expect ( screen . getByTestId ( "home-view" ) ) . toBeInTheDocument ( ) ;
278+ } ) ;
279+
280+ it ( "switches to chat view" , ( ) => {
281+ render ( < App /> ) ;
282+
283+ fireEvent . click ( screen . getByTestId ( "nav-chat" ) ) ;
284+
235285 expect ( screen . getByTestId ( "main-layout" ) ) . toHaveAttribute (
236286 "data-current-view" ,
237287 "chat"
@@ -264,6 +314,7 @@ describe("App", () => {
264314 it ( "sets conversationId from chat window" , ( ) => {
265315 render ( < App /> ) ;
266316
317+ fireEvent . click ( screen . getByTestId ( "nav-chat" ) ) ;
267318 expect ( screen . getByTestId ( "conversation-id" ) ) . toHaveTextContent ( "none" ) ;
268319
269320 fireEvent . click ( screen . getByTestId ( "set-conversation" ) ) ;
@@ -273,6 +324,7 @@ describe("App", () => {
273324 it ( "clears conversationId on new attack" , ( ) => {
274325 render ( < App /> ) ;
275326
327+ fireEvent . click ( screen . getByTestId ( "nav-chat" ) ) ;
276328 fireEvent . click ( screen . getByTestId ( "set-conversation" ) ) ;
277329 expect ( screen . getByTestId ( "conversation-id" ) ) . toHaveTextContent ( "conv-123" ) ;
278330
@@ -283,7 +335,8 @@ describe("App", () => {
283335 it ( "sets active target from config page and passes to chat" , ( ) => {
284336 render ( < App /> ) ;
285337
286- // No target initially
338+ // Switch to chat and confirm no target initially
339+ fireEvent . click ( screen . getByTestId ( "nav-chat" ) ) ;
287340 expect ( screen . getByTestId ( "has-target" ) ) . toHaveTextContent ( "no" ) ;
288341
289342 // Switch to config and set target
@@ -322,6 +375,36 @@ describe("App", () => {
322375 await waitFor ( ( ) => expect ( screen . getByTestId ( "conversation-id" ) ) . toHaveTextContent ( "attack-conv-1" ) ) ;
323376 } ) ;
324377
378+ it ( "opens attack from home and switches to chat" , async ( ) => {
379+ mockGetAttack . mockResolvedValue ( {
380+ attack_result_id : "ar-home-attack" ,
381+ conversation_id : "home-conv-1" ,
382+ labels : { operator : "roakey" } ,
383+ } ) ;
384+ render ( < App /> ) ;
385+
386+ fireEvent . click ( screen . getByTestId ( "home-open-attack" ) ) ;
387+
388+ expect ( screen . getByTestId ( "main-layout" ) ) . toHaveAttribute (
389+ "data-current-view" ,
390+ "chat"
391+ ) ;
392+ await waitFor ( ( ) => expect ( mockGetAttack ) . toHaveBeenCalledWith ( "ar-home-attack" ) ) ;
393+ await waitFor ( ( ) => expect ( screen . getByTestId ( "conversation-id" ) ) . toHaveTextContent ( "home-conv-1" ) ) ;
394+ } ) ;
395+
396+ it ( "navigates to config from the home view" , ( ) => {
397+ render ( < App /> ) ;
398+
399+ fireEvent . click ( screen . getByTestId ( "home-go-config" ) ) ;
400+
401+ expect ( screen . getByTestId ( "main-layout" ) ) . toHaveAttribute (
402+ "data-current-view" ,
403+ "config"
404+ ) ;
405+ expect ( screen . getByTestId ( "target-config" ) ) . toBeInTheDocument ( ) ;
406+ } ) ;
407+
325408 it ( "handles failed attack open gracefully" , async ( ) => {
326409 mockGetAttack . mockRejectedValue ( new Error ( "Not found" ) ) ;
327410 render ( < App /> ) ;
@@ -349,6 +432,9 @@ describe("App", () => {
349432 expect ( mockedVersionApi . getVersion ) . toHaveBeenCalled ( ) ;
350433 } ) ;
351434
435+ // Switch to chat to inspect labels
436+ fireEvent . click ( screen . getByTestId ( "nav-chat" ) ) ;
437+
352438 await waitFor ( ( ) => {
353439 expect ( screen . getByTestId ( "labels-operator" ) ) . toHaveTextContent ( "default_user" ) ;
354440 expect ( screen . getByTestId ( "labels-json" ) ) . toHaveTextContent ( '"custom":"value"' ) ;
@@ -364,9 +450,12 @@ describe("App", () => {
364450
365451 render ( < App /> ) ;
366452
453+ // Home receives the same labels prop — assert there to avoid racing the
454+ // async initLabels effect against a view-change re-render.
367455 await waitFor ( ( ) => {
368- expect ( screen . getByTestId ( "labels-operator" ) ) . toHaveTextContent ( "test.user" ) ;
369- expect ( screen . getByTestId ( "labels-json" ) ) . toHaveTextContent ( '"custom":"value"' ) ;
456+ const labels = screen . getByTestId ( "home-labels-json" ) . textContent ?? "" ;
457+ expect ( labels ) . toContain ( '"operator":"test.user"' ) ;
458+ expect ( labels ) . toContain ( '"custom":"value"' ) ;
370459 } ) ;
371460 } ) ;
372461
@@ -380,8 +469,9 @@ describe("App", () => {
380469 render ( < App /> ) ;
381470
382471 await waitFor ( ( ) => {
383- expect ( screen . getByTestId ( "labels-operator" ) ) . toHaveTextContent ( "override_user" ) ;
384- expect ( screen . getByTestId ( "labels-json" ) ) . toHaveTextContent ( '"custom":"value"' ) ;
472+ const labels = screen . getByTestId ( "home-labels-json" ) . textContent ?? "" ;
473+ expect ( labels ) . toContain ( '"operator":"override_user"' ) ;
474+ expect ( labels ) . toContain ( '"custom":"value"' ) ;
385475 } ) ;
386476 } ) ;
387477
@@ -401,6 +491,8 @@ describe("App", () => {
401491 it ( "sets active conversation when onSelectConversation is called" , ( ) => {
402492 render ( < App /> ) ;
403493
494+ fireEvent . click ( screen . getByTestId ( "nav-chat" ) ) ;
495+
404496 // First create a conversation to have an attack
405497 fireEvent . click ( screen . getByTestId ( "set-conversation" ) ) ;
406498 expect ( screen . getByTestId ( "conversation-id" ) ) . toHaveTextContent ( "conv-123" ) ;
0 commit comments