From ddf080608ee378863124139caab4d99f0d5ee781 Mon Sep 17 00:00:00 2001 From: yiliang114 Date: Thu, 9 Jul 2026 23:12:08 +0800 Subject: [PATCH] test(cli): stabilize flaky UI tests --- .../cli/src/ui/hooks/useStatusLine.test.ts | 21 +++++++++++++------ .../src/ui/voice/voice-keyterms-race.test.ts | 14 +++++++------ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/ui/hooks/useStatusLine.test.ts b/packages/cli/src/ui/hooks/useStatusLine.test.ts index 08af6ce9c9..77eb8a0c66 100644 --- a/packages/cli/src/ui/hooks/useStatusLine.test.ts +++ b/packages/cli/src/ui/hooks/useStatusLine.test.ts @@ -4,7 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { + describe, + it, + expect, + vi, + beforeAll, + beforeEach, + afterEach, +} from 'vitest'; import { renderHook, act } from '@testing-library/react'; import * as child_process from 'child_process'; import { StreamingState } from '../types.js'; @@ -148,7 +156,12 @@ describe('useStatusLine', () => { // Must import dynamically after mocks are set up let useStatusLine: typeof import('./useStatusLine.js').useStatusLine; - beforeEach(async () => { + beforeAll(async () => { + const mod = await import('./useStatusLine.js'); + useStatusLine = mod.useStatusLine; + }, 20_000); + + beforeEach(() => { vi.useFakeTimers(); vi.clearAllMocks(); lastExecCommand = undefined; @@ -199,10 +212,6 @@ describe('useStatusLine', () => { mockConfig.getContentGeneratorConfig.mockReturnValue({ contextWindowSize: 131072, }); - - // Dynamic import to get fresh module after mocks - const mod = await import('./useStatusLine.js'); - useStatusLine = mod.useStatusLine; }); afterEach(() => { diff --git a/packages/cli/src/ui/voice/voice-keyterms-race.test.ts b/packages/cli/src/ui/voice/voice-keyterms-race.test.ts index 6d0951890a..3997320aeb 100644 --- a/packages/cli/src/ui/voice/voice-keyterms-race.test.ts +++ b/packages/cli/src/ui/voice/voice-keyterms-race.test.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { afterEach, describe, expect, it, vi } from 'vitest'; +import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'; import * as fs from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; @@ -72,6 +72,11 @@ function makeSettings(workspaceDir: string): LoadedSettings { describe('buildVoiceKeyterms race checks', () => { let workspaceDir = ''; + let buildVoiceKeyterms: typeof import('./voice-keyterms.js').buildVoiceKeyterms; + + beforeAll(async () => { + ({ buildVoiceKeyterms } = await import('./voice-keyterms.js')); + }, 20_000); afterEach(() => { raceState.target = ''; @@ -95,7 +100,6 @@ describe('buildVoiceKeyterms race checks', () => { raceState.replacementText = 'SwapSecret\n'; raceState.enabled = true; - const { buildVoiceKeyterms } = await import('./voice-keyterms.js'); const terms = buildVoiceKeyterms(makeSettings(workspaceDir)); expect(raceState.swapped).toBe(true); @@ -112,15 +116,14 @@ describe('buildVoiceKeyterms race checks', () => { fs.utimesSync(target, new Date(0), new Date(0)); raceState.target = fs.realpathSync(target); - raceState.replacementText = 'EvilTerm\n'; + raceState.replacementText = 'EvilTermWithDifferentSize\n'; raceState.enabled = true; raceState.mode = 'overwrite'; - const { buildVoiceKeyterms } = await import('./voice-keyterms.js'); const terms = buildVoiceKeyterms(makeSettings(workspaceDir)); expect(raceState.swapped).toBe(true); - expect(terms).not.toContain('EvilTerm'); + expect(terms).not.toContain('EvilTermWithDifferentSize'); expect(terms).toContain('TypeScript'); // globals only }); @@ -133,7 +136,6 @@ describe('buildVoiceKeyterms race checks', () => { raceState.oversizedReadText = `HugeTermMarker\n${'x'.repeat(64 * 1024)}`; - const { buildVoiceKeyterms } = await import('./voice-keyterms.js'); const terms = buildVoiceKeyterms(makeSettings(workspaceDir)); expect(terms).not.toContain('HugeTermMarker');