mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 17:45:22 +00:00
test: add testRender()
This adds a testRender() helper function to simplify UI tests. Instead of mocking the redux store, we use the real thing.
This commit is contained in:
@@ -1,59 +1,29 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2021 The Pybricks Authors
|
||||
// Copyright (c) 2021-2022 The Pybricks Authors
|
||||
|
||||
import { I18nContext, I18nManager } from '@shopify/react-i18n';
|
||||
import {
|
||||
getByLabelText,
|
||||
render,
|
||||
screen,
|
||||
waitForElementToBeRemoved,
|
||||
} from '@testing-library/react';
|
||||
import { getByLabelText, waitForElementToBeRemoved } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Store } from 'redux';
|
||||
import { testRender } from '../../test';
|
||||
import AboutDialog from './AboutDialog';
|
||||
|
||||
it('should close when the button is clicked', () => {
|
||||
const store = {
|
||||
getState: jest.fn(() => ({ licenses: { list: null } })),
|
||||
dispatch: jest.fn(),
|
||||
subscribe: jest.fn(),
|
||||
} as unknown as Store;
|
||||
const i18n = new I18nManager({ locale: 'en' });
|
||||
const close = jest.fn();
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<I18nContext.Provider value={i18n}>
|
||||
<AboutDialog isOpen={true} onClose={() => close()} />
|
||||
</I18nContext.Provider>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByLabelText('Close'));
|
||||
const dialog = testRender(<AboutDialog isOpen={true} onClose={() => close()} />);
|
||||
|
||||
userEvent.click(dialog.getByLabelText('Close'));
|
||||
|
||||
expect(close).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should manage license dialog open/close', async () => {
|
||||
const store = {
|
||||
getState: jest.fn(() => ({ licenses: { list: null } })),
|
||||
dispatch: jest.fn(),
|
||||
subscribe: jest.fn(),
|
||||
} as unknown as Store;
|
||||
const i18n = new I18nManager({ locale: 'en' });
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<I18nContext.Provider value={i18n}>
|
||||
<AboutDialog isOpen={true} onClose={() => undefined} />
|
||||
</I18nContext.Provider>
|
||||
</Provider>,
|
||||
);
|
||||
const dialog = testRender(<AboutDialog isOpen={true} onClose={() => undefined} />);
|
||||
|
||||
userEvent.click(screen.getByText('Software Licenses'));
|
||||
userEvent.click(dialog.getByText('Software Licenses'));
|
||||
|
||||
expect(
|
||||
screen.getByText(
|
||||
dialog.getByText(
|
||||
`${process.env.REACT_APP_NAME} is built on open source software.`,
|
||||
{
|
||||
exact: false,
|
||||
@@ -67,7 +37,7 @@ it('should manage license dialog open/close', async () => {
|
||||
userEvent.click(getByLabelText(licenseDialog, 'Close'));
|
||||
|
||||
await waitForElementToBeRemoved(() =>
|
||||
screen.queryByText(
|
||||
dialog.queryByText(
|
||||
`${process.env.REACT_APP_NAME} is built on open source software.`,
|
||||
{
|
||||
exact: false,
|
||||
|
||||
+3
-20
@@ -1,27 +1,10 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2021 The Pybricks Authors
|
||||
// Copyright (c) 2021-2022 The Pybricks Authors
|
||||
|
||||
import { I18nContext, I18nManager } from '@shopify/react-i18n';
|
||||
import { render } from '@testing-library/react';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Store } from 'redux';
|
||||
import { RootState } from '../reducers';
|
||||
import { testRender } from '../../test';
|
||||
import App from './App';
|
||||
|
||||
it.each([false, true])('should render', (darkMode) => {
|
||||
const store = mock<Store<RootState>>({
|
||||
getState: () => mock<RootState>({ settings: { darkMode } }),
|
||||
});
|
||||
|
||||
const i18n = new I18nManager({ locale: 'en' });
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<I18nContext.Provider value={i18n}>
|
||||
<App />
|
||||
</I18nContext.Provider>
|
||||
</Provider>,
|
||||
);
|
||||
testRender(<App />, { settings: { darkMode } });
|
||||
});
|
||||
|
||||
+16
-66
@@ -1,102 +1,52 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2021 The Pybricks Authors
|
||||
// Copyright (c) 2021-2022 The Pybricks Authors
|
||||
|
||||
import { I18nContext, I18nManager } from '@shopify/react-i18n';
|
||||
import {
|
||||
RenderResult,
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
waitForElementToBeRemoved,
|
||||
} from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Store } from 'redux';
|
||||
import { testRender } from '../../test';
|
||||
import Editor from './Editor';
|
||||
|
||||
function getTextArea(): HTMLTextAreaElement {
|
||||
function getTextArea(editor: RenderResult): HTMLTextAreaElement {
|
||||
// the textarea in ace editor doesn't actually have any contents, but
|
||||
// it gets the focus for input.
|
||||
return screen.getByDisplayValue('') as HTMLTextAreaElement;
|
||||
return editor.getByDisplayValue('') as HTMLTextAreaElement;
|
||||
}
|
||||
|
||||
it('should focus the text area', () => {
|
||||
const store = {
|
||||
getState: jest.fn(() => ({
|
||||
editor: { current: null },
|
||||
settings: { darkMode: false, showDocs: false },
|
||||
})),
|
||||
dispatch: jest.fn(),
|
||||
subscribe: jest.fn(),
|
||||
} as unknown as Store;
|
||||
const i18n = new I18nManager({ locale: 'en' });
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<I18nContext.Provider value={i18n}>
|
||||
<Editor />
|
||||
</I18nContext.Provider>
|
||||
</Provider>,
|
||||
);
|
||||
const editor = testRender(<Editor />);
|
||||
|
||||
expect(getTextArea()).toHaveFocus();
|
||||
expect(getTextArea(editor)).toHaveFocus();
|
||||
});
|
||||
|
||||
describe('context menu', () => {
|
||||
it('should show the context menu', async () => {
|
||||
const store = {
|
||||
getState: jest.fn(() => ({
|
||||
editor: { current: null },
|
||||
settings: { darkMode: false, showDocs: false },
|
||||
})),
|
||||
dispatch: jest.fn(),
|
||||
subscribe: jest.fn(),
|
||||
} as unknown as Store;
|
||||
const i18n = new I18nManager({ locale: 'en' });
|
||||
const editor = testRender(<Editor />);
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<I18nContext.Provider value={i18n}>
|
||||
<Editor />
|
||||
</I18nContext.Provider>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
fireEvent.contextMenu(screen.getByText('Write your program here...'));
|
||||
fireEvent.contextMenu(editor.getByText('Write your program here...'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Copy')).toBeInTheDocument();
|
||||
expect(editor.getByText('Copy')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should hide the context menu when Escape is pressed', async () => {
|
||||
const store = {
|
||||
getState: jest.fn(() => ({
|
||||
editor: { current: null },
|
||||
settings: { darkMode: false, showDocs: false },
|
||||
})),
|
||||
dispatch: jest.fn(),
|
||||
subscribe: jest.fn(),
|
||||
} as unknown as Store;
|
||||
const i18n = new I18nManager({ locale: 'en' });
|
||||
const editor = testRender(<Editor />);
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<I18nContext.Provider value={i18n}>
|
||||
<Editor />
|
||||
</I18nContext.Provider>
|
||||
</Provider>,
|
||||
);
|
||||
fireEvent.contextMenu(editor.getByText('Write your program here...'));
|
||||
|
||||
fireEvent.contextMenu(screen.getByText('Write your program here...'));
|
||||
expect(editor.getByText('Copy')).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Copy')).toBeInTheDocument();
|
||||
userEvent.type(editor.getByText('Copy'), '{esc}');
|
||||
|
||||
userEvent.type(screen.getByText('Copy'), '{esc}');
|
||||
|
||||
await waitForElementToBeRemoved(() => screen.queryByText('Copy'));
|
||||
await waitForElementToBeRemoved(() => editor.queryByText('Copy'));
|
||||
|
||||
// editor should be focused after context menu closes
|
||||
expect(document.activeElement).toBe(getTextArea());
|
||||
expect(document.activeElement).toBe(getTextArea(editor));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,94 +1,55 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2021 The Pybricks Authors
|
||||
// Copyright (c) 2021-2022 The Pybricks Authors
|
||||
|
||||
import { I18nContext, I18nManager } from '@shopify/react-i18n';
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { fireEvent, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Store } from 'redux';
|
||||
import { testRender } from '../../test';
|
||||
import { BleConnectionState } from '../ble/reducers';
|
||||
import { RootState } from '../reducers';
|
||||
import StatusBar from './StatusBar';
|
||||
|
||||
it('should prevent browser context menu', () => {
|
||||
const store = {
|
||||
getState: jest.fn(() => ({
|
||||
ble: { connection: BleConnectionState.Disconnected, deviceName: '' },
|
||||
})),
|
||||
dispatch: jest.fn(),
|
||||
subscribe: jest.fn(),
|
||||
} as unknown as Store;
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<StatusBar />
|
||||
</Provider>,
|
||||
);
|
||||
const statusBar = testRender(<StatusBar />, {
|
||||
ble: { connection: BleConnectionState.Disconnected, deviceName: '' },
|
||||
});
|
||||
|
||||
expect(fireEvent.contextMenu(screen.getByRole('status'))).toBe(false);
|
||||
expect(fireEvent.contextMenu(statusBar.getByRole('status'))).toBe(false);
|
||||
});
|
||||
|
||||
it('should show popover when hub name is clicked', async () => {
|
||||
const testHubName = 'Test hub';
|
||||
|
||||
const store = mock<Store<RootState>>({
|
||||
getState: () =>
|
||||
mock<RootState>({
|
||||
ble: {
|
||||
connection: BleConnectionState.Connected,
|
||||
deviceName: testHubName,
|
||||
deviceType: 'hub type',
|
||||
deviceFirmwareVersion: 'v0.0.0',
|
||||
deviceLowBatteryWarning: false,
|
||||
deviceBatteryCharging: false,
|
||||
},
|
||||
}),
|
||||
const statusBar = testRender(<StatusBar />, {
|
||||
ble: {
|
||||
connection: BleConnectionState.Connected,
|
||||
deviceName: testHubName,
|
||||
deviceType: 'hub type',
|
||||
deviceFirmwareVersion: 'v0.0.0',
|
||||
deviceLowBatteryWarning: false,
|
||||
deviceBatteryCharging: false,
|
||||
},
|
||||
});
|
||||
|
||||
const i18n = new I18nManager({ locale: 'en' });
|
||||
userEvent.click(statusBar.getByText(testHubName));
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<I18nContext.Provider value={i18n}>
|
||||
<StatusBar />
|
||||
</I18nContext.Provider>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByText(testHubName));
|
||||
|
||||
await waitFor(() => screen.getByText('Connected to:'));
|
||||
await waitFor(() => statusBar.getByText('Connected to:'));
|
||||
});
|
||||
|
||||
it('should show popover when battery is clicked', async () => {
|
||||
const testHubName = 'Test hub';
|
||||
|
||||
const store = mock<Store<RootState>>({
|
||||
getState: () =>
|
||||
mock<RootState>({
|
||||
ble: {
|
||||
connection: BleConnectionState.Connected,
|
||||
deviceName: testHubName,
|
||||
deviceType: 'hub type',
|
||||
deviceFirmwareVersion: 'v0.0.0',
|
||||
deviceLowBatteryWarning: false,
|
||||
deviceBatteryCharging: false,
|
||||
},
|
||||
}),
|
||||
const statusBar = testRender(<StatusBar />, {
|
||||
ble: {
|
||||
connection: BleConnectionState.Connected,
|
||||
deviceName: testHubName,
|
||||
deviceType: 'hub type',
|
||||
deviceFirmwareVersion: 'v0.0.0',
|
||||
deviceLowBatteryWarning: false,
|
||||
deviceBatteryCharging: false,
|
||||
},
|
||||
});
|
||||
|
||||
const i18n = new I18nManager({ locale: 'en' });
|
||||
userEvent.click(statusBar.getByTitle('Battery'));
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<I18nContext.Provider value={i18n}>
|
||||
<StatusBar />
|
||||
</I18nContext.Provider>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByTitle('Battery'));
|
||||
|
||||
await waitFor(() => screen.getByText('Battery level is OK.'));
|
||||
await waitFor(() => statusBar.getByText('Battery level is OK.'));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user