Skip to content

Commit d75d622

Browse files
committed
fixup: consistency with existig code
1 parent b3a096b commit d75d622

File tree

1 file changed

+31
-20
lines changed

1 file changed

+31
-20
lines changed

src/fallback-implementation.ts

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,45 @@
11
import type { AnyCallable, MockInstance } from './types.ts'
22

3-
export function getFallbackImplementation<TFunc extends AnyCallable>(
4-
spy: MockInstance<TFunc>,
5-
) {
6-
// In vitest@<4, getMockImplementation() returns undefined after calling mockReset(), even
7-
// if the mock was initialized with vi.fn(impl) and impl is still active.
8-
// In this case, case the actual implementation from the wrapped tinyspy object.
9-
return spy.getMockImplementation() ?? getTinyspyOriginalImplementation(spy)
3+
/** Get the fallback implementation of a mock if no matching stub is found. */
4+
export const getFallbackImplementation = <TFunc extends AnyCallable>(
5+
mock: MockInstance<TFunc>,
6+
): TFunc | undefined => {
7+
return (
8+
mock.getMockImplementation() ?? getTinyspyInternals(mock)?.getOriginal()
9+
)
1010
}
1111

12-
function getTinyspyOriginalImplementation<TFunc extends AnyCallable>(
13-
maybeTinyspy: MockInstance<TFunc>,
14-
): TFunc | undefined {
15-
// tinyspy stores its internal state in a symbol property of the spy instance.
16-
// This state "survives" vitest's mockReset and is the basis for returning the original
17-
// function's behavior (the one it was instantiated with).
18-
// Note that the state's impl field is not the original implementation after a reset, but getOriginal() is.
19-
for (const sym of Object.getOwnPropertySymbols(maybeTinyspy)) {
20-
const maybeTinyspyInternals = (
21-
maybeTinyspy as unknown as { [sym]: unknown }
22-
)[sym]
12+
/** Internal state from Tinyspy, where a mock's default implementation is stored. */
13+
interface TinyspyInternals<TFunc extends AnyCallable> {
14+
getOriginal: () => TFunc | undefined
15+
}
16+
17+
/**
18+
* Get the fallback implementation out of tinyspy internals.
19+
*
20+
* This slight hack works around a bug in Vitest <= 3
21+
* where `getMockImplementation` will return `undefined` after `mockReset`,
22+
* even if a default implementation is still active.
23+
* The implementation remains present in tinyspy internal state,
24+
* which is stored on a Symbol key in the mock object.
25+
*/
26+
const getTinyspyInternals = <TFunc extends AnyCallable>(
27+
mock: MockInstance<TFunc>,
28+
): TinyspyInternals<TFunc> | undefined => {
29+
const maybeTinyspy = mock as unknown as Record<PropertyKey, unknown>
30+
31+
for (const key of Object.getOwnPropertySymbols(maybeTinyspy)) {
32+
const maybeTinyspyInternals = maybeTinyspy[key]
33+
2334
if (
2435
maybeTinyspyInternals &&
2536
typeof maybeTinyspyInternals === 'object' &&
2637
'getOriginal' in maybeTinyspyInternals &&
2738
typeof maybeTinyspyInternals.getOriginal === 'function'
2839
) {
29-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
30-
return maybeTinyspyInternals.getOriginal() as TFunc
40+
return maybeTinyspyInternals as TinyspyInternals<TFunc>
3141
}
3242
}
43+
3344
return undefined
3445
}

0 commit comments

Comments
 (0)