From 2029a3b134e6d4d1fb3041e132ee91dcbf07df9f Mon Sep 17 00:00:00 2001 From: Matthew Pietz Date: Fri, 7 Nov 2025 15:14:32 -0800 Subject: [PATCH 1/7] Add `ignoreExtraArgs` option --- README.md | 18 +++++++++++++--- src/behaviors.ts | 44 ++++++++++++++++++++++++---------------- test/vitest-when.test.ts | 24 ++++++++++++++++++++++ 3 files changed, 65 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 293e889..e7f9207 100644 --- a/README.md +++ b/README.md @@ -205,9 +205,10 @@ expect(mock()).toBe(undefined) import type { WhenOptions } from 'vitest-when' ``` -| option | default | type | description | -| ------- | ------- | ------- | -------------------------------------------------- | -| `times` | N/A | integer | Only trigger configured behavior a number of times | +| option | default | type | description | +| ----------------- | ------- | ------- | -------------------------------------------------- | +| `ignoreExtraArgs` | `false` | boolean | Ignore extra arguments when matching arguments | +| `times` | N/A | integer | Only trigger configured behavior a number of times | ### `.calledWith(...args: Parameters): Stub` @@ -306,6 +307,17 @@ expect(mock('hello')).toEqual('sup?') expect(mock('hello')).toEqual('sup?') ``` +You can also ignore extra arguments when matching arguments. + +```ts +const mock = when(vi.fn(), { ignoreExtraArgs: true }) + .calledWith('hello') + .thenReturn('world') + +expect(mock('hello')).toEqual('world') +expect(mock('hello', 'jello')).toEqual('world') +``` + ### `.thenResolve(value: TReturn) -> Mock` When the stubbing is satisfied, resolve a `Promise` with `value` diff --git a/src/behaviors.ts b/src/behaviors.ts index e6526a7..2d48fea 100644 --- a/src/behaviors.ts +++ b/src/behaviors.ts @@ -10,6 +10,7 @@ import type { } from './types.ts' export interface WhenOptions { + ignoreExtraArgs?: boolean times?: number } @@ -40,6 +41,7 @@ export interface BehaviorEntry { args: WithMatchers behavior: Behavior calls: TArgs[] + ignoreExtraArgs: boolean maxCallCount?: number | undefined } @@ -60,6 +62,7 @@ export type Behavior = export interface BehaviorOptions { value: TValue + ignoreExtraArgs: boolean maxCallCount: number | undefined } @@ -92,8 +95,9 @@ export const createBehaviorStack = < addReturn: (values) => { behaviors.unshift( ...getBehaviorOptions(values, options).map( - ({ value, maxCallCount }) => ({ + ({ value, ignoreExtraArgs, maxCallCount }) => ({ args, + ignoreExtraArgs, maxCallCount, behavior: { type: BehaviorType.RETURN, value }, calls: [], @@ -104,8 +108,9 @@ export const createBehaviorStack = < addResolve: (values) => { behaviors.unshift( ...getBehaviorOptions(values, options).map( - ({ value, maxCallCount }) => ({ + ({ value, ignoreExtraArgs, maxCallCount }) => ({ args, + ignoreExtraArgs, maxCallCount, behavior: { type: BehaviorType.RESOLVE, value }, calls: [], @@ -116,8 +121,9 @@ export const createBehaviorStack = < addThrow: (values) => { behaviors.unshift( ...getBehaviorOptions(values, options).map( - ({ value, maxCallCount }) => ({ + ({ value, ignoreExtraArgs, maxCallCount }) => ({ args, + ignoreExtraArgs, maxCallCount, behavior: { type: BehaviorType.THROW, error: value }, calls: [], @@ -128,8 +134,9 @@ export const createBehaviorStack = < addReject: (values) => { behaviors.unshift( ...getBehaviorOptions(values, options).map( - ({ value, maxCallCount }) => ({ + ({ value, ignoreExtraArgs, maxCallCount }) => ({ args, + ignoreExtraArgs, maxCallCount, behavior: { type: BehaviorType.REJECT, error: value }, calls: [], @@ -140,8 +147,9 @@ export const createBehaviorStack = < addDo: (values) => { behaviors.unshift( ...getBehaviorOptions(values, options).map( - ({ value, maxCallCount }) => ({ + ({ value, ignoreExtraArgs, maxCallCount }) => ({ args, + ignoreExtraArgs, maxCallCount, behavior: { type: BehaviorType.DO, @@ -158,7 +166,7 @@ export const createBehaviorStack = < const getBehaviorOptions = ( values: TValue[], - { times }: WhenOptions, + { ignoreExtraArgs, times }: WhenOptions, ): BehaviorOptions[] => { if (values.length === 0) { values = [undefined as TValue] @@ -166,6 +174,7 @@ const getBehaviorOptions = ( return values.map((value, index) => ({ value, + ignoreExtraArgs: ignoreExtraArgs ?? false, maxCallCount: times ?? (index < values.length - 1 ? 1 : undefined), })) } @@ -179,18 +188,17 @@ const behaviorAvailable = ( ) } -const behaviorMatches = (args: TArgs) => { +const behaviorMatches = (actualArguments: TArgs) => { return (behavior: BehaviorEntry): boolean => { - let index = 0 - - while (index < args.length || index < behavior.args.length) { - if (!equals(args[index], behavior.args[index])) { - return false - } - - index += 1 - } - - return true + // Check arity + const expectedArguments = behavior.args + const { ignoreExtraArgs } = behavior + if (expectedArguments.length !== actualArguments.length && !ignoreExtraArgs) + return false + + // Check arguments + return expectedArguments.every((expectedArgument, index) => { + return equals(actualArguments[index], expectedArgument) + }) } } diff --git a/test/vitest-when.test.ts b/test/vitest-when.test.ts index c04758d..f044fea 100644 --- a/test/vitest-when.test.ts +++ b/test/vitest-when.test.ts @@ -279,4 +279,28 @@ describe('vitest-when', () => { // intentionally do not call the spy expect(true).toBe(true) }) + + it('should ignore extra args if configured', () => { + const spy = subject + .when(vi.fn(), { ignoreExtraArgs: true }) + .calledWith('Outcomes are:') + .thenReturn('loggy') + + expect(spy('Outcomes are:')).toEqual('loggy') + expect(spy('Outcomes are:', 'stuff')).toEqual('loggy') + expect(spy('Outcomes are:', 'stuff', 'that', 'keeps', 'going')).toEqual( + 'loggy', + ) + expect(spy('Outcomes are not:', 'stuff')).toEqual(undefined) + }) + + it('should ignore all args if configured', () => { + const spy = subject + .when(vi.fn(), { ignoreExtraArgs: true }) + .calledWith() + .thenReturn('yesss') + + expect(spy()).toEqual('yesss') + expect(spy(1, 2, 3, 4, 5)).toEqual('yesss') + }) }) From 8592cdb643ed613db7efae934302626080c8c3d1 Mon Sep 17 00:00:00 2001 From: Matthew Pietz Date: Mon, 10 Nov 2025 15:37:38 -0800 Subject: [PATCH 2/7] Adjust `when` type to support `ignoreExtraArgs` --- src/behaviors.ts | 4 ++-- src/vitest-when.ts | 18 ++++++++++++++++-- test/fixtures.ts | 4 ++++ test/typing.test-d.ts | 12 ++++++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/behaviors.ts b/src/behaviors.ts index 2d48fea..67f5a11 100644 --- a/src/behaviors.ts +++ b/src/behaviors.ts @@ -9,8 +9,8 @@ import type { WithMatchers, } from './types.ts' -export interface WhenOptions { - ignoreExtraArgs?: boolean +export interface WhenOptions { + ignoreExtraArgs?: TIgnore times?: number } diff --git a/src/vitest-when.ts b/src/vitest-when.ts index ad35033..a0c8a4d 100644 --- a/src/vitest-when.ts +++ b/src/vitest-when.ts @@ -22,6 +22,12 @@ export interface StubWrapper { ): Stub } +export interface StubWrapperFlexible { + calledWith>( + ...args: Partial> + ): Stub +} + export interface Stub { thenReturn: (...values: ReturnTypeOf[]) => Mock thenResolve: (...values: Awaited>[]) => Mock @@ -30,10 +36,18 @@ export interface Stub { thenDo: (...callbacks: AsFunction[]) => Mock } -export const when = ( +export function when( + mock: TFunc | MockInstance, + options: WhenOptions, +): StubWrapperFlexible> +export function when( + mock: TFunc | MockInstance, + options?: WhenOptions, +): StubWrapper> +export function when( mock: TFunc | MockInstance, options: WhenOptions = {}, -): StubWrapper> => { +): StubWrapper> { const validatedMock = validateMock(mock) const behaviorStack = configureMock(validatedMock) const result = asMock(validatedMock) diff --git a/test/fixtures.ts b/test/fixtures.ts index d5e7cbd..595b72a 100644 --- a/test/fixtures.ts +++ b/test/fixtures.ts @@ -31,6 +31,10 @@ export function overloaded(input?: number): string | boolean { throw new Error(`overloaded(${input})`) } +export function extraArguments(first: number, second: number): string { + throw new Error(`extraArguments(${first}, ${second})`) +} + export class SimpleClass { constructor(input: number) { throw new Error(`SimpleClass(${input})`) diff --git a/test/typing.test-d.ts b/test/typing.test-d.ts index 493b893..440cac6 100644 --- a/test/typing.test-d.ts +++ b/test/typing.test-d.ts @@ -16,6 +16,7 @@ import { import * as subject from '../src/vitest-when.ts' import { complex, + extraArguments, generic, overloaded, simple, @@ -45,6 +46,17 @@ describe('vitest-when type signatures', () => { >() }) + it('should handle fewer than required arguments', () => { + const result = subject + .when(extraArguments, { ignoreExtraArgs: true }) + .calledWith(1) + .thenReturn('hello') + + expectTypeOf(result).toEqualTypeOf< + MockedFunction<(input: number, second: number) => string> + >() + }) + it('returns mock type for then resolve', () => { const result = subject.when(simpleAsync).calledWith(1).thenResolve('hello') From 5054078fbf3400528e738b41e48a4b8c42f604ef Mon Sep 17 00:00:00 2001 From: Matthew Pietz Date: Mon, 10 Nov 2025 15:44:00 -0800 Subject: [PATCH 3/7] Use variable name for conditional --- src/behaviors.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/behaviors.ts b/src/behaviors.ts index 67f5a11..47536eb 100644 --- a/src/behaviors.ts +++ b/src/behaviors.ts @@ -190,13 +190,12 @@ const behaviorAvailable = ( const behaviorMatches = (actualArguments: TArgs) => { return (behavior: BehaviorEntry): boolean => { - // Check arity const expectedArguments = behavior.args - const { ignoreExtraArgs } = behavior - if (expectedArguments.length !== actualArguments.length && !ignoreExtraArgs) + const arityMatches = expectedArguments.length === actualArguments.length + if (!arityMatches && !behavior.ignoreExtraArgs) { return false + } - // Check arguments return expectedArguments.every((expectedArgument, index) => { return equals(actualArguments[index], expectedArgument) }) From a5ff0b7117fff4cf0418f07bfadd6a1ceb123616 Mon Sep 17 00:00:00 2001 From: Matthew Pietz Date: Mon, 10 Nov 2025 16:24:55 -0800 Subject: [PATCH 4/7] Improve typing --- src/types.ts | 8 ++++++++ src/vitest-when.ts | 3 ++- test/typing.test-d.ts | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/types.ts b/src/types.ts index 916c8dc..62d39a0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -82,3 +82,11 @@ export type Mock = TFunc extends AnyConstructor : TFunc extends AnyFunction ? MockedFunction : never + +/** Produce a union of progressively longer tuples based on the input tuple */ +export type PartialArguments< + Tuple extends any[], + _Previous extends any[] = [], +> = Tuple extends [infer First, ...infer Rest] + ? [..._Previous, First] | PartialArguments + : never diff --git a/src/vitest-when.ts b/src/vitest-when.ts index a0c8a4d..9b730f7 100644 --- a/src/vitest-when.ts +++ b/src/vitest-when.ts @@ -8,6 +8,7 @@ import type { MockInstance, NormalizeMockable, ParametersOf, + PartialArguments, ReturnTypeOf, WithMatchers, } from './types.ts' @@ -24,7 +25,7 @@ export interface StubWrapper { export interface StubWrapperFlexible { calledWith>( - ...args: Partial> + ...args: PartialArguments> ): Stub } diff --git a/test/typing.test-d.ts b/test/typing.test-d.ts index 440cac6..db2b526 100644 --- a/test/typing.test-d.ts +++ b/test/typing.test-d.ts @@ -57,6 +57,13 @@ describe('vitest-when type signatures', () => { >() }) + it('should ensure correct type of previous arguments', () => { + subject + .when(extraArguments, { ignoreExtraArgs: true }) + /* @ts-expect-error: first arg is not correct */ + .calledWith(undefined, 2) + }) + it('returns mock type for then resolve', () => { const result = subject.when(simpleAsync).calledWith(1).thenResolve('hello') From 8ef622651673952ec1b1d4778a90eade6c36aafc Mon Sep 17 00:00:00 2001 From: Matthew Pietz Date: Mon, 10 Nov 2025 16:50:29 -0800 Subject: [PATCH 5/7] Pass any[] out if it was received --- src/behaviors.ts | 4 ++-- src/types.ts | 5 ++++- src/vitest-when.ts | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/behaviors.ts b/src/behaviors.ts index 47536eb..02f7385 100644 --- a/src/behaviors.ts +++ b/src/behaviors.ts @@ -9,8 +9,8 @@ import type { WithMatchers, } from './types.ts' -export interface WhenOptions { - ignoreExtraArgs?: TIgnore +export interface WhenOptions { + ignoreExtraArgs?: boolean times?: number } diff --git a/src/types.ts b/src/types.ts index 62d39a0..c474db5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -83,10 +83,13 @@ export type Mock = TFunc extends AnyConstructor ? MockedFunction : never +/** If the type is any, return Yes, otherwise return No */ +type IfAny = 0 extends (1 & T) ? Yes : No; + /** Produce a union of progressively longer tuples based on the input tuple */ export type PartialArguments< Tuple extends any[], _Previous extends any[] = [], > = Tuple extends [infer First, ...infer Rest] ? [..._Previous, First] | PartialArguments - : never + : IfAny diff --git a/src/vitest-when.ts b/src/vitest-when.ts index 9b730f7..a2bcf00 100644 --- a/src/vitest-when.ts +++ b/src/vitest-when.ts @@ -39,11 +39,11 @@ export interface Stub { export function when( mock: TFunc | MockInstance, - options: WhenOptions, + options: { ignoreExtraArgs: true } & WhenOptions, ): StubWrapperFlexible> export function when( mock: TFunc | MockInstance, - options?: WhenOptions, + options?: WhenOptions, ): StubWrapper> export function when( mock: TFunc | MockInstance, From 36ae1ca7b29647ac1c5ad167e9abfe0130684fa9 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Tue, 11 Nov 2025 12:28:17 -0500 Subject: [PATCH 6/7] fixup: simplify types --- src/behaviors.ts | 8 ++------ src/types.ts | 21 ++++++++++----------- src/vitest-when.ts | 34 +++++++++++++--------------------- test/fixtures.ts | 8 ++++---- test/typing.test-d.ts | 37 +++++++++++++++++++++++++------------ 5 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/behaviors.ts b/src/behaviors.ts index 02f7385..184fcba 100644 --- a/src/behaviors.ts +++ b/src/behaviors.ts @@ -6,7 +6,6 @@ import type { AsFunction, ParametersOf, ReturnTypeOf, - WithMatchers, } from './types.ts' export interface WhenOptions { @@ -23,10 +22,7 @@ export interface BehaviorStack { getUnmatchedCalls: () => readonly ParametersOf[] - bindArgs: ( - args: WithMatchers>, - options: WhenOptions, - ) => BoundBehaviorStack + bindArgs: (args: unknown[], options: WhenOptions) => BoundBehaviorStack } export interface BoundBehaviorStack { @@ -38,7 +34,7 @@ export interface BoundBehaviorStack { } export interface BehaviorEntry { - args: WithMatchers + args: unknown[] behavior: Behavior calls: TArgs[] ignoreExtraArgs: boolean diff --git a/src/types.ts b/src/types.ts index c474db5..4f679d0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -49,6 +49,16 @@ export type ParametersOf = ? Parameters : never +/** An arguments list, optionally without every argument specified */ +export type ArgumentsSpec< + TArgs extends any[], + TOptions extends { ignoreExtraArgs?: boolean } | undefined, +> = TOptions extends { ignoreExtraArgs: true } + ? TArgs extends [infer Head, ...infer Tail] + ? [] | [Head] | [Head, ...ArgumentsSpec] + : TArgs + : TArgs + /** Extract return type from either a function or constructor */ export type ReturnTypeOf = TFunc extends AnyConstructor @@ -82,14 +92,3 @@ export type Mock = TFunc extends AnyConstructor : TFunc extends AnyFunction ? MockedFunction : never - -/** If the type is any, return Yes, otherwise return No */ -type IfAny = 0 extends (1 & T) ? Yes : No; - -/** Produce a union of progressively longer tuples based on the input tuple */ -export type PartialArguments< - Tuple extends any[], - _Previous extends any[] = [], -> = Tuple extends [infer First, ...infer Rest] - ? [..._Previous, First] | PartialArguments - : IfAny diff --git a/src/vitest-when.ts b/src/vitest-when.ts index a2bcf00..0a6f549 100644 --- a/src/vitest-when.ts +++ b/src/vitest-when.ts @@ -3,12 +3,12 @@ import { type DebugResult, getDebug } from './debug.ts' import { asMock, configureMock, validateMock } from './stubs.ts' import type { AnyMockable, + ArgumentsSpec, AsFunction, Mock, MockInstance, NormalizeMockable, ParametersOf, - PartialArguments, ReturnTypeOf, WithMatchers, } from './types.ts' @@ -17,18 +17,15 @@ export { type Behavior, BehaviorType, type WhenOptions } from './behaviors.ts' export type { DebugResult, Stubbing } from './debug.ts' export * from './errors.ts' -export interface StubWrapper { - calledWith>( +export interface StubWrapper< + TFunc extends AnyMockable, + TOptions extends WhenOptions | undefined, +> { + calledWith, TOptions>>( ...args: WithMatchers ): Stub } -export interface StubWrapperFlexible { - calledWith>( - ...args: PartialArguments> - ): Stub -} - export interface Stub { thenReturn: (...values: ReturnTypeOf[]) => Mock thenResolve: (...values: Awaited>[]) => Mock @@ -37,25 +34,20 @@ export interface Stub { thenDo: (...callbacks: AsFunction[]) => Mock } -export function when( - mock: TFunc | MockInstance, - options: { ignoreExtraArgs: true } & WhenOptions, -): StubWrapperFlexible> -export function when( - mock: TFunc | MockInstance, - options?: WhenOptions, -): StubWrapper> -export function when( +export const when = < + TFunc extends AnyMockable, + TOptions extends WhenOptions | undefined = undefined, +>( mock: TFunc | MockInstance, - options: WhenOptions = {}, -): StubWrapper> { + options?: TOptions, +): StubWrapper, TOptions> => { const validatedMock = validateMock(mock) const behaviorStack = configureMock(validatedMock) const result = asMock(validatedMock) return { calledWith: (...args) => { - const behaviors = behaviorStack.bindArgs(args, options) + const behaviors = behaviorStack.bindArgs(args as unknown[], options ?? {}) return { thenReturn: (...values) => { diff --git a/test/fixtures.ts b/test/fixtures.ts index 595b72a..84f44ae 100644 --- a/test/fixtures.ts +++ b/test/fixtures.ts @@ -17,6 +17,10 @@ export async function simpleAsync(input: number): Promise { throw new Error(`simpleAsync(${input})`) } +export function multipleArgs(a: number, b: string, c: boolean): string { + throw new Error(`multipleArgs(${a}, ${b}, ${c})`) +} + export function complex(input: { a: number; b: string }): string { throw new Error(`simple({ a: ${input.a}, b: ${input.b} })`) } @@ -31,10 +35,6 @@ export function overloaded(input?: number): string | boolean { throw new Error(`overloaded(${input})`) } -export function extraArguments(first: number, second: number): string { - throw new Error(`extraArguments(${first}, ${second})`) -} - export class SimpleClass { constructor(input: number) { throw new Error(`SimpleClass(${input})`) diff --git a/test/typing.test-d.ts b/test/typing.test-d.ts index db2b526..bbc94ae 100644 --- a/test/typing.test-d.ts +++ b/test/typing.test-d.ts @@ -16,8 +16,8 @@ import { import * as subject from '../src/vitest-when.ts' import { complex, - extraArguments, generic, + multipleArgs, overloaded, simple, simpleAsync, @@ -47,21 +47,34 @@ describe('vitest-when type signatures', () => { }) it('should handle fewer than required arguments', () => { - const result = subject - .when(extraArguments, { ignoreExtraArgs: true }) - .calledWith(1) - .thenReturn('hello') + subject.when(multipleArgs, { ignoreExtraArgs: true }).calledWith(42) - expectTypeOf(result).toEqualTypeOf< - MockedFunction<(input: number, second: number) => string> - >() + subject + .when(multipleArgs, { ignoreExtraArgs: true }) + .calledWith(42, 'hello') + + subject + .when(multipleArgs, { ignoreExtraArgs: true }) + .calledWith(42, 'hello', true) + + subject + .when(multipleArgs, { ignoreExtraArgs: true }) + // @ts-expect-error: too many arguments + .calledWith(42, 'hello', true, 'oh no') }) - it('should ensure correct type of previous arguments', () => { + it('supports using matchers with ignoreExtraArgs', () => { + subject + .when(multipleArgs, { ignoreExtraArgs: true }) + .calledWith(expect.any(Number)) + + subject + .when(multipleArgs, { ignoreExtraArgs: true }) + .calledWith(expect.any(Number), expect.any(String)) + subject - .when(extraArguments, { ignoreExtraArgs: true }) - /* @ts-expect-error: first arg is not correct */ - .calledWith(undefined, 2) + .when(multipleArgs, { ignoreExtraArgs: true }) + .calledWith(expect.any(Number), expect.any(String), expect.any(Boolean)) }) it('returns mock type for then resolve', () => { From 0d660d752baa583223910ba892b3484223e51c39 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Tue, 11 Nov 2025 15:14:47 -0500 Subject: [PATCH 7/7] fixup: fix arrity check, add test --- src/behaviors.ts | 21 ++++++++------- test/vitest-when.test.ts | 56 +++++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/behaviors.ts b/src/behaviors.ts index 184fcba..49b6ec9 100644 --- a/src/behaviors.ts +++ b/src/behaviors.ts @@ -75,7 +75,7 @@ export const createBehaviorStack = < use: (args) => { const behavior = behaviors - .filter((b) => behaviorAvailable(b)) + .filter(behaviorAvailable) .find(behaviorMatches(args)) if (!behavior) { @@ -184,16 +184,19 @@ const behaviorAvailable = ( ) } -const behaviorMatches = (actualArguments: TArgs) => { - return (behavior: BehaviorEntry): boolean => { - const expectedArguments = behavior.args - const arityMatches = expectedArguments.length === actualArguments.length - if (!arityMatches && !behavior.ignoreExtraArgs) { +const behaviorMatches = (actualArgs: TArgs) => { + return (behaviorEntry: BehaviorEntry): boolean => { + const { args: expectedArgs, ignoreExtraArgs } = behaviorEntry + const isArgsLengthMatch = ignoreExtraArgs + ? expectedArgs.length <= actualArgs.length + : expectedArgs.length === actualArgs.length + + if (!isArgsLengthMatch) { return false } - return expectedArguments.every((expectedArgument, index) => { - return equals(actualArguments[index], expectedArgument) - }) + return expectedArgs.every((expected, index) => + equals(actualArgs[index], expected), + ) } } diff --git a/test/vitest-when.test.ts b/test/vitest-when.test.ts index f044fea..92e49ac 100644 --- a/test/vitest-when.test.ts +++ b/test/vitest-when.test.ts @@ -280,27 +280,37 @@ describe('vitest-when', () => { expect(true).toBe(true) }) - it('should ignore extra args if configured', () => { - const spy = subject - .when(vi.fn(), { ignoreExtraArgs: true }) - .calledWith('Outcomes are:') - .thenReturn('loggy') - - expect(spy('Outcomes are:')).toEqual('loggy') - expect(spy('Outcomes are:', 'stuff')).toEqual('loggy') - expect(spy('Outcomes are:', 'stuff', 'that', 'keeps', 'going')).toEqual( - 'loggy', - ) - expect(spy('Outcomes are not:', 'stuff')).toEqual(undefined) - }) - - it('should ignore all args if configured', () => { - const spy = subject - .when(vi.fn(), { ignoreExtraArgs: true }) - .calledWith() - .thenReturn('yesss') - - expect(spy()).toEqual('yesss') - expect(spy(1, 2, 3, 4, 5)).toEqual('yesss') - }) + it.each([ + { stubArgs: [] as unknown[], callArgs: [] as unknown[] }, + { stubArgs: [], callArgs: ['a'] }, + { stubArgs: [], callArgs: ['a', 'b'] }, + { stubArgs: ['a'], callArgs: ['a'] }, + { stubArgs: ['a'], callArgs: ['a', 'b'] }, + { stubArgs: ['a', 'b'], callArgs: ['a', 'b'] }, + ])( + 'matches call $callArgs against stub $stubArgs args with ignoreExtraArgs', + ({ stubArgs, callArgs }) => { + const spy = subject + .when(vi.fn().mockReturnValue('failure'), { ignoreExtraArgs: true }) + .calledWith(...stubArgs) + .thenReturn('success') + + expect(spy(...callArgs)).toEqual('success') + }, + ) + + it.each([ + { stubArgs: ['a'] as unknown[], callArgs: ['b'] as unknown[] }, + { stubArgs: [undefined], callArgs: [] }, + ])( + 'does not match call $callArgs against stub $stubArgs with ignoreExtraArgs', + ({ stubArgs, callArgs }) => { + const spy = subject + .when(vi.fn().mockReturnValue('success'), { ignoreExtraArgs: true }) + .calledWith(...stubArgs) + .thenReturn('failure') + + expect(spy(...callArgs)).toBe('success') + }, + ) })