Files
DECNET/decnet_web/src/components/CanaryTokens/FileDropListView.test.tsx
anti c5cbe084cb refactor(decnet_web/CanaryTokens): extract list views
Lift the three tab bodies — tokens, blobs, file drops — into
their own files. Each takes plain props (data + the operations
its rows need), so the page shell stops mixing tab markup with
data plumbing.

- New CanaryTokens/TokenListView.tsx (text search + state/scope
  filter selectors + flat row grid; visibleTokens memo lives here
  now). Exports StateFilter / ScopeFilter union types so the page
  can declare its filter useState with the right shape.
- New CanaryTokens/BlobListView.tsx (delete refused while a token
  references a blob; ref count badge reuses the disabled button).
- New CanaryTokens/FileDropListView.tsx (CLEAR LIST hidden when
  the local log is empty).
- Three companion tests cover empty states, filter behavior,
  delete refused-vs-allowed, and the per-tab callback wiring.

Wiring into CanaryTokens.tsx + the hook lands next.
2026-05-09 05:16:18 -04:00

49 lines
1.6 KiB
TypeScript

import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { FileDropListView } from './FileDropListView';
import type { FileDropEntry } from './FileDropModal';
const entry = (overrides: Partial<FileDropEntry> = {}): FileDropEntry => ({
id: 'fd-1',
decky_name: 'decoy-99',
topology_id: null,
path: '/tmp/payload.bin',
size_bytes: 4096,
filename: 'payload.bin',
mode: 0o644,
mtime_offset: 0,
dropped_at: '2026-05-09T11:00:00Z',
...overrides,
});
describe('FileDropListView', () => {
it('shows the empty hint when fileDrops is []', () => {
render(<FileDropListView fileDrops={[]} onClear={() => {}} />);
expect(screen.getByText(/No file drops in this browser yet/)).toBeInTheDocument();
});
it('hides CLEAR LIST when there are no entries', () => {
render(<FileDropListView fileDrops={[]} onClear={() => {}} />);
expect(screen.queryByText('CLEAR LIST')).not.toBeInTheDocument();
});
it('renders one row per drop with its path + decky name', () => {
render(
<FileDropListView fileDrops={[entry()]} onClear={() => {}} />,
);
expect(screen.getByText('decoy-99')).toBeInTheDocument();
expect(screen.getByText('/tmp/payload.bin')).toBeInTheDocument();
});
it('CLEAR LIST invokes onClear', async () => {
const onClear = vi.fn();
const user = userEvent.setup();
render(
<FileDropListView fileDrops={[entry()]} onClear={onClear} />,
);
await user.click(screen.getByText('CLEAR LIST'));
expect(onClear).toHaveBeenCalled();
});
});