apple2js/test/components/FileChooser.spec.tsx
Ian Flanigan 04ae0327c2
Add the recommended eslint plugins for TypeScript (#121)
This adds both the recommended TypeScript checks, plus the recommended
TypeScript checks that require type checking.  This latter addition
means that eslint essentially has to compile all of the TypeScript in
the project, causing it to be slower. This isn't much of a problem in
VS Code because there's a lot of caching being done, but it's clearly
slower when run on the commandline.

All of the errors are either fixed or suppressed.  Some errors are
suppressed because fixing them would be too laborious for the little
value gained.

The eslint config is also slightly refactored to separate the strictly
TypeScript checks from the JavaScript checks.
2022-05-31 08:38:40 -07:00

133 lines
5.2 KiB
TypeScript

/**
* @jest-environment ./test/env/jsdom-with-backdoors
*/
import { h } from 'preact';
import { fireEvent, render, screen, waitFor } from '@testing-library/preact';
import 'test/env/jsdom-with-backdoors';
import { FileChooser, FileChooserProps } from '../../js/components/FileChooser';
type ShowOpenFilePicker = typeof window.showOpenFilePicker;
const FAKE_FILE_HANDLE = {
kind: 'file',
name: 'file name',
createWritable: jest.fn(),
getFile: jest.fn(),
isSameEntry: jest.fn(),
queryPermission: jest.fn(),
requestPermission: jest.fn(),
isFile: true,
isDirectory: false,
} as const;
const NOP = () => { /* do nothing */ };
// eslint-disable-next-line no-undef
const EMPTY_FILE_LIST = backdoors.newFileList();
const FAKE_FILE = new File([], 'fake');
describe('FileChooser', () => {
describe('input-based chooser', () => {
it('should be instantiable', () => {
const { container } = render(<FileChooser control='input' onChange={NOP} />);
expect(container).not.toBeNull();
});
it('should use the file input element', async () => {
render(<FileChooser control='input' onChange={NOP} />);
const inputElement = await screen.findByRole('button') as HTMLInputElement;
expect(inputElement.type).toBe('file');
});
it('should fire a callback with empty list when no files are selected', async () => {
const onChange = jest.fn();
render(<FileChooser control='input' onChange={onChange} />);
const inputElement = await screen.findByRole('button') as HTMLInputElement;
inputElement.files = EMPTY_FILE_LIST;
fireEvent.change(inputElement);
await waitFor(() => {
expect(onChange).toBeCalledWith([]);
});
});
it('should fire a callback with a file handle when a file is selected', async () => {
const onChange = jest.fn<ReturnType<FileChooserProps['onChange']>, Parameters<FileChooserProps['onChange']>>();
render(<FileChooser control='input' onChange={onChange} />);
const inputElement = await screen.findByRole('button') as HTMLInputElement;
// eslint-disable-next-line no-undef
inputElement.files = backdoors.newFileList(FAKE_FILE);
fireEvent.change(inputElement);
await waitFor(async () => {
expect(onChange).toHaveBeenCalled();
const handleList = onChange.mock.calls[0][0];
expect(handleList).toHaveLength(1);
const handle = handleList[0];
expect(handle.kind).toBe('file');
expect(handle.name).toBe(FAKE_FILE.name);
expect(handle.isWritable).toBe(false);
await expect(handle.getFile()).resolves.toBe(FAKE_FILE);
await expect(handle.createWritable()).rejects.toEqual('File not writable.');
});
});
});
describe('picker-base chooser', () => {
const mockFilePicker = jest.fn<ReturnType<ShowOpenFilePicker>, Parameters<ShowOpenFilePicker>>();
beforeEach(() => {
if (typeof window.showOpenFilePicker !== 'undefined') {
throw new Error('window.showOpenFilePicker not undefined');
}
window.showOpenFilePicker = mockFilePicker as unknown as ShowOpenFilePicker;
mockFilePicker.mockReset();
});
afterEach(() => {
window.showOpenFilePicker = undefined as unknown as typeof window.showOpenFilePicker;
});
it('should be instantiable', () => {
const { container } = render(<FileChooser control='picker' onChange={NOP} />);
expect(container).not.toBeNull();
});
it('should fire a callback with empty list when no files are selected', async () => {
mockFilePicker.mockResolvedValueOnce([]);
const onChange = jest.fn();
render(<FileChooser control='picker' onChange={onChange} />);
fireEvent.click(await screen.findByText('Choose File'));
await waitFor(() => {
expect(mockFilePicker).toBeCalled();
expect(onChange).toBeCalledWith([]);
});
});
it('should fire a callback with a file handle when a file is selected', async () => {
mockFilePicker.mockResolvedValueOnce([FAKE_FILE_HANDLE]);
const onChange = jest.fn<ReturnType<FileChooserProps['onChange']>, Parameters<FileChooserProps['onChange']>>();
render(<FileChooser control='picker' onChange={onChange} />);
fireEvent.click(await screen.findByText('Choose File'));
await waitFor(() => {
expect(mockFilePicker).toBeCalled();
expect(onChange).toHaveBeenCalled();
const handleList = onChange.mock.calls[0][0];
expect(handleList).toHaveLength(1);
const handle = handleList[0];
expect(handle.kind).toBe(FAKE_FILE_HANDLE.kind);
expect(handle.name).toBe(FAKE_FILE_HANDLE.name);
expect(handle.isWritable).toBe(true);
});
});
});
});