Skip to content

Commit 45b6821

Browse files
authored
feat(support): Vitest plugin (#29)
* feat(support): Vitest plugin * Document usage with Vitest * Fix local tests env and plugin * Fix native mocks on Vitest * Add build to CI workflow
1 parent a1b6162 commit 45b6821

File tree

21 files changed

+247
-113
lines changed

21 files changed

+247
-113
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ jobs:
2424
node-version: ${{ matrix.nodejs }}
2525
cache: yarn
2626
- run: yarn install --immutable
27+
- run: yarn build
2728
- run: yarn compile
2829
- run: yarn lint
2930
- run: yarn test

README.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,28 @@ import "react-native-testing-mocks/register";
7272
// ...rest of your setup code
7373
```
7474

75-
### Mocha.js Example
75+
### With Vitest
7676

77-
Some frameworks also provide mechanisms to load setup modules. In Mocha.js, you can use the `--require` CLI option:
77+
Ideally, Vitest should be able to replace Babel when it comes to resolving and transforming the React Native code. However, so much more goes on in [@react-native/babel-preset](https://www.npmjs.com/package/@react-native/babel-preset) that replacing this module with Vite is not only a hard task but also increases the risk of something going wrong. So, until React Native delivers their code in a more conventional format (CommonJS/ESM), is my opinion that it's safer to keep using Babel to transform React Native's code with Vitest.
78+
79+
That being said, this package also provides a Vite Plugin you can add to your `vitest.config.ts` configuration file:
80+
81+
```ts
82+
import { reactNativePlugin } from "react-native-testing-mocks/vitest";
83+
import { defineConfig } from "vitest/config";
84+
85+
export default defineConfig({
86+
plugins: [reactNativePlugin()],
87+
test: {
88+
include: ["test/**/*.test.ts?(x)"],
89+
setupFiles: "./test/setup.ts",
90+
},
91+
});
92+
```
93+
94+
### With Mocha
95+
96+
Some frameworks like Mocha provide a mechanism to require modules during the Node.js execution. For instance, you can use the `--require` CLI option:
7897

7998
```bash
8099
mocha --require react-native-testing-mocks/register
@@ -91,9 +110,9 @@ Or you can add it to the `.mocharc.json` file:
91110
"require": [
92111
"ts-node/register",
93112
"react-native-testing-mocks/register",
94-
"test/hooks.ts"
113+
"./test/setup.ts"
95114
],
96-
"spec": ["test/**/*.test.*"]
115+
"spec": ["test/**/*.test.ts?(x)"]
97116
}
98117
```
99118

package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,25 @@
3232
"types": "./dist/register.d.ts",
3333
"default": "./dist/register.js"
3434
},
35+
"./vitest": {
36+
"import": "./dist/vitest.js",
37+
"require": "./dist/vitest.cjs",
38+
"types": "./dist/vitest/plugin.d.ts",
39+
"default": "./dist/vitest.js"
40+
},
41+
"./vitest/env": {
42+
"import": "./dist/vitestEnv.js",
43+
"require": "./dist/vitestEnv.cjs",
44+
"types": "./dist/vitest/env.d.ts",
45+
"default": "./dist/vitestEnv.js"
46+
},
3547
"./package.json": "./package.json"
3648
},
3749
"files": [
3850
"./dist",
3951
"./src/",
52+
"./register.d.ts",
53+
"./vitest.d.ts",
4054
"./package.json"
4155
],
4256
"engines": {

register.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./dist/register";

src/helpers/commons.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import { createRequire } from "module";
12
import path from "path";
23

34
type ExportsLike = object | { default?: unknown; };
45

6+
const require = createRequire(import.meta.url);
7+
58
/**
69
* A simple no-operation function
710
*/

src/helpers/mockComponent.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import {
77
createElement,
88
} from "react";
99

10-
import type { ScrollViewMethods } from "../lib/Components/ScrollView";
11-
import type { TextInputMethods } from "../lib/Components/TextInput";
10+
import type { ScrollViewMethods, TextInputMethods } from "./nativeMethodsMock";
1211
import type { NativeMethods } from "react-native";
1312

1413
export type AllNativeMethods = NativeMethods | ScrollViewMethods | TextInputMethods;

src/helpers/nativeMethodsMock.ts

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
import { noop } from "./commons";
22

3-
import type { NativeMethods } from "react-native";
3+
import type { ElementRef } from "react";
4+
import type { HostComponent, Image, NativeMethods, ScrollView, TextInput, View } from "react-native";
5+
6+
export type ImageMethods = Partial<typeof Image>;
7+
8+
export type ScrollViewMethods = NativeMethods | ScrollView & {
9+
getInnerViewRef: () => ElementRef<typeof View> | null;
10+
getNativeScrollRef: () => ElementRef<HostComponent<unknown>> | null;
11+
};
12+
13+
export type TextInputMethods = NativeMethods | TextInput & {
14+
getNativeRef: () => ElementRef<HostComponent<unknown>> | undefined;
15+
};
416

517
export const nativeMethodsMock: NativeMethods = {
618
blur: noop,
@@ -11,3 +23,68 @@ export const nativeMethodsMock: NativeMethods = {
1123
refs: { },
1224
setNativeProps: noop,
1325
};
26+
27+
export const imageMethodsMock: ImageMethods = {
28+
getSize: noop,
29+
getSizeWithHeaders: noop,
30+
prefetch: () => Promise.resolve(false),
31+
prefetchWithMetadata: () => Promise.resolve(false),
32+
queryCache: () => Promise.resolve({ }),
33+
resolveAssetSource: () => ({
34+
height: 0,
35+
scale: 0,
36+
uri: "",
37+
width: 0,
38+
}),
39+
};
40+
41+
export const scrollViewMethodsMock: ScrollViewMethods = {
42+
...nativeMethodsMock,
43+
flashScrollIndicators: noop,
44+
getInnerViewNode: noop,
45+
getInnerViewRef: () => null,
46+
getNativeScrollRef: () => null,
47+
getScrollResponder: () => ({
48+
addListenerOn: noop,
49+
componentWillMount: noop,
50+
scrollResponderGetScrollableNode: noop,
51+
scrollResponderHandleMomentumScrollBegin: noop,
52+
scrollResponderHandleMomentumScrollEnd: noop,
53+
scrollResponderHandleResponderGrant: noop,
54+
scrollResponderHandleResponderReject: noop,
55+
scrollResponderHandleResponderRelease: noop,
56+
scrollResponderHandleScroll: noop,
57+
scrollResponderHandleScrollBeginDrag: noop,
58+
scrollResponderHandleScrollEndDrag: noop,
59+
scrollResponderHandleScrollShouldSetResponder: () => false,
60+
scrollResponderHandleStartShouldSetResponder: () => false,
61+
scrollResponderHandleStartShouldSetResponderCapture: () => false,
62+
scrollResponderHandleTerminationRequest: () => false,
63+
scrollResponderHandleTouchEnd: noop,
64+
scrollResponderHandleTouchMove: noop,
65+
scrollResponderHandleTouchStart: noop,
66+
scrollResponderInputMeasureAndScrollToKeyboard: noop,
67+
scrollResponderIsAnimating: () => false,
68+
scrollResponderKeyboardDidHide: noop,
69+
scrollResponderKeyboardDidShow: noop,
70+
scrollResponderKeyboardWillHide: noop,
71+
scrollResponderKeyboardWillShow: noop,
72+
scrollResponderScrollNativeHandleToKeyboard: noop,
73+
scrollResponderScrollTo: noop,
74+
scrollResponderTextInputFocusError: noop,
75+
scrollResponderZoomTo: noop,
76+
}),
77+
getScrollableNode: noop,
78+
scrollResponderScrollNativeHandleToKeyboard: noop,
79+
scrollResponderZoomTo: noop,
80+
scrollTo: noop,
81+
scrollToEnd: noop,
82+
};
83+
84+
export const textInputMethodsMock: TextInputMethods = {
85+
...nativeMethodsMock,
86+
clear: noop,
87+
getNativeRef: () => undefined,
88+
isFocused: () => false,
89+
setSelection: noop,
90+
};

src/lib/Components/Image.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,10 @@
11
import { Image } from "react-native";
22

3-
import { noop } from "../../helpers/commons";
43
import { mockComponent } from "../../helpers/mockComponent";
4+
import { imageMethodsMock } from "../../helpers/nativeMethodsMock";
55

66
import type { ComponentClass } from "react";
77

8-
export type ImageMethods = Partial<typeof Image>;
9-
10-
export const imageMethodsMock: ImageMethods = {
11-
getSize: noop,
12-
getSizeWithHeaders: noop,
13-
prefetch: () => Promise.resolve(false),
14-
prefetchWithMetadata: () => Promise.resolve(false),
15-
queryCache: () => Promise.resolve({ }),
16-
resolveAssetSource: () => ({
17-
height: 0,
18-
scale: 0,
19-
uri: "",
20-
width: 0,
21-
}),
22-
};
23-
248
const Mock = mockComponent(Image as ComponentClass);
259

2610
export const ImageMock = Object.assign(Mock, imageMethodsMock);

src/lib/Components/ScrollView.ts

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,8 @@
1-
/* eslint-disable sort-keys */
2-
import { type ElementRef, type PropsWithChildren, type ReactNode, createElement } from "react";
3-
import { type HostComponent, type NativeMethods, ScrollView, View, requireNativeComponent } from "react-native";
1+
import { type PropsWithChildren, type ReactNode, createElement } from "react";
2+
import { ScrollView, View, requireNativeComponent } from "react-native";
43

5-
import { noop } from "../../helpers/commons";
64
import { mockComponent } from "../../helpers/mockComponent";
7-
import { nativeMethodsMock } from "../../helpers/nativeMethodsMock";
8-
9-
export type ScrollViewMethods = NativeMethods | ScrollView & {
10-
getInnerViewRef: () => ElementRef<typeof View> | null;
11-
getNativeScrollRef: () => ElementRef<HostComponent<unknown>> | null;
12-
};
13-
14-
export const scrollViewMethodsMock: ScrollViewMethods = {
15-
...nativeMethodsMock,
16-
getScrollResponder: () => ({
17-
addListenerOn: noop,
18-
componentWillMount: noop,
19-
scrollResponderGetScrollableNode: noop,
20-
scrollResponderHandleMomentumScrollBegin: noop,
21-
scrollResponderHandleMomentumScrollEnd: noop,
22-
scrollResponderHandleResponderGrant: noop,
23-
scrollResponderHandleResponderReject: noop,
24-
scrollResponderHandleResponderRelease: noop,
25-
scrollResponderHandleScroll: noop,
26-
scrollResponderHandleScrollBeginDrag: noop,
27-
scrollResponderHandleScrollEndDrag: noop,
28-
scrollResponderHandleScrollShouldSetResponder: () => false,
29-
scrollResponderHandleStartShouldSetResponder: () => false,
30-
scrollResponderHandleStartShouldSetResponderCapture: () => false,
31-
scrollResponderHandleTerminationRequest: () => false,
32-
scrollResponderHandleTouchEnd: noop,
33-
scrollResponderHandleTouchMove: noop,
34-
scrollResponderHandleTouchStart: noop,
35-
scrollResponderInputMeasureAndScrollToKeyboard: noop,
36-
scrollResponderIsAnimating: () => false,
37-
scrollResponderKeyboardDidHide: noop,
38-
scrollResponderKeyboardDidShow: noop,
39-
scrollResponderKeyboardWillHide: noop,
40-
scrollResponderKeyboardWillShow: noop,
41-
scrollResponderScrollNativeHandleToKeyboard: noop,
42-
scrollResponderScrollTo: noop,
43-
scrollResponderTextInputFocusError: noop,
44-
scrollResponderZoomTo: noop,
45-
}),
46-
getScrollableNode: noop,
47-
getInnerViewNode: noop,
48-
getInnerViewRef: () => null,
49-
getNativeScrollRef: () => null,
50-
scrollTo: noop,
51-
scrollToEnd: noop,
52-
flashScrollIndicators: noop,
53-
scrollResponderZoomTo: noop,
54-
scrollResponderScrollNativeHandleToKeyboard: noop,
55-
56-
};
5+
import { scrollViewMethodsMock } from "../../helpers/nativeMethodsMock";
576

587
const RCTScrollView = requireNativeComponent("RCTScrollView");
598
const BaseMock = mockComponent(ScrollView, scrollViewMethodsMock);

src/lib/Components/TextInput.ts

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,6 @@
1-
import { type HostComponent, type NativeMethods, TextInput } from "react-native";
1+
import { TextInput } from "react-native";
22

3-
import { noop } from "../../helpers/commons";
43
import { mockComponent } from "../../helpers/mockComponent";
5-
import { nativeMethodsMock } from "../../helpers/nativeMethodsMock";
6-
7-
import type { ElementRef } from "react";
8-
9-
export type TextInputMethods = NativeMethods | TextInput & {
10-
getNativeRef: () => ElementRef<HostComponent<unknown>> | undefined;
11-
};
12-
13-
export const textInputMethodsMock: TextInputMethods = {
14-
...nativeMethodsMock,
15-
clear: noop,
16-
getNativeRef: () => undefined,
17-
isFocused: () => false,
18-
setSelection: noop,
19-
};
4+
import { textInputMethodsMock } from "../../helpers/nativeMethodsMock";
205

216
export const TextInputMock = mockComponent(TextInput, textInputMethodsMock);

0 commit comments

Comments
 (0)