mirror of
https://github.com/whscullin/apple2js.git
synced 2024-01-12 14:14:38 +00:00
52a1c65fe4
* Create a FileChooser component using showOpenFilePicker Before, `FileModal` always used a file input control for selecting local files. This allowed the emulator to read from the file, but precluded writing back to the file. With this change, the `FileModal` delegates to the new `FileChooser` component. The `FileChooser` will use `showOpenFilePicker` if it is available and a regular file input if it's not. Using `showOpenFilePicker` has the advantage of allowing the emulator to write back to the file (if the user grants permission). While the emulator does not yet take advantage of this write capability, that will come. * Addressed comments * useState() instead of direct DOM manipulation * backed out eslint changes in favor of suppressing the warning
129 lines
5.1 KiB
TypeScript
129 lines
5.1 KiB
TypeScript
/**
|
|
* @jest-environment ./test/env/jsdom-with-backdoors
|
|
*/
|
|
|
|
import 'jest';
|
|
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;
|
|
|
|
// 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={() => { }} />);
|
|
|
|
expect(container).not.toBeNull();
|
|
});
|
|
|
|
it('should use the file input element', async () => {
|
|
render(<FileChooser control='input' onChange={() => { }} />);
|
|
|
|
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(() => {
|
|
expect(window.showOpenFilePicker).not.toBeDefined();
|
|
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={() => { }} />);
|
|
|
|
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);
|
|
});
|
|
});
|
|
});
|
|
}); |