From d786e238db782c65265111bfab7535da420796a8 Mon Sep 17 00:00:00 2001 From: hainenber Date: Thu, 5 Sep 2024 00:16:34 +0700 Subject: [PATCH 01/12] refactor(fe/pkg/ui-core): migrate TooltipTable.test.tsx from Enzyme to RTL Signed-off-by: hainenber --- .../tooltip/TooltipTable.test.tsx | 46 ++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/test/chart-composition/tooltip/TooltipTable.test.tsx b/superset-frontend/packages/superset-ui-core/test/chart-composition/tooltip/TooltipTable.test.tsx index ebd3f0d073de7..08f2b5c98da14 100644 --- a/superset-frontend/packages/superset-ui-core/test/chart-composition/tooltip/TooltipTable.test.tsx +++ b/superset-frontend/packages/superset-ui-core/test/chart-composition/tooltip/TooltipTable.test.tsx @@ -17,23 +17,24 @@ * under the License. */ -import { shallow } from 'enzyme'; +import '@testing-library/jest-dom'; +import { screen, render } from '@testing-library/react'; import { TooltipTable } from '@superset-ui/core'; describe('TooltipTable', () => { it('sets className', () => { - const wrapper = shallow(); - expect(wrapper.render().hasClass('test-class')).toEqual(true); + const { container } = render(); + expect(container.querySelector('[class="test-class"]')).toBeInTheDocument(); }); it('renders empty table', () => { - const wrapper = shallow(); - expect(wrapper.find('tbody')).toHaveLength(1); - expect(wrapper.find('tr')).toHaveLength(0); + const { container } = render(); + expect(container.querySelector('tbody')).toBeInTheDocument(); + expect(container.querySelector('tr')).not.toBeInTheDocument(); }); - it('renders table with content', () => { - const wrapper = shallow( + it('renders table with content', async () => { + render( { ]} />, ); - expect(wrapper.find('tbody')).toHaveLength(1); - expect(wrapper.find('tr')).toHaveLength(3); - expect(wrapper.find('tr > td').first().text()).toEqual('Cersei'); + let keyCell; + let valueCell; + + keyCell = await screen.findByText('Cersei'); + valueCell = keyCell?.nextSibling as HTMLElement; + expect(keyCell).toBeInTheDocument(); + expect(valueCell?.textContent).toEqual('2'); + expect(valueCell?.getAttribute('style')).toEqual( + 'padding-left: 8px; text-align: right;', + ); + + keyCell = await screen.findByText('Jaime'); + valueCell = keyCell?.nextSibling as HTMLElement; + expect(keyCell).toBeInTheDocument(); + expect(valueCell?.textContent).toEqual('1'); + expect(valueCell?.getAttribute('style')).toEqual( + 'padding-left: 8px; text-align: right;', + ); + + keyCell = await screen.findByText('Tyrion'); + valueCell = keyCell?.nextSibling as HTMLElement; + expect(keyCell).toBeInTheDocument(); + expect(valueCell?.textContent).toEqual('2'); + expect(valueCell?.getAttribute('style')).toEqual( + 'padding-left: 8px; text-align: right;', + ); }); }); From e7ff2a24b3ec1846060cdbfe5c1865da56e6b6bd Mon Sep 17 00:00:00 2001 From: hainenber Date: Sat, 7 Sep 2024 23:00:43 +0700 Subject: [PATCH 02/12] refactor(fe/src/explore/comps/controls): refactor SelectControl.test.jsx from Enzyme to RTL Signed-off-by: hainenber --- .../components/controls/SelectControl.jsx | 64 ++--- .../controls/SelectControl.test.jsx | 236 +++++++++++------- 2 files changed, 185 insertions(+), 115 deletions(-) diff --git a/superset-frontend/src/explore/components/controls/SelectControl.jsx b/superset-frontend/src/explore/components/controls/SelectControl.jsx index e0f1ae5f04461..b50d54c9adc76 100644 --- a/superset-frontend/src/explore/components/controls/SelectControl.jsx +++ b/superset-frontend/src/explore/components/controls/SelectControl.jsx @@ -87,6 +87,39 @@ const defaultProps = { valueKey: 'value', }; +export const innerGetOptions = props => { + const { choices, optionRenderer, valueKey } = props; + let options = []; + if (props.options) { + options = props.options.map(o => ({ + ...o, + value: o[valueKey], + label: o.label || o[valueKey], + customLabel: optionRenderer ? optionRenderer(o) : undefined, + })); + } else if (choices) { + // Accepts different formats of input + options = choices.map(c => { + if (Array.isArray(c)) { + const [value, label] = c.length > 1 ? c : [c[0], c[0]]; + return { + value, + label, + }; + } + if (Object.is(c)) { + return { + ...c, + value: c[valueKey], + label: c.label || c[valueKey], + }; + } + return { value: c, label: c }; + }); + } + return options; +}; + export default class SelectControl extends PureComponent { constructor(props) { super(props); @@ -127,36 +160,7 @@ export default class SelectControl extends PureComponent { } getOptions(props) { - const { choices, optionRenderer, valueKey } = props; - let options = []; - if (props.options) { - options = props.options.map(o => ({ - ...o, - value: o[valueKey], - label: o.label || o[valueKey], - customLabel: optionRenderer ? optionRenderer(o) : undefined, - })); - } else if (choices) { - // Accepts different formats of input - options = choices.map(c => { - if (Array.isArray(c)) { - const [value, label] = c.length > 1 ? c : [c[0], c[0]]; - return { - value, - label, - }; - } - if (Object.is(c)) { - return { - ...c, - value: c[valueKey], - label: c.label || c[valueKey], - }; - } - return { value: c, label: c }; - }); - } - return options; + return innerGetOptions(props); } handleFilterOptions(text, option) { diff --git a/superset-frontend/src/explore/components/controls/SelectControl.test.jsx b/superset-frontend/src/explore/components/controls/SelectControl.test.jsx index d86c334be17b5..4a0c5c7e2fdae 100644 --- a/superset-frontend/src/explore/components/controls/SelectControl.test.jsx +++ b/superset-frontend/src/explore/components/controls/SelectControl.test.jsx @@ -16,11 +16,18 @@ * specific language governing permissions and limitations * under the License. */ -import sinon from 'sinon'; -import { shallow } from 'enzyme'; -import { Select as SelectComponent } from 'src/components'; -import SelectControl from 'src/explore/components/controls/SelectControl'; -import { styledMount as mount } from 'spec/helpers/theming'; +import { + createEvent, + fireEvent, + render, + screen, + within, +} from 'spec/helpers/testing-library'; +import SelectControl, { + innerGetOptions, +} from 'src/explore/components/controls/SelectControl'; +import userEvent from '@testing-library/user-event'; +import { act } from '@testing-library/react-hooks'; const defaultProps = { choices: [ @@ -30,139 +37,198 @@ const defaultProps = { ], name: 'row_limit', label: 'Row Limit', - valueKey: 'value', // shallow isn't passing SelectControl.defaultProps.valueKey through - onChange: sinon.spy(), + valueKey: 'value', + onChange: jest.fn(), }; +jest.useFakeTimers(); + const options = [ { value: '1 year ago', label: '1 year ago' }, { value: '1 week ago', label: '1 week ago' }, { value: 'today', label: 'today' }, ]; -describe('SelectControl', () => { - let wrapper; - - beforeEach(() => { - wrapper = shallow(); - }); +const renderSelectControl = (props = {}) => { + const overrideProps = { + ...defaultProps, + ...props, + }; + const { container } = render(); + return container; +}; - it('calls props.onChange when select', () => { - const select = wrapper.instance(); - select.onChange(50); - expect(defaultProps.onChange.calledWith(50)).toBe(true); +describe('SelectControl', () => { + it('calls props.onChange when select', async () => { + renderSelectControl(); + defaultProps.onChange(50); + expect(defaultProps.onChange).toHaveBeenCalledWith(50); }); describe('render', () => { it('renders with Select by default', () => { - expect(wrapper.find(SelectComponent)).toExist(); + renderSelectControl(); + const selectorWrapper = screen.getByLabelText('Row Limit', { + selector: 'div', + }); + const selectorInput = within(selectorWrapper).getByLabelText( + 'Row Limit', + { selector: 'input' }, + ); + expect(selectorWrapper).toBeInTheDocument(); + expect(selectorInput).toBeInTheDocument(); }); it('renders as mode multiple', () => { - wrapper.setProps({ multi: true }); - expect(wrapper.find(SelectComponent)).toExist(); - expect(wrapper.find(SelectComponent).prop('mode')).toBe('multiple'); + renderSelectControl({ multi: true }); + const selectorWrapper = screen.getByLabelText('Row Limit', { + selector: 'div', + }); + const selectorInput = within(selectorWrapper).getByLabelText( + 'Row Limit', + { selector: 'input' }, + ); + expect(selectorWrapper).toBeInTheDocument(); + expect(selectorInput).toBeInTheDocument(); + userEvent.click(selectorInput); + expect(screen.getByText('Select All (3)')).toBeInTheDocument(); }); it('renders with allowNewOptions when freeForm', () => { - wrapper.setProps({ freeForm: true }); - expect(wrapper.find(SelectComponent)).toExist(); - expect(wrapper.find(SelectComponent).prop('allowNewOptions')).toBe(true); + renderSelectControl({ freeForm: true }); + const selectorWrapper = screen.getByLabelText('Row Limit', { + selector: 'div', + }); + const selectorInput = within(selectorWrapper).getByLabelText( + 'Row Limit', + { selector: 'input' }, + ); + expect(selectorWrapper).toBeInTheDocument(); + expect(selectorInput).toBeInTheDocument(); + + // Expect a new option to be selectable. + userEvent.click(selectorInput); + userEvent.type(selectorInput, 'a new option'); + act(() => jest.advanceTimersByTime(300)); + expect(within(selectorWrapper).getByRole('option')).toHaveTextContent( + 'a new option', + ); }); it('renders with allowNewOptions=false when freeForm=false', () => { - wrapper.setProps({ freeForm: false }); - expect(wrapper.find(SelectComponent)).toExist(); - expect(wrapper.find(SelectComponent).prop('allowNewOptions')).toBe(false); + const container = renderSelectControl({ freeForm: false }); + const selectorWrapper = screen.getByLabelText('Row Limit', { + selector: 'div', + }); + const selectorInput = within(selectorWrapper).getByLabelText( + 'Row Limit', + { selector: 'input' }, + ); + expect(selectorWrapper).toBeInTheDocument(); + expect(selectorInput).toBeInTheDocument(); + + // Expect no new option to be selectable. + userEvent.click(selectorInput); + userEvent.type(selectorInput, 'a new option'); + act(() => jest.advanceTimersByTime(300)); + + expect( + container.querySelector('[role="option"]'), + ).not.toBeInTheDocument(); + expect(within(selectorWrapper).getByText('No Data')).toBeInTheDocument(); }); it('renders with tokenSeparators', () => { - wrapper.setProps({ tokenSeparators: ['\n', '\t', ';'] }); - expect(wrapper.find(SelectComponent)).toExist(); - expect(wrapper.find(SelectComponent).prop('tokenSeparators')).toEqual( - expect.arrayContaining([expect.any(String)]), + renderSelectControl({ tokenSeparators: ['\n', '\t', ';'], multi: true }); + const selectorWrapper = screen.getByLabelText('Row Limit', { + selector: 'div', + }); + const selectorInput = within(selectorWrapper).getByLabelText( + 'Row Limit', + { selector: 'input' }, ); + expect(selectorWrapper).toBeInTheDocument(); + expect(selectorInput).toBeInTheDocument(); + + userEvent.click(selectorInput); + const paste = createEvent.paste(selectorInput, { + clipboardData: { + getData: () => '1 year ago;1 week ago', + }, + }); + fireEvent(selectorInput, paste); + const yearOption = screen.getByLabelText('1 year ago'); + expect(yearOption).toBeInTheDocument(); + expect(yearOption).toHaveAttribute('aria-selected', 'true'); + const weekOption = screen.getByText(/1 week ago/, { + selector: 'div', + }).parentNode; + expect(weekOption?.getAttribute('aria-selected')).toEqual('true'); }); describe('empty placeholder', () => { describe('withMulti', () => { it('does not show a placeholder if there are no choices', () => { - const withMulti = mount( - , - ); - expect(withMulti.html()).not.toContain('option(s'); + const container = renderSelectControl({ + choices: [], + multi: true, + placeholder: 'add something', + }); + expect( + container.querySelector('[role="option"]'), + ).not.toBeInTheDocument(); }); }); describe('withSingleChoice', () => { - it('does not show a placeholder if there are no choices', () => { - const singleChoice = mount( - , - ); - expect(singleChoice.html()).not.toContain('option(s'); - }); - }); - describe('default placeholder', () => { - it('does not show a placeholder if there are no options', () => { - const defaultPlaceholder = mount( - , - ); - expect(defaultPlaceholder.html()).not.toContain('option(s'); + it('does not show a placeholder if there are no choices', async () => { + const container = renderSelectControl({ + choices: [], + multi: false, + placeholder: 'add something', + }); + expect( + container.querySelector('[role="option"]'), + ).not.toBeInTheDocument(); }); }); describe('all choices selected', () => { it('does not show a placeholder', () => { - const allChoicesSelected = mount( - , - ); - expect(allChoicesSelected.html()).not.toContain('option(s'); + const container = renderSelectControl({ + multi: true, + value: ['today', '1 year ago'], + }); + expect( + container.querySelector('[role="option"]'), + ).not.toBeInTheDocument(); + expect(screen.queryByText('Select ...')).not.toBeInTheDocument(); }); }); }); describe('when select is multi', () => { it('does not render the placeholder when a selection has been made', () => { - wrapper = mount( - , - ); - expect(wrapper.html()).not.toContain('add something'); + renderSelectControl({ + multi: true, + value: ['today'], + placeholder: 'add something', + }); + expect(screen.queryByText('add something')).not.toBeInTheDocument(); }); }); describe('when select is single', () => { it('does not render the placeholder when a selection has been made', () => { - wrapper = mount( - , - ); - expect(wrapper.html()).not.toContain('add something'); + renderSelectControl({ + multi: true, + value: 50, + placeholder: 'add something', + }); + expect(screen.queryByText('add something')).not.toBeInTheDocument(); }); }); }); describe('getOptions', () => { it('returns the correct options', () => { - wrapper.setProps(defaultProps); - expect(wrapper.instance().getOptions(defaultProps)).toEqual(options); + expect(innerGetOptions(defaultProps)).toEqual(options); }); }); }); From e38d8ad0a25c44936a5f26e5fb156b0c2b589d9e Mon Sep 17 00:00:00 2001 From: hainenber Date: Sat, 7 Sep 2024 23:57:32 +0700 Subject: [PATCH 03/12] refactor(fe/plugins/legacy-chart-partition): refactor OptionDescription.test.tsx from Enzyme to RTL Signed-off-by: hainenber --- superset-frontend/package-lock.json | 144 +++++++++++++++--- .../package.json | 6 +- .../test/OptionDescription.test.jsx | 35 +++-- 3 files changed, 156 insertions(+), 29 deletions(-) diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 24a151f68055a..8e43ca630f309 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -69,7 +69,6 @@ "classnames": "^2.2.5", "core-js": "^3.38.1", "d3-scale": "^2.1.2", - "d3-time-format": "^4.1.0", "dom-to-image-more": "^3.2.0", "emotion-rgba": "0.0.12", "fast-glob": "^3.2.7", @@ -12667,6 +12666,8 @@ }, "node_modules/@testing-library/jest-dom": { "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", + "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", "license": "MIT", "dependencies": { "@adobe/css-tools": "^4.0.1", @@ -12707,6 +12708,7 @@ "version": "12.1.5", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.5.tgz", "integrity": "sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5", "@testing-library/dom": "^8.0.0", @@ -16731,6 +16733,7 @@ }, "node_modules/array.prototype.filter": { "version": "1.0.3", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -16780,6 +16783,7 @@ }, "node_modules/array.prototype.flat": { "version": "1.3.2", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -16825,6 +16829,7 @@ }, "node_modules/arraybuffer.prototype.slice": { "version": "1.0.3", + "dev": true, "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", @@ -18755,6 +18760,7 @@ }, "node_modules/cheerio": { "version": "1.0.0-rc.12", + "dev": true, "license": "MIT", "dependencies": { "cheerio-select": "^2.1.0", @@ -18774,6 +18780,7 @@ }, "node_modules/cheerio-select": { "version": "2.1.0", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", @@ -20354,6 +20361,7 @@ }, "node_modules/css-select": { "version": "5.1.0", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", @@ -22092,6 +22100,7 @@ }, "node_modules/discontinuous-range": { "version": "1.0.0", + "dev": true, "license": "MIT" }, "node_modules/distributions": { @@ -22160,6 +22169,7 @@ }, "node_modules/dom-serializer": { "version": "2.0.0", + "dev": true, "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", @@ -22189,6 +22199,7 @@ }, "node_modules/domhandler": { "version": "5.0.3", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" @@ -22202,6 +22213,7 @@ }, "node_modules/domutils": { "version": "3.1.0", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", @@ -22557,6 +22569,7 @@ }, "node_modules/enzyme": { "version": "3.11.0", + "dev": true, "license": "MIT", "dependencies": { "array.prototype.flat": "^1.2.3", @@ -22644,6 +22657,7 @@ }, "node_modules/enzyme-shallow-equal": { "version": "1.0.5", + "dev": true, "license": "MIT", "dependencies": { "has": "^1.0.3", @@ -22670,6 +22684,7 @@ }, "node_modules/enzyme/node_modules/object-inspect": { "version": "1.12.3", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -22700,6 +22715,7 @@ }, "node_modules/es-abstract": { "version": "1.22.4", + "dev": true, "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", @@ -22753,6 +22769,7 @@ }, "node_modules/es-abstract/node_modules/object-inspect": { "version": "1.13.1", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -22760,6 +22777,7 @@ }, "node_modules/es-array-method-boxes-properly": { "version": "1.0.0", + "dev": true, "license": "MIT" }, "node_modules/es-define-property": { @@ -22835,6 +22853,7 @@ }, "node_modules/es-set-tostringtag": { "version": "2.0.2", + "dev": true, "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.2", @@ -22847,6 +22866,7 @@ }, "node_modules/es-shim-unscopables": { "version": "1.0.2", + "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.0" @@ -22854,6 +22874,7 @@ }, "node_modules/es-to-primitive": { "version": "1.2.1", + "dev": true, "license": "MIT", "dependencies": { "is-callable": "^1.1.4", @@ -25891,6 +25912,7 @@ }, "node_modules/function.prototype.name": { "version": "1.1.6", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -26254,6 +26276,7 @@ }, "node_modules/get-symbol-description": { "version": "1.0.2", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.5", @@ -26759,6 +26782,7 @@ }, "node_modules/globalthis": { "version": "1.0.3", + "dev": true, "license": "MIT", "dependencies": { "define-properties": "^1.1.3" @@ -27711,6 +27735,7 @@ }, "node_modules/html-element-map": { "version": "1.3.1", + "dev": true, "license": "MIT", "dependencies": { "array.prototype.filter": "^1.0.0", @@ -27846,6 +27871,7 @@ }, "node_modules/htmlparser2": { "version": "8.0.2", + "dev": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -28946,6 +28972,7 @@ }, "node_modules/is-negative-zero": { "version": "2.0.2", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -29100,6 +29127,7 @@ }, "node_modules/is-subset": { "version": "0.1.1", + "dev": true, "license": "MIT" }, "node_modules/is-symbol": { @@ -29169,6 +29197,7 @@ }, "node_modules/is-weakref": { "version": "1.0.2", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2" @@ -33616,10 +33645,12 @@ }, "node_modules/lodash.escape": { "version": "4.0.1", + "dev": true, "license": "MIT" }, "node_modules/lodash.flattendeep": { "version": "4.4.0", + "dev": true, "license": "MIT" }, "node_modules/lodash.get": { @@ -39475,6 +39506,7 @@ }, "node_modules/moo": { "version": "0.4.3", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/mousetrap": { @@ -39644,6 +39676,7 @@ }, "node_modules/nearley": { "version": "2.18.0", + "dev": true, "license": "MIT", "dependencies": { "commander": "^2.19.0", @@ -41214,6 +41247,7 @@ }, "node_modules/object.entries": { "version": "1.1.6", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -41293,6 +41327,7 @@ }, "node_modules/object.values": { "version": "1.1.7", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -42196,6 +42231,7 @@ }, "node_modules/parse5-htmlparser2-tree-adapter": { "version": "7.0.0", + "dev": true, "license": "MIT", "dependencies": { "domhandler": "^5.0.2", @@ -42354,6 +42390,7 @@ }, "node_modules/performance-now": { "version": "2.1.0", + "dev": true, "license": "MIT" }, "node_modules/periscopic": { @@ -43799,6 +43836,7 @@ }, "node_modules/raf": { "version": "3.4.1", + "dev": true, "license": "MIT", "dependencies": { "performance-now": "^2.1.0" @@ -43806,6 +43844,7 @@ }, "node_modules/railroad-diagrams": { "version": "1.0.0", + "dev": true, "license": "CC0-1.0" }, "node_modules/ramda": { @@ -43815,6 +43854,7 @@ }, "node_modules/randexp": { "version": "0.4.6", + "dev": true, "license": "MIT", "dependencies": { "discontinuous-range": "1.0.0", @@ -47350,6 +47390,7 @@ }, "node_modules/ret": { "version": "0.1.15", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12" @@ -47561,6 +47602,7 @@ }, "node_modules/rst-selector-parser": { "version": "2.2.3", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "lodash.flattendeep": "^4.4.0", @@ -47645,6 +47687,7 @@ }, "node_modules/safe-array-concat": { "version": "1.1.0", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.5", @@ -47661,6 +47704,7 @@ }, "node_modules/safe-array-concat/node_modules/isarray": { "version": "2.0.5", + "dev": true, "license": "MIT" }, "node_modules/safe-buffer": { @@ -47677,6 +47721,7 @@ }, "node_modules/safe-regex-test": { "version": "1.0.3", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.6", @@ -47796,6 +47841,7 @@ }, "node_modules/semver": { "version": "5.7.2", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver" @@ -49284,6 +49330,7 @@ }, "node_modules/string.prototype.trim": { "version": "1.2.8", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -49299,6 +49346,7 @@ }, "node_modules/string.prototype.trimend": { "version": "1.0.7", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -49311,6 +49359,7 @@ }, "node_modules/string.prototype.trimstart": { "version": "1.0.7", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -50715,6 +50764,7 @@ }, "node_modules/typed-array-buffer": { "version": "1.0.1", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.6", @@ -50727,6 +50777,7 @@ }, "node_modules/typed-array-byte-length": { "version": "1.0.0", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -50743,6 +50794,7 @@ }, "node_modules/typed-array-byte-offset": { "version": "1.0.0", + "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.5", @@ -50760,6 +50812,7 @@ }, "node_modules/typed-array-length": { "version": "1.0.4", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -50838,6 +50891,7 @@ }, "node_modules/unbox-primitive": { "version": "1.0.2", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -56073,8 +56127,10 @@ "peerDependencies": { "@superset-ui/chart-controls": "*", "@superset-ui/core": "*", - "enzyme": "*", - "react": "^16.13.1" + "@testing-library/jest-dom": "^5.17.0", + "@testing-library/react": "^12.1.5", + "react": "^16.13.1", + "react-dom": "^16.13.1" } }, "plugins/legacy-plugin-chart-partition/node_modules/d3-hierarchy": { @@ -66999,6 +67055,8 @@ }, "@testing-library/jest-dom": { "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", + "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", "requires": { "@adobe/css-tools": "^4.0.1", "@babel/runtime": "^7.9.2", @@ -69911,6 +69969,7 @@ }, "array.prototype.filter": { "version": "1.0.3", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -69942,6 +70001,7 @@ }, "array.prototype.flat": { "version": "1.3.2", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -69972,6 +70032,7 @@ }, "arraybuffer.prototype.slice": { "version": "1.0.3", + "dev": true, "requires": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.5", @@ -71241,6 +71302,7 @@ }, "cheerio": { "version": "1.0.0-rc.12", + "dev": true, "requires": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", @@ -71253,6 +71315,7 @@ }, "cheerio-select": { "version": "2.1.0", + "dev": true, "requires": { "boolbase": "^1.0.0", "css-select": "^5.1.0", @@ -72303,6 +72366,7 @@ }, "css-select": { "version": "5.1.0", + "dev": true, "requires": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -73459,7 +73523,8 @@ } }, "discontinuous-range": { - "version": "1.0.0" + "version": "1.0.0", + "dev": true }, "distributions": { "version": "2.2.0", @@ -73512,6 +73577,7 @@ }, "dom-serializer": { "version": "2.0.0", + "dev": true, "requires": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -73529,12 +73595,14 @@ }, "domhandler": { "version": "5.0.3", + "dev": true, "requires": { "domelementtype": "^2.3.0" } }, "domutils": { "version": "3.1.0", + "dev": true, "requires": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -73794,6 +73862,7 @@ }, "enzyme": { "version": "3.11.0", + "dev": true, "requires": { "array.prototype.flat": "^1.2.3", "cheerio": "^1.0.0-rc.3", @@ -73820,7 +73889,8 @@ }, "dependencies": { "object-inspect": { - "version": "1.12.3" + "version": "1.12.3", + "dev": true } } }, @@ -73862,6 +73932,7 @@ }, "enzyme-shallow-equal": { "version": "1.0.5", + "dev": true, "requires": { "has": "^1.0.3", "object-is": "^1.1.5" @@ -73894,6 +73965,7 @@ }, "es-abstract": { "version": "1.22.4", + "dev": true, "requires": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", @@ -73939,12 +74011,14 @@ }, "dependencies": { "object-inspect": { - "version": "1.13.1" + "version": "1.13.1", + "dev": true } } }, "es-array-method-boxes-properly": { - "version": "1.0.0" + "version": "1.0.0", + "dev": true }, "es-define-property": { "version": "1.0.0", @@ -74005,6 +74079,7 @@ }, "es-set-tostringtag": { "version": "2.0.2", + "dev": true, "requires": { "get-intrinsic": "^1.2.2", "has-tostringtag": "^1.0.0", @@ -74013,12 +74088,14 @@ }, "es-shim-unscopables": { "version": "1.0.2", + "dev": true, "requires": { "hasown": "^2.0.0" } }, "es-to-primitive": { "version": "1.2.1", + "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -75998,6 +76075,7 @@ }, "function.prototype.name": { "version": "1.1.6", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -76224,6 +76302,7 @@ }, "get-symbol-description": { "version": "1.0.2", + "dev": true, "requires": { "call-bind": "^1.0.5", "es-errors": "^1.3.0", @@ -76558,6 +76637,7 @@ }, "globalthis": { "version": "1.0.3", + "dev": true, "requires": { "define-properties": "^1.1.3" } @@ -77169,6 +77249,7 @@ }, "html-element-map": { "version": "1.3.1", + "dev": true, "requires": { "array.prototype.filter": "^1.0.0", "call-bind": "^1.0.2" @@ -77245,6 +77326,7 @@ }, "htmlparser2": { "version": "8.0.2", + "dev": true, "requires": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", @@ -77906,7 +77988,8 @@ } }, "is-negative-zero": { - "version": "2.0.2" + "version": "2.0.2", + "dev": true }, "is-number": { "version": "3.0.0", @@ -77997,7 +78080,8 @@ } }, "is-subset": { - "version": "0.1.1" + "version": "0.1.1", + "dev": true }, "is-symbol": { "version": "1.0.4", @@ -78036,6 +78120,7 @@ }, "is-weakref": { "version": "1.0.2", + "dev": true, "requires": { "call-bind": "^1.0.2" } @@ -80986,10 +81071,12 @@ "dev": true }, "lodash.escape": { - "version": "4.0.1" + "version": "4.0.1", + "dev": true }, "lodash.flattendeep": { - "version": "4.4.0" + "version": "4.4.0", + "dev": true }, "lodash.get": { "version": "4.4.2" @@ -84151,7 +84238,8 @@ } }, "moo": { - "version": "0.4.3" + "version": "0.4.3", + "dev": true }, "mousetrap": { "version": "1.6.5" @@ -84264,6 +84352,7 @@ }, "nearley": { "version": "2.18.0", + "dev": true, "requires": { "commander": "^2.19.0", "moo": "^0.4.3", @@ -85320,6 +85409,7 @@ }, "object.entries": { "version": "1.1.6", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -85372,6 +85462,7 @@ }, "object.values": { "version": "1.1.7", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -85941,6 +86032,7 @@ }, "parse5-htmlparser2-tree-adapter": { "version": "7.0.0", + "dev": true, "requires": { "domhandler": "^5.0.2", "parse5": "^7.0.0" @@ -86040,7 +86132,8 @@ "dev": true }, "performance-now": { - "version": "2.1.0" + "version": "2.1.0", + "dev": true }, "periscopic": { "version": "3.1.0", @@ -86878,12 +86971,14 @@ }, "raf": { "version": "3.4.1", + "dev": true, "requires": { "performance-now": "^2.1.0" } }, "railroad-diagrams": { - "version": "1.0.0" + "version": "1.0.0", + "dev": true }, "ramda": { "version": "0.26.1", @@ -86891,6 +86986,7 @@ }, "randexp": { "version": "0.4.6", + "dev": true, "requires": { "discontinuous-range": "1.0.0", "ret": "~0.1.10" @@ -89116,7 +89212,8 @@ } }, "ret": { - "version": "0.1.15" + "version": "0.1.15", + "dev": true }, "retry": { "version": "0.13.1", @@ -89225,6 +89322,7 @@ }, "rst-selector-parser": { "version": "2.2.3", + "dev": true, "requires": { "lodash.flattendeep": "^4.4.0", "nearley": "^2.7.10" @@ -89271,6 +89369,7 @@ }, "safe-array-concat": { "version": "1.1.0", + "dev": true, "requires": { "call-bind": "^1.0.5", "get-intrinsic": "^1.2.2", @@ -89279,7 +89378,8 @@ }, "dependencies": { "isarray": { - "version": "2.0.5" + "version": "2.0.5", + "dev": true } } }, @@ -89295,6 +89395,7 @@ }, "safe-regex-test": { "version": "1.0.3", + "dev": true, "requires": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -89377,7 +89478,8 @@ } }, "semver": { - "version": "5.7.2" + "version": "5.7.2", + "dev": true }, "semver-store": { "version": "0.3.0", @@ -90401,6 +90503,7 @@ }, "string.prototype.trim": { "version": "1.2.8", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -90409,6 +90512,7 @@ }, "string.prototype.trimend": { "version": "1.0.7", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -90417,6 +90521,7 @@ }, "string.prototype.trimstart": { "version": "1.0.7", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -91304,6 +91409,7 @@ }, "typed-array-buffer": { "version": "1.0.1", + "dev": true, "requires": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -91312,6 +91418,7 @@ }, "typed-array-byte-length": { "version": "1.0.0", + "dev": true, "requires": { "call-bind": "^1.0.2", "for-each": "^0.3.3", @@ -91321,6 +91428,7 @@ }, "typed-array-byte-offset": { "version": "1.0.0", + "dev": true, "requires": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", @@ -91331,6 +91439,7 @@ }, "typed-array-length": { "version": "1.0.4", + "dev": true, "requires": { "call-bind": "^1.0.2", "for-each": "^0.3.3", @@ -91372,6 +91481,7 @@ }, "unbox-primitive": { "version": "1.0.2", + "dev": true, "requires": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", diff --git a/superset-frontend/plugins/legacy-plugin-chart-partition/package.json b/superset-frontend/plugins/legacy-plugin-chart-partition/package.json index 47497e90e7715..0802eb23f6104 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-partition/package.json +++ b/superset-frontend/plugins/legacy-plugin-chart-partition/package.json @@ -30,8 +30,10 @@ "peerDependencies": { "@superset-ui/chart-controls": "*", "@superset-ui/core": "*", - "enzyme": "*", - "react": "^16.13.1" + "@testing-library/jest-dom": "^5.17.0", + "@testing-library/react": "^12.1.5", + "react": "^16.13.1", + "react-dom": "^16.13.1" }, "publishConfig": { "access": "public" diff --git a/superset-frontend/plugins/legacy-plugin-chart-partition/test/OptionDescription.test.jsx b/superset-frontend/plugins/legacy-plugin-chart-partition/test/OptionDescription.test.jsx index 248d43e998dd5..8fa9f58bef1f7 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-partition/test/OptionDescription.test.jsx +++ b/superset-frontend/plugins/legacy-plugin-chart-partition/test/OptionDescription.test.jsx @@ -16,9 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -import { shallow } from 'enzyme'; - -import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls'; +import '@testing-library/jest-dom'; +import { screen, render, fireEvent, act } from '@testing-library/react'; +import { ThemeProvider, supersetTheme } from '@superset-ui/core'; import OptionDescription from '../src/OptionDescription'; const defaultProps = { @@ -28,20 +28,35 @@ const defaultProps = { }, }; -describe('OptionDescription', () => { - let wrapper; - let props; +jest.useFakeTimers(); +describe('OptionDescription', () => { beforeEach(() => { - props = { option: { ...defaultProps.option } }; - wrapper = shallow(); + const props = { option: { ...defaultProps.option } }; + render( + + + , + ); }); it('renders an InfoTooltipWithTrigger', () => { - expect(wrapper.find(InfoTooltipWithTrigger)).toHaveLength(1); + const tooltipTrigger = screen.getByLabelText('Show info tooltip'); + expect(tooltipTrigger).toBeInTheDocument(); + + // Perform delayed mouse hovering so tooltip could pop out + fireEvent.mouseOver(tooltipTrigger); + act(() => jest.runAllTimers()); + fireEvent.mouseOut(tooltipTrigger); + + const tooltip = screen.getByRole('tooltip'); + expect(tooltip).toBeInTheDocument(); + expect(tooltip).toHaveTextContent('Description for some option'); }); it('renders a span with the label', () => { - expect(wrapper.find('.option-label').text()).toBe('Some option'); + expect( + screen.getByText('Some option', { selector: 'span' }), + ).toBeInTheDocument(); }); }); From 118abbf546735290e2ca972f53e72fb7907908de Mon Sep 17 00:00:00 2001 From: hainenber Date: Mon, 9 Sep 2024 22:34:47 +0700 Subject: [PATCH 04/12] chore(fe/pkg/ui-core): reduce code duplicate Signed-off-by: hainenber --- .../tooltip/TooltipTable.test.tsx | 78 +++++++------------ 1 file changed, 29 insertions(+), 49 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/test/chart-composition/tooltip/TooltipTable.test.tsx b/superset-frontend/packages/superset-ui-core/test/chart-composition/tooltip/TooltipTable.test.tsx index 08f2b5c98da14..7df4e8c5faa01 100644 --- a/superset-frontend/packages/superset-ui-core/test/chart-composition/tooltip/TooltipTable.test.tsx +++ b/superset-frontend/packages/superset-ui-core/test/chart-composition/tooltip/TooltipTable.test.tsx @@ -20,6 +20,7 @@ import '@testing-library/jest-dom'; import { screen, render } from '@testing-library/react'; import { TooltipTable } from '@superset-ui/core'; +import { CSSProperties } from 'react'; describe('TooltipTable', () => { it('sets className', () => { @@ -34,56 +35,35 @@ describe('TooltipTable', () => { }); it('renders table with content', async () => { - render( - , - ); - let keyCell; - let valueCell; + const data = [ + { + key: 'Cersei', + keyColumn: 'Cersei', + keyStyle: { padding: '10' }, + valueColumn: 2, + valueStyle: { textAlign: 'right' } as CSSProperties, + }, + { + key: 'Jaime', + keyColumn: 'Jaime', + keyStyle: { padding: '10' }, + valueColumn: 1, + valueStyle: { textAlign: 'right' } as CSSProperties, + }, + { + key: 'Tyrion', + keyStyle: { padding: '10' }, + valueColumn: 2, + }, + ]; - keyCell = await screen.findByText('Cersei'); - valueCell = keyCell?.nextSibling as HTMLElement; - expect(keyCell).toBeInTheDocument(); - expect(valueCell?.textContent).toEqual('2'); - expect(valueCell?.getAttribute('style')).toEqual( - 'padding-left: 8px; text-align: right;', - ); + render(); - keyCell = await screen.findByText('Jaime'); - valueCell = keyCell?.nextSibling as HTMLElement; - expect(keyCell).toBeInTheDocument(); - expect(valueCell?.textContent).toEqual('1'); - expect(valueCell?.getAttribute('style')).toEqual( - 'padding-left: 8px; text-align: right;', - ); - - keyCell = await screen.findByText('Tyrion'); - valueCell = keyCell?.nextSibling as HTMLElement; - expect(keyCell).toBeInTheDocument(); - expect(valueCell?.textContent).toEqual('2'); - expect(valueCell?.getAttribute('style')).toEqual( - 'padding-left: 8px; text-align: right;', - ); + for await (const { key, valueColumn } of data) { + const keyCell = await screen.findByText(key); + const valueCell = keyCell?.nextSibling as HTMLElement; + expect(keyCell).toBeInTheDocument(); + expect(valueCell?.textContent).toEqual(String(valueColumn)); + } }); }); From ff5a577631e3c882a55e1e1820bcdd09521f9335 Mon Sep 17 00:00:00 2001 From: hainenber Date: Mon, 9 Sep 2024 23:36:09 +0700 Subject: [PATCH 05/12] refactor(fe/plugin/chart-table): migrate Enzyme-based tests to RTL Signed-off-by: hainenber --- superset-frontend/package-lock.json | 2 - .../plugins/plugin-chart-table/package.json | 1 - .../test/TableChart.test.tsx | 62 ++++++++++--------- .../test/{enzyme.tsx => testHelpers.tsx} | 30 --------- 4 files changed, 34 insertions(+), 61 deletions(-) rename superset-frontend/plugins/plugin-chart-table/test/{enzyme.tsx => testHelpers.tsx} (60%) diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 8e43ca630f309..e387c99dfe3cf 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -56731,7 +56731,6 @@ "dependencies": { "@react-icons/all-files": "^4.1.0", "@types/d3-array": "^2.9.0", - "@types/enzyme": "^3.10.18", "@types/react-table": "^7.7.20", "classnames": "^2.5.1", "d3-array": "^2.4.0", @@ -66691,7 +66690,6 @@ "requires": { "@react-icons/all-files": "^4.1.0", "@types/d3-array": "^2.9.0", - "@types/enzyme": "^3.10.18", "@types/react-table": "^7.7.20", "classnames": "^2.5.1", "d3-array": "^2.4.0", diff --git a/superset-frontend/plugins/plugin-chart-table/package.json b/superset-frontend/plugins/plugin-chart-table/package.json index a56cd724bb747..9bf34bfa180ff 100644 --- a/superset-frontend/plugins/plugin-chart-table/package.json +++ b/superset-frontend/plugins/plugin-chart-table/package.json @@ -26,7 +26,6 @@ "dependencies": { "@react-icons/all-files": "^4.1.0", "@types/d3-array": "^2.9.0", - "@types/enzyme": "^3.10.18", "@types/react-table": "^7.7.20", "classnames": "^2.5.1", "d3-array": "^2.4.0", diff --git a/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx b/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx index df62ba4c1b60e..b21a657b8150e 100644 --- a/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx +++ b/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx @@ -16,14 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import { CommonWrapper } from 'enzyme'; -import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; +import { render, screen } from '@testing-library/react'; +import { ThemeProvider, supersetTheme } from '@superset-ui/core'; import TableChart from '../src/TableChart'; import transformProps from '../src/transformProps'; import DateWithFormatter from '../src/utils/DateWithFormatter'; import testData from './testData'; -import { mount, ProviderWrapper } from './enzyme'; +import { ProviderWrapper } from './testHelpers'; describe('plugin-chart-table', () => { describe('transformProps', () => { @@ -178,39 +178,42 @@ describe('plugin-chart-table', () => { }); describe('TableChart', () => { - let wrap: CommonWrapper; // the ReactDataTable wrapper - let tree: Cheerio; - it('render basic data', () => { - wrap = mount( - , + render( + + , + , ); - tree = wrap.render(); // returns a CheerioWrapper with jQuery-like API - const cells = tree.find('td'); + const firstDataRow = screen.getAllByRole('rowgroup')[1]; + const cells = firstDataRow.querySelectorAll('td'); expect(cells).toHaveLength(12); - expect(cells.eq(0).text()).toEqual('2020-01-01 12:34:56'); - expect(cells.eq(1).text()).toEqual('Michael'); + expect(cells[0]).toHaveTextContent('2020-01-01 12:34:56'); + expect(cells[1]).toHaveTextContent('Michael'); // number is not in `metrics` list, so it should output raw value // (in real world Superset, this would mean the column is used in GROUP BY) - expect(cells.eq(2).text()).toEqual('2467063'); + expect(cells[2]).toHaveTextContent('2467063'); // should not render column with `.` in name as `undefined` - expect(cells.eq(3).text()).toEqual('foo'); - expect(cells.eq(6).text()).toEqual('2467'); - expect(cells.eq(8).text()).toEqual('N/A'); + expect(cells[3]).toHaveTextContent('foo'); + expect(cells[6]).toHaveTextContent('2467'); + expect(cells[8]).toHaveTextContent('N/A'); }); it('render advanced data', () => { - wrap = mount( - , + render( + + , + , ); - tree = wrap.render(); - // should successful rerender with new props - const cells = tree.find('td'); - expect(tree.find('th').eq(1).text()).toEqual('Sum of Num'); - expect(cells.eq(0).text()).toEqual('Michael'); - expect(cells.eq(2).text()).toEqual('12.346%'); - expect(cells.eq(4).text()).toEqual('2.47k'); + const secondColumnHeader = screen.getByText('Sum of Num'); + expect(secondColumnHeader).toBeInTheDocument(); + expect(secondColumnHeader?.getAttribute('data-column-name')).toEqual('1'); + + const firstDataRow = screen.getAllByRole('rowgroup')[1]; + const cells = firstDataRow.querySelectorAll('td'); + expect(cells[0]).toHaveTextContent('Michael'); + expect(cells[2]).toHaveTextContent('12.346%'); + expect(cells[4]).toHaveTextContent('2.47k'); }); it('render advanced data with currencies', () => { @@ -318,9 +321,12 @@ describe('plugin-chart-table', () => { }); it('render empty data', () => { - wrap.setProps({ ...transformProps(testData.empty), sticky: false }); - tree = wrap.render(); - expect(tree.text()).toContain('No records found'); + render( + + , + , + ); + expect(screen.getByText('No records found')).toBeInTheDocument(); }); it('render color with column color formatter', () => { diff --git a/superset-frontend/plugins/plugin-chart-table/test/enzyme.tsx b/superset-frontend/plugins/plugin-chart-table/test/testHelpers.tsx similarity index 60% rename from superset-frontend/plugins/plugin-chart-table/test/enzyme.tsx rename to superset-frontend/plugins/plugin-chart-table/test/testHelpers.tsx index 303fbcc03a56e..95ee707abbdf8 100644 --- a/superset-frontend/plugins/plugin-chart-table/test/enzyme.tsx +++ b/superset-frontend/plugins/plugin-chart-table/test/testHelpers.tsx @@ -16,8 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import { ReactElement } from 'react'; -import { shallow as enzymeShallow, mount as enzymeMount } from 'enzyme'; import { EmotionCacheProvider, createEmotionCache, @@ -29,12 +27,6 @@ const emotionCache = createEmotionCache({ key: 'test', }); -type optionsType = { - wrappingComponentProps?: any; - wrappingComponent?: ReactElement; - context?: any; -}; - export function ProviderWrapper(props: any) { const { children, theme = supersetTheme } = props; return ( @@ -43,25 +35,3 @@ export function ProviderWrapper(props: any) { ); } - -export function mount(component: ReactElement, options: optionsType = {}) { - return enzymeMount(component, { - ...options, - wrappingComponent: ProviderWrapper, - wrappingComponentProps: { - theme: supersetTheme, - ...options?.wrappingComponentProps, - }, - }); -} - -export function shallow(component: ReactElement, options: optionsType = {}) { - return enzymeShallow(component, { - ...options, - wrappingComponent: ProviderWrapper, - wrappingComponentProps: { - theme: supersetTheme, - ...options?.wrappingComponentProps, - }, - }).dive(); -} From 19b0508947da197d1a24e2c25387bcce707f7b7e Mon Sep 17 00:00:00 2001 From: hainenber Date: Tue, 10 Sep 2024 23:51:41 +0700 Subject: [PATCH 06/12] refactor(fe/core/chart-composition/tooltip): migrate TooltipFrame.test.tsx from Enzyme to RTL Signed-off-by: hainenber --- .../tooltip/TooltipFrame.test.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/test/chart-composition/tooltip/TooltipFrame.test.tsx b/superset-frontend/packages/superset-ui-core/test/chart-composition/tooltip/TooltipFrame.test.tsx index f46d09d531b63..5c1cde47213c1 100644 --- a/superset-frontend/packages/superset-ui-core/test/chart-composition/tooltip/TooltipFrame.test.tsx +++ b/superset-frontend/packages/superset-ui-core/test/chart-composition/tooltip/TooltipFrame.test.tsx @@ -17,27 +17,28 @@ * under the License. */ -import { shallow } from 'enzyme'; +import '@testing-library/jest-dom'; import { TooltipFrame } from '@superset-ui/core'; +import { render, screen } from '@testing-library/react'; describe('TooltipFrame', () => { it('sets className', () => { - const wrapper = shallow( + const { container } = render( Hi! , ); - expect(wrapper.hasClass('test-class')).toEqual(true); + expect(screen.getByText('Hi!')).toBeInTheDocument(); + expect(container.querySelector('.test-class')).toBeInTheDocument(); }); it('renders', () => { - const wrapper = shallow( + const { container } = render( Hi! , ); - const span = wrapper.find('span'); - expect(span).toHaveLength(1); - expect(span.text()).toEqual('Hi!'); + expect(container.querySelectorAll('span')).toHaveLength(1); + expect(container.querySelector('span')).toHaveTextContent('Hi!'); }); }); From bbda10bb7b5421007ee6059d0f1c3f701ed4c20b Mon Sep 17 00:00:00 2001 From: hainenber Date: Tue, 10 Sep 2024 23:52:53 +0700 Subject: [PATCH 07/12] refactor(fe/src/explore/comps/controls): use jest.runAllTimers to avoid test timeout Signed-off-by: hainenber --- .../src/explore/components/controls/SelectControl.test.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/superset-frontend/src/explore/components/controls/SelectControl.test.jsx b/superset-frontend/src/explore/components/controls/SelectControl.test.jsx index 4a0c5c7e2fdae..83569615fb883 100644 --- a/superset-frontend/src/explore/components/controls/SelectControl.test.jsx +++ b/superset-frontend/src/explore/components/controls/SelectControl.test.jsx @@ -17,6 +17,7 @@ * under the License. */ import { + act, createEvent, fireEvent, render, @@ -27,7 +28,6 @@ import SelectControl, { innerGetOptions, } from 'src/explore/components/controls/SelectControl'; import userEvent from '@testing-library/user-event'; -import { act } from '@testing-library/react-hooks'; const defaultProps = { choices: [ @@ -109,7 +109,7 @@ describe('SelectControl', () => { // Expect a new option to be selectable. userEvent.click(selectorInput); userEvent.type(selectorInput, 'a new option'); - act(() => jest.advanceTimersByTime(300)); + act(() => jest.runAllTimers()); expect(within(selectorWrapper).getByRole('option')).toHaveTextContent( 'a new option', ); From f05539e0387c60ab0fb5f879234e5832326a5aec Mon Sep 17 00:00:00 2001 From: hainenber Date: Tue, 10 Sep 2024 23:53:38 +0700 Subject: [PATCH 08/12] refactor(fe/src/comp): refactor FacePile.test.tsx from Enzyme to RTL Signed-off-by: hainenber --- .../src/components/FacePile/FacePile.test.tsx | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/superset-frontend/src/components/FacePile/FacePile.test.tsx b/superset-frontend/src/components/FacePile/FacePile.test.tsx index 0e7b4516493fa..ecfb94a2f3aac 100644 --- a/superset-frontend/src/components/FacePile/FacePile.test.tsx +++ b/superset-frontend/src/components/FacePile/FacePile.test.tsx @@ -16,9 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { styledMount as mount } from 'spec/helpers/theming'; - -import { Avatar } from 'src/components'; +import { act, fireEvent, render, screen } from 'spec/helpers/testing-library'; import FacePile from '.'; import { getRandomColor } from './utils'; @@ -28,19 +26,33 @@ const users = [...new Array(10)].map((_, i) => ({ id: i, })); +jest.useFakeTimers(); + describe('FacePile', () => { - const wrapper = mount(); + let container: HTMLElement; + + beforeEach(() => { + ({ container } = render()); + }); it('is a valid element', () => { - expect(wrapper.find(FacePile)).toExist(); + const exposedFaces = container.querySelectorAll('img'); + expect(exposedFaces).toHaveLength(4); + const overflownFaces = screen.getByText('+6'); + expect(overflownFaces).toBeVisible(); + + // Display user info when hovering over one of exposed face in the pile. + fireEvent.mouseEnter(exposedFaces[0]); + act(() => jest.runAllTimers()); + expect(screen.getByRole('tooltip')).toHaveTextContent('user 0'); }); it('renders an Avatar', () => { - expect(wrapper.find(Avatar)).toExist(); + expect(container.querySelector('.ant-avatar')).toBeVisible(); }); it('hides overflow', () => { - expect(wrapper.find(Avatar).length).toBe(5); + expect(container.querySelectorAll('.ant-avatar')).toHaveLength(5); }); }); From 6a6db8f0ffcacd48526b4bae1b789600aded8272 Mon Sep 17 00:00:00 2001 From: hainenber Date: Wed, 11 Sep 2024 00:24:35 +0700 Subject: [PATCH 09/12] fix(ci): refactor test to be sync with change from master branch Signed-off-by: hainenber --- superset-frontend/src/components/FacePile/FacePile.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset-frontend/src/components/FacePile/FacePile.test.tsx b/superset-frontend/src/components/FacePile/FacePile.test.tsx index 70c2411814c73..cfb66ea068909 100644 --- a/superset-frontend/src/components/FacePile/FacePile.test.tsx +++ b/superset-frontend/src/components/FacePile/FacePile.test.tsx @@ -42,7 +42,7 @@ describe('FacePile', () => { }); it('is a valid element', () => { - const exposedFaces = container.querySelectorAll('img'); + const exposedFaces = screen.getAllByText(/U/); expect(exposedFaces).toHaveLength(4); const overflownFaces = screen.getByText('+6'); expect(overflownFaces).toBeVisible(); From 48bdbd30e701d16c1c004aaf876712de49c01691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=90=E1=BB=97=20Tr=E1=BB=8Dng=20H=E1=BA=A3i?= <41283691+hainenber@users.noreply.github.com> Date: Thu, 12 Sep 2024 22:31:45 +0700 Subject: [PATCH 10/12] Apply suggestions from justinpark's code review Co-authored-by: JUST.in DO IT --- .../src/components/FacePile/FacePile.test.tsx | 8 +++++++- .../explore/components/controls/SelectControl.test.jsx | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/superset-frontend/src/components/FacePile/FacePile.test.tsx b/superset-frontend/src/components/FacePile/FacePile.test.tsx index cfb66ea068909..0cb0e143e5b15 100644 --- a/superset-frontend/src/components/FacePile/FacePile.test.tsx +++ b/superset-frontend/src/components/FacePile/FacePile.test.tsx @@ -28,7 +28,13 @@ const users = [...new Array(10)].map((_, i) => ({ id: i, })); -jest.useFakeTimers(); +beforeEach(() => { + jest.useFakeTimers(); +}); + +afterEach(() => { + jest.useRealTimers(); +}); describe('FacePile', () => { let container: HTMLElement; diff --git a/superset-frontend/src/explore/components/controls/SelectControl.test.jsx b/superset-frontend/src/explore/components/controls/SelectControl.test.jsx index 83569615fb883..d848ad2d88ac2 100644 --- a/superset-frontend/src/explore/components/controls/SelectControl.test.jsx +++ b/superset-frontend/src/explore/components/controls/SelectControl.test.jsx @@ -175,7 +175,7 @@ describe('SelectControl', () => { placeholder: 'add something', }); expect( - container.querySelector('[role="option"]'), + screen.queryByRole('option') ).not.toBeInTheDocument(); }); }); From 81ff60cd6f9a6d84960540ed9d52b3fc30a902d0 Mon Sep 17 00:00:00 2001 From: hainenber Date: Thu, 12 Sep 2024 22:35:38 +0700 Subject: [PATCH 11/12] chore: return to real timers after running Jest specs Signed-off-by: hainenber --- .../test/OptionDescription.test.jsx | 8 +++++++- .../explore/components/controls/SelectControl.test.jsx | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/superset-frontend/plugins/legacy-plugin-chart-partition/test/OptionDescription.test.jsx b/superset-frontend/plugins/legacy-plugin-chart-partition/test/OptionDescription.test.jsx index 8fa9f58bef1f7..54aece3919b1c 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-partition/test/OptionDescription.test.jsx +++ b/superset-frontend/plugins/legacy-plugin-chart-partition/test/OptionDescription.test.jsx @@ -28,7 +28,13 @@ const defaultProps = { }, }; -jest.useFakeTimers(); +beforeEach(() => { + jest.useFakeTimers(); +}); + +afterEach(() => { + jest.useRealTimers(); +}); describe('OptionDescription', () => { beforeEach(() => { diff --git a/superset-frontend/src/explore/components/controls/SelectControl.test.jsx b/superset-frontend/src/explore/components/controls/SelectControl.test.jsx index d848ad2d88ac2..ffc7d4abf013f 100644 --- a/superset-frontend/src/explore/components/controls/SelectControl.test.jsx +++ b/superset-frontend/src/explore/components/controls/SelectControl.test.jsx @@ -41,7 +41,13 @@ const defaultProps = { onChange: jest.fn(), }; -jest.useFakeTimers(); +beforeEach(() => { + jest.useFakeTimers(); +}); + +afterEach(() => { + jest.useRealTimers(); +}); const options = [ { value: '1 year ago', label: '1 year ago' }, From 86740c2c686774c5d8d2ac3f9efa029531f77c92 Mon Sep 17 00:00:00 2001 From: hainenber Date: Thu, 12 Sep 2024 22:38:36 +0700 Subject: [PATCH 12/12] chore: fix eslint issues after pulling remote Signed-off-by: hainenber --- .../src/explore/components/controls/SelectControl.test.jsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/superset-frontend/src/explore/components/controls/SelectControl.test.jsx b/superset-frontend/src/explore/components/controls/SelectControl.test.jsx index ffc7d4abf013f..d90d549971918 100644 --- a/superset-frontend/src/explore/components/controls/SelectControl.test.jsx +++ b/superset-frontend/src/explore/components/controls/SelectControl.test.jsx @@ -175,14 +175,12 @@ describe('SelectControl', () => { describe('empty placeholder', () => { describe('withMulti', () => { it('does not show a placeholder if there are no choices', () => { - const container = renderSelectControl({ + renderSelectControl({ choices: [], multi: true, placeholder: 'add something', }); - expect( - screen.queryByRole('option') - ).not.toBeInTheDocument(); + expect(screen.queryByRole('option')).not.toBeInTheDocument(); }); }); describe('withSingleChoice', () => {