diff --git a/src/app/App.test.tsx b/src/app/App.test.tsx new file mode 100644 index 00000000..9de4a0c6 --- /dev/null +++ b/src/app/App.test.tsx @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 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 App from './App'; + +it.each([false, true])('should render', (darkMode) => { + const store = mock>({ + getState: () => mock({ settings: { darkMode } }), + }); + + const i18n = new I18nManager({ locale: 'en' }); + + render( + + + + + , + ); +}); diff --git a/src/ble-device-info-service/protocol.test.ts b/src/ble-device-info-service/protocol.test.ts new file mode 100644 index 00000000..33237599 --- /dev/null +++ b/src/ble-device-info-service/protocol.test.ts @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 The Pybricks Authors + +import { + HubType, + LegoCompanyId, + TechnicLargeHubVariant, +} from '../ble-lwp3-service/protocol'; +import { decodePnpId, getHubTypeName } from './protocol'; + +function encodeInfo(id: HubType, variant?: number) { + return new DataView( + new Uint8Array([ + 1, // Bluetooth SIG + LegoCompanyId & 0xff, + LegoCompanyId >> 8, + id, + 0, + variant ?? 0, + 0, + ]).buffer, + ); +} + +test.each([ + [encodeInfo(HubType.MoveHub), 'Move hub'], + [encodeInfo(HubType.CityHub), 'City hub'], + [encodeInfo(HubType.TechnicHub), 'Technic hub'], + [ + encodeInfo(HubType.TechnicLargeHub, TechnicLargeHubVariant.SpikePrimeHub), + 'Prime hub', + ], + [ + encodeInfo( + HubType.TechnicLargeHub, + TechnicLargeHubVariant.MindstormsInventorHub, + ), + 'Inventor hub', + ], + [encodeInfo(HubType.TechnicSmallHub), 'Essential hub'], +])('should correctly decode data', (data, expected) => { + expect(getHubTypeName(decodePnpId(data))).toBe(expected); +}); diff --git a/src/status-bar/StatusBar.test.tsx b/src/status-bar/StatusBar.test.tsx index 2719de2c..6b82be23 100644 --- a/src/status-bar/StatusBar.test.tsx +++ b/src/status-bar/StatusBar.test.tsx @@ -1,11 +1,15 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2021 The Pybricks Authors -import { fireEvent, render, screen } from '@testing-library/react'; +import { I18nContext, I18nManager } from '@shopify/react-i18n'; +import { fireEvent, render, screen, 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 { BleConnectionState } from '../ble/reducers'; +import { RootState } from '../reducers'; import StatusBar from './StatusBar'; it('should prevent browser context menu', () => { @@ -24,3 +28,67 @@ it('should prevent browser context menu', () => { expect(fireEvent.contextMenu(screen.getByRole('status'))).toBe(false); }); + +it('should show popover when hub name is clicked', async () => { + const testHubName = 'Test hub'; + + const store = mock>({ + getState: () => + mock({ + ble: { + connection: BleConnectionState.Connected, + deviceName: testHubName, + deviceType: 'hub type', + deviceFirmwareVersion: 'v0.0.0', + deviceLowBatteryWarning: false, + deviceBatteryCharging: false, + }, + }), + }); + + const i18n = new I18nManager({ locale: 'en' }); + + render( + + + + + , + ); + + userEvent.click(screen.getByText(testHubName)); + + await waitFor(() => screen.getByText('Connected to:')); +}); + +it('should show popover when battery is clicked', async () => { + const testHubName = 'Test hub'; + + const store = mock>({ + getState: () => + mock({ + ble: { + connection: BleConnectionState.Connected, + deviceName: testHubName, + deviceType: 'hub type', + deviceFirmwareVersion: 'v0.0.0', + deviceLowBatteryWarning: false, + deviceBatteryCharging: false, + }, + }), + }); + + const i18n = new I18nManager({ locale: 'en' }); + + render( + + + + + , + ); + + userEvent.click(screen.getByTitle('Battery')); + + await waitFor(() => screen.getByText('Battery level is OK.')); +}); diff --git a/src/terminal/Terminal.tsx b/src/terminal/Terminal.tsx index 7dcf7b3b..c327fa46 100644 --- a/src/terminal/Terminal.tsx +++ b/src/terminal/Terminal.tsx @@ -106,6 +106,7 @@ const Terminal: React.FC = (_props) => { // xterm.open() has to be called after terminalRef has been rendered useEffect(() => { + // istanbul ignore if: should not happen ever if (!terminalRef.current) { console.error('Missing terminal reference'); return; diff --git a/src/utils/ViewHeightSensor.test.tsx b/src/utils/ViewHeightSensor.test.tsx new file mode 100644 index 00000000..bca09b88 --- /dev/null +++ b/src/utils/ViewHeightSensor.test.tsx @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 The Pybricks Authors + +import { render } from '@testing-library/react'; +import React from 'react'; +import ViewHeightSensor from './ViewHeightSensor'; + +it('should set the --pb-vh variable on resize', () => { + render(); + // REVISIT: it is not easy to test the resize event since jest-dom doesn't do layout +}); diff --git a/src/utils/monkey-patch.ts b/src/utils/monkey-patch.ts index 4d16252b..7f7600b1 100644 --- a/src/utils/monkey-patch.ts +++ b/src/utils/monkey-patch.ts @@ -26,6 +26,7 @@ export function useTooltip2MonkeyPatch(): React.RefObject> { const tooltipRef = useRef>(null); useEffect(() => { + // istanbul ignore if: should not happen ever if (!tooltipRef.current) { return; } @@ -55,6 +56,7 @@ export function useTooltip2MonkeyPatch(): React.RefObject> { * @param tooltipRef The reference returned from useTooltip2MonkeyPatch() */ export function closeTooltip2(tooltipRef: React.RefObject>): void { + // istanbul ignore if: should not happen ever if (!tooltipRef.current) { return; }