/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import '@testing-library/jest-dom'; import PropTypes from 'prop-types'; import { PureComponent } from 'react'; import { reactify } from '@superset-ui/core'; import { render, screen } from '@testing-library/react'; import { RenderFuncType } from '../../../src/chart/components/reactify'; describe('reactify(renderFn)', () => { const renderFn: RenderFuncType<{ content?: string }> = jest.fn( (element, props) => { const container = element; container.innerHTML = ''; const child = document.createElement('b'); child.innerHTML = props.content ?? ''; container.append(child); }, ); renderFn.displayName = 'BoldText'; renderFn.propTypes = { content: PropTypes.string, }; renderFn.defaultProps = { content: 'ghi', }; const willUnmountCb = jest.fn(); const TheChart = reactify(renderFn); const TheChartWithWillUnmountHook = reactify(renderFn, { componentWillUnmount: willUnmountCb, }); class TestComponent extends PureComponent<{}, { content: string }> { constructor(props = {}) { super(props); this.state = { content: 'abc' }; } componentDidMount() { setTimeout(() => { this.setState({ content: 'def' }); }, 10); } render() { const { content } = this.state; return ; } } class AnotherTestComponent extends PureComponent<{}, {}> { render() { return ; } } it('returns a React component class', () => new Promise(done => { render(); expect(renderFn).toHaveBeenCalledTimes(1); expect(screen.getByText('abc')).toBeInTheDocument(); expect(screen.getByText('abc').parentNode).toHaveAttribute('id', 'test'); setTimeout(() => { expect(renderFn).toHaveBeenCalledTimes(2); expect(screen.getByText('def')).toBeInTheDocument(); expect(screen.getByText('def').parentNode).toHaveAttribute( 'id', 'test', ); done(undefined); }, 20); })); describe('displayName', () => { it('has displayName if renderFn.displayName is defined', () => { expect(TheChart.displayName).toEqual('BoldText'); }); it('does not have displayName if renderFn.displayName is not defined', () => { const AnotherChart = reactify(() => {}); expect(AnotherChart.displayName).toBeUndefined(); }); }); describe('propTypes', () => { it('has propTypes if renderFn.propTypes is defined', () => { /* eslint-disable-next-line react/forbid-foreign-prop-types */ expect(Object.keys(TheChart.propTypes ?? {})).toEqual(['content']); }); it('does not have propTypes if renderFn.propTypes is not defined', () => { const AnotherChart = reactify(() => {}); /* eslint-disable-next-line react/forbid-foreign-prop-types */ expect(Object.keys(AnotherChart.propTypes ?? {})).toEqual([]); }); }); describe('defaultProps', () => { it('has defaultProps if renderFn.defaultProps is defined', () => { expect(TheChart.defaultProps).toBe(renderFn.defaultProps); render(); expect(screen.getByText('ghi')).toBeInTheDocument(); expect(screen.getByText('ghi').parentNode).toHaveAttribute('id', 'test'); }); it('does not have defaultProps if renderFn.defaultProps is not defined', () => { const AnotherChart = reactify(() => {}); expect(AnotherChart.defaultProps).toBeUndefined(); }); }); it('does not try to render if not mounted', () => { const anotherRenderFn = jest.fn(); const AnotherChart = reactify(anotherRenderFn); // enables valid new AnotherChart() call // @ts-ignore new AnotherChart({ id: 'test' }).execute(); expect(anotherRenderFn).not.toHaveBeenCalled(); }); it('calls willUnmount hook when it is provided', () => new Promise(done => { const { unmount } = render(); setTimeout(() => { unmount(); expect(willUnmountCb).toHaveBeenCalledTimes(1); done(undefined); }, 20); })); });