mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-20 02:00:40 -07:00
Migrate core render util to use xterm.js as part of the rendering loop. (#19044)
This commit is contained in:
@@ -5,13 +5,13 @@
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useRef, act } from 'react';
|
||||
import { render } from 'ink-testing-library';
|
||||
import { render } from '../../../test-utils/render.js';
|
||||
import { Box, Text } from 'ink';
|
||||
import { ScrollableList, type ScrollableListRef } from './ScrollableList.js';
|
||||
import { ScrollProvider } from '../../contexts/ScrollProvider.js';
|
||||
import { KeypressProvider } from '../../contexts/KeypressContext.js';
|
||||
import { MouseProvider } from '../../contexts/MouseContext.js';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { waitFor } from '../../../test-utils/async.js';
|
||||
|
||||
vi.mock('../../contexts/UIStateContext.js', () => ({
|
||||
@@ -133,10 +133,19 @@ const TestComponent = ({
|
||||
);
|
||||
};
|
||||
describe('ScrollableList Demo Behavior', () => {
|
||||
beforeEach(() => {
|
||||
vi.stubEnv('NODE_ENV', 'test');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
it('should scroll to bottom when new items are added and stop when scrolled up', async () => {
|
||||
let addItem: (() => void) | undefined;
|
||||
let listRef: ScrollableListRef<Item> | null = null;
|
||||
let lastFrame: () => string | undefined;
|
||||
let lastFrame: (options?: { allowEmpty?: boolean }) => string | undefined;
|
||||
let waitUntilReady: () => Promise<void>;
|
||||
|
||||
let result: ReturnType<typeof render>;
|
||||
|
||||
@@ -146,14 +155,17 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
onAddItem={(add) => {
|
||||
addItem = add;
|
||||
}}
|
||||
onRef={(ref) => {
|
||||
onRef={async (ref) => {
|
||||
listRef = ref;
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
lastFrame = result.lastFrame;
|
||||
waitUntilReady = result.waitUntilReady;
|
||||
});
|
||||
|
||||
await waitUntilReady!();
|
||||
|
||||
// Initial render should show Item 1000
|
||||
expect(lastFrame!()).toContain('Item 1000');
|
||||
expect(lastFrame!()).toContain('Count: 1000');
|
||||
@@ -162,6 +174,8 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
await act(async () => {
|
||||
addItem?.();
|
||||
});
|
||||
await waitUntilReady!();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastFrame!()).toContain('Count: 1001');
|
||||
});
|
||||
@@ -172,6 +186,8 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
await act(async () => {
|
||||
addItem?.();
|
||||
});
|
||||
await waitUntilReady!();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastFrame!()).toContain('Count: 1002');
|
||||
});
|
||||
@@ -182,14 +198,14 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
await act(async () => {
|
||||
listRef?.scrollBy(-5);
|
||||
});
|
||||
await act(async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
});
|
||||
await waitUntilReady!();
|
||||
|
||||
// Add item 1003 - should NOT be visible because we scrolled up
|
||||
await act(async () => {
|
||||
addItem?.();
|
||||
});
|
||||
await waitUntilReady!();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastFrame!()).toContain('Count: 1003');
|
||||
});
|
||||
@@ -249,12 +265,16 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
};
|
||||
|
||||
let lastFrame: () => string | undefined;
|
||||
let waitUntilReady: () => Promise<void>;
|
||||
let result: ReturnType<typeof render>;
|
||||
await act(async () => {
|
||||
result = render(<StickyTestComponent />);
|
||||
lastFrame = result.lastFrame;
|
||||
waitUntilReady = result.waitUntilReady;
|
||||
});
|
||||
|
||||
await waitUntilReady!();
|
||||
|
||||
// Initially at top, should see Normal Item 1
|
||||
await waitFor(() => {
|
||||
expect(lastFrame!()).toContain('[Normal] Item 1');
|
||||
@@ -265,6 +285,7 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
await act(async () => {
|
||||
listRef?.scrollBy(2);
|
||||
});
|
||||
await waitUntilReady!();
|
||||
|
||||
// Now Item 1 should be stuck
|
||||
await waitFor(() => {
|
||||
@@ -278,6 +299,7 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
await act(async () => {
|
||||
listRef?.scrollTo(10);
|
||||
});
|
||||
await waitUntilReady!();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastFrame!()).not.toContain('[STICKY] Item 1');
|
||||
@@ -287,6 +309,7 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
await act(async () => {
|
||||
listRef?.scrollTo(0);
|
||||
});
|
||||
await waitUntilReady!();
|
||||
|
||||
// Should be normal again
|
||||
await waitFor(() => {
|
||||
@@ -302,8 +325,9 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
describe('Keyboard Navigation', () => {
|
||||
it('should handle scroll keys correctly', async () => {
|
||||
let listRef: ScrollableListRef<Item> | null = null;
|
||||
let lastFrame: () => string | undefined;
|
||||
let lastFrame: (options?: { allowEmpty?: boolean }) => string | undefined;
|
||||
let stdin: { write: (data: string) => void };
|
||||
let waitUntilReady: () => Promise<void>;
|
||||
|
||||
const items = Array.from({ length: 50 }, (_, i) => ({
|
||||
id: String(i),
|
||||
@@ -334,8 +358,11 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
);
|
||||
lastFrame = result.lastFrame;
|
||||
stdin = result.stdin;
|
||||
waitUntilReady = result.waitUntilReady;
|
||||
});
|
||||
|
||||
await waitUntilReady!();
|
||||
|
||||
// Initial state
|
||||
expect(lastFrame!()).toContain('Item 0');
|
||||
expect(listRef).toBeDefined();
|
||||
@@ -345,6 +372,8 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
await act(async () => {
|
||||
stdin.write('\x1b[b');
|
||||
});
|
||||
await waitUntilReady!();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(listRef?.getScrollState()?.scrollTop).toBeGreaterThan(0);
|
||||
});
|
||||
@@ -353,6 +382,8 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
await act(async () => {
|
||||
stdin.write('\x1b[a');
|
||||
});
|
||||
await waitUntilReady!();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(listRef?.getScrollState()?.scrollTop).toBe(0);
|
||||
});
|
||||
@@ -361,6 +392,8 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
await act(async () => {
|
||||
stdin.write('\x1b[6~');
|
||||
});
|
||||
await waitUntilReady!();
|
||||
|
||||
await waitFor(() => {
|
||||
// Height is 10, so should scroll ~10 units
|
||||
expect(listRef?.getScrollState()?.scrollTop).toBeGreaterThanOrEqual(9);
|
||||
@@ -370,6 +403,8 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
await act(async () => {
|
||||
stdin.write('\x1b[5~');
|
||||
});
|
||||
await waitUntilReady!();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(listRef?.getScrollState()?.scrollTop).toBeLessThan(2);
|
||||
});
|
||||
@@ -378,6 +413,8 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
await act(async () => {
|
||||
stdin.write('\x1b[1;5F');
|
||||
});
|
||||
await waitUntilReady!();
|
||||
|
||||
await waitFor(() => {
|
||||
// Total 50 items, height 10. Max scroll ~40.
|
||||
expect(listRef?.getScrollState()?.scrollTop).toBeGreaterThan(30);
|
||||
@@ -387,11 +424,15 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
await act(async () => {
|
||||
stdin.write('\x1b[1;5H');
|
||||
});
|
||||
await waitUntilReady!();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(listRef?.getScrollState()?.scrollTop).toBe(0);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
// Let the scrollbar fade out animation finish
|
||||
await new Promise((resolve) => setTimeout(resolve, 1600));
|
||||
result.unmount();
|
||||
});
|
||||
});
|
||||
@@ -400,7 +441,8 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
describe('Width Prop', () => {
|
||||
it('should apply the width prop to the container', async () => {
|
||||
const items = [{ id: '1', title: 'Item 1' }];
|
||||
let lastFrame: () => string | undefined;
|
||||
let lastFrame: (options?: { allowEmpty?: boolean }) => string | undefined;
|
||||
let waitUntilReady: () => Promise<void>;
|
||||
|
||||
let result: ReturnType<typeof render>;
|
||||
await act(async () => {
|
||||
@@ -423,8 +465,11 @@ describe('ScrollableList Demo Behavior', () => {
|
||||
</MouseProvider>,
|
||||
);
|
||||
lastFrame = result.lastFrame;
|
||||
waitUntilReady = result.waitUntilReady;
|
||||
});
|
||||
|
||||
await waitUntilReady!();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toContain('Item 1');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user