mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 08:47:44 +00:00
fix: resolve three flaky tests (#7058)
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
import type { Mock } from 'vitest';
|
import type { Mock } from 'vitest';
|
||||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
|
import { waitFor } from '@testing-library/react';
|
||||||
import { renderWithProviders } from '../test-utils/render.js';
|
import { renderWithProviders } from '../test-utils/render.js';
|
||||||
import { AppWrapper as App } from './App.js';
|
import { AppWrapper as App } from './App.js';
|
||||||
import type {
|
import type {
|
||||||
@@ -399,10 +400,11 @@ describe('App UI', () => {
|
|||||||
);
|
);
|
||||||
currentUnmount = unmount;
|
currentUnmount = unmount;
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
// Wait for any potential async operations to complete
|
||||||
|
await waitFor(() => {
|
||||||
expect(spawn).not.toHaveBeenCalled();
|
expect(spawn).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should show a success message when update succeeds', async () => {
|
it('should show a success message when update succeeds', async () => {
|
||||||
mockedIsGitRepository.mockResolvedValue(false);
|
mockedIsGitRepository.mockResolvedValue(false);
|
||||||
@@ -427,12 +429,13 @@ describe('App UI', () => {
|
|||||||
|
|
||||||
updateEventEmitter.emit('update-success', info);
|
updateEventEmitter.emit('update-success', info);
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
// Wait for the success message to appear
|
||||||
|
await waitFor(() => {
|
||||||
expect(lastFrame()).toContain(
|
expect(lastFrame()).toContain(
|
||||||
'Update successful! The new version will be used on your next run.',
|
'Update successful! The new version will be used on your next run.',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should show an error message when update fails', async () => {
|
it('should show an error message when update fails', async () => {
|
||||||
mockedIsGitRepository.mockResolvedValue(false);
|
mockedIsGitRepository.mockResolvedValue(false);
|
||||||
@@ -457,12 +460,13 @@ describe('App UI', () => {
|
|||||||
|
|
||||||
updateEventEmitter.emit('update-failed', info);
|
updateEventEmitter.emit('update-failed', info);
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
// Wait for the error message to appear
|
||||||
|
await waitFor(() => {
|
||||||
expect(lastFrame()).toContain(
|
expect(lastFrame()).toContain(
|
||||||
'Automatic update failed. Please try updating manually',
|
'Automatic update failed. Please try updating manually',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should show an error message when spawn fails', async () => {
|
it('should show an error message when spawn fails', async () => {
|
||||||
mockedIsGitRepository.mockResolvedValue(false);
|
mockedIsGitRepository.mockResolvedValue(false);
|
||||||
@@ -489,12 +493,13 @@ describe('App UI', () => {
|
|||||||
// which is what should be emitted when a spawn error occurs elsewhere.
|
// which is what should be emitted when a spawn error occurs elsewhere.
|
||||||
updateEventEmitter.emit('update-failed', info);
|
updateEventEmitter.emit('update-failed', info);
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
// Wait for the error message to appear
|
||||||
|
await waitFor(() => {
|
||||||
expect(lastFrame()).toContain(
|
expect(lastFrame()).toContain(
|
||||||
'Automatic update failed. Please try updating manually',
|
'Automatic update failed. Please try updating manually',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should not auto-update if GEMINI_CLI_DISABLE_AUTOUPDATER is true', async () => {
|
it('should not auto-update if GEMINI_CLI_DISABLE_AUTOUPDATER is true', async () => {
|
||||||
mockedIsGitRepository.mockResolvedValue(false);
|
mockedIsGitRepository.mockResolvedValue(false);
|
||||||
@@ -519,11 +524,12 @@ describe('App UI', () => {
|
|||||||
);
|
);
|
||||||
currentUnmount = unmount;
|
currentUnmount = unmount;
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
// Wait for any potential async operations to complete
|
||||||
|
await waitFor(() => {
|
||||||
expect(spawn).not.toHaveBeenCalled();
|
expect(spawn).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should display active file when available', async () => {
|
it('should display active file when available', async () => {
|
||||||
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { renderWithProviders } from '../../test-utils/render.js';
|
import { renderWithProviders } from '../../test-utils/render.js';
|
||||||
import { waitFor } from '@testing-library/react';
|
import { waitFor, act } from '@testing-library/react';
|
||||||
import type { InputPromptProps } from './InputPrompt.js';
|
import type { InputPromptProps } from './InputPrompt.js';
|
||||||
import { InputPrompt } from './InputPrompt.js';
|
import { InputPrompt } from './InputPrompt.js';
|
||||||
import type { TextBuffer } from './shared/text-buffer.js';
|
import type { TextBuffer } from './shared/text-buffer.js';
|
||||||
@@ -1412,12 +1412,20 @@ describe('InputPrompt', () => {
|
|||||||
const { stdin, stdout, unmount } = renderWithProviders(
|
const { stdin, stdout, unmount } = renderWithProviders(
|
||||||
<InputPrompt {...props} />,
|
<InputPrompt {...props} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Enter reverse search mode with Ctrl+R
|
||||||
|
act(() => {
|
||||||
stdin.write('\x12');
|
stdin.write('\x12');
|
||||||
|
});
|
||||||
await wait();
|
await wait();
|
||||||
|
|
||||||
// Verify reverse search is active
|
// Verify reverse search is active
|
||||||
expect(stdout.lastFrame()).toContain('(r:)');
|
expect(stdout.lastFrame()).toContain('(r:)');
|
||||||
|
|
||||||
|
// Press Tab to complete the highlighted entry
|
||||||
|
act(() => {
|
||||||
stdin.write('\t');
|
stdin.write('\t');
|
||||||
|
});
|
||||||
|
|
||||||
await waitFor(
|
await waitFor(
|
||||||
() => {
|
() => {
|
||||||
|
|||||||
@@ -201,12 +201,14 @@ describe('useAtCompletion', () => {
|
|||||||
});
|
});
|
||||||
await realFileSearch.initialize();
|
await realFileSearch.initialize();
|
||||||
|
|
||||||
|
// Mock that returns results immediately but we'll control timing with fake timers
|
||||||
const mockFileSearch: FileSearch = {
|
const mockFileSearch: FileSearch = {
|
||||||
initialize: vi.fn().mockResolvedValue(undefined),
|
initialize: vi.fn().mockResolvedValue(undefined),
|
||||||
search: vi.fn().mockImplementation(async (...args) => {
|
search: vi
|
||||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
.fn()
|
||||||
return realFileSearch.search(...args);
|
.mockImplementation(async (...args) =>
|
||||||
}),
|
realFileSearch.search(...args),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
vi.spyOn(FileSearchFactory, 'create').mockReturnValue(mockFileSearch);
|
vi.spyOn(FileSearchFactory, 'create').mockReturnValue(mockFileSearch);
|
||||||
|
|
||||||
@@ -216,33 +218,42 @@ describe('useAtCompletion', () => {
|
|||||||
{ initialProps: { pattern: 'a' } },
|
{ initialProps: { pattern: 'a' } },
|
||||||
);
|
);
|
||||||
|
|
||||||
// Wait for the initial (slow) search to complete
|
// Wait for the initial search to complete (using real timers)
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(result.current.suggestions.map((s) => s.value)).toEqual([
|
expect(result.current.suggestions.map((s) => s.value)).toEqual([
|
||||||
'a.txt',
|
'a.txt',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Now, rerender to trigger the second search
|
// Now switch to fake timers for precise control of the loading behavior
|
||||||
rerender({ pattern: 'b' });
|
vi.useFakeTimers();
|
||||||
|
|
||||||
// Wait for the loading indicator to appear
|
// Trigger the second search
|
||||||
await waitFor(() => {
|
act(() => {
|
||||||
expect(result.current.isLoadingSuggestions).toBe(true);
|
rerender({ pattern: 'b' });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Suggestions should be cleared while loading
|
// Initially, loading should be false (before 200ms timer)
|
||||||
|
expect(result.current.isLoadingSuggestions).toBe(false);
|
||||||
|
|
||||||
|
// Advance time by exactly 200ms to trigger the loading state
|
||||||
|
act(() => {
|
||||||
|
vi.advanceTimersByTime(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Now loading should be true and suggestions should be cleared
|
||||||
|
expect(result.current.isLoadingSuggestions).toBe(true);
|
||||||
expect(result.current.suggestions).toEqual([]);
|
expect(result.current.suggestions).toEqual([]);
|
||||||
|
|
||||||
// Wait for the final (slow) search to complete
|
// Switch back to real timers for the final waitFor
|
||||||
await waitFor(
|
vi.useRealTimers();
|
||||||
() => {
|
|
||||||
|
// Wait for the search results to be processed
|
||||||
|
await waitFor(() => {
|
||||||
expect(result.current.suggestions.map((s) => s.value)).toEqual([
|
expect(result.current.suggestions.map((s) => s.value)).toEqual([
|
||||||
'b.txt',
|
'b.txt',
|
||||||
]);
|
]);
|
||||||
},
|
});
|
||||||
{ timeout: 1000 },
|
|
||||||
); // Increase timeout for the slow search
|
|
||||||
|
|
||||||
expect(result.current.isLoadingSuggestions).toBe(false);
|
expect(result.current.isLoadingSuggestions).toBe(false);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user