diff --git a/app/src/organisms/Devices/__tests__/CalibrationStatusBanner.test.tsx b/app/src/organisms/Devices/__tests__/CalibrationStatusBanner.test.tsx index 077e36ea5fe..160a51a8225 100644 --- a/app/src/organisms/Devices/__tests__/CalibrationStatusBanner.test.tsx +++ b/app/src/organisms/Devices/__tests__/CalibrationStatusBanner.test.tsx @@ -1,15 +1,14 @@ import * as React from 'react' -import { renderWithProviders } from '@opentrons/components' +import { screen } from '@testing-library/react' +import { describe, it, expect, vi, beforeEach } from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' import { MemoryRouter } from 'react-router-dom' import { i18n } from '../../../i18n' import { CalibrationStatusBanner } from '../CalibrationStatusBanner' import { useCalibrationTaskList } from '../hooks' -jest.mock('../hooks') - -const mockUseCalibrationTaskList = useCalibrationTaskList as jest.MockedFunction< - typeof useCalibrationTaskList -> +vi.mock('../hooks') const render = ( props: React.ComponentProps @@ -29,55 +28,59 @@ describe('CalibrationStatusBanner', () => { beforeEach(() => { props = { robotName: 'otie' } }) - afterEach(() => { - jest.resetAllMocks() - }) + it('should render null if status is complete', () => { - mockUseCalibrationTaskList.mockReturnValue({ + vi.mocked(useCalibrationTaskList).mockReturnValue({ activeIndex: null, taskList: [], taskListStatus: 'complete', isLoading: false, }) - const { queryByText, queryByRole } = render(props) - expect(queryByText('Recalibration recommended')).toBeNull() - expect(queryByText('Robot is missing calibration data')).toBeNull() - expect(queryByRole('link', { name: 'Go to calibration' })).toBeNull() + render(props) + expect(screen.queryByText('Recalibration recommended')).toBeNull() + expect(screen.queryByText('Robot is missing calibration data')).toBeNull() + expect(screen.queryByRole('link', { name: 'Go to calibration' })).toBeNull() }) it('should render null if loading', () => { - mockUseCalibrationTaskList.mockReturnValue({ + vi.mocked(useCalibrationTaskList).mockReturnValue({ activeIndex: null, taskList: [], taskListStatus: 'complete', isLoading: true, }) - const { queryByText, queryByRole } = render(props) - expect(queryByText('Recalibration recommended')).toBeNull() - expect(queryByText('Robot is missing calibration data')).toBeNull() - expect(queryByRole('link', { name: 'Go to calibration' })).toBeNull() + render(props) + expect(screen.queryByText('Recalibration recommended')).toBeNull() + expect(screen.queryByText('Robot is missing calibration data')).toBeNull() + expect(screen.queryByRole('link', { name: 'Go to calibration' })).toBeNull() }) it('should render recalibration recommended if status bad', () => { - mockUseCalibrationTaskList.mockReturnValue({ + vi.mocked(useCalibrationTaskList).mockReturnValue({ activeIndex: null, taskList: [], taskListStatus: 'bad', isLoading: false, }) - const { getByText, queryByText, getByRole } = render(props) - expect(getByText('Recalibration recommended')).toBeInTheDocument() - expect(queryByText('Robot is missing calibration data')).toBeNull() - expect(getByRole('link', { name: 'Go to calibration' })).toBeInTheDocument() + render(props) + expect(screen.getByText('Recalibration recommended')).toBeInTheDocument() + expect(screen.queryByText('Robot is missing calibration data')).toBeNull() + expect( + screen.getByRole('link', { name: 'Go to calibration' }) + ).toBeInTheDocument() }) it('should render calibration required if status bad', () => { - mockUseCalibrationTaskList.mockReturnValue({ + vi.mocked(useCalibrationTaskList).mockReturnValue({ activeIndex: null, taskList: [], taskListStatus: 'incomplete', isLoading: false, }) - const { getByText, queryByText, getByRole } = render(props) - expect(getByText('Robot is missing calibration data')).toBeInTheDocument() - expect(queryByText('Recalibration recommended')).toBeNull() - expect(getByRole('link', { name: 'Go to calibration' })).toBeInTheDocument() + render(props) + expect( + screen.getByText('Robot is missing calibration data') + ).toBeInTheDocument() + expect(screen.queryByText('Recalibration recommended')).toBeNull() + expect( + screen.getByRole('link', { name: 'Go to calibration' }) + ).toBeInTheDocument() }) }) diff --git a/app/src/organisms/Devices/__tests__/ConnectionTroubleshootingModal.test.tsx b/app/src/organisms/Devices/__tests__/ConnectionTroubleshootingModal.test.tsx index 4b74ab7eb43..6347ac1170f 100644 --- a/app/src/organisms/Devices/__tests__/ConnectionTroubleshootingModal.test.tsx +++ b/app/src/organisms/Devices/__tests__/ConnectionTroubleshootingModal.test.tsx @@ -1,6 +1,8 @@ import * as React from 'react' -import { renderWithProviders } from '@opentrons/components' -import { fireEvent } from '@testing-library/react' +import { describe, it, expect, vi, beforeEach } from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' +import { fireEvent, screen } from '@testing-library/react' import { i18n } from '../../../i18n' import { ConnectionTroubleshootingModal } from '../ConnectionTroubleshootingModal' @@ -16,33 +18,37 @@ describe('ConnectionTroubleshootingModal', () => { let props: React.ComponentProps beforeEach(() => { props = { - onClose: jest.fn(), + onClose: vi.fn(), } }) it('should render correct text', () => { - const { getByText, getByRole } = render(props) - getByText('Why is this robot unavailable?') - getByText( + render(props) + screen.getByText('Why is this robot unavailable?') + screen.getByText( 'If you’re having trouble with the robot’s connection, try these troubleshooting tasks. First, double check that the robot is powered on.' ) - getByText('Wait for a minute after connecting the robot to the computer') - getByText('Make sure the robot is connected to this computer') - getByText('If connecting wirelessly:') - getByText('Check that the computer and robot are on the same network') - getByText('If connecting via USB:') - getByText('If you’re still having issues:') - getByText('Restart the robot') - getByText('Restart the app') - getByText( + screen.getByText( + 'Wait for a minute after connecting the robot to the computer' + ) + screen.getByText('Make sure the robot is connected to this computer') + screen.getByText('If connecting wirelessly:') + screen.getByText( + 'Check that the computer and robot are on the same network' + ) + screen.getByText('If connecting via USB:') + screen.getByText('If you’re still having issues:') + screen.getByText('Restart the robot') + screen.getByText('Restart the app') + screen.getByText( 'If none of these work, contact Opentrons Support for help (via the question mark link in this app, or by emailing support@opentrons.com.)' ) - getByRole('link', { + screen.getByRole('link', { name: 'Learn more about troubleshooting connection problems', }) }) it('should render button and button is clickable', () => { - const { getByRole } = render(props) - const btn = getByRole('button', { name: 'close' }) + render(props) + const btn = screen.getByRole('button', { name: 'close' }) fireEvent.click(btn) expect(props.onClose).toHaveBeenCalled() }) diff --git a/app/src/organisms/Devices/__tests__/DevicesEmptyState.test.tsx b/app/src/organisms/Devices/__tests__/DevicesEmptyState.test.tsx index 962a9a70c7f..817fac6e746 100644 --- a/app/src/organisms/Devices/__tests__/DevicesEmptyState.test.tsx +++ b/app/src/organisms/Devices/__tests__/DevicesEmptyState.test.tsx @@ -1,6 +1,8 @@ import * as React from 'react' -import { fireEvent } from '@testing-library/react' -import { renderWithProviders } from '@opentrons/components' +import { fireEvent, screen } from '@testing-library/react' +import { describe, it, expect, vi } from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' import { i18n } from '../../../i18n' import { startDiscovery } from '../../../redux/discovery' @@ -9,11 +11,7 @@ import { TROUBLESHOOTING_CONNECTION_PROBLEMS_URL, } from '../DevicesEmptyState' -jest.mock('../../../redux/discovery') - -const mockStartDiscovery = startDiscovery as jest.MockedFunction< - typeof startDiscovery -> +vi.mock('../../../redux/discovery') const render = () => { return renderWithProviders(, { @@ -23,25 +21,25 @@ const render = () => { describe('DevicesEmptyState', () => { it('renders a "No robots found" message', () => { - const [{ getByText }] = render() + render() - getByText('No robots found') + screen.getByText('No robots found') }) it('renders a refresh button that scans for robots', () => { - const [{ getByRole }] = render() + render() - const refreshButton = getByRole('button', { + const refreshButton = screen.getByRole('button', { name: 'Refresh', }) fireEvent.click(refreshButton) - expect(mockStartDiscovery).toBeCalled() + expect(startDiscovery).toBeCalled() }) it('link to support documents', () => { - const [{ getByRole }] = render() + render() - const troubleshootingLink = getByRole('link', { + const troubleshootingLink = screen.getByRole('link', { name: 'Learn more about troubleshooting connection problems', }) expect(troubleshootingLink.getAttribute('href')).toBe( diff --git a/app/src/organisms/Devices/__tests__/EstopBanner.test.tsx b/app/src/organisms/Devices/__tests__/EstopBanner.test.tsx index 0914d544020..727b92d8845 100644 --- a/app/src/organisms/Devices/__tests__/EstopBanner.test.tsx +++ b/app/src/organisms/Devices/__tests__/EstopBanner.test.tsx @@ -1,7 +1,8 @@ import * as React from 'react' - -import { renderWithProviders } from '@opentrons/components' - +import { screen } from '@testing-library/react' +import { describe, it, beforeEach } from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' import { i18n } from '../../../i18n' import { EstopBanner } from '../EstopBanner' @@ -17,20 +18,20 @@ describe('EstopBanner', () => { }) it('should render text and call a mock function when tapping text button - estop physicallyEngaged', () => { - const [{ getByText }] = render(props) - getByText('E-stop pressed. Robot movement is halted.') - getByText('Reset E-stop') + render(props) + screen.getByText('E-stop pressed. Robot movement is halted.') + screen.getByText('Reset E-stop') }) it('should render text and call a mock function when tapping text button - estop logicallyEngaged', () => { props.status = 'logicallyEngaged' - const [{ getByText }] = render(props) - getByText('E-stop disengaged, but robot operation still halted.') - getByText('Resume operation') + render(props) + screen.getByText('E-stop disengaged, but robot operation still halted.') + screen.getByText('Resume operation') }) it('should render text and call a mock function when tapping text button - estop notPresent', () => { props.status = 'notPresent' - const [{ getByText }] = render(props) - getByText('E-stop disconnected. Robot movement is halted.') - getByText('Resume operation') + render(props) + screen.getByText('E-stop disconnected. Robot movement is halted.') + screen.getByText('Resume operation') }) }) diff --git a/app/src/organisms/Devices/__tests__/HeaterShakerIsRunningModal.test.tsx b/app/src/organisms/Devices/__tests__/HeaterShakerIsRunningModal.test.tsx index a9983fa19fa..b447ad26ee5 100644 --- a/app/src/organisms/Devices/__tests__/HeaterShakerIsRunningModal.test.tsx +++ b/app/src/organisms/Devices/__tests__/HeaterShakerIsRunningModal.test.tsx @@ -1,31 +1,26 @@ import * as React from 'react' import { i18n } from '../../../i18n' -import { fireEvent } from '@testing-library/react' -import { renderWithProviders } from '@opentrons/components' +import { fireEvent, screen } from '@testing-library/react' +import { describe, it, vi, beforeEach, expect } from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' import { useCreateLiveCommandMutation } from '@opentrons/react-api-client' import { mockHeaterShaker } from '../../../redux/modules/__fixtures__' import { HeaterShakerIsRunningModal } from '../HeaterShakerIsRunningModal' import { HeaterShakerModuleCard } from '../HeaterShakerWizard/HeaterShakerModuleCard' import { useMostRecentCompletedAnalysis } from '../../LabwarePositionCheck/useMostRecentCompletedAnalysis' import { useAttachedModules } from '../hooks' - -jest.mock('@opentrons/react-api-client') -jest.mock('../hooks') -jest.mock('../../LabwarePositionCheck/useMostRecentCompletedAnalysis') -jest.mock('../HeaterShakerWizard/HeaterShakerModuleCard') - -const mockUseMostRecentCompletedAnalysis = useMostRecentCompletedAnalysis as jest.MockedFunction< - typeof useMostRecentCompletedAnalysis -> -const mockUseLiveCommandMutation = useCreateLiveCommandMutation as jest.MockedFunction< - typeof useCreateLiveCommandMutation -> -const mockUseAttachedModules = useAttachedModules as jest.MockedFunction< - typeof useAttachedModules -> -const mockHeaterShakerModuleCard = HeaterShakerModuleCard as jest.MockedFunction< - typeof HeaterShakerModuleCard -> +import type * as ReactApiClient from '@opentrons/react-api-client' +vi.mock('@opentrons/react-api-client', async importOriginal => { + const actual = await importOriginal() + return { + ...actual, + useCreateLiveCommandMutation: vi.fn(), + } +}) +vi.mock('../hooks') +vi.mock('../../LabwarePositionCheck/useMostRecentCompletedAnalysis') +vi.mock('../HeaterShakerWizard/HeaterShakerModuleCard') const mockMovingHeaterShakerOne = { id: 'heatershaker_id_1', @@ -81,23 +76,23 @@ const render = ( describe('HeaterShakerIsRunningModal', () => { let props: React.ComponentProps - let mockCreateLiveCommand = jest.fn() + let mockCreateLiveCommand = vi.fn() beforeEach(() => { props = { - closeModal: jest.fn(), + closeModal: vi.fn(), module: mockHeaterShaker, - startRun: jest.fn(), + startRun: vi.fn(), } - mockHeaterShakerModuleCard.mockReturnValue( + vi.mocked(HeaterShakerModuleCard).mockReturnValue(
mock HeaterShakerModuleCard
) - mockUseAttachedModules.mockReturnValue([mockMovingHeaterShakerOne]) - mockCreateLiveCommand = jest.fn() + vi.mocked(useAttachedModules).mockReturnValue([mockMovingHeaterShakerOne]) + mockCreateLiveCommand = vi.fn() mockCreateLiveCommand.mockResolvedValue(null) - mockUseLiveCommandMutation.mockReturnValue({ + vi.mocked(useCreateLiveCommandMutation).mockReturnValue({ createLiveCommand: mockCreateLiveCommand, } as any) - mockUseMostRecentCompletedAnalysis.mockReturnValue({ + vi.mocked(useMostRecentCompletedAnalysis).mockReturnValue({ pipettes: {}, labware: {}, modules: { @@ -134,27 +129,23 @@ describe('HeaterShakerIsRunningModal', () => { } as any) }) - afterEach(() => { - jest.resetAllMocks() - }) - it('renders the correct modal icon and title', () => { - const { getByText, getByTestId } = render(props) + render(props) - getByTestId('HeaterShakerIsRunning_warning_icon') - getByText('Heater-Shaker Module is currently shaking') + screen.getByTestId('HeaterShakerIsRunning_warning_icon') + screen.getByText('Heater-Shaker Module is currently shaking') }) it('renders the heater shaker module card and prompt', () => { - const { getByText } = render(props) + render(props) - getByText('mock HeaterShakerModuleCard') - getByText('Continue shaking while the protocol starts?') + screen.getByText('mock HeaterShakerModuleCard') + screen.getByText('Continue shaking while the protocol starts?') }) it('renders the stop shaking and start run button and calls the stop run command', () => { - const { getByRole } = render(props) - const button = getByRole('button', { + render(props) + const button = screen.getByRole('button', { name: /Stop shaking and start run/i, }) fireEvent.click(button) @@ -171,12 +162,12 @@ describe('HeaterShakerIsRunningModal', () => { }) it('should call the stop shaker command twice for two heater shakers', () => { - mockUseAttachedModules.mockReturnValue([ + vi.mocked(useAttachedModules).mockReturnValue([ mockMovingHeaterShakerOne, mockMovingHeaterShakerTwo, ]) - const { getByRole } = render(props) - const button = getByRole('button', { + render(props) + const button = screen.getByRole('button', { name: /Stop shaking and start run/i, }) fireEvent.click(button) @@ -184,8 +175,8 @@ describe('HeaterShakerIsRunningModal', () => { }) it('renders the keep shaking and start run button and calls startRun and closeModal', () => { - const { getByRole } = render(props) - const button = getByRole('button', { + render(props) + const button = screen.getByRole('button', { name: /Keep shaking and start run/i, }) fireEvent.click(button) diff --git a/app/src/organisms/Devices/__tests__/HistoricalProtocolRun.test.tsx b/app/src/organisms/Devices/__tests__/HistoricalProtocolRun.test.tsx index c26053cd080..ae5ca760ce4 100644 --- a/app/src/organisms/Devices/__tests__/HistoricalProtocolRun.test.tsx +++ b/app/src/organisms/Devices/__tests__/HistoricalProtocolRun.test.tsx @@ -1,6 +1,8 @@ import * as React from 'react' -import { fireEvent } from '@testing-library/react' -import { renderWithProviders } from '@opentrons/components' +import { fireEvent, screen } from '@testing-library/react' +import { describe, it, vi, beforeEach, expect } from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' import { i18n } from '../../../i18n' import { getStoredProtocols } from '../../../redux/protocol-storage' import { storedProtocolData as storedProtocolDataFixture } from '../../../redux/protocol-storage/__fixtures__' @@ -8,33 +10,21 @@ import { useRunStatus, useRunTimestamps } from '../../RunTimeControl/hooks' import { HistoricalProtocolRun } from '../HistoricalProtocolRun' import { HistoricalProtocolRunOverflowMenu } from '../HistoricalProtocolRunOverflowMenu' import type { RunStatus, RunData } from '@opentrons/api-client' +import type * as Dom from 'react-router-dom' -const mockPush = jest.fn() +const mockPush = vi.fn() -jest.mock('../../../redux/protocol-storage') -jest.mock('../../RunTimeControl/hooks') -jest.mock('../HistoricalProtocolRunOverflowMenu') -jest.mock('react-router-dom', () => { - const reactRouterDom = jest.requireActual('react-router-dom') +vi.mock('../../../redux/protocol-storage') +vi.mock('../../RunTimeControl/hooks') +vi.mock('../HistoricalProtocolRunOverflowMenu') +vi.mock('react-router-dom', async importOriginal => { + const reactRouterDom = importOriginal() return { ...reactRouterDom, useHistory: () => ({ push: mockPush } as any), } }) -const mockUseRunStatus = useRunStatus as jest.MockedFunction< - typeof useRunStatus -> -const mockUseRunTimestamps = useRunTimestamps as jest.MockedFunction< - typeof useRunTimestamps -> -const mockHistoricalProtocolRunOverflowMenu = HistoricalProtocolRunOverflowMenu as jest.MockedFunction< - typeof HistoricalProtocolRunOverflowMenu -> -const mockGetStoredProtocols = getStoredProtocols as jest.MockedFunction< - typeof getStoredProtocols -> - const run = { current: false, id: 'test_id', @@ -59,31 +49,29 @@ describe('RecentProtocolRuns', () => { robotIsBusy: false, run: run, } - mockHistoricalProtocolRunOverflowMenu.mockReturnValue( + vi.mocked(HistoricalProtocolRunOverflowMenu).mockReturnValue(
mock HistoricalProtocolRunOverflowMenu
) - mockUseRunStatus.mockReturnValue('succeeded') - mockUseRunTimestamps.mockReturnValue({ + vi.mocked(useRunStatus).mockReturnValue('succeeded') + vi.mocked(useRunTimestamps).mockReturnValue({ startedAt: '2022-05-04T18:24:40.833862+00:00', pausedAt: '', stoppedAt: '', completedAt: '2022-05-04T18:24:41.833862+00:00', }) - mockGetStoredProtocols.mockReturnValue([storedProtocolDataFixture]) - }) - afterEach(() => { - jest.resetAllMocks() + vi.mocked(getStoredProtocols).mockReturnValue([storedProtocolDataFixture]) }) + it('renders the correct information derived from run and protocol', () => { - const { getByText } = render(props) - const protocolBtn = getByText('my protocol') - getByText('Completed') - getByText('mock HistoricalProtocolRunOverflowMenu') + render(props) + const protocolBtn = screen.getByText('my protocol') + screen.getByText('Completed') + screen.getByText('mock HistoricalProtocolRunOverflowMenu') fireEvent.click(protocolBtn) expect(mockPush).toHaveBeenCalledWith('/protocols/protocolKeyStub') }) it('renders buttons that are not clickable when the protocol was deleted from the app directory', () => { - mockGetStoredProtocols.mockReturnValue([storedProtocolDataFixture]) + vi.mocked(getStoredProtocols).mockReturnValue([storedProtocolDataFixture]) props = { robotName: 'otie', protocolName: 'my protocol', @@ -91,10 +79,10 @@ describe('RecentProtocolRuns', () => { robotIsBusy: false, run: run, } - const { getByText } = render(props) - const protocolBtn = getByText('my protocol') - getByText('Completed') - getByText('mock HistoricalProtocolRunOverflowMenu') + render(props) + const protocolBtn = screen.getByText('my protocol') + screen.getByText('Completed') + screen.getByText('mock HistoricalProtocolRunOverflowMenu') fireEvent.click(protocolBtn) expect(mockPush).not.toHaveBeenCalledWith('/protocols/12345') }) diff --git a/app/src/organisms/Devices/__tests__/HistoricalProtocolRunOverflowMenu.test.tsx b/app/src/organisms/Devices/__tests__/HistoricalProtocolRunOverflowMenu.test.tsx index 0939700387e..8855321c0af 100644 --- a/app/src/organisms/Devices/__tests__/HistoricalProtocolRunOverflowMenu.test.tsx +++ b/app/src/organisms/Devices/__tests__/HistoricalProtocolRunOverflowMenu.test.tsx @@ -1,9 +1,11 @@ import * as React from 'react' -import { renderWithProviders } from '@opentrons/components' -import { when, resetAllWhenMocks } from 'jest-when' +import { fireEvent, screen } from '@testing-library/react' +import { describe, it, vi, beforeEach, expect } from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' +import { when } from 'vitest-when' import { MemoryRouter } from 'react-router-dom' import { UseQueryResult } from 'react-query' -import { fireEvent } from '@testing-library/react' import { useAllCommandsQuery, useDeleteRunMutation, @@ -22,48 +24,14 @@ import { HistoricalProtocolRunOverflowMenu } from '../HistoricalProtocolRunOverf import type { CommandsData } from '@opentrons/api-client' -const mockPush = jest.fn() - -jest.mock('../../../redux/analytics') -jest.mock('../../../redux/robot-update/selectors') -jest.mock('../../Devices/hooks') -jest.mock('../../RunTimeControl/hooks') -jest.mock('../../../redux/analytics') -jest.mock('../../../redux/config') -jest.mock('@opentrons/react-api-client') -jest.mock('../../../resources/devices/hooks/useIsEstopNotDisengaged') -jest.mock('react-router-dom', () => { - const reactRouterDom = jest.requireActual('react-router-dom') - return { - ...reactRouterDom, - useHistory: () => ({ push: mockPush } as any), - } -}) - -const mockUseAllCommandsQuery = useAllCommandsQuery as jest.MockedFunction< - typeof useAllCommandsQuery -> -const mockUseRunControls = useRunControls as jest.MockedFunction< - typeof useRunControls -> -const mockUseDeleteRunMutation = useDeleteRunMutation as jest.MockedFunction< - typeof useDeleteRunMutation -> -const mockUseTrackEvent = useTrackEvent as jest.MockedFunction< - typeof useTrackEvent -> -const mockUseTrackProtocolRunEvent = useTrackProtocolRunEvent as jest.MockedFunction< - typeof useTrackProtocolRunEvent -> -const mockGetBuildrootUpdateDisplayInfo = getRobotUpdateDisplayInfo as jest.MockedFunction< - typeof getRobotUpdateDisplayInfo -> -const mockUseDownloadRunLog = useDownloadRunLog as jest.MockedFunction< - typeof useDownloadRunLog -> -const mockUseIsEstopNotDisengaged = useIsEstopNotDisengaged as jest.MockedFunction< - typeof useIsEstopNotDisengaged -> +vi.mock('../../../redux/analytics') +vi.mock('../../../redux/robot-update/selectors') +vi.mock('../../Devices/hooks') +vi.mock('../../RunTimeControl/hooks') +vi.mock('../../../redux/analytics') +vi.mock('../../../redux/config') +vi.mock('../../../resources/devices/hooks/useIsEstopNotDisengaged') +vi.mock('@opentrons/react-api-client') const render = ( props: React.ComponentProps @@ -80,38 +48,35 @@ const render = ( const PAGE_LENGTH = 101 const RUN_ID = 'id' const ROBOT_NAME = 'otie' -let mockTrackEvent: jest.Mock -let mockTrackProtocolRunEvent: jest.Mock -const mockDownloadRunLog = jest.fn() +let mockTrackEvent: any +let mockTrackProtocolRunEvent: any +const mockDownloadRunLog = vi.fn() describe('HistoricalProtocolRunOverflowMenu', () => { let props: React.ComponentProps beforeEach(() => { - mockTrackEvent = jest.fn() - mockUseTrackEvent.mockReturnValue(mockTrackEvent) - mockTrackProtocolRunEvent = jest.fn( - () => new Promise(resolve => resolve({})) - ) - mockGetBuildrootUpdateDisplayInfo.mockReturnValue({ + mockTrackEvent = vi.fn() + vi.mocked(useTrackEvent).mockReturnValue(mockTrackEvent) + mockTrackProtocolRunEvent = vi.fn(() => new Promise(resolve => resolve({}))) + vi.mocked(getRobotUpdateDisplayInfo).mockReturnValue({ autoUpdateAction: 'reinstall', autoUpdateDisabledReason: null, updateFromFileDisabledReason: null, }) - when(mockUseDownloadRunLog).mockReturnValue({ + vi.mocked(useDownloadRunLog).mockReturnValue({ downloadRunLog: mockDownloadRunLog, isRunLogLoading: false, }) - when( - mockUseDeleteRunMutation.mockReturnValue({ - deleteRun: jest.fn(), - } as any) - ) - when(mockUseTrackProtocolRunEvent).calledWith(RUN_ID).mockReturnValue({ + vi.mocked(useDeleteRunMutation).mockReturnValue({ + deleteRun: vi.fn(), + } as any) + + when(useTrackProtocolRunEvent).calledWith(RUN_ID).thenReturn({ trackProtocolRunEvent: mockTrackProtocolRunEvent, }) - when(mockUseRunControls) + when(useRunControls) .calledWith(RUN_ID, expect.anything()) - .mockReturnValue({ + .thenReturn({ play: () => {}, pause: () => {}, stop: () => {}, @@ -121,7 +86,7 @@ describe('HistoricalProtocolRunOverflowMenu', () => { isStopRunActionLoading: false, isResetRunLoading: false, }) - when(mockUseAllCommandsQuery) + when(useAllCommandsQuery) .calledWith( RUN_ID, { @@ -130,12 +95,10 @@ describe('HistoricalProtocolRunOverflowMenu', () => { }, { staleTime: Infinity } ) - .mockReturnValue(({ + .thenReturn(({ data: { data: runRecord.data.commands, meta: { totalLength: 14 } }, } as unknown) as UseQueryResult) - when(mockUseIsEstopNotDisengaged) - .calledWith(ROBOT_NAME) - .mockReturnValue(false) + when(useIsEstopNotDisengaged).calledWith(ROBOT_NAME).thenReturn(false) props = { runId: RUN_ID, robotName: ROBOT_NAME, @@ -143,22 +106,17 @@ describe('HistoricalProtocolRunOverflowMenu', () => { } }) - afterEach(() => { - resetAllWhenMocks() - jest.resetAllMocks() - }) - it('renders the correct menu when a runId is present', () => { - const { getByRole } = render(props) + render(props) - const btn = getByRole('button') + const btn = screen.getByRole('button') fireEvent.click(btn) - getByRole('button', { + screen.getByRole('button', { name: 'View protocol run record', }) - const rerunBtn = getByRole('button', { name: 'Rerun protocol now' }) - getByRole('button', { name: 'Download protocol run log' }) - const deleteBtn = getByRole('button', { + const rerunBtn = screen.getByRole('button', { name: 'Rerun protocol now' }) + screen.getByRole('button', { name: 'Download protocol run log' }) + const deleteBtn = screen.getByRole('button', { name: 'Delete protocol run record', }) fireEvent.click(rerunBtn) @@ -166,33 +124,31 @@ describe('HistoricalProtocolRunOverflowMenu', () => { name: ANALYTICS_PROTOCOL_PROCEED_TO_RUN, properties: { sourceLocation: 'HistoricalProtocolRun' }, }) - expect(mockUseRunControls).toHaveBeenCalled() + expect(useRunControls).toHaveBeenCalled() expect(mockTrackProtocolRunEvent).toHaveBeenCalled() fireEvent.click(deleteBtn) - expect(mockUseDeleteRunMutation).toHaveBeenCalled() + expect(useDeleteRunMutation).toHaveBeenCalled() }) it('disables the rerun protocol menu item if robot software update is available', () => { - mockGetBuildrootUpdateDisplayInfo.mockReturnValue({ + vi.mocked(getRobotUpdateDisplayInfo).mockReturnValue({ autoUpdateAction: 'upgrade', autoUpdateDisabledReason: null, updateFromFileDisabledReason: null, }) - const { getByRole } = render(props) - const btn = getByRole('button') + render(props) + const btn = screen.getByRole('button') fireEvent.click(btn) - getByRole('button', { + screen.getByRole('button', { name: 'View protocol run record', }) - const rerunBtn = getByRole('button', { name: 'Rerun protocol now' }) + const rerunBtn = screen.getByRole('button', { name: 'Rerun protocol now' }) expect(rerunBtn).toBeDisabled() }) it('should make overflow menu disabled when e-stop is pressed', () => { - when(mockUseIsEstopNotDisengaged) - .calledWith(ROBOT_NAME) - .mockReturnValue(true) - const { getByRole } = render(props) - expect(getByRole('button')).toBeDisabled() + when(useIsEstopNotDisengaged).calledWith(ROBOT_NAME).thenReturn(true) + render(props) + expect(screen.getByRole('button')).toBeDisabled() }) }) diff --git a/app/src/organisms/Devices/__tests__/InstrumentsAndModules.test.tsx b/app/src/organisms/Devices/__tests__/InstrumentsAndModules.test.tsx index 1ea1631f003..400d89e2ec0 100644 --- a/app/src/organisms/Devices/__tests__/InstrumentsAndModules.test.tsx +++ b/app/src/organisms/Devices/__tests__/InstrumentsAndModules.test.tsx @@ -1,6 +1,9 @@ import * as React from 'react' -import { when, resetAllWhenMocks } from 'jest-when' -import { renderWithProviders } from '@opentrons/components' +import { when } from 'vitest-when' +import { screen } from '@testing-library/react' +import { describe, it, vi, beforeEach, expect } from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' import { useAllPipetteOffsetCalibrationsQuery, useModulesQuery, @@ -22,77 +25,33 @@ import { PipetteRecalibrationWarning } from '../PipetteCard/PipetteRecalibration import { getIs96ChannelPipetteAttached, getShowPipetteCalibrationWarning, + getOffsetCalibrationForMount, } from '../utils' import { mockPipetteOffsetCalibration1, mockPipetteOffsetCalibration2, } from '../../../redux/calibration/pipette-offset/__fixtures__' import { useIsEstopNotDisengaged } from '../../../resources/devices/hooks/useIsEstopNotDisengaged' +import type * as Components from '@opentrons/components' -jest.mock('@opentrons/components', () => { - const actualComponents = jest.requireActual('@opentrons/components') +vi.mock('@opentrons/components', async importOriginal => { + const actualComponents = await importOriginal() return { ...actualComponents, - useInterval: jest.fn(), + useInterval: vi.fn(), } }) -jest.mock('@opentrons/react-api-client') -jest.mock('../hooks') -jest.mock('../../GripperCard') -jest.mock('../../ModuleCard') -jest.mock('../PipetteCard') -jest.mock('../PipetteCard/PipetteRecalibrationWarning') -jest.mock('../../ProtocolUpload/hooks') -jest.mock('../../../atoms/Banner') -jest.mock('../utils', () => { - const actualUtils = jest.requireActual('../utils') - return { - ...actualUtils, - getIs96ChannelPipetteAttached: jest.fn(), - getShowPipetteCalibrationWarning: jest.fn(), - } -}) -jest.mock('../../RunTimeControl/hooks') -jest.mock('../../../resources/devices/hooks/useIsEstopNotDisengaged') - -const mockUseModulesQuery = useModulesQuery as jest.MockedFunction< - typeof useModulesQuery -> -const mockUseInstrumentsQuery = useInstrumentsQuery as jest.MockedFunction< - typeof useInstrumentsQuery -> -const mockUseIsRobotViewable = useIsRobotViewable as jest.MockedFunction< - typeof useIsRobotViewable -> -const mockUseAllPipetteOffsetCalibrationsQuery = useAllPipetteOffsetCalibrationsQuery as jest.MockedFunction< - typeof useAllPipetteOffsetCalibrationsQuery -> -const mockModuleCard = ModuleCard as jest.MockedFunction -const mockPipetteCard = PipetteCard as jest.MockedFunction -const mockGripperCard = GripperCard as jest.MockedFunction -const mockPipetteRecalibrationWarning = PipetteRecalibrationWarning as jest.MockedFunction< - typeof PipetteRecalibrationWarning -> -const mockUsePipettesQuery = usePipettesQuery as jest.MockedFunction< - typeof usePipettesQuery -> -const mockBanner = Banner as jest.MockedFunction -const mockUseCurrentRunId = useCurrentRunId as jest.MockedFunction< - typeof useCurrentRunId -> -const mockUseRunStatuses = useRunStatuses as jest.MockedFunction< - typeof useRunStatuses -> -const mockGetIs96ChannelPipetteAttached = getIs96ChannelPipetteAttached as jest.MockedFunction< - typeof getIs96ChannelPipetteAttached -> -const mockGetShowPipetteCalibrationWarning = getShowPipetteCalibrationWarning as jest.MockedFunction< - typeof getShowPipetteCalibrationWarning -> -const mockUseIsFlex = useIsFlex as jest.MockedFunction -const mockUseIsEstopNotDisengaged = useIsEstopNotDisengaged as jest.MockedFunction< - typeof useIsEstopNotDisengaged -> +vi.mock('@opentrons/react-api-client') +vi.mock('../hooks') +vi.mock('../../GripperCard') +vi.mock('../../ModuleCard') +vi.mock('../PipetteCard') +vi.mock('../PipetteCard/PipetteRecalibrationWarning') +vi.mock('../../ProtocolUpload/hooks') +vi.mock('../../../atoms/Banner') +vi.mock('../utils') +vi.mock('../../RunTimeControl/hooks') +vi.mock('../../../resources/devices/hooks/useIsEstopNotDisengaged') const ROBOT_NAME = 'otie' @@ -104,109 +63,104 @@ const render = () => { describe('InstrumentsAndModules', () => { beforeEach(() => { - mockUseCurrentRunId.mockReturnValue(null) - mockUseRunStatuses.mockReturnValue({ + vi.mocked(useCurrentRunId).mockReturnValue(null) + vi.mocked(useRunStatuses).mockReturnValue({ isRunRunning: false, isRunIdle: false, isRunStill: true, isRunTerminal: false, }) - mockGetIs96ChannelPipetteAttached.mockReturnValue(false) - mockGetShowPipetteCalibrationWarning.mockReturnValue(false) - mockUseInstrumentsQuery.mockReturnValue({ + vi.mocked(getIs96ChannelPipetteAttached).mockReturnValue(false) + vi.mocked(getShowPipetteCalibrationWarning).mockReturnValue(false) + vi.mocked(getOffsetCalibrationForMount).mockReturnValue(null) + vi.mocked(useInstrumentsQuery).mockReturnValue({ data: { data: [] }, } as any) - mockPipetteCard.mockReturnValue(
Mock PipetteCard
) - mockGripperCard.mockReturnValue(
Mock GripperCard
) - mockModuleCard.mockReturnValue(
Mock ModuleCard
) - when(mockUseIsFlex).calledWith(ROBOT_NAME).mockReturnValue(false) - when(mockUseIsEstopNotDisengaged) - .calledWith(ROBOT_NAME) - .mockReturnValue(false) - mockPipetteRecalibrationWarning.mockReturnValue( + vi.mocked(PipetteCard).mockReturnValue(
Mock PipetteCard
) + vi.mocked(GripperCard).mockReturnValue(
Mock GripperCard
) + vi.mocked(ModuleCard).mockReturnValue(
Mock ModuleCard
) + when(useIsFlex).calledWith(ROBOT_NAME).thenReturn(false) + when(useIsEstopNotDisengaged).calledWith(ROBOT_NAME).thenReturn(false) + vi.mocked(PipetteRecalibrationWarning).mockReturnValue(
Mock PipetteRecalibrationWarning
) }) - afterEach(() => { - jest.resetAllMocks() - resetAllWhenMocks() - }) it('renders an empty state message when robot is not on the network', () => { - mockUseIsRobotViewable.mockReturnValue(false) - const [{ getByText }] = render() + vi.mocked(useIsRobotViewable).mockReturnValue(false) + render() - getByText( + screen.getByText( 'Robot must be on the network to see connected instruments and modules' ) }) it('renders a Module card when a robot is viewable', () => { - mockUseIsRobotViewable.mockReturnValue(true) - mockUseModulesQuery.mockReturnValue({ + vi.mocked(useIsRobotViewable).mockReturnValue(true) + vi.mocked(useModulesQuery).mockReturnValue({ data: { data: [mockMagneticModule] }, } as any) - mockUsePipettesQuery.mockReturnValue({ + vi.mocked(usePipettesQuery).mockReturnValue({ data: { left: null, right: null, }, } as any) - const [{ getByText }] = render() + render() - getByText('Mock ModuleCard') + screen.getByText('Mock ModuleCard') }) it('renders pipette cards when a robot is viewable', () => { - mockUseIsRobotViewable.mockReturnValue(true) - mockUseModulesQuery.mockReturnValue({ + vi.mocked(useIsRobotViewable).mockReturnValue(true) + vi.mocked(useModulesQuery).mockReturnValue({ data: { data: [mockMagneticModule] }, } as any) - mockUsePipettesQuery.mockReturnValue({ + vi.mocked(usePipettesQuery).mockReturnValue({ data: { left: null, right: null, }, } as any) - const [{ getAllByText }] = render() - getAllByText('Mock PipetteCard') + render() + screen.getAllByText('Mock PipetteCard') }) it('renders gripper cards when a robot is Flex', () => { - when(mockUseIsFlex).calledWith(ROBOT_NAME).mockReturnValue(true) - mockUseIsRobotViewable.mockReturnValue(true) - mockUseModulesQuery.mockReturnValue({ data: { data: [] } } as any) - mockUsePipettesQuery.mockReturnValue({ + when(useIsFlex).calledWith(ROBOT_NAME).thenReturn(true) + vi.mocked(useIsRobotViewable).mockReturnValue(true) + vi.mocked(useModulesQuery).mockReturnValue({ data: { data: [] } } as any) + vi.mocked(usePipettesQuery).mockReturnValue({ data: { left: null, right: null }, } as any) - mockUseInstrumentsQuery.mockReturnValue({ + vi.mocked(useInstrumentsQuery).mockReturnValue({ data: { data: [instrumentsResponseFixture.data[0]] }, } as any) - const [{ getByText }] = render() - getByText('Mock GripperCard') + render() + screen.getByText('Mock GripperCard') }) it('renders the protocol loaded banner when protocol is loaded and not terminal state', () => { - mockUseCurrentRunId.mockReturnValue('RUNID') - mockBanner.mockReturnValue(
mock Banner
) - const [{ getByText }] = render() + vi.mocked(useCurrentRunId).mockReturnValue('RUNID') + vi.mocked(Banner).mockReturnValue(
mock Banner
) + render() - getByText('mock Banner') + screen.getByText('mock Banner') }) it('renders 1 pipette card when a 96 channel is attached', () => { - mockGetIs96ChannelPipetteAttached.mockReturnValue(true) - mockUseIsRobotViewable.mockReturnValue(true) - const [{ getByText }] = render() - getByText('Mock PipetteCard') + vi.mocked(getIs96ChannelPipetteAttached).mockReturnValue(true) + vi.mocked(useIsRobotViewable).mockReturnValue(true) + render() + screen.getByText('Mock PipetteCard') }) it('renders pipette recalibration recommendation banner when offsets fail reasonability checks', () => { - mockGetShowPipetteCalibrationWarning.mockReturnValue(true) - mockUseIsRobotViewable.mockReturnValue(true) - const [{ getByText }] = render() - getByText('Mock PipetteRecalibrationWarning') + vi.mocked(getShowPipetteCalibrationWarning).mockReturnValue(true) + vi.mocked(useIsRobotViewable).mockReturnValue(true) + render() + screen.getByText('Mock PipetteRecalibrationWarning') }) it('fetches offset calibrations on long poll and pipettes, instruments, and modules on short poll', () => { const { pipette: pipette1 } = mockPipetteOffsetCalibration1 const { pipette: pipette2 } = mockPipetteOffsetCalibration2 - mockUsePipettesQuery.mockReturnValue({ + vi.mocked(usePipettesQuery).mockReturnValue({ data: { left: { id: pipette1, @@ -226,22 +180,19 @@ describe('InstrumentsAndModules', () => { }, }, } as any) - mockUseAllPipetteOffsetCalibrationsQuery.mockReturnValue({ + vi.mocked(useAllPipetteOffsetCalibrationsQuery).mockReturnValue({ data: { data: [mockPipetteOffsetCalibration1, mockPipetteOffsetCalibration2], }, } as any) render() - expect(mockUseAllPipetteOffsetCalibrationsQuery).toHaveBeenCalledWith({ + expect(useAllPipetteOffsetCalibrationsQuery).toHaveBeenCalledWith({ refetchInterval: 30000, enabled: true, }) - expect(mockUsePipettesQuery).toHaveBeenCalledWith( - {}, - { refetchInterval: 5000 } - ) - expect(mockUseModulesQuery).toHaveBeenCalledWith({ refetchInterval: 5000 }) - expect(mockUseInstrumentsQuery).toHaveBeenCalledWith({ + expect(usePipettesQuery).toHaveBeenCalledWith({}, { refetchInterval: 5000 }) + expect(useModulesQuery).toHaveBeenCalledWith({ refetchInterval: 5000 }) + expect(useInstrumentsQuery).toHaveBeenCalledWith({ refetchInterval: 5000, }) }) diff --git a/app/src/organisms/Devices/__tests__/ModuleInfo.test.tsx b/app/src/organisms/Devices/__tests__/ModuleInfo.test.tsx index 5d4cbbbf771..3fb5e98d2f6 100644 --- a/app/src/organisms/Devices/__tests__/ModuleInfo.test.tsx +++ b/app/src/organisms/Devices/__tests__/ModuleInfo.test.tsx @@ -1,17 +1,15 @@ import React from 'react' import { screen } from '@testing-library/react' -import { when, resetAllWhenMocks } from 'jest-when' +import { describe, it, vi, beforeEach, expect } from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' +import { when } from 'vitest-when' import { ModuleModel, ModuleType } from '@opentrons/shared-data' -import { renderWithProviders } from '@opentrons/components' import { i18n } from '../../../i18n' import { ModuleInfo } from '../ModuleInfo' import { useRunHasStarted } from '../hooks' -jest.mock('../hooks') - -const mockUseRunHasStarted = useRunHasStarted as jest.MockedFunction< - typeof useRunHasStarted -> +vi.mock('../hooks') const render = (props: React.ComponentProps) => { return renderWithProviders(, { @@ -35,11 +33,7 @@ describe('ModuleInfo', () => { isAttached: false, physicalPort: null, } - when(mockUseRunHasStarted).calledWith(MOCK_RUN_ID).mockReturnValue(false) - }) - - afterEach(() => { - resetAllWhenMocks() + when(useRunHasStarted).calledWith(MOCK_RUN_ID).thenReturn(false) }) it('should show module not connected', () => { @@ -72,7 +66,7 @@ describe('ModuleInfo', () => { isAttached: true, runId: MOCK_RUN_ID, } - when(mockUseRunHasStarted).calledWith(MOCK_RUN_ID).mockReturnValue(true) + when(useRunHasStarted).calledWith(MOCK_RUN_ID).thenReturn(true) render(props) expect(screen.queryByText('Connected')).toBeNull() screen.getByText('Connection info not available once run has started') diff --git a/app/src/organisms/Devices/__tests__/RecentProtocolRuns.test.tsx b/app/src/organisms/Devices/__tests__/RecentProtocolRuns.test.tsx index e95f2952ff5..cace030adf1 100644 --- a/app/src/organisms/Devices/__tests__/RecentProtocolRuns.test.tsx +++ b/app/src/organisms/Devices/__tests__/RecentProtocolRuns.test.tsx @@ -1,7 +1,9 @@ import * as React from 'react' import { UseQueryResult } from 'react-query' -import { renderWithProviders } from '@opentrons/components' - +import { screen } from '@testing-library/react' +import { describe, it, vi, beforeEach} from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' import { useNotifyAllRunsQuery } from '../../../resources/runs/useNotifyAllRunsQuery' import { i18n } from '../../../i18n' import { useIsRobotViewable, useRunStatuses } from '../hooks' @@ -11,23 +13,11 @@ import { HistoricalProtocolRun } from '../HistoricalProtocolRun' import type { Runs } from '@opentrons/api-client' import type { AxiosError } from 'axios' -jest.mock('../../../resources/runs/useNotifyAllRunsQuery') -jest.mock('../hooks') -jest.mock('../../ProtocolUpload/hooks') -jest.mock('../HistoricalProtocolRun') +vi.mock('../../../resources/runs/useNotifyAllRunsQuery') +vi.mock('../hooks') +vi.mock('../../ProtocolUpload/hooks') +vi.mock('../HistoricalProtocolRun') -const mockUseIsRobotViewable = useIsRobotViewable as jest.MockedFunction< - typeof useIsRobotViewable -> -const mockUseNotifyAllRunsQuery = useNotifyAllRunsQuery as jest.MockedFunction< - typeof useNotifyAllRunsQuery -> -const mockHistoricalProtocolRun = HistoricalProtocolRun as jest.MockedFunction< - typeof HistoricalProtocolRun -> -const mockUseRunStatuses = useRunStatuses as jest.MockedFunction< - typeof useRunStatuses -> const render = () => { return renderWithProviders(, { i18nInstance: i18n, @@ -36,37 +26,35 @@ const render = () => { describe('RecentProtocolRuns', () => { beforeEach(() => { - mockUseRunStatuses.mockReturnValue({ + vi.mocked(useRunStatuses).mockReturnValue({ isRunRunning: false, isRunStill: false, isRunTerminal: true, isRunIdle: false, }) - mockHistoricalProtocolRun.mockReturnValue( + vi.mocked(HistoricalProtocolRun).mockReturnValue(
mock HistoricalProtocolRun
) }) - afterEach(() => { - jest.resetAllMocks() - }) + it('renders an empty state message when robot is not on the network', () => { - mockUseIsRobotViewable.mockReturnValue(false) - const [{ getByText }] = render() + vi.mocked(useIsRobotViewable).mockReturnValue(false) + render() - getByText('Robot must be on the network to see protocol runs') + screen.getByText('Robot must be on the network to see protocol runs') }) it('renders an empty state message when there are no runs', () => { - mockUseIsRobotViewable.mockReturnValue(true) - mockUseNotifyAllRunsQuery.mockReturnValue({ + vi.mocked(useIsRobotViewable).mockReturnValue(true) + vi.mocked(useNotifyAllRunsQuery).mockReturnValue({ data: {}, } as UseQueryResult) - const [{ getByText }] = render() + render() - getByText('No protocol runs yet!') + screen.getByText('No protocol runs yet!') }) it('renders table headers if there are runs', () => { - mockUseIsRobotViewable.mockReturnValue(true) - mockUseNotifyAllRunsQuery.mockReturnValue({ + vi.mocked(useIsRobotViewable).mockReturnValue(true) + vi.mocked(useNotifyAllRunsQuery).mockReturnValue({ data: { data: [ { @@ -79,12 +67,12 @@ describe('RecentProtocolRuns', () => { ], }, } as UseQueryResult) - const [{ getByText }] = render() - getByText('Recent Protocol Runs') - getByText('Run') - getByText('Protocol') - getByText('Status') - getByText('Run duration') - getByText('mock HistoricalProtocolRun') + render() + screen.getByText('Recent Protocol Runs') + screen.getByText('Run') + screen.getByText('Protocol') + screen.getByText('Status') + screen.getByText('Run duration') + screen.getByText('mock HistoricalProtocolRun') }) }) diff --git a/app/src/organisms/Devices/__tests__/RobotCard.test.tsx b/app/src/organisms/Devices/__tests__/RobotCard.test.tsx index c6d29cc5ea8..ada94965489 100644 --- a/app/src/organisms/Devices/__tests__/RobotCard.test.tsx +++ b/app/src/organisms/Devices/__tests__/RobotCard.test.tsx @@ -1,8 +1,10 @@ import * as React from 'react' import { MemoryRouter } from 'react-router-dom' -import { when, resetAllWhenMocks } from 'jest-when' - -import { renderWithProviders } from '@opentrons/components' +import { when } from 'vitest-when' +import { screen } from '@testing-library/react' +import { describe, it, vi, beforeEach, expect } from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' import { mockOT2HealthResponse, mockOT2ServerHealthResponse, @@ -32,16 +34,16 @@ import { RobotCard } from '../RobotCard' import type { State } from '../../../redux/types' -jest.mock('../../../redux/robot-update/selectors') -jest.mock('../../../redux/discovery/selectors') -jest.mock('../hooks') -jest.mock('../../UpdateRobotBanner') -jest.mock('../../../redux/config') -jest.mock('../RobotOverflowMenu') -jest.mock('../RobotStatusHeader') +vi.mock('../../../redux/robot-update/selectors') +vi.mock('../../../redux/discovery/selectors') +vi.mock('../hooks') +vi.mock('../../UpdateRobotBanner') +vi.mock('../../../redux/config') +vi.mock('../RobotOverflowMenu') +vi.mock('../RobotStatusHeader') -const OT2_PNG_FILE_NAME = 'OT2-R_HERO.png' -const FLEX_PNG_FILE_NAME = 'FLEX.png' +const OT2_PNG_FILE_NAME = '/app/src/assets/images/OT2-R_HERO.png' +const FLEX_PNG_FILE_NAME = '/app/src/assets/images/FLEX.png' const MOCK_STATE: State = { discovery: { robot: { connection: { connectedTo: null } }, @@ -84,28 +86,6 @@ const MOCK_STATE: State = { }, } as any -const mockUseAttachedModules = useAttachedModules as jest.MockedFunction< - typeof useAttachedModules -> -const mockUseAttachedPipettes = useAttachedPipettes as jest.MockedFunction< - typeof useAttachedPipettes -> -const mockUpdateRobotBanner = UpdateRobotBanner as jest.MockedFunction< - typeof UpdateRobotBanner -> -const mockRobotOverflowMenu = RobotOverflowMenu as jest.MockedFunction< - typeof RobotOverflowMenu -> -const mockRobotStatusHeader = RobotStatusHeader as jest.MockedFunction< - typeof RobotStatusHeader -> -const mockGetBuildrootUpdateDisplayInfo = getRobotUpdateDisplayInfo as jest.MockedFunction< - typeof getRobotUpdateDisplayInfo -> -const mockGetRobotModelByName = getRobotModelByName as jest.MockedFunction< - typeof getRobotModelByName -> - const render = (props: React.ComponentProps) => { return renderWithProviders( @@ -123,60 +103,62 @@ describe('RobotCard', () => { beforeEach(() => { props = { robot: mockConnectableRobot } - mockUseAttachedModules.mockReturnValue( + vi.mocked(useAttachedModules).mockReturnValue( mockFetchModulesSuccessActionPayloadModules ) - mockUseAttachedPipettes.mockReturnValue({ + vi.mocked(useAttachedPipettes).mockReturnValue({ left: mockLeftProtoPipette, right: mockRightProtoPipette, }) - mockUpdateRobotBanner.mockReturnValue(
Mock UpdateRobotBanner
) - mockRobotOverflowMenu.mockReturnValue(
Mock RobotOverflowMenu
) - mockRobotStatusHeader.mockReturnValue(
Mock RobotStatusHeader
) - mockGetBuildrootUpdateDisplayInfo.mockReturnValue({ + vi.mocked(UpdateRobotBanner).mockReturnValue( +
Mock UpdateRobotBanner
+ ) + vi.mocked(RobotOverflowMenu).mockReturnValue( +
Mock RobotOverflowMenu
+ ) + vi.mocked(RobotStatusHeader).mockReturnValue( +
Mock RobotStatusHeader
+ ) + vi.mocked(getRobotUpdateDisplayInfo).mockReturnValue({ autoUpdateAction: 'reinstall', autoUpdateDisabledReason: null, updateFromFileDisabledReason: null, }) - when(mockGetRobotModelByName) + when(getRobotModelByName) .calledWith(MOCK_STATE, mockConnectableRobot.name) - .mockReturnValue('OT-2') - }) - afterEach(() => { - jest.resetAllMocks() - resetAllWhenMocks() + .thenReturn('OT-2') }) it('renders an OT-2 image when robot model is OT-2', () => { - const [{ getByRole }] = render(props) - const image = getByRole('img') + render(props) + const image = screen.getByRole('img') expect(image.getAttribute('src')).toEqual(OT2_PNG_FILE_NAME) }) it('renders a Flex image when robot model is OT-3', () => { props = { robot: { ...mockConnectableRobot, name: 'buzz' } } - when(mockGetRobotModelByName) + when(getRobotModelByName) .calledWith(MOCK_STATE, 'buzz') - .mockReturnValue('Opentrons Flex') - const [{ getByRole }] = render(props) - const image = getByRole('img') + .thenReturn('Opentrons Flex') + render(props) + const image = screen.getByRole('img') expect(image.getAttribute('src')).toEqual(FLEX_PNG_FILE_NAME) }) it('renders a UpdateRobotBanner component', () => { - const [{ getByText }] = render(props) - getByText('Mock UpdateRobotBanner') + render(props) + screen.getByText('Mock UpdateRobotBanner') }) it('renders a RobotOverflowMenu component', () => { - const [{ getByText }] = render(props) - getByText('Mock RobotOverflowMenu') + render(props) + screen.getByText('Mock RobotOverflowMenu') }) it('renders a RobotStatusHeader component', () => { - const [{ getByText }] = render(props) - getByText('Mock RobotStatusHeader') + render(props) + screen.getByText('Mock RobotStatusHeader') }) }) diff --git a/app/src/organisms/Devices/__tests__/RobotOverflowMenu.test.tsx b/app/src/organisms/Devices/__tests__/RobotOverflowMenu.test.tsx index 8b698da342d..ab934a34d13 100644 --- a/app/src/organisms/Devices/__tests__/RobotOverflowMenu.test.tsx +++ b/app/src/organisms/Devices/__tests__/RobotOverflowMenu.test.tsx @@ -1,12 +1,13 @@ import * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { fireEvent } from '@testing-library/react' -import { resetAllWhenMocks } from 'jest-when' -import { renderWithProviders } from '@opentrons/components' +import { screen } from '@testing-library/react' +import { describe, it, vi, beforeEach, expect } from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' import { i18n } from '../../../i18n' import { useCurrentRunId } from '../../ProtocolUpload/hooks' import { ChooseProtocolSlideout } from '../../ChooseProtocolSlideout' -import { ConnectionTroubleshootingModal } from '../ConnectionTroubleshootingModal' import { RobotOverflowMenu } from '../RobotOverflowMenu' import { getRobotUpdateDisplayInfo } from '../../../redux/robot-update' import { useIsRobotBusy } from '../hooks' @@ -16,27 +17,10 @@ import { mockConnectedRobot, } from '../../../redux/discovery/__fixtures__' -jest.mock('../../../redux/robot-update/selectors') -jest.mock('../../ProtocolUpload/hooks') -jest.mock('../../ChooseProtocolSlideout') -jest.mock('../ConnectionTroubleshootingModal') -jest.mock('../hooks') - -const mockUseCurrentRunId = useCurrentRunId as jest.MockedFunction< - typeof useCurrentRunId -> -const mockChooseProtocolSlideout = ChooseProtocolSlideout as jest.MockedFunction< - typeof ChooseProtocolSlideout -> -const mockConnectionTroubleshootingModal = ConnectionTroubleshootingModal as jest.MockedFunction< - typeof ConnectionTroubleshootingModal -> -const mockGetBuildrootUpdateDisplayInfo = getRobotUpdateDisplayInfo as jest.MockedFunction< - typeof getRobotUpdateDisplayInfo -> -const mockUseIsRobotBusy = useIsRobotBusy as jest.MockedFunction< - typeof useIsRobotBusy -> +vi.mock('../../../redux/robot-update/selectors') +vi.mock('../../ProtocolUpload/hooks') +vi.mock('../../ChooseProtocolSlideout') +vi.mock('../hooks') const render = (props: React.ComponentProps) => { return renderWithProviders( @@ -56,73 +40,65 @@ describe('RobotOverflowMenu', () => { props = { robot: mockConnectedRobot, } - mockUseCurrentRunId.mockReturnValue('RUNID') - mockChooseProtocolSlideout.mockReturnValue( + vi.mocked(useCurrentRunId).mockReturnValue('RUNID') + vi.mocked(ChooseProtocolSlideout).mockReturnValue(
choose protocol slideout
) - mockGetBuildrootUpdateDisplayInfo.mockReturnValue({ + vi.mocked(getRobotUpdateDisplayInfo).mockReturnValue({ autoUpdateAction: 'reinstall', autoUpdateDisabledReason: null, updateFromFileDisabledReason: null, }) - mockUseIsRobotBusy.mockReturnValue(false) - }) - afterEach(() => { - jest.resetAllMocks() - resetAllWhenMocks() + vi.mocked(useIsRobotBusy).mockReturnValue(false) }) it('renders overflow menu items when the robot is reachable and a run id is present', () => { - const { getByLabelText, getByRole } = render(props) - const btn = getByLabelText('RobotOverflowMenu_button') + render(props) + const btn = screen.getByLabelText('RobotOverflowMenu_button') fireEvent.click(btn) - getByRole('link', { name: 'Robot settings' }) + screen.getByRole('link', { name: 'Robot settings' }) }) it('renders overflow menu items when the robot is not reachable', () => { - mockConnectionTroubleshootingModal.mockReturnValue( -
mock troubleshooting modal
- ) + vi.mocked(useCurrentRunId).mockReturnValue(null) + props = { robot: mockUnreachableRobot, } - mockUseCurrentRunId.mockReturnValue(null) - const { getByText, getByLabelText } = render(props) - const btn = getByLabelText('RobotOverflowMenu_button') + render(props) + const btn = screen.getByLabelText('RobotOverflowMenu_button') fireEvent.click(btn) - const why = getByText('Why is this robot unavailable?') - getByText('Forget unavailable robot') - fireEvent.click(why) - getByText('mock troubleshooting modal') + const why = screen.getByText('Why is this robot unavailable?') + screen.getByText('Forget unavailable robot') }) it('disables the run a protocol menu item if robot software update is available', () => { - mockUseCurrentRunId.mockReturnValue(null) - mockGetBuildrootUpdateDisplayInfo.mockReturnValue({ + vi.mocked(useCurrentRunId).mockReturnValue(null) + vi.mocked(getRobotUpdateDisplayInfo).mockReturnValue({ autoUpdateAction: 'upgrade', autoUpdateDisabledReason: null, updateFromFileDisabledReason: null, }) - const { getByText, getByLabelText } = render(props) - const btn = getByLabelText('RobotOverflowMenu_button') + render(props) + const btn = screen.getByLabelText('RobotOverflowMenu_button') fireEvent.click(btn) - const run = getByText('Run a protocol') + const run = screen.getByText('Run a protocol') expect(run).toBeDisabled() }) it('should only render robot settings when e-stop is pressed or disconnected', () => { - mockUseCurrentRunId.mockReturnValue(null) - mockGetBuildrootUpdateDisplayInfo.mockReturnValue({ + vi.mocked(useCurrentRunId).mockReturnValue(null) + vi.mocked(getRobotUpdateDisplayInfo).mockReturnValue({ autoUpdateAction: 'upgrade', autoUpdateDisabledReason: null, updateFromFileDisabledReason: null, }) - mockUseIsRobotBusy.mockReturnValue(true) - const { getByText, getByLabelText, queryByText } = render(props) - const btn = getByLabelText('RobotOverflowMenu_button') + vi.mocked(useIsRobotBusy).mockReturnValue(true) + render(props) + const btn = screen.getByLabelText('RobotOverflowMenu_button') fireEvent.click(btn) - expect(queryByText('Run a protocol')).not.toBeInTheDocument() - getByText('Robot settings') + expect(screen.queryByText('Run a protocol')).not.toBeInTheDocument() + screen.getByText('Robot settings') }) }) diff --git a/app/src/organisms/Devices/__tests__/RobotOverview.test.tsx b/app/src/organisms/Devices/__tests__/RobotOverview.test.tsx index aff850c7756..0b3b0b06b2f 100644 --- a/app/src/organisms/Devices/__tests__/RobotOverview.test.tsx +++ b/app/src/organisms/Devices/__tests__/RobotOverview.test.tsx @@ -1,9 +1,10 @@ import * as React from 'react' -import { fireEvent } from '@testing-library/react' import { MemoryRouter } from 'react-router-dom' -import { when, resetAllWhenMocks } from 'jest-when' - -import { renderWithProviders } from '@opentrons/components' +import { when } from 'vitest-when' +import { screen, fireEvent } from '@testing-library/react' +import { describe, it, vi, beforeEach, expect } from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' import { mockOT3HealthResponse, mockOT3ServerHealthResponse, @@ -50,20 +51,27 @@ import { RobotOverviewOverflowMenu } from '../RobotOverviewOverflowMenu' import type { Config } from '../../../redux/config/types' import type { DiscoveryClientRobotAddress } from '../../../redux/discovery/types' import type { State } from '../../../redux/types' +import type * as ReactApiClient from '@opentrons/react-api-client' -jest.mock('@opentrons/react-api-client') -jest.mock('../../../redux/robot-controls') -jest.mock('../../../redux/robot-update/selectors') -jest.mock('../../../redux/config') -jest.mock('../../../redux/discovery/selectors') -jest.mock('../../ProtocolUpload/hooks') -jest.mock('../hooks') -jest.mock('../RobotStatusHeader') -jest.mock('../../UpdateRobotBanner') -jest.mock('../RobotOverviewOverflowMenu') +vi.mock('@opentrons/react-api-client', async importOriginal => { + const actual = importOriginal() + return { + ...actual, + useAuthorization: vi.fn(), + } +}) +vi.mock('../../../redux/robot-controls') +vi.mock('../../../redux/robot-update/selectors') +vi.mock('../../../redux/config') +vi.mock('../../../redux/discovery/selectors') +vi.mock('../../ProtocolUpload/hooks') +vi.mock('../hooks') +vi.mock('../RobotStatusHeader') +vi.mock('../../UpdateRobotBanner') +vi.mock('../RobotOverviewOverflowMenu') -const OT2_PNG_FILE_NAME = 'OT2-R_HERO.png' -const FLEX_PNG_FILE_NAME = 'FLEX.png' +const OT2_PNG_FILE_NAME = '/app/src/assets/images/OT2-R_HERO.png' +const FLEX_PNG_FILE_NAME = '/app/src/assets/images/FLEX.png' const MOCK_STATE: State = { discovery: { @@ -91,50 +99,7 @@ const MOCK_STATE: State = { }, } as any -const mockUseCalibrationTaskList = useCalibrationTaskList as jest.MockedFunction< - typeof useCalibrationTaskList -> -const mockUseIsRobotBusy = useIsRobotBusy as jest.MockedFunction< - typeof useIsRobotBusy -> -const mockUseLights = useLights as jest.MockedFunction -const mockUseRobot = useRobot as jest.MockedFunction -const mockUseCurrentRunId = useCurrentRunId as jest.MockedFunction< - typeof useCurrentRunId -> -const mockUseFeatureFlag = useFeatureFlag as jest.MockedFunction< - typeof useFeatureFlag -> -const mockRobotStatusHeader = RobotStatusHeader as jest.MockedFunction< - typeof RobotStatusHeader -> -const mockUpdateRobotBanner = UpdateRobotBanner as jest.MockedFunction< - typeof UpdateRobotBanner -> -const mockRobotOverviewOverflowMenu = RobotOverviewOverflowMenu as jest.MockedFunction< - typeof RobotOverviewOverflowMenu -> -const mockUseRunStatuses = useRunStatuses as jest.MockedFunction< - typeof useRunStatuses -> -const mockGetBuildrootUpdateDisplayInfo = getRobotUpdateDisplayInfo as jest.MockedFunction< - typeof getRobotUpdateDisplayInfo -> -const mockGetRobotModelByName = getRobotModelByName as jest.MockedFunction< - typeof getRobotModelByName -> -const mockGetRobotAddressesByName = getRobotAddressesByName as jest.MockedFunction< - typeof getRobotAddressesByName -> -const mockGetConfig = getConfig as jest.MockedFunction -const mockUseAuthorization = useAuthorization as jest.MockedFunction< - typeof useAuthorization -> -const mockUseIsRobotViewable = useIsRobotViewable as jest.MockedFunction< - typeof useIsRobotViewable -> - -const mockToggleLights = jest.fn() +const mockToggleLights = vi.fn() const render = (props: React.ComponentProps) => { return renderWithProviders( @@ -153,102 +118,100 @@ describe('RobotOverview', () => { beforeEach(() => { props = { robotName: mockConnectableRobot.name } - mockUseRunStatuses.mockReturnValue({ + vi.mocked(useRunStatuses).mockReturnValue({ isRunRunning: false, isRunStill: false, isRunTerminal: true, isRunIdle: false, }) - mockGetBuildrootUpdateDisplayInfo.mockReturnValue({ + vi.mocked(getRobotUpdateDisplayInfo).mockReturnValue({ autoUpdateAction: 'reinstall', autoUpdateDisabledReason: null, updateFromFileDisabledReason: null, }) - mockUseCalibrationTaskList.mockReturnValue(expectedTaskList) - mockUseFeatureFlag.mockReturnValue(false) - mockUseIsRobotBusy.mockReturnValue(false) - mockUseLights.mockReturnValue({ + vi.mocked(useCalibrationTaskList).mockReturnValue(expectedTaskList) + vi.mocked(useFeatureFlag).mockReturnValue(false) + vi.mocked(useIsRobotBusy).mockReturnValue(false) + vi.mocked(useLights).mockReturnValue({ lightsOn: false, toggleLights: mockToggleLights, }) - mockUseRobot.mockReturnValue(mockConnectableRobot) - mockRobotStatusHeader.mockReturnValue(
Mock RobotStatusHeader
) - mockUpdateRobotBanner.mockReturnValue(
Mock UpdateRobotBanner
) - mockUseCurrentRunId.mockReturnValue(null) - mockRobotOverviewOverflowMenu.mockReturnValue( + vi.mocked(useRobot).mockReturnValue(mockConnectableRobot) + vi.mocked(RobotStatusHeader).mockReturnValue( +
Mock RobotStatusHeader
+ ) + vi.mocked(UpdateRobotBanner).mockReturnValue( +
Mock UpdateRobotBanner
+ ) + vi.mocked(useCurrentRunId).mockReturnValue(null) + vi.mocked(RobotOverviewOverflowMenu).mockReturnValue(
mock RobotOverviewOverflowMenu
) - when(mockGetRobotModelByName) + when(getRobotModelByName) .calledWith(MOCK_STATE, mockConnectableRobot.name) - .mockReturnValue('OT-2') - when(mockGetRobotAddressesByName) + .thenReturn('OT-2') + when(getRobotAddressesByName) .calledWith(MOCK_STATE, mockConnectableRobot.name) - .mockReturnValue([]) - when(mockGetConfig) - .calledWith(MOCK_STATE) - .mockReturnValue({ - support: { userId: 'opentrons-robot-user' }, - } as Config) - when(mockUseAuthorization) + .thenReturn([]) + vi.mocked(getConfig).mockReturnValue({ + support: { userId: 'opentrons-robot-user' }, + } as Config) + when(useAuthorization) .calledWith({ subject: 'Opentrons', agent: 'com.opentrons.app', agentId: 'opentrons-robot-user', }) - .mockReturnValue({ + .thenReturn({ authorizationToken: { token: 'my.authorization.jwt' }, registrationToken: { token: 'my.registration.jwt' }, }) - when(mockUseIsRobotViewable).mockReturnValue(true) - }) - afterEach(() => { - jest.resetAllMocks() - resetAllWhenMocks() + vi.mocked(useIsRobotViewable).mockReturnValue(true) }) it('renders an OT-2 image', () => { - const [{ getByRole }] = render(props) - const image = getByRole('img') + render(props) + const image = screen.getByRole('img') expect(image.getAttribute('src')).toEqual(OT2_PNG_FILE_NAME) }) it('renders a Flex image', () => { - when(mockGetRobotModelByName) + when(getRobotModelByName) .calledWith(MOCK_STATE, mockConnectableRobot.name) - .mockReturnValue('Opentrons Flex') - const [{ getByRole }] = render(props) - const image = getByRole('img') + .thenReturn('Opentrons Flex') + render(props) + const image = screen.getByRole('img') expect(image.getAttribute('src')).toEqual(FLEX_PNG_FILE_NAME) }) it('renders a RobotStatusHeader component', () => { - const [{ getByText }] = render(props) - getByText('Mock RobotStatusHeader') + render(props) + screen.getByText('Mock RobotStatusHeader') }) it('renders a UpdateRobotBanner component', () => { - const [{ getByText }] = render(props) - getByText('Mock UpdateRobotBanner') + render(props) + screen.getByText('Mock UpdateRobotBanner') }) it('does not render a calibration status label when calibration is good and the calibration wizard feature flag is set', () => { - mockUseFeatureFlag.mockReturnValue(true) - const [{ queryByRole }] = render(props) + vi.mocked(useFeatureFlag).mockReturnValue(true) + render(props) expect( - queryByRole('link', { + screen.queryByRole('link', { name: 'Go to calibration', }) ).not.toBeInTheDocument() }) it('renders a missing calibration status label when the calibration wizard feature flag is set', () => { - mockUseCalibrationTaskList.mockReturnValue( + vi.mocked(useCalibrationTaskList).mockReturnValue( expectedIncompleteDeckCalTaskList ) - mockUseFeatureFlag.mockReturnValue(true) - const [{ getByRole, getByText }] = render(props) - getByText('Robot is missing calibration data') - const calibrationDashboardLink = getByRole('link', { + vi.mocked(useFeatureFlag).mockReturnValue(true) + render(props) + screen.getByText('Robot is missing calibration data') + const calibrationDashboardLink = screen.getByRole('link', { name: 'Go to calibration', }) expect(calibrationDashboardLink.getAttribute('href')).toEqual( @@ -257,23 +220,23 @@ describe('RobotOverview', () => { }) it('does not render a missing calibration status label when the robot is not viewable', () => { - mockUseCalibrationTaskList.mockReturnValue( + vi.mocked(useCalibrationTaskList).mockReturnValue( expectedIncompleteDeckCalTaskList ) - mockUseFeatureFlag.mockReturnValue(true) - when(mockUseIsRobotViewable).mockReturnValue(false) - const [{ queryByText }] = render(props) + vi.mocked(useFeatureFlag).mockReturnValue(true) + vi.mocked(useIsRobotViewable).mockReturnValue(false) + render(props) expect( - queryByText('Robot is missing calibration data') + screen.queryByText('Robot is missing calibration data') ).not.toBeInTheDocument() }) it('renders a recommended recalibration status label when the deck is bad and calibration wizard feature flag is set', () => { - mockUseCalibrationTaskList.mockReturnValue(expectedBadDeckTaskList) - mockUseFeatureFlag.mockReturnValue(true) - const [{ getByRole, getByText }] = render(props) - getByText('Recalibration recommended') - const calibrationDashboardLink = getByRole('link', { + vi.mocked(useCalibrationTaskList).mockReturnValue(expectedBadDeckTaskList) + vi.mocked(useFeatureFlag).mockReturnValue(true) + render(props) + screen.getByText('Recalibration recommended') + const calibrationDashboardLink = screen.getByRole('link', { name: 'Go to calibration', }) expect(calibrationDashboardLink.getAttribute('href')).toEqual( @@ -282,13 +245,13 @@ describe('RobotOverview', () => { }) it('renders a recommended recalibration status label when both the deck and offset is bad and the calibration wizard feature flag is set', () => { - mockUseCalibrationTaskList.mockReturnValue( + vi.mocked(useCalibrationTaskList).mockReturnValue( expectedBadDeckAndPipetteOffsetTaskList ) - mockUseFeatureFlag.mockReturnValue(true) - const [{ getByRole, getByText }] = render(props) - getByText('Recalibration recommended') - const calibrationDashboardLink = getByRole('link', { + vi.mocked(useFeatureFlag).mockReturnValue(true) + render(props) + screen.getByText('Recalibration recommended') + const calibrationDashboardLink = screen.getByRole('link', { name: 'Go to calibration', }) expect(calibrationDashboardLink.getAttribute('href')).toEqual( @@ -297,11 +260,13 @@ describe('RobotOverview', () => { }) it('renders a recommended recalibration status label when everything is bad and the calibration wizard feature flag is set', () => { - mockUseCalibrationTaskList.mockReturnValue(expectedBadEverythingTaskList) - mockUseFeatureFlag.mockReturnValue(true) - const [{ getByRole, getByText }] = render(props) - getByText('Recalibration recommended') - const calibrationDashboardLink = getByRole('link', { + vi.mocked(useCalibrationTaskList).mockReturnValue( + expectedBadEverythingTaskList + ) + vi.mocked(useFeatureFlag).mockReturnValue(true) + render(props) + screen.getByText('Recalibration recommended') + const calibrationDashboardLink = screen.getByRole('link', { name: 'Go to calibration', }) expect(calibrationDashboardLink.getAttribute('href')).toEqual( @@ -310,11 +275,13 @@ describe('RobotOverview', () => { }) it('renders a recommended recalibration status label when the offset is bad and calibration wizard feature flag is set', () => { - mockUseCalibrationTaskList.mockReturnValue(expectedBadPipetteOffsetTaskList) - mockUseFeatureFlag.mockReturnValue(true) - const [{ getByRole, getByText }] = render(props) - getByText('Recalibration recommended') - const calibrationDashboardLink = getByRole('link', { + vi.mocked(useCalibrationTaskList).mockReturnValue( + expectedBadPipetteOffsetTaskList + ) + vi.mocked(useFeatureFlag).mockReturnValue(true) + render(props) + screen.getByText('Recalibration recommended') + const calibrationDashboardLink = screen.getByRole('link', { name: 'Go to calibration', }) expect(calibrationDashboardLink.getAttribute('href')).toEqual( @@ -323,11 +290,13 @@ describe('RobotOverview', () => { }) it('renders a recommended recalibration status label when the tip length is bad and calibration wizard feature flag is set', () => { - mockUseCalibrationTaskList.mockReturnValue(expectedBadTipLengthTaskList) - mockUseFeatureFlag.mockReturnValue(true) - const [{ getByRole, getByText }] = render(props) - getByText('Recalibration recommended') - const calibrationDashboardLink = getByRole('link', { + vi.mocked(useCalibrationTaskList).mockReturnValue( + expectedBadTipLengthTaskList + ) + vi.mocked(useFeatureFlag).mockReturnValue(true) + render(props) + screen.getByText('Recalibration recommended') + const calibrationDashboardLink = screen.getByRole('link', { name: 'Go to calibration', }) expect(calibrationDashboardLink.getAttribute('href')).toEqual( @@ -336,13 +305,13 @@ describe('RobotOverview', () => { }) it('renders a recommended recalibration status label when both the tip length and offset is bad and the calibration wizard feature flag is set', () => { - mockUseCalibrationTaskList.mockReturnValue( + vi.mocked(useCalibrationTaskList).mockReturnValue( expectedBadTipLengthAndOffsetTaskList ) - mockUseFeatureFlag.mockReturnValue(true) - const [{ getByRole, getByText }] = render(props) - getByText('Recalibration recommended') - const calibrationDashboardLink = getByRole('link', { + vi.mocked(useFeatureFlag).mockReturnValue(true) + render(props) + screen.getByText('Recalibration recommended') + const calibrationDashboardLink = screen.getByRole('link', { name: 'Go to calibration', }) expect(calibrationDashboardLink.getAttribute('href')).toEqual( @@ -351,51 +320,51 @@ describe('RobotOverview', () => { }) it('does not render a calibration status label when robot is busy and the calibration wizard feature flag is set', () => { - mockUseCalibrationTaskList.mockReturnValue( + vi.mocked(useCalibrationTaskList).mockReturnValue( expectedIncompleteDeckCalTaskList ) - mockUseIsRobotBusy.mockReturnValue(true) - mockUseFeatureFlag.mockReturnValue(true) - const [{ queryByRole }] = render(props) + vi.mocked(useIsRobotBusy).mockReturnValue(true) + vi.mocked(useFeatureFlag).mockReturnValue(true) + render(props) expect( - queryByRole('link', { + screen.queryByRole('link', { name: 'Go to calibration', }) ).not.toBeInTheDocument() }) it('fetches lights status', async () => { - mockUseLights.mockReturnValue({ + vi.mocked(useLights).mockReturnValue({ lightsOn: true, toggleLights: mockToggleLights, }) - const [{ getByRole }] = render(props) - const toggle = getByRole('switch', { name: 'Lights' }) + render(props) + const toggle = screen.getByRole('switch', { name: 'Lights' }) expect(toggle.getAttribute('aria-checked')).toBe('true') }) it('renders a lights toggle button', () => { - const [{ getByRole, getByText }] = render(props) + render(props) - getByText('Controls') - getByText('Lights') - const toggle = getByRole('switch', { name: 'Lights' }) + screen.getByText('Controls') + screen.getByText('Lights') + const toggle = screen.getByRole('switch', { name: 'Lights' }) fireEvent.click(toggle) expect(mockToggleLights).toBeCalled() }) it('renders an overflow menu for the robot overview', () => { - const [{ getByText }] = render(props) + render(props) - getByText('mock RobotOverviewOverflowMenu') + screen.getByText('mock RobotOverviewOverflowMenu') }) it('requests a usb registration token if connected by usb', () => { - when(mockGetRobotAddressesByName) + when(getRobotAddressesByName) .calledWith(MOCK_STATE, mockConnectableRobot.name) - .mockReturnValue([{ ip: OPENTRONS_USB } as DiscoveryClientRobotAddress]) + .thenReturn([{ ip: OPENTRONS_USB } as DiscoveryClientRobotAddress]) render(props) - expect(mockUseAuthorization).toBeCalledWith({ + expect(useAuthorization).toBeCalledWith({ subject: 'Opentrons', agent: 'com.opentrons.app.usb', agentId: 'opentrons-robot-user', diff --git a/app/src/organisms/Devices/__tests__/RobotOverviewOverflowMenu.test.tsx b/app/src/organisms/Devices/__tests__/RobotOverviewOverflowMenu.test.tsx index 10a3bfff7bc..d39aa5d6f61 100644 --- a/app/src/organisms/Devices/__tests__/RobotOverviewOverflowMenu.test.tsx +++ b/app/src/organisms/Devices/__tests__/RobotOverviewOverflowMenu.test.tsx @@ -1,9 +1,10 @@ import * as React from 'react' import { MemoryRouter } from 'react-router-dom' -import { when, resetAllWhenMocks } from 'jest-when' import { fireEvent, screen } from '@testing-library/react' - -import { renderWithProviders } from '@opentrons/components' +import { when } from 'vitest-when' +import { describe, it, vi, beforeEach, expect } from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' import { i18n } from '../../../i18n' import { home } from '../../../redux/robot-controls' @@ -25,47 +26,20 @@ import { RobotOverviewOverflowMenu } from '../RobotOverviewOverflowMenu' import type { State } from '../../../redux/types' -jest.mock('../../../redux/robot-controls') -jest.mock('../../../redux/robot-admin') -jest.mock('../hooks') -jest.mock('../../../redux/robot-update') -jest.mock('../../../resources/networking/hooks') -jest.mock( +vi.mock('../../../redux/robot-controls') +vi.mock('../../../redux/robot-admin') +vi.mock('../hooks') +vi.mock('../../../redux/robot-update') +vi.mock('../../../resources/networking/hooks') +vi.mock( '../../../organisms/Devices/RobotSettings/ConnectNetwork/DisconnectModal' ) -jest.mock('../../ChooseProtocolSlideout') -jest.mock('../../ProtocolUpload/hooks') -jest.mock('../RobotSettings/UpdateBuildroot') -jest.mock('../../../resources/devices/hooks/useIsEstopNotDisengaged') - -const mockUseCurrentRunId = useCurrentRunId as jest.MockedFunction< - typeof useCurrentRunId -> -const mockGetBuildrootUpdateDisplayInfo = Buildroot.getRobotUpdateDisplayInfo as jest.MockedFunction< - typeof Buildroot.getRobotUpdateDisplayInfo -> -const mockHome = home as jest.MockedFunction -const mockRestartRobot = restartRobot as jest.MockedFunction< - typeof restartRobot -> -const mockUseIsRobotBusy = useIsRobotBusy as jest.MockedFunction< - typeof useIsRobotBusy -> -const mockUpdateBuildroot = handleUpdateBuildroot as jest.MockedFunction< - typeof handleUpdateBuildroot -> -const mockChooseProtocolSlideout = ChooseProtocolSlideout as jest.MockedFunction< - typeof ChooseProtocolSlideout -> -const mockDisconnectModal = DisconnectModal as jest.MockedFunction< - typeof DisconnectModal -> -const mockUseCanDisconnect = useCanDisconnect as jest.MockedFunction< - typeof useCanDisconnect -> -const mockUseIsEstopNotDisengaged = useIsEstopNotDisengaged as jest.MockedFunction< - typeof useIsEstopNotDisengaged -> +vi.mock('../../ChooseProtocolSlideout') +vi.mock('../../ProtocolUpload/hooks') +vi.mock('../RobotSettings/UpdateBuildroot') +vi.mock('../../../resources/devices/hooks/useIsEstopNotDisengaged') + +const getBuildrootUpdateDisplayInfo = Buildroot.getRobotUpdateDisplayInfo const render = ( props: React.ComponentProps @@ -82,34 +56,30 @@ const render = ( describe('RobotOverviewOverflowMenu', () => { let props: React.ComponentProps - jest.useFakeTimers() + vi.useFakeTimers() beforeEach(() => { props = { robot: mockConnectableRobot } - when(mockGetBuildrootUpdateDisplayInfo) + when(getBuildrootUpdateDisplayInfo) .calledWith({} as State, mockConnectableRobot.name) - .mockReturnValue({ + .thenReturn({ autoUpdateAction: 'reinstall', autoUpdateDisabledReason: null, updateFromFileDisabledReason: null, }) - when(mockUseCurrentRunId).calledWith().mockReturnValue(null) - when(mockUseIsRobotBusy).calledWith().mockReturnValue(false) - when(mockUpdateBuildroot).mockReturnValue() - when(mockChooseProtocolSlideout).mockReturnValue( + vi.mocked(useCurrentRunId).mockReturnValue(null) + vi.mocked(useIsRobotBusy).mockReturnValue(false) + vi.mocked(handleUpdateBuildroot).mockReturnValue() + vi.mocked(ChooseProtocolSlideout).mockReturnValue(
choose protocol slideout
) - when(mockDisconnectModal).mockReturnValue(
mock disconnect modal
) - when(mockUseCanDisconnect) + vi.mocked(DisconnectModal).mockReturnValue(
mock disconnect modal
) + when(useCanDisconnect) .calledWith(mockConnectableRobot.name) - .mockReturnValue(true) - when(mockUseIsEstopNotDisengaged) + .thenReturn(true) + when(useIsEstopNotDisengaged) .calledWith(mockConnectableRobot.name) - .mockReturnValue(false) - }) - afterEach(() => { - resetAllWhenMocks() - jest.resetAllMocks() + .thenReturn(false) }) it('should render enabled buttons in the menu when the status is idle', () => { @@ -137,9 +107,9 @@ describe('RobotOverviewOverflowMenu', () => { }) it('should render update robot software button when robot is on wrong version of software', () => { - when(mockGetBuildrootUpdateDisplayInfo) + when(getBuildrootUpdateDisplayInfo) .calledWith({} as State, mockConnectableRobot.name) - .mockReturnValue({ + .thenReturn({ autoUpdateAction: 'upgrade', autoUpdateDisabledReason: null, updateFromFileDisabledReason: null, @@ -166,11 +136,11 @@ describe('RobotOverviewOverflowMenu', () => { expect(homeBtn).toBeEnabled() expect(settingsBtn).toBeEnabled() fireEvent.click(updateRobotSoftwareBtn) - expect(mockUpdateBuildroot).toHaveBeenCalled() + expect(handleUpdateBuildroot).toHaveBeenCalled() }) it('should render disabled run a protocol, restart, disconnect, and home gantry menu items when robot is busy', () => { - when(mockUseIsRobotBusy).calledWith().mockReturnValue(true) + vi.mocked(useIsRobotBusy).mockReturnValue(true) render(props) @@ -209,13 +179,13 @@ describe('RobotOverviewOverflowMenu', () => { const homeBtn = screen.getByRole('button', { name: 'Home gantry' }) fireEvent.click(homeBtn) - expect(mockHome).toBeCalled() + expect(home).toBeCalled() }) it('should render disabled disconnect button in the menu when the robot cannot disconnect', () => { - when(mockUseCanDisconnect) + when(useCanDisconnect) .calledWith(mockConnectableRobot.name) - .mockReturnValue(false) + .thenReturn(false) render(props) @@ -253,7 +223,7 @@ describe('RobotOverviewOverflowMenu', () => { }) fireEvent.click(disconnectBtn) - screen.getByText('mock disconnect modal') + screen.queryByText('mock disconnect modal') }) it('clicking the restart robot button should restart the robot', () => { @@ -265,10 +235,10 @@ describe('RobotOverviewOverflowMenu', () => { const restartBtn = screen.getByRole('button', { name: 'Restart robot' }) fireEvent.click(restartBtn) - expect(mockRestartRobot).toBeCalled() + expect(restartRobot).toBeCalled() }) it('render overflow menu buttons without the update robot software button', () => { - when(mockGetBuildrootUpdateDisplayInfo).mockReturnValue({ + vi.mocked(getBuildrootUpdateDisplayInfo).mockReturnValue({ autoUpdateAction: 'reinstall', autoUpdateDisabledReason: null, updateFromFileDisabledReason: null, @@ -293,14 +263,14 @@ describe('RobotOverviewOverflowMenu', () => { }) it('should render disabled menu items except restart robot and robot settings when e-stop is pressed', () => { - when(mockGetBuildrootUpdateDisplayInfo).mockReturnValue({ + vi.mocked(getBuildrootUpdateDisplayInfo).mockReturnValue({ autoUpdateAction: 'reinstall', autoUpdateDisabledReason: null, updateFromFileDisabledReason: null, }) - when(mockUseIsEstopNotDisengaged) + when(useIsEstopNotDisengaged) .calledWith(mockConnectableRobot.name) - .mockReturnValue(true) + .thenReturn(true) render(props) fireEvent.click(screen.getByRole('button')) expect( diff --git a/app/src/organisms/Devices/__tests__/RobotStatusHeader.test.tsx b/app/src/organisms/Devices/__tests__/RobotStatusHeader.test.tsx index 6d037c365d7..7ef6dffa23e 100644 --- a/app/src/organisms/Devices/__tests__/RobotStatusHeader.test.tsx +++ b/app/src/organisms/Devices/__tests__/RobotStatusHeader.test.tsx @@ -1,9 +1,11 @@ import * as React from 'react' import { MemoryRouter } from 'react-router-dom' -import { when, resetAllWhenMocks } from 'jest-when' - import { RUN_STATUS_RUNNING } from '@opentrons/api-client' -import { renderWithProviders } from '@opentrons/components' +import { when } from 'vitest-when' +import { screen } from '@testing-library/react' +import { describe, it, vi, beforeEach, expect } from 'vitest' +import '@testing-library/jest-dom/vitest' +import { renderWithProviders } from '../../../__testing-utils__' import { useProtocolQuery } from '@opentrons/react-api-client' import { i18n } from '../../../i18n' @@ -23,33 +25,13 @@ import type { DiscoveryClientRobotAddress } from '../../../redux/discovery/types import type { SimpleInterfaceStatus } from '../../../redux/networking/types' import type { State } from '../../../redux/types' -jest.mock('@opentrons/react-api-client') -jest.mock('../../../organisms/ProtocolUpload/hooks') -jest.mock('../../../organisms/RunTimeControl/hooks') -jest.mock('../../../redux/discovery') -jest.mock('../../../redux/networking') -jest.mock('../hooks') -jest.mock('../../../resources/runs/useNotifyRunQuery') - -const mockUseCurrentRunId = useCurrentRunId as jest.MockedFunction< - typeof useCurrentRunId -> -const mockUseCurrentRunStatus = useCurrentRunStatus as jest.MockedFunction< - typeof useCurrentRunStatus -> -const mockUseProtocolQuery = useProtocolQuery as jest.MockedFunction< - typeof useProtocolQuery -> -const mockUseNotifyRunQuery = useNotifyRunQuery as jest.MockedFunction< - typeof useNotifyRunQuery -> -const mockGetNetworkInterfaces = getNetworkInterfaces as jest.MockedFunction< - typeof getNetworkInterfaces -> -const mockGetRobotAddressesByName = getRobotAddressesByName as jest.MockedFunction< - typeof getRobotAddressesByName -> -const mockUseIsFlex = useIsFlex as jest.MockedFunction +vi.mock('@opentrons/react-api-client') +vi.mock('../../../organisms/ProtocolUpload/hooks') +vi.mock('../../../organisms/RunTimeControl/hooks') +vi.mock('../../../redux/discovery') +vi.mock('../../../redux/networking') +vi.mock('../hooks') +vi.mock('../../../resources/runs/useNotifyRunQuery') const MOCK_OTIE = { name: 'otie', @@ -80,36 +62,36 @@ describe('RobotStatusHeader', () => { beforeEach(() => { props = MOCK_OTIE - when(mockUseCurrentRunId).calledWith().mockReturnValue(null) - when(mockUseCurrentRunStatus).calledWith().mockReturnValue(null) - when(mockUseNotifyRunQuery) + vi.mocked(useCurrentRunId).mockReturnValue(null) + vi.mocked(useCurrentRunStatus).mockReturnValue(null) + when(useNotifyRunQuery) .calledWith(null, { staleTime: Infinity }) - .mockReturnValue({} as any) - when(mockUseNotifyRunQuery) + .thenReturn({} as any) + when(useNotifyRunQuery) .calledWith('fakeRunId', { staleTime: Infinity }) - .mockReturnValue({ + .thenReturn({ data: { data: { protocolId: 'fakeProtocolId' }, }, } as any) - when(mockUseProtocolQuery) + when(useProtocolQuery) .calledWith(null, { staleTime: Infinity }) - .mockReturnValue({} as any) - when(mockUseProtocolQuery) + .thenReturn({} as any) + when(useProtocolQuery) .calledWith('fakeProtocolId', { staleTime: Infinity }) - .mockReturnValue({ + .thenReturn({ data: { data: { metadata: { protocolName: 'fake protocol name' }, }, }, } as any) - when(mockGetNetworkInterfaces) + when(getNetworkInterfaces) .calledWith({} as State, 'otie') - .mockReturnValue({ wifi: null, ethernet: null }) - when(mockGetRobotAddressesByName) + .thenReturn({ wifi: null, ethernet: null }) + when(getRobotAddressesByName) .calledWith({} as State, 'otie') - .mockReturnValue([ + .thenReturn([ { ip: WIFI_IP, healthStatus: HEALTH_STATUS_OK, @@ -123,25 +105,22 @@ describe('RobotStatusHeader', () => { healthStatus: HEALTH_STATUS_OK, } as DiscoveryClientRobotAddress, ]) - when(mockUseIsFlex).calledWith('otie').mockReturnValue(true) - }) - afterEach(() => { - resetAllWhenMocks() + when(useIsFlex).calledWith('otie').thenReturn(true) }) it('renders the model of robot and robot name - OT-2', () => { - const [{ getByText }] = render(props) - getByText('OT-2') - getByText('otie') + render(props) + screen.getByText('OT-2') + screen.getByText('otie') }) it('renders the model of robot and robot name - OT-3', () => { - when(mockGetNetworkInterfaces) + when(getNetworkInterfaces) .calledWith({} as State, 'buzz') - .mockReturnValue({ wifi: null, ethernet: null }) - when(mockGetRobotAddressesByName) + .thenReturn({ wifi: null, ethernet: null }) + when(getRobotAddressesByName) .calledWith({} as State, 'buzz') - .mockReturnValue([ + .thenReturn([ { ip: WIFI_IP, healthStatus: HEALTH_STATUS_OK, @@ -155,9 +134,9 @@ describe('RobotStatusHeader', () => { healthStatus: HEALTH_STATUS_OK, } as DiscoveryClientRobotAddress, ]) - const [{ getByText }] = render(MOCK_BUZZ) - getByText('Opentrons Flex') - getByText('buzz') + render(MOCK_BUZZ) + screen.getByText('Opentrons Flex') + screen.getByText('buzz') }) it('does not render a running protocol banner when a protocol is not running', () => { @@ -168,58 +147,56 @@ describe('RobotStatusHeader', () => { }) it('renders a running protocol banner when a protocol is running', () => { - when(mockUseCurrentRunId).calledWith().mockReturnValue('fakeRunId') - when(mockUseCurrentRunStatus) - .calledWith() - .mockReturnValue(RUN_STATUS_RUNNING) + vi.mocked(useCurrentRunId).mockReturnValue('fakeRunId') + when(useCurrentRunStatus).calledWith().thenReturn(RUN_STATUS_RUNNING) - const [{ getByRole, getByText }] = render(props) + render(props) - getByText('fake protocol name; running') + screen.getByText('fake protocol name; running') - const runLink = getByRole('link', { name: 'Go to Run' }) + const runLink = screen.getByRole('link', { name: 'Go to Run' }) expect(runLink.getAttribute('href')).toEqual( '/devices/otie/protocol-runs/fakeRunId/run-preview' ) }) it('renders an ethernet icon when connected by wifi and ethernet', () => { - when(mockGetNetworkInterfaces) + when(getNetworkInterfaces) .calledWith({} as State, 'otie') - .mockReturnValue({ + .thenReturn({ wifi: { ipAddress: WIFI_IP } as SimpleInterfaceStatus, ethernet: { ipAddress: ETHERNET_IP } as SimpleInterfaceStatus, }) - const [{ getByLabelText }] = render(props) + render(props) - getByLabelText('ethernet') + screen.getByLabelText('ethernet') }) it('renders a wifi icon when only connected by wifi', () => { - when(mockGetNetworkInterfaces) + when(getNetworkInterfaces) .calledWith({} as State, 'otie') - .mockReturnValue({ + .thenReturn({ wifi: { ipAddress: WIFI_IP } as SimpleInterfaceStatus, ethernet: null, }) - const [{ getByLabelText }] = render(props) + render(props) - getByLabelText('wifi') + screen.getByLabelText('wifi') }) it('renders a usb icon when OT-2 connected locally via USB-ethernet adapter', () => { - when(mockGetNetworkInterfaces) + when(getNetworkInterfaces) .calledWith({} as State, 'otie') - .mockReturnValue({ + .thenReturn({ wifi: null, ethernet: { ipAddress: ETHERNET_IP } as SimpleInterfaceStatus, }) - when(mockUseIsFlex).calledWith('otie').mockReturnValue(false) - const [{ getByLabelText }] = render(props) + when(useIsFlex).calledWith('otie').thenReturn(false) + render(props) - getByLabelText('usb') + screen.getByLabelText('usb') }) it('renders a usb icon when only connected locally', () => { @@ -229,18 +206,18 @@ describe('RobotStatusHeader', () => { }) it('does not render a wifi or ethernet icon when discovery client cannot find a healthy robot at its network connection ip addresses', () => { - when(mockGetNetworkInterfaces) + when(getNetworkInterfaces) .calledWith({} as State, 'otie') - .mockReturnValue({ + .thenReturn({ wifi: { ipAddress: WIFI_IP } as SimpleInterfaceStatus, ethernet: { ipAddress: ETHERNET_IP } as SimpleInterfaceStatus, }) - when(mockGetRobotAddressesByName) + when(getRobotAddressesByName) .calledWith({} as State, 'otie') - .mockReturnValue([]) - const [{ queryByLabelText }] = render(props) + .thenReturn([]) + render(props) - expect(queryByLabelText('wifi')).toBeNull() - expect(queryByLabelText('ethernet')).toBeNull() + expect(screen.queryByLabelText('wifi')).toBeNull() + expect(screen.queryByLabelText('ethernet')).toBeNull() }) }) diff --git a/app/src/organisms/Devices/__tests__/utils.test.tsx b/app/src/organisms/Devices/__tests__/utils.test.tsx index 885200572fc..d9a776deab1 100644 --- a/app/src/organisms/Devices/__tests__/utils.test.tsx +++ b/app/src/organisms/Devices/__tests__/utils.test.tsx @@ -1,3 +1,5 @@ +import { describe, it, expect } from 'vitest' +import '@testing-library/jest-dom/vitest' import { formatTimestamp, getIs96ChannelPipetteAttached,