Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions packages/react-web-cli/src/AblyCliTerminal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@
expect(onConnectionStatusChangeMock).toHaveBeenCalledWith("disconnected");
});

test.skip("shows installation tip after 6 seconds during connection attempts", async () => {

Check warning on line 777 in packages/react-web-cli/src/AblyCliTerminal.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Disabled test - if you want to skip a test temporarily, use .todo() instead

Check warning on line 777 in packages/react-web-cli/src/AblyCliTerminal.test.tsx

View workflow job for this annotation

GitHub Actions / e2e-cli

Disabled test - if you want to skip a test temporarily, use .todo() instead
// SKIPPED: This test has timing issues with fake timers in CI environments
// The 6-second delay doesn't advance consistently with vi.advanceTimersByTime
vi.useFakeTimers();
Expand Down Expand Up @@ -830,7 +830,7 @@
});
});

test.skip("shows installation tip during reconnection after 6 seconds", async () => {

Check warning on line 833 in packages/react-web-cli/src/AblyCliTerminal.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Disabled test - if you want to skip a test temporarily, use .todo() instead

Check warning on line 833 in packages/react-web-cli/src/AblyCliTerminal.test.tsx

View workflow job for this annotation

GitHub Actions / e2e-cli

Disabled test - if you want to skip a test temporarily, use .todo() instead
// SKIPPED: This test has timing issues with fake timers in CI environments
// The 6-second delay doesn't advance consistently with vi.advanceTimersByTime
vi.useFakeTimers();
Expand Down Expand Up @@ -913,7 +913,7 @@
expect(onConnectionStatusChangeMock).toHaveBeenCalledWith("connecting");
});

test.skip("manual reconnect resets attempt counter after max attempts reached - skipped due to CI timing issues", async () => {

Check warning on line 916 in packages/react-web-cli/src/AblyCliTerminal.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Disabled test - if you want to skip a test temporarily, use .todo() instead

Check warning on line 916 in packages/react-web-cli/src/AblyCliTerminal.test.tsx

View workflow job for this annotation

GitHub Actions / e2e-cli

Disabled test - if you want to skip a test temporarily, use .todo() instead
// SKIPPED: This test has timing issues in CI environments
// Manual reconnect state transitions don't complete reliably with mocked timers
// Set up max attempts reached state
Expand Down Expand Up @@ -1211,7 +1211,7 @@
);
});

test.skip("connection timeout triggers error after 30 seconds", async () => {

Check warning on line 1214 in packages/react-web-cli/src/AblyCliTerminal.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Disabled test - if you want to skip a test temporarily, use .todo() instead

Check warning on line 1214 in packages/react-web-cli/src/AblyCliTerminal.test.tsx

View workflow job for this annotation

GitHub Actions / e2e-cli

Disabled test - if you want to skip a test temporarily, use .todo() instead
// SKIPPED: This test has timing issues with fake timers
// The 30-second timeout doesn't trigger consistently with vi.advanceTimersByTime
vi.useFakeTimers();
Expand Down Expand Up @@ -1614,7 +1614,7 @@
setItemMock.mockRestore();
});

test.skip("prompt detection correctly handles ANSI color codes", async () => {

Check warning on line 1617 in packages/react-web-cli/src/AblyCliTerminal.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Disabled test - if you want to skip a test temporarily, use .todo() instead

Check warning on line 1617 in packages/react-web-cli/src/AblyCliTerminal.test.tsx

View workflow job for this annotation

GitHub Actions / e2e-cli

Disabled test - if you want to skip a test temporarily, use .todo() instead
// SKIPPED: This test depends on React fiber internal structure
// React's internal structure is not stable across versions and breaks this test
// Create a mock component and socket
Expand Down Expand Up @@ -1652,7 +1652,7 @@
expect(instance.isSessionActive).toBe(true);
});

test.skip("onConnectionStatusChange only reports status for the primary terminal in split-screen mode", async () => {

Check warning on line 1655 in packages/react-web-cli/src/AblyCliTerminal.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Disabled test - if you want to skip a test temporarily, use .todo() instead

Check warning on line 1655 in packages/react-web-cli/src/AblyCliTerminal.test.tsx

View workflow job for this annotation

GitHub Actions / e2e-cli

Disabled test - if you want to skip a test temporarily, use .todo() instead
// SKIPPED: This test verifies implementation details that are subject to change
// Core functionality is covered by other unit and integration tests
// The environment is not stable enough for this internal implementation test
Expand Down Expand Up @@ -2557,3 +2557,76 @@
expect(hasTestCmd).toBe(true);
}, 15_000);
});

describe("AblyCliTerminal - Unmount cleanup", () => {
test("closes socket normally on unmount (no special code for resume support)", async () => {
mockClose.mockClear();

const { unmount } = render(
<AblyCliTerminal
websocketUrl="wss://test.ably.com"
ablyApiKey="test-key"
/>,
);

await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 50));
});

expect(mockSocketInstance).toBeTruthy();
expect(mockSocketInstance.readyState).toBe(WebSocket.OPEN);

unmount();

// Should close without special code (allows grace period for resume)
expect(mockClose).toHaveBeenCalledWith();
});

test("does not call close if socket already closing", async () => {
mockClose.mockClear();

const { unmount } = render(
<AblyCliTerminal
websocketUrl="wss://test.ably.com"
ablyApiKey="test-key"
/>,
);

await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 50));
});

mockSocketInstance.readyState = WebSocket.CLOSING;

unmount();

expect(mockClose).not.toHaveBeenCalled();
});

test("terminateSession() sends close code 4001 for immediate cleanup", async () => {
mockClose.mockClear();
const terminalRef = React.createRef<AblyCliTerminalHandle>();

render(
<AblyCliTerminal
ref={terminalRef}
websocketUrl="wss://test.ably.com"
ablyApiKey="test-key"
/>,
);

await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 50));
});

expect(mockSocketInstance).toBeTruthy();
expect(mockSocketInstance.readyState).toBe(WebSocket.OPEN);

// Call terminateSession explicitly
act(() => {
terminalRef.current?.terminateSession();
});

expect(mockClose).toHaveBeenCalledWith(4001, "user-closed-panel");
});
});
21 changes: 20 additions & 1 deletion packages/react-web-cli/src/AblyCliTerminal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ export interface AblyCliTerminalHandle {
setSplitPosition: (percent: number) => void;
/** Read current split state. */
getSplitState: () => { isSplit: boolean; splitPosition: number };
/** Terminate the session immediately. Call this before unmounting when user explicitly closes the panel. */
terminateSession: () => void;
}

// Use shared debug logging
Expand Down Expand Up @@ -340,6 +342,23 @@ const AblyCliTerminalInner = (
setSplitPosition(clamped);
},
getSplitState: () => ({ isSplit, splitPosition }),
terminateSession: () => {
debugLog(
"[AblyCLITerminal] terminateSession called - closing with code 4001",
);
if (
socketReference.current &&
socketReference.current.readyState < WebSocket.CLOSING
) {
socketReference.current.close(4001, "user-closed-panel");
}
if (
secondarySocketReference.current &&
secondarySocketReference.current.readyState < WebSocket.CLOSING
) {
secondarySocketReference.current.close(4001, "user-closed-panel");
}
},
}),
[
enableSplitScreen,
Expand Down Expand Up @@ -2382,7 +2401,7 @@ const AblyCliTerminalInner = (
socketReference.current &&
socketReference.current.readyState < WebSocket.CLOSING
) {
// close websocket
// Normal close (no 4001) so server grace period allows resume
debugLog("[AblyCLITerminal] Closing WebSocket on unmount.");
socketReference.current.close();
}
Expand Down
Loading