Skip to content

Instantly share code, notes, and snippets.

@Doko-Demo-Doa
Created February 15, 2024 16:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Doko-Demo-Doa/74995f3701e1e1db2237ad033bdc0b37 to your computer and use it in GitHub Desktop.
Save Doko-Demo-Doa/74995f3701e1e1db2237ad033bdc0b37 to your computer and use it in GitHub Desktop.
Sample Jest setup file with mocking
// https://github.com/react-navigation/react-navigation/issues/9727
jest.useFakeTimers();
import "@testing-library/jest-native/extend-expect";
import "react-native-gesture-handler/jestSetup";
import type {
UnistylesBreakpoints,
UnistylesThemes,
} from "react-native-unistyles";
import type {
ColorSchemeName,
UnistylesBridge,
} from "react-native-unistyles/lib/typescript/src/types";
jest.mock("react-native-reanimated", () =>
require("react-native-reanimated/mock"),
);
// Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing
jest.mock("react-native/Libraries/Animated/NativeAnimatedHelper");
// react-native-theme-switch-animation
jest.mock("react-native-theme-switch-animation", () => jest.fn());
jest.mock("react-native-bootsplash", () => {
return {
hide: jest.fn().mockResolvedValue(undefined),
isVisible: jest.fn().mockResolvedValue(false),
useHideAnimation: jest.fn().mockReturnValue({
container: {},
logo: { source: 0 },
brand: { source: 0 },
}),
};
});
// Ignore yellow logbox
console = {
...console,
log: jest.fn(),
warn: jest.fn(),
};
jest.mock("react-native/Libraries/EventEmitter/NativeEventEmitter");
// Unistyles complete mock
jest.mock("react-native", () => {
const RN = jest.requireActual("react-native");
class MockUnistylesBridge {
#timerRef?: ReturnType<typeof setTimeout> = undefined;
#hasAdaptiveThemes = false;
#supportsAutomaticColorScheme = false;
#screenWidth = 375;
#screenHeight = 812;
#themes: Array<keyof UnistylesThemes> = [];
#breakpoints: UnistylesBreakpoints = {} as UnistylesBreakpoints;
#colorScheme: ColorSchemeName = "light";
#themeName: keyof UnistylesThemes = "" as keyof UnistylesThemes;
#enabledPlugins: Array<string> = [];
#unistylesEvents = new RN.NativeEventEmitter(RN.NativeModules.Unistyles);
#sortedBreakpointPairs: Array<[keyof UnistylesBreakpoints, number]> = [];
#breakpoint: keyof UnistylesBreakpoints = "" as keyof UnistylesBreakpoints;
#contentSizeCategory = "unspecified";
public install() {
// @ts-ignore
global.__UNISTYLES__ = this;
return true;
}
public useTheme(themeName: keyof UnistylesThemes) {
this.#themeName = themeName;
this.emitThemeChange();
}
public updateTheme(themeName: keyof UnistylesThemes) {
if (this.#themeName === themeName) {
this.emitThemeChange();
}
}
public useBreakpoints(breakpoints: UnistylesBreakpoints) {
this.#breakpoints = breakpoints;
this.#sortedBreakpointPairs = Object.entries(breakpoints).sort(
([, a], [, b]) => (a ?? 0) - (b ?? 0),
) as Array<[keyof UnistylesBreakpoints, number]>;
this.#breakpoint = this.getBreakpointFromScreenWidth(
this.#screenWidth as number,
);
}
public useAdaptiveThemes(enable: boolean) {
this.#hasAdaptiveThemes = enable;
if (!this.#hasAdaptiveThemes || !this.#supportsAutomaticColorScheme)
return;
if (this.#themeName !== this.#colorScheme) {
this.#themeName = this.#colorScheme as keyof UnistylesThemes;
this.emitThemeChange();
}
}
public addPlugin(pluginName: string, notify: boolean) {
this.#enabledPlugins = [pluginName].concat(this.#enabledPlugins);
if (notify) this.emitPluginChange();
}
public removePlugin(pluginName: string) {
this.#enabledPlugins = this.#enabledPlugins.filter(
(name) => name !== pluginName,
);
this.emitPluginChange();
}
get themeName(): keyof UnistylesThemes {
// biome-ignore lint/style/noNonNullAssertion: <explanation>
if (this.#themes.length === 1) return this.#themes.at(0)!;
return this.#themeName;
}
// set themeName(themeName: keyof UnistylesThemes) {
// this.#themeName = themeName as keyof UnistylesThemes;
// this.emitThemeChange();
// }
set themes(themes: Array<keyof UnistylesThemes>) {
this.#themes = themes;
this.#supportsAutomaticColorScheme =
themes.includes("light") && themes.includes("dark");
}
get screenWidth() {
return this.#screenWidth;
}
get screenHeight() {
return this.#screenHeight;
}
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
get contentSizeCategory(): any {
return this.#contentSizeCategory;
}
get breakpoint() {
return this.#breakpoint || undefined;
}
get breakpoints() {
return this.#breakpoints;
}
get hasAdaptiveThemes() {
return this.#hasAdaptiveThemes;
}
get sortedBreakpointPairs() {
return this.#sortedBreakpointPairs;
}
get enabledPlugins() {
return this.#enabledPlugins;
}
get colorScheme() {
return this.#colorScheme;
}
private emitPluginChange() {
this.#unistylesEvents.emit("__unistylesOnChange", { type: "plugin" });
}
private emitThemeChange() {
this.#unistylesEvents.emit("__unistylesOnChange", {
type: "theme",
payload: { themeName: this.#themeName },
});
}
private getBreakpointFromScreenWidth(
width: number,
): keyof UnistylesBreakpoints {
const breakpoint = this.#sortedBreakpointPairs.find(
([, value], index, otherBreakpoints) => {
const minVal = value;
const maxVal = otherBreakpoints[index + 1]?.[1];
if (!maxVal) return true;
return width >= minVal && width < maxVal;
},
);
return breakpoint?.at(0) as keyof UnistylesBreakpoints;
}
}
if (!RN.NativeModules.Unistyles)
RN.NativeModules.Unistyles =
new MockUnistylesBridge() satisfies UnistylesBridge;
return RN;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment