From b84f416e07ebea8273ce8e4699503dca40a492d6 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Tue, 9 Jun 2026 23:32:57 -0700 Subject: [PATCH] FN-6175: stabilize TerminalModal mobile tab regression tests Harden TerminalModal mobile keyboard regression coverage against async tab setup and xterm mock issues. - add missing xterm terminal mock methods used by mobile tab interactions - replace anonymous constructor mock implementations with named function mocks for Terminal and FitAddon - wait for second-session callbacks before simulating viewport and keyboard transitions in mobile tab-switch tests Files changed: packages/dashboard/app/components/__tests__/TerminalModal.test.tsx | 48 ++++++++++++++++++---- 1 file changed, 39 insertions(+), 9 deletions(-) Fusion-Task-Id: FN-6175 Fusion-Task-Lineage: fc95f3d6-586a-475c-a226-be1a57c88ff4 --- .../__tests__/TerminalModal.test.tsx | 48 +++++++++++++++---- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/packages/dashboard/app/components/__tests__/TerminalModal.test.tsx b/packages/dashboard/app/components/__tests__/TerminalModal.test.tsx index f8de390a1b..85583c21f4 100644 --- a/packages/dashboard/app/components/__tests__/TerminalModal.test.tsx +++ b/packages/dashboard/app/components/__tests__/TerminalModal.test.tsx @@ -2317,6 +2317,10 @@ describe("TerminalModal — FN-1234 mobile tab switch with keyboard", () => { loadAddon: vi.fn(), open: vi.fn(), onData: vi.fn((_cb: (data: string) => void) => ({ dispose: vi.fn() })), + attachCustomKeyEventHandler: vi.fn(), + hasSelection: vi.fn(() => false), + getSelection: vi.fn(() => ""), + paste: vi.fn(), dispose: vi.fn(), write: vi.fn(), clear: vi.fn(), @@ -2413,8 +2417,12 @@ describe("TerminalModal — FN-1234 mobile tab switch with keyboard", () => { const xtermModule = await import("@xterm/xterm"); vi.mocked(xtermModule.Terminal) - .mockImplementationOnce(() => terminalOne as any) - .mockImplementationOnce(() => terminalTwo as any); + .mockImplementationOnce(function TerminalOneMock() { + return terminalOne as any; + } as never) + .mockImplementationOnce(function TerminalTwoMock() { + return terminalTwo as any; + } as never); let sessionOneDataCallback: ((data: string) => void) | null = null; let sessionTwoDataCallback: ((data: string) => void) | null = null; @@ -2453,6 +2461,10 @@ describe("TerminalModal — FN-1234 mobile tab switch with keyboard", () => { expect(terminalTwo.open).toHaveBeenCalled(); }); + await waitFor(() => { + expect(sessionTwoDataCallback).not.toBeNull(); + }); + Object.defineProperty(mockVV, "height", { value: 417, writable: true, @@ -2489,13 +2501,21 @@ describe("TerminalModal — FN-1234 mobile tab switch with keyboard", () => { const xtermModule = await import("@xterm/xterm"); vi.mocked(xtermModule.Terminal) - .mockImplementationOnce(() => terminalOne as any) - .mockImplementationOnce(() => terminalTwo as any); + .mockImplementationOnce(function TerminalOneMock() { + return terminalOne as any; + } as never) + .mockImplementationOnce(function TerminalTwoMock() { + return terminalTwo as any; + } as never); const fitModule = await import("@xterm/addon-fit"); vi.mocked(fitModule.FitAddon) - .mockImplementationOnce(() => fitOne as any) - .mockImplementationOnce(() => fitTwo as any); + .mockImplementationOnce(function FitOneMock() { + return fitOne as any; + } as never) + .mockImplementationOnce(function FitTwoMock() { + return fitTwo as any; + } as never); mockUseTerminalSessions.mockReturnValue(makeSessionState([tab1, tab2])); mockUseTerminal.mockReturnValue(createMockTerminalState({ resize: resizeOne })); @@ -2578,8 +2598,12 @@ describe("TerminalModal — FN-1234 mobile tab switch with keyboard", () => { const xtermModule = await import("@xterm/xterm"); vi.mocked(xtermModule.Terminal) - .mockImplementationOnce(() => terminalOne as any) - .mockImplementationOnce(() => terminalTwo as any); + .mockImplementationOnce(function TerminalOneMock() { + return terminalOne as any; + } as never) + .mockImplementationOnce(function TerminalTwoMock() { + return terminalTwo as any; + } as never); let sessionOneScrollbackCallback: ((data: string) => void) | null = null; let sessionTwoScrollbackCallback: ((data: string) => void) | null = null; @@ -2618,6 +2642,10 @@ describe("TerminalModal — FN-1234 mobile tab switch with keyboard", () => { expect(terminalTwo.open).toHaveBeenCalled(); }); + await waitFor(() => { + expect(sessionTwoScrollbackCallback).not.toBeNull(); + }); + Object.defineProperty(mockVV, "height", { value: 390, writable: true, @@ -3656,7 +3684,9 @@ describe("TerminalModal — FN-872 real-device keyboard overlap refinement", () const mockFit = vi.fn(); const mockFitAddon = { fit: mockFit, dispose: vi.fn() }; const fitAddonModule = await import("@xterm/addon-fit"); - (fitAddonModule.FitAddon as unknown as ReturnType).mockImplementation(() => mockFitAddon); + (fitAddonModule.FitAddon as unknown as ReturnType).mockImplementation(function FitAddonMock() { + return mockFitAddon; + }); mockUseTerminal.mockReturnValue( createMockTerminalState({ connectionStatus: "connected" })