test: increase test timeouts and improve mock consistency across component and utility suites

This commit is contained in:
reikernodd 2026-05-15 21:57:56 +01:00
parent 20aee68a36
commit 5134e9a528
5 changed files with 102 additions and 67 deletions

View File

@ -67,9 +67,11 @@ describe('modifiers-napi', () => {
const mod = await loadModule()
await mod.prewarm()
const callsAfterFirst = dlopenCalls
await mod.prewarm()
expect(dlopenCalls).toBe(1)
expect(dlopenCalls).toBe(callsAfterFirst)
})
test('returns false when ffi loading fails on darwin', async () => {

View File

@ -9,17 +9,25 @@ import { renderToString } from '../../../utils/staticRender.js';
import { AutofixProgress } from '../AutofixProgress.js';
describe('AutofixProgress', () => {
test('renders target in header', async () => {
const out = await renderToString(<AutofixProgress phase="detecting" target="acme/myrepo#42" />);
expect(out).toContain('acme/myrepo#42');
expect(out).toContain('Autofix PR');
});
test(
'renders target in header',
async () => {
const out = await renderToString(<AutofixProgress phase="detecting" target="acme/myrepo#42" />);
expect(out).toContain('acme/myrepo#42');
expect(out).toContain('Autofix PR');
},
{ timeout: 10000 },
);
test('detecting phase shows arrow on detecting step', async () => {
const out = await renderToString(<AutofixProgress phase="detecting" target="owner/repo#1" />);
// detecting step should be active (→) and later steps pending (·)
expect(out).toContain('Detecting repository');
});
test(
'detecting phase shows arrow on detecting step',
async () => {
const out = await renderToString(<AutofixProgress phase="detecting" target="owner/repo#1" />);
// detecting step should be active (→) and later steps pending (·)
expect(out).toContain('Detecting repository');
},
{ timeout: 10000 },
);
test('checking_eligibility phase renders eligibility label', async () => {
const out = await renderToString(<AutofixProgress phase="checking_eligibility" target="owner/repo#2" />);

View File

@ -69,20 +69,28 @@ afterEach(() => {
});
describe('useFrustrationDetection', () => {
test('stays closed without frustration signals', async () => {
const result = await renderDetection({ messages: [] });
test(
'stays closed without frustration signals',
async () => {
const result = await renderDetection({ messages: [] });
expect(result.state).toBe('closed');
expect(typeof result.handleTranscriptSelect).toBe('function');
});
expect(result.state).toBe('closed');
expect(typeof result.handleTranscriptSelect).toBe('function');
},
{ timeout: 10000 },
);
test('opens a transcript prompt for repeated API errors', async () => {
const result = await renderDetection({
messages: [apiError('a'), apiError('b')],
});
test(
'opens a transcript prompt for repeated API errors',
async () => {
const result = await renderDetection({
messages: [apiError('a'), apiError('b')],
});
expect(result.state).toBe('transcript_prompt');
});
expect(result.state).toBe('transcript_prompt');
},
{ timeout: 10000 },
);
test('does not prompt while loading, prompting, blocked by another survey, dismissed, or policy-denied', async () => {
const messages = [apiError('a'), apiError('b')];

View File

@ -1,54 +1,55 @@
import { describe, expect, test, mock } from 'bun:test';
import * as React from 'react';
import { renderToString } from '../../utils/staticRender.js';
mock.module('react', () => ({
...React,
useState: (initial: any) => [typeof initial === 'function' ? initial() : initial, () => {}],
useEffect: () => {},
useRef: (initial: any) => ({ current: initial }),
useCallback: (fn: any) => fn,
useMemo: (fn: any) => fn(),
useContext: () => ({}),
useLayoutEffect: () => {}, // Add a simple mock for useLayoutEffect
}));
import { ConsoleOAuthFlow } from '../ConsoleOAuthFlow.js';
// Mock dependencies
// Mock dependencies MUST be called before importing the component
mock.module('src/services/analytics/index.js', () => ({
logEvent: () => {},
}));
mock.module('../utils/localLlm.js', () => ({
checkOllamaStatus: async () => true,
listOllamaModels: async () => ['llama3.1', 'mistral'],
pullOllamaModel: async () => {},
pingUrl: async () => true,
mock.module('../cli/handlers/auth.js', () => ({
installOAuthTokens: async () => {},
}));
mock.module('../utils/browser.js', () => ({
openBrowser: async () => {},
}));
mock.module('../utils/log.js', () => ({
logError: () => {},
}));
mock.module('../utils/settings/settings.js', () => ({
getSettings_DEPRECATED: () => ({}),
updateSettingsForSource: () => {},
}));
mock.module('../utils/auth.js', () => ({
getOauthAccountInfo: async () => ({}),
validateForceLoginOrg: async () => true,
updateSettingsForSource: async () => {},
}));
mock.module('../services/oauth/index.js', () => ({
OAuthService: {
start: async () => {},
getAuthorizationUrl: async () => 'https://example.com/auth',
exchangeCode: async () => ({ accessToken: 'abc' }),
},
}));
mock.module('@anthropic/ink', () => ({
useTerminalNotification: () => () => {},
setClipboard: () => {},
Box: ({ children }: any) => <div>{children}</div>,
Link: ({ children }: any) => <div>{children}</div>,
Text: ({ children }: any) => <div>{children}</div>,
KeyboardShortcutHint: () => null,
mock.module('../utils/auth.js', () => ({
getOauthAccountInfo: async () => ({ email: 'test@example.com' }),
validateForceLoginOrg: async () => true,
}));
mock.module('../services/notifier.js', () => ({
sendNotification: () => {},
}));
mock.module('./CustomSelect/select.js', () => ({
Select: () => null,
}));
mock.module('./Spinner.js', () => ({
Spinner: () => null,
}));
mock.module('./TextInput.js', () => ({
default: () => null,
}));
mock.module('../hooks/useTerminalSize.js', () => ({
@ -59,16 +60,19 @@ mock.module('../keybindings/useKeybinding.js', () => ({
useKeybinding: () => {},
}));
describe('ConsoleOAuthFlow', () => {
test('renders initial login method selection', () => {
const onDone = () => {};
const element = ConsoleOAuthFlow({ onDone }) as React.ReactElement;
import { ConsoleOAuthFlow } from '../ConsoleOAuthFlow.js';
// The component returns a React element tree
// We expect it to contain the title and options
const str = JSON.stringify(element);
expect(str).toContain('Select login method');
expect(str).toContain('Anthropic Console');
expect(str).toContain('Local LLM');
});
describe('ConsoleOAuthFlow', () => {
test(
'renders initial login method selection',
async () => {
const onDone = () => {};
const out = await renderToString(<ConsoleOAuthFlow onDone={onDone} />);
expect(out).toContain('Select login method');
expect(out).toContain('Local LLM');
expect(out).toContain('Gemini API');
},
{ timeout: 10000 },
);
});

View File

@ -4,7 +4,12 @@ let nativePrewarmCalls = 0
let nativeReturnValue = false
let nativeShouldThrow = false
const MODIFIERS_TEST_GUARD = '__MODIFIERS_TEST_ACTIVE__'
const nativeIsModifierPressed = mock((modifier: string) => {
if (!(globalThis as any)[MODIFIERS_TEST_GUARD]) {
return false
}
if (nativeShouldThrow) {
throw new Error('native modifier failure')
}
@ -13,7 +18,9 @@ const nativeIsModifierPressed = mock((modifier: string) => {
mock.module('modifiers-napi', () => ({
prewarm: async () => {
nativePrewarmCalls++
if ((globalThis as any)[MODIFIERS_TEST_GUARD]) {
nativePrewarmCalls++
}
},
isModifierPressed: nativeIsModifierPressed,
}))
@ -25,6 +32,7 @@ async function loadModule() {
}
beforeEach(() => {
;(globalThis as any)[MODIFIERS_TEST_GUARD] = true
nativePrewarmCalls = 0
nativeReturnValue = false
nativeShouldThrow = false
@ -36,6 +44,11 @@ beforeEach(() => {
})
afterEach(() => {
;(globalThis as any)[MODIFIERS_TEST_GUARD] = false
nativePrewarmCalls = 0
nativeReturnValue = false
nativeShouldThrow = false
nativeIsModifierPressed.mockClear()
Object.defineProperty(process, 'platform', {
value: originalPlatform,
configurable: true,