reactify.test.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. import '@testing-library/jest-dom';
  20. import PropTypes from 'prop-types';
  21. import { PureComponent } from 'react';
  22. import { reactify } from '@superset-ui/core';
  23. import { render, screen } from '@testing-library/react';
  24. import { RenderFuncType } from '../../../src/chart/components/reactify';
  25. describe('reactify(renderFn)', () => {
  26. const renderFn: RenderFuncType<{ content?: string }> = jest.fn(
  27. (element, props) => {
  28. const container = element;
  29. container.innerHTML = '';
  30. const child = document.createElement('b');
  31. child.innerHTML = props.content ?? '';
  32. container.append(child);
  33. },
  34. );
  35. renderFn.displayName = 'BoldText';
  36. renderFn.propTypes = {
  37. content: PropTypes.string,
  38. };
  39. renderFn.defaultProps = {
  40. content: 'ghi',
  41. };
  42. const willUnmountCb = jest.fn();
  43. const TheChart = reactify(renderFn);
  44. const TheChartWithWillUnmountHook = reactify(renderFn, {
  45. componentWillUnmount: willUnmountCb,
  46. });
  47. class TestComponent extends PureComponent<{}, { content: string }> {
  48. constructor(props = {}) {
  49. super(props);
  50. this.state = { content: 'abc' };
  51. }
  52. componentDidMount() {
  53. setTimeout(() => {
  54. this.setState({ content: 'def' });
  55. }, 10);
  56. }
  57. render() {
  58. const { content } = this.state;
  59. return <TheChart id="test" content={content} />;
  60. }
  61. }
  62. class AnotherTestComponent extends PureComponent<{}, {}> {
  63. render() {
  64. return <TheChartWithWillUnmountHook id="another_test" />;
  65. }
  66. }
  67. it('returns a React component class', () =>
  68. new Promise(done => {
  69. render(<TestComponent />);
  70. expect(renderFn).toHaveBeenCalledTimes(1);
  71. expect(screen.getByText('abc')).toBeInTheDocument();
  72. expect(screen.getByText('abc').parentNode).toHaveAttribute('id', 'test');
  73. setTimeout(() => {
  74. expect(renderFn).toHaveBeenCalledTimes(2);
  75. expect(screen.getByText('def')).toBeInTheDocument();
  76. expect(screen.getByText('def').parentNode).toHaveAttribute(
  77. 'id',
  78. 'test',
  79. );
  80. done(undefined);
  81. }, 20);
  82. }));
  83. describe('displayName', () => {
  84. it('has displayName if renderFn.displayName is defined', () => {
  85. expect(TheChart.displayName).toEqual('BoldText');
  86. });
  87. it('does not have displayName if renderFn.displayName is not defined', () => {
  88. const AnotherChart = reactify(() => {});
  89. expect(AnotherChart.displayName).toBeUndefined();
  90. });
  91. });
  92. describe('propTypes', () => {
  93. it('has propTypes if renderFn.propTypes is defined', () => {
  94. /* eslint-disable-next-line react/forbid-foreign-prop-types */
  95. expect(Object.keys(TheChart.propTypes ?? {})).toEqual(['content']);
  96. });
  97. it('does not have propTypes if renderFn.propTypes is not defined', () => {
  98. const AnotherChart = reactify(() => {});
  99. /* eslint-disable-next-line react/forbid-foreign-prop-types */
  100. expect(Object.keys(AnotherChart.propTypes ?? {})).toEqual([]);
  101. });
  102. });
  103. describe('defaultProps', () => {
  104. it('has defaultProps if renderFn.defaultProps is defined', () => {
  105. expect(TheChart.defaultProps).toBe(renderFn.defaultProps);
  106. render(<TheChart id="test" />);
  107. expect(screen.getByText('ghi')).toBeInTheDocument();
  108. expect(screen.getByText('ghi').parentNode).toHaveAttribute('id', 'test');
  109. });
  110. it('does not have defaultProps if renderFn.defaultProps is not defined', () => {
  111. const AnotherChart = reactify(() => {});
  112. expect(AnotherChart.defaultProps).toBeUndefined();
  113. });
  114. });
  115. it('does not try to render if not mounted', () => {
  116. const anotherRenderFn = jest.fn();
  117. const AnotherChart = reactify(anotherRenderFn); // enables valid new AnotherChart() call
  118. // @ts-ignore
  119. new AnotherChart({ id: 'test' }).execute();
  120. expect(anotherRenderFn).not.toHaveBeenCalled();
  121. });
  122. it('calls willUnmount hook when it is provided', () =>
  123. new Promise(done => {
  124. const { unmount } = render(<AnotherTestComponent />);
  125. setTimeout(() => {
  126. unmount();
  127. expect(willUnmountCb).toHaveBeenCalledTimes(1);
  128. done(undefined);
  129. }, 20);
  130. }));
  131. });