diff --git a/e2e/js/api.spec.js b/e2e/js/api.spec.js deleted file mode 100644 index 648fbc03d..000000000 --- a/e2e/js/api.spec.js +++ /dev/null @@ -1,748 +0,0 @@ -import { expect } from '@wdio/globals'; -import { browser } from 'wdio-electron-service'; - -const { name: pkgAppName, version: pkgAppVersion } = globalThis.packageJson; - -describe('mock', () => { - it('should mock an electron API function', async () => { - const mockShowOpenDialog = await browser.electron.mock('dialog', 'showOpenDialog'); - await browser.electron.execute(async (electron) => { - await electron.dialog.showOpenDialog({ - title: 'my dialog', - properties: ['openFile', 'openDirectory'], - }); - return electron.dialog.showOpenDialog.mock.calls; - }); - - expect(mockShowOpenDialog).toHaveBeenCalledTimes(1); - expect(mockShowOpenDialog).toHaveBeenCalledWith({ - title: 'my dialog', - properties: ['openFile', 'openDirectory'], - }); - }); - - it('should mock a synchronous electron API function', async () => { - const mockShowOpenDialogSync = await browser.electron.mock('dialog', 'showOpenDialogSync'); - await browser.electron.execute((electron) => - electron.dialog.showOpenDialogSync({ - title: 'my dialog', - properties: ['openFile', 'openDirectory'], - }), - ); - - expect(mockShowOpenDialogSync).toHaveBeenCalledTimes(1); - expect(mockShowOpenDialogSync).toHaveBeenCalledWith({ - title: 'my dialog', - properties: ['openFile', 'openDirectory'], - }); - }); -}); - -describe('mockAll', () => { - it('should mock all functions on an API', async () => { - const mockedDialog = await browser.electron.mockAll('dialog'); - await browser.electron.execute( - async (electron) => - await electron.dialog.showOpenDialog({ - title: 'my dialog', - }), - ); - await browser.electron.execute((electron) => - electron.dialog.showOpenDialogSync({ - title: 'my dialog', - }), - ); - - expect(mockedDialog.showOpenDialog).toHaveBeenCalledTimes(1); - expect(mockedDialog.showOpenDialog).toHaveBeenCalledWith({ - title: 'my dialog', - }); - expect(mockedDialog.showOpenDialogSync).toHaveBeenCalledTimes(1); - expect(mockedDialog.showOpenDialogSync).toHaveBeenCalledWith({ - title: 'my dialog', - }); - }); -}); - -describe('clearAllMocks', () => { - it('should clear existing mocks', async () => { - const mockSetName = await browser.electron.mock('app', 'setName'); - const mockWriteText = await browser.electron.mock('clipboard', 'writeText'); - - await browser.electron.execute((electron) => electron.app.setName('new app name')); - await browser.electron.execute((electron) => electron.clipboard.writeText('text to be written')); - - await browser.electron.clearAllMocks(); - - expect(mockSetName.mock.calls).toStrictEqual([]); - expect(mockSetName.mock.invocationCallOrder).toStrictEqual([]); - expect(mockSetName.mock.lastCall).toBeUndefined(); - expect(mockSetName.mock.results).toStrictEqual([]); - - expect(mockWriteText.mock.calls).toStrictEqual([]); - expect(mockWriteText.mock.invocationCallOrder).toStrictEqual([]); - expect(mockWriteText.mock.lastCall).toBeUndefined(); - expect(mockWriteText.mock.results).toStrictEqual([]); - }); - - it('should clear existing mocks on an API', async () => { - const mockSetName = await browser.electron.mock('app', 'setName'); - const mockWriteText = await browser.electron.mock('clipboard', 'writeText'); - - await browser.electron.execute((electron) => electron.app.setName('new app name')); - await browser.electron.execute((electron) => electron.clipboard.writeText('text to be written')); - - await browser.electron.clearAllMocks('app'); - - expect(mockSetName.mock.calls).toStrictEqual([]); - expect(mockSetName.mock.invocationCallOrder).toStrictEqual([]); - expect(mockSetName.mock.lastCall).toBeUndefined(); - expect(mockSetName.mock.results).toStrictEqual([]); - - expect(mockWriteText.mock.calls).toStrictEqual([['text to be written']]); - expect(mockWriteText.mock.invocationCallOrder).toStrictEqual([expect.any(Number)]); - expect(mockWriteText.mock.lastCall).toStrictEqual(['text to be written']); - expect(mockWriteText.mock.results).toStrictEqual([{ type: 'return', value: undefined }]); - }); - - it('should not reset existing mocks', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - const mockReadText = await browser.electron.mock('clipboard', 'readText'); - await mockGetName.mockReturnValue('mocked appName'); - await mockReadText.mockReturnValue('mocked clipboardText'); - - await browser.electron.clearAllMocks(); - - const appName = await browser.electron.execute((electron) => electron.app.getName()); - const clipboardText = await browser.electron.execute((electron) => electron.clipboard.readText()); - expect(appName).toBe('mocked appName'); - expect(clipboardText).toBe('mocked clipboardText'); - }); - - it('should not reset existing mocks on an API', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - const mockReadText = await browser.electron.mock('clipboard', 'readText'); - await mockGetName.mockReturnValue('mocked appName'); - await mockReadText.mockReturnValue('mocked clipboardText'); - - await browser.electron.clearAllMocks('app'); - - const appName = await browser.electron.execute((electron) => electron.app.getName()); - const clipboardText = await browser.electron.execute((electron) => electron.clipboard.readText()); - expect(appName).toBe('mocked appName'); - expect(clipboardText).toBe('mocked clipboardText'); - }); -}); - -describe('resetAllMocks', () => { - it('should clear existing mocks', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - const mockReadText = await browser.electron.mock('clipboard', 'readText'); - await mockGetName.mockReturnValue('mocked appName'); - await mockReadText.mockReturnValue('mocked clipboardText'); - - await browser.electron.execute((electron) => electron.app.getName()); - await browser.electron.execute((electron) => electron.clipboard.readText()); - - await browser.electron.resetAllMocks(); - - expect(mockGetName.mock.calls).toStrictEqual([]); - expect(mockGetName.mock.invocationCallOrder).toStrictEqual([]); - expect(mockGetName.mock.lastCall).toBeUndefined(); - expect(mockGetName.mock.results).toStrictEqual([]); - - expect(mockReadText.mock.calls).toStrictEqual([]); - expect(mockReadText.mock.invocationCallOrder).toStrictEqual([]); - expect(mockReadText.mock.lastCall).toBeUndefined(); - expect(mockReadText.mock.results).toStrictEqual([]); - }); - - it('should clear existing mocks on an API', async () => { - const mockSetName = await browser.electron.mock('app', 'setName'); - const mockWriteText = await browser.electron.mock('clipboard', 'writeText'); - - await browser.electron.execute((electron) => electron.app.setName('new app name')); - await browser.electron.execute((electron) => electron.clipboard.writeText('text to be written')); - - await browser.electron.resetAllMocks('app'); - - expect(mockSetName.mock.calls).toStrictEqual([]); - expect(mockSetName.mock.invocationCallOrder).toStrictEqual([]); - expect(mockSetName.mock.lastCall).toBeUndefined(); - expect(mockSetName.mock.results).toStrictEqual([]); - - expect(mockWriteText.mock.calls).toStrictEqual([['text to be written']]); - expect(mockWriteText.mock.invocationCallOrder).toStrictEqual([expect.any(Number)]); - expect(mockWriteText.mock.lastCall).toStrictEqual(['text to be written']); - expect(mockWriteText.mock.results).toStrictEqual([{ type: 'return', value: undefined }]); - }); - - it('should reset existing mocks', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - const mockReadText = await browser.electron.mock('clipboard', 'readText'); - await mockGetName.mockReturnValue('mocked appName'); - await mockReadText.mockReturnValue('mocked clipboardText'); - - await browser.electron.resetAllMocks(); - - const appName = await browser.electron.execute((electron) => electron.app.getName()); - const clipboardText = await browser.electron.execute((electron) => electron.clipboard.readText()); - expect(appName).toBeUndefined(); - expect(clipboardText).toBeUndefined(); - }); - - it('should reset existing mocks on an API', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - const mockReadText = await browser.electron.mock('clipboard', 'readText'); - await mockGetName.mockReturnValue('mocked appName'); - await mockReadText.mockReturnValue('mocked clipboardText'); - - await browser.electron.resetAllMocks('app'); - - const appName = await browser.electron.execute((electron) => electron.app.getName()); - const clipboardText = await browser.electron.execute((electron) => electron.clipboard.readText()); - expect(appName).toBeUndefined(); - expect(clipboardText).toBe('mocked clipboardText'); - }); -}); - -describe('restoreAllMocks', () => { - beforeEach(async () => { - await browser.electron.execute((electron) => { - electron.clipboard.clear(); - electron.clipboard.writeText('some real clipboard text'); - }); - }); - - it('should restore existing mocks', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - const mockReadText = await browser.electron.mock('clipboard', 'readText'); - await mockGetName.mockReturnValue('mocked appName'); - await mockReadText.mockReturnValue('mocked clipboardText'); - - await browser.electron.restoreAllMocks(); - - const appName = await browser.electron.execute((electron) => electron.app.getName()); - const clipboardText = await browser.electron.execute((electron) => electron.clipboard.readText()); - expect(appName).toBe(pkgAppName); - expect(clipboardText).toBe('some real clipboard text'); - }); - - it('should restore existing mocks on an API', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - const mockReadText = await browser.electron.mock('clipboard', 'readText'); - await mockGetName.mockReturnValue('mocked appName'); - await mockReadText.mockReturnValue('mocked clipboardText'); - - await browser.electron.restoreAllMocks('app'); - - const appName = await browser.electron.execute((electron) => electron.app.getName()); - const clipboardText = await browser.electron.execute((electron) => electron.clipboard.readText()); - expect(appName).toBe(pkgAppName); - expect(clipboardText).toBe('mocked clipboardText'); - }); -}); - -describe('isMockFunction', () => { - it('should return true when provided with an electron mock', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - - expect(browser.electron.isMockFunction(mockGetName)).toBe(true); - }); - - it('should return false when provided with a function', async () => { - expect(browser.electron.isMockFunction(() => {})).toBe(false); - }); - - it('should return false when provided with a vitest mock', async () => { - // We have to dynamic import `@vitest/spy` due to it being an ESM only module - const spy = await import('@vitest/spy'); - expect(browser.electron.isMockFunction(spy.fn())).toBe(false); - }); -}); - -describe('execute', () => { - it('should execute an arbitrary function in the electron main process', async () => { - expect( - await browser.electron.execute( - (electron, a, b, c) => { - const version = electron.app.getVersion(); - return [version, a + b + c]; - }, - 1, - 2, - 3, - ), - ).toEqual([pkgAppVersion, 6]); - }); - - it('should execute a string-based function in the electron main process', async () => { - await expect(browser.electron.execute('return 1 + 2 + 3')).resolves.toEqual(6); - }); - - it('should handle executing a function which declares a function', async () => { - expect( - await browser.execute(() => { - function newFunc() { - return 'boom!'; - } - return newFunc(); - }), - ).toEqual('boom!'); - }); - - it('should handle executing a function which declares a fat arrow function', async () => { - expect( - await browser.execute(() => { - const newFunc = () => 'boom!'; - return newFunc(); - }), - ).toEqual('boom!'); - }); -}); - -describe('mock object functionality', () => { - describe('mockImplementation', () => { - it('should use the specified implementation for an existing mock', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - let callsCount = 0; - await mockGetName.mockImplementation(() => { - // callsCount is not accessible in the electron context so we need to guard it - if (typeof callsCount !== 'undefined') { - callsCount++; - } - - return 'mocked value'; - }); - const result = await browser.electron.execute(async (electron) => await electron.app.getName()); - - expect(callsCount).toBe(1); - expect(result).toBe('mocked value'); - }); - }); - - describe('mockImplementationOnce', () => { - it('should use the specified implementation for an existing mock once', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - - await mockGetName.mockImplementation(() => 'default mocked name'); - await mockGetName.mockImplementationOnce(() => 'first mocked name'); - await mockGetName.mockImplementationOnce(() => 'second mocked name'); - await mockGetName.mockImplementationOnce(() => 'third mocked name'); - - let name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBe('first mocked name'); - name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBe('second mocked name'); - name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBe('third mocked name'); - name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBe('default mocked name'); - name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBe('default mocked name'); - }); - }); - - describe('mockReturnValue', () => { - it('should return the specified value from an existing mock', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - await mockGetName.mockReturnValue('This is a mock'); - - const electronName = await browser.electron.execute((electron) => electron.app.getName()); - - expect(electronName).toBe('This is a mock'); - }); - }); - - describe('mockReturnValueOnce', () => { - it('should return the specified value from an existing mock once', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - - await mockGetName.mockReturnValue('default mocked name'); - await mockGetName.mockReturnValueOnce('first mocked name'); - await mockGetName.mockReturnValueOnce('second mocked name'); - await mockGetName.mockReturnValueOnce('third mocked name'); - - let name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBe('first mocked name'); - name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBe('second mocked name'); - name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBe('third mocked name'); - name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBe('default mocked name'); - name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBe('default mocked name'); - }); - }); - - describe('mockResolvedValue', () => { - it('should resolve with the specified value from an existing mock', async () => { - const mockGetFileIcon = await browser.electron.mock('app', 'getFileIcon'); - await mockGetFileIcon.mockResolvedValue('This is a mock'); - - const fileIcon = await browser.electron.execute( - async (electron) => await electron.app.getFileIcon('/path/to/icon'), - ); - - expect(fileIcon).toBe('This is a mock'); - }); - }); - - describe('mockResolvedValueOnce', () => { - it('should resolve with the specified value from an existing mock once', async () => { - const mockGetFileIcon = await browser.electron.mock('app', 'getFileIcon'); - - await mockGetFileIcon.mockResolvedValue('default mocked icon'); - await mockGetFileIcon.mockResolvedValueOnce('first mocked icon'); - await mockGetFileIcon.mockResolvedValueOnce('second mocked icon'); - await mockGetFileIcon.mockResolvedValueOnce('third mocked icon'); - - let fileIcon = await browser.electron.execute( - async (electron) => await electron.app.getFileIcon('/path/to/icon'), - ); - expect(fileIcon).toBe('first mocked icon'); - fileIcon = await browser.electron.execute(async (electron) => await electron.app.getFileIcon('/path/to/icon')); - expect(fileIcon).toBe('second mocked icon'); - fileIcon = await browser.electron.execute(async (electron) => await electron.app.getFileIcon('/path/to/icon')); - expect(fileIcon).toBe('third mocked icon'); - fileIcon = await browser.electron.execute(async (electron) => await electron.app.getFileIcon('/path/to/icon')); - expect(fileIcon).toBe('default mocked icon'); - fileIcon = await browser.electron.execute(async (electron) => await electron.app.getFileIcon('/path/to/icon')); - expect(fileIcon).toBe('default mocked icon'); - }); - }); - - describe('mockRejectedValue', () => { - it('should reject with the specified value from an existing mock', async () => { - const mockGetFileIcon = await browser.electron.mock('app', 'getFileIcon'); - await mockGetFileIcon.mockRejectedValue('This is a mock error'); - - const fileIconError = await browser.electron.execute(async (electron) => { - try { - await electron.app.getFileIcon('/path/to/icon'); - } catch (e) { - return e; - } - }); - - expect(fileIconError).toBe('This is a mock error'); - }); - }); - - describe('mockRejectedValueOnce', () => { - it('should reject with the specified value from an existing mock once', async () => { - const mockGetFileIcon = await browser.electron.mock('app', 'getFileIcon'); - - await mockGetFileIcon.mockRejectedValue('default mocked icon error'); - await mockGetFileIcon.mockRejectedValueOnce('first mocked icon error'); - await mockGetFileIcon.mockRejectedValueOnce('second mocked icon error'); - await mockGetFileIcon.mockRejectedValueOnce('third mocked icon error'); - - const getFileIcon = async () => - await browser.electron.execute(async (electron) => { - try { - await electron.app.getFileIcon('/path/to/icon'); - } catch (e) { - return e; - } - }); - - let fileIcon = await getFileIcon(); - expect(fileIcon).toBe('first mocked icon error'); - fileIcon = await getFileIcon(); - expect(fileIcon).toBe('second mocked icon error'); - fileIcon = await getFileIcon(); - expect(fileIcon).toBe('third mocked icon error'); - fileIcon = await getFileIcon(); - expect(fileIcon).toBe('default mocked icon error'); - fileIcon = await getFileIcon(); - expect(fileIcon).toBe('default mocked icon error'); - }); - }); - - describe('mockClear', () => { - it('should clear an existing mock', async () => { - const mockShowOpenDialog = await browser.electron.mock('dialog', 'showOpenDialog'); - await mockShowOpenDialog.mockReturnValue('mocked name'); - - await browser.electron.execute((electron) => electron.dialog.showOpenDialog({})); - await browser.electron.execute((electron) => - electron.dialog.showOpenDialog({ - title: 'my dialog', - }), - ); - await browser.electron.execute((electron) => - electron.dialog.showOpenDialog({ - title: 'another dialog', - }), - ); - - await mockShowOpenDialog.mockClear(); - - expect(mockShowOpenDialog.mock.calls).toStrictEqual([]); - expect(mockShowOpenDialog.mock.invocationCallOrder).toStrictEqual([]); - expect(mockShowOpenDialog.mock.lastCall).toBeUndefined(); - expect(mockShowOpenDialog.mock.results).toStrictEqual([]); - }); - }); - - describe('mockReset', () => { - it('should reset the implementation of an existing mock', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - await mockGetName.mockReturnValue('mocked name'); - - await mockGetName.mockReset(); - - const name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBeUndefined(); - }); - - it('should reset mockReturnValueOnce implementations of an existing mock', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - await mockGetName.mockReturnValueOnce('first mocked name'); - await mockGetName.mockReturnValueOnce('second mocked name'); - await mockGetName.mockReturnValueOnce('third mocked name'); - - await mockGetName.mockReset(); - - const name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBeUndefined(); - }); - - it('should reset mockImplementationOnce implementations of an existing mock', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - await mockGetName.mockImplementationOnce(() => 'first mocked name'); - await mockGetName.mockImplementationOnce(() => 'second mocked name'); - await mockGetName.mockImplementationOnce(() => 'third mocked name'); - - await mockGetName.mockReset(); - - const name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBeUndefined(); - }); - - it('should clear the history of an existing mock', async () => { - const mockShowOpenDialog = await browser.electron.mock('dialog', 'showOpenDialog'); - await mockShowOpenDialog.mockReturnValue('mocked name'); - - await browser.electron.execute((electron) => electron.dialog.showOpenDialog({})); - await browser.electron.execute((electron) => - electron.dialog.showOpenDialog({ - title: 'my dialog', - }), - ); - await browser.electron.execute((electron) => - electron.dialog.showOpenDialog({ - title: 'another dialog', - }), - ); - - await mockShowOpenDialog.mockReset(); - - expect(mockShowOpenDialog.mock.calls).toStrictEqual([]); - expect(mockShowOpenDialog.mock.invocationCallOrder).toStrictEqual([]); - expect(mockShowOpenDialog.mock.lastCall).toBeUndefined(); - expect(mockShowOpenDialog.mock.results).toStrictEqual([]); - }); - }); - - describe('mockRestore', () => { - it('should restore an existing mock', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - await mockGetName.mockReturnValue('mocked name'); - - let name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBe('mocked name'); - - await mockGetName.mockRestore(); - - name = await browser.electron.execute((electron) => electron.app.getName()); - expect(name).toBe(pkgAppName); - }); - }); - - describe('getMockName', () => { - it('should retrieve the mock name', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - - expect(mockGetName.getMockName()).toBe('electron.app.getName'); - }); - }); - - describe('mockName', () => { - it('should set the mock name', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - mockGetName.mockName('my first mock'); - - expect(mockGetName.getMockName()).toBe('my first mock'); - }); - }); - - describe('getMockImplementation', () => { - it('should retrieve the mock implementation', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - await mockGetName.mockImplementation(() => 'mocked name'); - const mockImpl = mockGetName.getMockImplementation(); - - expect(mockImpl()).toBe('mocked name'); - }); - - it('should retrieve an empty mock implementation', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - const mockImpl = mockGetName.getMockImplementation(); - - expect(mockImpl()).toBeUndefined(); - }); - }); - - describe('mockReturnThis', () => { - it('should allow chaining', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - const mockGetVersion = await browser.electron.mock('app', 'getVersion'); - await mockGetName.mockReturnThis(); - await browser.electron.execute((electron) => electron.app.getName().getVersion()); - - expect(mockGetVersion).toHaveBeenCalled(); - }); - }); - - describe('withImplementation', () => { - it('should temporarily override mock implementation', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - await mockGetName.mockImplementation(() => 'default mock name'); - await mockGetName.mockImplementationOnce(() => 'first mock name'); - await mockGetName.mockImplementationOnce(() => 'second mock name'); - const withImplementationResult = await mockGetName.withImplementation( - () => 'temporary mock name', - (electron) => electron.app.getName(), - ); - - expect(withImplementationResult).toBe('temporary mock name'); - const firstName = await browser.electron.execute((electron) => electron.app.getName()); - expect(firstName).toBe('first mock name'); - const secondName = await browser.electron.execute((electron) => electron.app.getName()); - expect(secondName).toBe('second mock name'); - const thirdName = await browser.electron.execute((electron) => electron.app.getName()); - expect(thirdName).toBe('default mock name'); - }); - - it('should handle promises', async () => { - const mockGetFileIcon = await browser.electron.mock('app', 'getFileIcon'); - await mockGetFileIcon.mockResolvedValue('default mock icon'); - await mockGetFileIcon.mockResolvedValueOnce('first mock icon'); - await mockGetFileIcon.mockResolvedValueOnce('second mock icon'); - const withImplementationResult = await mockGetFileIcon.withImplementation( - () => Promise.resolve('temporary mock icon'), - async (electron) => await electron.app.getFileIcon('/path/to/icon'), - ); - - expect(withImplementationResult).toBe('temporary mock icon'); - const firstIcon = await browser.electron.execute((electron) => electron.app.getFileIcon('/path/to/icon')); - expect(firstIcon).toBe('first mock icon'); - const secondIcon = await browser.electron.execute((electron) => electron.app.getFileIcon('/path/to/icon')); - expect(secondIcon).toBe('second mock icon'); - const thirdIcon = await browser.electron.execute((electron) => electron.app.getFileIcon('/path/to/icon')); - expect(thirdIcon).toBe('default mock icon'); - }); - }); - - describe('mock.calls', () => { - it('should return the calls of the mock execution', async () => { - const mockGetFileIcon = await browser.electron.mock('app', 'getFileIcon'); - - await browser.electron.execute((electron) => electron.app.getFileIcon('/path/to/icon')); - await browser.electron.execute((electron) => - electron.app.getFileIcon('/path/to/another/icon', { size: 'small' }), - ); - - expect(mockGetFileIcon.mock.calls).toStrictEqual([ - ['/path/to/icon'], // first call - ['/path/to/another/icon', { size: 'small' }], // second call - ]); - }); - - it('should return an empty array when the mock was never invoked', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - - expect(mockGetName.mock.calls).toStrictEqual([]); - }); - }); - - describe('mock.lastCall', () => { - it('should return the last call of the mock execution', async () => { - const mockGetFileIcon = await browser.electron.mock('app', 'getFileIcon'); - - await browser.electron.execute((electron) => electron.app.getFileIcon('/path/to/icon')); - expect(mockGetFileIcon.mock.lastCall).toStrictEqual(['/path/to/icon']); - await browser.electron.execute((electron) => - electron.app.getFileIcon('/path/to/another/icon', { size: 'small' }), - ); - expect(mockGetFileIcon.mock.lastCall).toStrictEqual(['/path/to/another/icon', { size: 'small' }]); - await browser.electron.execute((electron) => - electron.app.getFileIcon('/path/to/a/massive/icon', { size: 'large' }), - ); - expect(mockGetFileIcon.mock.lastCall).toStrictEqual(['/path/to/a/massive/icon', { size: 'large' }]); - }); - - it('should return undefined when the mock was never invoked', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - - expect(mockGetName.mock.lastCall).toBeUndefined(); - }); - }); - - describe('mock.results', () => { - it('should return the results of the mock execution', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - - // TODO: why does `mockReturnValueOnce` not work for returning 'result' here? - await mockGetName.mockImplementationOnce(() => 'result'); - await mockGetName.mockImplementation(() => { - throw new Error('thrown error'); - }); - - await expect(browser.electron.execute((electron) => electron.app.getName())).resolves.toBe('result'); - await expect(browser.electron.execute((electron) => electron.app.getName())).rejects.toThrow('thrown error'); - - expect(mockGetName.mock.results).toStrictEqual([ - { - type: 'return', - value: 'result', - }, - { - type: 'throw', - value: new Error('thrown error'), - }, - ]); - }); - - it('should return an empty array when the mock was never invoked', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - - expect(mockGetName.mock.results).toStrictEqual([]); - }); - }); - - describe('mock.invocationCallOrder', () => { - it('should return the order of execution', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - const mockGetVersion = await browser.electron.mock('app', 'getVersion'); - - await browser.electron.execute((electron) => electron.app.getName()); - await browser.electron.execute((electron) => electron.app.getVersion()); - await browser.electron.execute((electron) => electron.app.getName()); - - const firstInvocationIndex = mockGetName.mock.invocationCallOrder[0]; - - expect(mockGetName.mock.invocationCallOrder).toStrictEqual([firstInvocationIndex, firstInvocationIndex + 2]); - expect(mockGetVersion.mock.invocationCallOrder).toStrictEqual([firstInvocationIndex + 1]); - }); - - it('should return an empty array when the mock was never invoked', async () => { - const mockGetName = await browser.electron.mock('app', 'getName'); - - expect(mockGetName.mock.invocationCallOrder).toStrictEqual([]); - }); - }); -}); diff --git a/e2e/js/application.spec.js b/e2e/js/application.spec.js deleted file mode 100644 index 2112f44ba..000000000 --- a/e2e/js/application.spec.js +++ /dev/null @@ -1,18 +0,0 @@ -import { expect } from '@wdio/globals'; -import { browser } from 'wdio-electron-service'; - -describe('application loading', () => { - describe('App', () => { - it('should launch the application', async () => { - const title = await browser.getTitle(); - expect(title).toEqual('Test'); - }); - - it('should pass args through to the launched application', async () => { - // custom args are set in the wdio.conf.js file as they need to be set before WDIO starts - const argv = await browser.electron.execute(() => process.argv); - expect(argv).toContain('--foo'); - expect(argv).toContain('--bar=baz'); - }); - }); -}); diff --git a/e2e/js/dom.spec.js b/e2e/js/dom.spec.js deleted file mode 100644 index 876e65e7a..000000000 --- a/e2e/js/dom.spec.js +++ /dev/null @@ -1,35 +0,0 @@ -import { expect } from '@wdio/globals'; -import { browser } from 'wdio-electron-service'; -import { setupBrowser } from '@testing-library/webdriverio'; - -describe('application loading', () => { - let screen; - - before(() => { - screen = setupBrowser(browser); - }); - - describe('DOM', () => { - it('should determine when an element is in the document', async () => { - await expect(await screen.getByTestId('disabled-checkbox')).toExist(); - }); - - it('should determine when an element is not in the document', async () => { - await expect(await screen.queryByTestId('not-there')).not.toExist(); - }); - - it('should determine when an element is visible', async () => { - await expect(await screen.getByTestId('disabled-checkbox')).toBeDisplayed(); - }); - - it('should determine when an element is not visible', async () => { - await expect(await screen.getByTestId('hidden-textarea')).not.toBeDisplayed(); - }); - }); - - describe('WDIO Matchers', () => { - it('should be able to use WebdriverIO matchers', async () => { - await expect(await $('#disabled-checkbox')).toBePresent(); - }); - }); -}); diff --git a/e2e/js/interaction.spec.js b/e2e/js/interaction.spec.js deleted file mode 100644 index 1eecbe60f..000000000 --- a/e2e/js/interaction.spec.js +++ /dev/null @@ -1,62 +0,0 @@ -import { expect } from '@wdio/globals'; -import { browser } from 'wdio-electron-service'; -import { setupBrowser } from '@testing-library/webdriverio'; - -describe('application loading', () => { - let screen; - - before(() => { - screen = setupBrowser(browser); - }); - - describe('keyboard input', () => { - it('should detect keyboard input', async () => { - await browser.keys(['y', 'o']); - expect(await (await screen.getByTestId('keypress-count')).getText()).toEqual('YO'); - }); - }); - - describe('click events', () => { - describe('when the make larger button is clicked', () => { - it('should increase the window height and width by 10 pixels', async () => { - let bounds = await browser.electron.execute((electron) => { - const browserWindow = electron.BrowserWindow.getAllWindows()[0]; - return browserWindow.getBounds(); - }); - expect(bounds.width).toEqual(200); - expect(bounds.height).toEqual(300); - let biggerClickCount = await browser.$('.click-count .bigger').getText(); - expect(biggerClickCount).toEqual('0'); - const elem = await browser.$('.make-bigger'); - await elem.click(); - biggerClickCount = await browser.$('.click-count .bigger').getText(); - expect(biggerClickCount).toEqual('1'); - bounds = await browser.electron.execute((electron) => { - const browserWindow = electron.BrowserWindow.getAllWindows()[0]; - return browserWindow.getBounds(); - }); - expect(bounds.width).toEqual(210); - expect(bounds.height).toEqual(310); - }); - }); - - describe('when the make smaller button is clicked', () => { - it('should decrease the window height and width by 10 pixels', async () => { - let bounds = await browser.electron.execute((electron) => { - const browserWindow = electron.BrowserWindow.getAllWindows()[0]; - return browserWindow.getBounds(); - }); - expect(bounds.width).toEqual(210); - expect(bounds.height).toEqual(310); - const elem = await browser.$('.make-smaller'); - await elem.click(); - bounds = await browser.electron.execute((electron) => { - const browserWindow = electron.BrowserWindow.getAllWindows()[0]; - return browserWindow.getBounds(); - }); - expect(bounds.width).toEqual(200); - expect(bounds.height).toEqual(300); - }); - }); - }); -}); diff --git a/e2e/package.json b/e2e/package.json index 77472a8cd..bfdd38b60 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -5,7 +5,8 @@ "private": "true", "scripts": { "ci": "pnpm i && pnpm test", - "clean": "pnpm dlx shx rm -rf ./node_modules pnpm-lock.yaml ./wdio-logs* ./out", + "clean": "pnpm dlx shx rm -rf ./js ./node_modules pnpm-lock.yaml ./wdio-logs* ./out", + "init": "pnpm dlx shx mkdir -p ./js && rollup -c", "logs": "pnpm dlx shx cat ./wdio-logs*/*.log", "test:e2e:forge-cjs": "cross-env EXAMPLE_DIR=forge-cjs pnpm run exec", "test:e2e:forge-esm": "cross-env EXAMPLE_DIR=forge-esm pnpm run exec", @@ -44,6 +45,7 @@ }, "devDependencies": { "@electron-forge/cli": "^7.2.0", + "@rollup/plugin-typescript": "^11.1.6", "@types/mocha": "^10.0.6", "@types/node": "^20.16.1", "@vitest/spy": "^2.0.5", @@ -51,6 +53,7 @@ "global-jsdom": "^24.0.0", "jsdom": "^24.1.1", "read-package-up": "^11.0.0", + "rollup": "^4.21.2", "ts-loader": "^9.4.4", "typescript": "^5.5.4" } diff --git a/e2e/rollup.config.js b/e2e/rollup.config.js new file mode 100644 index 000000000..0faa3758d --- /dev/null +++ b/e2e/rollup.config.js @@ -0,0 +1,10 @@ +import typescript from '@rollup/plugin-typescript'; + +export default { + input: '*.spec.ts', + output: { + dir: 'js', + format: 'esm', + }, + plugins: [typescript()], +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 858cb58be..d512a46db 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -465,6 +465,9 @@ importers: '@electron-forge/cli': specifier: ^7.2.0 version: 7.4.0(encoding@0.1.13) + '@rollup/plugin-typescript': + specifier: ^11.1.6 + version: 11.1.6(rollup@4.21.2)(tslib@2.7.0)(typescript@5.5.4) '@types/mocha': specifier: ^10.0.6 version: 10.0.7 @@ -486,6 +489,9 @@ importers: read-package-up: specifier: ^11.0.0 version: 11.0.0 + rollup: + specifier: ^4.21.2 + version: 4.21.2 ts-loader: specifier: ^9.4.4 version: 9.5.1(typescript@5.5.4)(webpack@5.94.0) @@ -1379,6 +1385,19 @@ packages: rollup: optional: true + '@rollup/plugin-typescript@11.1.6': + resolution: {integrity: sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.14.0||^3.0.0||^4.0.0 + tslib: '*' + typescript: '>=3.7.0' + peerDependenciesMeta: + rollup: + optional: true + tslib: + optional: true + '@rollup/pluginutils@5.1.0': resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} engines: {node: '>=14.0.0'} @@ -6584,6 +6603,15 @@ snapshots: optionalDependencies: rollup: 4.21.2 + '@rollup/plugin-typescript@11.1.6(rollup@4.21.2)(tslib@2.7.0)(typescript@5.5.4)': + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.21.2) + resolve: 1.22.8 + typescript: 5.5.4 + optionalDependencies: + rollup: 4.21.2 + tslib: 2.7.0 + '@rollup/pluginutils@5.1.0(rollup@4.21.2)': dependencies: '@types/estree': 1.0.5 diff --git a/turbo.json b/turbo.json index f9a960e02..5da2689e9 100644 --- a/turbo.json +++ b/turbo.json @@ -20,43 +20,46 @@ "//#lint": { "inputs": ["**/*.{j,mj,cj,t}s"] }, + "@repo/e2e#init": { + "dependsOn": ["wdio-electron-service#build"] + }, "@repo/e2e#test:e2e:builder-cjs": { - "dependsOn": ["example-builder-cjs#build", "wdio-electron-service#build", "@repo/e2e#test:e2e:builder-esm"] + "dependsOn": ["example-builder-cjs#build", "@repo/e2e#init", "@repo/e2e#test:e2e:builder-esm"] }, "@repo/e2e#test:e2e:builder-esm": { - "dependsOn": ["example-builder-esm#build", "wdio-electron-service#build"] + "dependsOn": ["example-builder-esm#build", "@repo/e2e#init"] }, "@repo/e2e#test:e2e:forge-cjs": { - "dependsOn": ["example-forge-cjs#build", "wdio-electron-service#build", "@repo/e2e#test:e2e:forge-esm"] + "dependsOn": ["example-forge-cjs#build", "@repo/e2e#init", "@repo/e2e#test:e2e:forge-esm"] }, "@repo/e2e#test:e2e:forge-esm": { - "dependsOn": ["example-forge-esm#build", "wdio-electron-service#build"] + "dependsOn": ["example-forge-esm#build", "@repo/e2e#init"] }, "@repo/e2e#test:e2e:no-binary-cjs": { - "dependsOn": ["example-no-binary-cjs#build", "wdio-electron-service#build", "@repo/e2e#test:e2e:no-binary-esm"] + "dependsOn": ["example-no-binary-cjs#build", "@repo/e2e#init", "@repo/e2e#test:e2e:no-binary-esm"] }, "@repo/e2e#test:e2e:no-binary-esm": { - "dependsOn": ["example-no-binary-esm#build", "example-no-binary-cjs#build", "wdio-electron-service#build"] + "dependsOn": ["example-no-binary-esm#build", "example-no-binary-cjs#build", "@repo/e2e#init"] }, "@repo/e2e#test:e2e-mac-universal:forge-cjs": { "dependsOn": [ "example-forge-cjs#build:mac-universal", - "wdio-electron-service#build", + "@repo/e2e#init", "@repo/e2e#test:e2e-mac-universal:forge-esm" ] }, "@repo/e2e#test:e2e-mac-universal:forge-esm": { - "dependsOn": ["example-forge-esm#build:mac-universal", "wdio-electron-service#build"] + "dependsOn": ["example-forge-esm#build:mac-universal", "@repo/e2e#init"] }, "@repo/e2e#test:e2e-mac-universal:builder-cjs": { "dependsOn": [ "example-builder-cjs#build:mac-universal", - "wdio-electron-service#build", + "@repo/e2e#init", "@repo/e2e#test:e2e-mac-universal:builder-esm" ] }, "@repo/e2e#test:e2e-mac-universal:builder-esm": { - "dependsOn": ["example-builder-esm#build:mac-universal", "wdio-electron-service#build"] + "dependsOn": ["example-builder-esm#build:mac-universal", "@repo/e2e#init"] }, "@wdio/electron-types#build": { "dependsOn": ["//#lint", "//#format:check", "test:unit"],