diff --git a/jest.config.js b/jest.config.js index 5c175413d..7e3071cc2 100644 --- a/jest.config.js +++ b/jest.config.js @@ -16,8 +16,7 @@ module.exports = { "setupFilesAfterEnv": ["jest-rdf"], "collectCoverage": true, "coveragePathIgnorePatterns": [ - "/node_modules/", - "/src/init/CliRunner.ts" + "/node_modules/" ], "coverageThreshold": { "./src": { diff --git a/test/unit/init/CliRunner.test.ts b/test/unit/init/CliRunner.test.ts new file mode 100644 index 000000000..f1c547fec --- /dev/null +++ b/test/unit/init/CliRunner.test.ts @@ -0,0 +1,102 @@ +import * as Path from 'path'; +import type { Loader } from 'componentsjs'; +import { runCli } from '../../../src/init/CliRunner'; +import type { Setup } from '../../../src/init/Setup'; +import { setGlobalLoggerFactory } from '../../../src/logging/LogUtil'; +import { VoidLoggerFactory } from '../../../src/logging/VoidLoggerFactory'; + +let calledInstantiateFromUrl: boolean; +let calledRegisterAvailableModuleResources: boolean; +let throwError: boolean; +let outsideResolve: () => void; +let functionToResolve: Promise; + +const mockSetup = { + setup: jest.fn(), +} as unknown as jest.Mocked; + +// Mock the Loader class. +jest.mock('componentsjs', (): any => ( + // eslint-disable-next-line @typescript-eslint/naming-convention, object-shorthand + { Loader: function(): Loader { + return { + instantiateFromUrl(): any { + calledInstantiateFromUrl = true; + if (throwError) { + throw new Error('Error! :o'); + } + return mockSetup; + }, + registerAvailableModuleResources(): any { + calledRegisterAvailableModuleResources = true; + }, + } as unknown as Loader; + } } +)); + +jest.mock('yargs', (): any => ({ + usage(): any { + return this; + }, + options(): any { + return this; + }, + help(): any { + // Return once with and once without config value so that both branches are tested. + if (throwError) { + return { + argv: { config: 'value' }, + }; + } + return { + argv: { }, + }; + }, +})); + +describe('CliRunner', (): void => { + beforeAll(async(): Promise => { + setGlobalLoggerFactory(new VoidLoggerFactory()); + + mockSetup.setup.mockImplementation(async(): Promise => { + // The info method will be called when all other code has been executed, so end the waiting function. + outsideResolve(); + }); + }); + + beforeEach(async(): Promise => { + calledInstantiateFromUrl = false; + calledRegisterAvailableModuleResources = false; + throwError = false; + + // Initialize a function that will be resolved as soon as all necessary but asynchronous calls are completed. + functionToResolve = new Promise((resolve): any => { + outsideResolve = resolve; + }); + }); + + it('Runs function for starting the server from the command line.', async(): Promise => { + runCli(Path.join(__dirname, '..')); + + await functionToResolve; + + expect(calledInstantiateFromUrl).toBeTruthy(); + expect(calledRegisterAvailableModuleResources).toBeTruthy(); + expect(mockSetup.setup).toBeCalledTimes(1); + }); + + it('Writes to stderr when an exception occurs.', async(): Promise => { + const mockStderr = jest.spyOn(process.stderr, 'write').mockImplementation((): any => { + // This method will be called when an error has occurred, so end the waiting function. + outsideResolve(); + }); + throwError = true; + + runCli(Path.join(__dirname, '..')); + + await functionToResolve; + + expect(mockStderr).toHaveBeenCalledTimes(1); + mockStderr.mockRestore(); + }); +});