Skip to content

Commit ff026c6

Browse files
committed
Merge branch 'moo/rerun-tests-after-failure' of github.com:mendix/native-widgets into moo/rerun-tests-after-failure
2 parents c215d49 + 58c5c4c commit ff026c6

24 files changed

+708
-600
lines changed

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/jsActions/mobile-resources-native/CHANGELOG.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
- We migrated from react-native-sound to react-native-track-player.
910
- We updated react-native-permissions to 5.4.2.
10-
1111
- We removed react-native-schedule-exact-alarm-permission since it's no longer required.
12-
1312
- Updated react-native from version 0.75.4 to 0.77.3.
1413
- We migrated from react-native-file-viewer to react-native-file-viewer-turbo for new architecture compatibility
1514
- File viewer now uses modal to display content

packages/jsActions/mobile-resources-native/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"react-native-image-picker": "7.2.3",
4040
"react-native-localize": "3.2.1",
4141
"react-native-permissions": "5.4.2",
42-
"react-native-sound": "0.11.0",
42+
"react-native-track-player": "4.1.2",
4343
"url-parse": "^1.4.7"
4444
},
4545
"devDependencies": {

packages/jsActions/mobile-resources-native/src/platform/PlaySound.ts

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// - the code between BEGIN USER CODE and END USER CODE
66
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
77
// Other code you write will be lost the next time you deploy the project.
8-
import Sound from "react-native-sound";
8+
import TrackPlayer, { State, Event } from "react-native-track-player";
99

1010
// BEGIN EXTRA CODE
1111
// END EXTRA CODE
@@ -19,7 +19,7 @@ import Sound from "react-native-sound";
1919
*/
2020
export async function PlaySound(audioFile?: mendix.lib.MxObject): Promise<void> {
2121
// BEGIN USER CODE
22-
// Documentation https://github.com/zmxv/react-native-sound
22+
// Documentation https://rntp.dev
2323

2424
if (!audioFile) {
2525
return Promise.reject(new Error("Input parameter 'Audio file' is required"));
@@ -34,19 +34,39 @@ export async function PlaySound(audioFile?: mendix.lib.MxObject): Promise<void>
3434
const changedDate = audioFile.get("changedDate") as number;
3535
const url = mx.data.getDocumentUrl(guid, changedDate);
3636

37-
const audio = new Sound(url, undefined, error => {
38-
if (error) {
39-
return Promise.reject(new Error(error));
37+
try {
38+
// Initialize the player if it hasn't been set up yet
39+
const state = await TrackPlayer.getPlaybackState();
40+
if (state.state === State.None) {
41+
await TrackPlayer.setupPlayer({
42+
maxCacheSize: 1024
43+
});
4044
}
4145

42-
audio.play(success => {
43-
audio.release();
44-
if (success) {
45-
return Promise.resolve();
46-
}
47-
return Promise.reject(new Error("Playback failed due to an audio encoding error"));
46+
await TrackPlayer.reset();
47+
await TrackPlayer.add({
48+
id: guid,
49+
url,
50+
title: `Audio ${guid}`,
51+
artist: "Mendix App"
4852
});
49-
});
53+
54+
await TrackPlayer.play();
55+
56+
return new Promise<void>((resolve, reject) => {
57+
const subscription = TrackPlayer.addEventListener(Event.PlaybackState, event => {
58+
if (event.state === State.Stopped || event.state === State.Ended) {
59+
subscription.remove();
60+
resolve();
61+
} else if (event.state === State.Error) {
62+
subscription.remove();
63+
reject(new Error(event.error.message || "Playback error"));
64+
}
65+
});
66+
});
67+
} catch (error) {
68+
return Promise.reject(new Error(`Failed to play audio: ${error}`));
69+
}
5070

5171
// END USER CODE
5272
}

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)