Skip to content

Commit

Permalink
feat(core): Add Animated mocks
Browse files Browse the repository at this point in the history
  • Loading branch information
JoseLion committed Mar 10, 2024
1 parent 0651156 commit 7094ff2
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 33 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"compile": "tsc",
"lint": "eslint . --report-unused-disable-directives",
"release": "semantic-release",
"test": "NODE_ENV=test mocha"
"test": "RNTL_SKIP_AUTO_DETECT_FAKE_TIMERS=true NODE_ENV=test mocha"
},
"packageManager": "[email protected]",
"dependencies": {
Expand Down
20 changes: 13 additions & 7 deletions src/helpers/commons.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import path from "path";

/**
Expand All @@ -9,15 +8,12 @@ export function noop(): void {
}

/**
* Replaces a module with a given `exports` value or another module path.
* Replaces a Module with a given `exports` value or another module path.
*
* @param modulePath the path to the module
* @param other the exports to replace or another module path
* @param exports the exports to replace
*/
export function replace<T>(modulePath: string, other: T | string): void {
const exports = typeof other === "string"
? require(other) as T
: other;
export function replace<T>(modulePath: string, exports: T): void {
const id = resolveId(modulePath);

require.cache[id] = {
Expand All @@ -34,6 +30,16 @@ export function replace<T>(modulePath: string, other: T | string): void {
};
}

/**
* Replaces am ESModule with a given `exports` value or another module path.
*
* @param modulePath the path to the ESModule
* @param defaultExport the default export to replace
*/
export function replaceEsm<T>(modulePath: string, defaultExport: T): void {
replace(modulePath, { __esModule: true, default: defaultExport });
}

function resolveId(modulePath: string): string {
try {
return require.resolve(modulePath);
Expand Down
10 changes: 10 additions & 0 deletions src/lib/Animated/AnimatedMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Animated } from "react-native";

import { AnimatedValueMock } from "./AnimatedValueMock";
import { AnimatedValueXYMock } from "./AnimatedValueXY";

export const AnimatedMock: typeof Animated = {
...Animated,
Value: AnimatedValueMock,
ValueXY: AnimatedValueXYMock,
};
11 changes: 11 additions & 0 deletions src/lib/Animated/AnimatedValueMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Animated } from "react-native";

export class AnimatedValueMock extends Animated.Value {

public constructor(
value: number,
config: Animated.AnimatedConfig = { useNativeDriver: false },
) {
super(value, { ...config, useNativeDriver: false });
}
}
16 changes: 16 additions & 0 deletions src/lib/Animated/AnimatedValueXY.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Animated } from "react-native";

interface ValueXY {
x: number | Animated.AnimatedValue;
y: number | Animated.AnimatedValue;
}

export class AnimatedValueXYMock extends Animated.ValueXY {

public constructor (
value: ValueXY,
config: Animated.AnimatedConfig = { useNativeDriver: false },
) {
super(value, { ...config, useNativeDriver: false });
}
}
2 changes: 1 addition & 1 deletion src/lib/Core/NativeModules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const NativeModulesMock = {
},
PlatformConstants: {
getConstants() {
return {};
return { isTesting: true };
},
},
PushNotificationManager: {
Expand Down
6 changes: 4 additions & 2 deletions src/lib/coreMocks.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { replace } from "../helpers/commons";
import { noop, replace, replaceEsm } from "../helpers/commons";
import { mockNativeComponent } from "../helpers/mockNativeComponent";

import { NativeComponentRegistryMock } from "./Core/NativeComponentRegistry";
import { NativeModulesMock } from "./Core/NativeModules";
import { UIManagerMock } from "./Core/UIManager";
import { verifyComponentAttrEqMock } from "./Core/verifyComponentAttributeEquivalence";

Object.assign(global, { jest: { fn: () => noop } });

replace("react-native/Libraries/Core/InitializeCore", { });
replace("react-native/Libraries/Core/NativeExceptionsManager", { });
replace("react-native/Libraries/ReactNative/UIManager", UIManagerMock);
replace("react-native/Libraries/BatchedBridge/NativeModules", NativeModulesMock);
replace("react-native/Libraries/NativeComponent/NativeComponentRegistry", NativeComponentRegistryMock);
replace("react-native/Libraries/ReactNative/requireNativeComponent", { default: mockNativeComponent });
replaceEsm("react-native/Libraries/ReactNative/requireNativeComponent", mockNativeComponent);
replace("react-native/Libraries/Utilities/verifyComponentAttributeEquivalence", verifyComponentAttrEqMock);
replace(
"react-native/Libraries/ReactNative/RendererProxy",
Expand Down
8 changes: 5 additions & 3 deletions src/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import "./lib/babelRegister";
import "./lib/polyfills";
import "./lib/coreMocks";

import { replace } from "./helpers/commons";
import { replace, replaceEsm } from "./helpers/commons";
import { AnimatedMock } from "./lib/Animated/AnimatedMock";
import { AccessibilityInfoMock } from "./lib/Components/AccessibilityInfo";
import { ActivityIndicatorMock } from "./lib/Components/ActivityIndicator";
import { AppStateMock } from "./lib/Components/AppState";
Expand All @@ -22,13 +23,14 @@ replace("react-native/Libraries/Image/Image", ImageMock);
replace("react-native/Libraries/Text/Text", TextMock);
replace("react-native/Libraries/Components/TextInput/TextInput", TextInputMock);
replace("react-native/Libraries/Modal/Modal", ModalMock);
replace("react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo", { default: AccessibilityInfoMock });
replaceEsm("react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo", AccessibilityInfoMock);
replace("react-native/Libraries/Components/Clipboard/Clipboard", ClipboardMock);
replace("react-native/Libraries/Components/RefreshControl/RefreshControl", RefreshControlMock);
replace("react-native/Libraries/Components/ScrollView/ScrollView", ScrollViewMock);
replace("react-native/Libraries/Components/ActivityIndicator/ActivityIndicator", { default: ActivityIndicatorMock });
replaceEsm("react-native/Libraries/Components/ActivityIndicator/ActivityIndicator", ActivityIndicatorMock);
replace("react-native/Libraries/AppState/AppState", AppStateMock);
replace("react-native/Libraries/Linking/Linking", LinkingMock);
replace("react-native/Libraries/Vibration/Vibration", VibrationMock);
replace("react-native/Libraries/Components/View/View", ViewMock);
replace("react-native/Libraries/Components/View/ViewNativeComponent", ViewNativeComponentMock);
replaceEsm("react-native/Libraries/Animated/Animated", AnimatedMock);
80 changes: 61 additions & 19 deletions test/unit/register.test.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,68 @@
import "../../src/register";

import { expect } from "@assertive-ts/core";
import { render } from "@testing-library/react-native";
import { ActivityIndicator, Image, Modal, ScrollView, Text, TextInput, View } from "react-native";
import { render, waitFor, userEvent } from "@testing-library/react-native";
import { ReactElement, useCallback, useRef, useState } from "react";
import { ActivityIndicator, Animated, Button, Image, Modal, ScrollView, Text, TextInput, View } from "react-native";

function TestScreen(): ReactElement {

const [animated, setAnimated] = useState(false);

const enterLeft = useRef(new Animated.Value(100, { useNativeDriver: true })).current;
const movePoint = useRef(new Animated.ValueXY({ x: 0, y: 0 }, { useNativeDriver: true })).current;

const animateView = useCallback(() => {
const enterAnim = Animated.timing(enterLeft, {
duration: 100,
toValue: 0,
useNativeDriver: true,
});
const moveAnim = Animated.spring(movePoint, {
damping: 45,
mass: 4,
stiffness: 350,
toValue: { x: 50, y: 10 },
useNativeDriver: true,
});

Animated.parallel([enterAnim, moveAnim]).start(({ finished }) => {
setAnimated(finished);
});
}, []);

return (
<ScrollView>
<View>
<ActivityIndicator aria-label="Loading" animating={true} />
<Text>{"Hello world!"}</Text>
<TextInput placeholder="Say hello here..." value="Hello :)" />
<Image alt="Profile picture" />
<Modal visible={true}>
<Text>{"I'm on a modal"}</Text>
</Modal>
<Modal visible={false}>
<Text>{"foo"}</Text>
</Modal>
</View>
<Button title="Click Me!" onPress={animateView} />
<Animated.View style={{ marginLeft: enterLeft }}>
<Text>{`Animated view: ${animated}`}</Text>
</Animated.View>
</ScrollView>
);
}

describe("[Unit] register.test.ts", () => {
context("when main is called", () => {
it("mocks react native so it can render on Node.js", () => {
it("mocks react native so it can render on Node.js", async () => {
const {
getByText,
getByPlaceholderText,
getByDisplayValue,
getByLabelText,
} = render(
<ScrollView>
<View>
<ActivityIndicator aria-label="Loading" animating={true} />
<Text>{"Hello world!"}</Text>
<TextInput placeholder="Say hello here..." value="Hello :)" />
<Image alt="Profile picture" />
<Modal visible={true}>
<Text>{"I'm on a modal"}</Text>
</Modal>
<Modal visible={false}>
<Text>{"foo"}</Text>
</Modal>
</View>
</ScrollView>,
);
findByText,
} = render(<TestScreen />);

expect(getByLabelText("Loading")).toBePresent();
expect(getByText("Hello world!")).toBePresent();
Expand All @@ -36,6 +71,13 @@ describe("[Unit] register.test.ts", () => {
expect(getByLabelText("Profile picture")).toBePresent();
expect(getByText("I'm on a modal")).toBePresent();
expect(() => getByText("foo")).toThrowError();
expect(getByText("Animated view: false")).toBePresent();

const clickMeButton = await findByText("Click Me!");

await userEvent.press(clickMeButton);

await waitFor(() => getByText("Animated view: true"));
});
});
});

0 comments on commit 7094ff2

Please sign in to comment.