/* eslint-disable @typescript-eslint/ban-ts-comment */ import React from 'react'; import {render} from '@testing-library/react-native'; import {HostComponent} from 'react-native'; import {act} from 'react-test-renderer'; import 'jest-extended'; import {inMemoryCounter} from '../utils'; import {getPerformanceMarker, PerformanceMarkerProps} from '../PerformanceMarker'; import PerformanceMeasureView, { DEFAULT_INTERACTIVE_RENDER_PASS_NAME, DEFAULT_NON_INTERACTIVE_RENDER_PASS_NAME, } from '../PerformanceMeasureView'; import MockStateController from './MockStateController'; import profilerTestWrapper from './profilerTestWrapper'; jest.mock('../PerformanceMarker', () => { return { getPerformanceMarker: jest.fn(), }; }); jest.mock('../utils/inMemoryCounter', () => { return jest.fn(); }); const TestView = (_props: {[key: string]: any}) => { return null; }; const emptyComponent = () => null; const inMemoryCounterMock = inMemoryCounter as jest.Mock; describe('PerformanceMeasureView', () => { let stateController: MockStateController; let PerformanceMarker: HostComponent; let Wrapper: (_: {children: React.ReactElement}) => JSX.Element; beforeEach(() => { inMemoryCounterMock.mockReturnValue('some-uuid'); // @ts-ignore PerformanceMarker = emptyComponent; // @ts-ignore getPerformanceMarker.mockReturnValue(PerformanceMarker); stateController = new MockStateController(); stateController.isEnabled = true; Wrapper = profilerTestWrapper(stateController).wrapper; }); afterEach(() => { jest.resetAllMocks(); }); it('renders children unaddected', () => { const props = { prop1: 'prop1', prop2: 123, prop3: false, style: { flex: 1, width: 1, height: 2, }, }; const screen = render( , ); const view = screen.UNSAFE_getByType(TestView); expect(view.props).toStrictEqual(props); }); it('renders measure view with invisible style', () => { const screen = render( , ); const view = screen.UNSAFE_getByType(PerformanceMarker); expect(view.props.style).toStrictEqual( expect.objectContaining({ width: 0, height: 0, }), ); }); it('configures the default non-interactive render pass name correctly', () => { const screen = render( , ); const view = screen.UNSAFE_getByType(PerformanceMarker); expect(view.props.renderPassName).toBe(DEFAULT_NON_INTERACTIVE_RENDER_PASS_NAME); }); it('configures the default interactive render pass name correctly', () => { const screen = render( , ); const view = screen.UNSAFE_getByType(PerformanceMarker); expect(view.props.renderPassName).toBe(DEFAULT_INTERACTIVE_RENDER_PASS_NAME); }); it('configures the destination screen correctly', () => { const screen = render( , ); const view = screen.UNSAFE_getByType(PerformanceMarker); expect(view.props.destinationScreen).toBe('SomeScreen'); }); it('configures the interactive value to false correctly', () => { const screen = render( , ); const view = screen.UNSAFE_getByType(PerformanceMarker); expect(view.props.interactive).toBe('FALSE'); }); it('configures the interactive value to true correctly', () => { const screen = render( , ); const view = screen.UNSAFE_getByType(PerformanceMarker); expect(view.props.interactive).toBe('TRUE'); }); it('configures the renderPassName correctly if one is provided', () => { const screen = render( , ); const view = screen.UNSAFE_getByType(PerformanceMarker); expect(view.props.renderPassName).toBe('renderPass1'); }); it('does not render the measure view marker if disabled', () => { stateController.isEnabled = false; const screen = render( , ); const views = screen.UNSAFE_queryAllByType(PerformanceMarker); expect(views).toHaveLength(0); }); it('notifies the state controller when the screen is mounted', () => { inMemoryCounterMock.mockReturnValueOnce('mock-mount-id'); expect(stateController.onScreenMounted).not.toHaveBeenCalled(); render( , ); expect(stateController.onScreenMounted).toHaveBeenCalledTimes(1); expect(stateController.onScreenMounted).toHaveBeenCalledWith({ destinationScreen: 'SomeScreen', componentInstanceId: 'mock-mount-id', }); }); it('does not notify the state controller of a screen mount if the screen re-renders', () => { inMemoryCounterMock.mockReturnValueOnce('mock-mount-id'); inMemoryCounterMock.mockReturnValueOnce('mock-render-id1'); inMemoryCounterMock.mockReturnValueOnce('mock-render-id2'); const screen = render( , ); expect(stateController.onScreenMounted).toHaveBeenCalledTimes(1); screen.rerender( , ); expect(stateController.onScreenMounted).toHaveBeenCalledTimes(1); expect(stateController.onScreenMounted).toHaveBeenCalledWith({ destinationScreen: 'SomeScreen', componentInstanceId: 'mock-mount-id', }); }); it('uses the same promise reference for tracking the componentInstanceId between mounts and unmounts', () => { inMemoryCounterMock.mockReturnValueOnce('mock-mount-uuid'); const screen = render( , ); const {componentInstanceId: mountId} = stateController.onScreenMounted.mock.calls[0][0]; act(() => { screen.unmount(); }); const {componentInstanceId: unmountId} = stateController.onScreenUnmounted.mock.calls[0][0]; expect(mountId === unmountId).toBe(true); }); it('notifies the state controller when the screen is unmounted', () => { inMemoryCounterMock.mockReturnValueOnce('mock-mount-id'); const screen = render( , ); expect(stateController.onScreenUnmounted).not.toHaveBeenCalled(); act(() => { screen.unmount(); }); expect(stateController.onScreenUnmounted).toHaveBeenCalledTimes(1); expect(stateController.onScreenUnmounted).toHaveBeenCalledWith({ destinationScreen: 'SomeScreen', componentInstanceId: 'mock-mount-id', }); }); it("notifies the state controller of a new mount followed by the previous instance's unmount", () => { const Component1 = () => ( ); const Component2 = () => ( ); inMemoryCounterMock.mockReturnValueOnce('mock-mount-id1'); inMemoryCounterMock.mockReturnValueOnce('mock-mount-id2'); const screen = render(); expect(stateController.onScreenMounted).toHaveBeenCalledTimes(1); expect(stateController.onScreenMounted).toHaveBeenCalledWith({ destinationScreen: 'SomeScreen', componentInstanceId: 'mock-mount-id1', }); expect(stateController.onScreenUnmounted).not.toHaveBeenCalled(); screen.rerender(); expect(stateController.onScreenMounted).toHaveBeenCalledTimes(2); expect(stateController.onScreenMounted).toHaveBeenLastCalledWith({ destinationScreen: 'SomeScreen', componentInstanceId: 'mock-mount-id2', }); expect(stateController.onScreenUnmounted).toHaveBeenCalledTimes(1); expect(stateController.onScreenUnmounted).toHaveBeenCalledWith({ destinationScreen: 'SomeScreen', componentInstanceId: 'mock-mount-id1', }); expect(stateController.onScreenUnmounted).not.toHaveBeenCalledBefore(stateController.onScreenMounted); }); it('notifies the state controller when the screen is rendered', () => { inMemoryCounterMock.mockReturnValueOnce('mock-mount-id'); expect(stateController.onRenderPassCompleted).not.toHaveBeenCalled(); const screen = render( , ); const view = screen.UNSAFE_getByType(PerformanceMarker); view.props.onRenderComplete({ nativeEvent: { timestamp: 2000, renderPassName: 'renderPass1', interactive: 'TRUE', destinationScreen: 'SomeScreen', componentInstanceId: 'mock-mount-id', }, }); expect(stateController.onRenderPassCompleted).toHaveBeenCalledTimes(1); expect(stateController.onRenderPassCompleted).toHaveBeenCalledWith({ timestamp: 2000, renderPassName: 'renderPass1', interactive: true, destinationScreen: 'SomeScreen', componentInstanceId: 'mock-mount-id', }); }); });