Skip to content

Commit

Permalink
Use more idiomatic Vitest in the testing suite (#1245)
Browse files Browse the repository at this point in the history
* Use vitest except where possible and organize tests

* Use vi.fn() instead of CallTracker in 'deprecated' helper

- CallTracker has been deprecated in Node.js
- vi.fn() is probably more familiar to Vitest users

---------

Co-authored-by: Artur Müller <me@arturmuller.com>
  • Loading branch information
yeoffrey and arturmuller committed Jun 21, 2024
1 parent b64f64c commit 625cea5
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 112 deletions.
31 changes: 11 additions & 20 deletions test/api/assert.test.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,29 @@
import { throws, doesNotThrow } from 'assert'
import { describe, it } from 'vitest'
import { describe, expect, it } from 'vitest'
import { assert, string, StructError } from '../../src'

describe('assert', () => {
it('valid as helper', () => {
doesNotThrow(() => {
assert('valid', string())
})
expect(() => assert('valid', string())).not.toThrow(StructError)
})

it('valid as method', () => {
doesNotThrow(() => {
// @ts-ignore
string().assert('valid')
})
expect(() => string().assert('valid')).not.toThrow(StructError)
})

it('invalid as helper', () => {
throws(() => {
assert(42, string())
}, StructError)
expect(() => assert(42, string())).toThrow(StructError)
})

it('invalid as method', () => {
throws(() => {
// @ts-ignore
string().assert(42)
}, StructError)
expect(() => string().assert(42)).toThrow(StructError)
})

it('custom error message', () => {
throws(() => string().assert(42, 'Not a string!'), {
cause: 'Expected a string, but received: 42',
message: 'Not a string!',
})
expect(() => string().assert(42, 'Not a string!')).toThrow(
expect.objectContaining({
cause: 'Expected a string, but received: 42',
message: 'Not a string!',
})
)
})
})
25 changes: 13 additions & 12 deletions test/api/create.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { strictEqual, deepEqual, deepStrictEqual, throws } from 'assert'
import { describe, it } from 'vitest'
import { describe, expect, it } from 'vitest'
import {
type,
optional,
Expand All @@ -13,22 +12,22 @@ import {
describe('create', () => {
it('missing as helper', () => {
const S = defaulted(string(), 'default')
strictEqual(create(undefined, S), 'default')
expect(create(undefined, S)).toBe('default')
})

it('missing as method', () => {
const S = defaulted(string(), 'default')
strictEqual(S.create(undefined), 'default')
expect(S.create(undefined)).toBe('default')
})

it('not missing as helper', () => {
const S = defaulted(string(), 'default')
strictEqual(create('string', S), 'string')
expect(create('string', S)).toBe('string')
})

it('not missing as method', () => {
const S = defaulted(string(), 'default')
strictEqual(S.create('string'), 'string')
expect(S.create('string')).toBe('string')
})

it('missing optional fields remain missing', () => {
Expand All @@ -37,7 +36,7 @@ describe('create', () => {
b: optional(string()),
c: optional(type({ d: string() })),
})
deepEqual(S.create({ a: 'a' }), { a: 'a' })
expect(S.create({ a: 'a' })).toStrictEqual({ a: 'a' })
})

it('explicit undefined values are kept', () => {
Expand All @@ -46,17 +45,19 @@ describe('create', () => {
b: coerce(optional(string()), literal(null), () => undefined),
c: optional(type({ d: string() })),
})
deepStrictEqual(S.create({ a: 'a', b: null, c: undefined }), {
expect(S.create({ a: 'a', b: null, c: undefined })).toStrictEqual({
a: 'a',
b: undefined,
c: undefined,
})
})

it('custom error message', () => {
throws(() => string().create(42, 'Not a string!'), {
cause: 'Expected a string, but received: 42',
message: 'Not a string!',
})
expect(() => string().create(42, 'Not a string!')).toThrow(
expect.objectContaining({
cause: 'Expected a string, but received: 42',
message: 'Not a string!',
})
)
})
})
11 changes: 5 additions & 6 deletions test/api/is.test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import { strictEqual } from 'assert'
import { describe, it } from 'vitest'
import { describe, expect, it } from 'vitest'
import { is, string } from '../../src'

describe('is', () => {
it('valid as helper', () => {
strictEqual(is('valid', string()), true)
expect(is('valid', string())).toBe(true)
})

it('valid as method', () => {
strictEqual(string().is('valid'), true)
expect(string().is('valid')).toBe(true)
})

it('invalid as helper', () => {
strictEqual(is(42, string()), false)
expect(is(42, string())).toBe(false)
})

it('invalid as method', () => {
strictEqual(string().is(42), false)
expect(string().is(42)).toBe(false)
})
})
31 changes: 15 additions & 16 deletions test/api/mask.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { deepStrictEqual, throws } from 'assert'
import { describe, it } from 'vitest'
import { describe, expect, it } from 'vitest'
import {
mask,
object,
Expand All @@ -14,21 +13,19 @@ describe('mask', () => {
it('object as helper', () => {
const S = object({ id: string() })
const value = { id: '1', unknown: true }
deepStrictEqual(mask(value, S), { id: '1' })
expect(mask(value, S)).toStrictEqual({ id: '1' })
})

it('non-object as helper', () => {
const S = object({ id: string() })
const value = 'invalid'
throws(() => {
mask(value, S)
}, StructError)
expect(() => mask(value, S)).toThrow(StructError)
})

it('coercing', () => {
const S = defaulted(object({ id: string() }), { id: '0' })
const value = { unknown: true }
deepStrictEqual(mask(value, S), { id: '0' })
expect(mask(value, S)).toStrictEqual({ id: '0' })
})

it('deep masking of objects', () => {
Expand All @@ -41,7 +38,7 @@ describe('mask', () => {
unknown: true,
sub: [{ prop: '2', unknown: true }],
}
deepStrictEqual(mask(value, S), { id: '1', sub: [{ prop: '2' }] })
expect(mask(value, S)).toStrictEqual({ id: '1', sub: [{ prop: '2' }] })
})

it('masking of a nested type', () => {
Expand All @@ -54,7 +51,7 @@ describe('mask', () => {
unknown: true,
sub: [{ prop: '2', unknown: true }],
}
deepStrictEqual(mask(value, S), {
expect(mask(value, S)).toStrictEqual({
id: '1',
sub: [{ prop: '2', unknown: true }],
})
Expand All @@ -70,7 +67,7 @@ describe('mask', () => {
unknown: true,
sub: [{ prop: '2', unknown: true }],
}
deepStrictEqual(mask(value, S), {
expect(mask(value, S)).toStrictEqual({
id: '1',
unknown: true,
sub: [{ prop: '2' }],
Expand All @@ -80,14 +77,16 @@ describe('mask', () => {
it('masking does not change the original value', () => {
const S = object({ id: string() })
const value = { id: '1', unknown: true }
deepStrictEqual(mask(value, S), { id: '1' })
deepStrictEqual(value, { id: '1', unknown: true })
expect(mask(value, S)).toStrictEqual({ id: '1' })
expect(value).toStrictEqual({ id: '1', unknown: true })
})

it('custom error message', () => {
throws(() => string().mask(42, 'Not a string!'), {
cause: 'Expected a string, but received: 42',
message: 'Not a string!',
})
expect(() => string().mask(42, 'Not a string!')).toThrow(
expect.objectContaining({
cause: 'Expected a string, but received: 42',
message: 'Not a string!',
})
)
})
})
36 changes: 17 additions & 19 deletions test/api/validate.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { deepStrictEqual, strictEqual } from 'assert'
import { describe, it } from 'vitest'
import { describe, expect, it } from 'vitest'
import {
validate,
string,
Expand All @@ -13,20 +12,20 @@ import {
describe('validate', () => {
it('valid as helper', () => {
const S = string()
deepStrictEqual(validate('valid', S), [undefined, 'valid'])
expect(validate('valid', S)).toStrictEqual([undefined, 'valid'])
})

it('valid as method', () => {
const S = string()
deepStrictEqual(S.validate('valid'), [undefined, 'valid'])
expect(S.validate('valid')).toStrictEqual([undefined, 'valid'])
})

it('invalid as helper', () => {
const S = string()
const [err, value] = validate(42, S)
strictEqual(value, undefined)
strictEqual(err instanceof StructError, true)
deepStrictEqual(Array.from((err as StructError).failures()), [
expect(value).toStrictEqual(undefined)
expect(err).toBeInstanceOf(StructError)
expect(Array.from((err as StructError).failures())).toStrictEqual([
{
value: 42,
key: undefined,
Expand All @@ -43,9 +42,9 @@ describe('validate', () => {
it('invalid as method', () => {
const S = string()
const [err, value] = S.validate(42)
strictEqual(value, undefined)
strictEqual(err instanceof StructError, true)
deepStrictEqual(Array.from((err as StructError).failures()), [
expect(value).toStrictEqual(undefined)
expect(err).toBeInstanceOf(StructError)
expect(Array.from((err as StructError).failures())).toStrictEqual([
{
value: 42,
key: undefined,
Expand All @@ -62,17 +61,16 @@ describe('validate', () => {
it('error message path', () => {
const S = object({ author: object({ name: string() }) })
const [err] = S.validate({ author: { name: 42 } })
strictEqual(
(err as StructError).message,
expect(err?.message).toBe(
'At path: author.name -- Expected a string, but received: 42'
)
})

it('custom error message', () => {
const S = string()
const [err] = S.validate(42, { message: 'Validation failed!' })
strictEqual(err?.message, 'Validation failed!')
strictEqual(err?.cause, 'Expected a string, but received: 42')
expect(err?.message).toBe('Validation failed!')
expect(err?.cause).toBe('Expected a string, but received: 42')
})

it('early exit', () => {
Expand All @@ -91,8 +89,8 @@ describe('validate', () => {

const S = object({ a: A, b: B })
S.validate({ a: null, b: null })
strictEqual(ranA, true)
strictEqual(ranB, false)
expect(ranA).toBe(true)
expect(ranB).toBe(false)
})

it('refiners after children', () => {
Expand All @@ -109,7 +107,7 @@ describe('validate', () => {
})

B.validate({ a: null })
deepStrictEqual(order, ['validator', 'refiner'])
expect(order).toStrictEqual(['validator', 'refiner'])
})

it('refiners even if nested refiners fail', () => {
Expand All @@ -127,7 +125,7 @@ describe('validate', () => {
const [error] = B.validate({ a: null })
// Collect all failures. Ensures all validation runs.
error?.failures()
strictEqual(ranOuterRefiner, true)
expect(ranOuterRefiner).toBe(true)
})

it('skips refiners if validators return errors', () => {
Expand All @@ -145,6 +143,6 @@ describe('validate', () => {
const [error] = B.validate({ a: null })
// Collect all failures. Ensures all validation runs.
error?.failures()
strictEqual(ranRefiner, false)
expect(ranRefiner).toBe(false)
})
})
18 changes: 18 additions & 0 deletions test/deprecated.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, expect, it, vi } from 'vitest'
import { any, assert, deprecated } from '../src'

describe('deprecated', () => {
it('does not log deprecated type if value is undefined', () => {
const spy = vi.fn()
expect(spy).not.toHaveBeenCalled()
assert(undefined, deprecated(any(), spy))
expect(spy).not.toHaveBeenCalled()
})

it('logs deprecated type to passed function if value is present', () => {
const spy = vi.fn()
expect(spy).not.toHaveBeenCalled()
assert('present', deprecated(any(), spy))
expect(spy).toHaveBeenCalledOnce()
})
})
Loading

0 comments on commit 625cea5

Please sign in to comment.