You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by kg...@apache.org on 2023/04/28 15:33:57 UTC

[superset] branch master updated: chore: Add Cypress tests for drill by (#23849)

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

kgabryje 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 60046ca1cc chore: Add Cypress tests for drill by (#23849)
60046ca1cc is described below

commit 60046ca1ccd99a2c2b80e500289dc9762556c3a5
Author: Kamil Gabryjelski <ka...@gmail.com>
AuthorDate: Fri Apr 28 17:33:45 2023 +0200

    chore: Add Cypress tests for drill by (#23849)
---
 .../cypress/e2e/dashboard/drillby.test.ts          | 705 +++++++++++++++++++++
 .../cypress/e2e/dashboard/drilltodetail.test.ts    |  14 +-
 .../cypress-base/cypress/e2e/dashboard/utils.ts    |  18 +
 .../cypress-base/cypress/e2e/explore/utils.ts      |  14 +-
 .../src/components/Chart/DrillBy/DrillByChart.tsx  |   1 +
 .../Chart/DrillBy/useDisplayModeToggle.tsx         |   1 +
 .../Chart/DrillBy/useResultsTableView.tsx          |   4 +-
 superset/examples/supported_charts_dashboard.py    |   2 +-
 tests/integration_tests/superset_test_config.py    |   3 +-
 9 files changed, 746 insertions(+), 16 deletions(-)

diff --git a/superset-frontend/cypress-base/cypress/e2e/dashboard/drillby.test.ts b/superset-frontend/cypress-base/cypress/e2e/dashboard/drillby.test.ts
new file mode 100644
index 0000000000..c365f66b4a
--- /dev/null
+++ b/superset-frontend/cypress-base/cypress/e2e/dashboard/drillby.test.ts
@@ -0,0 +1,705 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { Interception } from 'cypress/types/net-stubbing';
+import { waitForChartLoad } from 'cypress/utils';
+import { SUPPORTED_CHARTS_DASHBOARD } from 'cypress/utils/urls';
+import {
+  openTopLevelTab,
+  SUPPORTED_TIER1_CHARTS,
+  SUPPORTED_TIER2_CHARTS,
+} from './utils';
+import {
+  interceptExploreJson,
+  interceptV1ChartData,
+  interceptFormDataKey,
+} from '../explore/utils';
+
+const closeModal = () => {
+  cy.get('body').then($body => {
+    if ($body.find('[data-test="close-drill-by-modal"]').length) {
+      cy.getBySel('close-drill-by-modal').click({ force: true });
+    }
+  });
+};
+
+const openTableContextMenu = (
+  cellContent: string,
+  tableSelector = "[data-test-viz-type='table']",
+) => {
+  cy.get(tableSelector)
+    .scrollIntoView()
+    .contains(cellContent)
+    .first()
+    .rightclick();
+};
+
+const drillBy = (targetDrillByColumn: string, isLegacy = false) => {
+  if (isLegacy) {
+    interceptExploreJson('legacyData');
+  } else {
+    interceptV1ChartData();
+  }
+
+  cy.get('.ant-dropdown:not(.ant-dropdown-hidden)')
+    .first()
+    .find("[role='menu'] [role='menuitem'] [title='Drill by']")
+    .trigger('mouseover');
+  cy.get(
+    '.ant-dropdown-menu-submenu:not(.ant-dropdown-menu-hidden) [data-test="drill-by-submenu"]',
+  )
+    .find('[role="menuitem"]')
+    .contains(new RegExp(`^${targetDrillByColumn}$`))
+    .first()
+    .click({ force: true });
+
+  if (isLegacy) {
+    return cy.wait('@legacyData');
+  }
+  return cy.wait('@v1Data');
+};
+
+const verifyExpectedFormData = (
+  interceptedRequest: Interception,
+  expectedFormData: Record<string, any>,
+) => {
+  const actualFormData = interceptedRequest.request.body?.form_data;
+  Object.entries(expectedFormData).forEach(([key, val]) => {
+    expect(actualFormData?.[key]).to.eql(val);
+  });
+};
+
+const testEchart = (
+  vizType: string,
+  chartName: string,
+  drillClickCoordinates: [[number, number], [number, number]],
+  furtherDrillDimension = 'name',
+) => {
+  cy.get(`[data-test-viz-type='${vizType}'] canvas`).then($canvas => {
+    // click 'boy'
+    cy.wrap($canvas)
+      .scrollIntoView()
+      .trigger(
+        'mouseover',
+        drillClickCoordinates[0][0],
+        drillClickCoordinates[0][1],
+      )
+      .rightclick(drillClickCoordinates[0][0], drillClickCoordinates[0][1]);
+
+    drillBy('state').then(intercepted => {
+      verifyExpectedFormData(intercepted, {
+        groupby: ['state'],
+        adhoc_filters: [
+          {
+            clause: 'WHERE',
+            comparator: 'boy',
+            expressionType: 'SIMPLE',
+            operator: '==',
+            operatorId: 'EQUALS',
+            subject: 'gender',
+          },
+        ],
+      });
+    });
+
+    cy.getBySel(`"Drill by: ${chartName}-modal"`).as('drillByModal');
+
+    cy.get('@drillByModal')
+      .find('.draggable-trigger')
+      .should('contain', chartName);
+
+    cy.get('@drillByModal')
+      .find('.ant-breadcrumb')
+      .should('be.visible')
+      .and('contain', 'gender (boy)')
+      .and('contain', '/')
+      .and('contain', 'state');
+
+    cy.get('@drillByModal')
+      .find('[data-test="drill-by-chart"]')
+      .should('be.visible');
+
+    // further drill
+    cy.get(`[data-test="drill-by-chart"] canvas`).then($canvas => {
+      // click 'other'
+      cy.wrap($canvas)
+        .scrollIntoView()
+        .trigger(
+          'mouseover',
+          drillClickCoordinates[1][0],
+          drillClickCoordinates[1][1],
+        )
+        .rightclick(drillClickCoordinates[1][0], drillClickCoordinates[1][1]);
+
+      drillBy(furtherDrillDimension).then(intercepted => {
+        verifyExpectedFormData(intercepted, {
+          groupby: [furtherDrillDimension],
+          adhoc_filters: [
+            {
+              clause: 'WHERE',
+              comparator: 'boy',
+              expressionType: 'SIMPLE',
+              operator: '==',
+              operatorId: 'EQUALS',
+              subject: 'gender',
+            },
+            {
+              clause: 'WHERE',
+              comparator: 'other',
+              expressionType: 'SIMPLE',
+              operator: '==',
+              operatorId: 'EQUALS',
+              subject: 'state',
+            },
+          ],
+        });
+      });
+
+      cy.get('@drillByModal')
+        .find('[data-test="drill-by-chart"]')
+        .should('be.visible');
+
+      // undo - back to drill by state
+      interceptV1ChartData('drillByUndo');
+      cy.get('@drillByModal')
+        .find('.ant-breadcrumb')
+        .should('be.visible')
+        .and('contain', 'gender (boy)')
+        .and('contain', '/')
+        .and('contain', 'state (other)')
+        .and('contain', furtherDrillDimension)
+        .contains('state (other)')
+        .click();
+      cy.wait('@drillByUndo').then(intercepted => {
+        verifyExpectedFormData(intercepted, {
+          groupby: ['state'],
+          adhoc_filters: [
+            {
+              clause: 'WHERE',
+              comparator: 'boy',
+              expressionType: 'SIMPLE',
+              operator: '==',
+              operatorId: 'EQUALS',
+              subject: 'gender',
+            },
+          ],
+        });
+      });
+
+      cy.get('@drillByModal')
+        .find('.ant-breadcrumb')
+        .should('be.visible')
+        .and('contain', 'gender (boy)')
+        .and('contain', '/')
+        .and('not.contain', 'state (other)')
+        .and('not.contain', furtherDrillDimension)
+        .and('contain', 'state');
+
+      cy.get('@drillByModal')
+        .find('[data-test="drill-by-chart"]')
+        .should('be.visible');
+    });
+  });
+};
+
+describe('Drill by modal', () => {
+  beforeEach(() => {
+    closeModal();
+  });
+  before(() => {
+    cy.visit(SUPPORTED_CHARTS_DASHBOARD);
+  });
+
+  describe('Modal actions + Table', () => {
+    before(() => {
+      closeModal();
+      openTopLevelTab('Tier 1');
+      SUPPORTED_TIER1_CHARTS.forEach(waitForChartLoad);
+    });
+
+    it('opens the modal from the context menu', () => {
+      openTableContextMenu('boy');
+      drillBy('state').then(intercepted => {
+        verifyExpectedFormData(intercepted, {
+          groupby: ['state'],
+          adhoc_filters: [
+            {
+              clause: 'WHERE',
+              comparator: 'boy',
+              expressionType: 'SIMPLE',
+              operator: '==',
+              operatorId: 'EQUALS',
+              subject: 'gender',
+            },
+          ],
+        });
+      });
+
+      cy.getBySel('"Drill by: Table-modal"').as('drillByModal');
+
+      cy.get('@drillByModal')
+        .find('.draggable-trigger')
+        .should('contain', 'Drill by: Table');
+
+      cy.get('@drillByModal')
+        .find('[data-test="metadata-bar"]')
+        .should('be.visible');
+
+      cy.get('@drillByModal')
+        .find('.ant-breadcrumb')
+        .should('be.visible')
+        .and('contain', 'gender (boy)')
+        .and('contain', '/')
+        .and('contain', 'state');
+
+      cy.get('@drillByModal')
+        .find('[data-test="drill-by-chart"]')
+        .should('be.visible')
+        .and('contain', 'state')
+        .and('contain', 'sum__num');
+
+      // further drilling
+      openTableContextMenu('CA', '[data-test="drill-by-chart"]');
+      drillBy('name').then(intercepted => {
+        verifyExpectedFormData(intercepted, {
+          groupby: ['name'],
+          adhoc_filters: [
+            {
+              clause: 'WHERE',
+              comparator: 'boy',
+              expressionType: 'SIMPLE',
+              operator: '==',
+              operatorId: 'EQUALS',
+              subject: 'gender',
+            },
+            {
+              clause: 'WHERE',
+              comparator: 'CA',
+              expressionType: 'SIMPLE',
+              operator: '==',
+              operatorId: 'EQUALS',
+              subject: 'state',
+            },
+          ],
+        });
+      });
+
+      cy.get('@drillByModal')
+        .find('[data-test="drill-by-chart"]')
+        .should('be.visible')
+        .and('not.contain', 'state')
+        .and('contain', 'name')
+        .and('contain', 'sum__num');
+
+      // undo - back to drill by state
+      interceptV1ChartData('drillByUndo');
+      interceptFormDataKey();
+      cy.get('@drillByModal')
+        .find('.ant-breadcrumb')
+        .should('be.visible')
+        .and('contain', 'gender (boy)')
+        .and('contain', '/')
+        .and('contain', 'state (CA)')
+        .and('contain', 'name')
+        .contains('state (CA)')
+        .click();
+      cy.wait('@drillByUndo').then(intercepted => {
+        verifyExpectedFormData(intercepted, {
+          groupby: ['state'],
+          adhoc_filters: [
+            {
+              clause: 'WHERE',
+              comparator: 'boy',
+              expressionType: 'SIMPLE',
+              operator: '==',
+              operatorId: 'EQUALS',
+              subject: 'gender',
+            },
+          ],
+        });
+      });
+
+      cy.get('@drillByModal')
+        .find('[data-test="drill-by-chart"]')
+        .should('be.visible')
+        .and('not.contain', 'name')
+        .and('contain', 'state')
+        .and('contain', 'sum__num');
+
+      cy.get('@drillByModal')
+        .find('.ant-breadcrumb')
+        .should('be.visible')
+        .and('contain', 'gender (boy)')
+        .and('contain', '/')
+        .and('not.contain', 'state (CA)')
+        .and('not.contain', 'name')
+        .and('contain', 'state');
+
+      cy.get('@drillByModal')
+        .find('[data-test="drill-by-display-toggle"]')
+        .contains('Table')
+        .click();
+
+      cy.getBySel('drill-by-chart').should('not.exist');
+
+      cy.get('@drillByModal')
+        .find('[data-test="drill-by-results-table"]')
+        .should('be.visible');
+
+      cy.wait('@formDataKey').then(intercept => {
+        cy.get('@drillByModal')
+          .contains('Edit chart')
+          .should('have.attr', 'href')
+          .and(
+            'contain',
+            `/explore/?form_data_key=${intercept.response?.body?.key}`,
+          );
+      });
+    });
+  });
+
+  describe('Tier 1 charts', () => {
+    before(() => {
+      closeModal();
+      openTopLevelTab('Tier 1');
+      SUPPORTED_TIER1_CHARTS.forEach(waitForChartLoad);
+    });
+
+    it('Pivot Table', () => {
+      openTableContextMenu('boy', "[data-test-viz-type='pivot_table_v2']");
+      drillBy('name').then(intercepted => {
+        verifyExpectedFormData(intercepted, {
+          groupbyRows: ['state'],
+          groupbyColumns: ['name'],
+          adhoc_filters: [
+            {
+              clause: 'WHERE',
+              comparator: 'boy',
+              expressionType: 'SIMPLE',
+              operator: '==',
+              operatorId: 'EQUALS',
+              subject: 'gender',
+            },
+          ],
+        });
+      });
+
+      cy.getBySel('"Drill by: Pivot Table-modal"').as('drillByModal');
+
+      cy.get('@drillByModal')
+        .find('.draggable-trigger')
+        .should('contain', 'Drill by: Pivot Table');
+
+      cy.get('@drillByModal')
+        .find('.ant-breadcrumb')
+        .should('be.visible')
+        .and('contain', 'gender (boy)')
+        .and('contain', '/')
+        .and('contain', 'name');
+
+      cy.get('@drillByModal')
+        .find('[data-test="drill-by-chart"]')
+        .should('be.visible')
+        .and('contain', 'state')
+        .and('contain', 'name')
+        .and('contain', 'sum__num')
+        .and('not.contain', 'Gender');
+
+      openTableContextMenu('CA', '[data-test="drill-by-chart"]');
+      drillBy('ds').then(intercepted => {
+        verifyExpectedFormData(intercepted, {
+          groupbyColumns: ['name'],
+          groupbyRows: ['ds'],
+          adhoc_filters: [
+            {
+              clause: 'WHERE',
+              comparator: 'boy',
+              expressionType: 'SIMPLE',
+              operator: '==',
+              operatorId: 'EQUALS',
+              subject: 'gender',
+            },
+            {
+              clause: 'WHERE',
+              comparator: 'CA',
+              expressionType: 'SIMPLE',
+              operator: '==',
+              operatorId: 'EQUALS',
+              subject: 'state',
+            },
+          ],
+        });
+      });
+
+      cy.get('@drillByModal')
+        .find('[data-test="drill-by-chart"]')
+        .should('be.visible')
+        .and('contain', 'name')
+        .and('contain', 'ds')
+        .and('contain', 'sum__num')
+        .and('not.contain', 'state');
+
+      interceptV1ChartData('drillByUndo');
+
+      cy.get('@drillByModal')
+        .find('.ant-breadcrumb')
+        .should('be.visible')
+        .and('contain', 'gender (boy)')
+        .and('contain', '/')
+        .and('contain', 'name (CA)')
+        .and('contain', 'ds')
+        .contains('name (CA)')
+        .click();
+      cy.wait('@drillByUndo').then(intercepted => {
+        verifyExpectedFormData(intercepted, {
+          groupbyRows: ['state'],
+          groupbyColumns: ['name'],
+          adhoc_filters: [
+            {
+              clause: 'WHERE',
+              comparator: 'boy',
+              expressionType: 'SIMPLE',
+              operator: '==',
+              operatorId: 'EQUALS',
+              subject: 'gender',
+            },
+          ],
+        });
+      });
+
+      cy.get('@drillByModal')
+        .find('[data-test="drill-by-chart"]')
+        .should('be.visible')
+        .and('not.contain', 'ds')
+        .and('contain', 'state')
+        .and('contain', 'name')
+        .and('contain', 'sum__num');
+
+      cy.get('@drillByModal')
+        .find('.ant-breadcrumb')
+        .should('be.visible')
+        .and('contain', 'gender (boy)')
+        .and('contain', '/')
+        .and('not.contain', 'name (CA)')
+        .and('not.contain', 'ds')
+        .and('contain', 'name');
+    });
+
+    it('Line chart', () => {
+      testEchart('echarts_timeseries_line', 'Time-Series Line Chart', [
+        [70, 93],
+        [70, 93],
+      ]);
+    });
+
+    it('Area Chart', () => {
+      testEchart('echarts_area', 'Time-Series Area Chart', [
+        [70, 93],
+        [70, 93],
+      ]);
+    });
+
+    it('Time-Series Scatter Chart', () => {
+      testEchart('echarts_timeseries_scatter', 'Time-Series Scatter Chart', [
+        [70, 93],
+        [70, 93],
+      ]);
+    });
+
+    it('Time-Series Bar Chart V2', () => {
+      testEchart('echarts_timeseries_bar', 'Time-Series Bar Chart V2', [
+        [70, 94],
+        [362, 68],
+      ]);
+    });
+
+    it('Pie Chart', () => {
+      testEchart('pie', 'Pie Chart', [
+        [243, 167],
+        [534, 248],
+      ]);
+    });
+  });
+
+  describe('Tier 2 charts', () => {
+    before(() => {
+      closeModal();
+      openTopLevelTab('Tier 2');
+      SUPPORTED_TIER2_CHARTS.forEach(waitForChartLoad);
+    });
+
+    it('Box Plot Chart', () => {
+      testEchart(
+        'box_plot',
+        'Box Plot Chart',
+        [
+          [139, 277],
+          [787, 441],
+        ],
+        'ds',
+      );
+    });
+
+    it('Time-Series Generic Chart', () => {
+      testEchart('echarts_timeseries', 'Time-Series Generic Chart', [
+        [70, 93],
+        [70, 93],
+      ]);
+    });
+
+    it('Time-Series Smooth Line Chart', () => {
+      testEchart('echarts_timeseries_smooth', 'Time-Series Smooth Line Chart', [
+        [70, 93],
+        [70, 93],
+      ]);
+    });
+
+    it('Time-Series Step Line Chart', () => {
+      testEchart('echarts_timeseries_step', 'Time-Series Step Line Chart', [
+        [70, 93],
+        [70, 93],
+      ]);
+    });
+
+    it('Funnel Chart', () => {
+      testEchart('funnel', 'Funnel Chart', [
+        [154, 80],
+        [421, 39],
+      ]);
+    });
+
+    it('Gauge Chart', () => {
+      testEchart('gauge_chart', 'Gauge Chart', [
+        [151, 95],
+        [300, 143],
+      ]);
+    });
+
+    it('Radar Chart', () => {
+      testEchart('radar', 'Radar Chart', [
+        [182, 49],
+        [423, 91],
+      ]);
+    });
+
+    it('Treemap V2 Chart', () => {
+      testEchart('treemap_v2', 'Treemap V2 Chart', [
+        [145, 84],
+        [220, 105],
+      ]);
+    });
+
+    it('Mixed Chart', () => {
+      cy.get('[data-test-viz-type="mixed_timeseries"] canvas').then($canvas => {
+        // click 'boy'
+        cy.wrap($canvas)
+          .scrollIntoView()
+          .trigger('mouseover', 70, 93)
+          .rightclick(70, 93);
+
+        drillBy('name').then(intercepted => {
+          const { queries } = intercepted.request.body;
+          expect(queries[0].columns).to.eql(['name']);
+          expect(queries[0].filters).to.eql([
+            { col: 'gender', op: '==', val: 'boy' },
+          ]);
+          expect(queries[1].columns).to.eql(['state']);
+          expect(queries[1].filters).to.eql([]);
+        });
+
+        cy.getBySel('"Drill by: Mixed Chart-modal"').as('drillByModal');
+
+        cy.get('@drillByModal')
+          .find('.draggable-trigger')
+          .should('contain', 'Mixed Chart');
+
+        cy.get('@drillByModal')
+          .find('.ant-breadcrumb')
+          .should('be.visible')
+          .and('contain', 'gender (boy)')
+          .and('contain', '/')
+          .and('contain', 'name');
+
+        cy.get('@drillByModal')
+          .find('[data-test="drill-by-chart"]')
+          .should('be.visible');
+
+        // further drill
+        cy.get(`[data-test="drill-by-chart"] canvas`).then($canvas => {
+          // click second query
+          cy.wrap($canvas)
+            .scrollIntoView()
+            .trigger('mouseover', 246, 114)
+            .rightclick(246, 114);
+
+          drillBy('ds').then(intercepted => {
+            const { queries } = intercepted.request.body;
+            expect(queries[0].columns).to.eql(['name']);
+            expect(queries[0].filters).to.eql([
+              { col: 'gender', op: '==', val: 'boy' },
+            ]);
+            expect(queries[1].columns).to.eql(['ds']);
+            expect(queries[1].filters).to.eql([
+              { col: 'state', op: '==', val: 'other' },
+            ]);
+          });
+
+          cy.get('@drillByModal')
+            .find('[data-test="drill-by-chart"]')
+            .should('be.visible');
+
+          // undo - back to drill by state
+          interceptV1ChartData('drillByUndo');
+          cy.get('@drillByModal')
+            .find('.ant-breadcrumb')
+            .should('be.visible')
+            .and('contain', 'gender (boy)')
+            .and('contain', '/')
+            .and('contain', 'name (other)')
+            .and('contain', 'ds')
+            .contains('name (other)')
+            .click();
+
+          cy.wait('@drillByUndo').then(intercepted => {
+            const { queries } = intercepted.request.body;
+            expect(queries[0].columns).to.eql(['name']);
+            expect(queries[0].filters).to.eql([
+              { col: 'gender', op: '==', val: 'boy' },
+            ]);
+            expect(queries[1].columns).to.eql(['state']);
+            expect(queries[1].filters).to.eql([]);
+          });
+
+          cy.get('@drillByModal')
+            .find('.ant-breadcrumb')
+            .should('be.visible')
+            .and('contain', 'gender (boy)')
+            .and('contain', '/')
+            .and('not.contain', 'name (other)')
+            .and('not.contain', 'ds')
+            .and('contain', 'name');
+
+          cy.get('@drillByModal')
+            .find('[data-test="drill-by-chart"]')
+            .should('be.visible');
+        });
+      });
+    });
+  });
+});
diff --git a/superset-frontend/cypress-base/cypress/e2e/dashboard/drilltodetail.test.ts b/superset-frontend/cypress-base/cypress/e2e/dashboard/drilltodetail.test.ts
index 2ab4966d57..ff1872333b 100644
--- a/superset-frontend/cypress-base/cypress/e2e/dashboard/drilltodetail.test.ts
+++ b/superset-frontend/cypress-base/cypress/e2e/dashboard/drilltodetail.test.ts
@@ -18,7 +18,11 @@
  */
 import { waitForChartLoad } from 'cypress/utils';
 import { SUPPORTED_CHARTS_DASHBOARD } from 'cypress/utils/urls';
-import { SUPPORTED_TIER1_CHARTS, SUPPORTED_TIER2_CHARTS } from './utils';
+import {
+  openTopLevelTab,
+  SUPPORTED_TIER1_CHARTS,
+  SUPPORTED_TIER2_CHARTS,
+} from './utils';
 
 function interceptSamples() {
   cy.intercept(`/datasource/samples*`).as('samples');
@@ -77,10 +81,6 @@ function closeModal() {
   });
 }
 
-function setTopLevelTab(tabName: string) {
-  cy.get("div#TABS-TOP div[role='tab']").contains(tabName).click();
-}
-
 function testTimeChart(vizType: string) {
   interceptSamples();
 
@@ -139,7 +139,7 @@ describe('Drill to detail modal', () => {
   describe('Tier 1 charts', () => {
     before(() => {
       cy.visit(SUPPORTED_CHARTS_DASHBOARD);
-      setTopLevelTab('Tier 1');
+      openTopLevelTab('Tier 1');
       SUPPORTED_TIER1_CHARTS.forEach(waitForChartLoad);
     });
 
@@ -438,7 +438,7 @@ describe('Drill to detail modal', () => {
   describe('Tier 2 charts', () => {
     before(() => {
       cy.visit(SUPPORTED_CHARTS_DASHBOARD);
-      setTopLevelTab('Tier 2');
+      openTopLevelTab('Tier 2');
       SUPPORTED_TIER2_CHARTS.forEach(waitForChartLoad);
     });
 
diff --git a/superset-frontend/cypress-base/cypress/e2e/dashboard/utils.ts b/superset-frontend/cypress-base/cypress/e2e/dashboard/utils.ts
index 211ae88ff8..159e9368ec 100644
--- a/superset-frontend/cypress-base/cypress/e2e/dashboard/utils.ts
+++ b/superset-frontend/cypress-base/cypress/e2e/dashboard/utils.ts
@@ -37,10 +37,24 @@ export const SUPPORTED_TIER1_CHARTS = [
   { name: 'Big Number', viz: 'big_number_total' },
   { name: 'Big Number with Trendline', viz: 'big_number' },
   { name: 'Pie Chart', viz: 'pie' },
+  { name: 'Table', viz: 'table' },
+  { name: 'Pivot Table', viz: 'pivot_table_v2' },
+  { name: 'Time-Series Line Chart', viz: 'echarts_timeseries_line' },
+  { name: 'Time-Series Area Chart', viz: 'echarts_area' },
+  { name: 'Time-Series Scatter Chart', viz: 'echarts_timeseries_scatter' },
+  { name: 'Time-Series Bar Chart V2', viz: 'echarts_timeseries_bar' },
 ] as ChartSpec[];
 
 export const SUPPORTED_TIER2_CHARTS = [
   { name: 'Box Plot Chart', viz: 'box_plot' },
+  { name: 'Time-Series Generic Chart', viz: 'echarts_timeseries' },
+  { name: 'Time-Series Smooth Line Chart', viz: 'echarts_timeseries_smooth' },
+  { name: 'Time-Series Step Line Chart', viz: 'echarts_timeseries_step' },
+  { name: 'Funnel Chart', viz: 'funnel' },
+  { name: 'Gauge Chart', viz: 'gauge_chart' },
+  { name: 'Radar Chart', viz: 'radar' },
+  { name: 'Treemap V2 Chart', viz: 'treemap_v2' },
+  { name: 'Mixed Chart', viz: 'mixed_timeseries' },
 ] as ChartSpec[];
 
 export const testItems = {
@@ -515,3 +529,7 @@ export function openTab(tabComponentIndex: number, tabIndex: number) {
     .eq(tabIndex)
     .click();
 }
+
+export const openTopLevelTab = (tabName: string) => {
+  cy.get("div#TABS-TOP div[role='tab']").contains(tabName).click();
+};
diff --git a/superset-frontend/cypress-base/cypress/e2e/explore/utils.ts b/superset-frontend/cypress-base/cypress/e2e/explore/utils.ts
index 0a1dc50faa..95af89db5f 100644
--- a/superset-frontend/cypress-base/cypress/e2e/explore/utils.ts
+++ b/superset-frontend/cypress-base/cypress/e2e/explore/utils.ts
@@ -35,14 +35,18 @@ export function interceptUpdate() {
   cy.intercept('PUT', `/api/v1/chart/*`).as('update');
 }
 
-export function interceptPost() {
-  cy.intercept('POST', `/api/v1/chart/`).as('post');
-}
+export const interceptV1ChartData = (alias = 'v1Data') => {
+  cy.intercept('/api/v1/chart/data*').as(alias);
+};
 
-export function interceptExploreJson() {
-  cy.intercept('POST', `/superset/explore_json/**`).as('getJson');
+export function interceptExploreJson(alias = 'getJson') {
+  cy.intercept('POST', `/superset/explore_json/**`).as(alias);
 }
 
+export const interceptFormDataKey = () => {
+  cy.intercept('POST', '/api/v1/explore/form_data').as('formDataKey');
+};
+
 export function interceptExploreGet() {
   cy.intercept({
     method: 'GET',
diff --git a/superset-frontend/src/components/Chart/DrillBy/DrillByChart.tsx b/superset-frontend/src/components/Chart/DrillBy/DrillByChart.tsx
index 91faa05c9c..f58a646db9 100644
--- a/superset-frontend/src/components/Chart/DrillBy/DrillByChart.tsx
+++ b/superset-frontend/src/components/Chart/DrillBy/DrillByChart.tsx
@@ -54,6 +54,7 @@ export default function DrillByChart({
         height: 100%;
         min-height: 0;
       `}
+      data-test="drill-by-chart"
     >
       <SuperChart
         disableErrorBoundary
diff --git a/superset-frontend/src/components/Chart/DrillBy/useDisplayModeToggle.tsx b/superset-frontend/src/components/Chart/DrillBy/useDisplayModeToggle.tsx
index 5e7b812cc4..9ca1351544 100644
--- a/superset-frontend/src/components/Chart/DrillBy/useDisplayModeToggle.tsx
+++ b/superset-frontend/src/components/Chart/DrillBy/useDisplayModeToggle.tsx
@@ -36,6 +36,7 @@ export const useDisplayModeToggle = () => {
             box-shadow: none;
           }
         `}
+        data-test="drill-by-display-toggle"
       >
         <Radio.Group
           onChange={({ target: { value } }) => {
diff --git a/superset-frontend/src/components/Chart/DrillBy/useResultsTableView.tsx b/superset-frontend/src/components/Chart/DrillBy/useResultsTableView.tsx
index 2778fab72c..b424b95ea5 100644
--- a/superset-frontend/src/components/Chart/DrillBy/useResultsTableView.tsx
+++ b/superset-frontend/src/components/Chart/DrillBy/useResultsTableView.tsx
@@ -40,7 +40,7 @@ export const useResultsTableView = (
   }
   if (chartDataResult.length === 1) {
     return (
-      <PaginationContainer>
+      <PaginationContainer data-test="drill-by-results-table">
         <SingleQueryResultPane
           colnames={chartDataResult[0].colnames}
           coltypes={chartDataResult[0].coltypes}
@@ -53,7 +53,7 @@ export const useResultsTableView = (
     );
   }
   return (
-    <Tabs fullWidth={false}>
+    <Tabs fullWidth={false} data-test="drill-by-results-tabs">
       {chartDataResult.map((res, index) => (
         <Tabs.TabPane tab={t('Results %s', index + 1)} key={index}>
           <PaginationContainer>
diff --git a/superset/examples/supported_charts_dashboard.py b/superset/examples/supported_charts_dashboard.py
index 54aa650d50..e6d7557deb 100644
--- a/superset/examples/supported_charts_dashboard.py
+++ b/superset/examples/supported_charts_dashboard.py
@@ -335,7 +335,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
                 viz_type="mixed_timeseries",
                 metrics=["sum__num"],
                 groupby=["gender"],
-                metrics_b=["count"],
+                metrics_b=["sum__num"],
                 groupby_b=["state"],
             ),
         ),
diff --git a/tests/integration_tests/superset_test_config.py b/tests/integration_tests/superset_test_config.py
index 19c2cc000f..c3f9b350f8 100644
--- a/tests/integration_tests/superset_test_config.py
+++ b/tests/integration_tests/superset_test_config.py
@@ -61,7 +61,7 @@ PRESTO_POLL_INTERVAL = 0.1
 HIVE_POLL_INTERVAL = 0.1
 
 SQL_MAX_ROW = 10000
-SQLLAB_CTAS_NO_LIMIT = True  # SQL_MAX_ROW will not take affect for the CTA queries
+SQLLAB_CTAS_NO_LIMIT = True  # SQL_MAX_ROW will not take effect for the CTA queries
 FEATURE_FLAGS = {
     **FEATURE_FLAGS,
     "foo": "bar",
@@ -71,6 +71,7 @@ FEATURE_FLAGS = {
     "ALERT_REPORTS": True,
     "DASHBOARD_NATIVE_FILTERS": True,
     "DRILL_TO_DETAIL": True,
+    "DRILL_BY": True,
     "HORIZONTAL_FILTER_BAR": True,
 }