import {AssertionConfig, AssertionMode, Assertions} from "./Assertions"; describe("Assertion", () => { describe("check", () => { const defaults = { value: Symbol("test value"), config: AssertionConfig.Skip, conditionImpl: () => true, messageImpl: () => "Mockery", } as const function run({value, config, conditionImpl, messageImpl}: {value: T, config: typeof AssertionConfig.Skip, conditionImpl: (value: T) => boolean, messageImpl: (value: T) => string}): {value: T, condition: jest.Mock, message: jest.Mock, retval: T} function run({value, config, conditionImpl, messageImpl}: {value: T, config: typeof AssertionConfig.Throw, conditionImpl: (value: T) => boolean, messageImpl: (value: T) => string}): {value: T, condition: jest.Mock, message: jest.Mock, retval?: T, error?: Error} function run({value, config, conditionImpl, messageImpl}: {value: T, config: ReturnType, conditionImpl: (value: T) => boolean, messageImpl: (value: T) => string}): {value: T, log: jest.Mock, condition: jest.Mock, message: jest.Mock, retval: T} function run({value, config, conditionImpl, messageImpl}: {value: T, config: AssertionConfig.Type, conditionImpl: (value: T) => boolean, messageImpl: (value: T) => string}): {value: T, log?: jest.Mock, condition: jest.Mock, message: jest.Mock, retval?: T, error?: Error} function run({value, config, conditionImpl, messageImpl}: {value: T, config: AssertionConfig.Type, conditionImpl: (value: T) => boolean, messageImpl: (value: T) => string}): {value: T, log?: jest.Mock, condition: jest.Mock, message: jest.Mock, retval?: T, error?: Error} { const assertion = new Assertions() assertion.config = config let log: jest.Mock|undefined = undefined if (config.mode === AssertionMode.LOG) { log = jest.fn(config.logger) assertion.config = AssertionConfig.Log(log) } const condition = jest.fn(conditionImpl) const message = jest.fn(messageImpl) let error: Error|undefined = undefined let retval: T|undefined = undefined try { retval = assertion.check(value, condition, message) } catch (e) { if (e instanceof Error && config.mode === AssertionMode.THROW) { error = e } else { throw e } } return { value, log, condition, message, error, retval } } it(`returns the input and does not throw in SKIP`, () => { const {value, retval} = run({ ...defaults, config: AssertionConfig.Skip }) expect(retval).toBe(value) }); it(`does not call the condition or message in SKIP`, () => { const {condition, message} = run({ ...defaults, config: AssertionConfig.Skip }) expect(condition).not.toHaveBeenCalled() expect(message).not.toHaveBeenCalled() }); it(`calls the log function with an error, returns the value and does not throw when the condition fails in LOG`, () => { const {value, log, retval} = run({ ...defaults, config: AssertionConfig.Log(() => {}), conditionImpl: () => false, messageImpl: () => "Mockery" }) expect(log).toHaveBeenCalledTimes(1) expect(log).toHaveBeenCalledWith(expect.objectContaining({message: "Mockery"})) expect(log).toHaveBeenCalledWith(expect.any(Error)) expect(retval).toBe(value) }); it(`throws an error when the condition fails in THROW`, () => { const {error} = run({ ...defaults, config: AssertionConfig.Throw, conditionImpl: () => false, messageImpl: () => "Mockery" }) expect(error).toEqual(expect.objectContaining({message: "Mockery"})) expect(error).toBeInstanceOf(Error) }); it(`does not log and returns the input when the condition succeeds in LOG`, () => { const {value, retval, log} = run({ ...defaults, config: AssertionConfig.Log(() => {}), conditionImpl: () => true }) expect(log).not.toHaveBeenCalled() expect(retval).toBe(value) }); it(`does not throw and returns the input when the condition succeeds in THROW`, () => { const {value, retval, error} = run({ ...defaults, config: AssertionConfig.Throw, conditionImpl: () => true }) expect(error).toBeUndefined() expect(retval).toBe(value) }); [AssertionConfig.Log(() => {}), AssertionConfig.Throw].forEach((config) => { it(`calls the condition, but not the message, once with the value in ${config.mode} when the condition succeeds`, () => { const {value, condition, message} = run({ ...defaults, config, conditionImpl: () => true }) expect(condition).toHaveBeenCalledWith(value) expect(condition).toHaveBeenCalledTimes(1) expect(message).not.toHaveBeenCalled() }) it(`calls the condition and message once each with the value in ${config.mode} when the condition fails`, () => { const {value, condition, message} = run({ ...defaults, config, conditionImpl: () => false }) expect(condition).toHaveBeenCalledWith(value) expect(condition).toHaveBeenCalledTimes(1) expect(message).toHaveBeenCalledWith(value) expect(message).toHaveBeenCalledTimes(1) }) }) }) function testCheck(run: (input: T) => void, valid: T[], invalid: T[]) { valid.forEach((input) => { test(`passes ${input}`, () => { expect(() => run(input)).not.toThrow() }) }) invalid.forEach((input) => { test(`fails ${input}`, () => { expect(() => run(input)).toThrow() }) }) } describe("checkInteger", () => { function run(input: number) { const assertion = new Assertions() assertion.config = AssertionConfig.Throw assertion.checkInteger(input, () => "Assertion failed") } const valid: number[] = [0, 1, -1, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER] const invalid: number[] = [Number.NaN, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY, Number.MAX_SAFE_INTEGER + 999, Number.MAX_VALUE, Number.MIN_SAFE_INTEGER - 999, Number.EPSILON] testCheck(run, valid, invalid) }) describe("checkPositiveIntegerOrZero", () => { function run(input: number) { const assertion = new Assertions() assertion.config = AssertionConfig.Throw assertion.checkPositiveIntegerOrZero(input, () => "Assertion failed") } const valid: number[] = [0, 1, Number.MAX_SAFE_INTEGER] const invalid: number[] = [-1, Number.MIN_SAFE_INTEGER, Number.NaN, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY, Number.MAX_SAFE_INTEGER + 999, Number.MAX_VALUE, Number.MIN_SAFE_INTEGER - 999, Number.EPSILON] testCheck(run, valid, invalid) }) })