From 21175f82b5d30617b31f838ae876669e36cbc3be Mon Sep 17 00:00:00 2001 From: Jamey Huffnagle Date: Tue, 5 Mar 2024 13:19:41 -0500 Subject: [PATCH] app shell tests --- app-shell-odd/src/__tests__/discovery.test.ts | 109 +++++++--------- app-shell-odd/vite.config.ts | 10 +- .../index.ts => __fixtures__/config.ts} | 2 +- app-shell/src/__fixtures__/index.ts | 1 + app-shell/src/__tests__/discovery.test.ts | 120 ++++++++---------- .../src/config/__tests__/migrate.test.ts | 2 +- .../__tests__/protocolAnalysis.test.ts | 119 ++++++++--------- .../__tests__/protocol-storage.test.ts | 11 +- .../__tests__/release-files.test.ts | 7 +- .../system-info/__tests__/dispatch.test.ts | 78 +++++------- .../system-info/__tests__/usb-devices.test.ts | 69 ++++------ 11 files changed, 228 insertions(+), 300 deletions(-) rename app-shell/src/{config/__fixtures__/index.ts => __fixtures__/config.ts} (99%) create mode 100644 app-shell/src/__fixtures__/index.ts diff --git a/app-shell-odd/src/__tests__/discovery.test.ts b/app-shell-odd/src/__tests__/discovery.test.ts index 77b2f26957d..07808dbedda 100644 --- a/app-shell-odd/src/__tests__/discovery.test.ts +++ b/app-shell-odd/src/__tests__/discovery.test.ts @@ -1,7 +1,8 @@ // tests for the app-shell's discovery module import { app } from 'electron' +import { vi, it, expect, describe, beforeEach } from 'vitest' import Store from 'electron-store' -import { when } from 'jest-when' +import { when } from 'vitest-when' import * as DiscoveryClient from '@opentrons/discovery-client' import { @@ -11,71 +12,49 @@ import { import { registerDiscovery } from '../discovery' import * as Cfg from '../config' -jest.mock('electron') -jest.mock('electron-store') -jest.mock('@opentrons/discovery-client') -jest.mock('../config') - -const createDiscoveryClient = DiscoveryClient.createDiscoveryClient as jest.MockedFunction< - typeof DiscoveryClient.createDiscoveryClient -> - -const getFullConfig = Cfg.getFullConfig as jest.MockedFunction< - typeof Cfg.getFullConfig -> - -const getOverrides = Cfg.getOverrides as jest.MockedFunction< - typeof Cfg.getOverrides -> - -const handleConfigChange = Cfg.handleConfigChange as jest.MockedFunction< - typeof Cfg.handleConfigChange -> - -const appOnce = app.once as jest.MockedFunction - -const MockStore = Store as jest.MockedClass +vi.mock('electron') +vi.mock('electron-store') +vi.mock('@opentrons/discovery-client') +vi.mock('../config') describe('app-shell/discovery', () => { - const dispatch = jest.fn() + const dispatch = vi.fn() const mockClient = { - start: jest.fn(), - stop: jest.fn(), - getRobots: jest.fn(), - removeRobot: jest.fn(), + start: vi.fn(), + stop: vi.fn(), + getRobots: vi.fn(), + removeRobot: vi.fn(), } const emitListChange = (): void => { - const lastCall = - createDiscoveryClient.mock.calls[ - createDiscoveryClient.mock.calls.length - 1 - ] + const lastCall = vi.mocked(DiscoveryClient.createDiscoveryClient).mock + .calls[ + vi.mocked(DiscoveryClient.createDiscoveryClient).mock.calls.length - 1 + ] const { onListChange } = lastCall[0] onListChange([]) } beforeEach(() => { - getFullConfig.mockReturnValue(({ + vi.mocked(Cfg.getFullConfig).mockReturnValue(({ discovery: { disableCache: false, candidates: [] }, } as unknown) as Cfg.Config) - getOverrides.mockReturnValue({}) - createDiscoveryClient.mockReturnValue(mockClient) + vi.mocked(Cfg.getOverrides).mockReturnValue({}) + vi.mocked(DiscoveryClient.createDiscoveryClient).mockReturnValue(mockClient) - when(MockStore.prototype.get).calledWith('robots', []).mockReturnValue([]) - when(MockStore.prototype.get) + when(vi.mocked(Store).prototype.get).calledWith('robots', []).thenReturn([]) + when(vi.mocked(Store).prototype.get) .calledWith('services', null) - .mockReturnValue(null) - }) - - afterEach(() => { - jest.resetAllMocks() + .thenReturn(null) }) it('registerDiscovery creates a DiscoveryClient', () => { registerDiscovery(dispatch) - expect(createDiscoveryClient).toHaveBeenCalledWith( + expect( + vi.mocked(DiscoveryClient.createDiscoveryClient) + ).toHaveBeenCalledWith( expect.objectContaining({ onListChange: expect.any(Function), }) @@ -95,14 +74,14 @@ describe('app-shell/discovery', () => { }) it('calls client.stop when electron app emits "will-quit"', () => { - expect(appOnce).toHaveBeenCalledTimes(0) + expect(vi.mocked(app.once)).toHaveBeenCalledTimes(0) registerDiscovery(dispatch) expect(mockClient.stop).toHaveBeenCalledTimes(0) - expect(appOnce).toHaveBeenCalledTimes(1) + expect(vi.mocked(app.once)).toHaveBeenCalledTimes(1) - const [event, handler] = appOnce.mock.calls[0] + const [event, handler] = vi.mocked(app.once).mock.calls[0] expect(event).toEqual('will-quit') // trigger event handler @@ -168,16 +147,16 @@ describe('app-shell/discovery', () => { mockClient.getRobots.mockReturnValue([{ name: 'foo' }, { name: 'bar' }]) emitListChange() - expect(MockStore.prototype.set).toHaveBeenLastCalledWith('robots', [ - { name: 'foo' }, - { name: 'bar' }, - ]) + expect(vi.mocked(Store).prototype.set).toHaveBeenLastCalledWith( + 'robots', + [{ name: 'foo' }, { name: 'bar' }] + ) }) it('loads robots from cache on client initialization', () => { const mockRobot = { name: 'foo' } - MockStore.prototype.get.mockImplementation(key => { + vi.mocked(Store).prototype.get.mockImplementation((key: string) => { if (key === 'robots') return [mockRobot] return null }) @@ -263,13 +242,13 @@ describe('app-shell/discovery', () => { }, ] - MockStore.prototype.get.mockImplementation(key => { + vi.mocked(Store).prototype.get.mockImplementation((key: string) => { if (key === 'services') return services return null }) registerDiscovery(dispatch) - expect(MockStore.prototype.delete).toHaveBeenCalledWith('services') + expect(vi.mocked(Store).prototype.delete).toHaveBeenCalledWith('services') expect(mockClient.start).toHaveBeenCalledWith( expect.objectContaining({ initialRobots: [ @@ -339,7 +318,7 @@ describe('app-shell/discovery', () => { it('does not update services from store when caching disabled', () => { // cache has been disabled - getFullConfig.mockReturnValue(({ + vi.mocked(Cfg.getFullConfig).mockReturnValue(({ discovery: { candidates: [], disableCache: true, @@ -347,7 +326,7 @@ describe('app-shell/discovery', () => { } as unknown) as Cfg.Config) // discovery.json contains 1 entry - MockStore.prototype.get.mockImplementation(key => { + vi.mocked(Store).prototype.get.mockImplementation((key: string) => { if (key === 'robots') return [{ name: 'foo' }] return null }) @@ -364,7 +343,7 @@ describe('app-shell/discovery', () => { it('should clear cache and suspend caching when caching becomes disabled', () => { // Cache enabled initially - getFullConfig.mockReturnValue(({ + vi.mocked(Cfg.getFullConfig).mockReturnValue(({ discovery: { candidates: [], disableCache: false, @@ -372,7 +351,7 @@ describe('app-shell/discovery', () => { } as unknown) as Cfg.Config) // discovery.json contains 1 entry - MockStore.prototype.get.mockImplementation(key => { + vi.mocked(Store).prototype.get.mockImplementation((key: string) => { if (key === 'robots') return [{ name: 'foo' }] return null }) @@ -380,25 +359,25 @@ describe('app-shell/discovery', () => { registerDiscovery(dispatch) // the 'discovery.disableCache' change handler - const changeHandler = handleConfigChange.mock.calls[1][1] + const changeHandler = vi.mocked(Cfg.handleConfigChange).mock.calls[1][1] const disableCache = true changeHandler(disableCache, false) - expect(MockStore.prototype.set).toHaveBeenCalledWith('robots', []) + expect(vi.mocked(Store).prototype.set).toHaveBeenCalledWith('robots', []) // new services discovered - MockStore.prototype.set.mockClear() + vi.mocked(Store).prototype.set.mockClear() mockClient.getRobots.mockReturnValue([{ name: 'foo' }, { name: 'bar' }]) emitListChange() // but discovery.json should not update - expect(MockStore.prototype.set).toHaveBeenCalledTimes(0) + expect(vi.mocked(Store).prototype.set).toHaveBeenCalledTimes(0) }) }) describe('manual addresses', () => { it('loads candidates from config on client initialization', () => { - getFullConfig.mockReturnValue(({ + vi.mocked(Cfg.getFullConfig).mockReturnValue(({ discovery: { cacheDisabled: false, candidates: ['1.2.3.4'] }, } as unknown) as Cfg.Config) @@ -415,7 +394,7 @@ describe('app-shell/discovery', () => { // ensures config override works with only one candidate specified it('candidates in config can be single string value', () => { - getFullConfig.mockReturnValue(({ + vi.mocked(Cfg.getFullConfig).mockReturnValue(({ discovery: { cacheDisabled: false, candidates: '1.2.3.4' }, } as unknown) as Cfg.Config) diff --git a/app-shell-odd/vite.config.ts b/app-shell-odd/vite.config.ts index d8beb3e5d27..a1cb7d6eeac 100644 --- a/app-shell-odd/vite.config.ts +++ b/app-shell-odd/vite.config.ts @@ -70,16 +70,16 @@ export default defineConfig( '@opentrons/components/styles': path.resolve( '../components/src/index.module.css' ), - '@opentrons/components': path.resolve('../components/src/index.ts'), - '@opentrons/shared-data': path.resolve('../shared-data/js/index.ts'), + '@opentrons/components': path.resolve('../components/src/config.ts'), + '@opentrons/shared-data': path.resolve('../shared-data/js/config.ts'), '@opentrons/step-generation': path.resolve( - '../step-generation/src/index.ts' + '../step-generation/src/config.ts' ), '@opentrons/discovery-client': path.resolve( - '../discovery-client/src/index.ts' + '../discovery-client/src/config.ts' ), '@opentrons/usb-bridge/node-client': path.resolve( - '../usb-bridge/node-client/src/index.ts' + '../usb-bridge/node-client/src/config.ts' ), }, }, diff --git a/app-shell/src/config/__fixtures__/index.ts b/app-shell/src/__fixtures__/config.ts similarity index 99% rename from app-shell/src/config/__fixtures__/index.ts rename to app-shell/src/__fixtures__/config.ts index 640fa1df429..bbd4e6296c0 100644 --- a/app-shell/src/config/__fixtures__/index.ts +++ b/app-shell/src/__fixtures__/config.ts @@ -21,7 +21,7 @@ import type { ConfigV19, ConfigV20, ConfigV21, -} from '@opentrons/app/src/redux/config/types' +} from '@opentrons/app/lib/redux/config/types' export const MOCK_CONFIG_V0: ConfigV0 = { version: 0, // Default key added on boot if missing in configs diff --git a/app-shell/src/__fixtures__/index.ts b/app-shell/src/__fixtures__/index.ts new file mode 100644 index 00000000000..f934b01b6f5 --- /dev/null +++ b/app-shell/src/__fixtures__/index.ts @@ -0,0 +1 @@ +export * from './config' diff --git a/app-shell/src/__tests__/discovery.test.ts b/app-shell/src/__tests__/discovery.test.ts index fa1236e9df5..2b65d7cb285 100644 --- a/app-shell/src/__tests__/discovery.test.ts +++ b/app-shell/src/__tests__/discovery.test.ts @@ -2,7 +2,8 @@ import { app } from 'electron' import Store from 'electron-store' import noop from 'lodash/noop' -import { when } from 'jest-when' +import { when } from 'vitest-when' +import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import * as DiscoveryClient from '@opentrons/discovery-client' import { @@ -12,78 +13,61 @@ import { import { registerDiscovery } from '../discovery' import * as Cfg from '../config' import * as SysInfo from '../system-info' +import { getSerialPortHttpAgent } from '../usb' -jest.mock('electron') -jest.mock('electron-store') -jest.mock('@opentrons/discovery-client') -jest.mock('../config') -jest.mock('../system-info') - -const createDiscoveryClient = DiscoveryClient.createDiscoveryClient as jest.MockedFunction< - typeof DiscoveryClient.createDiscoveryClient -> - -const getFullConfig = Cfg.getFullConfig as jest.MockedFunction< - typeof Cfg.getFullConfig -> - -const getOverrides = Cfg.getOverrides as jest.MockedFunction< - typeof Cfg.getOverrides -> - -const handleConfigChange = Cfg.handleConfigChange as jest.MockedFunction< - typeof Cfg.handleConfigChange -> - -const createNetworkInterfaceMonitor = SysInfo.createNetworkInterfaceMonitor as jest.MockedFunction< - typeof SysInfo.createNetworkInterfaceMonitor -> - -const appOnce = app.once as jest.MockedFunction - -const MockStore = Store as jest.MockedClass +vi.mock('electron') +// vi.mock('electron-store') +vi.mock('../usb') +vi.mock('@opentrons/discovery-client') +vi.mock('../config') +vi.mock('../system-info') describe('app-shell/discovery', () => { - const dispatch = jest.fn() + const dispatch = vi.fn() const mockClient = { - start: jest.fn(), - stop: jest.fn(), - getRobots: jest.fn(), - removeRobot: jest.fn(), + start: vi.fn(), + stop: vi.fn(), + getRobots: vi.fn(), + removeRobot: vi.fn(), } const emitListChange = (): void => { - const lastCall = - createDiscoveryClient.mock.calls[ - createDiscoveryClient.mock.calls.length - 1 - ] + const lastCall = vi.mocked(DiscoveryClient.createDiscoveryClient).mock + .calls[ + vi.mocked(DiscoveryClient.createDiscoveryClient).mock.calls.length - 1 + ] const { onListChange } = lastCall[0] onListChange([]) } beforeEach(() => { - getFullConfig.mockReturnValue(({ + vi.mocked(Cfg.getFullConfig).mockReturnValue(({ discovery: { disableCache: false, candidates: [] }, } as unknown) as Cfg.Config) - getOverrides.mockReturnValue({}) - createNetworkInterfaceMonitor.mockReturnValue({ stop: noop }) - createDiscoveryClient.mockReturnValue(mockClient) + vi.mocked(Cfg.getOverrides).mockReturnValue({}) + vi.mocked(SysInfo.createNetworkInterfaceMonitor).mockReturnValue({ + stop: noop, + }) + vi.mocked(DiscoveryClient.createDiscoveryClient).mockReturnValue(mockClient) + vi.mocked(getSerialPortHttpAgent).mockReturnValue({} as any) - when(MockStore.prototype.get).calledWith('robots', []).mockReturnValue([]) - when(MockStore.prototype.get) + when(vi.mocked(Store).prototype.get).calledWith('robots', []).thenReturn([]) + when(vi.mocked(Store).prototype.get) .calledWith('services', null) - .mockReturnValue(null) + .thenReturn(null) }) afterEach(() => { - jest.resetAllMocks() + vi.resetAllMocks() }) it('registerDiscovery creates a DiscoveryClient', () => { registerDiscovery(dispatch) - expect(createDiscoveryClient).toHaveBeenCalledWith( + expect( + vi.mocked(DiscoveryClient.createDiscoveryClient) + ).toHaveBeenCalledWith( expect.objectContaining({ onListChange: expect.any(Function), }) @@ -103,14 +87,14 @@ describe('app-shell/discovery', () => { }) it('calls client.stop when electron app emits "will-quit"', () => { - expect(appOnce).toHaveBeenCalledTimes(0) + expect(vi.mocked(app.once)).toHaveBeenCalledTimes(0) registerDiscovery(dispatch) expect(mockClient.stop).toHaveBeenCalledTimes(0) - expect(appOnce).toHaveBeenCalledTimes(1) + expect(vi.mocked(app.once)).toHaveBeenCalledTimes(1) - const [event, handler] = appOnce.mock.calls[0] + const [event, handler] = vi.mocked(app.once).mock.calls[0] expect(event).toEqual('will-quit') // trigger event handler @@ -176,16 +160,16 @@ describe('app-shell/discovery', () => { mockClient.getRobots.mockReturnValue([{ name: 'foo' }, { name: 'bar' }]) emitListChange() - expect(MockStore.prototype.set).toHaveBeenLastCalledWith('robots', [ - { name: 'foo' }, - { name: 'bar' }, - ]) + expect(vi.mocked(Store).prototype.set).toHaveBeenLastCalledWith( + 'robots', + [{ name: 'foo' }, { name: 'bar' }] + ) }) it('loads robots from cache on client initialization', () => { const mockRobot = { name: 'foo' } - MockStore.prototype.get.mockImplementation(key => { + vi.mocked(Store).prototype.get.mockImplementation((key: string) => { if (key === 'robots') return [mockRobot] return null }) @@ -271,13 +255,13 @@ describe('app-shell/discovery', () => { }, ] - MockStore.prototype.get.mockImplementation(key => { + vi.mocked(Store).prototype.get.mockImplementation((key: string) => { if (key === 'services') return services return null }) registerDiscovery(dispatch) - expect(MockStore.prototype.delete).toHaveBeenCalledWith('services') + expect(vi.mocked(Store).prototype.delete).toHaveBeenCalledWith('services') expect(mockClient.start).toHaveBeenCalledWith( expect.objectContaining({ initialRobots: [ @@ -347,7 +331,7 @@ describe('app-shell/discovery', () => { it('does not update services from store when caching disabled', () => { // cache has been disabled - getFullConfig.mockReturnValue(({ + vi.mocked(Cfg.getFullConfig).mockReturnValue(({ discovery: { candidates: [], disableCache: true, @@ -355,7 +339,7 @@ describe('app-shell/discovery', () => { } as unknown) as Cfg.Config) // discovery.json contains 1 entry - MockStore.prototype.get.mockImplementation(key => { + vi.mocked(Store).prototype.get.mockImplementation((key: string) => { if (key === 'robots') return [{ name: 'foo' }] return null }) @@ -372,7 +356,7 @@ describe('app-shell/discovery', () => { it('should clear cache and suspend caching when caching becomes disabled', () => { // Cache enabled initially - getFullConfig.mockReturnValue(({ + vi.mocked(Cfg.getFullConfig).mockReturnValue(({ discovery: { candidates: [], disableCache: false, @@ -380,7 +364,7 @@ describe('app-shell/discovery', () => { } as unknown) as Cfg.Config) // discovery.json contains 1 entry - MockStore.prototype.get.mockImplementation(key => { + vi.mocked(Store).prototype.get.mockImplementation((key: string) => { if (key === 'robots') return [{ name: 'foo' }] return null }) @@ -388,25 +372,25 @@ describe('app-shell/discovery', () => { registerDiscovery(dispatch) // the 'discovery.disableCache' change handler - const changeHandler = handleConfigChange.mock.calls[1][1] + const changeHandler = vi.mocked(Cfg.handleConfigChange).mock.calls[1][1] const disableCache = true changeHandler(disableCache, false) - expect(MockStore.prototype.set).toHaveBeenCalledWith('robots', []) + expect(vi.mocked(Store).prototype.set).toHaveBeenCalledWith('robots', []) // new services discovered - MockStore.prototype.set.mockClear() + vi.mocked(Store).prototype.set.mockClear() mockClient.getRobots.mockReturnValue([{ name: 'foo' }, { name: 'bar' }]) emitListChange() // but discovery.json should not update - expect(MockStore.prototype.set).toHaveBeenCalledTimes(0) + expect(vi.mocked(Store).prototype.set).toHaveBeenCalledTimes(0) }) }) describe('manual addresses', () => { it('loads candidates from config on client initialization', () => { - getFullConfig.mockReturnValue(({ + vi.mocked(Cfg.getFullConfig).mockReturnValue(({ discovery: { cacheDisabled: false, candidates: ['1.2.3.4'] }, } as unknown) as Cfg.Config) @@ -423,7 +407,7 @@ describe('app-shell/discovery', () => { // ensures config override works with only one candidate specified it('candidates in config can be single string value', () => { - getFullConfig.mockReturnValue(({ + vi.mocked(Cfg.getFullConfig).mockReturnValue(({ discovery: { cacheDisabled: false, candidates: '1.2.3.4' }, } as unknown) as Cfg.Config) diff --git a/app-shell/src/config/__tests__/migrate.test.ts b/app-shell/src/config/__tests__/migrate.test.ts index 18ef0eb3de2..24dcd9fcd38 100644 --- a/app-shell/src/config/__tests__/migrate.test.ts +++ b/app-shell/src/config/__tests__/migrate.test.ts @@ -23,7 +23,7 @@ import { MOCK_CONFIG_V19, MOCK_CONFIG_V20, MOCK_CONFIG_V21, -} from '../__fixtures__' +} from '../../__fixtures__' import { migrate } from '../migrate' const NEWEST_VERSION = 21 diff --git a/app-shell/src/protocol-analysis/__tests__/protocolAnalysis.test.ts b/app-shell/src/protocol-analysis/__tests__/protocolAnalysis.test.ts index dfd8e074121..e1ab2323920 100644 --- a/app-shell/src/protocol-analysis/__tests__/protocolAnalysis.test.ts +++ b/app-shell/src/protocol-analysis/__tests__/protocolAnalysis.test.ts @@ -1,4 +1,5 @@ -import { when, resetAllWhenMocks } from 'jest-when' +import { vi, it, expect, describe, beforeEach } from 'vitest' +import { when } from 'vitest-when' import electron from 'electron' import * as ProtocolAnalysis from '@opentrons/app/src/redux/protocol-analysis' import * as Cfg from '@opentrons/app/src/redux/config' @@ -17,37 +18,13 @@ import { } from '..' import { Dispatch } from '../../types' -jest.mock('../../labware') -jest.mock('../../dialogs') -jest.mock('../getPythonPath') -jest.mock('../executeAnalyzeCli') -jest.mock('../writeFailedAnalysis') - -const mockGetConfig = getConfig as jest.MockedFunction -const mockSelectPythonPath = selectPythonPath as jest.MockedFunction< - typeof selectPythonPath -> -const mockGetPythonPath = getPythonPath as jest.MockedFunction< - typeof getPythonPath -> -const mockExecuteAnalyzeCli = executeAnalyzeCli as jest.MockedFunction< - typeof executeAnalyzeCli -> -const mockWriteFailedAnalysis = writeFailedAnalysis as jest.MockedFunction< - typeof writeFailedAnalysis -> -const mockGetValidLabwareFilePaths = getValidLabwareFilePaths as jest.MockedFunction< - typeof getValidLabwareFilePaths -> -const mockHandleConfigChange = handleConfigChange as jest.MockedFunction< - typeof handleConfigChange -> -const mockShowOpenDirectoryDialog = Dialogs.showOpenDirectoryDialog as jest.MockedFunction< - typeof Dialogs.showOpenDirectoryDialog -> -const mockOpenDirectoryInFileExplorer = Dialogs.openDirectoryInFileExplorer as jest.MockedFunction< - typeof Dialogs.openDirectoryInFileExplorer -> +vi.mock('../../labware') +vi.mock('../../dialogs') +vi.mock('../getPythonPath') +vi.mock('../executeAnalyzeCli') +vi.mock('../writeFailedAnalysis') +vi.mock('electron-store') +vi.mock('../../config') // wait a few ticks to let the mock Promises clear const flush = (): Promise => @@ -57,32 +34,32 @@ describe('analyzeProtocolSource', () => { const mockMainWindow = ({ browserWindow: true, } as unknown) as electron.BrowserWindow - let dispatch: jest.MockedFunction + let dispatch = vi.fn() let handleAction: Dispatch beforeEach(() => { - dispatch = jest.fn() - mockGetConfig.mockReturnValue({ + dispatch = vi.fn() + vi.mocked(getConfig).mockReturnValue({ python: { pathToPythonOverride: '/some/override/python' }, } as Config) handleAction = registerProtocolAnalysis(dispatch, mockMainWindow) }) - afterEach(() => { - resetAllWhenMocks() - }) - it('should be able to initialize the Python path', () => { - expect(mockSelectPythonPath).toHaveBeenCalledWith('/some/override/python') - expect(mockHandleConfigChange).toHaveBeenCalledWith( + expect(vi.mocked(selectPythonPath)).toHaveBeenCalledWith( + '/some/override/python' + ) + expect(vi.mocked(handleConfigChange)).toHaveBeenCalledWith( 'python.pathToPythonOverride', expect.any(Function) ) // the 'python.pathToPythonOverride' change handler - const changeHandler = mockHandleConfigChange.mock.calls[0][1] + const changeHandler = vi.mocked(handleConfigChange).mock.calls[0][1] changeHandler('/new/override/python', '/old/path/does/not/matter') - expect(mockSelectPythonPath).toHaveBeenCalledWith('/new/override/python') + expect(vi.mocked(selectPythonPath)).toHaveBeenCalledWith( + '/new/override/python' + ) }) it('should get the Python path and execute the analyze CLI with custom labware', () => { @@ -94,13 +71,13 @@ describe('analyzeProtocolSource', () => { '/some/custom/labware/directory/fakeLabwareTwo.json', ] - when(mockGetPythonPath).calledWith().mockResolvedValue(pythonPath) - when(mockGetValidLabwareFilePaths) + when(vi.mocked(getPythonPath)).calledWith().thenResolve(pythonPath) + when(vi.mocked(getValidLabwareFilePaths)) .calledWith() - .mockResolvedValue(labwarePaths) + .thenResolve(labwarePaths) return analyzeProtocolSource(sourcePath, outputPath).then(() => { - expect(mockExecuteAnalyzeCli).toHaveBeenCalledWith( + expect(vi.mocked(executeAnalyzeCli)).toHaveBeenCalledWith( pythonPath, outputPath, [sourcePath, ...labwarePaths] @@ -113,11 +90,14 @@ describe('analyzeProtocolSource', () => { const outputPath = '/path/to/output.json' const error = new Error('oh no') - when(mockGetPythonPath).calledWith().mockRejectedValue(error) - when(mockGetValidLabwareFilePaths).calledWith().mockResolvedValue([]) + when(vi.mocked(getPythonPath)).calledWith().thenReject(error) + when(vi.mocked(getValidLabwareFilePaths)).calledWith().thenResolve([]) return analyzeProtocolSource(sourcePath, outputPath).then(() => { - expect(mockWriteFailedAnalysis).toHaveBeenCalledWith(outputPath, 'oh no') + expect(vi.mocked(writeFailedAnalysis)).toHaveBeenCalledWith( + outputPath, + 'oh no' + ) }) }) @@ -127,37 +107,44 @@ describe('analyzeProtocolSource', () => { const pythonPath = '/path/to/python' const error = new Error('oh no') - when(mockGetPythonPath).calledWith().mockResolvedValue(pythonPath) - when(mockGetValidLabwareFilePaths).calledWith().mockResolvedValue([]) - when(mockExecuteAnalyzeCli) + when(vi.mocked(getPythonPath)).calledWith().thenResolve(pythonPath) + when(vi.mocked(getValidLabwareFilePaths)).calledWith().thenResolve([]) + when(vi.mocked(executeAnalyzeCli)) .calledWith(pythonPath, outputPath, [sourcePath]) - .mockRejectedValue(error) + .thenReject(error) return analyzeProtocolSource(sourcePath, outputPath).then(() => { - expect(mockWriteFailedAnalysis).toHaveBeenCalledWith(outputPath, 'oh no') + expect(vi.mocked(writeFailedAnalysis)).toHaveBeenCalledWith( + outputPath, + 'oh no' + ) }) }) it('should open file picker in response to CHANGE_PYTHON_PATH_OVERRIDE and not call dispatch if no directory is returned from showOpenDirectoryDialog', () => { - when(mockShowOpenDirectoryDialog) + when(vi.mocked(Dialogs.showOpenDirectoryDialog)) .calledWith(mockMainWindow) - .mockResolvedValue([]) + .thenResolve([]) handleAction(ProtocolAnalysis.changePythonPathOverrideConfig()) return flush().then(() => { - expect(mockShowOpenDirectoryDialog).toHaveBeenCalledWith(mockMainWindow) + expect(vi.mocked(Dialogs.showOpenDirectoryDialog)).toHaveBeenCalledWith( + mockMainWindow + ) expect(dispatch).not.toHaveBeenCalled() }) }) it('should open file picker in response to CHANGE_PYTHON_PATH_OVERRIDE and call dispatch with directory returned from showOpenDirectoryDialog', () => { - when(mockShowOpenDirectoryDialog) + when(vi.mocked(Dialogs.showOpenDirectoryDialog)) .calledWith(mockMainWindow) - .mockResolvedValue(['path/to/override']) + .thenResolve(['path/to/override']) handleAction(ProtocolAnalysis.changePythonPathOverrideConfig()) return flush().then(() => { - expect(mockShowOpenDirectoryDialog).toHaveBeenCalledWith(mockMainWindow) + expect(vi.mocked(Dialogs.showOpenDirectoryDialog)).toHaveBeenCalledWith( + mockMainWindow + ) expect(dispatch).toHaveBeenCalledWith( Cfg.updateConfigValue( CONFIG_PYTHON_PATH_TO_PYTHON_OVERRIDE, @@ -168,15 +155,15 @@ describe('analyzeProtocolSource', () => { }) it('should call openDirectoryInFileExplorer in response to OPEN_PYTHON_DIRECTORY', () => { - when(mockOpenDirectoryInFileExplorer) + when(vi.mocked(Dialogs.openDirectoryInFileExplorer)) .calledWith('/some/override/python') - .mockResolvedValue(null) + .thenResolve(null) handleAction(ProtocolAnalysis.openPythonInterpreterDirectory()) return flush().then(() => { - expect(mockOpenDirectoryInFileExplorer).toHaveBeenCalledWith( - '/some/override/python' - ) + expect( + vi.mocked(Dialogs.openDirectoryInFileExplorer) + ).toHaveBeenCalledWith('/some/override/python') }) }) }) diff --git a/app-shell/src/protocol-storage/__tests__/protocol-storage.test.ts b/app-shell/src/protocol-storage/__tests__/protocol-storage.test.ts index f998ccb8b00..65661f5f786 100644 --- a/app-shell/src/protocol-storage/__tests__/protocol-storage.test.ts +++ b/app-shell/src/protocol-storage/__tests__/protocol-storage.test.ts @@ -3,7 +3,7 @@ import path from 'path' import fs from 'fs-extra' import tempy from 'tempy' -import { describe, it, vi, beforeEach, afterEach } from 'vitest' +import { describe, it, vi, beforeEach, afterEach, expect } from 'vitest' import { PROTOCOLS_DIRECTORY_NAME } from '../file-system' import { @@ -12,6 +12,8 @@ import { getParsedAnalysisFromPath, } from '../' +vi.mock('electron-store') + describe('protocol storage directory utilities', () => { let protocolsDir: string let mockAnalysisFilePath: string @@ -27,15 +29,12 @@ describe('protocol storage directory utilities', () => { afterEach(() => { return requiredRmdir - ? Promise.all([ + ? (Promise.all([ fs.rmdir(protocolsDir, { recursive: true }), fs.rm(mockAnalysisFilePath, { force: true }), - ]) + ]) as any) : fs.rm(mockAnalysisFilePath, { force: true }) }) - afterAll(() => { - jest.resetAllMocks() - }) describe('fetchProtocols', () => { it('reads and parses directories', () => { diff --git a/app-shell/src/robot-update/__tests__/release-files.test.ts b/app-shell/src/robot-update/__tests__/release-files.test.ts index 5cbbde68f34..4f149154d5b 100644 --- a/app-shell/src/robot-update/__tests__/release-files.test.ts +++ b/app-shell/src/robot-update/__tests__/release-files.test.ts @@ -3,10 +3,13 @@ import path from 'path' import { promises as fs } from 'fs' import fse from 'fs-extra' import tempy from 'tempy' -import { describe, it, afterAll } from 'vitest' +import { vi, describe, it, afterAll, expect } from 'vitest' import { cleanupReleaseFiles } from '../release-files' +vi.mock('electron-updater') +vi.mock('electron-store') + describe('robot update release files utilities', () => { const tempDirs: string[] = [] const makeEmptyDir = (): string => { @@ -16,7 +19,7 @@ describe('robot update release files utilities', () => { } afterAll(() => { - return Promise.all(tempDirs.map(d => fse.remove(d))) + return Promise.all(tempDirs.map(d => fse.remove(d))) as any }) describe('cleanupReleaseFiles', () => { diff --git a/app-shell/src/system-info/__tests__/dispatch.test.ts b/app-shell/src/system-info/__tests__/dispatch.test.ts index 5376367cfc6..013f7b87bc8 100644 --- a/app-shell/src/system-info/__tests__/dispatch.test.ts +++ b/app-shell/src/system-info/__tests__/dispatch.test.ts @@ -1,4 +1,5 @@ import noop from 'lodash/noop' +import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { app } from 'electron' import * as Fixtures from '@opentrons/app/src/redux/system-info/__fixtures__' import * as SystemInfo from '@opentrons/app/src/redux/system-info' @@ -15,38 +16,19 @@ import type { Dispatch } from '../../types' import type { UsbDeviceMonitor } from '../usb-devices' import type { NetworkInterfaceMonitor } from '../network-interfaces' -jest.mock('../../os') -jest.mock('../usb-devices') -jest.mock('../network-interfaces') - -const mockCreateUsbDeviceMonitor = createUsbDeviceMonitor as jest.MockedFunction< - typeof createUsbDeviceMonitor -> - -const mockGetWindowsDriverVersion = getWindowsDriverVersion as jest.MockedFunction< - typeof getWindowsDriverVersion -> - -const mockGetActiveInterfaces = getActiveInterfaces as jest.MockedFunction< - typeof getActiveInterfaces -> - -const mockCreateNetworkInterfaceMonitor = createNetworkInterfaceMonitor as jest.MockedFunction< - typeof createNetworkInterfaceMonitor -> - -const isWindows = OS.isWindows as jest.MockedFunction - -const appOnce = app.once as jest.MockedFunction +vi.mock('../../os') +vi.mock('../usb-devices') +vi.mock('../network-interfaces') +vi.mock('electron-store') const flush = (): Promise => new Promise(resolve => setTimeout(resolve, 0)) describe('app-shell::system-info module action tests', () => { - const dispatch = jest.fn() - const getAllDevices = jest.fn() - const usbMonitor: UsbDeviceMonitor = { getAllDevices, stop: jest.fn() } - const ifaceMonitor: NetworkInterfaceMonitor = { stop: jest.fn() } + const dispatch = vi.fn() + const getAllDevices = vi.fn() + const usbMonitor: UsbDeviceMonitor = { getAllDevices, stop: vi.fn() } + const ifaceMonitor: NetworkInterfaceMonitor = { stop: vi.fn() } const { windowsDriverVersion: _, ...notRealtek } = Fixtures.mockUsbDevice const realtek0 = { ...notRealtek, manufacturerName: 'Realtek' } const realtek1 = { ...notRealtek, manufacturerName: 'realtek' } @@ -54,18 +36,18 @@ describe('app-shell::system-info module action tests', () => { beforeEach(() => { handler = registerSystemInfo(dispatch) - isWindows.mockReturnValue(false) - mockCreateUsbDeviceMonitor.mockReturnValue(usbMonitor) - mockCreateNetworkInterfaceMonitor.mockReturnValue(ifaceMonitor) + vi.mocked(OS.isWindows).mockReturnValue(false) + vi.mocked(createUsbDeviceMonitor).mockReturnValue(usbMonitor) + vi.mocked(createNetworkInterfaceMonitor).mockReturnValue(ifaceMonitor) getAllDevices.mockResolvedValue([realtek0]) - mockGetActiveInterfaces.mockReturnValue([ + vi.mocked(getActiveInterfaces).mockReturnValue([ Fixtures.mockNetworkInterface, Fixtures.mockNetworkInterfaceV6, ]) }) afterEach(() => { - jest.resetAllMocks() + vi.resetAllMocks() }) it('sends initial USB device and network list on shell:UI_INITIALIZED', () => { @@ -78,7 +60,7 @@ describe('app-shell::system-info module action tests', () => { [Fixtures.mockNetworkInterface, Fixtures.mockNetworkInterfaceV6] ) ) - expect(mockGetWindowsDriverVersion).toHaveBeenCalledTimes(0) + expect(vi.mocked(getWindowsDriverVersion)).toHaveBeenCalledTimes(0) }) }) @@ -88,14 +70,14 @@ describe('app-shell::system-info module action tests', () => { return flush().then(() => { expect(createUsbDeviceMonitor).toHaveBeenCalledTimes(1) - expect(mockCreateNetworkInterfaceMonitor).toHaveBeenCalledTimes(1) + expect(vi.mocked(createNetworkInterfaceMonitor)).toHaveBeenCalledTimes(1) expect(dispatch).toHaveBeenCalledTimes(2) }) }) it('sends systemInfo:USB_DEVICE_ADDED when device added', () => { handler(uiInitialized()) - const usbMonitorOptions = mockCreateUsbDeviceMonitor.mock.calls[0][0] + const usbMonitorOptions = vi.mocked(createUsbDeviceMonitor).mock.calls[0][0] expect(usbMonitorOptions?.onDeviceAdd).toEqual(expect.any(Function)) const onDeviceAdd = usbMonitorOptions?.onDeviceAdd ?? noop @@ -109,7 +91,7 @@ describe('app-shell::system-info module action tests', () => { it('sends systemInfo:USB_DEVICE_REMOVED when device removed', () => { handler(uiInitialized()) - const usbMonitorOptions = mockCreateUsbDeviceMonitor.mock.calls[0][0] + const usbMonitorOptions = vi.mocked(createUsbDeviceMonitor).mock.calls[0][0] expect(usbMonitorOptions?.onDeviceRemove).toEqual(expect.any(Function)) const onDeviceRemove = usbMonitorOptions?.onDeviceRemove ?? noop @@ -124,7 +106,8 @@ describe('app-shell::system-info module action tests', () => { it('sends systemInfo:NETWORK_INTERFACES_CHANGED when ifaces change', () => { handler(uiInitialized()) - const ifaceMonitorOpts = mockCreateNetworkInterfaceMonitor.mock.calls[0][0] + const ifaceMonitorOpts = vi.mocked(createNetworkInterfaceMonitor).mock + .calls[0][0] expect(ifaceMonitorOpts.onInterfaceChange).toEqual(expect.any(Function)) const { onInterfaceChange } = ifaceMonitorOpts @@ -147,7 +130,7 @@ describe('app-shell::system-info module action tests', () => { it('stops monitoring on app quit', () => { handler(uiInitialized()) - const appQuitHandler = appOnce.mock.calls.find( + const appQuitHandler = vi.mocked(app.once).mock.calls.find( // @ts-expect-error(mc, 2021-02-17): event strings don't match, investigate ([event, handler]) => event === 'will-quit' )?.[1] @@ -160,8 +143,8 @@ describe('app-shell::system-info module action tests', () => { describe('on windows', () => { beforeEach(() => { - isWindows.mockReturnValue(true) - mockGetWindowsDriverVersion.mockResolvedValue('1.2.3') + vi.mocked(OS.isWindows).mockReturnValue(true) + vi.mocked(getWindowsDriverVersion).mockResolvedValue('1.2.3') }) it('should add Windows driver versions to Realtek devices on initialization', () => { @@ -169,8 +152,12 @@ describe('app-shell::system-info module action tests', () => { handler(uiInitialized()) return flush().then(() => { - expect(mockGetWindowsDriverVersion).toHaveBeenCalledWith(realtek0) - expect(mockGetWindowsDriverVersion).toHaveBeenCalledWith(realtek1) + expect(vi.mocked(getWindowsDriverVersion)).toHaveBeenCalledWith( + realtek0 + ) + expect(vi.mocked(getWindowsDriverVersion)).toHaveBeenCalledWith( + realtek1 + ) expect(dispatch).toHaveBeenCalledWith( SystemInfo.initialized( @@ -188,12 +175,15 @@ describe('app-shell::system-info module action tests', () => { it('should add Windows driver versions to Realtek devices on add', () => { getAllDevices.mockResolvedValue([]) handler(uiInitialized()) - const usbMonitorOptions = mockCreateUsbDeviceMonitor.mock.calls[0][0] + const usbMonitorOptions = vi.mocked(createUsbDeviceMonitor).mock + .calls[0][0] const onDeviceAdd = usbMonitorOptions?.onDeviceAdd ?? noop onDeviceAdd(realtek0) return flush().then(() => { - expect(mockGetWindowsDriverVersion).toHaveBeenCalledWith(realtek0) + expect(vi.mocked(getWindowsDriverVersion)).toHaveBeenCalledWith( + realtek0 + ) expect(dispatch).toHaveBeenCalledWith( SystemInfo.usbDeviceAdded({ diff --git a/app-shell/src/system-info/__tests__/usb-devices.test.ts b/app-shell/src/system-info/__tests__/usb-devices.test.ts index d239330a8df..f131d976991 100644 --- a/app-shell/src/system-info/__tests__/usb-devices.test.ts +++ b/app-shell/src/system-info/__tests__/usb-devices.test.ts @@ -1,30 +1,15 @@ import execa from 'execa' import { usb } from 'usb' +import { vi, it, expect, describe, afterEach } from 'vitest' import * as Fixtures from '@opentrons/app/src/redux/system-info/__fixtures__' import { createUsbDeviceMonitor, getWindowsDriverVersion } from '../usb-devices' import { isWindows } from '../../os' -jest.mock('execa') -jest.mock('usb') - -const usbGetDeviceList = usb.getDeviceList as jest.MockedFunction< - typeof usb.getDeviceList -> - -const usbDeviceGetStringDescriptor = jest.fn() as jest.MockedFunction< - InstanceType['getStringDescriptor'] -> - -const usbDeviceOpen = jest.fn() as jest.MockedFunction< - InstanceType['open'] -> -const usbDeviceClose = jest.fn() as jest.MockedFunction< - InstanceType['close'] -> -const usbOn = usb.on as jest.MockedFunction - -const execaCommand = execa.command as jest.MockedFunction +vi.mock('execa') +vi.mock('usb') +vi.mock('electron-store') +//TOME: App import change thing. const mockFixtureDevice = { ...Fixtures.mockUsbDevice, @@ -72,16 +57,16 @@ const getProductIterator = () => { const mockUSBDevice = { ...mockDescriptor, - getStringDescriptor: usbDeviceGetStringDescriptor, - open: usbDeviceOpen, - close: usbDeviceClose, + getStringDescriptor: vi.mocked(usb.Device), + open: vi.mocked(usb.Device), + close: vi.mocked(usb.Device), } if (!isWindows()) { describe('app-shell::system-info::usb-devices::detection', () => { const { windowsDriverVersion: _, ...mockDevice } = Fixtures.mockUsbDevice afterEach(() => { - jest.resetAllMocks() + vi.resetAllMocks() }) it('can return the list of all devices', async () => { @@ -89,13 +74,14 @@ if (!isWindows()) { const serialIterator = getSerialIterator() const mfrIterator = getManufacturerIterator() const productIterator = getProductIterator() - usbGetDeviceList.mockReturnValueOnce(mockDevices) - usbDeviceGetStringDescriptor.mockImplementation( - (descriptorId, callback) => - callback( - undefined, - [serialIterator, mfrIterator, productIterator][descriptorId]() - ) + vi.mocked(usb.getDeviceList).mockReturnValueOnce(mockDevices) + vi.mocked( + usb.Device as usb.Device['getStringDescriptor'] + ).mockImplementation((descriptorId, callback) => + callback( + undefined, + [serialIterator, mfrIterator, productIterator][descriptorId]() + ) ) const monitor = createUsbDeviceMonitor() @@ -126,7 +112,7 @@ if (!isWindows()) { it('can notify when devices are added', () => new Promise((resolve, reject) => { - const onDeviceAdd = jest.fn() + const onDeviceAdd = vi.fn() onDeviceAdd.mockImplementation(device => { try { expect(device).toEqual({ @@ -141,15 +127,14 @@ if (!isWindows()) { } }) let attachListener - usbOn.mockImplementationOnce((event, listener) => { + vi.mocked(usb.on).mockImplementationOnce((event, listener) => { if (event === 'attach') { attachListener = listener } }) createUsbDeviceMonitor({ onDeviceAdd }) - usbDeviceGetStringDescriptor.mockImplementation( - (descriptorId, callback) => - callback(undefined, ['sn1', 'mfr1', 'pn1'][descriptorId]) + vi.mocked(usb.Device).mockImplementation((descriptorId, callback) => + callback(undefined, ['sn1', 'mfr1', 'pn1'][descriptorId]) ) if (attachListener) { // @ts-expect-error: this is gross @@ -161,7 +146,7 @@ if (!isWindows()) { it('can notify when devices are removed', () => new Promise((resolve, reject) => { - const onDeviceRemove = jest.fn() + const onDeviceRemove = vi.fn() onDeviceRemove.mockImplementation(device => { try { expect(device).toEqual({ @@ -181,12 +166,12 @@ if (!isWindows()) { let detachListener - usbOn.mockImplementationOnce((event, listener) => { + vi.mocked(usb.on).mockImplementationOnce((event, listener) => { if (event === 'detach') { detachListener = listener } }) - usbDeviceOpen.mockImplementation(() => { + vi.mocked(usb.Device).mockImplementation(() => { throw new Error('Cannot open detached device') }) createUsbDeviceMonitor({ onDeviceRemove }) @@ -203,11 +188,11 @@ if (!isWindows()) { describe('app-shell::system-info::usb-devices', () => { const { windowsDriverVersion: _, ...mockDevice } = Fixtures.mockUsbDevice afterEach(() => { - jest.resetAllMocks() + vi.resetAllMocks() }) it('can get the Windows driver version of a device', () => { - execaCommand.mockResolvedValue({ stdout: '1.2.3' } as any) + vi.mocked(execa.command).mockResolvedValue({ stdout: '1.2.3' } as any) const device = { ...mockDevice, @@ -231,7 +216,7 @@ describe('app-shell::system-info::usb-devices', () => { }) it('returns null for unknown if command errors out', () => { - execaCommand.mockRejectedValue('AH!') + vi.mocked(execa.command).mockRejectedValue('AH!') return getWindowsDriverVersion(mockDevice).then(version => { expect(version).toBe(null)