From 33c4fd3efe831925615f0fe09f8714308ed364db Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Mon, 27 Dec 2021 16:47:00 +0100 Subject: refactor: Simplify preload Jest mocking keeps the electron interaction testable --- .../__tests__/SophieRendererImpl.spec.ts | 68 +++++++++++++--------- 1 file changed, 39 insertions(+), 29 deletions(-) (limited to 'packages/preload/src/contextBridge/__tests__/SophieRendererImpl.spec.ts') diff --git a/packages/preload/src/contextBridge/__tests__/SophieRendererImpl.spec.ts b/packages/preload/src/contextBridge/__tests__/SophieRendererImpl.spec.ts index bd2b1d5..2f295e6 100644 --- a/packages/preload/src/contextBridge/__tests__/SophieRendererImpl.spec.ts +++ b/packages/preload/src/contextBridge/__tests__/SophieRendererImpl.spec.ts @@ -19,13 +19,21 @@ */ import { mocked } from 'jest-mock'; +import { ipcRenderer } from 'electron'; import type { IJsonPatch } from 'mobx-state-tree'; -import type { Action, SharedStoreSnapshotIn, SophieRenderer } from '@sophie/shared'; +import { + Action, + MainToRendererIpcMessage, + RendererToMainIpcMessage, + SharedStoreSnapshotIn, + SophieRenderer, +} from '@sophie/shared'; -import { IpcRendererService } from '../../services/IpcRendererService'; import { createSophieRenderer } from '../SophieRendererImpl'; -jest.mock('../../services/IpcRendererService'); +jest.mock('electron'); + +const event: Electron.IpcRendererEvent = null as unknown as Electron.IpcRendererEvent; const snapshot: SharedStoreSnapshotIn = { shouldUseDarkColors: true, @@ -50,44 +58,46 @@ const invalidAction = { action: 'not-a-valid-action', } as unknown as Action; -describe('constructor', () => { +describe('createSophieRenderer', () => { it('registers a shared store patch listener', () => { - const service = new IpcRendererService(); - createSophieRenderer(service); - expect(service.onSharedStorePatch).toHaveBeenCalledTimes(1); + createSophieRenderer(); + expect(ipcRenderer.on).toHaveBeenCalledWith( + MainToRendererIpcMessage.SharedStorePatch, + expect.anything(), + ); }); }); -describe('instance', () => { +describe('SophieRendererImpl', () => { let sut: SophieRenderer; - let service: IpcRendererService; - let onSharedStorePatch: (patch: unknown) => void; + let onSharedStorePatch: (event: Electron.IpcRendererEvent, patch: unknown) => void; let listener = { onSnapshot: jest.fn((_snapshot: SharedStoreSnapshotIn) => {}), onPatch: jest.fn((_patch: IJsonPatch) => {}), }; beforeEach(() => { - service = new IpcRendererService(); - sut = createSophieRenderer(service); - onSharedStorePatch = mocked(service.onSharedStorePatch).mock.calls[0][0]; + sut = createSophieRenderer(); + onSharedStorePatch = mocked(ipcRenderer.on).mock.calls.find(([channel]) => { + return channel === MainToRendererIpcMessage.SharedStorePatch; + })?.[1]!; }); describe('onSharedStoreChange', () => { it('requests a snapshot from the service', async () => { - mocked(service.getSharedStoreSnapshot).mockResolvedValueOnce(snapshot); + mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot); await sut.onSharedStoreChange(listener); - expect(service.getSharedStoreSnapshot).toBeCalledTimes(1); + expect(ipcRenderer.invoke).toBeCalledWith(RendererToMainIpcMessage.GetSharedStoreSnapshot); }); it('passes the snapshot to the listener', async () => { - mocked(service.getSharedStoreSnapshot).mockResolvedValueOnce(snapshot); + mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot); await sut.onSharedStoreChange(listener); expect(listener.onSnapshot).toBeCalledWith(snapshot); }); it('catches service errors without exposing them', async () => { - mocked(service.getSharedStoreSnapshot).mockRejectedValue(new Error('s3cr3t')); + mocked(ipcRenderer.invoke).mockRejectedValue(new Error('s3cr3t')); await expect(sut.onSharedStoreChange(listener)).rejects.not.toHaveProperty( 'message', expect.stringMatching(/s3cr3t/), @@ -96,7 +106,7 @@ describe('instance', () => { }); it('does not pass on invalid snapshots', async () => { - mocked(service.getSharedStoreSnapshot).mockResolvedValueOnce(invalidSnapshot); + mocked(ipcRenderer.invoke).mockResolvedValueOnce(invalidSnapshot); await expect(sut.onSharedStoreChange(listener)).rejects.toBeInstanceOf(Error); expect(listener.onSnapshot).toBeCalledTimes(0); }); @@ -105,18 +115,18 @@ describe('instance', () => { describe('dispatchAction', () => { it('dispatched valid actions', () => { sut.dispatchAction(action); - expect(service.dispatchAction).toBeCalledWith(action); + expect(ipcRenderer.send).toBeCalledWith(RendererToMainIpcMessage.DispatchAction, action); }); it('does not dispatch invalid actions', () => { expect(() => sut.dispatchAction(invalidAction)).toThrowError(); - expect(service.dispatchAction).toBeCalledTimes(0); + expect(ipcRenderer.send).toBeCalledTimes(0); }); }); describe('when no listener is registered', () => { it('discards the received patch', () => { - onSharedStorePatch(patch); + onSharedStorePatch(event, patch); }); }); @@ -128,25 +138,25 @@ describe('instance', () => { function itDoesNotPassPatchesToTheListener() { it('does not pass patches to the listener', () => { - onSharedStorePatch(patch); + onSharedStorePatch(event, patch); expect(listener.onPatch).toBeCalledTimes(0); }); } describe('when a listener registered successfully', () => { beforeEach(async () => { - mocked(service.getSharedStoreSnapshot).mockResolvedValueOnce(snapshot); + mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot); await sut.onSharedStoreChange(listener); }); it('passes patches to the listener', () => { - onSharedStorePatch(patch); + onSharedStorePatch(event, patch); expect(listener.onPatch).toBeCalledWith(patch); }); it('catches listener errors', () => { mocked(listener.onPatch).mockImplementation(() => { throw new Error(); }); - onSharedStorePatch(patch); + onSharedStorePatch(event, patch); }); itRefusesToRegisterAnotherListener(); @@ -154,7 +164,7 @@ describe('instance', () => { describe('that later threw an error', () => { beforeEach(() => { mocked(listener.onPatch).mockImplementation(() => { throw new Error(); }); - onSharedStorePatch(patch); + onSharedStorePatch(event, patch); listener.onPatch.mockRestore(); }); @@ -164,7 +174,7 @@ describe('instance', () => { describe('when a listener failed to register due to service error', () => { beforeEach(async () => { - mocked(service.getSharedStoreSnapshot).mockRejectedValue(new Error()); + mocked(ipcRenderer.invoke).mockRejectedValue(new Error()); try { await sut.onSharedStoreChange(listener); } catch { @@ -179,7 +189,7 @@ describe('instance', () => { describe('when a listener failed to register due to an invalid snapshot', () => { beforeEach(async () => { - mocked(service.getSharedStoreSnapshot).mockResolvedValueOnce(invalidSnapshot); + mocked(ipcRenderer.invoke).mockResolvedValueOnce(invalidSnapshot); try { await sut.onSharedStoreChange(listener); } catch { @@ -194,7 +204,7 @@ describe('instance', () => { describe('when a listener failed to register due to listener error', () => { beforeEach(async () => { - mocked(service.getSharedStoreSnapshot).mockResolvedValueOnce(snapshot); + mocked(ipcRenderer.invoke).mockResolvedValueOnce(snapshot); mocked(listener.onSnapshot).mockImplementation(() => { throw new Error(); }); try { await sut.onSharedStoreChange(listener); -- cgit v1.2.3-54-g00ecf