folktest/src/index.js
2025-04-18 03:36:54 -05:00

81 lines
2 KiB
JavaScript

export { TerminalRunner } from './runner.js'
export class AssertionError extends Error {
constructor(message) {
super(message)
}
}
/**
* @template T
* @param {T} obj
* @returns {obj is Exclude<T, Function|symbol|bigint|string|number|boolean|undefined|null|RegExp|Date|Map|Set>}
*/
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
}
}
}
}