You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ju...@apache.org on 2024/02/07 17:17:41 UTC

(superset) branch master updated: chore(dashboard): migrate enzyme to RTL (#26260)

This is an automated email from the ASF dual-hosted git repository.

justinpark pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new f8c75ca50b chore(dashboard): migrate enzyme to RTL (#26260)
f8c75ca50b is described below

commit f8c75ca50b154a90460ea17263c2ad882c43daa6
Author: JUST.in DO IT <ju...@airbnb.com>
AuthorDate: Wed Feb 7 09:17:34 2024 -0800

    chore(dashboard): migrate enzyme to RTL (#26260)
---
 .../dashboard/components/DashboardGrid.test.jsx    | 125 +++++----
 .../components/FiltersBadge/FiltersBadge.test.tsx  | 197 ++++++-------
 .../PropertiesModal/PropertiesModal.test.jsx       |  22 +-
 .../components/RefreshIntervalModal.test.tsx       | 210 ++++++--------
 .../components/gridComponents/Chart.test.jsx       | 305 +++++++++++---------
 .../components/gridComponents/Column.test.jsx      | 275 +++++++++---------
 .../components/gridComponents/Row.test.jsx         | 227 ++++++++-------
 .../components/gridComponents/Tabs.test.jsx        | 311 ++++++++++-----------
 .../gridComponents/new/NewColumn.test.jsx          |  37 +--
 .../gridComponents/new/NewDivider.test.jsx         |  37 +--
 .../gridComponents/new/NewHeader.test.jsx          |  37 +--
 .../components/gridComponents/new/NewRow.test.jsx  |  37 +--
 .../components/gridComponents/new/NewTabs.test.jsx |  37 +--
 .../dashboard/components/menu/HoverMenu.test.tsx   |  10 +-
 .../components/menu/WithPopoverMenu.test.jsx       | 107 ++++---
 .../FiltersConfigModal/FiltersConfigModal.tsx      |   2 +-
 .../FiltersConfigModal/NativeFiltersModal.test.tsx | 127 +++------
 17 files changed, 1036 insertions(+), 1067 deletions(-)

diff --git a/superset-frontend/src/dashboard/components/DashboardGrid.test.jsx b/superset-frontend/src/dashboard/components/DashboardGrid.test.jsx
index c954ecff7b..ea24a2483a 100644
--- a/superset-frontend/src/dashboard/components/DashboardGrid.test.jsx
+++ b/superset-frontend/src/dashboard/components/DashboardGrid.test.jsx
@@ -17,77 +17,94 @@
  * under the License.
  */
 import React from 'react';
-import { shallow } from 'enzyme';
-import sinon from 'sinon';
+import { fireEvent, render } from 'spec/helpers/testing-library';
 
-import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
 import DashboardGrid from 'src/dashboard/components/DashboardGrid';
-import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
 import newComponentFactory from 'src/dashboard/util/newComponentFactory';
 
 import { DASHBOARD_GRID_TYPE } from 'src/dashboard/util/componentTypes';
 import { GRID_COLUMN_COUNT } from 'src/dashboard/util/constants';
 
-describe('DashboardGrid', () => {
-  const props = {
-    depth: 1,
-    editMode: false,
-    gridComponent: {
-      ...newComponentFactory(DASHBOARD_GRID_TYPE),
-      children: ['a'],
-    },
-    handleComponentDrop() {},
-    resizeComponent() {},
-    width: 500,
-    isComponentVisible: true,
-    setDirectPathToChild() {},
-  };
+const args = { id: 'id', widthMultiple: 1, heightMultiple: 3 };
 
-  function setup(overrideProps) {
-    const wrapper = shallow(<DashboardGrid {...props} {...overrideProps} />);
-    return wrapper;
-  }
+jest.mock(
+  'src/dashboard/containers/DashboardComponent',
+  () =>
+    ({ onResizeStart, onResizeStop }) =>
+      (
+        <button
+          type="button"
+          data-test="mock-dashboard-component"
+          onClick={() => onResizeStart()}
+          onBlur={() => onResizeStop(args)}
+        >
+          Mock
+        </button>
+      ),
+);
 
-  it('should render a div with class "dashboard-grid"', () => {
-    const wrapper = setup();
-    expect(wrapper.find('.dashboard-grid')).toExist();
-  });
+const props = {
+  depth: 1,
+  editMode: false,
+  gridComponent: {
+    ...newComponentFactory(DASHBOARD_GRID_TYPE),
+    children: ['a'],
+  },
+  handleComponentDrop() {},
+  resizeComponent() {},
+  width: 500,
+  isComponentVisible: true,
+  setDirectPathToChild() {},
+};
 
-  it('should render one DashboardComponent for each gridComponent child', () => {
-    const wrapper = setup({
-      gridComponent: { ...props.gridComponent, children: ['a', 'b'] },
-    });
-    expect(wrapper.find(DashboardComponent)).toHaveLength(2);
+function setup(overrideProps) {
+  return render(<DashboardGrid {...props} {...overrideProps} />, {
+    useRedux: true,
+    useDnd: true,
   });
+}
+
+test('should render a div with class "dashboard-grid"', () => {
+  const { container } = setup();
+  expect(container.querySelector('.dashboard-grid')).toBeInTheDocument();
+});
 
-  it('should render two empty DragDroppables in editMode to increase the drop target zone', () => {
-    const viewMode = setup({ editMode: false });
-    const editMode = setup({ editMode: true });
-    expect(viewMode.find(DragDroppable)).toHaveLength(0);
-    expect(editMode.find(DragDroppable)).toHaveLength(2);
+test('should render one DashboardComponent for each gridComponent child', () => {
+  const { getAllByTestId } = setup({
+    gridComponent: { ...props.gridComponent, children: ['a', 'b'] },
   });
+  expect(getAllByTestId('mock-dashboard-component')).toHaveLength(2);
+});
 
-  it('should render grid column guides when resizing', () => {
-    const wrapper = setup({ editMode: true });
-    expect(wrapper.find('.grid-column-guide')).not.toExist();
+test('should render two empty DragDroppables in editMode to increase the drop target zone', () => {
+  const { queryAllByTestId } = setup({ editMode: false });
+  expect(queryAllByTestId('dragdroppable-object').length).toEqual(0);
+  const { getAllByTestId } = setup({ editMode: true });
+  expect(getAllByTestId('dragdroppable-object').length).toEqual(2);
+});
 
-    wrapper.setState({ isResizing: true });
+test('should render grid column guides when resizing', () => {
+  const { container, getAllByTestId } = setup({ editMode: true });
+  expect(container.querySelector('.grid-column-guide')).not.toBeInTheDocument();
 
-    expect(wrapper.find('.grid-column-guide')).toHaveLength(GRID_COLUMN_COUNT);
-  });
+  // map handleResizeStart to the onClick prop of the mock DashboardComponent
+  fireEvent.click(getAllByTestId('mock-dashboard-component')[0]);
+
+  expect(container.querySelectorAll('.grid-column-guide')).toHaveLength(
+    GRID_COLUMN_COUNT,
+  );
+});
 
-  it('should call resizeComponent when a child DashboardComponent calls resizeStop', () => {
-    const resizeComponent = sinon.spy();
-    const args = { id: 'id', widthMultiple: 1, heightMultiple: 3 };
-    const wrapper = setup({ resizeComponent });
-    const dashboardComponent = wrapper.find(DashboardComponent).first();
-    dashboardComponent.prop('onResizeStop')(args);
+test('should call resizeComponent when a child DashboardComponent calls resizeStop', () => {
+  const resizeComponent = jest.fn();
+  const { getAllByTestId } = setup({ resizeComponent });
+  const dashboardComponent = getAllByTestId('mock-dashboard-component')[0];
+  fireEvent.blur(dashboardComponent);
 
-    expect(resizeComponent.callCount).toBe(1);
-    expect(resizeComponent.getCall(0).args[0]).toEqual({
-      id: 'id',
-      width: 1,
-      height: 3,
-    });
+  expect(resizeComponent).toHaveBeenCalledTimes(1);
+  expect(resizeComponent).toHaveBeenCalledWith({
+    id: 'id',
+    width: 1,
+    height: 3,
   });
 });
diff --git a/superset-frontend/src/dashboard/components/FiltersBadge/FiltersBadge.test.tsx b/superset-frontend/src/dashboard/components/FiltersBadge/FiltersBadge.test.tsx
index c5e0d0df9c..cc0cad0896 100644
--- a/superset-frontend/src/dashboard/components/FiltersBadge/FiltersBadge.test.tsx
+++ b/superset-frontend/src/dashboard/components/FiltersBadge/FiltersBadge.test.tsx
@@ -17,11 +17,8 @@
  * under the License.
  */
 import React from 'react';
-import { shallow } from 'enzyme';
-import { Provider } from 'react-redux';
 import { Store } from 'redux';
-import * as SupersetUI from '@superset-ui/core';
-import { styledMount as mount } from 'spec/helpers/theming';
+import { render } from 'spec/helpers/testing-library';
 import {
   CHART_RENDERING_SUCCEEDED,
   CHART_UPDATE_SUCCEEDED,
@@ -36,123 +33,105 @@ import { sliceId } from 'spec/fixtures/mockChartQueries';
 import { dashboardFilters } from 'spec/fixtures/mockDashboardFilters';
 import { dashboardWithFilter } from 'spec/fixtures/mockDashboardLayout';
 
+jest.mock(
+  'src/dashboard/components/FiltersBadge/DetailsPanel',
+  () =>
+    ({ children }: { children: React.ReactNode }) =>
+      <div data-test="mock-details-panel">{children}</div>,
+);
+
 const defaultStore = getMockStoreWithFilters();
 function setup(store: Store = defaultStore) {
-  return mount(
-    <Provider store={store}>
-      <FiltersBadge chartId={sliceId} />
-    </Provider>,
-  );
+  return render(<FiltersBadge chartId={sliceId} />, { store });
 }
 
-describe('FiltersBadge', () => {
-  // there's this bizarre "active filters" thing
-  // that doesn't actually use any kind of state management.
-  // Have to set variables in there.
-  buildActiveFilters({
-    dashboardFilters,
-    components: dashboardWithFilter,
-  });
-
-  beforeEach(() => {
-    // shallow rendering in enzyme doesn't propagate contexts correctly,
-    // so we have to mock the hook.
-    // See https://medium.com/7shifts-engineering-blog/testing-usecontext-react-hook-with-enzyme-shallow-da062140fc83
-    jest
-      .spyOn(SupersetUI, 'useTheme')
-      .mockImplementation(() => SupersetUI.supersetTheme);
-  });
+// there's this bizarre "active filters" thing
+// that doesn't actually use any kind of state management.
+// Have to set variables in there.
+buildActiveFilters({
+  dashboardFilters,
+  components: dashboardWithFilter,
+});
 
-  describe('for dashboard filters', () => {
-    it("doesn't show number when there are no active filters", () => {
-      const store = getMockStoreWithFilters();
-      // start with basic dashboard state, dispatch an event to simulate query completion
-      store.dispatch({
-        type: CHART_UPDATE_SUCCEEDED,
-        key: sliceId,
-        queriesResponse: [
-          {
-            status: 'success',
-            applied_filters: [],
-            rejected_filters: [],
-          },
-        ],
-        dashboardFilters,
-      });
-      const wrapper = shallow(
-        <Provider store={store}>
-          <FiltersBadge chartId={sliceId} />,
-        </Provider>,
-      );
-      expect(wrapper.find('[data-test="applied-filter-count"]')).not.toExist();
+describe('for dashboard filters', () => {
+  test('does not show number when there are no active filters', () => {
+    const store = getMockStoreWithFilters();
+    // start with basic dashboard state, dispatch an event to simulate query completion
+    store.dispatch({
+      type: CHART_UPDATE_SUCCEEDED,
+      key: sliceId,
+      queriesResponse: [
+        {
+          status: 'success',
+          applied_filters: [],
+          rejected_filters: [],
+        },
+      ],
+      dashboardFilters,
     });
+    const { queryByTestId } = setup(store);
+    expect(queryByTestId('applied-filter-count')).not.toBeInTheDocument();
+  });
 
-    it('shows the indicator when filters have been applied', () => {
-      const store = getMockStoreWithFilters();
-      // start with basic dashboard state, dispatch an event to simulate query completion
-      store.dispatch({
-        type: CHART_UPDATE_SUCCEEDED,
-        key: sliceId,
-        queriesResponse: [
-          {
-            status: 'success',
-            applied_filters: [{ column: 'region' }],
-            rejected_filters: [],
-          },
-        ],
-        dashboardFilters,
-      });
-      store.dispatch({ type: CHART_RENDERING_SUCCEEDED, key: sliceId });
-      const wrapper = setup(store);
-      expect(wrapper.find('DetailsPanelPopover')).toExist();
-      expect(
-        wrapper.find('[data-test="applied-filter-count"] .current'),
-      ).toHaveText('1');
-      expect(wrapper.find('WarningFilled')).not.toExist();
+  test('shows the indicator when filters have been applied', () => {
+    const store = getMockStoreWithFilters();
+    // start with basic dashboard state, dispatch an event to simulate query completion
+    store.dispatch({
+      type: CHART_UPDATE_SUCCEEDED,
+      key: sliceId,
+      queriesResponse: [
+        {
+          status: 'success',
+          applied_filters: [{ column: 'region' }],
+          rejected_filters: [],
+        },
+      ],
+      dashboardFilters,
     });
+    store.dispatch({ type: CHART_RENDERING_SUCCEEDED, key: sliceId });
+    const { getByTestId } = setup(store);
+    expect(getByTestId('applied-filter-count')).toHaveTextContent('1');
+    expect(getByTestId('mock-details-panel')).toBeInTheDocument();
   });
+});
 
-  describe('for native filters', () => {
-    it("doesn't show number when there are no active filters", () => {
-      const store = getMockStoreWithNativeFilters();
-      // start with basic dashboard state, dispatch an event to simulate query completion
-      store.dispatch({
-        type: CHART_UPDATE_SUCCEEDED,
-        key: sliceId,
-        queriesResponse: [
-          {
-            status: 'success',
-            applied_filters: [],
-            rejected_filters: [],
-          },
-        ],
-      });
-      store.dispatch({ type: CHART_RENDERING_SUCCEEDED, key: sliceId });
-      const wrapper = setup(store);
-      expect(wrapper.find('[data-test="applied-filter-count"]')).not.toExist();
+describe('for native filters', () => {
+  test('does not show number when there are no active filters', () => {
+    const store = getMockStoreWithNativeFilters();
+    // start with basic dashboard state, dispatch an event to simulate query completion
+    store.dispatch({
+      type: CHART_UPDATE_SUCCEEDED,
+      key: sliceId,
+      queriesResponse: [
+        {
+          status: 'success',
+          applied_filters: [],
+          rejected_filters: [],
+        },
+      ],
     });
+    store.dispatch({ type: CHART_RENDERING_SUCCEEDED, key: sliceId });
+    const { queryByTestId } = setup(store);
+    expect(queryByTestId('applied-filter-count')).not.toBeInTheDocument();
+  });
 
-    it('shows the indicator when filters have been applied', () => {
-      const store = getMockStoreWithNativeFilters();
-      // start with basic dashboard state, dispatch an event to simulate query completion
-      store.dispatch({
-        type: CHART_UPDATE_SUCCEEDED,
-        key: sliceId,
-        queriesResponse: [
-          {
-            status: 'success',
-            applied_filters: [{ column: 'region' }],
-            rejected_filters: [],
-          },
-        ],
-      });
-      store.dispatch({ type: CHART_RENDERING_SUCCEEDED, key: sliceId });
-      const wrapper = setup(store);
-      expect(wrapper.find('DetailsPanelPopover')).toExist();
-      expect(
-        wrapper.find('[data-test="applied-filter-count"] .current'),
-      ).toHaveText('1');
-      expect(wrapper.find('WarningFilled')).not.toExist();
+  test('shows the indicator when filters have been applied', () => {
+    const store = getMockStoreWithNativeFilters();
+    // start with basic dashboard state, dispatch an event to simulate query completion
+    store.dispatch({
+      type: CHART_UPDATE_SUCCEEDED,
+      key: sliceId,
+      queriesResponse: [
+        {
+          status: 'success',
+          applied_filters: [{ column: 'region' }],
+          rejected_filters: [],
+        },
+      ],
     });
+    store.dispatch({ type: CHART_RENDERING_SUCCEEDED, key: sliceId });
+    const { getByTestId } = setup(store);
+    expect(getByTestId('applied-filter-count')).toHaveTextContent('1');
+    expect(getByTestId('mock-details-panel')).toBeInTheDocument();
   });
 });
diff --git a/superset-frontend/src/dashboard/components/PropertiesModal/PropertiesModal.test.jsx b/superset-frontend/src/dashboard/components/PropertiesModal/PropertiesModal.test.jsx
index 2fe2ad81ca..a909a6eebd 100644
--- a/superset-frontend/src/dashboard/components/PropertiesModal/PropertiesModal.test.jsx
+++ b/superset-frontend/src/dashboard/components/PropertiesModal/PropertiesModal.test.jsx
@@ -17,15 +17,10 @@
  * under the License.
  */
 import React from 'react';
-import { mount } from 'enzyme';
-import { Provider } from 'react-redux';
+import { render } from 'spec/helpers/testing-library';
 import fetchMock from 'fetch-mock';
 
-import {
-  supersetTheme,
-  SupersetClient,
-  ThemeProvider,
-} from '@superset-ui/core';
+import { SupersetClient } from '@superset-ui/core';
 
 import Modal from 'src/components/Modal';
 import PropertiesModal from 'src/dashboard/components/PropertiesModal';
@@ -73,15 +68,10 @@ describe.skip('PropertiesModal', () => {
   };
 
   function setup(overrideProps) {
-    return mount(
-      <Provider store={mockStore}>
-        <PropertiesModal {...requiredProps} {...overrideProps} />
-      </Provider>,
-      {
-        wrappingComponent: ThemeProvider,
-        wrappingComponentProps: { theme: supersetTheme },
-      },
-    );
+    return render(<PropertiesModal {...requiredProps} {...overrideProps} />, {
+      useRedux: true,
+      store: mockStore,
+    });
   }
 
   describe('onColorSchemeChange', () => {
diff --git a/superset-frontend/src/dashboard/components/RefreshIntervalModal.test.tsx b/superset-frontend/src/dashboard/components/RefreshIntervalModal.test.tsx
index 30a7c7ec3d..17c08e0701 100644
--- a/superset-frontend/src/dashboard/components/RefreshIntervalModal.test.tsx
+++ b/superset-frontend/src/dashboard/components/RefreshIntervalModal.test.tsx
@@ -17,54 +17,12 @@
  * under the License.
  */
 import React from 'react';
-import { mount } from 'enzyme';
 import { render, screen } from 'spec/helpers/testing-library';
 import userEvent from '@testing-library/user-event';
 import fetchMock from 'fetch-mock';
 
-import ModalTrigger from 'src/components/ModalTrigger';
 import RefreshIntervalModal from 'src/dashboard/components/RefreshIntervalModal';
 import HeaderActionsDropdown from 'src/dashboard/components/Header/HeaderActionsDropdown';
-import Alert from 'src/components/Alert';
-import { supersetTheme, ThemeProvider } from '@superset-ui/core';
-
-describe('RefreshIntervalModal - Enzyme', () => {
-  const getMountWrapper = (props: any) =>
-    mount(<RefreshIntervalModal {...props} />, {
-      wrappingComponent: ThemeProvider,
-      wrappingComponentProps: {
-        theme: supersetTheme,
-      },
-    });
-  const mockedProps = {
-    triggerNode: <i className="fa fa-edit" />,
-    refreshFrequency: 10,
-    onChange: jest.fn(),
-    editMode: true,
-    refreshIntervalOptions: [],
-  };
-  it('should show warning message', () => {
-    const props = {
-      ...mockedProps,
-      refreshLimit: 3600,
-      refreshWarning: 'Show warning',
-    };
-
-    const wrapper = getMountWrapper(props);
-    wrapper.find('span[role="button"]').simulate('click');
-
-    // @ts-ignore (for handleFrequencyChange)
-    wrapper.instance().handleFrequencyChange(30);
-    wrapper.update();
-    expect(wrapper.find(ModalTrigger).find(Alert)).toExist();
-
-    // @ts-ignore (for handleFrequencyChange)
-    wrapper.instance().handleFrequencyChange(3601);
-    wrapper.update();
-    expect(wrapper.find(ModalTrigger).find(Alert)).not.toExist();
-    wrapper.unmount();
-  });
-});
 
 const createProps = () => ({
   addSuccessToast: jest.fn(),
@@ -150,103 +108,99 @@ const defaultRefreshIntervalModalProps = {
   refreshIntervalOptions: [],
 };
 
-describe('RefreshIntervalModal - RTL', () => {
-  it('is valid', () => {
-    expect(
-      React.isValidElement(
-        <RefreshIntervalModal {...defaultRefreshIntervalModalProps} />,
-      ),
-    ).toBe(true);
-  });
+test('is valid', () => {
+  expect(
+    React.isValidElement(
+      <RefreshIntervalModal {...defaultRefreshIntervalModalProps} />,
+    ),
+  ).toBe(true);
+});
 
-  it('renders refresh interval modal', async () => {
-    render(setup(editModeOnProps));
-    await openRefreshIntervalModal();
+test('renders refresh interval modal', async () => {
+  render(setup(editModeOnProps));
+  await openRefreshIntervalModal();
 
-    // Assert that modal exists by checking for the modal title
-    expect(screen.getByText('Refresh interval')).toBeVisible();
-  });
+  // Assert that modal exists by checking for the modal title
+  expect(screen.getByText('Refresh interval')).toBeVisible();
+});
 
-  it('renders refresh interval options', async () => {
-    render(setup(editModeOnProps));
-    await openRefreshIntervalModal();
-    await displayOptions();
-
-    // Assert that both "Don't refresh" instances exist
-    // - There will be two at this point, the default option and the dropdown option
-    const dontRefreshInstances = screen.getAllByText(/don't refresh/i);
-    expect(dontRefreshInstances).toHaveLength(2);
-    dontRefreshInstances.forEach(option => {
-      expect(option).toBeInTheDocument();
-    });
-
-    // Assert that all the other options exist
-    const options = [
-      screen.getByText(/10 seconds/i),
-      screen.getByText(/30 seconds/i),
-      screen.getByText(/1 minute/i),
-      screen.getByText(/5 minutes/i),
-      screen.getByText(/30 minutes/i),
-      screen.getByText(/1 hour/i),
-      screen.getByText(/6 hours/i),
-      screen.getByText(/12 hours/i),
-      screen.getByText(/24 hours/i),
-    ];
-    options.forEach(option => {
-      expect(option).toBeInTheDocument();
-    });
+test('renders refresh interval options', async () => {
+  render(setup(editModeOnProps));
+  await openRefreshIntervalModal();
+  await displayOptions();
+
+  // Assert that both "Don't refresh" instances exist
+  // - There will be two at this point, the default option and the dropdown option
+  const dontRefreshInstances = screen.getAllByText(/don't refresh/i);
+  expect(dontRefreshInstances).toHaveLength(2);
+  dontRefreshInstances.forEach(option => {
+    expect(option).toBeInTheDocument();
   });
 
-  it('should change selected value', async () => {
-    render(setup(editModeOnProps));
-    await openRefreshIntervalModal();
+  // Assert that all the other options exist
+  const options = [
+    screen.getByText(/10 seconds/i),
+    screen.getByText(/30 seconds/i),
+    screen.getByText(/1 minute/i),
+    screen.getByText(/5 minutes/i),
+    screen.getByText(/30 minutes/i),
+    screen.getByText(/1 hour/i),
+    screen.getByText(/6 hours/i),
+    screen.getByText(/12 hours/i),
+    screen.getByText(/24 hours/i),
+  ];
+  options.forEach(option => {
+    expect(option).toBeInTheDocument();
+  });
+});
 
-    // Initial selected value should be "Don't refresh"
-    const selectedValue = screen.getByText(/don't refresh/i);
-    expect(selectedValue.title).toMatch(/don't refresh/i);
+test('should change selected value', async () => {
+  render(setup(editModeOnProps));
+  await openRefreshIntervalModal();
 
-    // Display options and select "10 seconds"
-    await displayOptions();
-    userEvent.click(screen.getByText(/10 seconds/i));
+  // Initial selected value should be "Don't refresh"
+  const selectedValue = screen.getByText(/don't refresh/i);
+  expect(selectedValue.title).toMatch(/don't refresh/i);
 
-    // Selected value should now be "10 seconds"
-    expect(selectedValue.title).toMatch(/10 seconds/i);
-    expect(selectedValue.title).not.toMatch(/don't refresh/i);
-  });
+  // Display options and select "10 seconds"
+  await displayOptions();
+  userEvent.click(screen.getByText(/10 seconds/i));
 
-  it('should save a newly-selected value', async () => {
-    render(setup(editModeOnProps));
-    await openRefreshIntervalModal();
-    await displayOptions();
-
-    // Select a new interval and click save
-    userEvent.click(screen.getByText(/10 seconds/i));
-    userEvent.click(screen.getByRole('button', { name: /save/i }));
-
-    expect(editModeOnProps.setRefreshFrequency).toHaveBeenCalled();
-    expect(editModeOnProps.setRefreshFrequency).toHaveBeenCalledWith(
-      10,
-      editModeOnProps.editMode,
-    );
-    expect(editModeOnProps.addSuccessToast).toHaveBeenCalled();
-  });
+  // Selected value should now be "10 seconds"
+  expect(selectedValue.title).toMatch(/10 seconds/i);
+  expect(selectedValue.title).not.toMatch(/don't refresh/i);
+});
 
-  it('should show warning message', async () => {
-    // TODO (lyndsiWilliams): This test is incomplete
-    const warningProps = {
-      ...editModeOnProps,
-      refreshLimit: 3600,
-      refreshWarning: 'Show warning',
-    };
+test('should save a newly-selected value', async () => {
+  render(setup(editModeOnProps));
+  await openRefreshIntervalModal();
+  await displayOptions();
+
+  // Select a new interval and click save
+  userEvent.click(screen.getByText(/10 seconds/i));
+  userEvent.click(screen.getByRole('button', { name: /save/i }));
+
+  expect(editModeOnProps.setRefreshFrequency).toHaveBeenCalled();
+  expect(editModeOnProps.setRefreshFrequency).toHaveBeenCalledWith(
+    10,
+    editModeOnProps.editMode,
+  );
+  expect(editModeOnProps.addSuccessToast).toHaveBeenCalled();
+});
 
-    render(setup(warningProps));
-    await openRefreshIntervalModal();
-    await displayOptions();
+test('should show warning message', async () => {
+  const warningProps = {
+    ...editModeOnProps,
+    refreshLimit: 3600,
+    refreshWarning: 'Show warning',
+  };
 
-    userEvent.click(screen.getByText(/30 seconds/i));
-    userEvent.click(screen.getByRole('button', { name: /save/i }));
+  const { getByRole, queryByRole } = render(setup(warningProps));
+  await openRefreshIntervalModal();
+  await displayOptions();
 
-    // screen.debug(screen.getByRole('alert'));
-    expect.anything();
-  });
+  userEvent.click(screen.getByText(/30 seconds/i));
+  expect(getByRole('alert')).toBeInTheDocument();
+  userEvent.click(screen.getByText(/6 hours/i));
+  expect(queryByRole('alert')).not.toBeInTheDocument();
 });
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Chart.test.jsx b/superset-frontend/src/dashboard/components/gridComponents/Chart.test.jsx
index 89bcca7f78..9ca691d3d2 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Chart.test.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Chart.test.jsx
@@ -17,12 +17,10 @@
  * under the License.
  */
 import React from 'react';
-import { shallow } from 'enzyme';
-import sinon from 'sinon';
+import { fireEvent, render } from 'spec/helpers/testing-library';
+import { FeatureFlag } from '@superset-ui/core';
 
 import Chart from 'src/dashboard/components/gridComponents/Chart';
-import SliceHeader from 'src/dashboard/components/SliceHeader';
-import ChartContainer from 'src/components/Chart/ChartContainer';
 import * as exploreUtils from 'src/explore/exploreUtils';
 import { sliceEntitiesForChart as sliceEntities } from 'spec/fixtures/mockSliceEntities';
 import mockDatasource from 'spec/fixtures/mockDatasource';
@@ -30,146 +28,183 @@ import chartQueries, {
   sliceId as queryId,
 } from 'spec/fixtures/mockChartQueries';
 
-describe('Chart', () => {
-  const props = {
-    id: queryId,
-    width: 100,
-    height: 100,
-    updateSliceName() {},
+const props = {
+  id: queryId,
+  width: 100,
+  height: 100,
+  updateSliceName() {},
 
-    // from redux
-    maxRows: 666,
-    chart: chartQueries[queryId],
-    formData: chartQueries[queryId].form_data,
-    datasource: mockDatasource[sliceEntities.slices[queryId].datasource],
-    slice: {
-      ...sliceEntities.slices[queryId],
-      description_markeddown: 'markdown',
-      owners: [],
-    },
-    sliceName: sliceEntities.slices[queryId].slice_name,
-    timeout: 60,
-    filters: {},
-    refreshChart() {},
-    toggleExpandSlice() {},
-    addFilter() {},
-    logEvent() {},
-    handleToggleFullSize() {},
-    changeFilter() {},
-    setFocusedFilterField() {},
-    unsetFocusedFilterField() {},
-    addSuccessToast() {},
-    addDangerToast() {},
-    exportCSV() {},
-    exportFullCSV() {},
-    exportXLSX() {},
-    exportFullXLSX() {},
-    componentId: 'test',
-    dashboardId: 111,
-    editMode: false,
-    isExpanded: false,
-    supersetCanExplore: false,
-    supersetCanCSV: false,
-  };
-
-  function setup(overrideProps) {
-    const wrapper = shallow(
-      <Chart.WrappedComponent {...props} {...overrideProps} />,
-    );
-    return wrapper;
-  }
+  // from redux
+  maxRows: 666,
+  chart: chartQueries[queryId],
+  formData: chartQueries[queryId].form_data,
+  datasource: mockDatasource[sliceEntities.slices[queryId].datasource],
+  slice: {
+    ...sliceEntities.slices[queryId],
+    description_markeddown: 'markdown',
+    owners: [],
+    viz_type: 'table',
+  },
+  sliceName: sliceEntities.slices[queryId].slice_name,
+  timeout: 60,
+  filters: {},
+  refreshChart() {},
+  toggleExpandSlice() {},
+  addFilter() {},
+  logEvent() {},
+  handleToggleFullSize() {},
+  changeFilter() {},
+  setFocusedFilterField() {},
+  unsetFocusedFilterField() {},
+  addSuccessToast() {},
+  addDangerToast() {},
+  exportCSV() {},
+  exportFullCSV() {},
+  exportXLSX() {},
+  exportFullXLSX() {},
+  componentId: 'test',
+  dashboardId: 111,
+  editMode: false,
+  isExpanded: false,
+  supersetCanExplore: false,
+  supersetCanCSV: false,
+  supersetCanShare: false,
+};
 
-  it('should render a SliceHeader', () => {
-    const wrapper = setup();
-    expect(wrapper.find(SliceHeader)).toExist();
+function setup(overrideProps) {
+  return render(<Chart.WrappedComponent {...props} {...overrideProps} />, {
+    useRedux: true,
+    useRouter: true,
   });
+}
 
-  it('should render a ChartContainer', () => {
-    const wrapper = setup();
-    expect(wrapper.find(ChartContainer)).toExist();
-  });
+test('should render a SliceHeader', () => {
+  const { getByTestId, container } = setup();
+  expect(getByTestId('slice-header')).toBeInTheDocument();
+  expect(container.querySelector('.slice_description')).not.toBeInTheDocument();
+});
 
-  it('should render a description if it has one and isExpanded=true', () => {
-    const wrapper = setup();
-    expect(wrapper.find('.slice_description')).not.toExist();
-    wrapper.setProps({ ...props, isExpanded: true });
-    expect(wrapper.find('.slice_description')).toExist();
-  });
+test('should render a ChartContainer', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('chart-container')).toBeInTheDocument();
+});
+
+test('should render a description if it has one and isExpanded=true', () => {
+  const { container } = setup({ isExpanded: true });
+  expect(container.querySelector('.slice_description')).toBeInTheDocument();
+});
 
-  it('should calculate the description height if it has one and isExpanded=true', () => {
-    const spy = jest.spyOn(
-      Chart.WrappedComponent.prototype,
-      'getDescriptionHeight',
-    );
-    const wrapper = setup({ isExpanded: true });
+test('should calculate the description height if it has one and isExpanded=true', () => {
+  const spy = jest.spyOn(
+    Chart.WrappedComponent.prototype,
+    'getDescriptionHeight',
+  );
+  const { container } = setup({ isExpanded: true });
+  expect(container.querySelector('.slice_description')).toBeInTheDocument();
+  expect(spy).toHaveBeenCalled();
+});
 
-    expect(wrapper.find('.slice_description')).toExist();
-    expect(spy).toHaveBeenCalled();
-  });
+test('should call refreshChart when SliceHeader calls forceRefresh', () => {
+  const refreshChart = jest.fn();
+  const { getByText, getByRole } = setup({ refreshChart });
+  fireEvent.click(getByRole('button', { name: 'More Options' }));
+  fireEvent.click(getByText('Force refresh'));
+  expect(refreshChart).toHaveBeenCalled();
+});
 
-  it('should call refreshChart when SliceHeader calls forceRefresh', () => {
-    const refreshChart = sinon.spy();
-    const wrapper = setup({ refreshChart });
-    wrapper.instance().forceRefresh();
-    expect(refreshChart.callCount).toBe(1);
-  });
+test.skip('should call changeFilter when ChartContainer calls changeFilter', () => {
+  const changeFilter = jest.fn();
+  const wrapper = setup({ changeFilter });
+  wrapper.instance().changeFilter();
+  expect(changeFilter.callCount).toBe(1);
+});
 
-  it('should call changeFilter when ChartContainer calls changeFilter', () => {
-    const changeFilter = sinon.spy();
-    const wrapper = setup({ changeFilter });
-    wrapper.instance().changeFilter();
-    expect(changeFilter.callCount).toBe(1);
-  });
-  it('should call exportChart when exportCSV is clicked', () => {
-    const stubbedExportCSV = sinon
-      .stub(exploreUtils, 'exportChart')
-      .returns(() => {});
-    const wrapper = setup();
-    wrapper.instance().exportCSV(props.slice.sliceId);
-    expect(stubbedExportCSV.calledOnce).toBe(true);
-    expect(stubbedExportCSV.lastCall.args[0]).toEqual(
-      expect.objectContaining({
-        formData: expect.anything(),
-        resultType: 'full',
-        resultFormat: 'csv',
+test('should call exportChart when exportCSV is clicked', async () => {
+  const stubbedExportCSV = jest
+    .spyOn(exploreUtils, 'exportChart')
+    .mockImplementation(() => {});
+  const { findByText, getByRole } = setup({ supersetCanCSV: true });
+  fireEvent.click(getByRole('button', { name: 'More Options' }));
+  fireEvent.mouseOver(getByRole('button', { name: 'Download' }));
+  const exportAction = await findByText('Export to .CSV');
+  fireEvent.click(exportAction);
+  expect(stubbedExportCSV).toHaveBeenCalledTimes(1);
+  expect(stubbedExportCSV).toHaveBeenCalledWith(
+    expect.objectContaining({
+      formData: expect.anything(),
+      resultType: 'full',
+      resultFormat: 'csv',
+    }),
+  );
+  stubbedExportCSV.mockRestore();
+});
+
+test('should call exportChart with row_limit props.maxRows when exportFullCSV is clicked', async () => {
+  global.featureFlags = {
+    [FeatureFlag.AllowFullCsvExport]: true,
+  };
+  const stubbedExportCSV = jest
+    .spyOn(exploreUtils, 'exportChart')
+    .mockImplementation(() => {});
+  const { findByText, getByRole } = setup({ supersetCanCSV: true });
+  fireEvent.click(getByRole('button', { name: 'More Options' }));
+  fireEvent.mouseOver(getByRole('button', { name: 'Download' }));
+  const exportAction = await findByText('Export to full .CSV');
+  fireEvent.click(exportAction);
+  expect(stubbedExportCSV).toHaveBeenCalledTimes(1);
+  expect(stubbedExportCSV).toHaveBeenCalledWith(
+    expect.objectContaining({
+      formData: expect.objectContaining({
+        row_limit: 666,
       }),
-    );
-    exploreUtils.exportChart.restore();
-  });
-  it('should call exportChart with row_limit props.maxRows when exportFullCSV is clicked', () => {
-    const stubbedExportCSV = sinon
-      .stub(exploreUtils, 'exportChart')
-      .returns(() => {});
-    const wrapper = setup();
-    wrapper.instance().exportFullCSV(props.slice.sliceId);
-    expect(stubbedExportCSV.calledOnce).toBe(true);
-    expect(stubbedExportCSV.lastCall.args[0].formData.row_limit).toEqual(666);
-    exploreUtils.exportChart.restore();
-  });
-  it('should call exportChart when exportXLSX is clicked', () => {
-    const stubbedExportXLSX = sinon
-      .stub(exploreUtils, 'exportChart')
-      .returns(() => {});
-    const wrapper = setup();
-    wrapper.instance().exportXLSX(props.slice.sliceId);
-    expect(stubbedExportXLSX.calledOnce).toBe(true);
-    expect(stubbedExportXLSX.lastCall.args[0]).toEqual(
-      expect.objectContaining({
-        formData: expect.anything(),
-        resultType: 'full',
-        resultFormat: 'xlsx',
+      resultType: 'full',
+      resultFormat: 'csv',
+    }),
+  );
+  stubbedExportCSV.mockRestore();
+});
+
+test('should call exportChart when exportXLSX is clicked', async () => {
+  const stubbedExportXLSX = jest
+    .spyOn(exploreUtils, 'exportChart')
+    .mockImplementation(() => {});
+  const { findByText, getByRole } = setup({ supersetCanCSV: true });
+  fireEvent.click(getByRole('button', { name: 'More Options' }));
+  fireEvent.mouseOver(getByRole('button', { name: 'Download' }));
+  const exportAction = await findByText('Export to Excel');
+  fireEvent.click(exportAction);
+  expect(stubbedExportXLSX).toHaveBeenCalledTimes(1);
+  expect(stubbedExportXLSX).toHaveBeenCalledWith(
+    expect.objectContaining({
+      resultType: 'full',
+      resultFormat: 'xlsx',
+    }),
+  );
+  stubbedExportXLSX.mockRestore();
+});
+
+test('should call exportChart with row_limit props.maxRows when exportFullXLSX is clicked', async () => {
+  global.featureFlags = {
+    [FeatureFlag.AllowFullCsvExport]: true,
+  };
+  const stubbedExportXLSX = jest
+    .spyOn(exploreUtils, 'exportChart')
+    .mockImplementation(() => {});
+  const { findByText, getByRole } = setup({ supersetCanCSV: true });
+  fireEvent.click(getByRole('button', { name: 'More Options' }));
+  fireEvent.mouseOver(getByRole('button', { name: 'Download' }));
+  const exportAction = await findByText('Export to full Excel');
+  fireEvent.click(exportAction);
+  expect(stubbedExportXLSX).toHaveBeenCalledTimes(1);
+  expect(stubbedExportXLSX).toHaveBeenCalledWith(
+    expect.objectContaining({
+      formData: expect.objectContaining({
+        row_limit: 666,
       }),
-    );
-    exploreUtils.exportChart.restore();
-  });
-  it('should call exportChart with row_limit props.maxRows when exportFullXLSX is clicked', () => {
-    const stubbedExportXLSX = sinon
-      .stub(exploreUtils, 'exportChart')
-      .returns(() => {});
-    const wrapper = setup();
-    wrapper.instance().exportFullXLSX(props.slice.sliceId);
-    expect(stubbedExportXLSX.calledOnce).toBe(true);
-    expect(stubbedExportXLSX.lastCall.args[0].formData.row_limit).toEqual(666);
-    exploreUtils.exportChart.restore();
-  });
+      resultType: 'full',
+      resultFormat: 'xlsx',
+    }),
+  );
+
+  stubbedExportXLSX.mockRestore();
 });
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Column.test.jsx b/superset-frontend/src/dashboard/components/gridComponents/Column.test.jsx
index 72e89075b5..0efc549855 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Column.test.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Column.test.jsx
@@ -16,158 +16,179 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Provider } from 'react-redux';
 import React from 'react';
-import { mount } from 'enzyme';
-import sinon from 'sinon';
-import { MemoryRouter } from 'react-router-dom';
-import { supersetTheme, ThemeProvider } from '@superset-ui/core';
-import { DndProvider } from 'react-dnd';
-import { HTML5Backend } from 'react-dnd-html5-backend';
+import { fireEvent, render } from 'spec/helpers/testing-library';
 
 import BackgroundStyleDropdown from 'src/dashboard/components/menu/BackgroundStyleDropdown';
 import Column from 'src/dashboard/components/gridComponents/Column';
-import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
-import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
-import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
-import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
 import IconButton from 'src/dashboard/components/IconButton';
-import ResizableContainer from 'src/dashboard/components/resizable/ResizableContainer';
-import WithPopoverMenu from 'src/dashboard/components/menu/WithPopoverMenu';
 
 import { getMockStore } from 'spec/fixtures/mockStore';
 import { dashboardLayout as mockLayout } from 'spec/fixtures/mockDashboardLayout';
 import { initialState } from 'src/SqlLab/fixtures';
 
-describe('Column', () => {
-  const columnWithoutChildren = {
-    ...mockLayout.present.COLUMN_ID,
-    children: [],
-  };
-  const props = {
-    id: 'COLUMN_ID',
-    parentId: 'ROW_ID',
-    component: mockLayout.present.COLUMN_ID,
-    parentComponent: mockLayout.present.ROW_ID,
-    index: 0,
-    depth: 2,
-    editMode: false,
-    availableColumnCount: 12,
-    minColumnWidth: 2,
-    columnWidth: 50,
-    occupiedColumnCount: 6,
-    onResizeStart() {},
-    onResize() {},
-    onResizeStop() {},
-    handleComponentDrop() {},
-    deleteComponent() {},
-    updateComponents() {},
-  };
-
-  function setup(overrideProps) {
-    // We have to wrap provide DragDropContext for the underlying DragDroppable
-    // otherwise we cannot assert on DragDroppable children
-    const mockStore = getMockStore({
-      ...initialState,
-    });
-    const wrapper = mount(
-      <Provider store={mockStore}>
-        <MemoryRouter>
-          <DndProvider backend={HTML5Backend}>
-            <Column {...props} {...overrideProps} />
-          </DndProvider>
-        </MemoryRouter>
-      </Provider>,
-      {
-        wrappingComponent: ThemeProvider,
-        wrappingComponentProps: { theme: supersetTheme },
-      },
-    );
-    return wrapper;
-  }
-
-  it('should render a DragDroppable', () => {
-    // don't count child DragDroppables
-    const wrapper = setup({ component: columnWithoutChildren });
-    expect(wrapper.find(DragDroppable)).toExist();
+jest.mock(
+  'src/dashboard/containers/DashboardComponent',
+  () =>
+    ({ availableColumnCount, depth }) =>
+      (
+        <div data-test="mock-dashboard-component" depth={depth}>
+          {availableColumnCount}
+        </div>
+      ),
+);
+jest.mock(
+  'src/dashboard/components/menu/WithPopoverMenu',
+  () =>
+    ({ children }) =>
+      <div data-test="mock-with-popover-menu">{children}</div>,
+);
+jest.mock(
+  'src/dashboard/components/DeleteComponentButton',
+  () =>
+    ({ onDelete }) =>
+      (
+        <button
+          type="button"
+          data-test="mock-delete-component-button"
+          onClick={onDelete}
+        >
+          Delete
+        </button>
+      ),
+);
+
+const columnWithoutChildren = {
+  ...mockLayout.present.COLUMN_ID,
+  children: [],
+};
+const props = {
+  id: 'COLUMN_ID',
+  parentId: 'ROW_ID',
+  component: mockLayout.present.COLUMN_ID,
+  parentComponent: mockLayout.present.ROW_ID,
+  index: 0,
+  depth: 2,
+  editMode: false,
+  availableColumnCount: 12,
+  minColumnWidth: 2,
+  columnWidth: 50,
+  occupiedColumnCount: 6,
+  onResizeStart() {},
+  onResize() {},
+  onResizeStop() {},
+  handleComponentDrop() {},
+  deleteComponent() {},
+  updateComponents() {},
+};
+
+function setup(overrideProps) {
+  // We have to wrap provide DragDropContext for the underlying DragDroppable
+  // otherwise we cannot assert on DragDroppable children
+  const mockStore = getMockStore({
+    ...initialState,
   });
-
-  it('should render a WithPopoverMenu', () => {
-    // don't count child DragDroppables
-    const wrapper = setup({ component: columnWithoutChildren });
-    expect(wrapper.find(WithPopoverMenu)).toExist();
+  return render(<Column {...props} {...overrideProps} />, {
+    store: mockStore,
+    useDnd: true,
+    useRouter: true,
   });
+}
+
+test('should render a DragDroppable', () => {
+  // don't count child DragDroppables
+  const { getByTestId } = setup({ component: columnWithoutChildren });
+  expect(getByTestId('dragdroppable-object')).toBeInTheDocument();
+});
 
-  it('should render a ResizableContainer', () => {
-    // don't count child DragDroppables
-    const wrapper = setup({ component: columnWithoutChildren });
-    expect(wrapper.find(ResizableContainer)).toExist();
+test('should skip rendering HoverMenu and DeleteComponentButton when not in editMode', () => {
+  const { container, queryByTestId } = setup({
+    component: columnWithoutChildren,
   });
+  expect(container.querySelector('.hover-menu')).not.toBeInTheDocument();
+  expect(queryByTestId('mock-delete-component-button')).not.toBeInTheDocument();
+});
+
+test('should render a WithPopoverMenu', () => {
+  // don't count child DragDroppables
+  const { getByTestId } = setup({ component: columnWithoutChildren });
+  expect(getByTestId('mock-with-popover-menu')).toBeInTheDocument();
+});
 
-  it('should render a HoverMenu in editMode', () => {
-    let wrapper = setup({ component: columnWithoutChildren });
-    expect(wrapper.find(HoverMenu)).not.toExist();
+test('should render a ResizableContainer', () => {
+  // don't count child DragDroppables
+  const { container } = setup({ component: columnWithoutChildren });
+  expect(container.querySelector('.resizable-container')).toBeInTheDocument();
+});
 
-    // we cannot set props on the Row because of the WithDragDropContext wrapper
-    wrapper = setup({ component: columnWithoutChildren, editMode: true });
-    expect(wrapper.find(HoverMenu)).toExist();
+test('should render a HoverMenu in editMode', () => {
+  // we cannot set props on the Row because of the WithDragDropContext wrapper
+  const { container } = setup({
+    component: columnWithoutChildren,
+    editMode: true,
   });
+  expect(container.querySelector('.hover-menu')).toBeInTheDocument();
+});
 
-  it('should render a DeleteComponentButton in editMode', () => {
-    let wrapper = setup({ component: columnWithoutChildren });
-    expect(wrapper.find(DeleteComponentButton)).not.toExist();
-
-    // we cannot set props on the Row because of the WithDragDropContext wrapper
-    wrapper = setup({ component: columnWithoutChildren, editMode: true });
-    expect(wrapper.find(DeleteComponentButton)).toExist();
+test('should render a DeleteComponentButton in editMode', () => {
+  // we cannot set props on the Row because of the WithDragDropContext wrapper
+  const { getByTestId } = setup({
+    component: columnWithoutChildren,
+    editMode: true,
   });
+  expect(getByTestId('mock-delete-component-button')).toBeInTheDocument();
+});
 
-  it('should render a BackgroundStyleDropdown when focused', () => {
-    let wrapper = setup({ component: columnWithoutChildren });
-    expect(wrapper.find(BackgroundStyleDropdown)).not.toExist();
+test.skip('should render a BackgroundStyleDropdown when focused', () => {
+  let wrapper = setup({ component: columnWithoutChildren });
+  expect(wrapper.find(BackgroundStyleDropdown)).not.toExist();
 
-    // we cannot set props on the Row because of the WithDragDropContext wrapper
-    wrapper = setup({ component: columnWithoutChildren, editMode: true });
-    wrapper
-      .find(IconButton)
-      .at(1) // first one is delete button
-      .simulate('click');
+  // we cannot set props on the Row because of the WithDragDropContext wrapper
+  wrapper = setup({ component: columnWithoutChildren, editMode: true });
+  wrapper
+    .find(IconButton)
+    .at(1) // first one is delete button
+    .simulate('click');
 
-    expect(wrapper.find(BackgroundStyleDropdown)).toExist();
-  });
+  expect(wrapper.find(BackgroundStyleDropdown)).toExist();
+});
 
-  it('should call deleteComponent when deleted', () => {
-    const deleteComponent = sinon.spy();
-    const wrapper = setup({ editMode: true, deleteComponent });
-    wrapper.find(DeleteComponentButton).simulate('click');
-    expect(deleteComponent.callCount).toBe(1);
-  });
+test('should call deleteComponent when deleted', () => {
+  const deleteComponent = jest.fn();
+  const { getByTestId } = setup({ editMode: true, deleteComponent });
+  fireEvent.click(getByTestId('mock-delete-component-button'));
+  expect(deleteComponent).toHaveBeenCalledTimes(1);
+});
 
-  it('should pass its own width as availableColumnCount to children', () => {
-    const wrapper = setup();
-    const dashboardComponent = wrapper.find(DashboardComponent).first();
-    expect(dashboardComponent.props().availableColumnCount).toBe(
-      props.component.meta.width,
-    );
-  });
+test('should pass its own width as availableColumnCount to children', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('mock-dashboard-component')).toHaveTextContent(
+    props.component.meta.width,
+  );
+});
 
-  it('should pass appropriate dimensions to ResizableContainer', () => {
-    const wrapper = setup({ component: columnWithoutChildren });
-    const columnWidth = columnWithoutChildren.meta.width;
-    const resizableProps = wrapper.find(ResizableContainer).props();
-    expect(resizableProps.adjustableWidth).toBe(true);
-    expect(resizableProps.adjustableHeight).toBe(false);
-    expect(resizableProps.widthStep).toBe(props.columnWidth);
-    expect(resizableProps.widthMultiple).toBe(columnWidth);
-    expect(resizableProps.minWidthMultiple).toBe(props.minColumnWidth);
-    expect(resizableProps.maxWidthMultiple).toBe(
-      props.availableColumnCount + columnWidth,
-    );
-  });
+test.skip('should pass appropriate dimensions to ResizableContainer', () => {
+  const { container } = setup({ component: columnWithoutChildren });
+  const columnWidth = columnWithoutChildren.meta.width;
 
-  it('should increment the depth of its children', () => {
-    const wrapper = setup();
-    const dashboardComponent = wrapper.find(DashboardComponent);
-    expect(dashboardComponent.props().depth).toBe(props.depth + 1);
+  expect(container.querySelector('.resizable-container')).toEqual({
+    columnWidth,
   });
+  // const resizableProps = wrapper.find(ResizableContainer).props();
+  // expect(resizableProps.adjustableWidth).toBe(true);
+  // expect(resizableProps.adjustableHeight).toBe(false);
+  // expect(resizableProps.widthStep).toBe(props.columnWidth);
+  // expect(resizableProps.widthMultiple).toBe(columnWidth);
+  // expect(resizableProps.minWidthMultiple).toBe(props.minColumnWidth);
+  // expect(resizableProps.maxWidthMultiple).toBe(
+  //   props.availableColumnCount + columnWidth,
+  // );
+});
+
+test('should increment the depth of its children', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('mock-dashboard-component')).toHaveAttribute(
+    'depth',
+    `${props.depth + 1}`,
+  );
 });
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Row.test.jsx b/superset-frontend/src/dashboard/components/gridComponents/Row.test.jsx
index 84591e5a88..db63f1f1cc 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Row.test.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Row.test.jsx
@@ -16,134 +16,153 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Provider } from 'react-redux';
 import React from 'react';
-import { mount } from 'enzyme';
-import sinon from 'sinon';
-import { DndProvider } from 'react-dnd';
-import { HTML5Backend } from 'react-dnd-html5-backend';
-import { MemoryRouter } from 'react-router-dom';
+import { fireEvent, render } from 'spec/helpers/testing-library';
 
 import BackgroundStyleDropdown from 'src/dashboard/components/menu/BackgroundStyleDropdown';
-import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
-import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
-import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
-import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
 import IconButton from 'src/dashboard/components/IconButton';
 import Row from 'src/dashboard/components/gridComponents/Row';
-import WithPopoverMenu from 'src/dashboard/components/menu/WithPopoverMenu';
 import { DASHBOARD_GRID_ID } from 'src/dashboard/util/constants';
-import { supersetTheme, ThemeProvider } from '@superset-ui/core';
 
 import { getMockStore } from 'spec/fixtures/mockStore';
 import { dashboardLayout as mockLayout } from 'spec/fixtures/mockDashboardLayout';
 import { initialState } from 'src/SqlLab/fixtures';
 
-describe('Row', () => {
-  const rowWithoutChildren = { ...mockLayout.present.ROW_ID, children: [] };
-  const props = {
-    id: 'ROW_ID',
-    parentId: DASHBOARD_GRID_ID,
-    component: mockLayout.present.ROW_ID,
-    parentComponent: mockLayout.present[DASHBOARD_GRID_ID],
-    index: 0,
-    depth: 2,
-    editMode: false,
-    availableColumnCount: 12,
-    columnWidth: 50,
-    occupiedColumnCount: 6,
-    onResizeStart() {},
-    onResize() {},
-    onResizeStop() {},
-    handleComponentDrop() {},
-    deleteComponent() {},
-    updateComponents() {},
-  };
-
-  function setup(overrideProps) {
-    // We have to wrap provide DragDropContext for the underlying DragDroppable
-    // otherwise we cannot assert on DragDroppable children
-    const mockStore = getMockStore({
-      ...initialState,
-    });
-    const wrapper = mount(
-      <Provider store={mockStore}>
-        <MemoryRouter>
-          <DndProvider backend={HTML5Backend}>
-            <Row {...props} {...overrideProps} />
-          </DndProvider>
-        </MemoryRouter>
-      </Provider>,
-      {
-        wrappingComponent: ThemeProvider,
-        wrappingComponentProps: { theme: supersetTheme },
-      },
-    );
-    return wrapper;
-  }
-
-  it('should render a DragDroppable', () => {
-    // don't count child DragDroppables
-    const wrapper = setup({ component: rowWithoutChildren });
-    expect(wrapper.find(DragDroppable)).toExist();
+jest.mock(
+  'src/dashboard/containers/DashboardComponent',
+  () =>
+    ({ availableColumnCount, depth }) =>
+      (
+        <div data-test="mock-dashboard-component" depth={depth}>
+          {availableColumnCount}
+        </div>
+      ),
+);
+
+jest.mock(
+  'src/dashboard/components/menu/WithPopoverMenu',
+  () =>
+    ({ children }) =>
+      <div data-test="mock-with-popover-menu">{children}</div>,
+);
+
+jest.mock(
+  'src/dashboard/components/DeleteComponentButton',
+  () =>
+    ({ onDelete }) =>
+      (
+        <button
+          type="button"
+          data-test="mock-delete-component-button"
+          onClick={onDelete}
+        >
+          Delete
+        </button>
+      ),
+);
+
+const rowWithoutChildren = { ...mockLayout.present.ROW_ID, children: [] };
+const props = {
+  id: 'ROW_ID',
+  parentId: DASHBOARD_GRID_ID,
+  component: mockLayout.present.ROW_ID,
+  parentComponent: mockLayout.present[DASHBOARD_GRID_ID],
+  index: 0,
+  depth: 2,
+  editMode: false,
+  availableColumnCount: 12,
+  columnWidth: 50,
+  occupiedColumnCount: 6,
+  onResizeStart() {},
+  onResize() {},
+  onResizeStop() {},
+  handleComponentDrop() {},
+  deleteComponent() {},
+  updateComponents() {},
+};
+
+function setup(overrideProps) {
+  // We have to wrap provide DragDropContext for the underlying DragDroppable
+  // otherwise we cannot assert on DragDroppable children
+  const mockStore = getMockStore({
+    ...initialState,
   });
 
-  it('should render a WithPopoverMenu', () => {
-    // don't count child DragDroppables
-    const wrapper = setup({ component: rowWithoutChildren });
-    expect(wrapper.find(WithPopoverMenu)).toExist();
+  return render(<Row {...props} {...overrideProps} />, {
+    store: mockStore,
+    useDnd: true,
+    useRouter: true,
   });
+}
 
-  it('should render a HoverMenu in editMode', () => {
-    let wrapper = setup({ component: rowWithoutChildren });
-    expect(wrapper.find(HoverMenu)).not.toExist();
+test('should render a DragDroppable', () => {
+  // don't count child DragDroppables
+  const { getByTestId } = setup({ component: rowWithoutChildren });
+  expect(getByTestId('dragdroppable-object')).toBeInTheDocument();
+});
 
-    // we cannot set props on the Row because of the WithDragDropContext wrapper
-    wrapper = setup({ component: rowWithoutChildren, editMode: true });
-    expect(wrapper.find(HoverMenu)).toExist();
+test('should skip rendering HoverMenu and DeleteComponentButton when not in editMode', () => {
+  const { container, queryByTestId } = setup({
+    component: rowWithoutChildren,
   });
+  expect(container.querySelector('.hover-menu')).not.toBeInTheDocument();
+  expect(queryByTestId('mock-delete-component-button')).not.toBeInTheDocument();
+});
 
-  it('should render a DeleteComponentButton in editMode', () => {
-    let wrapper = setup({ component: rowWithoutChildren });
-    expect(wrapper.find(DeleteComponentButton)).not.toExist();
+test('should render a WithPopoverMenu', () => {
+  // don't count child DragDroppables
+  const { getByTestId } = setup({ component: rowWithoutChildren });
+  expect(getByTestId('mock-with-popover-menu')).toBeInTheDocument();
+});
 
-    // we cannot set props on the Row because of the WithDragDropContext wrapper
-    wrapper = setup({ component: rowWithoutChildren, editMode: true });
-    expect(wrapper.find(DeleteComponentButton)).toExist();
+test('should render a HoverMenu in editMode', () => {
+  const { container } = setup({
+    component: rowWithoutChildren,
+    editMode: true,
   });
+  expect(container.querySelector('.hover-menu')).toBeInTheDocument();
+});
 
-  it('should render a BackgroundStyleDropdown when focused', () => {
-    let wrapper = setup({ component: rowWithoutChildren });
-    expect(wrapper.find(BackgroundStyleDropdown)).not.toExist();
+test('should render a DeleteComponentButton in editMode', () => {
+  const { getByTestId } = setup({
+    component: rowWithoutChildren,
+    editMode: true,
+  });
+  expect(getByTestId('mock-delete-component-button')).toBeInTheDocument();
+});
 
-    // we cannot set props on the Row because of the WithDragDropContext wrapper
-    wrapper = setup({ component: rowWithoutChildren, editMode: true });
-    wrapper
-      .find(IconButton)
-      .at(1) // first one is delete button
-      .simulate('click');
+test.skip('should render a BackgroundStyleDropdown when focused', () => {
+  let wrapper = setup({ component: rowWithoutChildren });
+  expect(wrapper.find(BackgroundStyleDropdown)).not.toExist();
 
-    expect(wrapper.find(BackgroundStyleDropdown)).toExist();
-  });
+  // we cannot set props on the Row because of the WithDragDropContext wrapper
+  wrapper = setup({ component: rowWithoutChildren, editMode: true });
+  wrapper
+    .find(IconButton)
+    .at(1) // first one is delete button
+    .simulate('click');
 
-  it('should call deleteComponent when deleted', () => {
-    const deleteComponent = sinon.spy();
-    const wrapper = setup({ editMode: true, deleteComponent });
-    wrapper.find(DeleteComponentButton).simulate('click');
-    expect(deleteComponent.callCount).toBe(1);
-  });
+  expect(wrapper.find(BackgroundStyleDropdown)).toExist();
+});
 
-  it('should pass appropriate availableColumnCount to children', () => {
-    const wrapper = setup();
-    const dashboardComponent = wrapper.find(DashboardComponent).first();
-    expect(dashboardComponent.props().availableColumnCount).toBe(
-      props.availableColumnCount - props.occupiedColumnCount,
-    );
-  });
+test('should call deleteComponent when deleted', () => {
+  const deleteComponent = jest.fn();
+  const { getByTestId } = setup({ editMode: true, deleteComponent });
+  fireEvent.click(getByTestId('mock-delete-component-button'));
+  expect(deleteComponent).toHaveBeenCalledTimes(1);
+});
 
-  it('should increment the depth of its children', () => {
-    const wrapper = setup();
-    const dashboardComponent = wrapper.find(DashboardComponent).first();
-    expect(dashboardComponent.props().depth).toBe(props.depth + 1);
-  });
+test('should pass appropriate availableColumnCount to children', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('mock-dashboard-component')).toHaveTextContent(
+    props.availableColumnCount - props.occupiedColumnCount,
+  );
+});
+
+test('should increment the depth of its children', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('mock-dashboard-component')).toHaveAttribute(
+    'depth',
+    `${props.depth + 1}`,
+  );
 });
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tabs.test.jsx b/superset-frontend/src/dashboard/components/gridComponents/Tabs.test.jsx
index 359e58b5a8..34a42e3750 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tabs.test.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tabs.test.jsx
@@ -16,20 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Provider } from 'react-redux';
 import React from 'react';
-import { shallow } from 'enzyme';
-import sinon from 'sinon';
-import { DndProvider } from 'react-dnd';
-import { HTML5Backend } from 'react-dnd-html5-backend';
+import { fireEvent, render } from 'spec/helpers/testing-library';
 
-import { LineEditableTabs } from 'src/components/Tabs';
 import { AntdModal } from 'src/components';
-import { styledMount as mount } from 'spec/helpers/theming';
-import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
-import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
-import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
-import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
+import fetchMock from 'fetch-mock';
 import { Tabs } from 'src/dashboard/components/gridComponents/Tabs';
 import { DASHBOARD_ROOT_ID } from 'src/dashboard/util/constants';
 import emptyDashboardLayout from 'src/dashboard/fixtures/emptyDashboardLayout';
@@ -38,177 +29,167 @@ import { getMockStore } from 'spec/fixtures/mockStore';
 import { nativeFilters } from 'spec/fixtures/mockNativeFilters';
 import { initialState } from 'src/SqlLab/fixtures';
 
-describe('Tabs', () => {
-  const props = {
-    id: 'TABS_ID',
-    parentId: DASHBOARD_ROOT_ID,
-    component: dashboardLayoutWithTabs.present.TABS_ID,
-    parentComponent: dashboardLayoutWithTabs.present[DASHBOARD_ROOT_ID],
-    index: 0,
-    depth: 1,
-    renderTabContent: true,
-    editMode: false,
-    availableColumnCount: 12,
-    columnWidth: 50,
-    dashboardId: 1,
-    onResizeStart() {},
-    onResize() {},
-    onResizeStop() {},
-    createComponent() {},
-    handleComponentDrop() {},
-    onChangeTab() {},
-    deleteComponent() {},
-    updateComponents() {},
-    logEvent() {},
-    dashboardLayout: emptyDashboardLayout,
-    nativeFilters: nativeFilters.filters,
-  };
-
-  const mockStore = getMockStore({
-    ...initialState,
-    dashboardLayout: dashboardLayoutWithTabs,
-    dashboardFilters: {},
-  });
-
-  function setup(overrideProps) {
-    // We have to wrap provide DragDropContext for the underlying DragDroppable
-    // otherwise we cannot assert on DragDroppable children
-    const wrapper = mount(
-      <Provider store={mockStore}>
-        <DndProvider backend={HTML5Backend}>
-          <Tabs {...props} {...overrideProps} />
-        </DndProvider>
-      </Provider>,
-    );
-    return wrapper;
-  }
-
-  it('should render a DragDroppable', () => {
-    // test just Tabs with no children DragDroppables
-    const wrapper = setup({ component: { ...props.component, children: [] } });
-    expect(wrapper.find(DragDroppable)).toExist();
-  });
-
-  it('should render non-editable tabs', () => {
-    const wrapper = setup();
-    expect(wrapper.find(LineEditableTabs)).toExist();
-    expect(wrapper.find('.ant-tabs-nav-add').exists()).toBeFalsy();
-  });
-
-  it('should render a tab pane for each child', () => {
-    const wrapper = setup();
-    expect(wrapper.find(LineEditableTabs.TabPane)).toHaveLength(
-      props.component.children.length,
-    );
-  });
+jest.mock('src/dashboard/containers/DashboardComponent', () => ({ id }) => (
+  <div data-test="mock-dashboard-component">{id}</div>
+));
+
+jest.mock(
+  'src/dashboard/components/DeleteComponentButton',
+  () =>
+    ({ onDelete }) =>
+      (
+        <button
+          type="button"
+          data-test="mock-delete-component-button"
+          onClick={onDelete}
+        >
+          Delete
+        </button>
+      ),
+);
+
+fetchMock.post('glob:*/r/shortener/', {});
+
+const props = {
+  id: 'TABS_ID',
+  parentId: DASHBOARD_ROOT_ID,
+  component: dashboardLayoutWithTabs.present.TABS_ID,
+  parentComponent: dashboardLayoutWithTabs.present[DASHBOARD_ROOT_ID],
+  index: 0,
+  depth: 1,
+  renderTabContent: true,
+  editMode: false,
+  availableColumnCount: 12,
+  columnWidth: 50,
+  dashboardId: 1,
+  onResizeStart() {},
+  onResize() {},
+  onResizeStop() {},
+  createComponent() {},
+  handleComponentDrop() {},
+  onChangeTab() {},
+  deleteComponent() {},
+  updateComponents() {},
+  logEvent() {},
+  dashboardLayout: emptyDashboardLayout,
+  nativeFilters: nativeFilters.filters,
+};
+
+const mockStore = getMockStore({
+  ...initialState,
+  dashboardLayout: dashboardLayoutWithTabs,
+  dashboardFilters: {},
+});
 
-  it('should render editable tabs in editMode', () => {
-    const wrapper = setup({ editMode: true });
-    expect(wrapper.find(LineEditableTabs)).toExist();
-    expect(wrapper.find('.ant-tabs-nav-add')).toExist();
+function setup(overrideProps) {
+  return render(<Tabs {...props} {...overrideProps} />, {
+    useDnd: true,
+    useRouter: true,
+    store: mockStore,
   });
+}
 
-  it('should render a DashboardComponent for each child', () => {
-    // note: this does not test Tab content
-    const wrapper = setup({ renderTabContent: false });
-    expect(wrapper.find(DashboardComponent)).toHaveLength(
-      props.component.children.length,
-    );
+test('should render a DragDroppable', () => {
+  // test just Tabs with no children DragDroppables
+  const { getByTestId } = setup({
+    component: { ...props.component, children: [] },
   });
+  expect(getByTestId('dragdroppable-object')).toBeInTheDocument();
+});
 
-  it('should call createComponent if the (+) tab is clicked', () => {
-    const createComponent = sinon.spy();
-    const wrapper = setup({ editMode: true, createComponent });
-    wrapper
-      .find('[data-test="dashboard-component-tabs"] .ant-tabs-nav-add')
-      .last()
-      .simulate('click');
+test('should render non-editable tabs', () => {
+  const { getAllByRole, container } = setup();
+  expect(getAllByRole('tab')[0]).toBeInTheDocument();
+  expect(container.querySelector('.ant-tabs-nav-add')).not.toBeInTheDocument();
+});
 
-    expect(createComponent.callCount).toBe(1);
-  });
+test('should render a tab pane for each child', () => {
+  const { getAllByRole } = setup();
+  expect(getAllByRole('tab')).toHaveLength(props.component.children.length);
+});
 
-  it('should call onChangeTab when a tab is clicked', () => {
-    const onChangeTab = sinon.spy();
-    const wrapper = setup({ editMode: true, onChangeTab });
-    wrapper
-      .find('[data-test="dashboard-component-tabs"] .ant-tabs-tab')
-      .at(1) // will not call if it is already selected
-      .simulate('click');
+test('should render editable tabs in editMode', () => {
+  const { getAllByRole, container } = setup({ editMode: true });
+  expect(getAllByRole('tab')[0]).toBeInTheDocument();
+  expect(container.querySelector('.ant-tabs-nav-add')).toBeInTheDocument();
+});
 
-    expect(onChangeTab.callCount).toBe(1);
-  });
+test('should render a DashboardComponent for each child', () => {
+  // note: this does not test Tab content
+  const { getAllByTestId } = setup({ renderTabContent: false });
+  expect(getAllByTestId('mock-dashboard-component')).toHaveLength(
+    props.component.children.length,
+  );
+});
 
-  it('should not call onChangeTab when anchor link is clicked', () => {
-    const onChangeTab = sinon.spy();
-    const wrapper = setup({ editMode: true, onChangeTab });
-    wrapper
-      .find(
-        '[data-test="dashboard-component-tabs"] .ant-tabs-tab [role="button"]',
-      )
-      .at(1) // will not call if it is already selected
-      .simulate('click');
-
-    expect(onChangeTab.callCount).toBe(0);
-  });
+test('should call createComponent if the (+) tab is clicked', () => {
+  const createComponent = jest.fn();
+  const { getAllByRole } = setup({ editMode: true, createComponent });
+  const addButtons = getAllByRole('button', { name: 'Add tab' });
+  fireEvent.click(addButtons[0]);
+  expect(createComponent).toHaveBeenCalledTimes(1);
+});
 
-  it('should render a HoverMenu in editMode', () => {
-    let wrapper = setup();
-    expect(wrapper.find(HoverMenu)).not.toExist();
+test('should call onChangeTab when a tab is clicked', () => {
+  const onChangeTab = jest.fn();
+  const { getByRole } = setup({ editMode: true, onChangeTab });
+  const newTab = getByRole('tab', { selected: false });
+  fireEvent.click(newTab);
+  expect(onChangeTab).toHaveBeenCalledTimes(1);
+});
 
-    wrapper = setup({ editMode: true });
-    expect(wrapper.find(HoverMenu)).toExist();
-  });
+test('should not call onChangeTab when anchor link is clicked', () => {
+  const onChangeTab = jest.fn();
+  const { getByRole } = setup({ editMode: true, onChangeTab });
+  const currentTab = getByRole('tab', { selected: true });
+  fireEvent.click(currentTab);
 
-  it('should render a DeleteComponentButton in editMode', () => {
-    let wrapper = setup();
-    expect(wrapper.find(DeleteComponentButton)).not.toExist();
+  expect(onChangeTab).toHaveBeenCalledTimes(0);
+});
 
-    wrapper = setup({ editMode: true });
-    expect(wrapper.find(DeleteComponentButton)).toExist();
-  });
+test('should render a HoverMenu in editMode', () => {
+  const { container } = setup({ editMode: true });
+  expect(container.querySelector('.hover-menu')).toBeInTheDocument();
+});
 
-  it('should call deleteComponent when deleted', () => {
-    const deleteComponent = sinon.spy();
-    const wrapper = setup({ editMode: true, deleteComponent });
-    wrapper.find(DeleteComponentButton).simulate('click');
+test('should render a DeleteComponentButton in editMode', () => {
+  const { getByTestId } = setup({ editMode: true });
+  expect(getByTestId('mock-delete-component-button')).toBeInTheDocument();
+});
 
-    expect(deleteComponent.callCount).toBe(1);
-  });
+test('should call deleteComponent when deleted', () => {
+  const deleteComponent = jest.fn();
+  const { getByTestId } = setup({ editMode: true, deleteComponent });
+  fireEvent.click(getByTestId('mock-delete-component-button'));
+  expect(deleteComponent).toHaveBeenCalledTimes(1);
+});
 
-  it('should direct display direct-link tab', () => {
-    let wrapper = shallow(<Tabs {...props} />);
-    // default show first tab child
-    expect(wrapper.state('tabIndex')).toBe(0);
-
-    // display child in directPathToChild list
-    const directPathToChild =
-      dashboardLayoutWithTabs.present.ROW_ID2.parents.slice();
-    const directLinkProps = {
-      ...props,
-      directPathToChild,
-    };
-
-    wrapper = shallow(<Tabs {...directLinkProps} />);
-    expect(wrapper.state('tabIndex')).toBe(1);
-  });
+test('should direct display direct-link tab', () => {
+  // display child in directPathToChild list
+  const directPathToChild =
+    dashboardLayoutWithTabs.present.ROW_ID2.parents.slice();
+  const directLinkProps = {
+    ...props,
+    directPathToChild,
+  };
+  const { getByRole } = setup(directLinkProps);
+  expect(getByRole('tab', { selected: true })).toHaveTextContent('TAB_ID2');
+});
 
-  it('should render Modal when clicked remove tab button', () => {
-    const deleteComponent = sinon.spy();
-    const modalMock = jest.spyOn(AntdModal, 'confirm');
-    const wrapper = setup({ editMode: true, deleteComponent });
-    wrapper.find('.ant-tabs-tab-remove').at(0).simulate('click');
-    expect(modalMock.mock.calls).toHaveLength(1);
-    expect(deleteComponent.callCount).toBe(0);
-  });
+test('should render Modal when clicked remove tab button', () => {
+  const deleteComponent = jest.fn();
+  const modalMock = jest.spyOn(AntdModal, 'confirm');
+  const { container } = setup({ editMode: true, deleteComponent });
+  fireEvent.click(container.querySelector('.ant-tabs-tab-remove'));
+  expect(modalMock).toHaveBeenCalledTimes(1);
+  expect(deleteComponent).toHaveBeenCalledTimes(0);
+});
 
-  it('should set new tab key if dashboardId was changed', () => {
-    const wrapper = shallow(<Tabs {...props} />);
-    expect(wrapper.state('activeKey')).toBe('TAB_ID');
-    wrapper.setProps({
-      ...props,
-      dashboardId: 2,
-      component: dashboardLayoutWithTabs.present.TAB_ID,
-    });
-    expect(wrapper.state('activeKey')).toBe('ROW_ID');
+test('should set new tab key if dashboardId was changed', () => {
+  const { getByRole } = setup({
+    ...props,
+    dashboardId: 2,
+    component: dashboardLayoutWithTabs.present.TAB_ID,
   });
+  expect(getByRole('tab', { selected: true })).toHaveTextContent('ROW_ID');
 });
diff --git a/superset-frontend/src/dashboard/components/gridComponents/new/NewColumn.test.jsx b/superset-frontend/src/dashboard/components/gridComponents/new/NewColumn.test.jsx
index 696dec899a..0818a9584e 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/new/NewColumn.test.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/new/NewColumn.test.jsx
@@ -17,29 +17,32 @@
  * under the License.
  */
 import React from 'react';
-import { shallow } from 'enzyme';
+import { render } from 'spec/helpers/testing-library';
 
-import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
 import NewColumn from 'src/dashboard/components/gridComponents/new/NewColumn';
 
 import { NEW_COLUMN_ID } from 'src/dashboard/util/constants';
 import { COLUMN_TYPE } from 'src/dashboard/util/componentTypes';
 
-describe('NewColumn', () => {
-  function setup() {
-    return shallow(<NewColumn />);
-  }
+jest.mock(
+  'src/dashboard/components/gridComponents/new/DraggableNewComponent',
+  () =>
+    ({ type, id }) =>
+      <div data-test="mock-draggable-new-component">{`${type}:${id}`}</div>,
+);
 
-  it('should render a DraggableNewComponent', () => {
-    const wrapper = setup();
-    expect(wrapper.find(DraggableNewComponent)).toExist();
-  });
+function setup() {
+  return render(<NewColumn />);
+}
 
-  it('should set appropriate type and id', () => {
-    const wrapper = setup();
-    expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
-      type: COLUMN_TYPE,
-      id: NEW_COLUMN_ID,
-    });
-  });
+test('should render a DraggableNewComponent', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('mock-draggable-new-component')).toBeInTheDocument();
+});
+
+test('should set appropriate type and id', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('mock-draggable-new-component')).toHaveTextContent(
+    `${COLUMN_TYPE}:${NEW_COLUMN_ID}`,
+  );
 });
diff --git a/superset-frontend/src/dashboard/components/gridComponents/new/NewDivider.test.jsx b/superset-frontend/src/dashboard/components/gridComponents/new/NewDivider.test.jsx
index 77a0ab4f26..ad067a2f2f 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/new/NewDivider.test.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/new/NewDivider.test.jsx
@@ -17,29 +17,32 @@
  * under the License.
  */
 import React from 'react';
-import { shallow } from 'enzyme';
+import { render } from 'spec/helpers/testing-library';
 
-import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
 import NewDivider from 'src/dashboard/components/gridComponents/new/NewDivider';
 
 import { NEW_DIVIDER_ID } from 'src/dashboard/util/constants';
 import { DIVIDER_TYPE } from 'src/dashboard/util/componentTypes';
 
-describe('NewDivider', () => {
-  function setup() {
-    return shallow(<NewDivider />);
-  }
+jest.mock(
+  'src/dashboard/components/gridComponents/new/DraggableNewComponent',
+  () =>
+    ({ type, id }) =>
+      <div data-test="mock-draggable-new-component">{`${type}:${id}`}</div>,
+);
 
-  it('should render a DraggableNewComponent', () => {
-    const wrapper = setup();
-    expect(wrapper.find(DraggableNewComponent)).toExist();
-  });
+function setup() {
+  return render(<NewDivider />);
+}
 
-  it('should set appropriate type and id', () => {
-    const wrapper = setup();
-    expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
-      type: DIVIDER_TYPE,
-      id: NEW_DIVIDER_ID,
-    });
-  });
+test('should render a DraggableNewComponent', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('mock-draggable-new-component')).toBeInTheDocument();
+});
+
+test('should set appropriate type and id', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('mock-draggable-new-component')).toHaveTextContent(
+    `${DIVIDER_TYPE}:${NEW_DIVIDER_ID}`,
+  );
 });
diff --git a/superset-frontend/src/dashboard/components/gridComponents/new/NewHeader.test.jsx b/superset-frontend/src/dashboard/components/gridComponents/new/NewHeader.test.jsx
index 8c91cb655b..da87442399 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/new/NewHeader.test.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/new/NewHeader.test.jsx
@@ -17,29 +17,32 @@
  * under the License.
  */
 import React from 'react';
-import { shallow } from 'enzyme';
+import { render } from 'spec/helpers/testing-library';
 
-import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
 import NewHeader from 'src/dashboard/components/gridComponents/new/NewHeader';
 
 import { NEW_HEADER_ID } from 'src/dashboard/util/constants';
 import { HEADER_TYPE } from 'src/dashboard/util/componentTypes';
 
-describe('NewHeader', () => {
-  function setup() {
-    return shallow(<NewHeader />);
-  }
+jest.mock(
+  'src/dashboard/components/gridComponents/new/DraggableNewComponent',
+  () =>
+    ({ type, id }) =>
+      <div data-test="mock-draggable-new-component">{`${type}:${id}`}</div>,
+);
 
-  it('should render a DraggableNewComponent', () => {
-    const wrapper = setup();
-    expect(wrapper.find(DraggableNewComponent)).toExist();
-  });
+function setup() {
+  return render(<NewHeader />);
+}
 
-  it('should set appropriate type and id', () => {
-    const wrapper = setup();
-    expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
-      type: HEADER_TYPE,
-      id: NEW_HEADER_ID,
-    });
-  });
+test('should render a DraggableNewComponent', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('mock-draggable-new-component')).toBeInTheDocument();
+});
+
+test('should set appropriate type and id', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('mock-draggable-new-component')).toHaveTextContent(
+    `${HEADER_TYPE}:${NEW_HEADER_ID}`,
+  );
 });
diff --git a/superset-frontend/src/dashboard/components/gridComponents/new/NewRow.test.jsx b/superset-frontend/src/dashboard/components/gridComponents/new/NewRow.test.jsx
index de70ece0bd..ce8af91719 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/new/NewRow.test.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/new/NewRow.test.jsx
@@ -17,29 +17,32 @@
  * under the License.
  */
 import React from 'react';
-import { shallow } from 'enzyme';
+import { render } from 'spec/helpers/testing-library';
 
-import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
 import NewRow from 'src/dashboard/components/gridComponents/new/NewRow';
 
 import { NEW_ROW_ID } from 'src/dashboard/util/constants';
 import { ROW_TYPE } from 'src/dashboard/util/componentTypes';
 
-describe('NewRow', () => {
-  function setup() {
-    return shallow(<NewRow />);
-  }
+jest.mock(
+  'src/dashboard/components/gridComponents/new/DraggableNewComponent',
+  () =>
+    ({ type, id }) =>
+      <div data-test="mock-draggable-new-component">{`${type}:${id}`}</div>,
+);
 
-  it('should render a DraggableNewComponent', () => {
-    const wrapper = setup();
-    expect(wrapper.find(DraggableNewComponent)).toExist();
-  });
+function setup() {
+  return render(<NewRow />);
+}
 
-  it('should set appropriate type and id', () => {
-    const wrapper = setup();
-    expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
-      type: ROW_TYPE,
-      id: NEW_ROW_ID,
-    });
-  });
+test('should render a DraggableNewComponent', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('mock-draggable-new-component')).toBeInTheDocument();
+});
+
+test('should set appropriate type and id', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('mock-draggable-new-component')).toHaveTextContent(
+    `${ROW_TYPE}:${NEW_ROW_ID}`,
+  );
 });
diff --git a/superset-frontend/src/dashboard/components/gridComponents/new/NewTabs.test.jsx b/superset-frontend/src/dashboard/components/gridComponents/new/NewTabs.test.jsx
index ec174340b4..1bcaeb84e4 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/new/NewTabs.test.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/new/NewTabs.test.jsx
@@ -17,29 +17,32 @@
  * under the License.
  */
 import React from 'react';
-import { shallow } from 'enzyme';
+import { render } from 'spec/helpers/testing-library';
 
-import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
 import NewTabs from 'src/dashboard/components/gridComponents/new/NewTabs';
 
 import { NEW_TABS_ID } from 'src/dashboard/util/constants';
 import { TABS_TYPE } from 'src/dashboard/util/componentTypes';
 
-describe('NewTabs', () => {
-  function setup() {
-    return shallow(<NewTabs />);
-  }
+jest.mock(
+  'src/dashboard/components/gridComponents/new/DraggableNewComponent',
+  () =>
+    ({ type, id }) =>
+      <div data-test="mock-draggable-new-component">{`${type}:${id}`}</div>,
+);
 
-  it('should render a DraggableNewComponent', () => {
-    const wrapper = setup();
-    expect(wrapper.find(DraggableNewComponent)).toExist();
-  });
+function setup() {
+  return render(<NewTabs />);
+}
 
-  it('should set appropriate type and id', () => {
-    const wrapper = setup();
-    expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
-      type: TABS_TYPE,
-      id: NEW_TABS_ID,
-    });
-  });
+test('should render a DraggableNewComponent', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('mock-draggable-new-component')).toBeInTheDocument();
+});
+
+test('should set appropriate type and id', () => {
+  const { getByTestId } = setup();
+  expect(getByTestId('mock-draggable-new-component')).toHaveTextContent(
+    `${TABS_TYPE}:${NEW_TABS_ID}`,
+  );
 });
diff --git a/superset-frontend/src/dashboard/components/menu/HoverMenu.test.tsx b/superset-frontend/src/dashboard/components/menu/HoverMenu.test.tsx
index 24106f1641..adf34938f5 100644
--- a/superset-frontend/src/dashboard/components/menu/HoverMenu.test.tsx
+++ b/superset-frontend/src/dashboard/components/menu/HoverMenu.test.tsx
@@ -17,13 +17,11 @@
  * under the License.
  */
 import React from 'react';
-import { shallow } from 'enzyme';
+import { render } from 'spec/helpers/testing-library';
 
 import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
 
-describe('HoverMenu', () => {
-  it('should render a div.hover-menu', () => {
-    const wrapper = shallow(<HoverMenu />);
-    expect(wrapper.find('.hover-menu')).toExist();
-  });
+test('should render a div.hover-menu', () => {
+  const { container } = render(<HoverMenu />);
+  expect(container.querySelector('.hover-menu')).toBeInTheDocument();
 });
diff --git a/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.test.jsx b/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.test.jsx
index af49b0df79..ca970da4e3 100644
--- a/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.test.jsx
+++ b/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.test.jsx
@@ -17,71 +17,68 @@
  * under the License.
  */
 import React from 'react';
-import { shallow } from 'enzyme';
+import { fireEvent, render } from 'spec/helpers/testing-library';
 
 import WithPopoverMenu from 'src/dashboard/components/menu/WithPopoverMenu';
 
-describe('WithPopoverMenu', () => {
-  const props = {
-    children: <div id="child" />,
-    disableClick: false,
-    menuItems: [<div id="menu1" />, <div id="menu2" />],
-    onChangeFocus() {},
-    shouldFocus: () => true, // needed for mock
-    isFocused: false,
-    editMode: false,
-  };
+const props = {
+  children: <div id="child" />,
+  disableClick: false,
+  menuItems: [<div id="menu1" />, <div id="menu2" />],
+  onChangeFocus() {},
+  shouldFocus: () => true, // needed for mock
+  isFocused: false,
+  editMode: false,
+};
 
-  function setup(overrideProps) {
-    const wrapper = shallow(<WithPopoverMenu {...props} {...overrideProps} />);
-    return wrapper;
-  }
+function setup(overrideProps) {
+  return render(<WithPopoverMenu {...props} {...overrideProps} />);
+}
 
-  it('should render a div with class "with-popover-menu"', () => {
-    const wrapper = setup();
-    expect(wrapper.find('.with-popover-menu')).toExist();
-  });
-
-  it('should render the passed children', () => {
-    const wrapper = setup();
-    expect(wrapper.find('#child')).toExist();
-  });
-
-  it('should focus on click in editMode', () => {
-    const wrapper = setup();
-    expect(wrapper.state('isFocused')).toBe(false);
-
-    wrapper.simulate('click');
-    expect(wrapper.state('isFocused')).toBe(false);
+test('should render a div with class "with-popover-menu"', () => {
+  const { container } = setup();
+  expect(container.querySelector('.with-popover-menu')).toBeInTheDocument();
+});
 
-    wrapper.setProps({ ...props, editMode: true });
-    wrapper.simulate('click');
-    expect(wrapper.state('isFocused')).toBe(true);
-  });
+test('should render the passed children', () => {
+  const { container } = setup();
+  expect(container.querySelector('#child')).toBeInTheDocument();
+});
 
-  it('should render menuItems when focused', () => {
-    const wrapper = setup({ editMode: true });
-    expect(wrapper.find('#menu1')).not.toExist();
-    expect(wrapper.find('#menu2')).not.toExist();
+test('should focus on click in editMode', () => {
+  const { container } = setup({ editMode: true });
+  fireEvent.click(container.querySelector('.with-popover-menu'));
+  expect(
+    container.querySelector('.with-popover-menu--focused'),
+  ).toBeInTheDocument();
+});
 
-    wrapper.simulate('click');
-    expect(wrapper.find('#menu1')).toExist();
-    expect(wrapper.find('#menu2')).toExist();
-  });
+test('should render menuItems when focused', () => {
+  const { container } = setup({ editMode: true });
+  expect(container.querySelector('#menu1')).not.toBeInTheDocument();
+  expect(container.querySelector('#menu2')).not.toBeInTheDocument();
 
-  it('should not focus when disableClick=true', () => {
-    const wrapper = setup({ disableClick: true, editMode: true });
-    expect(wrapper.state('isFocused')).toBe(false);
+  fireEvent.click(container.querySelector('.with-popover-menu'));
+  expect(container.querySelector('#menu1')).toBeInTheDocument();
+  expect(container.querySelector('#menu2')).toBeInTheDocument();
+});
 
-    wrapper.simulate('click');
-    expect(wrapper.state('isFocused')).toBe(false);
-  });
+test('should not focus when disableClick=true', () => {
+  const { container } = setup({ disableClick: true, editMode: true });
 
-  it('should use the passed shouldFocus func to determine if it should focus', () => {
-    const wrapper = setup({ editMode: true, shouldFocus: () => false });
-    expect(wrapper.state('isFocused')).toBe(false);
+  fireEvent.click(container.querySelector('.with-popover-menu'));
+  expect(
+    container.querySelector('.with-popover-menu--focused'),
+  ).not.toBeInTheDocument();
+});
 
-    wrapper.simulate('click');
-    expect(wrapper.state('isFocused')).toBe(false);
-  });
+test('should use the passed shouldFocus func to determine if it should focus', () => {
+  const { container } = setup({ editMode: true, shouldFocus: () => false });
+  expect(
+    container.querySelector('.with-popover-menu--focused'),
+  ).not.toBeInTheDocument();
+  fireEvent.click(container.querySelector('.with-popover-menu'));
+  expect(
+    container.querySelector('.with-popover-menu--focused'),
+  ).not.toBeInTheDocument();
 });
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx
index a1e600ad20..a9a598415a 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx
@@ -423,7 +423,7 @@ function FiltersConfigModal({
       )();
       resetForm(true);
     } else {
-      configFormRef.current.changeTab('configuration');
+      configFormRef.current?.changeTab?.('configuration');
     }
   };
 
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/NativeFiltersModal.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/NativeFiltersModal.test.tsx
index 06a566937d..3d2665ad47 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/NativeFiltersModal.test.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/NativeFiltersModal.test.tsx
@@ -16,18 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { ReactWrapper } from 'enzyme';
 import React from 'react';
-import { DndProvider } from 'react-dnd';
-import { HTML5Backend } from 'react-dnd-html5-backend';
-import { act } from 'react-dom/test-utils';
-import { Provider } from 'react-redux';
-import { mockStore } from 'spec/fixtures/mockStore';
-import { styledMount as mount } from 'spec/helpers/theming';
-import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
-import { AntdDropdown } from 'src/components';
-import { Menu } from 'src/components/Menu';
-import Alert from 'src/components/Alert';
+import { fireEvent, render } from 'spec/helpers/testing-library';
 import FiltersConfigModal from 'src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal';
 
 Object.defineProperty(window, 'matchMedia', {
@@ -59,83 +49,56 @@ jest.mock('@superset-ui/core', () => ({
   }),
 }));
 
-describe('FiltersConfigModal', () => {
-  const mockedProps = {
-    isOpen: true,
-    initialFilterId: 'NATIVE_FILTER-1',
-    createNewOnOpen: true,
-    onCancel: jest.fn(),
-    onSave: jest.fn(),
-  };
-  function setup(overridesProps?: any) {
-    return mount(
-      <Provider store={mockStore}>
-        <DndProvider backend={HTML5Backend}>
-          <FiltersConfigModal {...mockedProps} {...overridesProps} />
-        </DndProvider>
-      </Provider>,
-    );
-  }
-
-  it('should be a valid react element', () => {
-    expect(React.isValidElement(<FiltersConfigModal {...mockedProps} />)).toBe(
-      true,
-    );
+const mockedProps = {
+  isOpen: true,
+  initialFilterId: 'NATIVE_FILTER-1',
+  createNewOnOpen: true,
+  onCancel: jest.fn(),
+  onSave: jest.fn(),
+};
+function setup(overridesProps?: any) {
+  return render(<FiltersConfigModal {...mockedProps} {...overridesProps} />, {
+    useDnd: true,
+    useRedux: true,
   });
+}
 
-  it('the form validates required fields', async () => {
-    const onSave = jest.fn();
-    const wrapper = setup({ save: onSave });
-    act(() => {
-      wrapper
-        .find('input')
-        .first()
-        .simulate('change', { target: { value: 'test name' } });
+test('should be a valid react element', () => {
+  const { container } = setup();
+  expect(container).toBeInTheDocument();
+});
 
-      wrapper.find('.ant-modal-footer button').at(1).simulate('click');
-    });
-    await waitForComponentToPaint(wrapper);
-    expect(onSave.mock.calls).toHaveLength(0);
+test('the form validates required fields', async () => {
+  const onSave = jest.fn();
+  const { getByRole } = setup({ save: onSave });
+  fireEvent.change(getByRole('textbox', { name: 'Description' }), {
+    target: { value: 'test name' },
   });
+  const saveButton = getByRole('button', { name: 'Save' });
+  fireEvent.click(saveButton);
+  expect(onSave).toHaveBeenCalledTimes(0);
+});
 
-  describe('when click cancel', () => {
-    let onCancel: jest.Mock;
-    let wrapper: ReactWrapper;
-
-    beforeEach(() => {
-      onCancel = jest.fn();
-      wrapper = setup({ onCancel, createNewOnOpen: false });
-    });
-
-    async function clickCancel() {
-      act(() => {
-        wrapper.find('.ant-modal-footer button').at(0).simulate('click');
-      });
-      await waitForComponentToPaint(wrapper);
-    }
-
-    async function addFilter() {
-      act(() => {
-        wrapper.find(AntdDropdown).at(0).simulate('mouseEnter');
-      });
-      await waitForComponentToPaint(wrapper, 300);
-      act(() => {
-        wrapper.find(Menu.Item).at(0).simulate('click');
-      });
-    }
-
-    it('does not show alert when there is no unsaved filters', async () => {
-      await clickCancel();
-      expect(onCancel.mock.calls).toHaveLength(1);
-    });
+describe('createNewOnOpen', () => {
+  test('does not show alert when there is no unsaved filters', async () => {
+    const onCancel = jest.fn();
+    const { getByRole } = setup({ onCancel, createNewOnOpen: false });
+    fireEvent.click(getByRole('button', { name: 'Cancel' }));
+    expect(onCancel).toHaveBeenCalledTimes(1);
+  });
 
-    it('shows correct alert message for unsaved filters', async () => {
-      await addFilter();
-      await clickCancel();
-      expect(onCancel.mock.calls).toHaveLength(0);
-      expect(wrapper.find(Alert).text()).toContain(
-        'There are unsaved changes.',
-      );
+  test('shows correct alert message for unsaved filters', async () => {
+    const onCancel = jest.fn();
+    const { getByRole, getByTestId, findByRole } = setup({
+      onCancel,
+      createNewOnOpen: false,
     });
+    fireEvent.mouseOver(getByTestId('new-dropdown-icon'));
+    const addFilterButton = await findByRole('menuitem', { name: 'Filter' });
+    fireEvent.click(addFilterButton);
+    fireEvent.click(getByRole('button', { name: 'Cancel' }));
+    expect(onCancel).toHaveBeenCalledTimes(0);
+    expect(getByRole('alert')).toBeInTheDocument();
+    expect(getByRole('alert')).toHaveTextContent('There are unsaved changes.');
   });
 });