Skip to content

Commit 3036273

Browse files
[MOO-1924] convert enzyme tests to RN testing library (#315)
2 parents c89e59c + 6c82b31 commit 3036273

21 files changed

Lines changed: 654 additions & 574 deletions

package.json

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"license": "Apache-2.0",
1010
"scripts": {
1111
"prepare": "npx husky install && pnpm -r run prepare",
12-
"postinstall": "patch-package && pnpm -r run postinstall",
12+
"postinstall": "pnpm -r run postinstall",
1313
"reinstall": "pnpm store prune && git clean -dfx && find . -type dir -name node_modules | xargs rm -rf && pnpm install && pnpm run postinstall",
1414
"prettier": "prettier --config \"./prettier.config.js\" --write \"**/*.{js,jsx,ts,tsx,scss,html,xml,yml,yaml}\"",
1515
"format": "pretty-quick --staged --config \"./prettier.config.js\" --pattern \"**/{src,script,typings,test,**}/**/*.{js,jsx,ts,tsx,scss,html,xml,md,json}\"",
@@ -29,7 +29,6 @@
2929
"version": "ts-node --project ./scripts/tsconfig.json ./scripts/release/BumpVersion.ts",
3030
"validate-staged-widget-versions": "node scripts/validation/validate-versions-staged-files.js",
3131
"setup-mobile": "pnpm setup-android && pnpm setup-ios",
32-
"patch-package": "sh ./scripts/patch/patch-package.sh",
3332
"build:widgets": "node ./scripts/widget/buildWidgets.js",
3433
"test_widgets:maestro:ios": "bash maestro/run_maestro_widget_tests.sh ios",
3534
"test_widgets:maestro:android": "bash maestro/run_maestro_widget_tests.sh android",
@@ -44,8 +43,7 @@
4443
"@commitlint/cli": "^18.6.1",
4544
"@commitlint/config-conventional": "^18.6.3",
4645
"@react-native/babel-preset": "0.77.3",
47-
"@testing-library/jest-native": "^5.4.3",
48-
"@testing-library/react-native": "^12.9.0",
46+
"@testing-library/react-native": "^13.3.3",
4947
"@types/big.js": "^6.2.2",
5048
"@types/concurrently": "^6.3.0",
5149
"@types/enzyme": "^3.10.18",
@@ -65,7 +63,6 @@
6563
"image-js": "^0.35.6",
6664
"lint-staged": "^10.5.4",
6765
"mendix-client": "^7.15.8",
68-
"patch-package": "^8.0.0",
6966
"pixelmatch": "^5.3.0",
7067
"pngjs": "^6.0.0",
7168
"pretty-quick": "^3.3.1",
@@ -94,6 +91,15 @@
9491
"@types/react-native": "0.73.0",
9592
"cheerio": "1.0.0-rc.12",
9693
"typescript": "~5.8.0"
94+
},
95+
"patchedDependencies": {
96+
"@mendix/pluggable-widgets-tools@10.21.1": "patches/@mendix+pluggable-widgets-tools+10.21.1.patch",
97+
"@ptomasroos/react-native-multi-slider@1.0.0": "patches/@ptomasroos+react-native-multi-slider+1.0.0.patch",
98+
"react-native-action-button@2.8.5": "patches/react-native-action-button+2.8.5.patch",
99+
"react-native-camera@3.40.0": "patches/react-native-camera+3.40.0.patch",
100+
"react-native-gesture-handler@2.24.0": "patches/react-native-gesture-handler+2.24.0.patch",
101+
"react-native-slider@0.11.0": "patches/react-native-slider+0.11.0.patch",
102+
"react-native-snap-carousel@3.9.1": "patches/react-native-snap-carousel+3.9.1.patch"
97103
}
98104
},
99105
"packageManager": "pnpm@10.13.1"

packages/pluggableWidgets/app-events-native/src/__tests__/AppEvents.spec.tsx

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { actionValue } from "@mendix/piw-utils-internal";
22
import { createElement } from "react";
33
import { AppStateStatus } from "react-native";
4-
5-
import { mount } from "enzyme";
4+
import { render } from "@testing-library/react-native";
65

76
import { AppEvents, Props } from "../AppEvents";
87

@@ -54,31 +53,31 @@ describe("AppEvents", () => {
5453
});
5554

5655
it("does not render anything", () => {
57-
const wrapper = mount(<AppEvents {...defaultProps} />);
58-
expect(wrapper).toMatchObject({});
56+
const { toJSON } = render(<AppEvents {...defaultProps} />);
57+
expect(toJSON()).toBeNull();
5958
});
6059

6160
describe("with on load action", () => {
6261
it("executes the on load action", () => {
6362
const onLoadAction = actionValue();
64-
mount(<AppEvents {...defaultProps} onLoadAction={onLoadAction} />);
63+
render(<AppEvents {...defaultProps} onLoadAction={onLoadAction} />);
6564
expect(onLoadAction.execute).toHaveBeenCalledTimes(1);
6665
});
6766
});
6867

6968
describe("with on resume action", () => {
7069
it("registers and unregisters an event listener", () => {
7170
const onResumeAction = actionValue();
72-
const wrapper = mount(<AppEvents {...defaultProps} onResumeAction={onResumeAction} />);
71+
const { unmount } = render(<AppEvents {...defaultProps} onResumeAction={onResumeAction} />);
7372

7473
expect(appStateChangeHandler).toBeDefined();
75-
wrapper.unmount();
74+
unmount();
7675
expect(appStateChangeHandler).toBeUndefined();
7776
});
7877

7978
it("executes the on resume action", () => {
8079
const onResumeAction = actionValue();
81-
mount(<AppEvents {...defaultProps} onResumeAction={onResumeAction} />);
80+
render(<AppEvents {...defaultProps} onResumeAction={onResumeAction} />);
8281

8382
appStateChangeHandler!("background");
8483
appStateChangeHandler!("active");
@@ -87,7 +86,7 @@ describe("AppEvents", () => {
8786

8887
it("does not execute the on resume action when the app state hasn't changed", () => {
8988
const onResumeAction = actionValue();
90-
mount(<AppEvents {...defaultProps} onResumeAction={onResumeAction} />);
89+
render(<AppEvents {...defaultProps} onResumeAction={onResumeAction} />);
9190

9291
appStateChangeHandler!("active");
9392
appStateChangeHandler!("active");
@@ -98,7 +97,7 @@ describe("AppEvents", () => {
9897
const dateNowSpy = jest.spyOn(Date, "now").mockReturnValue(0);
9998

10099
const onResumeAction = actionValue();
101-
mount(<AppEvents {...defaultProps} onResumeAction={onResumeAction} onResumeTimeout={5} />);
100+
render(<AppEvents {...defaultProps} onResumeAction={onResumeAction} onResumeTimeout={5} />);
102101

103102
dateNowSpy.mockReturnValue(4000);
104103
appStateChangeHandler!("background");
@@ -117,17 +116,17 @@ describe("AppEvents", () => {
117116
describe("with on online action", () => {
118117
it("registers and unregisters an event listener", async () => {
119118
const onOnlineAction = actionValue();
120-
const wrapper = mount(<AppEvents {...defaultProps} onOnlineAction={onOnlineAction} />);
119+
const { unmount } = render(<AppEvents {...defaultProps} onOnlineAction={onOnlineAction} />);
121120
await flushMicrotasksQueue();
122121

123122
expect(connectionChangeHandler).toBeDefined();
124-
wrapper.unmount();
123+
unmount();
125124
expect(connectionChangeHandler).toBeUndefined();
126125
});
127126

128127
it("executes the on online action", async () => {
129128
const onOnlineAction = actionValue();
130-
mount(<AppEvents {...defaultProps} onOnlineAction={onOnlineAction} />);
129+
render(<AppEvents {...defaultProps} onOnlineAction={onOnlineAction} />);
131130
await flushMicrotasksQueue();
132131

133132
connectionChangeHandler!({ isConnected: false });
@@ -139,7 +138,7 @@ describe("AppEvents", () => {
139138
const dateNowSpy = jest.spyOn(Date, "now").mockReturnValue(0);
140139

141140
const onOnlineAction = actionValue();
142-
mount(<AppEvents {...defaultProps} onOnlineAction={onOnlineAction} onOnlineTimeout={5} />);
141+
render(<AppEvents {...defaultProps} onOnlineAction={onOnlineAction} onOnlineTimeout={5} />);
143142
await flushMicrotasksQueue();
144143

145144
dateNowSpy.mockReturnValue(4000);
@@ -157,7 +156,7 @@ describe("AppEvents", () => {
157156

158157
it("does not execute the on online action if the connection state didn't change", async () => {
159158
const onOnlineAction = actionValue();
160-
mount(<AppEvents {...defaultProps} onOnlineAction={onOnlineAction} />);
159+
render(<AppEvents {...defaultProps} onOnlineAction={onOnlineAction} />);
161160
await flushMicrotasksQueue();
162161

163162
connectionChangeHandler!({ isConnected: true });
@@ -178,7 +177,7 @@ describe("AppEvents", () => {
178177

179178
it("executes the on timeout action once after the timeout has passed", () => {
180179
const onTimeoutAction = actionValue();
181-
mount(<AppEvents {...defaultProps} onTimeoutAction={onTimeoutAction} />);
180+
render(<AppEvents {...defaultProps} onTimeoutAction={onTimeoutAction} />);
182181

183182
expect(onTimeoutAction.execute).toHaveBeenCalledTimes(0);
184183
jest.advanceTimersByTime(30000);
@@ -189,16 +188,16 @@ describe("AppEvents", () => {
189188

190189
it("does not execute the on timeout action after the component has been unmounted", () => {
191190
const onTimeoutAction = actionValue();
192-
const wrapper = mount(<AppEvents {...defaultProps} onTimeoutAction={onTimeoutAction} />);
191+
const { unmount } = render(<AppEvents {...defaultProps} onTimeoutAction={onTimeoutAction} />);
193192
jest.advanceTimersByTime(15000);
194-
wrapper.unmount();
193+
unmount();
195194
jest.advanceTimersByTime(15000);
196195
expect(onTimeoutAction.execute).toHaveBeenCalledTimes(0);
197196
});
198197

199198
it("executes the interval on timeout action after every interval", () => {
200199
const onTimeoutAction = actionValue();
201-
mount(<AppEvents {...defaultProps} onTimeoutAction={onTimeoutAction} timerType={"interval"} />);
200+
render(<AppEvents {...defaultProps} onTimeoutAction={onTimeoutAction} timerType={"interval"} />);
202201

203202
expect(onTimeoutAction.execute).toHaveBeenCalledTimes(0);
204203
jest.advanceTimersByTime(30000);
@@ -209,20 +208,20 @@ describe("AppEvents", () => {
209208

210209
it("does not execute the interval on timeout action after the component has been unmounted", () => {
211210
const onTimeoutAction = actionValue();
212-
const wrapper = mount(
211+
const { unmount } = render(
213212
<AppEvents {...defaultProps} onTimeoutAction={onTimeoutAction} timerType={"interval"} />
214213
);
215214

216215
jest.advanceTimersByTime(30000);
217216
expect(onTimeoutAction.execute).toHaveBeenCalledTimes(1);
218-
wrapper.unmount();
217+
unmount();
219218
jest.advanceTimersByTime(30000);
220219
expect(onTimeoutAction.execute).toHaveBeenCalledTimes(1);
221220
});
222221

223222
it("does not execute the interval on timeout action when it is already executing", () => {
224223
const onTimeoutAction = actionValue(true, true);
225-
mount(<AppEvents {...defaultProps} onTimeoutAction={onTimeoutAction} timerType={"interval"} />);
224+
render(<AppEvents {...defaultProps} onTimeoutAction={onTimeoutAction} timerType={"interval"} />);
226225

227226
jest.advanceTimersByTime(30000);
228227
expect(onTimeoutAction.execute).not.toHaveBeenCalled();

packages/pluggableWidgets/feedback-native/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@
2020
},
2121
"dependencies": {
2222
"@mendix/piw-native-utils-internal": "*",
23-
"querystringify": "^2.1.1",
24-
"react-native-dialog": "9.2.2",
23+
"querystringify": "^2.2.0",
24+
"react-native-dialog": "9.3.0",
2525
"react-native-view-shot": "4.0.3"
2626
},
2727
"devDependencies": {
2828
"@mendix/piw-utils-internal": "1.0.0",
2929
"@mendix/pluggable-widgets-tools": "*",
30-
"@types/querystringify": "^2.0.0",
31-
"@types/react-native-dialog": "^5.5.0"
30+
"@types/querystringify": "^2.0.2",
31+
"@types/react-native-dialog": "^5.6.3"
3232
}
3333
}

packages/pluggableWidgets/feedback-native/src/Feedback.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export class Feedback extends Component<FeedbackProps<FeedbackStyle>, State> {
6666
componentDidMount() {
6767
Dimensions.addEventListener("change", this.updateDeviceHeight);
6868
}
69+
6970
componentDidUpdate(_: Readonly<FeedbackProps<FeedbackStyle>>, prevState: Readonly<State>) {
7071
if (
7172
["todo", "inprogress"].includes(prevState.status) &&

packages/pluggableWidgets/feedback-native/src/__tests__/Feedback.spec.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { FeedbackStyle } from "../ui/styles";
2-
import { fireEvent, render, waitFor, cleanup } from "@testing-library/react-native";
2+
import { render, cleanup, userEvent } from "@testing-library/react-native";
33
import { createElement } from "react";
44
import { FeedbackProps } from "../../typings/FeedbackProps";
55
import { Feedback } from "../Feedback";
@@ -52,12 +52,15 @@ describe("Feedback", () => {
5252

5353
it("should call the api when sending", async () => {
5454
const feedbackMsg = "unittest";
55+
const user = userEvent.setup();
5556
const component = render(<Feedback {...defaultProps} />);
56-
fireEvent.press(component.getByTestId("feedback-test$button"));
57-
await waitFor(() => {
58-
fireEvent.changeText(component.getByTestId("feedback-test$input"), feedbackMsg);
59-
});
60-
fireEvent.press(component.getByTestId("feedback-test$send"));
57+
58+
await user.press(component.getByTestId("feedback-test$button"));
59+
60+
await user.type(component.getByTestId("feedback-test$input"), feedbackMsg);
61+
62+
await user.press(component.getByTestId("feedback-test$send"));
63+
6164
expect(fetch).toHaveBeenCalledWith(
6265
"https://feedback-api.mendix.com/rest/v3/feedbackapi/projects/sprintr-app-id/issues",
6366
{
@@ -68,5 +71,5 @@ describe("Feedback", () => {
6871
referrer: "no-referrer"
6972
}
7073
);
71-
});
74+
}, 8000); // increased timeout due to slow test execution on Github
7275
});

0 commit comments

Comments
 (0)