mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-18 13:23:33 +08:00
Contributors and coding agents need one local command that both reports and enforces the quality contract. This change turns the PR gate into the shared verification entrypoint, adds path-selected local lanes, tightens coverage accounting around changed lines, and documents the repair loop in contributor and agent-facing guidance. Constraint: Ordinary PR verification must stay non-live and runnable without provider credentials Constraint: Coverage policy updates in this commit require maintainer approval before push/merge Rejected: Keep quality guidance only in docs | agents need executable scripts and AGENTS.md instructions to follow the loop consistently Confidence: high Scope-risk: broad Directive: Do not bypass `bun run verify` for production changes; fix failed lanes and coverage reports instead of lowering thresholds Tested: bun run check:policy Tested: ALLOW_CLI_CORE_CHANGE=1 ALLOW_COVERAGE_BASELINE_CHANGE=1 bun run verify Not-tested: live provider baseline; no provider credentials were required for this non-live PR gate
114 lines
3.2 KiB
TypeScript
114 lines
3.2 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react'
|
|
import '@testing-library/jest-dom'
|
|
import { SessionTaskBar } from './SessionTaskBar'
|
|
import { useCLITaskStore } from '../../stores/cliTaskStore'
|
|
|
|
vi.mock('../../api/cliTasks', () => ({
|
|
cliTasksApi: {
|
|
getTasksForList: vi.fn(),
|
|
resetTaskList: vi.fn(async () => ({ ok: true })),
|
|
},
|
|
}))
|
|
|
|
vi.mock('../../i18n', () => ({
|
|
useTranslation: () => (key: string) => {
|
|
const translations: Record<string, string> = {
|
|
'tasks.title': 'Tasks',
|
|
'tasks.dismissCompleted': 'Hide completed tasks',
|
|
}
|
|
|
|
return translations[key] ?? key
|
|
},
|
|
}))
|
|
|
|
describe('SessionTaskBar', () => {
|
|
beforeEach(() => {
|
|
useCLITaskStore.setState({
|
|
sessionId: 'session-1',
|
|
tasks: [],
|
|
expanded: false,
|
|
completedAndDismissed: false,
|
|
dismissedCompletionKey: null,
|
|
})
|
|
})
|
|
|
|
afterEach(() => {
|
|
cleanup()
|
|
useCLITaskStore.getState().clearTasks()
|
|
})
|
|
|
|
it('only shows the dismiss button once every task is completed', () => {
|
|
act(() => {
|
|
useCLITaskStore.getState().setTasksFromTodos([
|
|
{ content: 'first', status: 'completed' },
|
|
{ content: 'second', status: 'in_progress', activeForm: 'working' },
|
|
])
|
|
})
|
|
|
|
act(() => {
|
|
render(<SessionTaskBar />)
|
|
})
|
|
|
|
expect(screen.getByText('Tasks')).toBeInTheDocument()
|
|
expect(screen.queryByRole('button', { name: 'Hide completed tasks' })).toBeNull()
|
|
})
|
|
|
|
it('hides the bar after dismissing a completed task set', async () => {
|
|
act(() => {
|
|
useCLITaskStore.getState().setTasksFromTodos([
|
|
{ content: 'first', status: 'completed' },
|
|
{ content: 'second', status: 'completed' },
|
|
])
|
|
})
|
|
|
|
act(() => {
|
|
render(<SessionTaskBar />)
|
|
})
|
|
|
|
await act(async () => {
|
|
fireEvent.click(screen.getByRole('button', { name: 'Hide completed tasks' }))
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(screen.queryByText('Tasks')).toBeNull()
|
|
expect(useCLITaskStore.getState().tasks).toEqual([])
|
|
})
|
|
|
|
it('shows the bar again for a new task cycle after a previous completed set was dismissed', async () => {
|
|
act(() => {
|
|
useCLITaskStore.getState().setTasksFromTodos([
|
|
{ content: 'first', status: 'completed' },
|
|
])
|
|
})
|
|
|
|
act(() => {
|
|
render(<SessionTaskBar />)
|
|
})
|
|
|
|
await act(async () => {
|
|
fireEvent.click(screen.getByRole('button', { name: 'Hide completed tasks' }))
|
|
await Promise.resolve()
|
|
})
|
|
expect(screen.queryByText('Tasks')).toBeNull()
|
|
|
|
act(() => {
|
|
useCLITaskStore.getState().setTasksFromTodos([
|
|
{ content: 'next task', status: 'in_progress', activeForm: 'running next task' },
|
|
])
|
|
})
|
|
|
|
expect(screen.getByText('Tasks')).toBeInTheDocument()
|
|
expect(screen.queryByRole('button', { name: 'Hide completed tasks' })).toBeNull()
|
|
|
|
act(() => {
|
|
useCLITaskStore.getState().setTasksFromTodos([
|
|
{ content: 'next task', status: 'completed' },
|
|
])
|
|
})
|
|
|
|
expect(screen.getByText('Tasks')).toBeInTheDocument()
|
|
expect(screen.getByRole('button', { name: 'Hide completed tasks' })).toBeInTheDocument()
|
|
})
|
|
})
|