Skip to content

Releases: timkindberg/jest-when

4.0.0

23 Mar 23:44

Choose a tag to compare

Highlights

  • jest-when is now authored in TypeScript
  • fixes a number of long-standing typing issues, especially around wrapper mocks, calledWith inference, and void / Promise<void> cases
  • compatibility is now verified in both Jest 29 and Jest 27 environments

What changed

Native TypeScript source

The library source has been migrated from JavaScript to TypeScript, with the public type contract now tested directly in the repo.

Stronger, more accurate typings

This release tightens a number of important typing behaviors:

  • calledWith / expectCalledWith preserve argument tuple types again
  • mockReturnValue / mockResolvedValue / related methods track the mocked function's return type much more accurately
  • void / Promise<void> cases allow omitted values correctly
  • better support for function-like wrapper mocks, including jest-mock-extended-style mocked object methods
  • typed casts, mocked module functions, spies, optional args, rest args, allArgs, and matcher cases are now covered by contract tests

Compatibility guardrails

The repo now has:

  • a primary Jest 29 development environment
  • a dedicated compat/jest27 workspace

The full build, runtime tests, and type tests run in both environments. This was added specifically to avoid fixing newer Jest/mock ecosystems while regressing older supported ones.

Breaking changes

  • minimum supported Jest version is now 27
  • TypeScript usages that previously compiled because the old typings were too loose may now fail typechecking

There are no intentional runtime API changes in this release.

Thanks

Thanks to @tlevesque-ueat for the repros, patience, and repeated validation while this got sorted out.

Convert to TypeScript

05 Sep 01:50

Choose a tag to compare

Convert to TypeScript Pre-release
Pre-release
  • feat: Convert jest-when to TypeScript with excellent type inference 5201c3e

v3.7.0...v3.8.0-0

v3.7.0

03 Dec 14:46

Choose a tag to compare

  • Support resetting of specific calledWith mocks ce60dd5 (closes #108)

v3.6.0...v3.7.0

Support @golevelup/ts-jest and jest-mock-extended proxies

07 Feb 23:39

Choose a tag to compare

v3.5.0...v3.5.1

Default Behavior Methods

14 Dec 19:53

Choose a tag to compare

New methods:

  • defaultReturnValue
  • defaultResolvedValue
  • defaultRejectedValue
  • defaultImplementation

Use them to set up a default behavior, which will serve as fallback if no matcher fits.

when(fn)
  .calledWith('foo').mockReturnValue('special')
  .defaultReturnValue('default') // This line can be placed anywhere, doesn't have to be at the end

expect(fn('foo')).toEqual('special')
expect(fn('bar')).toEqual('default')

Previously you needed to use any of mockReturnValue, mockResolvedValue, mockRejectedValue, or mockImplementation directly on the object before using calledWith to create a default fallback.

// Same as above example
when(fn)
  .mockReturnValue('default')
  .calledWith('foo').mockReturnValue('special')

One neat idea, and the reason I even did this work, is to set up a default implementation that throws an error if an improper call is made to the mock.

when(fn)
  .calledWith(correctArgs)
  .mockReturnValue(expectedValue)
  .defaultImplementation(unsupportedCallError)

// A default implementation that fails your test
function unsupportedCallError(...args) {
  throw new Error(`Wrong args: ${JSON.stringify(args, null, 2)}`);
}

Commits:

  • Merge pull request #92 from timkindberg/default_methods 67b7532
  • Add several methods to set up default behaviors. These methods can be placed anywhere in the mocking set up, including after other calledWith setups, unlike previous "default" behavior using when(fn).mockReturnValue(z). You can now do: when(fn).calledWith(x).mockReturnValue(y).defaultReturnValue(z). 77fe672
  • Add documentation example for unsupported call error as default implementation a6dc74d

v3.4.2...v3.5.0

v3.4.2

28 Oct 01:24

Choose a tag to compare

  • Reset assertionCalls to zero so that our hack doesn't interfere with existing expect.assertions() usage 3a27d0b

v3.4.1...v3.4.2

v3.4.1

05 Oct 16:10

Choose a tag to compare

  • Access the equals util by grabbing it from the expect this arg. Temporary hack until core jest exposes this util officially. 5a8cc93

See jestjs/jest#11867 (comment)

v3.4.0...v3.4.1

v3.4.0

20 Sep 00:07

Choose a tag to compare

New Feature: Use mockReturnValue after or without calledWith usage

Works with mockReturnValue, mockResolvedValue, mockRejectedValue, mockImplementation

Previously you were not allowed to set a default implementation with a mock return function without a calledWith:

when(fn).mockReturnValue(true)
fn() // ⚠️ Threw error!

Now you can!

when(fn).mockReturnValue(true)
fn() // ✅ Returns `true`

Also you could set the default after using calledWith:

when(fn).mockReturnValue('b') // ✅ Before was supported
when(fn).calledWith(1).mockReturnValue('a')
when(fn).mockReturnValue('b') // ⚠️ After was not

Now you can!

when(fn).mockReturnValue('b') // ✅ Before is still supported
when(fn).calledWith(1).mockReturnValue('a')
when(fn).mockReturnValue('b') // ✅ After is now supported

New Feature: Keep original function implementation when not matched

class TheClass {
  fn () {
    return 'real'
  }
}
const instance = new TheClass()

const spy = jest.spyOn(instance, 'fn')
when(spy)
  .calledWith(1)
  .mockReturnValue('mock')
expect(instance.fn(2)).toBe('real') // ✅ Successfully falls back to the real underlying spied function

Installation Improvement: Jest is now a peer dependency

This helps avoid installing lots of duplicated dependencies.

We presume that you were not implicitly relying on jest-when to install the jest dependency in your codebase. And assuming this we are able to bump by a minor instead of major version.

Tested with jest 24, 25, 26 and 27, it should work with other versions as well. We will likely not work to support more than the last couple versions without community PRs.

Commits:

  • Allow defaults to be set up after calledWiths or even without any calledWiths cc6ca58
  • Merge pull request #82 from mfressdorf/master fe6dba9
  • Merge pull request #79 from timkindberg/dependabot/npm_and_yarn/path-parse-1.0.7 bbb5396
  • Merge pull request #74 from timkindberg/dependabot/npm_and_yarn/hosted-git-info-2.8.9 6a523f9
  • Merge pull request #84 from chyzwar/mmake-jest-peer-deps 26a907a
  • Merge branch 'master' into mmake-jest-peer-deps 1379b18
  • Merge pull request #85 from AndrewSouthpaw/replace-bunyan-with-smaller-logger d591350
  • Fully remove logger. fb72636
  • Replace bunyan with more stable, zero-dependency logger. 5624306
  • fix(): lower peer deps to jest 24, make it non breaking ad2084a
  • feat(): make jest an peer dependancy 4092a59
  • Keep original function implementation when not matched 9b81e2b
  • Bump path-parse from 1.0.5 to 1.0.7 1eca1bc
  • Bump hosted-git-info from 2.6.0 to 2.8.9 40e8ef1
  • Merge pull request #73 from timkindberg/dependabot/npm_and_yarn/glob-parent-5.1.2 8af0e4c
  • Merge pull request #72 from timkindberg/dependabot/npm_and_yarn/normalize-url-4.5.1 f8c822e
  • Merge pull request #71 from timkindberg/dependabot/npm_and_yarn/trim-newlines-3.0.1 5c3ef60
  • Merge pull request #70 from timkindberg/dependabot/npm_and_yarn/lodash-4.17.21 0a35f5b
  • Merge pull request #69 from timkindberg/dependabot/npm_and_yarn/handlebars-4.7.7 83e8d88
  • Bump glob-parent from 5.1.1 to 5.1.2 73da46c
  • Bump normalize-url from 4.5.0 to 4.5.1 2577978
  • Bump trim-newlines from 3.0.0 to 3.0.1 a3b5623
  • Bump lodash from 4.17.19 to 4.17.21 d397bc1
  • Bump handlebars from 4.5.3 to 4.7.7 a32694c

v3.3.1...v3.4.0

Fix calling `calledWith` with `null`

03 May 14:12

Choose a tag to compare

This should work again now:

const fn = jest.fn();
when(fn).calledWith(null).mockReturnValue('yay!');
expect(fn(null)).toBe('yay!');

v3.3.0

30 Apr 18:37

Choose a tag to compare

Match All Args with when.allArgs

This release adds support for matching or asserting against all of the arguments together using when.allArgs:

Just pass a single special matcher, when.allArgs. Pass when.allArgs an array predicate function.

calledWith(when.allArgs(anArrayPredicateFn))

The array predicate function will receive all of the arguments as an array as well as the powerful equals utility from Jasmine. It should return true if the args are a match, or false if not.

calledWith(
  when.allArgs(
    (allArgs: Array<any>, equals: Function) => boolean
  )
)

This allows some convenient patterns:

  • Less verbose for variable args where all need to be of a certain type or match (e.g. all numbers)
  • Can be useful for partial matching, because you can assert just the first arg for example and ignore the rest

Examples

All args should be numbers:

const areNumbers = (args, equals) => args.every(arg => equals(arg, expect.any(Number)))
when(fn).calledWith(when.allArgs(areNumbers)).mockReturnValue('yay!')

expect(fn(3, 6, 9)).toEqual('yay!')
expect(fn(3, 666)).toEqual('yay!')
expect(fn(-100, 2, 3.234234, 234, 90e3)).toEqual('yay!')
expect(fn(123, 'not a number')).toBeUndefined()

Single arg match:

const argAtIndex = (index, matcher) => when.allArgs((args, equals) => equals(args[index], matcher))

when(fn).calledWith(argAtIndex(0, expect.any(Number))).mockReturnValue('yay!')

expect(fn(3, 6, 9)).toEqual('yay!')
expect(fn(3, 666)).toEqual('yay!')
expect(fn(-100, 2, 3.234234, 234, 90e3)).toEqual('yay!')
expect(fn(123, 'not a number')).toBeUndefined()

Partial match, only first defined matching args matter:

const fn = jest.fn()
const partialArgs = (...argsToMatch) => when.allArgs((args, equals) => equals(args, expect.arrayContaining(argsToMatch)))

when(fn)
  .calledWith(partialArgs(1, 2, 3))
  .mockReturnValue('x')

expect(fn(1, 2, 3)).toEqual('x')
expect(fn(1, 2, 3, 4, 5, 6)).toEqual('x')
expect(fn(1, 2)).toBeUndefined()
expect(fn(1, 2, 4)).toBeUndefined()