Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
9cec3ec
refactor: update Switch component tests to use React Testing Library
vadymv-mendix Aug 21, 2025
c2c4abf
test: wrap fireEvent calls in act for Switch component interactions
vadymv-mendix Aug 21, 2025
3aeaa57
chore: migrate Maps component tests to use React Testing Library
vadymv-mendix Aug 21, 2025
bc92b12
test: wrap async operations in act for Maps component rendering
vadymv-mendix Aug 21, 2025
628f011
test: replace getByTestId with screen.getByTestId in Switch component…
vadymv-mendix Aug 21, 2025
afb8202
test: remove jsdom environment declaration from Switch component tests
vadymv-mendix Aug 21, 2025
ab54144
test: replace waitFor with act in Image component tests to fix compon…
vadymv-mendix Aug 27, 2025
cae88ff
test: update Image and AppEvents component tests to use React Testing…
vadymv-mendix Aug 27, 2025
48748cd
test: update Maps component snapshot
vadymv-mendix Aug 27, 2025
6671299
Merge branch 'main' into moo/MOO-1924-convert-enzyme-tests-to-rn-test…
vadymv-mendix Aug 27, 2025
37d5b08
feat: remove the patch-package script as it is no longer needed for PNPM
vadymv-mendix Aug 27, 2025
0052ff7
chore: use recommended userEvent instead of fireEvent; update depende…
vadymv-mendix Aug 27, 2025
cbbb730
test: increase timeout for Feedback tests due to slow execution on Gi…
vadymv-mendix Aug 27, 2025
c5662f9
Merge branch 'main' into moo/MOO-1924-convert-enzyme-tests-to-rn-test…
vadymv-mendix Aug 28, 2025
390f695
refactor: rename unnecessary UNSAFE lifecycle methods
vadymv-mendix Aug 28, 2025
49fe8bd
chore: update @testing-library/react-native to version 13.3.3
vadymv-mendix Aug 28, 2025
6c82b31
Merge branch 'main' into moo/MOO-1924-convert-enzyme-tests-to-rn-test…
vadymv-mendix Aug 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"license": "Apache-2.0",
"scripts": {
"prepare": "npx husky install && pnpm -r run prepare",
"postinstall": "patch-package && pnpm -r run postinstall",
"postinstall": "pnpm -r run postinstall",
"reinstall": "pnpm store prune && git clean -dfx && find . -type dir -name node_modules | xargs rm -rf && pnpm install && pnpm run postinstall",
"prettier": "prettier --config \"./prettier.config.js\" --write \"**/*.{js,jsx,ts,tsx,scss,html,xml,yml,yaml}\"",
"format": "pretty-quick --staged --config \"./prettier.config.js\" --pattern \"**/{src,script,typings,test,**}/**/*.{js,jsx,ts,tsx,scss,html,xml,md,json}\"",
Expand All @@ -29,7 +29,6 @@
"version": "ts-node --project ./scripts/tsconfig.json ./scripts/release/BumpVersion.ts",
"validate-staged-widget-versions": "node scripts/validation/validate-versions-staged-files.js",
"setup-mobile": "pnpm setup-android && pnpm setup-ios",
"patch-package": "sh ./scripts/patch/patch-package.sh",
"build:widgets": "node ./scripts/widget/buildWidgets.js",
"test_widgets:maestro:ios": "bash maestro/run_maestro_widget_tests.sh ios",
"test_widgets:maestro:android": "bash maestro/run_maestro_widget_tests.sh android",
Expand All @@ -44,8 +43,7 @@
"@commitlint/cli": "^18.6.1",
"@commitlint/config-conventional": "^18.6.3",
"@react-native/babel-preset": "0.77.3",
"@testing-library/jest-native": "^5.4.3",
"@testing-library/react-native": "^12.9.0",
"@testing-library/react-native": "^13.3.3",
"@types/big.js": "^6.2.2",
"@types/concurrently": "^6.3.0",
"@types/enzyme": "^3.10.18",
Expand All @@ -65,7 +63,6 @@
"image-js": "^0.35.6",
"lint-staged": "^10.5.4",
"mendix-client": "^7.15.8",
"patch-package": "^8.0.0",
"pixelmatch": "^5.3.0",
"pngjs": "^6.0.0",
"pretty-quick": "^3.3.1",
Expand Down Expand Up @@ -94,6 +91,15 @@
"@types/react-native": "0.73.0",
"cheerio": "1.0.0-rc.12",
"typescript": "~5.8.0"
},
"patchedDependencies": {
"@mendix/pluggable-widgets-tools@10.21.1": "patches/@mendix+pluggable-widgets-tools+10.21.1.patch",
"@ptomasroos/react-native-multi-slider@1.0.0": "patches/@ptomasroos+react-native-multi-slider+1.0.0.patch",
"react-native-action-button@2.8.5": "patches/react-native-action-button+2.8.5.patch",
"react-native-camera@3.40.0": "patches/react-native-camera+3.40.0.patch",
"react-native-gesture-handler@2.24.0": "patches/react-native-gesture-handler+2.24.0.patch",
"react-native-slider@0.11.0": "patches/react-native-slider+0.11.0.patch",
"react-native-snap-carousel@3.9.1": "patches/react-native-snap-carousel+3.9.1.patch"
}
},
"packageManager": "pnpm@10.13.1"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { actionValue } from "@mendix/piw-utils-internal";
import { createElement } from "react";
import { AppStateStatus } from "react-native";

import { mount } from "enzyme";
import { render } from "@testing-library/react-native";

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

Expand Down Expand Up @@ -54,31 +53,31 @@ describe("AppEvents", () => {
});

it("does not render anything", () => {
const wrapper = mount(<AppEvents {...defaultProps} />);
expect(wrapper).toMatchObject({});
const { toJSON } = render(<AppEvents {...defaultProps} />);
expect(toJSON()).toBeNull();
});

describe("with on load action", () => {
it("executes the on load action", () => {
const onLoadAction = actionValue();
mount(<AppEvents {...defaultProps} onLoadAction={onLoadAction} />);
render(<AppEvents {...defaultProps} onLoadAction={onLoadAction} />);
expect(onLoadAction.execute).toHaveBeenCalledTimes(1);
});
});

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

expect(appStateChangeHandler).toBeDefined();
wrapper.unmount();
unmount();
expect(appStateChangeHandler).toBeUndefined();
});

it("executes the on resume action", () => {
const onResumeAction = actionValue();
mount(<AppEvents {...defaultProps} onResumeAction={onResumeAction} />);
render(<AppEvents {...defaultProps} onResumeAction={onResumeAction} />);

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

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

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

const onResumeAction = actionValue();
mount(<AppEvents {...defaultProps} onResumeAction={onResumeAction} onResumeTimeout={5} />);
render(<AppEvents {...defaultProps} onResumeAction={onResumeAction} onResumeTimeout={5} />);

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

expect(connectionChangeHandler).toBeDefined();
wrapper.unmount();
unmount();
expect(connectionChangeHandler).toBeUndefined();
});

it("executes the on online action", async () => {
const onOnlineAction = actionValue();
mount(<AppEvents {...defaultProps} onOnlineAction={onOnlineAction} />);
render(<AppEvents {...defaultProps} onOnlineAction={onOnlineAction} />);
await flushMicrotasksQueue();

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

const onOnlineAction = actionValue();
mount(<AppEvents {...defaultProps} onOnlineAction={onOnlineAction} onOnlineTimeout={5} />);
render(<AppEvents {...defaultProps} onOnlineAction={onOnlineAction} onOnlineTimeout={5} />);
await flushMicrotasksQueue();

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

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

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

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

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

it("does not execute the on timeout action after the component has been unmounted", () => {
const onTimeoutAction = actionValue();
const wrapper = mount(<AppEvents {...defaultProps} onTimeoutAction={onTimeoutAction} />);
const { unmount } = render(<AppEvents {...defaultProps} onTimeoutAction={onTimeoutAction} />);
jest.advanceTimersByTime(15000);
wrapper.unmount();
unmount();
jest.advanceTimersByTime(15000);
expect(onTimeoutAction.execute).toHaveBeenCalledTimes(0);
});

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

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

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

jest.advanceTimersByTime(30000);
expect(onTimeoutAction.execute).toHaveBeenCalledTimes(1);
wrapper.unmount();
unmount();
jest.advanceTimersByTime(30000);
expect(onTimeoutAction.execute).toHaveBeenCalledTimes(1);
});

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

jest.advanceTimersByTime(30000);
expect(onTimeoutAction.execute).not.toHaveBeenCalled();
Expand Down
8 changes: 4 additions & 4 deletions packages/pluggableWidgets/feedback-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
},
"dependencies": {
"@mendix/piw-native-utils-internal": "*",
"querystringify": "^2.1.1",
"react-native-dialog": "9.2.2",
"querystringify": "^2.2.0",
"react-native-dialog": "9.3.0",
"react-native-view-shot": "4.0.3"
},
"devDependencies": {
"@mendix/piw-utils-internal": "1.0.0",
"@mendix/pluggable-widgets-tools": "*",
"@types/querystringify": "^2.0.0",
"@types/react-native-dialog": "^5.5.0"
"@types/querystringify": "^2.0.2",
"@types/react-native-dialog": "^5.6.3"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export class Feedback extends Component<FeedbackProps<FeedbackStyle>, State> {
componentDidMount() {
Dimensions.addEventListener("change", this.updateDeviceHeight);
}

componentDidUpdate(_: Readonly<FeedbackProps<FeedbackStyle>>, prevState: Readonly<State>) {
if (
["todo", "inprogress"].includes(prevState.status) &&
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FeedbackStyle } from "../ui/styles";
import { fireEvent, render, waitFor, cleanup } from "@testing-library/react-native";
import { render, cleanup, userEvent } from "@testing-library/react-native";
import { createElement } from "react";
import { FeedbackProps } from "../../typings/FeedbackProps";
import { Feedback } from "../Feedback";
Expand Down Expand Up @@ -52,12 +52,15 @@ describe("Feedback", () => {

it("should call the api when sending", async () => {
const feedbackMsg = "unittest";
const user = userEvent.setup();
const component = render(<Feedback {...defaultProps} />);
fireEvent.press(component.getByTestId("feedback-test$button"));
await waitFor(() => {
fireEvent.changeText(component.getByTestId("feedback-test$input"), feedbackMsg);
});
fireEvent.press(component.getByTestId("feedback-test$send"));

await user.press(component.getByTestId("feedback-test$button"));

await user.type(component.getByTestId("feedback-test$input"), feedbackMsg);

await user.press(component.getByTestId("feedback-test$send"));

expect(fetch).toHaveBeenCalledWith(
"https://feedback-api.mendix.com/rest/v3/feedbackapi/projects/sprintr-app-id/issues",
{
Expand All @@ -68,5 +71,5 @@ describe("Feedback", () => {
referrer: "no-referrer"
}
);
});
}, 8000); // increased timeout due to slow test execution on Github
});
Loading
Loading