UNPKG

@applicaster/quick-brick-core

Version:

Core package for Applicaster's Quick Brick App

233 lines (195 loc) • 6.53 kB
import React from "react"; import { View, Text, TouchableOpacity } from "react-native"; import { render, fireEvent, act } from "@testing-library/react-native"; import { useErrorStore } from "../store"; import { ErrorBoundary } from "../index"; jest.mock( "@applicaster/zapp-react-native-ui-components/Components/ErrorScreen", () => ({ // eslint-disable-next-line react/prop-types ErrorScreen: ({ dismissError, message, buttons }) => ( <View testID="error-screen"> <Text testID="error-message">{message || "Default error message"}</Text> {/* eslint-disable-next-line react/prop-types */} {buttons?.map((button, index) => ( <TouchableOpacity key={index} onPress={button.onPress} testID={`button-${index}`} > <Text>{button.text}</Text> </TouchableOpacity> ))} {dismissError ? ( <TouchableOpacity onPress={dismissError} testID="dismiss-button"> <Text>Dismiss Error</Text> </TouchableOpacity> ) : null} </View> ), }) ); jest.mock("@applicaster/zapp-react-native-bridge/QuickBrick", () => ({ sendQuickBrickEvent: jest.fn(), QUICK_BRICK_EVENTS: { FORCE_APP_RELOAD: "force_app_reload", }, })); jest.mock("@applicaster/zapp-react-native-utils/logger", () => ({ coreLogger: { addSubsystem: () => ({ error: jest.fn(), }), }, })); // eslint-disable-next-line react/prop-types const TestComponent = ({ error, errorMessage = "error !!" }) => { if (error) { throw new Error(errorMessage); } return ( <View> <Text>all is good</Text> </View> ); }; describe("<ErrorBoundary />", () => { // Clear all error states between tests beforeEach(() => { useErrorStore.getState().dismissError(); }); describe("when there is no error", () => { it("renders children correctly", () => { const { getByText } = render( <ErrorBoundary> <TestComponent /> </ErrorBoundary> ); expect(getByText("all is good")).toBeTruthy(); }); }); describe("when an error is caught", () => { const _consoleLogs = { error: console.error, log: console.log, }; beforeAll(() => { console.error = jest.fn(); console.log = jest.fn(); }); afterAll(() => { console.error = _consoleLogs.error; console.log = _consoleLogs.log; }); it("renders error state with default values (recoverable error)", () => { const { getByTestId } = render( <ErrorBoundary> <TestComponent error /> </ErrorBoundary> ); expect(getByTestId("error-screen")).toBeTruthy(); expect(getByTestId("dismiss-button")).toBeTruthy(); // Should be recoverable by default }); it("handles non-recoverable errors (no dismiss button)", () => { const { getByTestId, queryByTestId } = render( <ErrorBoundary recoverable={false}> <TestComponent error /> </ErrorBoundary> ); expect(getByTestId("error-screen")).toBeTruthy(); expect(queryByTestId("dismiss-button")).toBeFalsy(); // Should not be recoverable }); it("shows custom error message from caught error", () => { const customErrorMessage = "Custom error message for testing"; const { getByTestId } = render( <ErrorBoundary> <TestComponent error errorMessage={customErrorMessage} /> </ErrorBoundary> ); // The error info should be passed to the ErrorScreen expect(getByTestId("error-message").props.children).toBeTruthy(); }); it("can dismiss the error and restore the UI", () => { // First render with error const { getByTestId, queryByText, rerender } = render( <ErrorBoundary> <TestComponent error /> </ErrorBoundary> ); // Verify error screen is shown expect(getByTestId("error-screen")).toBeTruthy(); // Dismiss the error fireEvent.press(getByTestId("dismiss-button")); // Force rerender to see the change rerender( <ErrorBoundary> <TestComponent /> </ErrorBoundary> ); // Now the regular content should be visible expect(queryByText("all is good")).toBeTruthy(); }); }); describe("error store functionality", () => { it("properly sets error state", () => { const error = new Error("Test error"); const errorInfo = "Test error info"; act(() => { useErrorStore.getState().setError(error, errorInfo, true); }); const state = useErrorStore.getState(); expect(state.hasError).toBe(true); expect(state.error).toBe(error); expect(state.info).toBe(errorInfo); expect(state.recoverable).toBe(true); }); it("properly resets error state", () => { // First set an error act(() => { useErrorStore .getState() .setError(new Error("Test error"), "Info", true); }); // Then dismiss it act(() => { useErrorStore.getState().dismissError(); }); const state = useErrorStore.getState(); expect(state.hasError).toBe(false); expect(state.error).toBeUndefined(); expect(state.info).toBeUndefined(); expect(state.recoverable).toBe(true); // Default value when reset }); it("remembers recoverable state", () => { act(() => { useErrorStore .getState() .setError(new Error("Test error"), "Info", false); }); const state = useErrorStore.getState(); expect(state.recoverable).toBe(false); }); }); describe("integration with ErrorScreen", () => { it("renders restart app button for non-recoverable errors", () => { const { getAllByTestId } = render( <ErrorBoundary recoverable={false}> <TestComponent error /> </ErrorBoundary> ); // Should have the restart app button const buttons = getAllByTestId(/button-/); expect(buttons.length).toBeGreaterThan(0); }); it("passes error info to the ErrorScreen component", () => { const customErrorMessage = "Very specific error message"; const { getByTestId } = render( <ErrorBoundary> <TestComponent error errorMessage={customErrorMessage} /> </ErrorBoundary> ); // The error info should be visible in the UI expect(getByTestId("error-message")).toBeTruthy(); }); }); });