Skip to content

Commit 55c19f9

Browse files
author
Michael Kuczera
committed
implement basic testing suite before native build
1 parent fa6529f commit 55c19f9

10 files changed

+9808
-236
lines changed

.nvmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v18

babel.config.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
presets: ["module:metro-react-native-babel-preset"],
3+
};

eslint.config.mjs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import globals from "globals";
2+
import pluginJs from "@eslint/js";
3+
import tseslint from "typescript-eslint";
4+
5+
export default [
6+
{ files: ["**/*.{ts}"] },
7+
{ languageOptions: { globals: globals.browser } },
8+
pluginJs.configs.recommended,
9+
...tseslint.configs.recommended,
10+
{ ignores: ["build/*", "node_modules/*", "example/*"] },
11+
{
12+
rules: {
13+
"@typescript-eslint/no-require-imports": "off",
14+
},
15+
},
16+
];

jest.config.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module.exports = {
2+
preset: "ts-jest",
3+
testEnvironment: "node",
4+
transform: {
5+
"^.+\\.jsx?$": "babel-jest",
6+
"^.+\\.tsx?$": "ts-jest",
7+
},
8+
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
9+
modulePathIgnorePatterns: [
10+
"<rootDir>/example/node_modules",
11+
"<rootDir>/lib/",
12+
],
13+
transformIgnorePatterns: [
14+
"node_modules/(?!(react-native|my-project|react-native-button)/)",
15+
],
16+
setupFiles: ["./setupTests.js"],
17+
};

package.json

+21-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
"nativePackage": true,
99
"scripts": {
1010
"build": "tsc",
11-
"test": "echo \"Error: no test specified\" && exit 1"
11+
"typecheck": "tsc --noEmit",
12+
"test": "jest",
13+
"lint": "eslint \"**/*.{js,ts,tsx}\""
1214
},
1315
"keywords": [
1416
"react-native",
@@ -44,13 +46,29 @@
4446
}
4547
},
4648
"devDependencies": {
47-
"@babel/core": "^7.21.3",
49+
"@babel/core": "^7.25.2",
50+
"@babel/preset-env": "^7.25.3",
51+
"@babel/preset-react": "^7.24.7",
4852
"@babel/runtime": "^7.21.0",
53+
"@eslint/js": "^9.8.0",
54+
"@react-native/eslint-config": "^0.74.86",
55+
"@types/jest": "^29.5.12",
4956
"@types/node": "^18.15.3",
5057
"@types/react": "^18.0.28",
5158
"@types/react-native": "^0.71.3",
59+
"babel-jest": "^29.7.0",
60+
"eslint": "9.x",
61+
"eslint-config-prettier": "^9.1.0",
62+
"eslint-plugin-prettier": "^5.2.1",
63+
"globals": "^15.9.0",
64+
"jest": "^29.7.0",
65+
"metro-react-native-babel-preset": "^0.77.0",
5266
"prettier": "^2.8.5",
53-
"typescript": "^5.5.4"
67+
"react-native": "^0.74.4",
68+
"ts-jest": "^29.2.4",
69+
"tslib": "^2.6.3",
70+
"typescript": "^5.5.4",
71+
"typescript-eslint": "^8.0.0"
5472
},
5573
"packageManager": "[email protected]",
5674
"dependencies": {

setupTests.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
global.__DEV__ = true;
2+
global.__turboModuleProxy = null;
3+
4+
jest.mock("react-native/Libraries/BatchedBridge/NativeModules", () => ({
5+
RNHapticFeedback: {
6+
trigger: jest.fn(),
7+
},
8+
}));

src/__tests__/index.test.tsx

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { NativeModules } from "react-native";
2+
import { trigger } from "../index";
3+
import { HapticFeedbackTypes } from "../types";
4+
5+
jest.mock("../NativeHapticFeedback", () => ({
6+
default: {
7+
trigger: jest.fn(),
8+
},
9+
}));
10+
11+
const NativeHapticFeedbackMock = require("../NativeHapticFeedback").default;
12+
13+
describe("RNReactNativeHapticFeedback", () => {
14+
beforeAll(() => {
15+
global.__turboModuleProxy = null;
16+
NativeModules.RNHapticFeedback = {
17+
trigger: jest.fn(),
18+
};
19+
});
20+
21+
afterEach(() => {
22+
jest.clearAllMocks();
23+
});
24+
25+
it("should trigger haptic feedback with default options using NativeModules when turbo module is not used", () => {
26+
trigger(HapticFeedbackTypes.selection);
27+
28+
expect(NativeModules.RNHapticFeedback.trigger).toHaveBeenCalledWith(
29+
"selection",
30+
{
31+
enableVibrateFallback: false,
32+
ignoreAndroidSystemSettings: false,
33+
},
34+
);
35+
});
36+
37+
it("should trigger haptic feedback with turbo module when enabled", () => {
38+
global.__turboModuleProxy = true;
39+
40+
trigger(HapticFeedbackTypes.selection);
41+
42+
expect(NativeHapticFeedbackMock.trigger).toHaveBeenCalledWith("selection", {
43+
enableVibrateFallback: false,
44+
ignoreAndroidSystemSettings: false,
45+
});
46+
47+
global.__turboModuleProxy = null;
48+
});
49+
50+
it("should pass the correct options", () => {
51+
const options = {
52+
enableVibrateFallback: true,
53+
ignoreAndroidSystemSettings: true,
54+
};
55+
56+
trigger(HapticFeedbackTypes.selection, options);
57+
58+
expect(NativeModules.RNHapticFeedback.trigger).toHaveBeenCalledWith(
59+
"selection",
60+
options,
61+
);
62+
});
63+
64+
it("should handle the case when options is a boolean", () => {
65+
// @ts-expect-error - we're testing the case when options is a boolean for deprecated behavior
66+
trigger(HapticFeedbackTypes.selection, true);
67+
68+
expect(NativeModules.RNHapticFeedback.trigger).toHaveBeenCalledWith(
69+
"selection",
70+
{
71+
enableVibrateFallback: true,
72+
ignoreAndroidSystemSettings: false,
73+
},
74+
);
75+
});
76+
77+
it("should warn when haptic feedback module is not available", () => {
78+
delete NativeModules.RNHapticFeedback;
79+
80+
const warnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
81+
82+
trigger(HapticFeedbackTypes.selection);
83+
84+
expect(warnSpy).toHaveBeenCalledWith(
85+
"RNReactNativeHapticFeedback is not available",
86+
);
87+
88+
warnSpy.mockRestore();
89+
});
90+
});

src/index.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,21 @@ const defaultOptions = {
1212

1313
class RNReactNativeHapticFeedback {
1414
static trigger = (
15-
type: keyof typeof HapticFeedbackTypes | HapticFeedbackTypes = HapticFeedbackTypes.selection,
15+
type:
16+
| keyof typeof HapticFeedbackTypes
17+
| HapticFeedbackTypes = HapticFeedbackTypes.selection,
1618
options: HapticOptions = {},
1719
) => {
1820
const triggerOptions = createTriggerOptions(options);
19-
21+
2022
try {
2123
const isTurboModuleEnabled = global.__turboModuleProxy != null;
2224
const hapticFeedback = isTurboModuleEnabled
2325
? (require("./NativeHapticFeedback").default as Spec)
2426
: NativeModules.RNHapticFeedback;
2527

2628
hapticFeedback.trigger(type, triggerOptions);
27-
} catch (err) {
29+
} catch {
2830
console.warn("RNReactNativeHapticFeedback is not available");
2931
}
3032
};
@@ -44,4 +46,4 @@ const createTriggerOptions = (options: HapticOptions) => {
4446

4547
export const trigger = RNReactNativeHapticFeedback.trigger;
4648

47-
export default RNReactNativeHapticFeedback;
49+
export default RNReactNativeHapticFeedback;

tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"alwaysStrict": true,
1717
"strictFunctionTypes": true,
1818
"resolveJsonModule": true,
19-
"importHelpers": false,
19+
"importHelpers": true,
2020
"experimentalDecorators": true,
2121
"strictPropertyInitialization": false,
2222
"allowSyntheticDefaultImports": true,

0 commit comments

Comments
 (0)