Skip to content

Commit

Permalink
feat: added jest expect support (#66)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <[email protected]>
  • Loading branch information
sheremet-va and antfu authored Dec 12, 2021
1 parent f932bca commit d9d2075
Show file tree
Hide file tree
Showing 12 changed files with 994 additions and 1 deletion.
108 changes: 108 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { MatchersObject } from './integrations/chai/types'
import type { UserOptions } from './types'

export * from './types'
Expand All @@ -16,6 +17,10 @@ declare module 'vite' {

declare global {
namespace Chai {
interface ExpectStatic {
extend(expects: MatchersObject): void
}

interface Assertion {
// Snapshot
toMatchSnapshot(message?: string): Assertion
Expand Down
83 changes: 83 additions & 0 deletions src/integrations/chai/jest-extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import chai, { util } from 'chai'

import * as matcherUtils from './jest-matcher-utils'

import {
iterableEquality,
subsetEquality,
equals,
} from './jest-utils'
import type {
ChaiPlugin,
MatcherState,
MatchersObject,
SyncExpectationResult,
} from './types'

const isAsyncFunction = (fn: unknown) =>
typeof fn === 'function' && (fn as any)[Symbol.toStringTag] === 'AsyncFunction'

const getMatcherState = (assertion: Chai.AssertionStatic & Chai.Assertion) => {
const actual = assertion._obj
const isNot = util.flag(assertion, 'negate') as boolean
const jestUtils = {
...matcherUtils,
iterableEquality,
subsetEquality,
}

const matcherState: MatcherState = {
isNot,
utils: jestUtils,
// global assertionCalls? needed for built-in jest function, but we don't use it
assertionCalls: 0,
promise: '',
equals,
// needed for built-in jest-snapshots, but we don't use it
suppressedErrors: [],
}

return {
state: matcherState,
isNot,
actual,
}
}

function JestExtendPlugin(expects: MatchersObject): ChaiPlugin {
return (c, utils) => {
Object.entries(expects).forEach(([expectAssertionName, expectAssertion]) => {
function expectSyncWrapper(this: Chai.AssertionStatic & Chai.Assertion, ...args: any[]) {
const { state, isNot, actual } = getMatcherState(this)

// @ts-expect-error args wanting tuple
const { pass, message } = expectAssertion.call(state, actual, ...args) as SyncExpectationResult

if ((pass && isNot) || (!pass && !isNot))
c.expect.fail(message())
}

async function expectAsyncWrapper(this: Chai.AssertionStatic & Chai.Assertion, ...args: any[]) {
const { state, isNot, actual } = getMatcherState(this)

// @ts-expect-error args wanting tuple
const { pass, message } = await expectAssertion.call(state, actual, ...args) as SyncExpectationResult

if ((pass && isNot) || (!pass && !isNot))
c.expect.fail(message())
}

const expectAssertionWrapper = isAsyncFunction(expectAssertion) ? expectAsyncWrapper : expectSyncWrapper

utils.addMethod(chai.Assertion.prototype, expectAssertionName, expectAssertionWrapper)
})
}
}

export function JestExtend(): ChaiPlugin {
return (chai, utils) => {
utils.addMethod(chai.expect, 'extend', (expects: MatchersObject) => {
chai.use(JestExtendPlugin(expects))
})
}
}
Loading

0 comments on commit d9d2075

Please sign in to comment.