You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by vi...@apache.org on 2022/04/04 08:22:26 UTC

[superset] 17/24: test(native filter): add new test for dependent filter (#19392)

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

villebro pushed a commit to tag 1.5.0rc1
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 3eece91378cf45755d472d4a95c1b47a6fb47f8a
Author: Jinghuayao <81...@users.noreply.github.com>
AuthorDate: Fri Apr 1 01:06:48 2022 -0700

    test(native filter): add new test for dependent filter (#19392)
    
    * add new test for dependent filter
    
    (cherry picked from commit 2a75e4c3c37ec6dddab5ecdec8908a0f18ed391d)
---
 docker/docker-init.sh                              |   2 +-
 .../integration/dashboard/dashboard.helper.ts      | 120 +++++++++++++++++++++
 .../integration/dashboard/nativeFilters.test.ts    |  97 ++++++++++++-----
 .../cypress-base/cypress/support/index.d.ts        |  14 +++
 .../cypress-base/cypress/support/index.ts          |  86 +++++++++++++++
 superset-frontend/package-lock.json                |   2 +-
 6 files changed, 292 insertions(+), 29 deletions(-)

diff --git a/docker/docker-init.sh b/docker/docker-init.sh
index d5ead50398..c928c1ba50 100755
--- a/docker/docker-init.sh
+++ b/docker/docker-init.sh
@@ -41,7 +41,7 @@ ADMIN_PASSWORD="admin"
 # If Cypress run – overwrite the password for admin and export env variables
 if [ "$CYPRESS_CONFIG" == "true" ]; then
     ADMIN_PASSWORD="general"
-    export SUPERSET_CONFIG=tests.superset_test_config
+    export SUPERSET_CONFIG=tests.integration_tests.superset_test_config
     export SUPERSET_TESTENV=true
     export ENABLE_REACT_CRUD_VIEWS=true
     export SUPERSET__SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://superset:superset@db:5432/superset
diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/dashboard.helper.ts b/superset-frontend/cypress-base/cypress/integration/dashboard/dashboard.helper.ts
index 1458fc7d59..9822f70359 100644
--- a/superset-frontend/cypress-base/cypress/integration/dashboard/dashboard.helper.ts
+++ b/superset-frontend/cypress-base/cypress/integration/dashboard/dashboard.helper.ts
@@ -1,4 +1,9 @@
 import { getChartAlias, Slice } from 'cypress/utils/vizPlugins';
+import {
+  dashboardView,
+  editDashboardView,
+  nativeFilters,
+} from 'cypress/support/directories';
 
 /**
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -25,7 +30,13 @@ export const testItems = {
   dashboard: 'Cypress Sales Dashboard',
   dataset: 'Vehicle Sales',
   chart: 'Cypress chart',
+  newChart: 'New Cypress Chart',
+  createdDashboard: 'New Dashboard',
   defaultNameDashboard: '[ untitled dashboard ]',
+  newDashboardTitle: `Test dashboard [NEW TEST]`,
+  bulkFirstNameDashboard: 'First Dash',
+  bulkSecondNameDashboard: 'Second Dash',
+  worldBanksDataCopy: `World Bank's Data [copy]`,
 };
 
 export const CHECK_DASHBOARD_FAVORITE_ENDPOINT =
@@ -133,3 +144,112 @@ export function resize(selector: string) {
     },
   };
 }
+
+export function cleanUp() {
+  cy.deleteDashboardByName(testItems.dashboard);
+  cy.deleteDashboardByName(testItems.defaultNameDashboard);
+  cy.deleteDashboardByName('');
+  cy.deleteDashboardByName(testItems.newDashboardTitle);
+  cy.deleteDashboardByName(testItems.bulkFirstNameDashboard);
+  cy.deleteDashboardByName(testItems.bulkSecondNameDashboard);
+  cy.deleteDashboardByName(testItems.createdDashboard);
+  cy.deleteDashboardByName(testItems.worldBanksDataCopy);
+  cy.deleteChartByName(testItems.chart);
+  cy.deleteChartByName(testItems.newChart);
+}
+
+/** ************************************************************************
+ * Clicks on new filter button
+ * @returns {None}
+ * @summary helper for adding new filter
+ ************************************************************************* */
+export function clickOnAddFilterInModal() {
+  return cy
+    .get(nativeFilters.addFilterButton.button)
+    .first()
+    .click()
+    .then(() => {
+      cy.get(nativeFilters.addFilterButton.dropdownItem)
+        .contains('Filter')
+        .click({ force: true });
+    });
+}
+
+/** ************************************************************************
+ * Fills value native filter form with basic information
+ * @param {string} name name for filter
+ * @param {string} dataset which dataset should be used
+ * @param {string} filterColumn which column should be used
+ * @returns {None}
+ * @summary helper for filling value native filter form
+ ************************************************************************* */
+export function fillValueNativeFilterForm(
+  name: string,
+  dataset: string,
+  filterColumn: string,
+) {
+  cy.get(nativeFilters.modal.container)
+    .find(nativeFilters.filtersPanel.filterName)
+    .last()
+    .click({ scrollBehavior: false })
+    .type(name, { scrollBehavior: false });
+  cy.get(nativeFilters.modal.container)
+    .find(nativeFilters.filtersPanel.datasetName)
+    .last()
+    .click({ scrollBehavior: false })
+    .type(`${dataset}{enter}`, { scrollBehavior: false });
+  cy.get(nativeFilters.silentLoading).should('not.exist');
+  cy.get(nativeFilters.filtersPanel.filterInfoInput)
+    .last()
+    .should('be.visible')
+    .click({ force: true });
+  cy.get(nativeFilters.filtersPanel.filterInfoInput).last().type(filterColumn);
+  cy.get(nativeFilters.filtersPanel.inputDropdown)
+    .should('be.visible', { timeout: 20000 })
+    .last()
+    .click();
+}
+/** ************************************************************************
+ * Get native filter placeholder e.g 9 options
+ * @param {number} index which input it fills
+ * @returns cy object for assertions
+ * @summary helper for getting placeholder value
+ ************************************************************************* */
+export function getNativeFilterPlaceholderWithIndex(index: number) {
+  return cy.get(nativeFilters.filtersPanel.columnEmptyInput).eq(index);
+}
+
+/** ************************************************************************
+ * Apply native filter value from dashboard view
+ * @param {number} index which input it fills
+ * @param {string} value what is filter value
+ * @returns {null}
+ * @summary put value to nth native filter input in view
+ ************************************************************************* */
+export function applyNativeFilterValueWithIndex(index: number, value: string) {
+  cy.get(nativeFilters.filterFromDashboardView.filterValueInput)
+    .eq(index)
+    .parent()
+    .should('be.visible', { timeout: 10000 })
+    .type(`${value}{enter}`);
+  // click the title to dismiss shown options
+  cy.get(nativeFilters.filterFromDashboardView.filterName).eq(index).click();
+}
+
+/** ************************************************************************
+ * Fills parent filter input
+ * @param {number} index which input it fills
+ * @param {string} value on which filter it depends on
+ * @returns {null}
+ * @summary takes first or second input and modify the depends on filter value
+ ************************************************************************* */
+export function addParentFilterWithValue(index: number, value: string) {
+  return cy
+    .get(nativeFilters.filterConfigurationSections.displayedSection)
+    .within(() => {
+      cy.get('input[aria-label="Limit type"]')
+        .eq(index)
+        .click({ force: true })
+        .type(`${value}{enter}`, { delay: 30, force: true });
+    });
+}
diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts b/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts
index 92a86f6369..6fab297a1d 100644
--- a/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts
+++ b/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts
@@ -22,7 +22,17 @@ import {
   nativeFilters,
   exploreView,
 } from 'cypress/support/directories';
-import { testItems } from './dashboard.helper';
+import {
+  cleanUp,
+  testItems,
+  WORLD_HEALTH_CHARTS,
+  waitForChartLoad,
+  clickOnAddFilterInModal,
+  fillValueNativeFilterForm,
+  getNativeFilterPlaceholderWithIndex,
+  addParentFilterWithValue,
+  applyNativeFilterValueWithIndex,
+} from './dashboard.helper';
 import { DASHBOARD_LIST } from '../dashboard_list/dashboard_list.helper';
 import { CHART_LIST } from '../chart_list/chart_list.helper';
 import { FORM_DATA_DEFAULTS } from '../explore/visualizations/shared.helper';
@@ -39,21 +49,27 @@ const milliseconds = new Date().getTime();
 const dashboard = `Test Dashboard${milliseconds}`;
 
 describe('Nativefilters Sanity test', () => {
-  before(() => {
+  beforeEach(() => {
     cy.login();
+    cleanUp();
     cy.intercept('/api/v1/dashboard/?q=**').as('dashboardsList');
     cy.intercept('POST', '**/copy_dash/*').as('copy');
     cy.intercept('/api/v1/dashboard/*').as('dashboard');
-    cy.request(
-      'api/v1/dashboard/?q=(order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:100)',
-    ).then(xhr => {
-      const dashboards = xhr.body.result;
+    cy.intercept('GET', '**/api/v1/dataset/**').as('datasetLoad');
+    cy.intercept('**/api/v1/dashboard/?q=**').as('dashboardsList');
+    cy.visit('dashboard/list/');
+    cy.contains('Actions');
+    cy.wait('@dashboardsList').then(xhr => {
+      const dashboards = xhr.response?.body.result;
+      /* eslint-disable no-unused-expressions */
+      expect(dashboards).not.to.be.undefined;
       const worldBankDashboard = dashboards.find(
         (d: { dashboard_title: string }) =>
           d.dashboard_title === "World Bank's Data",
       );
       cy.visit(worldBankDashboard.url);
     });
+    WORLD_HEALTH_CHARTS.forEach(waitForChartLoad);
     cy.get(dashboardView.threeDotsMenuIcon).should('be.visible').click();
     cy.get(dashboardView.saveAsMenuOption).should('be.visible').click();
     cy.get(dashboardView.saveModal.dashboardNameInput)
@@ -65,19 +81,10 @@ describe('Nativefilters Sanity test', () => {
       .its('response.statusCode')
       .should('eq', 200);
   });
-  beforeEach(() => {
-    cy.login();
-    cy.request(
-      'api/v1/dashboard/?q=(order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:100)',
-    ).then(xhr => {
-      const dashboards = xhr.body.result;
-      const testDashboard = dashboards.find(
-        (d: { dashboard_title: string }) =>
-          d.dashboard_title === testItems.dashboard,
-      );
-      cy.visit(testDashboard.url);
-    });
+  afterEach(() => {
+    cleanUp();
   });
+
   it('User can expand / retract native filter sidebar on a dashboard', () => {
     cy.get(nativeFilters.createFilterButton).should('not.exist');
     cy.get(nativeFilters.filterFromDashboardView.expand)
@@ -390,15 +397,6 @@ describe('Nativefilters Sanity test', () => {
     cy.get('.line').within(() => {
       cy.contains('United States').should('be.visible');
     });
-
-    // clean up the default setting
-    cy.get(nativeFilters.filterFromDashboardView.expand).click({ force: true });
-    cy.get(nativeFilters.filterFromDashboardView.createFilterButton).click();
-    cy.contains('Filter has default value').click();
-    cy.get(nativeFilters.modal.footer)
-      .find(nativeFilters.modal.saveButton)
-      .should('be.visible')
-      .click({ force: true });
   });
 
   it('User can create a time grain filter', () => {
@@ -566,6 +564,51 @@ describe('Nativefilters Sanity test', () => {
       .should('be.visible', { timeout: 40000 })
       .contains('country_name');
   });
+
+  it('User can create parent filters using "Values are dependent on other filters"', () => {
+    cy.get(nativeFilters.filterFromDashboardView.expand)
+      .should('be.visible')
+      .click({ force: true });
+    cy.get(nativeFilters.filterFromDashboardView.createFilterButton).click();
+    // Create parent filter 'region'.
+    fillValueNativeFilterForm('region', 'wb_health_population', 'region');
+    // Create filter 'country_name' depend on region filter.
+    clickOnAddFilterInModal();
+    fillValueNativeFilterForm(
+      'country_name',
+      'wb_health_population',
+      'country_name',
+    );
+    cy.get(nativeFilters.filterConfigurationSections.displayedSection).within(
+      () => {
+        cy.contains('Values are dependent on other filters')
+          .should('be.visible')
+          .click();
+      },
+    );
+    addParentFilterWithValue(0, 'region');
+    cy.wait(1000);
+    cy.get(nativeFilters.modal.footer)
+      .contains('Save')
+      .should('be.visible')
+      .click();
+    // Validate both filter in dashboard view.
+    WORLD_HEALTH_CHARTS.forEach(waitForChartLoad);
+    ['region', 'country_name'].forEach(it => {
+      cy.get(nativeFilters.filterFromDashboardView.filterName)
+        .contains(it)
+        .should('be.visible');
+    });
+    getNativeFilterPlaceholderWithIndex(1)
+      .invoke('text')
+      .should('equal', '214 options', { timeout: 20000 });
+    // apply first filter value and validate 2nd filter is depden on 1st filter.
+    applyNativeFilterValueWithIndex(0, 'East Asia & Pacific');
+
+    getNativeFilterPlaceholderWithIndex(0).should('have.text', '36 options', {
+      timeout: 20000,
+    });
+  });
 });
 
 xdescribe('Nativefilters', () => {
diff --git a/superset-frontend/cypress-base/cypress/support/index.d.ts b/superset-frontend/cypress-base/cypress/support/index.d.ts
index fdacf3232b..eca68a7ced 100644
--- a/superset-frontend/cypress-base/cypress/support/index.d.ts
+++ b/superset-frontend/cypress-base/cypress/support/index.d.ts
@@ -47,6 +47,20 @@ declare namespace Cypress {
       querySubstring?: string | RegExp;
       chartSelector?: JQuery.Selector;
     }): cy;
+
+    /**
+     * Get
+     */
+    getDashboards(): cy;
+    getCharts(): cy;
+
+    /**
+     * Delete
+     */
+    deleteDashboard(id: number): cy;
+    deleteDashboardByName(name: string): cy;
+    deleteChartByName(name: string): cy;
+    deleteChart(id: number): cy;
   }
 }
 
diff --git a/superset-frontend/cypress-base/cypress/support/index.ts b/superset-frontend/cypress-base/cypress/support/index.ts
index e22f69975e..905d00df9f 100644
--- a/superset-frontend/cypress-base/cypress/support/index.ts
+++ b/superset-frontend/cypress-base/cypress/support/index.ts
@@ -19,6 +19,7 @@
 import '@cypress/code-coverage/support';
 
 const BASE_EXPLORE_URL = '/superset/explore/?form_data=';
+const TokenName = Cypress.env('TOKEN_NAME');
 
 /* eslint-disable consistent-return */
 Cypress.on('uncaught:exception', err => {
@@ -102,3 +103,88 @@ Cypress.Commands.add(
     return cy;
   },
 );
+
+Cypress.Commands.add('deleteDashboardByName', (name: string) =>
+  cy.getDashboards().then((dashboards: any) => {
+    dashboards?.forEach((element: any) => {
+      if (element.dashboard_title === name) {
+        const elementId = element.id;
+        cy.deleteDashboard(elementId);
+      }
+    });
+  }),
+);
+
+Cypress.Commands.add('deleteDashboard', (id: number) =>
+  cy
+    .request({
+      method: 'DELETE',
+      url: `api/v1/dashboard/${id}`,
+      headers: {
+        Cookie: `csrf_access_token=${window.localStorage.getItem(
+          'access_token',
+        )}`,
+        'Content-Type': 'application/json',
+        Authorization: `Bearer ${TokenName}`,
+        'X-CSRFToken': `${window.localStorage.getItem('access_token')}`,
+        Referer: `${Cypress.config().baseUrl}/`,
+      },
+    })
+    .then(resp => resp),
+);
+
+Cypress.Commands.add('getDashboards', () =>
+  cy
+    .request({
+      method: 'GET',
+      url: `api/v1/dashboard/`,
+      headers: {
+        'Content-Type': 'application/json',
+        Authorization: `Bearer ${TokenName}`,
+      },
+    })
+    .then(resp => resp.body.result),
+);
+
+Cypress.Commands.add('deleteChart', (id: number) =>
+  cy
+    .request({
+      method: 'DELETE',
+      url: `api/v1/chart/${id}`,
+      headers: {
+        Cookie: `csrf_access_token=${window.localStorage.getItem(
+          'access_token',
+        )}`,
+        'Content-Type': 'application/json',
+        Authorization: `Bearer ${TokenName}`,
+        'X-CSRFToken': `${window.localStorage.getItem('access_token')}`,
+        Referer: `${Cypress.config().baseUrl}/`,
+      },
+      failOnStatusCode: false,
+    })
+    .then(resp => resp),
+);
+
+Cypress.Commands.add('getCharts', () =>
+  cy
+    .request({
+      method: 'GET',
+      url: `api/v1/chart/`,
+      headers: {
+        'Content-Type': 'application/json',
+        Authorization: `Bearer ${TokenName}`,
+      },
+    })
+    .then(resp => resp.body.result),
+);
+
+Cypress.Commands.add('deleteChartByName', (name: string) =>
+  cy.getCharts().then((slices: any) => {
+    slices?.forEach((element: any) => {
+      if (element.slice_name === name) {
+        const elementId = element.id;
+        cy.deleteChart(elementId);
+      }
+    });
+  }),
+);
diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json
index 92ee874f67..d9d49996a2 100644
--- a/superset-frontend/package-lock.json
+++ b/superset-frontend/package-lock.json
@@ -276,7 +276,7 @@
       },
       "engines": {
         "node": "^16.9.1",
-        "npm": "^7.5.4"
+        "npm": "^7.5.4 || ^8.1.2"
       }
     },
     "buildtools/eslint-plugin-theme-colors": {