export { TerminalRunner } from './runner.js' export class AssertionError extends Error { constructor(message) { super(message) } } /** * @template T * @param {T} obj * @returns {obj is Exclude} */ function isObject(obj) { return ( obj !== null && typeof obj === 'object' && [null, Object.prototype].includes(Object.getPrototypeOf(obj)) ) } export const assert = (value, message = `assertion error: ${value} is not true`) => { assertEq(value, true, message) } export const assertEq = (a, b, message = `assertion error: ${a} !== ${b}`) => { if (a === b) { return } else if (Array.isArray(b) && a.length === b.length) { b.forEach((n, i) => assertEq(a[i], n, message)) } else if (isObject(b) && isObject(a)) { assertEq(JSON.stringify(a), JSON.stringify(b), message) } else { throw new AssertionError(message) } } const test = (re, str = '') => re instanceof RegExp ? re.test(str) : new RegExp(re).test(str) export const assertErr = (f, err) => { try { f() } catch (e) { if (typeof err === 'string' || err instanceof RegExp) { assert(test(err, e.message), `${err} did not match ${e.message}`) } else if (typeof err === 'function') { assert(e.constructor === err) } return } const expected = err ? `"${err}" ` : "" assert(false, `expecting error ${expected}but function did not throw`) } export const assertCallback = () => { let result = false setTimeout(() => assert(result), 0) return () => result = true } export const it = (description, test) => { return () => { try { test() return { success: true, description, error: undefined } } catch (error) { return { success: false, description, error } } } }