diff --git a/config/__tests__/CLIConfiguration.ts b/config/__tests__/CLIConfiguration.ts index 54f8d957..d41d53ca 100644 --- a/config/__tests__/CLIConfiguration.ts +++ b/config/__tests__/CLIConfiguration.ts @@ -1,13 +1,130 @@ +import { ENVIRONMENTS } from '../../constants/environments'; import config from '../CLIConfiguration'; -// TODO write tests for CLIConfiguration.ts describe('config/CLIConfiguration', () => { + afterAll(() => { + config.setActive(false); + }); + describe('constructor()', () => { it('initializes correctly', () => { expect(config).toBeDefined(); expect(config.options).toBeDefined(); expect(config.useEnvConfig).toBe(false); expect(config.config).toBe(null); + expect(config.active).toBe(false); + }); + }); + + describe('isActive()', () => { + it('returns true when the class is being used', () => { + expect(config.isActive()).toBe(false); + config.setActive(true); + expect(config.isActive()).toBe(true); + }); + }); + + describe('getAccount()', () => { + it('returns null when no config is loaded', () => { + expect(config.getAccount('account-name')).toBe(null); + }); + }); + + describe('isConfigFlagEnabled()', () => { + it('returns default value when no config is loaded', () => { + expect(config.isConfigFlagEnabled('allowUsageTracking', false)).toBe( + false + ); + }); + }); + + describe('getAccountId()', () => { + it('returns null when it cannot find the account in the config', () => { + expect(config.getAccountId('account-name')).toBe(null); + }); + }); + + describe('getDefaultAccount()', () => { + it('returns null when no config is loaded', () => { + expect(config.getDefaultAccount()).toBe(null); + }); + }); + + describe('getConfigAccountIndex()', () => { + it('returns -1 when no config is loaded', () => { + expect(config.getConfigAccountIndex(123)).toBe(-1); + }); + }); + + describe('isAccountInConfig()', () => { + it('returns false when no config is loaded', () => { + expect(config.isAccountInConfig(123)).toBe(false); + }); + }); + + describe('getConfigForAccount()', () => { + it('returns null when no config is loaded', () => { + expect(config.getConfigForAccount(123)).toBe(null); + }); + }); + + describe('getEnv()', () => { + it('returns PROD when no config is loaded', () => { + expect(config.getEnv(123)).toBe(ENVIRONMENTS.PROD); + }); + }); + + describe('updateDefaultAccount()', () => { + it('throws when no config is loaded', () => { + expect(() => { + config.updateDefaultAccount('account-name'); + }).toThrow(); + }); + }); + + describe('renameAccount()', () => { + it('throws when no config is loaded', () => { + expect(() => { + config.renameAccount('account-name', 'new-account-name'); + }).toThrow(); + }); + }); + + describe('removeAccountFromConfig()', () => { + it('throws when no config is loaded', () => { + expect(() => { + config.removeAccountFromConfig('account-name'); + }).toThrow(); + }); + }); + + describe('updateDefaultMode()', () => { + it('throws when no config is loaded', () => { + expect(() => { + config.updateDefaultMode('newMode'); + }).toThrow(); + }); + }); + + describe('updateHttpTimeout()', () => { + it('throws when no config is loaded', () => { + expect(() => { + config.updateHttpTimeout('1000'); + }).toThrow(); + }); + }); + + describe('updateAllowUsageTracking()', () => { + it('throws when no config is loaded', () => { + expect(() => { + config.updateAllowUsageTracking(true); + }).toThrow(); + }); + }); + + describe('isTrackingAllowed()', () => { + it('returns true when no config is loaded', () => { + expect(config.isTrackingAllowed()).toBe(true); }); }); }); diff --git a/errors/__tests__/fileSystemErrors.ts b/errors/__tests__/fileSystemErrors.ts new file mode 100644 index 00000000..695d33e7 --- /dev/null +++ b/errors/__tests__/fileSystemErrors.ts @@ -0,0 +1,28 @@ +import { throwFileSystemError } from '../fileSystemErrors'; +import { BaseError } from '../../types/Error'; + +export const newError = (overrides = {}): BaseError => { + return { + name: 'Error', + message: 'An error ocurred', + errno: 1, + code: 'error_code', + syscall: 'error_syscall', + errors: [], + ...overrides, + }; +}; + +const fileSystemErrorContext = { + filepath: 'some/path', +}; + +describe('errors/fileSystemErrors', () => { + describe('throwFileSystemError()', () => { + it('throws a fileSystemError', () => { + expect(() => { + throwFileSystemError(newError(), fileSystemErrorContext); + }).toThrow(); + }); + }); +}); diff --git a/errors/fileSystemErrors.ts b/errors/fileSystemErrors.ts index 1a9d6f0d..35f2bb48 100644 --- a/errors/fileSystemErrors.ts +++ b/errors/fileSystemErrors.ts @@ -4,6 +4,9 @@ import { BaseError, FileSystemErrorContext } from '../types/Error'; const i18nKey = 'errors.fileSystemErrors'; +/** + * @throws + */ export function throwFileSystemError( error: BaseError, context: FileSystemErrorContext diff --git a/lib/__tests__/logger.ts b/lib/__tests__/logger.ts new file mode 100644 index 00000000..2c9d2df0 --- /dev/null +++ b/lib/__tests__/logger.ts @@ -0,0 +1,146 @@ +import chalk from 'chalk'; +import { + Styles, + stylize, + setLogLevel, + getLogLevel, + LOG_LEVEL, + logger, + shouldLog, +} from '../logging/logger'; + +describe('lib/logging/logger', () => { + afterEach(() => { + setLogLevel(LOG_LEVEL.LOG); + }); + + describe('stylize()', () => { + it('stylizes input', () => { + const res = stylize('[ERROR]', Styles.error, ['test']); + + expect(res[0]).toEqual(`${chalk.reset.red('[ERROR]')} test`); + }); + }); + + describe('setLogLevel()', () => { + it('sets the log level for the logger', () => { + setLogLevel(LOG_LEVEL.DEBUG); + expect(getLogLevel()).toBe(LOG_LEVEL.DEBUG); + + setLogLevel(LOG_LEVEL.WARN); + expect(getLogLevel()).toBe(LOG_LEVEL.WARN); + + setLogLevel(LOG_LEVEL.NONE); + expect(getLogLevel()).toBe(LOG_LEVEL.NONE); + }); + }); + + describe('shouldLog()', () => { + it('returns false for all logs when the currentLogLevel is NONE', () => { + setLogLevel(LOG_LEVEL.NONE); + expect(shouldLog(LOG_LEVEL.DEBUG)).toBeFalsy(); + expect(shouldLog(LOG_LEVEL.LOG)).toBeFalsy(); + expect(shouldLog(LOG_LEVEL.WARN)).toBeFalsy(); + expect(shouldLog(LOG_LEVEL.ERROR)).toBeFalsy(); + }); + + it('returns true for all logs when the currentLogLevel is DEBUG', () => { + setLogLevel(LOG_LEVEL.DEBUG); + expect(shouldLog(LOG_LEVEL.DEBUG)).toBeTruthy(); + expect(shouldLog(LOG_LEVEL.LOG)).toBeTruthy(); + expect(shouldLog(LOG_LEVEL.WARN)).toBeTruthy(); + expect(shouldLog(LOG_LEVEL.ERROR)).toBeTruthy(); + }); + + it('returns false for debugs when the currentLogLevel is LOG', () => { + setLogLevel(LOG_LEVEL.LOG); + expect(shouldLog(LOG_LEVEL.DEBUG)).toBeFalsy(); + expect(shouldLog(LOG_LEVEL.LOG)).toBeTruthy(); + expect(shouldLog(LOG_LEVEL.WARN)).toBeTruthy(); + expect(shouldLog(LOG_LEVEL.ERROR)).toBeTruthy(); + }); + + it('returns false for debugs and logs when the currentLogLevel is WARN', () => { + setLogLevel(LOG_LEVEL.WARN); + expect(shouldLog(LOG_LEVEL.DEBUG)).toBeFalsy(); + expect(shouldLog(LOG_LEVEL.LOG)).toBeFalsy(); + expect(shouldLog(LOG_LEVEL.WARN)).toBeTruthy(); + expect(shouldLog(LOG_LEVEL.ERROR)).toBeTruthy(); + }); + + it('returns false for debugs, logs, and warns when the currentLogLevel is ERROR', () => { + setLogLevel(LOG_LEVEL.ERROR); + expect(shouldLog(LOG_LEVEL.DEBUG)).toBeFalsy(); + expect(shouldLog(LOG_LEVEL.LOG)).toBeFalsy(); + expect(shouldLog(LOG_LEVEL.WARN)).toBeFalsy(); + expect(shouldLog(LOG_LEVEL.ERROR)).toBeTruthy(); + }); + }); + + describe('logger()', () => { + let warnSpy: jest.SpyInstance; + let logSpy: jest.SpyInstance; + let debugSpy: jest.SpyInstance; + let infoSpy: jest.SpyInstance; + let groupSpy: jest.SpyInstance; + let groupEndSpy: jest.SpyInstance; + + beforeEach(() => { + setLogLevel(LOG_LEVEL.LOG); + warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => null); + logSpy = jest.spyOn(console, 'log').mockImplementation(() => null); + debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => null); + infoSpy = jest.spyOn(console, 'info').mockImplementation(() => null); + groupSpy = jest.spyOn(console, 'group').mockImplementation(() => null); + groupEndSpy = jest + .spyOn(console, 'groupEnd') + .mockImplementation(() => null); + }); + + afterAll(() => { + warnSpy.mockReset(); + logSpy.mockReset(); + debugSpy.mockReset(); + infoSpy.mockReset(); + groupSpy.mockReset(); + groupEndSpy.mockReset(); + }); + + it('handles warnings', () => { + logger.log('test log'); + expect(warnSpy).not.toHaveBeenCalled(); + + logger.warn('test log'); + expect(warnSpy).toHaveBeenCalled(); + }); + + it('handles logs', () => { + logger.debug('test log'); + expect(debugSpy).not.toHaveBeenCalled(); + + logger.log('test log'); + expect(logSpy).toHaveBeenCalled(); + + setLogLevel(LOG_LEVEL.DEBUG); + logger.debug('test log'); + expect(debugSpy).toHaveBeenCalled(); + }); + + it('handles info', () => { + logger.info('test log'); + expect(infoSpy).toHaveBeenCalled(); + }); + + it('handles success', () => { + logger.success('test log'); + expect(logSpy).toHaveBeenCalled(); + }); + + it('handles group and groupEnd', () => { + logger.group('test log'); + logger.groupEnd(); + expect(groupSpy).toHaveBeenCalled(); + expect(groupEndSpy).toHaveBeenCalled(); + }); + }); +}); diff --git a/lib/__tests__/text.ts b/lib/__tests__/text.ts new file mode 100644 index 00000000..ce81f0f2 --- /dev/null +++ b/lib/__tests__/text.ts @@ -0,0 +1,23 @@ +import { commaSeparatedValues } from '../text'; + +describe('lib/text', () => { + describe('commaSeparatedValues()', () => { + it('returns a string with comma separated values', () => { + const res = commaSeparatedValues(['first', 'second', 'third']); + + expect(res).toBe('first, second, and third'); + }); + + it('supports a custom conjuction', async () => { + const res = commaSeparatedValues(['first', 'second', 'third'], 'custom'); + + expect(res).toBe('first, second, custom third'); + }); + + it('supports a custom if empty case', async () => { + const res = commaSeparatedValues([], null, 'input is empty'); + + expect(res).toBe('input is empty'); + }); + }); +}); diff --git a/lib/logging/logger.ts b/lib/logging/logger.ts index 98d5c7af..520d2170 100644 --- a/lib/logging/logger.ts +++ b/lib/logging/logger.ts @@ -90,8 +90,8 @@ export function setLogLevel(level: number): void { } } -export function shouldLog(level: number): number { - return currentLogLevel & level; +export function shouldLog(level: number): boolean { + return !!(currentLogLevel & level); } export function getLogLevel(): number { diff --git a/lib/text.ts b/lib/text.ts index c6e908e1..8fa08249 100644 --- a/lib/text.ts +++ b/lib/text.ts @@ -1,6 +1,6 @@ export function commaSeparatedValues( arr: Array, - conjunction = 'and', + conjunction: null | string = 'and', ifempty = '' ): string { const l = arr.length;