Files
spoon/apps/expo/tests/component/ui-primitives.test.tsx
Gabriel Brown 42f95530de
Build and Push Next App / quality (push) Successful in 1m27s
Build and Push Next App / build-next (push) Successful in 3m58s
Update expo application
2026-06-22 12:13:02 -04:00

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();
});
});