Skip to content

Commit

Permalink
feat: add assert exports to taps
Browse files Browse the repository at this point in the history
  • Loading branch information
hugomrdias committed Sep 1, 2023
1 parent d0ae762 commit 6369dcd
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 92 deletions.
2 changes: 1 addition & 1 deletion mocks/test.mocha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// eslint-disable-next-line strict
import { is, ok, equal } from 'uvu/assert'

describe('Array', () => {
it.skip('Array', () => {
describe('#indexOf()', () => {
it('should return -1 when the value is not present', () => {
const n: number = 1
Expand Down
3 changes: 1 addition & 2 deletions mocks/tops/test1.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { test } from '../../src/taps/index.js'
import delay from 'delay'
import { type, is, equal } from 'uvu/assert'
import assert from 'assert'
import { type, is } from 'uvu/assert'
// test.before(() => {
// console.log('before')
// })
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@
],
"parserOptions": {
"sourceType": "module"
},
"rules": {
"jsdoc/require-returns-check": "off"
}
},
"eslintIgnore": [
Expand Down
70 changes: 70 additions & 0 deletions src/taps/assert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import _assert from 'assert'
import kleur from 'kleur'
import { compare, formatObj } from './utils.js'

/**
* Assert that actual is a subset of expected.
*
* @example
* ```js
* // Supports compare functions
* subset({ a: 1, b: 2 }, { b: 2, a: (actual) => actual === 1 })
* ```
*
* @template T
* @param {unknown} actual
* @param {T} expected
* @param {string} [msg]
*
* @returns {asserts actual is Partial<T>} - returns true if actual is a subset of expected
*/
export function subset(actual, expected, msg) {
const pass = compare(expected, actual)
if (!pass) {
throw new _assert.AssertionError({
message:
msg ||
`Expected a subset of actual:
${kleur.green('+ actual')} ${kleur.red('- expected')}
${kleur.green('+')} ${formatObj(actual)}
${kleur.red('-')} ${formatObj(expected)}`,
actual,
expected,
operator: 'subset',
stackStartFn: subset,
})
}
}

export const ok = _assert.strict.ok
export const equal = _assert.strict.equal
export const notEqual = _assert.strict.notEqual
export const deepEqual = _assert.strict.deepEqual
export const notDeepEqual = _assert.strict.notDeepEqual
export const throws = _assert.strict.throws
export const doesNotThrow = _assert.strict.doesNotThrow
export const rejects = _assert.strict.rejects
export const doesNotReject = _assert.strict.doesNotReject
export const match = _assert.strict.match
export const doesNotMatch = _assert.strict.doesNotMatch
export const ifError = _assert.strict.ifError
export const fail = _assert.strict.fail

/** @type {import('./types.js').Assert} */
export const assert = {
ok,
equal,
notEqual,
deepEqual,
notDeepEqual,
throws,
doesNotThrow,
rejects,
doesNotReject,
match,
doesNotMatch,
ifError,
fail,
subset,
}
83 changes: 12 additions & 71 deletions src/taps/harness.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
/* eslint-disable no-unsafe-finally */
/* eslint-disable no-console */
import kleur from 'kleur'
import { compare, hrtime, stack } from './utils.js'
import assert from 'assert'
// eslint-disable-next-line unicorn/import-style
import util from 'util'
import { hrtime, stack } from './utils.js'
import { assert } from './assert.js'

/**
* @type {import("./types.js").Queue}
Expand Down Expand Up @@ -131,8 +129,7 @@ async function runner(ctx, testCount) {
throw new Error('beforeEach hook failed', { cause: error })
}
}
// @ts-ignore
await test.fn(harness(test.name))
await test.fn(harness(test.name), assert)
passed++

// After Each Hooks
Expand Down Expand Up @@ -199,101 +196,45 @@ export function harness(name = '') {
}

/**
* @param {string} name
* @param {import("./types.js").Fn} fn
* @type {import('./types.js').TestMethod}
*/
function test(name, fn) {
ctx.tests.push({ name, fn, skip: false })
}

test.test = test

test.before = (/** @type {import("./types.js").Hook} */ fn) => {
test.before = function (/** @type {import("./types.js").Hook} */ fn) {
ctx.before.push(fn)
}
test.after = (/** @type {import("./types.js").Hook} */ fn) => {
test.after = function (/** @type {import("./types.js").Hook} */ fn) {
ctx.after.push(fn)
}

test.beforeEach = (/** @type {import("./types.js").Hook} */ fn) => {
test.beforeEach = function (/** @type {import("./types.js").Hook} */ fn) {
ctx.beforeEach.push(fn)
}

test.afterEach = (/** @type {import("./types.js").Hook} */ fn) => {
test.afterEach = function (/** @type {import("./types.js").Hook} */ fn) {
ctx.afterEach.push(fn)
}

test.skip = (
test.skip = function (
/** @type {string} */ name,
/** @type {import('./types.js').Fn} */ fn
) => {
) {
ctx.tests.push({ name, fn, skip: true })
}

test.only = (
test.only = function (
/** @type {string} */ name,
/** @type {import('./types.js').Fn} */ fn
) => {
) {
globalThis.UVU_ONLY_MODE = true
ctx.only.push({ name, fn, skip: false })
}

test.ok = assert.strict.ok
test.equal = assert.strict.equal
test.notEqual = assert.strict.notEqual
test.deepEqual = assert.strict.deepEqual
test.notDeepEqual = assert.strict.notDeepEqual
test.throws = assert.strict.throws
test.doesNotThrow = assert.strict.doesNotThrow
test.rejects = assert.strict.rejects
test.doesNotReject = assert.strict.doesNotReject
test.match = assert.strict.match
test.doesNotMatch = assert.strict.doesNotMatch
test.ifError = assert.strict.ifError
test.fail = assert.strict.fail
test.subset = subset

TAPS_QUEUE.push(runner.bind(0, ctx))

return test
}

/**
*
* @param {unknown} v
* @returns
*/
function formatObj(v) {
return util.inspect(v, {
colors: true,
compact: false,
depth: Number.POSITIVE_INFINITY,
sorted: true,
numericSeparator: true,
})
}

/**
*
* @param {unknown} actual
* @param {unknown} expected
* @param {string} [msg]
*/
function subset(actual, expected, msg) {
const pass = compare(expected, actual)
if (!pass) {
throw new assert.AssertionError({
message:
msg ||
`Expected a subset of actual:
${kleur.green('+ actual')} ${kleur.red('- expected')}
${kleur.green('+')} ${formatObj(actual)}
${kleur.red('-')} ${formatObj(expected)}`,
actual,
expected,
operator: 'subset',
stackStartFn: subset,
})
}
}
2 changes: 2 additions & 0 deletions src/taps/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export const suite = (/** @type {string | undefined} */ name) => {
}
export const test = harness()

export * from './assert.js'

/**
* Execute the queued tests.
*/
Expand Down
36 changes: 20 additions & 16 deletions src/taps/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
import type assert from 'assert'
import type { subset } from './assert.js'
import type _assert from 'assert'

export type Fn = (test: Harness) => Promise<void> | void
export interface Assert {
ok: typeof _assert.strict.ok
equal: typeof _assert.strict.equal
notEqual: typeof _assert.strict.notEqual
deepEqual: typeof _assert.strict.deepEqual
notDeepEqual: typeof _assert.strict.notDeepEqual
throws: typeof _assert.strict.throws
rejects: typeof _assert.strict.rejects
doesNotThrow: typeof _assert.strict.doesNotThrow
doesNotReject: typeof _assert.strict.doesNotReject
fail: typeof _assert.strict.fail
ifError: typeof _assert.strict.ifError
match: typeof _assert.strict.match
doesNotMatch: typeof _assert.strict.doesNotMatch
subset: typeof subset
}

export type Fn = (test: Harness, a: Assert) => Promise<void> | void
export type Hook = () => Promise<void> | void
export type TestMethod = (name: string, fn: Fn) => void
export type HookMethod = (fn: Hook) => void
Expand Down Expand Up @@ -50,18 +68,4 @@ export interface Harness {
after: HookMethod
beforeEach: HookMethod
afterEach: HookMethod
ok: typeof assert.strict.ok
equal: typeof assert.strict.equal
notEqual: typeof assert.strict.notEqual
deepEqual: typeof assert.strict.deepEqual
notDeepEqual: typeof assert.strict.notDeepEqual
throws: typeof assert.strict.throws
rejects: typeof assert.strict.rejects
doesNotThrow: typeof assert.strict.doesNotThrow
doesNotReject: typeof assert.strict.doesNotReject
fail: typeof assert.strict.fail
ifError: typeof assert.strict.ifError
match: typeof assert.strict.match
doesNotMatch: typeof assert.strict.doesNotMatch
subset: (actual: unknown, expected: unknown, msg?: string) => void
}
20 changes: 19 additions & 1 deletion src/taps/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import kleur from 'kleur'
// eslint-disable-next-line unicorn/import-style
import util from 'util'

export const IS_ENV_WITH_DOM =
typeof window === 'object' &&
Expand Down Expand Up @@ -66,7 +68,7 @@ export function stack(err) {
}

/**
* Compare two values
* Compare two values are subset of each other. Supports compare functions.
*
* @param {any} expected
* @param {any} actual
Expand Down Expand Up @@ -118,3 +120,19 @@ export function compare(expected, actual) {
return ao === eo
})
}

/**
* Format an object for display in an error message.
*
* @param {unknown} v
* @returns
*/
export function formatObj(v) {
return util.inspect(v, {
colors: true,
compact: false,
depth: Number.POSITIVE_INFINITY,
sorted: true,
numericSeparator: true,
})
}
9 changes: 8 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,12 @@
"outDir": "dist",
"module": "ES2022"
},
"include": ["src", "src/utils", "test.js", "cli.js", "package.json"]
"include": [
"src",
"src/utils",
"test.js",
"cli.js",
"package.json",
"mocks/tops"
]
}

0 comments on commit 6369dcd

Please sign in to comment.