mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-15 12:55:51 +00:00
Squash-merge of feat/autofix-pr-test (69 commits) onto upstream/main with -X ours strategy (upstream as authoritative for content conflicts). Key features brought in from fork: - LocalMemoryRecall + VaultHttpFetch tools (end-to-end wired) - /local-memory, /local-vault, /memory-stores, /skill-store interactive panels - /agents-platform, /schedule, /vault command scaffolding - /login: switch / replace / remove of workspace API key - statusline refactor (built-in status row, /statusline as info command) - autofix-pr command + workflow Conflict resolutions (upstream-wins): - 10 .js command stubs kept from upstream (alongside fork's .ts implementations) - src/components/BuiltinStatusLine.tsx accepted upstream's deletion (fork's wire-up references in StatusLine.tsx will be cleaned up next) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
193 lines
6.2 KiB
TypeScript
193 lines
6.2 KiB
TypeScript
/**
|
|
* Regression tests for launchCommand factory (H2 finding).
|
|
* Tests MUST fail before the factory is created, then pass after.
|
|
*/
|
|
import { describe, test, expect, mock } from 'bun:test'
|
|
import { logMock } from '../../../../tests/mocks/log.js'
|
|
|
|
mock.module('src/utils/log.ts', logMock)
|
|
mock.module('bun:bundle', () => ({ feature: () => false }))
|
|
|
|
import React from 'react'
|
|
import type {
|
|
LocalJSXCommandCall,
|
|
LocalJSXCommandOnDone,
|
|
} from '../../../types/command.js'
|
|
import type { LaunchCommandOptions } from '../launchCommand.js'
|
|
|
|
let launchCommand: typeof import('../launchCommand.js').launchCommand
|
|
|
|
// Lazy import so mocks are in place first
|
|
const loadModule = async () => {
|
|
const mod = await import('../launchCommand.js')
|
|
launchCommand = mod.launchCommand
|
|
}
|
|
|
|
// Simple parsed union for tests
|
|
type TestParsed =
|
|
| { action: 'greet'; name: string }
|
|
| { action: 'invalid'; reason: string }
|
|
|
|
type TestViewProps = { greeting: string }
|
|
|
|
const TestView: React.FC<TestViewProps> = ({ greeting }) =>
|
|
React.createElement('span', null, greeting)
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
type AnyOpts = LaunchCommandOptions<any, any>
|
|
|
|
const makeOpts = (overrides: Partial<AnyOpts> = {}): AnyOpts => ({
|
|
commandName: 'test-cmd',
|
|
parseArgs: (
|
|
raw: string,
|
|
): TestParsed | { action: 'invalid'; reason: string } => {
|
|
if (raw.trim() === '') return { action: 'invalid', reason: 'empty args' }
|
|
return { action: 'greet', name: raw.trim() }
|
|
},
|
|
dispatch: async (parsed: TestParsed, onDone: LocalJSXCommandOnDone) => {
|
|
if (parsed.action !== 'greet') return null
|
|
onDone(`Hello ${parsed.name}`)
|
|
return { greeting: `Hello, ${parsed.name}!` }
|
|
},
|
|
View: TestView as React.FC<unknown>,
|
|
errorView: (msg: string) =>
|
|
React.createElement('span', null, `Error: ${msg}`),
|
|
...overrides,
|
|
})
|
|
|
|
describe('launchCommand factory', () => {
|
|
test('module loads and exports launchCommand function', async () => {
|
|
await loadModule()
|
|
expect(typeof launchCommand).toBe('function')
|
|
})
|
|
|
|
test('launchCommand returns a LocalJSXCommandCall function', async () => {
|
|
await loadModule()
|
|
const call = launchCommand(makeOpts())
|
|
expect(typeof call).toBe('function')
|
|
})
|
|
|
|
test('happy path: parseArgs + dispatch succeed → View rendered, onDone called', async () => {
|
|
await loadModule()
|
|
const call: LocalJSXCommandCall = launchCommand(makeOpts())
|
|
const onDone = mock(() => {})
|
|
const result = await call(onDone, {} as never, 'Alice')
|
|
expect(result).not.toBeNull()
|
|
expect(onDone).toHaveBeenCalledTimes(1)
|
|
const [msg] = onDone.mock.calls[0] as unknown as [string]
|
|
expect(msg).toContain('Alice')
|
|
})
|
|
|
|
test('parseArgs returns invalid → errorView returned, onDone called with reason', async () => {
|
|
await loadModule()
|
|
const call: LocalJSXCommandCall = launchCommand(makeOpts())
|
|
const onDone = mock(() => {})
|
|
const result = await call(onDone, {} as never, '')
|
|
expect(onDone).toHaveBeenCalledTimes(1)
|
|
const [msg] = onDone.mock.calls[0] as unknown as [string]
|
|
expect(msg).toContain('empty args')
|
|
// errorView should return something (not null from dispatch)
|
|
expect(result).not.toBeUndefined()
|
|
})
|
|
|
|
test('dispatch throws → errorView returned, onDone called with error message', async () => {
|
|
await loadModule()
|
|
const call: LocalJSXCommandCall = launchCommand(
|
|
makeOpts({
|
|
dispatch: async () => {
|
|
throw new Error('dispatch failed')
|
|
},
|
|
}),
|
|
)
|
|
const onDone = mock(() => {})
|
|
const result = await call(onDone, {} as never, 'Bob')
|
|
expect(onDone).toHaveBeenCalledTimes(1)
|
|
const [msg] = onDone.mock.calls[0] as unknown as [string]
|
|
expect(msg).toContain('dispatch failed')
|
|
expect(result).not.toBeUndefined()
|
|
})
|
|
|
|
test('dispatch returns null → null returned from call', async () => {
|
|
await loadModule()
|
|
const call: LocalJSXCommandCall = launchCommand(
|
|
makeOpts({
|
|
dispatch: async (_parsed, onDone) => {
|
|
onDone('done')
|
|
return null
|
|
},
|
|
}),
|
|
)
|
|
const onDone = mock(() => {})
|
|
const result = await call(onDone, {} as never, 'Charlie')
|
|
expect(result).toBeNull()
|
|
})
|
|
|
|
test('onDispatchError hook is called when dispatch throws', async () => {
|
|
await loadModule()
|
|
const onDispatchError = mock((_err: unknown) => {})
|
|
const call: LocalJSXCommandCall = launchCommand(
|
|
makeOpts({
|
|
dispatch: async () => {
|
|
throw new Error('boom')
|
|
},
|
|
onDispatchError,
|
|
}),
|
|
)
|
|
const onDone = mock(() => {})
|
|
await call(onDone, {} as never, 'Dave')
|
|
expect(onDispatchError).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
test('invalid args: onDone display option is system', async () => {
|
|
await loadModule()
|
|
const call: LocalJSXCommandCall = launchCommand(makeOpts())
|
|
const capturedOpts: unknown[] = []
|
|
const onDone = mock((_msg?: string, opts?: unknown) => {
|
|
capturedOpts.push(opts)
|
|
})
|
|
await call(onDone, {} as never, '')
|
|
expect(capturedOpts[0]).toEqual({ display: 'system' })
|
|
})
|
|
|
|
test('dispatch error: onDone is called exactly once with commandName in message', async () => {
|
|
await loadModule()
|
|
const call: LocalJSXCommandCall = launchCommand(
|
|
makeOpts({
|
|
commandName: 'my-special-cmd',
|
|
dispatch: async () => {
|
|
throw new Error('network timeout')
|
|
},
|
|
}),
|
|
)
|
|
const onDone = mock(() => {})
|
|
await call(onDone, {} as never, 'Eve')
|
|
expect(onDone).toHaveBeenCalledTimes(1)
|
|
const [msg] = onDone.mock.calls[0] as unknown as [string]
|
|
expect(msg).toContain('my-special-cmd')
|
|
expect(msg).toContain('network timeout')
|
|
})
|
|
|
|
test('errorView receives the error message string', async () => {
|
|
await loadModule()
|
|
const capturedMsgs: string[] = []
|
|
const call: LocalJSXCommandCall = launchCommand(
|
|
makeOpts({
|
|
dispatch: async () => {
|
|
throw new Error('specific-error-text')
|
|
},
|
|
errorView: (msg: string) => {
|
|
capturedMsgs.push(msg)
|
|
return React.createElement('span', null, msg)
|
|
},
|
|
}),
|
|
)
|
|
await call(
|
|
mock(() => {}),
|
|
{} as never,
|
|
'Frank',
|
|
)
|
|
expect(capturedMsgs).toHaveLength(1)
|
|
expect(capturedMsgs[0]).toBe('specific-error-text')
|
|
})
|
|
})
|