From 602bce34c8f7c2f8c68dd18f29243e467ed0fb08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20Marussy?= Date: Sun, 9 Jan 2022 21:02:36 +0100 Subject: build: Add eslint-plugin-jest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kristóf Marussy --- .eslintrc.cjs | 4 +++ package.json | 1 + .../src/controllers/__tests__/initConfig.spec.ts | 18 +++++++------- .../controllers/__tests__/initNativeTheme.spec.ts | 4 +-- .../__tests__/createSophieRenderer.spec.ts | 29 +++++++++++----------- yarn.lock | 20 ++++++++++++++- 6 files changed, 50 insertions(+), 26 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 1301de4..5bb3c21 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -9,6 +9,8 @@ module.exports = { 'airbnb/hooks', 'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/recommended-requiring-type-checking', + 'plugin:jest/recommended', + 'plugin:jest/style', 'plugin:prettier/recommended', ], env: { @@ -67,6 +69,8 @@ module.exports = { '@typescript-eslint/no-non-null-assertion': 'off', // Jest mocks use unbound method references. '@typescript-eslint/unbound-method': 'off', + // We can't turn this on yet, because it doesn't understand `mocked` from `jest-mock`. + // 'jest/unbound-method': 'error', }, }, { diff --git a/package.json b/package.json index 22cc738..5515d3d 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "eslint-formatter-gitlab": "^3.0.0", "eslint-import-resolver-typescript": "^2.5.0", "eslint-plugin-import": "^2.25.4", + "eslint-plugin-jest": "^25.3.4", "eslint-plugin-jsx-a11y": "^6.5.1", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-react": "^7.28.0", diff --git a/packages/main/src/controllers/__tests__/initConfig.spec.ts b/packages/main/src/controllers/__tests__/initConfig.spec.ts index 7b6d6ab..9a5f85e 100644 --- a/packages/main/src/controllers/__tests__/initConfig.spec.ts +++ b/packages/main/src/controllers/__tests__/initConfig.spec.ts @@ -56,7 +56,7 @@ describe('when initializing', () => { it('should create a new config file', async () => { await initConfig(config, persistenceService); - expect(persistenceService.writeConfig).toBeCalledTimes(1); + expect(persistenceService.writeConfig).toHaveBeenCalledTimes(1); }); it('should bail if there is an an error creating the config file', async () => { @@ -81,7 +81,7 @@ describe('when initializing', () => { it('should read the existing config file is there is one', async () => { await initConfig(config, persistenceService); - expect(persistenceService.writeConfig).not.toBeCalled(); + expect(persistenceService.writeConfig).not.toHaveBeenCalled(); expect(config.themeSource).toBe('dark'); }); @@ -138,14 +138,14 @@ describe('when it has loaded the config', () => { jest.advanceTimersByTime(lessThanThrottleMs); config.setThemeSource('light'); jest.advanceTimersByTime(throttleMs); - expect(persistenceService.writeConfig).toBeCalledTimes(1); + expect(persistenceService.writeConfig).toHaveBeenCalledTimes(1); }); it('should handle config writing errors gracefully', () => { mocked(persistenceService.writeConfig).mockRejectedValue(new Error('boo')); config.setThemeSource('dark'); jest.advanceTimersByTime(throttleMs); - expect(persistenceService.writeConfig).toBeCalledTimes(1); + expect(persistenceService.writeConfig).toHaveBeenCalledTimes(1); }); it('should read the config file when it has changed', async () => { @@ -157,7 +157,7 @@ describe('when it has loaded the config', () => { }); await configChangedCallback(); // Do not write back the changes we have just read. - expect(persistenceService.writeConfig).not.toBeCalled(); + expect(persistenceService.writeConfig).not.toHaveBeenCalled(); expect(config.themeSource).toBe('dark'); }); @@ -172,9 +172,9 @@ describe('when it has loaded the config', () => { expect(config.themeSource).not.toBe(-1); }); - it('should handle config writing errors gracefully', async () => { + it('should handle config reading errors gracefully', async () => { mocked(persistenceService.readConfig).mockRejectedValue(new Error('boo')); - await configChangedCallback(); + await expect(configChangedCallback()).resolves.not.toThrow(); }); describe('when it was disposed', () => { @@ -183,13 +183,13 @@ describe('when it has loaded the config', () => { }); it('should dispose the watcher', () => { - expect(watcherDisposer).toBeCalled(); + expect(watcherDisposer).toHaveBeenCalled(); }); it('should not listen to store changes any more', () => { config.setThemeSource('dark'); jest.advanceTimersByTime(2 * throttleMs); - expect(persistenceService.writeConfig).not.toBeCalled(); + expect(persistenceService.writeConfig).not.toHaveBeenCalled(); }); }); }); diff --git a/packages/main/src/controllers/__tests__/initNativeTheme.spec.ts b/packages/main/src/controllers/__tests__/initNativeTheme.spec.ts index d4068af..16acca5 100644 --- a/packages/main/src/controllers/__tests__/initNativeTheme.spec.ts +++ b/packages/main/src/controllers/__tests__/initNativeTheme.spec.ts @@ -49,7 +49,7 @@ beforeEach(() => { }); it('should register a nativeTheme updated listener', () => { - expect(nativeTheme.on).toBeCalledWith('updated', expect.anything()); + expect(nativeTheme.on).toHaveBeenCalledWith('updated', expect.anything()); }); it('should synchronize themeSource changes to the nativeTheme', () => { @@ -71,5 +71,5 @@ it('should remove the listener on dispose', () => { ([event]) => event === 'updated', )![1]; disposeSut(); - expect(nativeTheme.off).toBeCalledWith('updated', listener); + expect(nativeTheme.off).toHaveBeenCalledWith('updated', listener); }); diff --git a/packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts b/packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts index f63c3f6..b0af280 100644 --- a/packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts +++ b/packages/preload/src/contextBridge/__tests__/createSophieRenderer.spec.ts @@ -108,10 +108,10 @@ describe('SharedStoreConnector', () => { it('should request a snapshot from the main process', async () => { mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot); await sut.onSharedStoreChange(listener); - expect(ipcRenderer.invoke).toBeCalledWith( + expect(ipcRenderer.invoke).toHaveBeenCalledWith( RendererToMainIpcMessage.GetSharedStoreSnapshot, ); - expect(listener.onSnapshot).toBeCalledWith(snapshot); + expect(listener.onSnapshot).toHaveBeenCalledWith(snapshot); }); it('should catch IPC errors without exposing them', async () => { @@ -119,7 +119,7 @@ describe('SharedStoreConnector', () => { await expect( sut.onSharedStoreChange(listener), ).rejects.not.toHaveProperty('message', expect.stringMatching(/s3cr3t/)); - expect(listener.onSnapshot).not.toBeCalled(); + expect(listener.onSnapshot).not.toHaveBeenCalled(); }); it('should not pass on invalid snapshots', async () => { @@ -127,28 +127,28 @@ describe('SharedStoreConnector', () => { await expect(sut.onSharedStoreChange(listener)).rejects.toBeInstanceOf( Error, ); - expect(listener.onSnapshot).not.toBeCalled(); + expect(listener.onSnapshot).not.toHaveBeenCalled(); }); }); describe('dispatchAction', () => { it('should dispatch valid actions', () => { sut.dispatchAction(action); - expect(ipcRenderer.send).toBeCalledWith( + expect(ipcRenderer.send).toHaveBeenCalledWith( RendererToMainIpcMessage.DispatchAction, action, ); }); it('should not dispatch invalid actions', () => { - expect(() => sut.dispatchAction(invalidAction)).toThrowError(); - expect(ipcRenderer.send).not.toBeCalled(); + expect(() => sut.dispatchAction(invalidAction)).toThrow(); + expect(ipcRenderer.send).not.toHaveBeenCalled(); }); }); describe('when no listener is registered', () => { it('should discard the received patch without any error', () => { - onSharedStorePatch(event, patch); + expect(() => onSharedStorePatch(event, patch)).not.toThrow(); }); }); @@ -163,9 +163,10 @@ describe('SharedStoreConnector', () => { function itDoesNotPassPatchesToTheListener( name = 'should not pass patches to the listener', ): void { + // eslint-disable-next-line jest/valid-title -- Title is a string parameter. it(name, () => { onSharedStorePatch(event, patch); - expect(listener.onPatch).not.toBeCalled(); + expect(listener.onPatch).not.toHaveBeenCalled(); }); } @@ -177,14 +178,14 @@ describe('SharedStoreConnector', () => { it('should pass patches to the listener', () => { onSharedStorePatch(event, patch); - expect(listener.onPatch).toBeCalledWith(patch); + expect(listener.onPatch).toHaveBeenCalledWith(patch); }); it('should catch listener errors', () => { mocked(listener.onPatch).mockImplementation(() => { throw new Error(); }); - onSharedStorePatch(event, patch); + expect(() => onSharedStorePatch(event, patch)).not.toThrow(); }); itRefusesToRegisterAnotherListener(); @@ -264,17 +265,17 @@ describe('SharedStoreConnector', () => { it('should fetch a second snapshot', async () => { mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot2); await sut.onSharedStoreChange(listener2); - expect(ipcRenderer.invoke).toBeCalledWith( + expect(ipcRenderer.invoke).toHaveBeenCalledWith( RendererToMainIpcMessage.GetSharedStoreSnapshot, ); - expect(listener2.onSnapshot).toBeCalledWith(snapshot2); + expect(listener2.onSnapshot).toHaveBeenCalledWith(snapshot2); }); it('should pass the second snapshot to the new listener', async () => { mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot2); await sut.onSharedStoreChange(listener2); onSharedStorePatch(event, patch); - expect(listener2.onPatch).toBeCalledWith(patch); + expect(listener2.onPatch).toHaveBeenCalledWith(patch); }); }); }); diff --git a/yarn.lock b/yarn.lock index 4d051ac..3b96a84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1699,7 +1699,7 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/experimental-utils@npm:5.9.0": +"@typescript-eslint/experimental-utils@npm:5.9.0, @typescript-eslint/experimental-utils@npm:^5.0.0": version: 5.9.0 resolution: "@typescript-eslint/experimental-utils@npm:5.9.0" dependencies: @@ -4080,6 +4080,23 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-jest@npm:^25.3.4": + version: 25.3.4 + resolution: "eslint-plugin-jest@npm:25.3.4" + dependencies: + "@typescript-eslint/experimental-utils": ^5.0.0 + peerDependencies: + "@typescript-eslint/eslint-plugin": ^4.0.0 || ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + "@typescript-eslint/eslint-plugin": + optional: true + jest: + optional: true + checksum: 1ad168a25678025ac3b7f637683ae68e49972a091b7a57ab5e41ceadf7f9807b112855c9b0d38a332a033289a224bff0ae2c8d9a05326ca1d6c3007048794898 + languageName: node + linkType: hard + "eslint-plugin-jsx-a11y@npm:^6.5.1": version: 6.5.1 resolution: "eslint-plugin-jsx-a11y@npm:6.5.1" @@ -7992,6 +8009,7 @@ __metadata: eslint-formatter-gitlab: ^3.0.0 eslint-import-resolver-typescript: ^2.5.0 eslint-plugin-import: ^2.25.4 + eslint-plugin-jest: ^25.3.4 eslint-plugin-jsx-a11y: ^6.5.1 eslint-plugin-prettier: ^4.0.0 eslint-plugin-react: ^7.28.0 -- cgit v1.2.3