/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails react-core * @jest-environment node */ 'use strict'; let React; let ReactNative; let createReactNativeComponentClass; let computeComponentStackForErrorReporting; function normalizeCodeLocInfo(str) { return ( str && str.replace(/\n +(?:at|in) ([\S]+)[^\n]*/g, function (m, name) { return '\n in ' + name + ' (at **)'; }) ); } describe('ReactNativeError', () => { beforeEach(() => { jest.resetModules(); React = require('react'); ReactNative = require('react-native-renderer'); createReactNativeComponentClass = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface') .ReactNativeViewConfigRegistry.register; computeComponentStackForErrorReporting = ReactNative.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED .computeComponentStackForErrorReporting; }); it('should throw error if null component registration getter is used', () => { expect(() => { try { createReactNativeComponentClass('View', null); } catch (e) { throw new Error(e.toString()); } }).toThrow( 'View config getter callback for component `View` must be a function (received `null`)', ); }); it('should be able to extract a component stack from a native view', () => { const View = createReactNativeComponentClass('View', () => ({ validAttributes: {foo: true}, uiViewClassName: 'View', })); const ref = React.createRef(); function FunctionComponent(props) { return props.children; } class ClassComponent extends React.Component { render() { return ( ); } } ReactNative.render(, 1); const reactTag = ReactNative.findNodeHandle(ref.current); const componentStack = normalizeCodeLocInfo( computeComponentStackForErrorReporting(reactTag), ); expect(componentStack).toBe( '\n' + ' in View (at **)\n' + ' in FunctionComponent (at **)\n' + ' in ClassComponent (at **)', ); }); });