Files
Codewalkers/apps/web/src/components/hq/HQSections.test.tsx
Lukas May 40ec85deb8 fix: resolve all failing frontend tests
- Fix vitest.config.ts React alias to point to local node_modules
  instead of nonexistent ancestor path (../../../../node_modules/react)
- Remove stale HQWaitingForInputSection tests (component was deleted
  in e8d332e0 but test cases were left behind)
2026-03-07 00:14:44 +01:00

339 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// @vitest-environment happy-dom
import '@testing-library/jest-dom/vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import { vi, describe, it, expect, beforeEach } from 'vitest'
const mockNavigate = vi.fn()
vi.mock('@tanstack/react-router', () => ({
useNavigate: () => mockNavigate,
Link: ({ to, children, className }: { to: string; children: React.ReactNode; className?: string }) => (
<a href={to} className={className}>{children}</a>
),
}))
// Mock formatRelativeTime to return a predictable string
vi.mock('@/lib/utils', () => ({
cn: (...classes: string[]) => classes.filter(Boolean).join(' '),
formatRelativeTime: () => '5 minutes ago',
}))
import { HQNeedsReviewSection } from './HQNeedsReviewSection'
import { HQNeedsApprovalSection } from './HQNeedsApprovalSection'
import { HQResolvingConflictsSection } from './HQResolvingConflictsSection'
import { HQBlockedSection } from './HQBlockedSection'
import { HQEmptyState } from './HQEmptyState'
const since = new Date(Date.now() - 5 * 60 * 1000).toISOString()
// ─── HQNeedsReviewSection ────────────────────────────────────────────────────
describe('HQNeedsReviewSection', () => {
beforeEach(() => vi.clearAllMocks())
it('renders section heading "Needs Review"', () => {
render(<HQNeedsReviewSection initiatives={[]} phases={[]} />)
expect(screen.getByText('Needs Review')).toBeInTheDocument()
})
it('2a: shows initiative name, "Content ready for review", "Review" CTA navigates correctly', () => {
render(
<HQNeedsReviewSection
initiatives={[
{ initiativeId: 'init-1', initiativeName: 'Init One', since },
]}
phases={[]}
/>
)
expect(screen.getByText('Init One')).toBeInTheDocument()
expect(screen.getByText('Content ready for review')).toBeInTheDocument()
fireEvent.click(screen.getByRole('button', { name: /^review$/i }))
expect(mockNavigate).toHaveBeenCalledWith({
to: '/initiatives/$id',
params: { id: 'init-1' },
search: { tab: 'review' },
})
})
it('2b: shows initiative phase, "Phase execution complete — review diff", "Review Diff" navigates correctly', () => {
render(
<HQNeedsReviewSection
initiatives={[]}
phases={[
{
phaseId: 'ph-1',
phaseName: 'Phase One',
initiativeId: 'init-1',
initiativeName: 'Init One',
since,
},
]}
/>
)
expect(screen.getByText('Init One Phase One')).toBeInTheDocument()
expect(screen.getByText('Phase execution complete — review diff')).toBeInTheDocument()
fireEvent.click(screen.getByRole('button', { name: /review diff/i }))
expect(mockNavigate).toHaveBeenCalledWith({
to: '/initiatives/$id',
params: { id: 'init-1' },
search: { tab: 'review' },
})
})
it('when only initiatives provided, only 2a cards render', () => {
render(
<HQNeedsReviewSection
initiatives={[{ initiativeId: 'init-1', initiativeName: 'Init One', since }]}
phases={[]}
/>
)
expect(screen.getByText('Content ready for review')).toBeInTheDocument()
expect(screen.queryByText('Phase execution complete — review diff')).not.toBeInTheDocument()
})
it('when only phases provided, only 2b cards render', () => {
render(
<HQNeedsReviewSection
initiatives={[]}
phases={[
{
phaseId: 'ph-1',
phaseName: 'Phase One',
initiativeId: 'init-1',
initiativeName: 'Init One',
since,
},
]}
/>
)
expect(screen.getByText('Phase execution complete — review diff')).toBeInTheDocument()
expect(screen.queryByText('Content ready for review')).not.toBeInTheDocument()
})
})
// ─── HQNeedsApprovalSection ──────────────────────────────────────────────────
describe('HQNeedsApprovalSection', () => {
beforeEach(() => vi.clearAllMocks())
it('renders "Needs Approval to Continue" heading', () => {
render(<HQNeedsApprovalSection items={[]} />)
expect(screen.getByText('Needs Approval to Continue')).toBeInTheDocument()
})
it('shows singular phase count: "1 phase awaiting approval"', () => {
render(
<HQNeedsApprovalSection
items={[
{ initiativeId: 'init-1', initiativeName: 'Init One', pendingPhaseCount: 1, since },
]}
/>
)
expect(screen.getByText('Plan ready — 1 phase awaiting approval')).toBeInTheDocument()
})
it('shows plural phase count: "3 phases awaiting approval"', () => {
render(
<HQNeedsApprovalSection
items={[
{ initiativeId: 'init-1', initiativeName: 'Init One', pendingPhaseCount: 3, since },
]}
/>
)
expect(screen.getByText('Plan ready — 3 phases awaiting approval')).toBeInTheDocument()
})
it('"Review Plan" CTA navigates to /initiatives/$id?tab=plan', () => {
render(
<HQNeedsApprovalSection
items={[
{ initiativeId: 'init-1', initiativeName: 'Init One', pendingPhaseCount: 2, since },
]}
/>
)
fireEvent.click(screen.getByRole('button', { name: /review plan/i }))
expect(mockNavigate).toHaveBeenCalledWith({
to: '/initiatives/$id',
params: { id: 'init-1' },
search: { tab: 'plan' },
})
})
})
// ─── HQResolvingConflictsSection ──────────────────────────────────────────────
describe('HQResolvingConflictsSection', () => {
beforeEach(() => vi.clearAllMocks())
it('renders "Resolving Conflicts" heading', () => {
render(<HQResolvingConflictsSection items={[]} />)
expect(screen.getByText('Resolving Conflicts')).toBeInTheDocument()
})
it('shows initiative name and "Running" badge for running agent', () => {
render(
<HQResolvingConflictsSection
items={[
{
initiativeId: 'init-1',
initiativeName: 'My Initiative',
agentId: 'a1',
agentName: 'conflict-1234567890',
agentStatus: 'running',
since,
},
]}
/>
)
expect(screen.getByText('My Initiative')).toBeInTheDocument()
expect(screen.getByText('Running')).toBeInTheDocument()
})
it('shows "Needs Input" badge for waiting_for_input agent', () => {
render(
<HQResolvingConflictsSection
items={[
{
initiativeId: 'init-1',
initiativeName: 'My Initiative',
agentId: 'a1',
agentName: 'conflict-1234567890',
agentStatus: 'waiting_for_input',
since,
},
]}
/>
)
expect(screen.getByText('Needs Input')).toBeInTheDocument()
})
it('"View" CTA navigates to /initiatives/$id?tab=execution', () => {
render(
<HQResolvingConflictsSection
items={[
{
initiativeId: 'init-1',
initiativeName: 'My Initiative',
agentId: 'a1',
agentName: 'conflict-1234567890',
agentStatus: 'running',
since,
},
]}
/>
)
fireEvent.click(screen.getByRole('button', { name: /view/i }))
expect(mockNavigate).toHaveBeenCalledWith({
to: '/initiatives/$id',
params: { id: 'init-1' },
search: { tab: 'execution' },
})
})
})
// ─── HQBlockedSection ────────────────────────────────────────────────────────
describe('HQBlockedSection', () => {
beforeEach(() => vi.clearAllMocks())
it('renders "Blocked" heading', () => {
render(<HQBlockedSection items={[]} />)
expect(screen.getByText('Blocked')).toBeInTheDocument()
})
it('shows initiative phase with "Blocked" badge', () => {
render(
<HQBlockedSection
items={[
{
phaseId: 'ph-1',
phaseName: 'Phase One',
initiativeId: 'init-1',
initiativeName: 'Init One',
lastMessage: null,
since,
},
]}
/>
)
expect(screen.getByText('Init One Phase One')).toBeInTheDocument()
// The "Blocked" badge - there will be one in the heading and one in the card
const badges = screen.getAllByText('Blocked')
expect(badges.length).toBeGreaterThanOrEqual(1)
})
it('shows lastMessage when non-null', () => {
render(
<HQBlockedSection
items={[
{
phaseId: 'ph-1',
phaseName: 'Phase One',
initiativeId: 'init-1',
initiativeName: 'Init One',
lastMessage: 'Something went wrong.',
since,
},
]}
/>
)
expect(screen.getByText('Something went wrong.')).toBeInTheDocument()
})
it('omits lastMessage when null', () => {
render(
<HQBlockedSection
items={[
{
phaseId: 'ph-1',
phaseName: 'Phase One',
initiativeId: 'init-1',
initiativeName: 'Init One',
lastMessage: null,
since,
},
]}
/>
)
expect(screen.queryByText('Something went wrong.')).not.toBeInTheDocument()
})
it('"View" CTA navigates to /initiatives/$id?tab=execution', () => {
render(
<HQBlockedSection
items={[
{
phaseId: 'ph-1',
phaseName: 'Phase One',
initiativeId: 'init-1',
initiativeName: 'Init One',
lastMessage: null,
since,
},
]}
/>
)
fireEvent.click(screen.getByRole('button', { name: /view/i }))
expect(mockNavigate).toHaveBeenCalledWith({
to: '/initiatives/$id',
params: { id: 'init-1' },
search: { tab: 'execution' },
})
})
})
// ─── HQEmptyState ────────────────────────────────────────────────────────────
describe('HQEmptyState', () => {
it('renders "All clear." text', () => {
render(<HQEmptyState />)
expect(screen.getByText('All clear.')).toBeInTheDocument()
})
it('renders "Browse active work" link pointing to /initiatives', () => {
render(<HQEmptyState />)
const link = screen.getByRole('link', { name: /browse active work/i })
expect(link).toBeInTheDocument()
expect(link).toHaveAttribute('href', '/initiatives')
})
})