125 lines
3.4 KiB
TypeScript
125 lines
3.4 KiB
TypeScript
import { Alert } from 'react-native';
|
|
import { fireEvent, render, screen } from '@testing-library/react';
|
|
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
|
|
import { SpoonStatusBadge } from '../../src/components/spoons/spoon-status-badge';
|
|
import { ThreadStatusBadge } from '../../src/components/threads/thread-status-badge';
|
|
import { ConfirmButton } from '../../src/components/ui/confirm-button';
|
|
import { PillTabs } from '../../src/components/ui/pill-tabs';
|
|
import { SheetSelect } from '../../src/components/ui/sheet-select';
|
|
import { DiffPreview } from '../../src/components/workspace/diff-preview';
|
|
|
|
describe('mobile UI primitives', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
test('PillTabs renders labels and changes selection', () => {
|
|
const onChange = vi.fn();
|
|
|
|
render(
|
|
<PillTabs
|
|
tabs={[
|
|
{ label: 'Overview', value: 'overview' },
|
|
{ label: 'Settings', value: 'settings' },
|
|
]}
|
|
value='overview'
|
|
onChange={onChange}
|
|
/>,
|
|
);
|
|
|
|
fireEvent.click(screen.getByText('Settings'));
|
|
|
|
expect(screen.getByText('Overview')).toBeTruthy();
|
|
expect(onChange).toHaveBeenCalledWith('settings');
|
|
});
|
|
|
|
test('SheetSelect opens and chooses an option', () => {
|
|
const onChange = vi.fn();
|
|
|
|
render(
|
|
<SheetSelect
|
|
label='Provider'
|
|
options={[
|
|
{ label: 'OpenAI', value: 'openai' },
|
|
{ label: 'Anthropic', value: 'anthropic' },
|
|
]}
|
|
value='openai'
|
|
onChange={onChange}
|
|
/>,
|
|
);
|
|
|
|
fireEvent.click(screen.getByText('OpenAI'));
|
|
fireEvent.click(screen.getByText('Anthropic'));
|
|
|
|
expect(onChange).toHaveBeenCalledWith('anthropic');
|
|
});
|
|
|
|
test('SheetSelect respects disabled state', () => {
|
|
const onChange = vi.fn();
|
|
|
|
render(
|
|
<SheetSelect
|
|
disabled
|
|
label='Provider'
|
|
options={[{ label: 'OpenAI', value: 'openai' }]}
|
|
value='openai'
|
|
onChange={onChange}
|
|
/>,
|
|
);
|
|
|
|
fireEvent.click(screen.getByText('OpenAI'));
|
|
|
|
expect(onChange).not.toHaveBeenCalled();
|
|
});
|
|
|
|
test('ConfirmButton delegates confirmation to Alert', () => {
|
|
const onConfirm = vi.fn();
|
|
|
|
render(
|
|
<ConfirmButton
|
|
confirmLabel='Delete'
|
|
message='Delete this?'
|
|
title='Delete'
|
|
onConfirm={onConfirm}
|
|
>
|
|
Remove
|
|
</ConfirmButton>,
|
|
);
|
|
|
|
fireEvent.click(screen.getByText('Remove'));
|
|
const calls = vi.mocked(Alert.alert).mock.calls;
|
|
const confirm = calls[0]?.[2]?.[1];
|
|
confirm?.onPress?.();
|
|
|
|
expect(onConfirm).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
test('DiffPreview truncates and expands long diffs', () => {
|
|
const diff = Array.from({ length: 125 }, (_, index) =>
|
|
index % 2 === 0 ? `+added ${index}` : `-removed ${index}`,
|
|
).join('\n');
|
|
|
|
render(<DiffPreview content={diff} initialLines={3} />);
|
|
|
|
expect(screen.getByText('+added 0')).toBeTruthy();
|
|
expect(screen.queryByText('-removed 5')).toBeNull();
|
|
|
|
fireEvent.click(screen.getByText('Show 122 more lines'));
|
|
|
|
expect(screen.getByText('-removed 5')).toBeTruthy();
|
|
});
|
|
|
|
test('status badges render readable labels', () => {
|
|
render(
|
|
<>
|
|
<SpoonStatusBadge status='up_to_date' />
|
|
<ThreadStatusBadge status='waiting_for_user' />
|
|
</>,
|
|
);
|
|
|
|
expect(screen.getByText('up to date')).toBeTruthy();
|
|
expect(screen.getByText('waiting for user')).toBeTruthy();
|
|
});
|
|
});
|