spy4js
Advanced tools
Comparing version 4.0.0-beta.2 to 4.0.0-beta.3
{ | ||
"name": "spy4js", | ||
"version": "4.0.0-beta.2", | ||
"version": "4.0.0-beta.3", | ||
"description": "Smart, compact and powerful spy test framework", | ||
"main": "dist/cjs/index.js", | ||
"module": "dist/esm/index.js", | ||
"types": "dist/types/index.d.ts", | ||
"main": "dist/index.cjs", | ||
"module": "dist/index.mjs", | ||
"types": "dist/index.d.ts", | ||
"files": [ | ||
@@ -28,9 +28,5 @@ "dist" | ||
"devDependencies": { | ||
"@babel/core": "^7.23.7", | ||
"@babel/preset-env": "^7.23.8", | ||
"@babel/preset-typescript": "^7.23.3", | ||
"@rollup/plugin-babel": "^6.0.4", | ||
"@rollup/plugin-node-resolve": "^15.2.3", | ||
"@sucrase/jest-plugin": "^3.0.0", | ||
"@testing-library/react": "^14.1.2", | ||
"@types/bun": "^1.0.1", | ||
"@types/jest": "^29.5.11", | ||
@@ -41,4 +37,3 @@ "@types/node": "^20.11.0", | ||
"@typescript-eslint/parser": "^6.18.1", | ||
"@vitest/coverage-c8": "^0.33.0", | ||
"@vitest/coverage-v8": "^1.1.3", | ||
"@vitest/coverage-v8": "^1.2.0", | ||
"coveralls": "^3.1.1", | ||
@@ -50,12 +45,13 @@ "eslint": "^8.56.0", | ||
"jest-environment-jsdom": "^29.7.0", | ||
"prettier": "^3.1.1", | ||
"prettier": "^3.2.1", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0", | ||
"rollup": "^4.9.4", | ||
"sucrase": "^3.35.0", | ||
"typescript": "^5.3.3", | ||
"vitest": "^1.1.3" | ||
"vite": "^5.0.11", | ||
"vitest": "^1.2.0" | ||
}, | ||
"scripts": { | ||
"dist": "rm -rf dist && rollup -c && tsc -p tsconfig.publish.json", | ||
"dist": "vite build && pnpm dist:ts", | ||
"dist:ts": "tsc -p tsconfig.publish.json", | ||
"lint": "pnpm lint:es && pnpm lint:ts", | ||
@@ -66,3 +62,3 @@ "lint:es": "eslint --ext .ts src/ test/ --max-warnings 0", | ||
"test:jest": "jest --all --color", | ||
"test:vi": "vitest run --coverage", | ||
"test:vi": "VITE_CJS_IGNORE_WARNING=true vitest run --coverage", | ||
"test:bun": "bun test", | ||
@@ -69,0 +65,0 @@ "test:update": "jest -u", |
197
README.md
@@ -10,9 +10,9 @@ [![GitHub license][license-image]][license-url] | ||
- `TypeScript` support included | ||
- Performance | ||
- No foreign dependencies | ||
- Optimized error messages | ||
- Customizable | ||
- Intuitive | ||
- Used in production of large projects | ||
- `TypeScript` support included | ||
- Performance | ||
- No foreign dependencies | ||
- Optimized error messages | ||
- Customizable | ||
- Intuitive | ||
- Used in production of large projects | ||
@@ -24,4 +24,4 @@ ### Introduction | ||
⚠️Disclaimer: You don't need this library as the frameworks already everything in order to achieve | ||
everything without this library. | ||
⚠️ Disclaimer: You don't need this library as the frameworks already include everything in order | ||
to achieve everything without this library. | ||
@@ -31,3 +31,3 @@ **spy4js** exports only one object called `Spy`. The spy instances | ||
**Hint**: | ||
**Hint**: | ||
If you are using other frameworks you might not be able to benefit from all features of this library. | ||
@@ -38,3 +38,4 @@ Since the test frameworks already include excellent spies themselves, you might ask yourself, why `spy4js`... | ||
Advantages over Jest spies: | ||
Advantages over Jest/Vitest spies: | ||
- Very important for tests is their readability. This spy API is much easier to learn, and | ||
@@ -50,8 +51,12 @@ the tests can be understood even without any previous knowledge. | ||
### Installation | ||
##### With yarn | ||
``` | ||
```sh | ||
yarn add --dev spy4js | ||
``` | ||
##### With npm | ||
``` | ||
```sh | ||
npm install --save-dev spy4js | ||
@@ -83,3 +88,7 @@ ``` | ||
// mock exported functions from other modules | ||
// if your test runner executes with CJS | ||
const myModuleMocksInJest = Spy.mock(require('./my-module'), 'useMe'); | ||
const myModuleMocksInJestTypeSafe = Spy.mock(require('./my-module') as typeof import('./my-module'), 'useMe'); | ||
// if your test runner supports ESM | ||
vi.mock('./my-module', async () => ({ ...((await vi.importActual('./my-module')) as any) })); | ||
@@ -89,3 +98,10 @@ const myModuleMocksInVitest = Spy.mock(await import('./my-module'), 'useMe'); | ||
// mock React components from other modules | ||
// if your test runner executes with CJS | ||
const mockedReactComponentsInJest = Spy.mockReactComponents(require('./my/fancy/Calculator'), 'Calculator'); | ||
const mockedReactComponentsInJestTypeSafe = Spy.mockReactComponents( | ||
require('./my/fancy/Calculator') as typeof import('./my/fancy/Calculator'), | ||
'Calculator' | ||
); | ||
// if your test runner supports ESM | ||
vi.mock('./my/fancy/Calculator', async () => ({ ...((await vi.importActual('./my/fancy/Calculator')) as any) })); | ||
@@ -96,3 +112,3 @@ const mockedReactComponentsInVitest = Spy.mockReactComponents(await import('./my/fancy/Calculator'), 'Calculator'); | ||
You may apply additional behavior to every spy. The valid operations here are: | ||
- `configure` (allows to override some behavior of the spy) | ||
@@ -109,3 +125,3 @@ - `calls` (does make the spy call the provided functions sequentially) | ||
- `addSnapshotSerializer` (defines in `jest` snapshots how the spy will be serialized) | ||
All those methods on a spy has been designed in a builder pattern. So you may chain any of | ||
@@ -201,3 +217,3 @@ these configurations. Be aware some behaviors override existing behaviors. | ||
- `getCallCount` (returns the number of made calls) | ||
```ts | ||
@@ -237,8 +253,11 @@ const spy = Spy(); | ||
### Constructor | ||
```ts | ||
Spy(spyName:string = 'the spy') => SpyInstance | ||
``` | ||
The returned Spy instance has his own name-attribute (only) for debugging purpose. | ||
### setup (static) | ||
```ts | ||
@@ -256,4 +275,6 @@ Spy.setup(config: { | ||
``` | ||
This function should be ideally called in some setup-test file, but is required to be called to get all | ||
benefits of `spy4js`. | ||
- **afterEach**: The test runners `afterEach` hook (default: `global.afterEach`) | ||
@@ -266,2 +287,3 @@ - **beforeEach**: The test runners `beforeEach` hook (default: `global.beforeEach`) | ||
### configure (static) | ||
```ts | ||
@@ -275,6 +297,8 @@ Spy.configure(config: { | ||
``` | ||
Using this function you may edit the default behavior `spy4js` itself. | ||
- **useOwnEquals**: Applies for all spy instances. See [configure](#configure) for more details. | ||
- **enforceOrder**: Opt-in to the [enforce-order mode](#enforce-order-mode). | ||
- **useGenericReactMocks**: Lets you opt in into using generic react components for mocks | ||
- **useGenericReactMocks**: Lets you opt in into using generic react components for mocks | ||
created via [mockReactComponents](#mockreactcomponents-static). | ||
@@ -284,8 +308,10 @@ - **afterEachCb**: Lets you override the default afterEach fuctionality. | ||
### on (static) | ||
```ts | ||
Spy.on(object: object, methodName: string) => SpyInstance | ||
``` | ||
Initializing a spy on an object, simply replaces the original function by a spy and | ||
stores the necessary information to be able to restore the mocked method. | ||
Initializing a spy on an object, simply replaces the original function by a spy and | ||
stores the necessary information to be able to restore the mocked method. | ||
If the attribute has already been spied or is not a function, the Spy will throw an exception | ||
@@ -296,5 +322,7 @@ to avoid unexpected behavior. You never want to spy other attributes than functions and | ||
### mock (static) | ||
```ts | ||
Spy.mock(object: object, ...methodNames: string[]) => Object (Mock) | ||
``` | ||
Creating an object that references spies for all given methodNames. | ||
@@ -305,6 +333,8 @@ Initialize as many spies as required for the same object. Only | ||
### mockReactComponents (static) | ||
```ts | ||
Spy.mockReactComponents(object: object, ...methodNames: string[]) => Object (Mock) | ||
``` | ||
Same as [mockModule](#mockModule-static) but designed for ReactJS components. The registered | ||
Same as [mock](#mock-static) but designed for ReactJS components. The registered | ||
spies return `null` instead of `undefined`. This makes minimal usable React components. | ||
@@ -317,13 +347,17 @@ Even if in most cases the pure mocking is nice enough, you can even test the number | ||
### initMocks (static) | ||
```ts | ||
Spy.initMocks(scope?: string) => void | ||
``` | ||
Does initialize all global and scope-related mocks by applying spies. Mocks can be | ||
created with [mock](#mock) or [mockModule](#mockModule). This function has not to | ||
created with [mock](#mock-static) or [mockReactComponents](#mockreactcomponents-static). This function has not to | ||
be called manually, if you rely on the default test suite hooks. | ||
### restoreAll (static) | ||
```ts | ||
Spy.restoreAll() => void | ||
``` | ||
Does restore all mocked objects to their original state. See [restore](#restore) for | ||
@@ -334,5 +368,7 @@ further information. This function has not be called manually, if you rely on | ||
### resetAll (static) | ||
```ts | ||
Spy.resetAll() => void | ||
``` | ||
Does reset all existing spies. This applies even to persistent spies. | ||
@@ -344,7 +380,10 @@ See [reset](#reset) for further information. This function has not to be | ||
### IGNORE (static) | ||
```ts | ||
Spy.IGNORE = $Internal Symbol$ | ||
``` | ||
This object can be passed anywhere where you want the "[wasCalledWith](#wascalledwith-fact)" | ||
or "[hasCallHistory](#hascallhistory-fact)" to ignore that object or value for comparison. | ||
```ts | ||
@@ -358,8 +397,11 @@ spy({prop: 'value', other: 13}, 12); | ||
### COMPARE (static) | ||
```ts | ||
Spy.COMPARE(comparator: (arg: any) => boolean | void) => SpyComparator | ||
``` | ||
This function can be called with a custom comparator and passed anywhere where you want the "[wasCalledWith](#wascalledwith-fact)" | ||
or "[hasCallHistory](#hasCallHistory-fact)" to apply your custom comparison. Very useful if | ||
or "[hasCallHistory](#hascallhistory-fact)" to apply your custom comparison. Very useful if | ||
the spy gets called with functions that you want to test additionally. | ||
```ts | ||
@@ -375,10 +417,13 @@ spy(() => ({ prop: 'value', other: 13 }), 12); | ||
### MAPPER (static) | ||
```ts | ||
Spy.MAPPER(from: any | any[], to: any) => SpyComparator | ||
``` | ||
This function can be called in the same places like `Spy.COMPARE`. It is not that much | ||
customizable but provides a nice way to evaluate mapper functions. Meaning pure | ||
customizable but provides a nice way to evaluate mapper functions. Meaning pure | ||
functions that return some output for given inputs. The function will be called exactly | ||
once for each comparison, so you can even rely on site effects you might want to test, | ||
if you want to use this for non-pure functions. | ||
```ts | ||
@@ -393,12 +438,14 @@ spy((value: number) => ({ prop: 'here', other: value }), 12); | ||
### configure | ||
```ts | ||
spy.configure(config: { useOwnEquals?: boolean, persistent?: boolean }) => (this) SpyInstance | ||
``` | ||
With `configure` the spy can be configured. One configuration possibility | ||
is to ignore any `equals` methods while comparing objects. There might be libraries which | ||
come with those methods, but do not support ES6 classes or anything else. By default, this | ||
configuration has been set to favor own `equals` implementations while comparing objects. | ||
configuration has been set to favor own `equals` implementations while comparing objects. | ||
Another possible configuration is to make the spy persist while other spies have to restore | ||
when ["restoreAll"](#restoreall) was called. This spy can ONLY RESTORE the mocked object when | ||
when ["restoreAll"](#restoreall-static) was called. This spy can ONLY RESTORE the mocked object when | ||
you configure it back to be NOT PERSISTENT. This configuration can only be applied to mocking | ||
@@ -408,5 +455,7 @@ spies. For Spies created with `Spy()` this configuration will throw an exception. | ||
### calls | ||
```ts | ||
spy.calls(...functions:Array<Function>) => (this) SpyInstance | ||
``` | ||
The provided functions will be called sequentially in order when the spy will be called. | ||
@@ -417,5 +466,7 @@ Meaning `spy.calls(func1, func2, func3)` will call first `func1` then `func2` and the rest | ||
### returns | ||
```ts | ||
spy.returns(...args: Array<any>) => (this) SpyInstance | ||
``` | ||
The provided arguments will be returned sequentially in order when the spy will be called. | ||
@@ -426,5 +477,7 @@ Meaning `spy.returns(arg1, arg2, arg3)` will return first `arg1` then `arg2` and the rest | ||
### resolves | ||
```ts | ||
spy.resolves(...args: Array<any>) => (this) SpyInstance | ||
``` | ||
The provided arguments will be resolved sequentially in order when the spy will be called. | ||
@@ -434,7 +487,8 @@ Meaning `spy.resolves(arg1, arg2, arg3)` will return first `Promise.resolve(arg1)` then `Promise.resolve(arg2)` and the rest | ||
### rejects | ||
### rejects | ||
```ts | ||
spy.rejects(...args: Array<?string | Error>) => (this) SpyInstance | ||
``` | ||
The provided arguments will be rejected sequentially in order when the spy will be called. | ||
@@ -446,5 +500,7 @@ Meaning `spy.rejects('foo', null, new Error('bar'))` will return first `Promise.reject(new Error('foo'))` | ||
### throws | ||
```ts | ||
spy.throws(message: ?string | Error) => (this) SpyInstance | ||
``` | ||
Perform this on a spy to make it throw an error when called. The error message can be | ||
@@ -455,11 +511,15 @@ provided, but a default has also been implemented. If an Error instance will be passed, | ||
### reset | ||
```ts | ||
spy.reset() => (this) SpyInstance | ||
``` | ||
Does reset the registered calls on that spy. | ||
### restore | ||
```ts | ||
spy.restore() => (this) SpyInstance | ||
``` | ||
Restores the spied object, if existing, to its original state. The spy won't lose any | ||
@@ -472,5 +532,7 @@ other information. So it is still aware of made calls, can be plugged anywhere else | ||
### transparent | ||
```ts | ||
spy.transparent() => (this) SpyInstance | ||
``` | ||
Can be useful with spies on objects. It does make the spy behave like not existing. So | ||
@@ -481,5 +543,7 @@ the original function of the "mocked" object will be called, but the spy does remember | ||
### transparentAfter | ||
```ts | ||
spy.transparentAfter(callCount:number) => (this) SpyInstance | ||
``` | ||
Works like [transparent](#transparent) but the spy will get transparent after called as | ||
@@ -490,11 +554,15 @@ often as specified. Meaning `spy.transparentAfter(num)` will not be transparent on the first | ||
### addSnapshotSerializer | ||
```ts | ||
spy.addSnapshotSerializer(serialize: string | ((...args: any[]) => string)) => (this) SpyInstance | ||
``` | ||
Determines the rendered output of `jest` snapshots when the certain spy would get rendered. | ||
### wasCalled (fact) | ||
```ts | ||
spy.wasCalled(callCount: number = 0) => void | ||
``` | ||
This call does display a fact. So if the spy is violating the fact, it is told to throw | ||
@@ -504,5 +572,7 @@ an error. The provided argument does represent the registered calls on that spy. | ||
### wasNotCalled (fact) | ||
```ts | ||
spy.wasNotCalled() => void | ||
``` | ||
This fact displays that the spy has never been called. Directly after the spy was [reset](#reset)ed, | ||
@@ -512,7 +582,9 @@ this fact will be given. | ||
### wasCalledWith (fact) | ||
```ts | ||
spy.wasCalledWith(...args: Array<any>) => void | ||
``` | ||
This fact displays that the spy has been called at least once with equal arguments. | ||
This fact displays that the spy has been called at least once with equal arguments. | ||
The equality check is a deep equality check, which (by default) does consider | ||
@@ -522,6 +594,6 @@ own "equals" implementations. | ||
By supplying `Spy.IGNORE` anywhere inside the expected call arguments, you | ||
can avoid that the comparison will be further executed. See [Spy.IGNORE](#IGNORE) for further information and examples. | ||
can avoid that the comparison will be further executed. See [Spy.IGNORE](#ignore-static) for further information and examples. | ||
The deep equality check does also recursively iterate to the first difference found and is able | ||
to return a string which contains valuable information about the first found difference. | ||
to return a string which contains valuable information about the first found difference. | ||
@@ -533,11 +605,15 @@ If any difference will be detected, the fact isn't true, and a helpful error message will be displayed. | ||
### wasNotCalledWith (fact) | ||
```ts | ||
spy.wasNotCalledWith(...args: Array<any>) => void | ||
``` | ||
This fact displays simply the opposite of [wasCalledWith](#wascalledwith-fact). | ||
### hasCallHistory (fact) | ||
```ts | ||
spy.hasCallHistory(...callHistory: Array<Array<any> | any>) => void | ||
``` | ||
Works similar to [wasCalledWith](#wascalledwith-fact) but instead matches each | ||
@@ -550,5 +626,7 @@ call one by one in **correct order** and **correct call count**. | ||
### hasProps (fact) | ||
```ts | ||
spy.hasProps(props: any) => void | ||
``` | ||
This fact displays that the spy has currently the given props in React context of a mocked component. | ||
@@ -559,5 +637,7 @@ | ||
### getAllCallArguments | ||
```ts | ||
spy.getAllCallArguments() => Array<any[]> | ||
``` | ||
Returns the call arguments of all made calls to the spy. | ||
@@ -567,5 +647,7 @@ Especially returning an empty array if the spy has never been called. | ||
### getCallArguments | ||
```ts | ||
spy.getCallArguments(callNr: number = 0) => Array<any> | ||
``` | ||
Returns the call arguments that were registered on the given call. Meaning | ||
@@ -577,5 +659,7 @@ `spy.getCallArguments(num)` does return the (num + 1)'th call arguments. | ||
### getCallArgument | ||
```ts | ||
spy.getCallArgument(callNr: number = 0, argNr: number = 0) => any | ||
``` | ||
Same as [getCallArguments](#getcallarguments) but returns only a single entry out | ||
@@ -586,11 +670,15 @@ of the array of arguments. Most useful in situations where exactly one call param is expected. | ||
### getLatestCallArgument | ||
```ts | ||
spy.getLatestCallArgument(argNr: number = 0) => any | ||
``` | ||
Same as [getCallArgument](#getcallargument) but uses only the latest call. | ||
### getProps | ||
```ts | ||
spy.getProps() => any | ||
``` | ||
Same as [getLatestCallArgument](#getlatestcallargument) but uses only the first arg. Can be useful in | ||
@@ -600,13 +688,18 @@ combination with [mockReactComponents](#mockreactcomponents-static). | ||
### getCallCount | ||
```ts | ||
spy.getCallCount() => number | ||
``` | ||
This method simply returns the number of made calls on the spy. | ||
### showCallArguments | ||
```ts | ||
spy.showCallArguments(additionalInformation: Array<string> = []) => string | ||
``` | ||
This primarily internally used method is responsible for returning formatted informative debug | ||
messages when facts aren't true. Let's do an example: | ||
```ts | ||
@@ -620,8 +713,12 @@ const spy = Spy('my awesome spy'); | ||
``` | ||
The following broken fact... | ||
```ts | ||
spy.wasCalledWith(42, 'test', {attr1: [1, 2, new Date(2017, 1, 20)], attr2: 1336}); | ||
``` | ||
...would produce the following error output: | ||
``` | ||
```text | ||
Error: | ||
@@ -646,4 +743,5 @@ | ||
``` | ||
There you can see that the arguments of the fact (displayed above all others) does not | ||
match any of the call arguments on the 5 made calls. | ||
match any of the call arguments on the 5 made calls. | ||
@@ -660,2 +758,3 @@ For each call we display additional error information (the first found difference). | ||
different objects you could: | ||
```ts | ||
@@ -667,8 +766,12 @@ const callArgs = spy.getCallArguments(0/* for the 0'th call above*/); | ||
## Enforce-Order Mode | ||
You can opt-in to the enforce-order mode. Which might become the default in some | ||
future version but will need first further evaluation and will always stay configurable. | ||
```ts | ||
Spy.configure({ enforceOrder: true }); | ||
``` | ||
This mode enforces that the "facts" will be called in the correct order. | ||
```ts | ||
@@ -687,3 +790,5 @@ // success | ||
``` | ||
Be aware "facts" that you might need to get used to it, because the following would be valid, too. | ||
```ts | ||
@@ -700,4 +805,6 @@ // success | ||
``` | ||
Nevertheless, this mode should make your tests more readable and clear, because you can avoid | ||
checking the same things on and on again or resetting the spies in tests. Another example: | ||
```ts | ||
@@ -721,2 +828,30 @@ const mock_WS = Spy.mock(WS, 'fetchData', 'fetchFallback'); | ||
## Bun Support | ||
[`bun`](https://bun.sh/) includes an alternative fast runtime to NodeJS. | ||
In order to use this library with `bun test`, you need to add a `bunfig.toml` with | ||
a preload script: | ||
```toml | ||
[test] | ||
preload = "./bun-preload.ts" | ||
``` | ||
And the preload script should implement missing test APIs. At the time of writing that would be | ||
at least this: | ||
```ts | ||
// necessary until Bun has implemented them | ||
expect.addSnapshotSerializer = () => null; | ||
expect.getState = () => ({}) as any; | ||
``` | ||
**⚠️ Disclaimers:** | ||
- You cannot call `Spy.setup()` in a single place inside of the preload script as the `beforeEach` hook of | ||
will be differently scoped, when you want/need to support scoped mocks. However, you can call the `Spy.setup` | ||
again in the tests where you need scoped mocks and provide it a new `beforeEach` hook. | ||
- Since `expect.getState` is not correctly implemented, you will not be prevented correctly from instantiating mocks | ||
inside of test runs. | ||
[license-image]: https://img.shields.io/badge/license-MIT-blue.svg | ||
@@ -723,0 +858,0 @@ [license-url]: https://github.com/fdc-viktor-luft/spy4js/blob/master/LICENSE |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
118897
22
2590
816
0
1