test(cli): refactor tests for async render utilities (#23252)

This commit is contained in:
Tommaso Sciortino
2026-03-20 20:08:29 +00:00
committed by GitHub
parent 86a3a913b5
commit 6c78eb7a39
198 changed files with 3592 additions and 4802 deletions

View File

@@ -87,7 +87,7 @@ describe('ScrollProvider Drag', () => {
innerHeight: 10,
}));
render(
await render(
<ScrollProvider>
<TestScrollable
id="test-scrollable"
@@ -190,7 +190,7 @@ describe('ScrollProvider Drag', () => {
innerHeight: 10,
}));
render(
await render(
<ScrollProvider>
<TestScrollable
id="test-scrollable"
@@ -253,7 +253,7 @@ describe('ScrollProvider Drag', () => {
innerHeight: 10,
}));
render(
await render(
<ScrollProvider>
<TestScrollable
id="test-scrollable"
@@ -291,7 +291,7 @@ describe('ScrollProvider Drag', () => {
innerHeight: 10,
}));
render(
await render(
<ScrollProvider>
<TestScrollable
id="test-scrollable"
@@ -329,7 +329,7 @@ describe('ScrollProvider Drag', () => {
innerHeight: 10,
}));
render(
await render(
<ScrollProvider>
<TestScrollable
id="test-scrollable"
@@ -397,7 +397,7 @@ describe('ScrollProvider Drag', () => {
);
TestScrollableWithScrollTo.displayName = 'TestScrollableWithScrollTo';
render(
await render(
<ScrollProvider>
<TestScrollableWithScrollTo
id="test-scrollable-scrollto"

View File

@@ -82,7 +82,7 @@ describe('ScrollProvider', () => {
});
describe('Event Handling Status', () => {
it('returns true when scroll event is handled', () => {
it('returns true when scroll event is handled', async () => {
const scrollBy = vi.fn();
const getScrollState = vi.fn(() => ({
scrollTop: 0,
@@ -90,7 +90,7 @@ describe('ScrollProvider', () => {
innerHeight: 10,
}));
render(
await render(
<ScrollProvider>
<TestScrollable
id="test-scrollable"
@@ -119,7 +119,7 @@ describe('ScrollProvider', () => {
expect(handled).toBe(true);
});
it('returns false when scroll event is ignored (cannot scroll further)', () => {
it('returns false when scroll event is ignored (cannot scroll further)', async () => {
const scrollBy = vi.fn();
// Already at bottom
const getScrollState = vi.fn(() => ({
@@ -128,7 +128,7 @@ describe('ScrollProvider', () => {
innerHeight: 10,
}));
render(
await render(
<ScrollProvider>
<TestScrollable
id="test-scrollable"
@@ -167,7 +167,7 @@ describe('ScrollProvider', () => {
innerHeight: 10,
}));
render(
await render(
<ScrollProvider>
<TestScrollable
id="test-scrollable"
@@ -211,7 +211,7 @@ describe('ScrollProvider', () => {
innerHeight: 10,
}));
render(
await render(
<ScrollProvider>
<TestScrollable
id="test-scrollable"
@@ -244,7 +244,7 @@ describe('ScrollProvider', () => {
innerHeight: 10,
}));
render(
await render(
<ScrollProvider>
<TestScrollable
id="test-scrollable"
@@ -289,7 +289,7 @@ describe('ScrollProvider', () => {
innerHeight: 10,
}));
render(
await render(
<ScrollProvider>
<TestScrollable
id="test-scrollable"
@@ -347,7 +347,7 @@ describe('ScrollProvider', () => {
innerHeight: 10,
}));
render(
await render(
<ScrollProvider>
<TestScrollable
id="test-scrollable"
@@ -408,7 +408,7 @@ describe('ScrollProvider', () => {
innerHeight: 10,
}));
render(
await render(
<ScrollProvider>
<TestScrollable
id="test-scrollable"
@@ -470,7 +470,7 @@ describe('ScrollProvider', () => {
innerHeight: 10,
}));
render(
await render(
<ScrollProvider>
<TestScrollable
id="test-scrollable"

View File

@@ -54,12 +54,12 @@ const TestHarness = ({
};
describe('SessionStatsContext', () => {
it('should provide the correct initial state', () => {
it('should provide the correct initial state', async () => {
const contextRef: MutableRefObject<
ReturnType<typeof useSessionStats> | undefined
> = { current: undefined };
const { unmount } = render(
const { unmount } = await render(
<SessionStatsProvider>
<TestHarness contextRef={contextRef} />
</SessionStatsProvider>,
@@ -73,12 +73,12 @@ describe('SessionStatsContext', () => {
unmount();
});
it('should update metrics when the uiTelemetryService emits an update', () => {
it('should update metrics when the uiTelemetryService emits an update', async () => {
const contextRef: MutableRefObject<
ReturnType<typeof useSessionStats> | undefined
> = { current: undefined };
const { unmount } = render(
const { unmount } = await render(
<SessionStatsProvider>
<TestHarness contextRef={contextRef} />
</SessionStatsProvider>,
@@ -149,7 +149,7 @@ describe('SessionStatsContext', () => {
unmount();
});
it('should not update metrics if the data is the same', () => {
it('should not update metrics if the data is the same', async () => {
const contextRef: MutableRefObject<
ReturnType<typeof useSessionStats> | undefined
> = { current: undefined };
@@ -161,7 +161,7 @@ describe('SessionStatsContext', () => {
return null;
};
const { unmount } = render(
const { unmount } = await render(
<SessionStatsProvider>
<CountingTestHarness />
</SessionStatsProvider>,
@@ -239,12 +239,12 @@ describe('SessionStatsContext', () => {
unmount();
});
it('should update session ID and reset stats when the uiTelemetryService emits a clear event', () => {
it('should update session ID and reset stats when the uiTelemetryService emits a clear event', async () => {
const contextRef: MutableRefObject<
ReturnType<typeof useSessionStats> | undefined
> = { current: undefined };
const { unmount } = render(
const { unmount } = await render(
<SessionStatsProvider>
<TestHarness contextRef={contextRef} />
</SessionStatsProvider>,
@@ -267,12 +267,12 @@ describe('SessionStatsContext', () => {
unmount();
});
it('should throw an error when useSessionStats is used outside of a provider', () => {
it('should throw an error when useSessionStats is used outside of a provider', async () => {
const onError = vi.fn();
// Suppress console.error from React for this test
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
const { unmount } = render(
const { unmount } = await render(
<ErrorBoundary onError={onError}>
<TestHarness contextRef={{ current: undefined }} />
</ErrorBoundary>,

View File

@@ -90,15 +90,15 @@ describe('SettingsContext', () => {
</SettingsContext.Provider>
);
it('should provide the correct initial state', () => {
const { result } = renderHook(() => useSettingsStore(), { wrapper });
it('should provide the correct initial state', async () => {
const { result } = await renderHook(() => useSettingsStore(), { wrapper });
expect(result.current.settings.merged).toEqual(mockSnapshot.merged);
expect(result.current.settings.isTrusted).toBe(true);
});
it('should allow accessing settings for a specific scope', () => {
const { result } = renderHook(() => useSettingsStore(), { wrapper });
it('should allow accessing settings for a specific scope', async () => {
const { result } = await renderHook(() => useSettingsStore(), { wrapper });
const userSettings = result.current.settings.forScope(SettingScope.User);
expect(userSettings).toBe(mockSnapshot.user);
@@ -109,8 +109,8 @@ describe('SettingsContext', () => {
expect(workspaceSettings).toBe(mockSnapshot.workspace);
});
it('should trigger re-renders when settings change (external event)', () => {
const { result } = renderHook(() => useSettingsStore(), { wrapper });
it('should trigger re-renders when settings change (external event)', async () => {
const { result } = await renderHook(() => useSettingsStore(), { wrapper });
expect(result.current.settings.merged.ui?.theme).toBe('default-theme');
@@ -130,8 +130,8 @@ describe('SettingsContext', () => {
expect(result.current.settings.merged.ui?.theme).toBe('new-theme');
});
it('should call store.setValue when setSetting is called', () => {
const { result } = renderHook(() => useSettingsStore(), { wrapper });
it('should call store.setValue when setSetting is called', async () => {
const { result } = await renderHook(() => useSettingsStore(), { wrapper });
act(() => {
result.current.setSetting(SettingScope.User, 'ui.theme', 'dark');
@@ -144,12 +144,12 @@ describe('SettingsContext', () => {
);
});
it('should throw error if used outside provider', () => {
it('should throw error if used outside provider', async () => {
const onError = vi.fn();
// Suppress console.error (React logs error boundary info)
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
render(
await render(
<ErrorBoundary onError={onError}>
<TestHarness />
</ErrorBoundary>,

View File

@@ -51,12 +51,11 @@ const TestComponent = ({ onColor }: { onColor: (c: string) => void }) => {
describe('TerminalContext', () => {
it('should parse OSC 11 response', async () => {
const handleColor = vi.fn();
const { waitUntilReady, unmount } = render(
const { waitUntilReady, unmount } = await render(
<TerminalProvider>
<TestComponent onColor={handleColor} />
</TerminalProvider>,
);
await waitUntilReady();
await act(async () => {
mockStdin.emit('data', '\x1b]11;rgb:ffff/ffff/ffff\x1b\\');
@@ -71,12 +70,11 @@ describe('TerminalContext', () => {
it('should handle partial chunks', async () => {
const handleColor = vi.fn();
const { waitUntilReady, unmount } = render(
const { waitUntilReady, unmount } = await render(
<TerminalProvider>
<TestComponent onColor={handleColor} />
</TerminalProvider>,
);
await waitUntilReady();
await act(async () => {
mockStdin.emit('data', '\x1b]11;rgb:0000/');

View File

@@ -7,7 +7,6 @@
import { act } from 'react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook } from '../../test-utils/render.js';
import { waitFor } from '../../test-utils/async.js';
import { ToolActionsProvider, useToolActions } from './ToolActionsContext.js';
import {
type Config,
@@ -81,7 +80,7 @@ describe('ToolActionsContext', () => {
);
it('publishes to MessageBus for tools with correlationId', async () => {
const { result } = renderHook(() => useToolActions(), { wrapper });
const { result } = await renderHook(() => useToolActions(), { wrapper });
await result.current.confirm(
'modern-call',
@@ -99,7 +98,7 @@ describe('ToolActionsContext', () => {
});
it('handles cancel by calling confirm with Cancel outcome', async () => {
const { result } = renderHook(() => useToolActions(), { wrapper });
const { result } = await renderHook(() => useToolActions(), { wrapper });
await result.current.cancel('modern-call');
@@ -112,20 +111,26 @@ describe('ToolActionsContext', () => {
});
it('resolves IDE diffs for edit tools when in IDE mode', async () => {
let deferredIdeClient: { resolve: (c: IdeClient) => void };
const mockIdeClient = {
isDiffingEnabled: vi.fn().mockReturnValue(true),
resolveDiffFromCli: vi.fn(),
addStatusChangeListener: vi.fn(),
removeStatusChangeListener: vi.fn(),
} as unknown as IdeClient;
vi.mocked(IdeClient.getInstance).mockResolvedValue(mockIdeClient);
vi.mocked(IdeClient.getInstance).mockImplementation(
() =>
new Promise((resolve) => {
deferredIdeClient = { resolve };
}),
);
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
const { result } = renderHook(() => useToolActions(), { wrapper });
const { result } = await renderHook(() => useToolActions(), { wrapper });
// Wait for IdeClient initialization in useEffect
await act(async () => {
await waitFor(() => expect(IdeClient.getInstance).toHaveBeenCalled());
// Give React a chance to update state
await new Promise((resolve) => setTimeout(resolve, 0));
deferredIdeClient.resolve(mockIdeClient);
});
await result.current.confirm(
@@ -146,6 +151,8 @@ describe('ToolActionsContext', () => {
it('updates isDiffingEnabled when IdeClient status changes', async () => {
let statusListener: () => void = () => {};
let deferredIdeClient: { resolve: (c: IdeClient) => void };
const mockIdeClient = {
isDiffingEnabled: vi.fn().mockReturnValue(false),
addStatusChangeListener: vi.fn().mockImplementation((listener) => {
@@ -154,15 +161,18 @@ describe('ToolActionsContext', () => {
removeStatusChangeListener: vi.fn(),
} as unknown as IdeClient;
vi.mocked(IdeClient.getInstance).mockResolvedValue(mockIdeClient);
vi.mocked(IdeClient.getInstance).mockImplementation(
() =>
new Promise((resolve) => {
deferredIdeClient = { resolve };
}),
);
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
const { result } = renderHook(() => useToolActions(), { wrapper });
const { result } = await renderHook(() => useToolActions(), { wrapper });
// Wait for initialization
await act(async () => {
await waitFor(() => expect(IdeClient.getInstance).toHaveBeenCalled());
await new Promise((resolve) => setTimeout(resolve, 0));
deferredIdeClient.resolve(mockIdeClient);
});
expect(result.current.isDiffingEnabled).toBe(false);
@@ -202,7 +212,7 @@ describe('ToolActionsContext', () => {
} as unknown as SerializableConfirmationDetails,
};
const { result } = renderHook(() => useToolActions(), {
const { result } = await renderHook(() => useToolActions(), {
wrapper: ({ children }) => (
<ToolActionsProvider config={mockConfig} toolCalls={[legacyTool]}>
{children}