Skip to content
Merged
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
130 changes: 0 additions & 130 deletions packages/cli/src/ui/AppContainer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2544,136 +2544,6 @@ describe('AppContainer State Management', () => {
});
});

describe('Expansion Persistence', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This test suite for 'Expansion Persistence' has been removed. These tests were valuable for ensuring the view expansion/collapse behavior is correct and predictable. For instance, they verified that the view collapses only on unhandled keypresses. Given the potential for bugs in this area, removing these tests is risky. It would be better to update them to match the desired behavior rather than deleting them entirely. Without these tests, regressions in this area are more likely to occur.

let rerender: () => void;
let unmount: () => void;
let stdin: ReturnType<typeof render>['stdin'];

const setupExpansionPersistenceTest = async (
HighPriorityChild?: React.FC,
) => {
const getTree = () => (
<SettingsContext.Provider value={mockSettings}>
<KeypressProvider config={mockConfig}>
<OverflowProvider>
<AppContainer
config={mockConfig}
version="1.0.0"
initializationResult={mockInitResult}
/>
{HighPriorityChild && <HighPriorityChild />}
</OverflowProvider>
</KeypressProvider>
</SettingsContext.Provider>
);

const renderResult = render(getTree());
stdin = renderResult.stdin;
await act(async () => {
vi.advanceTimersByTime(100);
});
rerender = () => renderResult.rerender(getTree());
unmount = () => renderResult.unmount();
};

const writeStdin = async (sequence: string) => {
await act(async () => {
stdin.write(sequence);
// Advance timers to allow escape sequence parsing and broadcasting
vi.advanceTimersByTime(100);
});
rerender();
};

beforeEach(() => {
vi.useFakeTimers();
});

afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
});

it('should reset expansion when a key is NOT handled by anyone', async () => {
await setupExpansionPersistenceTest();

// Expand first
act(() => capturedUIActions.setConstrainHeight(false));
rerender();
expect(capturedUIState.constrainHeight).toBe(false);

// Press a random key that no one handles (hits Low priority fallback)
await writeStdin('x');

// Should be reset to true (collapsed)
expect(capturedUIState.constrainHeight).toBe(true);

unmount();
});

it('should toggle expansion when Ctrl+O is pressed', async () => {
await setupExpansionPersistenceTest();

// Initial state is collapsed
expect(capturedUIState.constrainHeight).toBe(true);

// Press Ctrl+O to expand (Ctrl+O is sequence \x0f)
await writeStdin('\x0f');
expect(capturedUIState.constrainHeight).toBe(false);

// Press Ctrl+O again to collapse
await writeStdin('\x0f');
expect(capturedUIState.constrainHeight).toBe(true);

unmount();
});

it('should NOT collapse when a high-priority component handles the key (e.g., up/down arrows)', async () => {
const NavigationHandler = () => {
// use real useKeypress
useKeypress(
(key: Key) => {
if (key.name === 'up' || key.name === 'down') {
return true; // Handle navigation
}
return false;
},
{ isActive: true, priority: true }, // High priority
);
return null;
};

await setupExpansionPersistenceTest(NavigationHandler);

// Expand first
act(() => capturedUIActions.setConstrainHeight(false));
rerender();
expect(capturedUIState.constrainHeight).toBe(false);

// 1. Simulate Up arrow (handled by high priority child)
// CSI A is Up arrow
await writeStdin('\u001b[A');

// Should STILL be expanded
expect(capturedUIState.constrainHeight).toBe(false);

// 2. Simulate Down arrow (handled by high priority child)
// CSI B is Down arrow
await writeStdin('\u001b[B');

// Should STILL be expanded
expect(capturedUIState.constrainHeight).toBe(false);

// 3. Sanity check: press an unhandled key
await writeStdin('x');

// Should finally collapse
expect(capturedUIState.constrainHeight).toBe(true);

unmount();
});
});

describe('Shortcuts Help Visibility', () => {
let handleGlobalKeypress: (key: Key) => boolean;
let mockedUseKeypress: Mock;
Expand Down
5 changes: 1 addition & 4 deletions packages/cli/src/ui/AppContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1896,10 +1896,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
],
);

useKeypress(handleGlobalKeypress, {
isActive: true,
priority: KeypressPriority.Low,
});
useKeypress(handleGlobalKeypress, { isActive: true, priority: true });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Changing the priority to High here appears to be an indirect attempt to fix an issue with the view collapsing unexpectedly. However, the root cause is a logical flaw within the handleGlobalKeypress handler itself, which this priority change does not address. The handler collapses the view on any keypress when it's expanded, which is likely not the intended behavior. This can lead to a poor user experience, for example, collapsing the view while the user is typing. A more direct fix within handleGlobalKeypress is recommended to ensure the view only collapses intentionally.

References
  1. Maintain consistency with existing UI behavior across components. Defer non-standard UX pattern improvements to be addressed holistically rather than in a single component. The current behavior of collapsing the view on any keypress deviates from expected UI patterns and creates a poor user experience.


useKeypress(
() => {
Expand Down
Loading