This is an automated email from the ASF dual-hosted git repository. rusackas pushed a commit to branch replace-jest-enzyme in repository https://gitbox.apache.org/repos/asf/superset.git
commit 0902783c69f75e1783fc0a84ac7cbb20ab5a55f7 Author: Evan Rusackas <[email protected]> AuthorDate: Fri Feb 7 10:04:18 2025 -0700 bunch o' test touchups --- superset-frontend/package-lock.json | 82 +-- superset-frontend/package.json | 2 +- superset-frontend/spec/helpers/setup.ts | 9 +- .../QueryAutoRefresh/QueryAutoRefresh.test.tsx | 31 +- .../DrillDetail/DrillDetailMenuItems.test.tsx | 14 +- .../Datasource/DatasourceEditor.test.jsx | 2 + .../PropertiesModal/PropertiesModal.test.tsx | 494 ++++++++------- .../FiltersConfigModal/FiltersConfigModal.test.tsx | 702 +++++++++++---------- .../DatasourcePanel/DatasourcePanel.test.tsx | 2 + .../ExploreChartHeader/ExploreChartHeader.test.tsx | 157 ++--- .../ExploreChartPanel/ExploreChartPanel.test.jsx | 2 + .../DndColumnSelect.test.tsx | 2 +- .../DashboardsSubMenu.test.tsx | 2 +- .../UploadDataModel/UploadDataModal.test.tsx | 2 + .../features/rls/RowLevelSecurityModal.test.tsx | 19 +- 15 files changed, 796 insertions(+), 726 deletions(-) diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 75f9f39d16..0d1373ae04 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -1146,22 +1146,22 @@ } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", + "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", + "@babel/helpers": "^7.26.7", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/traverse": "^7.26.7", + "@babel/types": "^7.26.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -1510,14 +1510,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.7" }, "engines": { "node": ">=6.9.0" @@ -1548,12 +1548,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", - "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.5" + "@babel/types": "^7.26.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -2921,13 +2921,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", - "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -3041,15 +3041,15 @@ "license": "MIT" }, "node_modules/@babel/preset-env": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", - "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.7.tgz", + "integrity": "sha512-Ycg2tnXwixaXOVb29rana8HNPgLVBof8qqtNQ9LE22IoyZboQbGSxI6ZySMdW3K5nAe6gu35IaJefUJflhUFTQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/compat-data": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", @@ -3063,7 +3063,7 @@ "@babel/plugin-transform-arrow-functions": "^7.25.9", "@babel/plugin-transform-async-generator-functions": "^7.25.9", "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", @@ -3074,7 +3074,7 @@ "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-for-of": "^7.25.9", "@babel/plugin-transform-function-name": "^7.25.9", @@ -3083,12 +3083,12 @@ "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", @@ -3105,7 +3105,7 @@ "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", "@babel/plugin-transform-template-literals": "^7.25.9", - "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", @@ -3269,16 +3269,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz", - "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", + "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.5", - "@babel/parser": "^7.26.5", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/types": "^7.26.5", + "@babel/types": "^7.26.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -3287,9 +3287,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", - "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", diff --git a/superset-frontend/package.json b/superset-frontend/package.json index ca423f2465..cc9beb02af 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -69,7 +69,7 @@ "prune": "rm -rf ./{packages,plugins}/*/{node_modules,lib,esm,tsconfig.tsbuildinfo,package-lock.json} ./.temp_cache", "storybook": "cross-env NODE_ENV=development BABEL_ENV=development storybook dev -p 6006", "tdd": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=4096\" jest --watch", - "test": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=4096\" jest --max-workers=50%", + "test": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=4096\" jest --max-workers=90% --silent", "type": "tsc --noEmit", "update-maps": "jupyter nbconvert --to notebook --execute --inplace 'plugins/legacy-plugin-chart-country-map/scripts/Country Map GeoJSON Generator.ipynb' -Xfrozen_modules=off", "validate-release": "../RELEASING/validate_this_release.sh" diff --git a/superset-frontend/spec/helpers/setup.ts b/superset-frontend/spec/helpers/setup.ts index 553bd416a7..b889850106 100644 --- a/superset-frontend/spec/helpers/setup.ts +++ b/superset-frontend/spec/helpers/setup.ts @@ -19,7 +19,10 @@ import './shim'; // eslint-disable-next-line no-restricted-syntax -- whole React import is required for mocking React module in tests. import React from 'react'; -import { configure as configureTestingLibrary } from '@testing-library/react'; +import { + cleanup, + configure as configureTestingLibrary, +} from '@testing-library/react'; import { matchers } from '@emotion/jest'; configureTestingLibrary({ @@ -31,3 +34,7 @@ expect.extend(matchers); // Allow JSX tests to have React import readily available global.React = React; + +afterEach(() => { + cleanup(); // Unmount components after each test to prevent memory leaks +}); diff --git a/superset-frontend/src/SqlLab/components/QueryAutoRefresh/QueryAutoRefresh.test.tsx b/superset-frontend/src/SqlLab/components/QueryAutoRefresh/QueryAutoRefresh.test.tsx index 68353ad1f5..d6cf4dafd7 100644 --- a/superset-frontend/src/SqlLab/components/QueryAutoRefresh/QueryAutoRefresh.test.tsx +++ b/superset-frontend/src/SqlLab/components/QueryAutoRefresh/QueryAutoRefresh.test.tsx @@ -194,30 +194,35 @@ describe('QueryAutoRefresh', () => { it('Does NOT Attempt to refresh when given only completed queries', async () => { const store = mockStore(); + + // Mock API response (no queries needing refresh) fetchMock.get(refreshApi, { - result: [ - { - id: runningQuery.id, - status: 'success', - }, - ], + result: [], }); + + console.log('Fetching:', refreshApi); + + // Render the component render( <QueryAutoRefresh - queries={successfulQueries} + queries={successfulQueries} // Only completed queries queriesLastUpdate={queriesLastUpdate} />, { useRedux: true, store }, ); + + // Wait and check if the expected action is dispatched await waitFor( - () => + () => { + console.log('Actions in store:', store.getActions()); expect(store.getActions()).toContainEqual( - expect.objectContaining({ - type: CLEAR_INACTIVE_QUERIES, - }), - ), - { timeout: QUERY_UPDATE_FREQ + 100 }, + expect.objectContaining({ type: CLEAR_INACTIVE_QUERIES }), + ); + }, + { timeout: QUERY_UPDATE_FREQ + 500 }, // Extend timeout slightly ); + + // Ensure API was NOT called expect(fetchMock.calls(refreshApi)).toHaveLength(0); }); }); diff --git a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.test.tsx b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.test.tsx index 002ceb67ad..d99de084ec 100644 --- a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.test.tsx +++ b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.test.tsx @@ -144,7 +144,7 @@ const expectDrillToDetailModal = async ( ) => { const button = screen.getByRole('menuitem', { name: buttonName }); - userEvent.click(button); + await userEvent.click(button); const modal = await screen.findByRole('dialog', { name: `Drill to detail: ${chartName}`, }); @@ -157,7 +157,10 @@ const expectDrillToDetailModal = async ( /** * Menu item should be enabled without explanatory tooltip */ -const expectMenuItemEnabled = async (menuItem: HTMLElement) => { +const expectMenuItemEnabled = async (menuItem: HTMLElement | null) => { + expect(menuItem).not.toBeNull(); // Ensure element exists + if (!menuItem) return; // Prevent further assertions on null + expect(menuItem).toBeInTheDocument(); expect(menuItem).not.toHaveAttribute('aria-disabled'); const tooltipTrigger = within(menuItem).queryByTestId('tooltip-trigger'); @@ -190,7 +193,7 @@ const expectMenuItemDisabled = async ( * "Drill to detail" item should be enabled and open the correct modal */ const expectDrillToDetailEnabled = async () => { - const drillToDetailMenuItem = screen.getByRole('menuitem', { + const drillToDetailMenuItem = await screen.findByRole('menuitem', { name: 'Drill to detail', }); @@ -202,7 +205,7 @@ const expectDrillToDetailEnabled = async () => { * "Drill to detail" item should be present and disabled */ const expectDrillToDetailDisabled = async (tooltipContent?: string) => { - const drillToDetailMenuItem = screen.getByRole('menuitem', { + const drillToDetailMenuItem = screen.findByRole('menuitem', { name: 'Drill to detail', }); @@ -379,6 +382,7 @@ test('context menu for supported chart, dimensions, 1 filter', async () => { }); test('context menu for supported chart, dimensions, filter A', async () => { + jest.setTimeout(10000); const filters = [filterA, filterB]; setupMenu(filters); await expectDrillToDetailByEnabled(); @@ -386,6 +390,7 @@ test('context menu for supported chart, dimensions, filter A', async () => { }); test('context menu for supported chart, dimensions, filter B', async () => { + jest.setTimeout(10000); const filters = [filterA, filterB]; setupMenu(filters); await expectDrillToDetailByEnabled(); @@ -393,6 +398,7 @@ test('context menu for supported chart, dimensions, filter B', async () => { }); test('context menu for supported chart, dimensions, all filters', async () => { + jest.setTimeout(10000); const filters = [filterA, filterB]; setupMenu(filters); await expectDrillToDetailByAll(filters); diff --git a/superset-frontend/src/components/Datasource/DatasourceEditor.test.jsx b/superset-frontend/src/components/Datasource/DatasourceEditor.test.jsx index b49e6ea59b..a92f378dc0 100644 --- a/superset-frontend/src/components/Datasource/DatasourceEditor.test.jsx +++ b/superset-frontend/src/components/Datasource/DatasourceEditor.test.jsx @@ -193,6 +193,8 @@ describe('DatasourceEditor', () => { }); describe('DatasourceEditor RTL', () => { + jest.setTimeout(15000); // Extend timeout to 15s for this test + it('properly renders the metric information', async () => { await asyncRender(props); const metricButton = screen.getByTestId('collection-tab-Metrics'); diff --git a/superset-frontend/src/dashboard/components/PropertiesModal/PropertiesModal.test.tsx b/superset-frontend/src/dashboard/components/PropertiesModal/PropertiesModal.test.tsx index f65f8bb30e..cf665ca3b2 100644 --- a/superset-frontend/src/dashboard/components/PropertiesModal/PropertiesModal.test.tsx +++ b/superset-frontend/src/dashboard/components/PropertiesModal/PropertiesModal.test.tsx @@ -161,292 +161,304 @@ afterAll(() => { fetchMock.restore(); }); -test('should render - FeatureFlag disabled', async () => { - mockedIsFeatureEnabled.mockReturnValue(false); - const props = createProps(); - render(<PropertiesModal {...props} />, { - useRedux: true, +describe('PropertiesModal', () => { + jest.setTimeout(15000); // ✅ Applies to all tests in this suite + + test('should render - FeatureFlag disabled', async () => { + mockedIsFeatureEnabled.mockReturnValue(false); + const props = createProps(); + render(<PropertiesModal {...props} />, { + useRedux: true, + }); + expect( + await screen.findByTestId('dashboard-edit-properties-form'), + ).toBeInTheDocument(); + + expect(screen.getByRole('dialog')).toBeInTheDocument(); + + expect( + screen.getByRole('heading', { name: 'Basic information' }), + ).toBeInTheDocument(); + expect(screen.getByRole('heading', { name: 'Access' })).toBeInTheDocument(); + expect(screen.getByRole('heading', { name: 'Colors' })).toBeInTheDocument(); + expect( + screen.getByRole('heading', { name: 'Advanced' }), + ).toBeInTheDocument(); + expect( + screen.getByRole('heading', { name: 'Certification' }), + ).toBeInTheDocument(); + expect(screen.getAllByRole('heading')).toHaveLength(5); + + expect(screen.getByRole('button', { name: 'Close' })).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'Advanced' }), + ).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument(); + expect(screen.getAllByRole('button')).toHaveLength(4); + + expect(screen.getAllByRole('textbox')).toHaveLength(4); + expect(screen.getByRole('combobox')).toBeInTheDocument(); + + expect(spyColorSchemeControlWrapper).toHaveBeenCalledWith( + expect.objectContaining({ colorScheme: 'supersetColors' }), + {}, + ); }); - expect( - await screen.findByTestId('dashboard-edit-properties-form'), - ).toBeInTheDocument(); - - expect(screen.getByRole('dialog')).toBeInTheDocument(); - - expect( - screen.getByRole('heading', { name: 'Basic information' }), - ).toBeInTheDocument(); - expect(screen.getByRole('heading', { name: 'Access' })).toBeInTheDocument(); - expect(screen.getByRole('heading', { name: 'Colors' })).toBeInTheDocument(); - expect(screen.getByRole('heading', { name: 'Advanced' })).toBeInTheDocument(); - expect( - screen.getByRole('heading', { name: 'Certification' }), - ).toBeInTheDocument(); - expect(screen.getAllByRole('heading')).toHaveLength(5); - - expect(screen.getByRole('button', { name: 'Close' })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Advanced' })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument(); - expect(screen.getAllByRole('button')).toHaveLength(4); - - expect(screen.getAllByRole('textbox')).toHaveLength(4); - expect(screen.getByRole('combobox')).toBeInTheDocument(); - - expect(spyColorSchemeControlWrapper).toHaveBeenCalledWith( - expect.objectContaining({ colorScheme: 'supersetColors' }), - {}, - ); -}); -test('should render - FeatureFlag enabled', async () => { - mockedIsFeatureEnabled.mockReturnValue(true); - const props = createProps(); - render(<PropertiesModal {...props} />, { - useRedux: true, + test('should render - FeatureFlag enabled', async () => { + mockedIsFeatureEnabled.mockReturnValue(true); + const props = createProps(); + render(<PropertiesModal {...props} />, { + useRedux: true, + }); + expect( + await screen.findByTestId('dashboard-edit-properties-form'), + ).toBeInTheDocument(); + + expect( + screen.getByRole('dialog', { name: 'Dashboard properties' }), + ).toBeInTheDocument(); + + expect( + screen.getByRole('heading', { name: 'Basic information' }), + ).toBeInTheDocument(); + expect(screen.getByRole('heading', { name: 'Access' })).toBeInTheDocument(); + expect( + screen.getByRole('heading', { name: 'Advanced' }), + ).toBeInTheDocument(); + expect( + screen.getByRole('heading', { name: 'Certification' }), + ).toBeInTheDocument(); + // Tags will be included since isFeatureFlag always returns true in this test + expect(screen.getByRole('heading', { name: 'Tags' })).toBeInTheDocument(); + expect(screen.getAllByRole('heading')).toHaveLength(5); + + expect(screen.getByRole('button', { name: 'Close' })).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'Advanced' }), + ).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument(); + expect(screen.getAllByRole('button')).toHaveLength(4); + + expect(screen.getAllByRole('textbox')).toHaveLength(4); + expect(screen.getAllByRole('combobox')).toHaveLength(3); + + expect(spyColorSchemeControlWrapper).toHaveBeenCalledWith( + expect.objectContaining({ colorScheme: 'supersetColors' }), + {}, + ); }); - expect( - await screen.findByTestId('dashboard-edit-properties-form'), - ).toBeInTheDocument(); - - expect( - screen.getByRole('dialog', { name: 'Dashboard properties' }), - ).toBeInTheDocument(); - - expect( - screen.getByRole('heading', { name: 'Basic information' }), - ).toBeInTheDocument(); - expect(screen.getByRole('heading', { name: 'Access' })).toBeInTheDocument(); - expect(screen.getByRole('heading', { name: 'Advanced' })).toBeInTheDocument(); - expect( - screen.getByRole('heading', { name: 'Certification' }), - ).toBeInTheDocument(); - // Tags will be included since isFeatureFlag always returns true in this test - expect(screen.getByRole('heading', { name: 'Tags' })).toBeInTheDocument(); - expect(screen.getAllByRole('heading')).toHaveLength(5); - - expect(screen.getByRole('button', { name: 'Close' })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Advanced' })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument(); - expect(screen.getAllByRole('button')).toHaveLength(4); - - expect(screen.getAllByRole('textbox')).toHaveLength(4); - expect(screen.getAllByRole('combobox')).toHaveLength(3); - - expect(spyColorSchemeControlWrapper).toHaveBeenCalledWith( - expect.objectContaining({ colorScheme: 'supersetColors' }), - {}, - ); -}); -test('should open advance', async () => { - mockedIsFeatureEnabled.mockReturnValue(true); - const props = createProps(); - render(<PropertiesModal {...props} />, { - useRedux: true, + test('should open advance', async () => { + mockedIsFeatureEnabled.mockReturnValue(true); + const props = createProps(); + render(<PropertiesModal {...props} />, { + useRedux: true, + }); + expect( + await screen.findByTestId('dashboard-edit-properties-form'), + ).toBeInTheDocument(); + + expect(screen.getAllByRole('textbox')).toHaveLength(4); + expect(screen.getAllByRole('combobox')).toHaveLength(3); + userEvent.click(screen.getByRole('button', { name: 'Advanced' })); + expect(screen.getAllByRole('textbox')).toHaveLength(5); + expect(screen.getAllByRole('combobox')).toHaveLength(3); }); - expect( - await screen.findByTestId('dashboard-edit-properties-form'), - ).toBeInTheDocument(); - - expect(screen.getAllByRole('textbox')).toHaveLength(4); - expect(screen.getAllByRole('combobox')).toHaveLength(3); - userEvent.click(screen.getByRole('button', { name: 'Advanced' })); - expect(screen.getAllByRole('textbox')).toHaveLength(5); - expect(screen.getAllByRole('combobox')).toHaveLength(3); -}); -test('should close modal', async () => { - mockedIsFeatureEnabled.mockReturnValue(true); - const props = createProps(); - render(<PropertiesModal {...props} />, { - useRedux: true, + test('should close modal', async () => { + mockedIsFeatureEnabled.mockReturnValue(true); + const props = createProps(); + render(<PropertiesModal {...props} />, { + useRedux: true, + }); + expect( + await screen.findByTestId('dashboard-edit-properties-form'), + ).toBeInTheDocument(); + + expect(props.onHide).not.toHaveBeenCalled(); + userEvent.click(screen.getByRole('button', { name: 'Cancel' })); + expect(props.onHide).toHaveBeenCalledTimes(1); + userEvent.click(screen.getByRole('button', { name: 'Close' })); + expect(props.onHide).toHaveBeenCalledTimes(2); }); - expect( - await screen.findByTestId('dashboard-edit-properties-form'), - ).toBeInTheDocument(); - - expect(props.onHide).not.toHaveBeenCalled(); - userEvent.click(screen.getByRole('button', { name: 'Cancel' })); - expect(props.onHide).toHaveBeenCalledTimes(1); - userEvent.click(screen.getByRole('button', { name: 'Close' })); - expect(props.onHide).toHaveBeenCalledTimes(2); -}); -test('submitting with onlyApply:false', async () => { - const put = jest.spyOn(SupersetCore.SupersetClient, 'put'); - put.mockResolvedValue({ - json: { - result: { - roles: 'roles', - dashboard_title: 'dashboard_title', - slug: 'slug', - json_metadata: 'json_metadata', - owners: 'owners', + test('submitting with onlyApply:false', async () => { + const put = jest.spyOn(SupersetCore.SupersetClient, 'put'); + put.mockResolvedValue({ + json: { + result: { + roles: 'roles', + dashboard_title: 'dashboard_title', + slug: 'slug', + json_metadata: 'json_metadata', + owners: 'owners', + }, }, - }, - } as any); - mockedIsFeatureEnabled.mockReturnValue(false); - const props = createProps(); - props.onlyApply = false; - render(<PropertiesModal {...props} />, { - useRedux: true, - }); - expect( - await screen.findByTestId('dashboard-edit-properties-form'), - ).toBeInTheDocument(); - - expect(props.onHide).not.toHaveBeenCalled(); - expect(props.onSubmit).not.toHaveBeenCalled(); - - userEvent.click(screen.getByRole('button', { name: 'Save' })); - await waitFor(() => { - expect(props.onSubmit).toHaveBeenCalledTimes(1); - expect(props.onSubmit).toHaveBeenCalledWith({ - certificationDetails: 'Sample certification', - certifiedBy: 'John Doe', - colorScheme: 'supersetColors', - colorNamespace: undefined, - id: 26, - jsonMetadata: expect.anything(), - owners: [], - slug: '', - title: 'COVID Vaccine Dashboard', + } as any); + mockedIsFeatureEnabled.mockReturnValue(false); + const props = createProps(); + props.onlyApply = false; + render(<PropertiesModal {...props} />, { + useRedux: true, + }); + expect( + await screen.findByTestId('dashboard-edit-properties-form'), + ).toBeInTheDocument(); + + expect(props.onHide).not.toHaveBeenCalled(); + expect(props.onSubmit).not.toHaveBeenCalled(); + + userEvent.click(screen.getByRole('button', { name: 'Save' })); + await waitFor(() => { + expect(props.onSubmit).toHaveBeenCalledTimes(1); + expect(props.onSubmit).toHaveBeenCalledWith({ + certificationDetails: 'Sample certification', + certifiedBy: 'John Doe', + colorScheme: 'supersetColors', + colorNamespace: undefined, + id: 26, + jsonMetadata: expect.anything(), + owners: [], + slug: '', + title: 'COVID Vaccine Dashboard', + }); }); }); -}); -test('submitting with onlyApply:true', async () => { - mockedIsFeatureEnabled.mockReturnValue(false); - const props = createProps(); - props.onlyApply = true; - render(<PropertiesModal {...props} />, { - useRedux: true, - }); - expect( - await screen.findByTestId('dashboard-edit-properties-form'), - ).toBeInTheDocument(); + test('submitting with onlyApply:true', async () => { + mockedIsFeatureEnabled.mockReturnValue(false); + const props = createProps(); + props.onlyApply = true; + render(<PropertiesModal {...props} />, { + useRedux: true, + }); + expect( + await screen.findByTestId('dashboard-edit-properties-form'), + ).toBeInTheDocument(); - expect(props.onHide).not.toHaveBeenCalled(); - expect(props.onSubmit).not.toHaveBeenCalled(); + expect(props.onHide).not.toHaveBeenCalled(); + expect(props.onSubmit).not.toHaveBeenCalled(); - userEvent.click(screen.getByRole('button', { name: 'Apply' })); - await waitFor(() => { - expect(props.onSubmit).toHaveBeenCalledTimes(1); + userEvent.click(screen.getByRole('button', { name: 'Apply' })); + await waitFor(() => { + expect(props.onSubmit).toHaveBeenCalledTimes(1); + }); }); -}); -test('Empty "Certified by" should clear "Certification details"', async () => { - const props = createProps(); - const noCertifiedByProps = { - ...props, - certified_by: '', - }; - render(<PropertiesModal {...noCertifiedByProps} />, { - useRedux: true, - }); + test('Empty "Certified by" should clear "Certification details"', async () => { + const props = createProps(); + const noCertifiedByProps = { + ...props, + certified_by: '', + }; + render(<PropertiesModal {...noCertifiedByProps} />, { + useRedux: true, + }); - expect( - screen.getByRole('textbox', { name: 'Certification details' }), - ).toHaveValue(''); -}); + expect( + screen.getByRole('textbox', { name: 'Certification details' }), + ).toHaveValue(''); + }); -test('should show all roles', async () => { - mockedIsFeatureEnabled.mockReturnValue(true); + test('should show all roles', async () => { + mockedIsFeatureEnabled.mockReturnValue(true); - const props = createProps(); - const propsWithDashboardInfo = { ...props, dashboardInfo }; + const props = createProps(); + const propsWithDashboardInfo = { ...props, dashboardInfo }; - const open = () => waitFor(() => userEvent.click(getSelect())); - const getSelect = () => - screen.getByRole('combobox', { name: SupersetCore.t('Roles') }); + const open = () => waitFor(() => userEvent.click(getSelect())); + const getSelect = () => + screen.getByRole('combobox', { name: SupersetCore.t('Roles') }); - const getElementsByClassName = (className: string) => - document.querySelectorAll(className)! as NodeListOf<HTMLElement>; + const getElementsByClassName = (className: string) => + document.querySelectorAll(className)! as NodeListOf<HTMLElement>; - const findAllSelectOptions = () => - waitFor(() => getElementsByClassName('.ant-select-item-option-content')); + const findAllSelectOptions = () => + waitFor(() => getElementsByClassName('.ant-select-item-option-content')); - render(<PropertiesModal {...propsWithDashboardInfo} />, { - useRedux: true, - }); + render(<PropertiesModal {...propsWithDashboardInfo} />, { + useRedux: true, + }); - expect(screen.getAllByRole('combobox')).toHaveLength(3); - expect( - screen.getByRole('combobox', { name: SupersetCore.t('Roles') }), - ).toBeInTheDocument(); + expect(screen.getAllByRole('combobox')).toHaveLength(3); + expect( + screen.getByRole('combobox', { name: SupersetCore.t('Roles') }), + ).toBeInTheDocument(); - await open(); + await open(); - const options = await findAllSelectOptions(); + const options = await findAllSelectOptions(); - expect(options).toHaveLength(5); - expect(options[0]).toHaveTextContent('Admin'); -}); + expect(options).toHaveLength(5); + expect(options[0]).toHaveTextContent('Admin'); + }); -test('should show active owners with dashboard rbac', async () => { - mockedIsFeatureEnabled.mockReturnValue(true); + test('should show active owners with dashboard rbac', async () => { + mockedIsFeatureEnabled.mockReturnValue(true); - const props = createProps(); - const propsWithDashboardInfo = { ...props, dashboardInfo }; + const props = createProps(); + const propsWithDashboardInfo = { ...props, dashboardInfo }; - const open = () => waitFor(() => userEvent.click(getSelect())); - const getSelect = () => - screen.getByRole('combobox', { name: SupersetCore.t('Owners') }); + const open = () => waitFor(() => userEvent.click(getSelect())); + const getSelect = () => + screen.getByRole('combobox', { name: SupersetCore.t('Owners') }); - const getElementsByClassName = (className: string) => - document.querySelectorAll(className)! as NodeListOf<HTMLElement>; + const getElementsByClassName = (className: string) => + document.querySelectorAll(className)! as NodeListOf<HTMLElement>; - const findAllSelectOptions = () => - waitFor(() => getElementsByClassName('.ant-select-item-option-content')); + const findAllSelectOptions = () => + waitFor(() => getElementsByClassName('.ant-select-item-option-content')); - render(<PropertiesModal {...propsWithDashboardInfo} />, { - useRedux: true, - }); + render(<PropertiesModal {...propsWithDashboardInfo} />, { + useRedux: true, + }); - expect(screen.getAllByRole('combobox')).toHaveLength(3); - expect( - screen.getByRole('combobox', { name: SupersetCore.t('Owners') }), - ).toBeInTheDocument(); + expect(screen.getAllByRole('combobox')).toHaveLength(3); + expect( + screen.getByRole('combobox', { name: SupersetCore.t('Owners') }), + ).toBeInTheDocument(); - await open(); + await open(); - const options = await findAllSelectOptions(); + const options = await findAllSelectOptions(); - expect(options).toHaveLength(1); - expect(options[0]).toHaveTextContent('Superset Admin'); -}); + expect(options).toHaveLength(1); + expect(options[0]).toHaveTextContent('Superset Admin'); + }); -test('should show active owners without dashboard rbac', async () => { - mockedIsFeatureEnabled.mockReturnValue(false); + test('should show active owners without dashboard rbac', async () => { + mockedIsFeatureEnabled.mockReturnValue(false); - const props = createProps(); - const propsWithDashboardInfo = { ...props, dashboardInfo }; + const props = createProps(); + const propsWithDashboardInfo = { ...props, dashboardInfo }; - const open = () => waitFor(() => userEvent.click(getSelect())); - const getSelect = () => - screen.getByRole('combobox', { name: SupersetCore.t('Owners') }); + const open = () => waitFor(() => userEvent.click(getSelect())); + const getSelect = () => + screen.getByRole('combobox', { name: SupersetCore.t('Owners') }); - const getElementsByClassName = (className: string) => - document.querySelectorAll(className)! as NodeListOf<HTMLElement>; + const getElementsByClassName = (className: string) => + document.querySelectorAll(className)! as NodeListOf<HTMLElement>; - const findAllSelectOptions = () => - waitFor(() => getElementsByClassName('.ant-select-item-option-content')); + const findAllSelectOptions = () => + waitFor(() => getElementsByClassName('.ant-select-item-option-content')); - render(<PropertiesModal {...propsWithDashboardInfo} />, { - useRedux: true, - }); + render(<PropertiesModal {...propsWithDashboardInfo} />, { + useRedux: true, + }); - expect(screen.getByRole('combobox')).toBeInTheDocument(); - expect( - screen.getByRole('combobox', { name: SupersetCore.t('Owners') }), - ).toBeInTheDocument(); + expect(screen.getByRole('combobox')).toBeInTheDocument(); + expect( + screen.getByRole('combobox', { name: SupersetCore.t('Owners') }), + ).toBeInTheDocument(); - await open(); + await open(); - const options = await findAllSelectOptions(); + const options = await findAllSelectOptions(); - expect(options).toHaveLength(1); - expect(options[0]).toHaveTextContent('Superset Admin'); + expect(options).toHaveLength(1); + expect(options[0]).toHaveTextContent('Superset Admin'); + }); }); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx index 91bfe6c282..b711e06b5b 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx @@ -172,414 +172,420 @@ function queryCheckbox(name: RegExp) { return screen.queryByRole('checkbox', { name }); } -test('renders a value filter type', () => { - defaultRender(); - - userEvent.click(screen.getByText(FILTER_SETTINGS_REGEX)); - - expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument(); - expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument(); - expect(screen.getByText(DATASET_REGEX)).toBeInTheDocument(); - expect(screen.getByText(COLUMN_REGEX)).toBeInTheDocument(); - - expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked(); - expect(getCheckbox(REQUIRED_REGEX)).not.toBeChecked(); - expect(queryCheckbox(DEPENDENCIES_REGEX)).not.toBeInTheDocument(); - expect(getCheckbox(FIRST_VALUE_REGEX)).not.toBeChecked(); - expect(getCheckbox(INVERSE_SELECTION_REGEX)).not.toBeChecked(); - expect(getCheckbox(SEARCH_ALL_REGEX)).not.toBeChecked(); - expect(getCheckbox(PRE_FILTER_REGEX)).not.toBeChecked(); - expect(getCheckbox(SORT_REGEX)).not.toBeChecked(); - - expect(getCheckbox(MULTIPLE_REGEX)).toBeChecked(); -}); +describe('FiltersConfigModal', () => { + jest.setTimeout(15000); -test('renders a numerical range filter type', async () => { - defaultRender(); + test('renders a value filter type', () => { + defaultRender(); - userEvent.click(screen.getByText(VALUE_REGEX)); + userEvent.click(screen.getByText(FILTER_SETTINGS_REGEX)); - await waitFor(() => userEvent.click(screen.getByText(NUMERICAL_RANGE_REGEX))); + expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument(); + expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument(); + expect(screen.getByText(DATASET_REGEX)).toBeInTheDocument(); + expect(screen.getByText(COLUMN_REGEX)).toBeInTheDocument(); - userEvent.click(screen.getByText(FILTER_SETTINGS_REGEX)); + expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked(); + expect(getCheckbox(REQUIRED_REGEX)).not.toBeChecked(); + expect(queryCheckbox(DEPENDENCIES_REGEX)).not.toBeInTheDocument(); + expect(getCheckbox(FIRST_VALUE_REGEX)).not.toBeChecked(); + expect(getCheckbox(INVERSE_SELECTION_REGEX)).not.toBeChecked(); + expect(getCheckbox(SEARCH_ALL_REGEX)).not.toBeChecked(); + expect(getCheckbox(PRE_FILTER_REGEX)).not.toBeChecked(); + expect(getCheckbox(SORT_REGEX)).not.toBeChecked(); - expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument(); - expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument(); - expect(screen.getByText(DATASET_REGEX)).toBeInTheDocument(); - expect(screen.getByText(COLUMN_REGEX)).toBeInTheDocument(); - expect(screen.getByText(REQUIRED_REGEX)).toBeInTheDocument(); + expect(getCheckbox(MULTIPLE_REGEX)).toBeChecked(); + }); - expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked(); - expect(getCheckbox(PRE_FILTER_REGEX)).not.toBeChecked(); + test('renders a numerical range filter type', async () => { + defaultRender(); - expect(queryCheckbox(MULTIPLE_REGEX)).not.toBeInTheDocument(); - expect(queryCheckbox(DEPENDENCIES_REGEX)).not.toBeInTheDocument(); - expect(queryCheckbox(FIRST_VALUE_REGEX)).not.toBeInTheDocument(); - expect(queryCheckbox(INVERSE_SELECTION_REGEX)).not.toBeInTheDocument(); - expect(queryCheckbox(SEARCH_ALL_REGEX)).not.toBeInTheDocument(); - expect(queryCheckbox(SORT_REGEX)).not.toBeInTheDocument(); -}); + userEvent.click(screen.getByText(VALUE_REGEX)); -test('renders a time range filter type', async () => { - defaultRender(); + await waitFor(() => + userEvent.click(screen.getByText(NUMERICAL_RANGE_REGEX)), + ); - userEvent.click(screen.getByText(VALUE_REGEX)); + userEvent.click(screen.getByText(FILTER_SETTINGS_REGEX)); - await waitFor(() => userEvent.click(screen.getByText(TIME_RANGE_REGEX))); + expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument(); + expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument(); + expect(screen.getByText(DATASET_REGEX)).toBeInTheDocument(); + expect(screen.getByText(COLUMN_REGEX)).toBeInTheDocument(); + expect(screen.getByText(REQUIRED_REGEX)).toBeInTheDocument(); - expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument(); - expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument(); - expect(screen.queryByText(DATASET_REGEX)).not.toBeInTheDocument(); - expect(screen.queryByText(COLUMN_REGEX)).not.toBeInTheDocument(); + expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked(); + expect(getCheckbox(PRE_FILTER_REGEX)).not.toBeChecked(); - expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked(); -}); + expect(queryCheckbox(MULTIPLE_REGEX)).not.toBeInTheDocument(); + expect(queryCheckbox(DEPENDENCIES_REGEX)).not.toBeInTheDocument(); + expect(queryCheckbox(FIRST_VALUE_REGEX)).not.toBeInTheDocument(); + expect(queryCheckbox(INVERSE_SELECTION_REGEX)).not.toBeInTheDocument(); + expect(queryCheckbox(SEARCH_ALL_REGEX)).not.toBeInTheDocument(); + expect(queryCheckbox(SORT_REGEX)).not.toBeInTheDocument(); + }); -test('renders a time column filter type', async () => { - defaultRender(); + test('renders a time range filter type', async () => { + defaultRender(); - userEvent.click(screen.getByText(VALUE_REGEX)); + userEvent.click(screen.getByText(VALUE_REGEX)); - await waitFor(() => userEvent.click(screen.getByText(TIME_COLUMN_REGEX))); + await waitFor(() => userEvent.click(screen.getByText(TIME_RANGE_REGEX))); - expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument(); - expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument(); - expect(screen.getByText(DATASET_REGEX)).toBeInTheDocument(); - expect(screen.queryByText(COLUMN_REGEX)).not.toBeInTheDocument(); + expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument(); + expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument(); + expect(screen.queryByText(DATASET_REGEX)).not.toBeInTheDocument(); + expect(screen.queryByText(COLUMN_REGEX)).not.toBeInTheDocument(); - expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked(); -}); + expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked(); + }); -test('renders a time grain filter type', async () => { - defaultRender(); + test('renders a time column filter type', async () => { + defaultRender(); - userEvent.click(screen.getByText(VALUE_REGEX)); + userEvent.click(screen.getByText(VALUE_REGEX)); - await waitFor(() => userEvent.click(screen.getByText(TIME_GRAIN_REGEX))); + await waitFor(() => userEvent.click(screen.getByText(TIME_COLUMN_REGEX))); - expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument(); - expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument(); - expect(screen.getByText(DATASET_REGEX)).toBeInTheDocument(); - expect(screen.queryByText(COLUMN_REGEX)).not.toBeInTheDocument(); + expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument(); + expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument(); + expect(screen.getByText(DATASET_REGEX)).toBeInTheDocument(); + expect(screen.queryByText(COLUMN_REGEX)).not.toBeInTheDocument(); - expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked(); -}); + expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked(); + }); -test('render time filter types as disabled if there are no temporal columns in the dataset', async () => { - defaultRender(noTemporalColumnsState()); + test('renders a time grain filter type', async () => { + defaultRender(); - userEvent.click(screen.getByText(VALUE_REGEX)); + userEvent.click(screen.getByText(VALUE_REGEX)); - const timeRange = await screen.findByText(TIME_RANGE_REGEX); - const timeGrain = await screen.findByText(TIME_GRAIN_REGEX); - const timeColumn = await screen.findByText(TIME_COLUMN_REGEX); - const disabledClass = '.ant-select-item-option-disabled'; + await waitFor(() => userEvent.click(screen.getByText(TIME_GRAIN_REGEX))); - expect(timeRange.closest(disabledClass)).toBeInTheDocument(); - expect(timeGrain.closest(disabledClass)).toBeInTheDocument(); - expect(timeColumn.closest(disabledClass)).toBeInTheDocument(); -}); + expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument(); + expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument(); + expect(screen.getByText(DATASET_REGEX)).toBeInTheDocument(); + expect(screen.queryByText(COLUMN_REGEX)).not.toBeInTheDocument(); -test('validates the name', async () => { - defaultRender(); - userEvent.click(screen.getByRole('button', { name: SAVE_REGEX })); - expect(await screen.findByText(NAME_REQUIRED_REGEX)).toBeInTheDocument(); -}); + expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked(); + }); -test('validates the column', async () => { - defaultRender(); - userEvent.click(screen.getByRole('button', { name: SAVE_REGEX })); - expect(await screen.findByText(COLUMN_REQUIRED_REGEX)).toBeInTheDocument(); -}); + test('render time filter types as disabled if there are no temporal columns in the dataset', async () => { + defaultRender(noTemporalColumnsState()); -// eslint-disable-next-line jest/no-disabled-tests -test.skip('validates the default value', async () => { - defaultRender(noTemporalColumnsState()); - expect(await screen.findByText('birth_names')).toBeInTheDocument(); - userEvent.type(screen.getByRole('combobox'), `Column A{Enter}`); - userEvent.click(getCheckbox(DEFAULT_VALUE_REGEX)); - await waitFor(() => { - expect( - screen.queryByText(FILL_REQUIRED_FIELDS_REGEX), - ).not.toBeInTheDocument(); - }); - expect( - await screen.findByText(DEFAULT_VALUE_REQUIRED_REGEX), - ).toBeInTheDocument(); -}); + userEvent.click(screen.getByText(VALUE_REGEX)); -test('validates the pre-filter value', async () => { - defaultRender(); - userEvent.click(screen.getByText(FILTER_SETTINGS_REGEX)); - userEvent.click(getCheckbox(PRE_FILTER_REGEX)); - expect( - await screen.findByText(PRE_FILTER_REQUIRED_REGEX), - ).toBeInTheDocument(); - // longer timeout to decrease flakiness -}, 10000); - -// eslint-disable-next-line jest/no-disabled-tests -test.skip("doesn't render time range pre-filter if there are no temporal columns in datasource", async () => { - defaultRender(noTemporalColumnsState()); - userEvent.click(screen.getByText(DATASET_REGEX)); - await waitFor(() => { - expect(screen.queryByLabelText('Loading')).not.toBeInTheDocument(); - userEvent.click(screen.getByText('birth_names')); - }); - userEvent.click(screen.getByText(FILTER_SETTINGS_REGEX)); - userEvent.click(getCheckbox(PRE_FILTER_REGEX)); - await waitFor(() => - expect( - screen.queryByText(TIME_RANGE_PREFILTER_REGEX), - ).not.toBeInTheDocument(), - ); -}); + const timeRange = await screen.findByText(TIME_RANGE_REGEX); + const timeGrain = await screen.findByText(TIME_GRAIN_REGEX); + const timeColumn = await screen.findByText(TIME_COLUMN_REGEX); + const disabledClass = '.ant-select-item-option-disabled'; -test('filters are draggable', async () => { - const nativeFilterState = [ - buildNativeFilter('NATIVE_FILTER-1', 'state', ['NATIVE_FILTER-2']), - buildNativeFilter('NATIVE_FILTER-2', 'country', []), - buildNativeFilter('NATIVE_FILTER-3', 'product', []), - ]; - const state = { - ...defaultState(), - dashboardInfo: { - metadata: { native_filter_configuration: nativeFilterState }, - }, - dashboardLayout, - }; - defaultRender(state, { ...props, createNewOnOpen: false }); - const draggables = document.querySelectorAll('div[draggable=true]'); - expect(draggables.length).toBe(3); -}); + expect(timeRange.closest(disabledClass)).toBeInTheDocument(); + expect(timeGrain.closest(disabledClass)).toBeInTheDocument(); + expect(timeColumn.closest(disabledClass)).toBeInTheDocument(); + }); -/* - TODO - adds a new value filter type with all fields filled - adds a new numerical range filter type with all fields filled - adds a new time range filter type with all fields filled - adds a new time column filter type with all fields filled - adds a new time grain filter type with all fields filled - collapsible controls opens by default when it is checked - advanced section opens by default when it has an option checked - disables the default value when default to first item is checked - changes the default value options when the column changes - switches to configuration tab when validation fails - displays cancel message when there are pending operations - do not displays cancel message when there are no pending operations -*/ - -test('deletes a filter', async () => { - const nativeFilterState = [ - buildNativeFilter('NATIVE_FILTER-1', 'state', ['NATIVE_FILTER-2']), - buildNativeFilter('NATIVE_FILTER-2', 'country', []), - buildNativeFilter('NATIVE_FILTER-3', 'product', []), - ]; - const state = { - ...defaultState(), - dashboardInfo: { - metadata: { native_filter_configuration: nativeFilterState }, - }, - dashboardLayout, - }; - const onSave = jest.fn(); + test('validates the name', async () => { + defaultRender(); + userEvent.click(screen.getByRole('button', { name: SAVE_REGEX })); + expect(await screen.findByText(NAME_REQUIRED_REGEX)).toBeInTheDocument(); + }); - defaultRender(state, { - ...props, - createNewOnOpen: false, - onSave, + test('validates the column', async () => { + defaultRender(); + userEvent.click(screen.getByRole('button', { name: SAVE_REGEX })); + expect(await screen.findByText(COLUMN_REQUIRED_REGEX)).toBeInTheDocument(); }); - const removeButtons = screen.getAllByRole('img', { name: 'trash' }); - userEvent.click(removeButtons[2]); - - userEvent.click(screen.getByRole('button', { name: SAVE_REGEX })); - - await waitFor(() => - expect(onSave).toHaveBeenCalledWith( - expect.objectContaining({ - deleted: expect.arrayContaining(['NATIVE_FILTER-3']), - modified: expect.arrayContaining([]), - reordered: expect.arrayContaining([]), - }), - ), - ); -}); -test('deletes a filter including dependencies', async () => { - const nativeFilterState = [ - buildNativeFilter('NATIVE_FILTER-1', 'state', ['NATIVE_FILTER-2']), - buildNativeFilter('NATIVE_FILTER-2', 'country', []), - buildNativeFilter('NATIVE_FILTER-3', 'product', []), - ]; - const state = { - ...defaultState(), - dashboardInfo: { - metadata: { native_filter_configuration: nativeFilterState }, - }, - dashboardLayout, - }; - const onSave = jest.fn(); - defaultRender(state, { - ...props, - createNewOnOpen: false, - onSave, + // eslint-disable-next-line jest/no-disabled-tests + test.skip('validates the default value', async () => { + defaultRender(noTemporalColumnsState()); + expect(await screen.findByText('birth_names')).toBeInTheDocument(); + userEvent.type(screen.getByRole('combobox'), `Column A{Enter}`); + userEvent.click(getCheckbox(DEFAULT_VALUE_REGEX)); + await waitFor(() => { + expect( + screen.queryByText(FILL_REQUIRED_FIELDS_REGEX), + ).not.toBeInTheDocument(); + }); + expect( + await screen.findByText(DEFAULT_VALUE_REQUIRED_REGEX), + ).toBeInTheDocument(); }); - const removeButtons = screen.getAllByRole('img', { name: 'trash' }); - userEvent.click(removeButtons[1]); - userEvent.click(screen.getByRole('button', { name: SAVE_REGEX })); - await waitFor(() => - expect(onSave).toHaveBeenCalledWith( - expect.objectContaining({ - deleted: ['NATIVE_FILTER-2'], - modified: expect.arrayContaining([ - expect.objectContaining({ - id: 'NATIVE_FILTER-1', - }), - ]), - reordered: [], - }), - ), - ); -}); -test('switches the order between two filters', async () => { - const nativeFilterState = [ - buildNativeFilter('NATIVE_FILTER-1', 'state', []), - buildNativeFilter('NATIVE_FILTER-2', 'country', []), - ]; + test('validates the pre-filter value', async () => { + defaultRender(); + userEvent.click(screen.getByText(FILTER_SETTINGS_REGEX)); + userEvent.click(getCheckbox(PRE_FILTER_REGEX)); + expect( + await screen.findByText(PRE_FILTER_REQUIRED_REGEX), + ).toBeInTheDocument(); + // longer timeout to decrease flakiness + }, 10000); + + // eslint-disable-next-line jest/no-disabled-tests + test.skip("doesn't render time range pre-filter if there are no temporal columns in datasource", async () => { + defaultRender(noTemporalColumnsState()); + userEvent.click(screen.getByText(DATASET_REGEX)); + await waitFor(() => { + expect(screen.queryByLabelText('Loading')).not.toBeInTheDocument(); + userEvent.click(screen.getByText('birth_names')); + }); + userEvent.click(screen.getByText(FILTER_SETTINGS_REGEX)); + userEvent.click(getCheckbox(PRE_FILTER_REGEX)); + await waitFor(() => + expect( + screen.queryByText(TIME_RANGE_PREFILTER_REGEX), + ).not.toBeInTheDocument(), + ); + }); - const state = { - ...defaultState(), - dashboardInfo: { - metadata: { native_filter_configuration: nativeFilterState }, - }, - dashboardLayout, - }; + test('filters are draggable', async () => { + const nativeFilterState = [ + buildNativeFilter('NATIVE_FILTER-1', 'state', ['NATIVE_FILTER-2']), + buildNativeFilter('NATIVE_FILTER-2', 'country', []), + buildNativeFilter('NATIVE_FILTER-3', 'product', []), + ]; + const state = { + ...defaultState(), + dashboardInfo: { + metadata: { native_filter_configuration: nativeFilterState }, + }, + dashboardLayout, + }; + defaultRender(state, { ...props, createNewOnOpen: false }); + const draggables = document.querySelectorAll('div[draggable=true]'); + expect(draggables.length).toBe(3); + }); - const onSave = jest.fn(); + /* + TODO + adds a new value filter type with all fields filled + adds a new numerical range filter type with all fields filled + adds a new time range filter type with all fields filled + adds a new time column filter type with all fields filled + adds a new time grain filter type with all fields filled + collapsible controls opens by default when it is checked + advanced section opens by default when it has an option checked + disables the default value when default to first item is checked + changes the default value options when the column changes + switches to configuration tab when validation fails + displays cancel message when there are pending operations + do not displays cancel message when there are no pending operations + */ + + test('deletes a filter', async () => { + const nativeFilterState = [ + buildNativeFilter('NATIVE_FILTER-1', 'state', ['NATIVE_FILTER-2']), + buildNativeFilter('NATIVE_FILTER-2', 'country', []), + buildNativeFilter('NATIVE_FILTER-3', 'product', []), + ]; + const state = { + ...defaultState(), + dashboardInfo: { + metadata: { native_filter_configuration: nativeFilterState }, + }, + dashboardLayout, + }; + const onSave = jest.fn(); + + defaultRender(state, { + ...props, + createNewOnOpen: false, + onSave, + }); + const removeButtons = screen.getAllByRole('img', { name: 'trash' }); + userEvent.click(removeButtons[2]); + + userEvent.click(screen.getByRole('button', { name: SAVE_REGEX })); + + await waitFor(() => + expect(onSave).toHaveBeenCalledWith( + expect.objectContaining({ + deleted: expect.arrayContaining(['NATIVE_FILTER-3']), + modified: expect.arrayContaining([]), + reordered: expect.arrayContaining([]), + }), + ), + ); + }); - defaultRender(state, { - ...props, - createNewOnOpen: false, - onSave, + test('deletes a filter including dependencies', async () => { + const nativeFilterState = [ + buildNativeFilter('NATIVE_FILTER-1', 'state', ['NATIVE_FILTER-2']), + buildNativeFilter('NATIVE_FILTER-2', 'country', []), + buildNativeFilter('NATIVE_FILTER-3', 'product', []), + ]; + const state = { + ...defaultState(), + dashboardInfo: { + metadata: { native_filter_configuration: nativeFilterState }, + }, + dashboardLayout, + }; + const onSave = jest.fn(); + defaultRender(state, { + ...props, + createNewOnOpen: false, + onSave, + }); + const removeButtons = screen.getAllByRole('img', { name: 'trash' }); + userEvent.click(removeButtons[1]); + userEvent.click(screen.getByRole('button', { name: SAVE_REGEX })); + await waitFor(() => + expect(onSave).toHaveBeenCalledWith( + expect.objectContaining({ + deleted: ['NATIVE_FILTER-2'], + modified: expect.arrayContaining([ + expect.objectContaining({ + id: 'NATIVE_FILTER-1', + }), + ]), + reordered: [], + }), + ), + ); }); - const draggableFilters = screen.getAllByRole('tab'); + test('switches the order between two filters', async () => { + const nativeFilterState = [ + buildNativeFilter('NATIVE_FILTER-1', 'state', []), + buildNativeFilter('NATIVE_FILTER-2', 'country', []), + ]; - fireEvent.dragStart(draggableFilters[0]); + const state = { + ...defaultState(), + dashboardInfo: { + metadata: { native_filter_configuration: nativeFilterState }, + }, + dashboardLayout, + }; - fireEvent.dragOver(draggableFilters[1]); + const onSave = jest.fn(); - fireEvent.drop(draggableFilters[1]); + defaultRender(state, { + ...props, + createNewOnOpen: false, + onSave, + }); - fireEvent.dragEnd(draggableFilters[0]); + const draggableFilters = screen.getAllByRole('tab'); - userEvent.click(screen.getByRole('button', { name: SAVE_REGEX })); + fireEvent.dragStart(draggableFilters[0]); - await waitFor(() => - expect(onSave).toHaveBeenCalledWith( - expect.objectContaining({ - deleted: [], - modified: [], - reordered: expect.arrayContaining([ - 'NATIVE_FILTER-2', - 'NATIVE_FILTER-1', - ]), - }), - ), - ); -}); + fireEvent.dragOver(draggableFilters[1]); -test('rearranges three filters and deletes one of them', async () => { - const nativeFilterState = [ - buildNativeFilter('NATIVE_FILTER-1', 'state', []), - buildNativeFilter('NATIVE_FILTER-2', 'country', []), - buildNativeFilter('NATIVE_FILTER-3', 'product', []), - ]; - - const state = { - ...defaultState(), - dashboardInfo: { - metadata: { native_filter_configuration: nativeFilterState }, - }, - dashboardLayout, - }; + fireEvent.drop(draggableFilters[1]); + + fireEvent.dragEnd(draggableFilters[0]); - const onSave = jest.fn(); + userEvent.click(screen.getByRole('button', { name: SAVE_REGEX })); - defaultRender(state, { - ...props, - createNewOnOpen: false, - onSave, + await waitFor(() => + expect(onSave).toHaveBeenCalledWith( + expect.objectContaining({ + deleted: [], + modified: [], + reordered: expect.arrayContaining([ + 'NATIVE_FILTER-2', + 'NATIVE_FILTER-1', + ]), + }), + ), + ); }); - const draggableFilters = screen.getAllByRole('tab'); - const deleteButtons = screen.getAllByRole('img', { name: 'trash' }); - userEvent.click(deleteButtons[1]); - - fireEvent.dragStart(draggableFilters[0]); - fireEvent.dragOver(draggableFilters[2]); - fireEvent.drop(draggableFilters[2]); - fireEvent.dragEnd(draggableFilters[0]); - - userEvent.click(screen.getByRole('button', { name: SAVE_REGEX })); - - await waitFor(() => - expect(onSave).toHaveBeenCalledWith( - expect.objectContaining({ - modified: [], - deleted: ['NATIVE_FILTER-2'], - reordered: expect.arrayContaining([ - 'NATIVE_FILTER-2', - 'NATIVE_FILTER-3', - 'NATIVE_FILTER-1', - ]), - }), - ), - ); -}); + test('rearranges three filters and deletes one of them', async () => { + const nativeFilterState = [ + buildNativeFilter('NATIVE_FILTER-1', 'state', []), + buildNativeFilter('NATIVE_FILTER-2', 'country', []), + buildNativeFilter('NATIVE_FILTER-3', 'product', []), + ]; + + const state = { + ...defaultState(), + dashboardInfo: { + metadata: { native_filter_configuration: nativeFilterState }, + }, + dashboardLayout, + }; -test('modifies the name of a filter', async () => { - jest.useFakeTimers(); - const nativeFilterState = [ - buildNativeFilter('NATIVE_FILTER-1', 'state', []), - buildNativeFilter('NATIVE_FILTER-2', 'country', []), - ]; - - const state = { - ...defaultState(), - dashboardInfo: { - metadata: { native_filter_configuration: nativeFilterState }, - }, - dashboardLayout, - }; + const onSave = jest.fn(); - const onSave = jest.fn(); + defaultRender(state, { + ...props, + createNewOnOpen: false, + onSave, + }); - defaultRender(state, { - ...props, - createNewOnOpen: false, - onSave, + const draggableFilters = screen.getAllByRole('tab'); + const deleteButtons = screen.getAllByRole('img', { name: 'trash' }); + userEvent.click(deleteButtons[1]); + + fireEvent.dragStart(draggableFilters[0]); + fireEvent.dragOver(draggableFilters[2]); + fireEvent.drop(draggableFilters[2]); + fireEvent.dragEnd(draggableFilters[0]); + + userEvent.click(screen.getByRole('button', { name: SAVE_REGEX })); + + await waitFor(() => + expect(onSave).toHaveBeenCalledWith( + expect.objectContaining({ + modified: [], + deleted: ['NATIVE_FILTER-2'], + reordered: expect.arrayContaining([ + 'NATIVE_FILTER-2', + 'NATIVE_FILTER-3', + 'NATIVE_FILTER-1', + ]), + }), + ), + ); }); - const filterNameInput = screen.getByRole('textbox', { - name: FILTER_NAME_REGEX, - }); + test('modifies the name of a filter', async () => { + jest.useFakeTimers(); + const nativeFilterState = [ + buildNativeFilter('NATIVE_FILTER-1', 'state', []), + buildNativeFilter('NATIVE_FILTER-2', 'country', []), + ]; + + const state = { + ...defaultState(), + dashboardInfo: { + metadata: { native_filter_configuration: nativeFilterState }, + }, + dashboardLayout, + }; + + const onSave = jest.fn(); - userEvent.clear(filterNameInput); - userEvent.type(filterNameInput, 'New Filter Name'); + defaultRender(state, { + ...props, + createNewOnOpen: false, + onSave, + }); - jest.runAllTimers(); + const filterNameInput = screen.getByRole('textbox', { + name: FILTER_NAME_REGEX, + }); - userEvent.click(screen.getByRole('button', { name: SAVE_REGEX })); + userEvent.clear(filterNameInput); + userEvent.type(filterNameInput, 'New Filter Name'); - await waitFor(() => - expect(onSave).toHaveBeenCalledWith( - expect.objectContaining({ - modified: expect.arrayContaining([ - expect.objectContaining({ name: 'New Filter Name' }), - ]), - }), - ), - ); + jest.runAllTimers(); + + userEvent.click(screen.getByRole('button', { name: SAVE_REGEX })); + + await waitFor(() => + expect(onSave).toHaveBeenCalledWith( + expect.objectContaining({ + modified: expect.arrayContaining([ + expect.objectContaining({ name: 'New Filter Name' }), + ]), + }), + ), + ); + }); }); diff --git a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx index bdd93d38f0..c2d234e139 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx @@ -111,6 +111,7 @@ test('should display items in controls', async () => { }); test('should render the metrics', async () => { + jest.setTimeout(10000); render( <ExploreContainer> <DatasourcePanel {...props} /> @@ -153,6 +154,7 @@ test('should render 0 search results', async () => { }); test('should search and render matching columns', async () => { + jest.setTimeout(10000); render( <ExploreContainer> <DatasourcePanel {...props} /> diff --git a/superset-frontend/src/explore/components/ExploreChartHeader/ExploreChartHeader.test.tsx b/superset-frontend/src/explore/components/ExploreChartHeader/ExploreChartHeader.test.tsx index 3735f51cfc..e316d52f2d 100644 --- a/superset-frontend/src/explore/components/ExploreChartHeader/ExploreChartHeader.test.tsx +++ b/superset-frontend/src/explore/components/ExploreChartHeader/ExploreChartHeader.test.tsx @@ -127,96 +127,105 @@ fetchMock.post( sendAsJson: false, }, ); +describe('ExploreChartHeader', () => { + jest.setTimeout(15000); // ✅ Applies to all tests in this suite -test('Cancelling changes to the properties should reset previous properties', async () => { - const props = createProps(); - render(<ExploreHeader {...props} />, { useRedux: true }); - const newChartName = 'New chart name'; - const prevChartName = props.slice_name; - expect( - await screen.findByText(/add the name of the chart/i), - ).toBeInTheDocument(); + test('Cancelling changes to the properties should reset previous properties', async () => { + const props = createProps(); + render(<ExploreHeader {...props} />, { useRedux: true }); + const newChartName = 'New chart name'; + const prevChartName = props.slice_name; + expect( + await screen.findByText(/add the name of the chart/i), + ).toBeInTheDocument(); - userEvent.click(screen.getByLabelText('Menu actions trigger')); - userEvent.click(screen.getByText('Edit chart properties')); + userEvent.click(screen.getByLabelText('Menu actions trigger')); + userEvent.click(screen.getByText('Edit chart properties')); - const nameInput = await screen.findByRole('textbox', { name: 'Name' }); + const nameInput = await screen.findByRole('textbox', { name: 'Name' }); - userEvent.clear(nameInput); - userEvent.type(nameInput, newChartName); + userEvent.clear(nameInput); + userEvent.type(nameInput, newChartName); - expect(screen.getByDisplayValue(newChartName)).toBeInTheDocument(); + expect(screen.getByDisplayValue(newChartName)).toBeInTheDocument(); - userEvent.click(screen.getByRole('button', { name: 'Cancel' })); + userEvent.click(screen.getByRole('button', { name: 'Cancel' })); - userEvent.click(screen.getByLabelText('Menu actions trigger')); - userEvent.click(screen.getByText('Edit chart properties')); + userEvent.click(screen.getByLabelText('Menu actions trigger')); + userEvent.click(screen.getByText('Edit chart properties')); - expect(await screen.findByDisplayValue(prevChartName)).toBeInTheDocument(); -}); + expect(await screen.findByDisplayValue(prevChartName)).toBeInTheDocument(); + }); -test('renders the metadata bar when saved', async () => { - const props = createProps({ showTitlePanelItems: true }); - render(<ExploreHeader {...props} />, { useRedux: true }); - expect(await screen.findByText('Added to 1 dashboard')).toBeInTheDocument(); - expect(await screen.findByText('Simple description')).toBeInTheDocument(); - expect(await screen.findByText('John Doe')).toBeInTheDocument(); - expect(await screen.findByText('2 days ago')).toBeInTheDocument(); -}); + test('renders the metadata bar when saved', async () => { + const props = createProps({ showTitlePanelItems: true }); + render(<ExploreHeader {...props} />, { useRedux: true }); + expect(await screen.findByText('Added to 1 dashboard')).toBeInTheDocument(); + expect(await screen.findByText('Simple description')).toBeInTheDocument(); + expect(await screen.findByText('John Doe')).toBeInTheDocument(); + expect(await screen.findByText('2 days ago')).toBeInTheDocument(); + }); -test('Changes "Added to X dashboards" to plural when more than 1 dashboard', async () => { - const props = createProps({ showTitlePanelItems: true }); - render( - <ExploreHeader - {...props} - metadata={{ - ...props.metadata, - dashboards: [ - { id: 1, dashboard_title: 'Test' }, - { id: 2, dashboard_title: 'Test2' }, - ], - }} - />, - { useRedux: true }, - ); - expect(await screen.findByText('Added to 2 dashboards')).toBeInTheDocument(); -}); + test('Changes "Added to X dashboards" to plural when more than 1 dashboard', async () => { + const props = createProps({ showTitlePanelItems: true }); + render( + <ExploreHeader + {...props} + metadata={{ + ...props.metadata, + dashboards: [ + { id: 1, dashboard_title: 'Test' }, + { id: 2, dashboard_title: 'Test2' }, + ], + }} + />, + { useRedux: true }, + ); + expect( + await screen.findByText('Added to 2 dashboards'), + ).toBeInTheDocument(); + }); -test('does not render the metadata bar when not saved', async () => { - const props = createProps({ showTitlePanelItems: true, slice: null }); - render(<ExploreHeader {...props} />, { useRedux: true }); - await waitFor(() => - expect(screen.queryByText('Added to 1 dashboard')).not.toBeInTheDocument(), - ); -}); + test('does not render the metadata bar when not saved', async () => { + const props = createProps({ showTitlePanelItems: true, slice: null }); + render(<ExploreHeader {...props} />, { useRedux: true }); + await waitFor(() => + expect( + screen.queryByText('Added to 1 dashboard'), + ).not.toBeInTheDocument(), + ); + }); -test('Save chart', async () => { - const setSaveChartModalVisibility = jest.spyOn( - saveModalActions, - 'setSaveChartModalVisibility', - ); - const props = createProps(); - render(<ExploreHeader {...props} />, { useRedux: true }); - expect(await screen.findByText('Save')).toBeInTheDocument(); - userEvent.click(screen.getByText('Save')); - expect(setSaveChartModalVisibility).toHaveBeenCalledWith(true); - setSaveChartModalVisibility.mockClear(); -}); + test('Save chart', async () => { + const setSaveChartModalVisibility = jest.spyOn( + saveModalActions, + 'setSaveChartModalVisibility', + ); + const props = createProps(); + render(<ExploreHeader {...props} />, { useRedux: true }); + expect(await screen.findByText('Save')).toBeInTheDocument(); + userEvent.click(screen.getByText('Save')); + expect(setSaveChartModalVisibility).toHaveBeenCalledWith(true); + setSaveChartModalVisibility.mockClear(); + }); -test('Save disabled', async () => { - const setSaveChartModalVisibility = jest.spyOn( - saveModalActions, - 'setSaveChartModalVisibility', - ); - const props = createProps(); - render(<ExploreHeader {...props} saveDisabled />, { useRedux: true }); - expect(await screen.findByText('Save')).toBeInTheDocument(); - userEvent.click(screen.getByText('Save')); - expect(setSaveChartModalVisibility).not.toHaveBeenCalled(); - setSaveChartModalVisibility.mockClear(); + test('Save disabled', async () => { + const setSaveChartModalVisibility = jest.spyOn( + saveModalActions, + 'setSaveChartModalVisibility', + ); + const props = createProps(); + render(<ExploreHeader {...props} saveDisabled />, { useRedux: true }); + expect(await screen.findByText('Save')).toBeInTheDocument(); + userEvent.click(screen.getByText('Save')); + expect(setSaveChartModalVisibility).not.toHaveBeenCalled(); + setSaveChartModalVisibility.mockClear(); + }); }); describe('Additional actions tests', () => { + jest.setTimeout(15000); // ✅ Applies to all tests in this suite + test('Should render a button', async () => { const props = createProps(); render(<ExploreHeader {...props} />, { useRedux: true }); diff --git a/superset-frontend/src/explore/components/ExploreChartPanel/ExploreChartPanel.test.jsx b/superset-frontend/src/explore/components/ExploreChartPanel/ExploreChartPanel.test.jsx index fce164b598..05352607b6 100644 --- a/superset-frontend/src/explore/components/ExploreChartPanel/ExploreChartPanel.test.jsx +++ b/superset-frontend/src/explore/components/ExploreChartPanel/ExploreChartPanel.test.jsx @@ -63,6 +63,8 @@ const createProps = (overrides = {}) => ({ }); describe('ChartContainer', () => { + jest.setTimeout(10000); + test('renders when vizType is line', () => { const props = createProps(); expect(isValidElement(<ChartContainer {...props} />)).toBe(true); diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.test.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.test.tsx index a1ce875c01..b6e91c2fb3 100644 --- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.test.tsx +++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.test.tsx @@ -63,7 +63,7 @@ test('renders adhoc column', async () => { ); expect(await screen.findByText('adhoc column')).toBeVisible(); expect(screen.getByLabelText('calculator')).toBeVisible(); -}); +}, 10000); test('warn selected custom metric when metric gets removed from dataset', async () => { const columnValues = ['column1', 'column2']; diff --git a/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/DashboardsSubMenu.test.tsx b/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/DashboardsSubMenu.test.tsx index 86880927e6..20ab141119 100644 --- a/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/DashboardsSubMenu.test.tsx +++ b/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/DashboardsSubMenu.test.tsx @@ -50,7 +50,7 @@ test('renders a submenu', async () => { test('renders a submenu with search', async () => { asyncRender(20); expect(await screen.findByPlaceholderText('Search')).toBeInTheDocument(); -}); +}, 10000); test('displays a searched value', async () => { asyncRender(20); diff --git a/superset-frontend/src/features/databases/UploadDataModel/UploadDataModal.test.tsx b/superset-frontend/src/features/databases/UploadDataModel/UploadDataModal.test.tsx index 5a3958e00a..61bd046567 100644 --- a/superset-frontend/src/features/databases/UploadDataModel/UploadDataModal.test.tsx +++ b/superset-frontend/src/features/databases/UploadDataModel/UploadDataModal.test.tsx @@ -564,6 +564,7 @@ test('Columnar, does not render the rows', () => { }); test('database and schema are correctly populated', async () => { + jest.setTimeout(10000); render(<UploadDataModal {...csvProps} />, { useRedux: true, }); @@ -611,6 +612,7 @@ test('form without required fields', async () => { }); test('CSV form post', async () => { + jest.setTimeout(10000); render(<UploadDataModal {...csvProps} />, { useRedux: true, }); diff --git a/superset-frontend/src/features/rls/RowLevelSecurityModal.test.tsx b/superset-frontend/src/features/rls/RowLevelSecurityModal.test.tsx index 9d50e70686..76a0289982 100644 --- a/superset-frontend/src/features/rls/RowLevelSecurityModal.test.tsx +++ b/superset-frontend/src/features/rls/RowLevelSecurityModal.test.tsx @@ -226,6 +226,7 @@ describe('Rule modal', () => { }); it('Does not allow to create rule without name, tables and clause', async () => { + jest.setTimeout(10000); await renderAndWait(addNewRuleDefaultProps); const addButton = screen.getByRole('button', { name: /add/i }); @@ -275,6 +276,22 @@ describe('Rule modal', () => { const addButton = screen.getByRole('button', { name: /save/i }); await waitFor(() => userEvent.click(addButton)); - expect(fetchMock.calls(putRuleEndpoint)).toHaveLength(4); + + const actualCalls = fetchMock.calls(putRuleEndpoint); + + // Verify that 4 API calls were made + expect(actualCalls).toHaveLength(4); + + // Check each request individually + expect(actualCalls[0]?.[1]?.method).toBe('GET'); // Ensure method is PUT + expect(actualCalls[1]?.[1]?.method).toBe('PUT'); + expect(actualCalls[2]?.[1]?.method).toBe('PUT'); + expect(actualCalls[3]?.[1]?.method).toBe('PUT'); + + // Check payloads if necessary + expect(actualCalls[0]?.[1]?.body).toContain('"name":"rls 1"'); // Ensuring correct update payload + expect(actualCalls[1]?.[1]?.body).toContain('"filter_type":"Base"'); + expect(actualCalls[2]?.[1]?.body).toContain('"group_key":"g1"'); + expect(actualCalls[3]?.[1]?.body).toContain('"clause":"gender=\\"boy\\""'); }); });
