mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-14 10:35:11 +00:00
This adds use of sessionStorage for the documentation visibility toggle. This way, when multiple windows are open, the docs can be controlled separately in each window. Local storage is still used as the default value for new windows. Issue: https://github.com/pybricks/support/issues/807
199 lines
7.1 KiB
TypeScript
199 lines
7.1 KiB
TypeScript
// SPDX-License-Identifier: MIT
|
|
// Copyright (c) 2021-2022 The Pybricks Authors
|
|
|
|
import { Classes } from '@blueprintjs/core';
|
|
import { cleanup, fireEvent, waitFor } from '@testing-library/react';
|
|
import * as monaco from 'monaco-editor';
|
|
import React from 'react';
|
|
import { testRender, uuid } from '../../test';
|
|
import { FileMetadata } from '../fileStorage';
|
|
import { useFileStorageMetadata, useFileStoragePath } from '../fileStorage/hooks';
|
|
import { defined } from '../utils';
|
|
import Editor from './Editor';
|
|
import { editorActivateFile, editorCloseFile } from './actions';
|
|
|
|
const testFile: FileMetadata = {
|
|
uuid: uuid(0),
|
|
path: 'test.file',
|
|
sha256: '',
|
|
viewState: null,
|
|
};
|
|
jest.setTimeout(1000000);
|
|
afterEach(() => {
|
|
cleanup();
|
|
jest.resetAllMocks();
|
|
localStorage.clear();
|
|
sessionStorage.clear();
|
|
});
|
|
|
|
describe('Editor', () => {
|
|
describe('tabs', () => {
|
|
it('should dispatch activate action when tab is clicked', async () => {
|
|
jest.mocked(useFileStorageMetadata).mockReturnValue([testFile]);
|
|
jest.mocked(useFileStoragePath).mockReturnValue(testFile.path);
|
|
|
|
const [user, editor, dispatch] = testRender(<Editor />, {
|
|
editor: { openFileUuids: [testFile.uuid] },
|
|
});
|
|
|
|
await user.click(editor.getByRole('tab', { name: 'test.file' }));
|
|
|
|
expect(dispatch).toHaveBeenCalledWith(editorActivateFile(testFile.uuid));
|
|
});
|
|
|
|
it.each(['{Enter}', '{Space}'])(
|
|
'should dispatch activate action when %s key is pressed',
|
|
async (key) => {
|
|
jest.mocked(useFileStorageMetadata).mockReturnValue([testFile]);
|
|
jest.mocked(useFileStoragePath).mockReturnValue(testFile.path);
|
|
|
|
const [user, editor, dispatch] = testRender(<Editor />, {
|
|
editor: { openFileUuids: [testFile.uuid] },
|
|
});
|
|
|
|
await user.type(editor.getByRole('tab', { name: 'test.file' }), key);
|
|
|
|
expect(dispatch).toHaveBeenCalledWith(
|
|
editorActivateFile(testFile.uuid),
|
|
);
|
|
},
|
|
);
|
|
|
|
it('should dispatch close action when close button is clicked', async () => {
|
|
jest.mocked(useFileStorageMetadata).mockReturnValue([testFile]);
|
|
jest.mocked(useFileStoragePath).mockReturnValue(testFile.path);
|
|
|
|
const [user, editor, dispatch] = testRender(<Editor />, {
|
|
editor: { openFileUuids: [testFile.uuid] },
|
|
});
|
|
|
|
await user.click(editor.getByRole('button', { name: 'Close test.file' }));
|
|
|
|
expect(dispatch).toHaveBeenCalledWith(editorCloseFile(testFile.uuid));
|
|
});
|
|
|
|
it('should dispatch close action when delete button is pressed', async () => {
|
|
jest.mocked(useFileStorageMetadata).mockReturnValue([testFile]);
|
|
jest.mocked(useFileStoragePath).mockReturnValue(testFile.path);
|
|
|
|
const [user, editor, dispatch] = testRender(<Editor />, {
|
|
editor: { openFileUuids: [testFile.uuid] },
|
|
});
|
|
|
|
await user.type(editor.getByRole('tab', { name: 'test.file' }), '{Delete}');
|
|
|
|
expect(dispatch).toHaveBeenCalledWith(editorCloseFile(testFile.uuid));
|
|
});
|
|
|
|
it('should dispatch close action when tab is middle clicked', async () => {
|
|
jest.mocked(useFileStorageMetadata).mockReturnValue([testFile]);
|
|
jest.mocked(useFileStoragePath).mockReturnValue(testFile.path);
|
|
|
|
const [user, editor, dispatch] = testRender(<Editor />, {
|
|
editor: { openFileUuids: [testFile.uuid] },
|
|
});
|
|
|
|
await user.pointer({
|
|
keys: '[MouseMiddle]',
|
|
target: editor.getByRole('tab', { name: 'test.file' }),
|
|
});
|
|
|
|
expect(dispatch).toHaveBeenCalledWith(editorCloseFile(testFile.uuid));
|
|
});
|
|
});
|
|
|
|
describe('context menu', () => {
|
|
it('should hide the context menu when Escape is pressed', async () => {
|
|
const didCreate = new Promise<monaco.editor.ICodeEditor>((resolve) =>
|
|
monaco.editor.onDidCreateEditor(resolve),
|
|
);
|
|
|
|
const [user, editor] = testRender(<Editor />);
|
|
const code = await didCreate;
|
|
|
|
code.setModel(monaco.editor.createModel('test'));
|
|
|
|
expect(
|
|
editor.queryByRole('menu', { name: 'Editor context menu' }),
|
|
).toBeNull();
|
|
|
|
// HACK: monaco editor uses deprecated event fields (keyCode),
|
|
// so regular userEvent.type() doesn't work. testing library
|
|
// doesn't have ContextMenu in its keymap either.
|
|
fireEvent(
|
|
editor.getByRole('textbox', { name: /^Editor content/ }),
|
|
new KeyboardEvent('keydown', {
|
|
key: 'ContextMenu',
|
|
code: 'ContextMenu',
|
|
keyCode: 93,
|
|
}),
|
|
);
|
|
|
|
const contextMenu = await editor.findByRole('menu', {
|
|
name: 'Editor context menu',
|
|
});
|
|
|
|
expect(contextMenu).toBeInTheDocument();
|
|
|
|
// a11y: first item in menu should be focused when menu opens
|
|
await waitFor(() =>
|
|
expect(editor.getByRole('menuitem', { name: 'Copy' })).toHaveFocus(),
|
|
);
|
|
|
|
await user.keyboard('{Escape}');
|
|
|
|
await waitFor(() => expect(contextMenu).not.toBeInTheDocument());
|
|
|
|
// editor should be focused after context menu closes
|
|
expect(
|
|
editor.getByRole('textbox', { name: /^Editor content/ }),
|
|
).toHaveFocus();
|
|
});
|
|
|
|
it('should hide the context menu when clicking away', async () => {
|
|
const didCreate = new Promise<monaco.editor.ICodeEditor>((resolve) =>
|
|
monaco.editor.onDidCreateEditor(resolve),
|
|
);
|
|
|
|
const [user, editor] = testRender(<Editor />);
|
|
const code = await didCreate;
|
|
|
|
code.setModel(monaco.editor.createModel('test'));
|
|
|
|
expect(
|
|
editor.queryByRole('menu', { name: 'Editor context menu' }),
|
|
).toBeNull();
|
|
|
|
await user.pointer({
|
|
keys: '[MouseRight]',
|
|
target: editor.getByRole('textbox', { name: /^Editor content/ }),
|
|
});
|
|
|
|
const contextMenu = await editor.findByRole('menu', {
|
|
name: 'Editor context menu',
|
|
});
|
|
|
|
expect(contextMenu).toBeInTheDocument();
|
|
|
|
// a11y: first item in menu should be focused when menu opens
|
|
await waitFor(() =>
|
|
expect(editor.getByRole('menuitem', { name: 'Copy' })).toHaveFocus(),
|
|
);
|
|
const overlay = document
|
|
.getElementsByClassName(Classes.OVERLAY_BACKDROP)
|
|
.item(0);
|
|
|
|
defined(overlay);
|
|
|
|
await user.click(overlay);
|
|
|
|
await waitFor(() => expect(contextMenu).not.toBeInTheDocument());
|
|
|
|
// editor should be focused after context menu closes
|
|
expect(
|
|
editor.getByRole('textbox', { name: /^Editor content/ }),
|
|
).toHaveFocus();
|
|
});
|
|
});
|
|
});
|