Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions src/components/CodeBlock.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* CodeBlock.test.tsx — fenced code block: highlight + copy. */

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { render, screen, waitFor, cleanup } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

const copyMock = vi.fn()
vi.mock('./Common', async () => {
const actual = await vi.importActual<typeof import('./Common')>('./Common')
return { ...actual, copyToClipboard: (...a: any[]) => copyMock(...a) }
})

import { CodeBlock } from './CodeBlock'

beforeEach(() => { vi.clearAllMocks() })
afterEach(() => cleanup())

describe('CodeBlock', () => {
it('renders monochrome with no language', () => {
render(<CodeBlock code="just text" lang={null} />)
expect(screen.getByText('just text')).toBeTruthy()
expect(document.querySelector('pre')!.getAttribute('data-lang')).toBe('')
expect(document.querySelector('.code-block-lang')).toBeNull()
})

it('renders an unknown language as monochrome', () => {
render(<CodeBlock code="x = 1" lang="rust" />)
expect(screen.getByText('x = 1')).toBeTruthy()
expect(document.querySelector('pre')!.getAttribute('data-lang')).toBe('')
})

it('highlights bash with the lang label', () => {
render(<CodeBlock code={'# comment\ncurl -X POST "url" 42'} lang="sh" />)
expect(document.querySelector('.code-block-lang')!.textContent).toBe('bash')
expect(document.querySelector('.tok-comment')).toBeTruthy()
expect(document.querySelector('.tok-keyword')).toBeTruthy()
expect(document.querySelector('.tok-flag')).toBeTruthy()
expect(document.querySelector('.tok-number')).toBeTruthy()
expect(document.querySelector('.tok-string')).toBeTruthy()
})

it('highlights json keys, strings, numbers, bools', () => {
render(<CodeBlock code={'{"k": "v", "n": 3, "b": true, "z": null}'} lang="jsonc" />)
expect(document.querySelector('.tok-key')).toBeTruthy()
expect(document.querySelector('.tok-string')).toBeTruthy()
expect(document.querySelector('.tok-number')).toBeTruthy()
expect(document.querySelector('.tok-bool')).toBeTruthy()
})

it('highlights yaml keys, comments, numbers, bools, strings', () => {
render(<CodeBlock code={'# c\nkey: "val"\nnum: 5\nflag: yes'} lang="yml" />)
expect(document.querySelector('.code-block-lang')!.textContent).toBe('yaml')
expect(document.querySelector('.tok-comment')).toBeTruthy()
expect(document.querySelector('.tok-key')).toBeTruthy()
expect(document.querySelector('.tok-number')).toBeTruthy()
expect(document.querySelector('.tok-bool')).toBeTruthy()
expect(document.querySelector('.tok-string')).toBeTruthy()
})

it('copies code and flashes "Copied!"', async () => {
copyMock.mockResolvedValue(true)
render(<CodeBlock code="echo hi" lang="bash" />)
const btn = screen.getByRole('button', { name: 'Copy code to clipboard' })
await userEvent.click(btn)
expect(copyMock).toHaveBeenCalledWith('echo hi')
await waitFor(() => expect(screen.getByText('Copied!')).toBeTruthy())
})

it('does not flash when the copy fails', async () => {
copyMock.mockResolvedValue(false)
render(<CodeBlock code="echo hi" lang="bash" />)
await userEvent.click(screen.getByRole('button', { name: 'Copy code to clipboard' }))
expect(screen.queryByText('Copied!')).toBeNull()
})
})
114 changes: 114 additions & 0 deletions src/components/Common.pills.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/* Common.pills.test.tsx — render coverage for the small Common.tsx
* presentational exports (StatusPill, RolePill, ScopePill, Sparkline,
* Skeleton) and the PromptCard copy paths. */

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { render, screen, waitFor, cleanup } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

import {
StatusPill,
RolePill,
ScopePill,
Sparkline,
Skeleton,
PromptCard,
} from './Common'

let writeText: ReturnType<typeof vi.fn>
beforeEach(() => {
vi.clearAllMocks()
writeText = vi.fn().mockResolvedValue(undefined)
Object.defineProperty(navigator, 'clipboard', {
value: { writeText }, configurable: true, writable: true,
})
})
afterEach(() => cleanup())

describe('StatusPill', () => {
it('maps running→healthy and deploying→building', () => {
const { rerender } = render(<StatusPill status="running" />)
expect(document.querySelector('.status-pill.healthy')!.textContent).toBe('healthy')
rerender(<StatusPill status="deploying" />)
expect(document.querySelector('.status-pill.building')!.textContent).toBe('building')
})
it('renders failed, expired (→stopped) and a fallback', () => {
const { rerender } = render(<StatusPill status="failed" />)
expect(document.querySelector('.status-pill.failed')).toBeTruthy()
rerender(<StatusPill status="expired" />)
expect(document.querySelector('.status-pill.stopped')!.textContent).toBe('expired')
rerender(<StatusPill status={'stopped' as any} />)
expect(document.querySelector('.status-pill.stopped')).toBeTruthy()
})
})

describe('RolePill', () => {
it('adds the role modifier class only for owner/admin', () => {
const { rerender } = render(<RolePill role={'owner' as any} />)
expect(document.querySelector('.role-pill.owner')).toBeTruthy()
rerender(<RolePill role={'admin' as any} />)
expect(document.querySelector('.role-pill.admin')).toBeTruthy()
rerender(<RolePill role={'member' as any} />)
expect(document.querySelector('.role-pill')!.className.trim()).toBe('role-pill')
})
})

describe('ScopePill', () => {
it('renders write, agent, and read variants', () => {
const { rerender } = render(<ScopePill scope="write" />)
expect(screen.getByText(/clickable/)).toBeTruthy()
rerender(<ScopePill scope="agent" />)
expect(screen.getByText(/agent surface/)).toBeTruthy()
rerender(<ScopePill scope="read" />)
expect(screen.getByText(/mirror/)).toBeTruthy()
})
})

describe('Sparkline + Skeleton', () => {
it('renders a polyline for the given points', () => {
render(<Sparkline points={[1, 5, 3, 8]} />)
const poly = document.querySelector('svg.sparkline polyline')
expect(poly).toBeTruthy()
expect(poly!.getAttribute('points')!.split(' ').length).toBe(4)
})
it('renders a single-point sparkline without dividing by zero', () => {
render(<Sparkline points={[4]} />)
expect(document.querySelector('svg.sparkline polyline')).toBeTruthy()
})
it('renders a skeleton with default and custom sizes', () => {
const { rerender } = render(<Skeleton />)
expect(document.querySelector('span.skel')).toBeTruthy()
rerender(<Skeleton width={50} height={8} />)
expect(document.querySelector('span.skel')).toBeTruthy()
})
})

describe('PromptCard copy', () => {
it('copies the fallback prompt when no promptText is given', async () => {
render(<PromptCard title="Make a DB" prompt="provision pg" method="POST" endpoint="/db/new" hint="hint" />)
await userEvent.click(screen.getByTestId('copy-prompt'))
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('/db/new'))
await waitFor(() => expect(screen.getByTestId('copy-prompt').textContent).toContain('copied'))
})

it('copies explicit promptText and the curl command', async () => {
render(<PromptCard title="Get" prompt="x" promptText="custom prompt" method="GET" endpoint="/healthz" danger />)
await userEvent.click(screen.getByTestId('copy-prompt'))
expect(writeText).toHaveBeenLastCalledWith('custom prompt')
await userEvent.click(screen.getByTestId('copy-curl'))
expect(writeText).toHaveBeenLastCalledWith(expect.stringContaining('curl -X GET'))
await waitFor(() => expect(screen.getByTestId('copy-curl').textContent).toContain('copied'))
})

it('warns and does not flash when copy fails', async () => {
writeText.mockRejectedValue(new Error('denied'))
// Force the execCommand fallback to also fail.
;(document as any).execCommand = vi.fn(() => false)
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
render(<PromptCard title="t" prompt="p" method="DELETE" endpoint="/x" />)
await userEvent.click(screen.getByTestId('copy-curl'))
await waitFor(() => expect(warn).toHaveBeenCalled())
expect(screen.getByTestId('copy-curl').textContent).toContain('copy curl')
warn.mockRestore()
})
})
Loading
Loading