mirror of https://github.com/apache/superset.git
test(native filter): add new test for dependent filter (#19392)
* add new test for dependent filter
This commit is contained in:
parent
eab9388f7c
commit
2a75e4c3c3
|
@ -41,7 +41,7 @@ ADMIN_PASSWORD="admin"
|
||||||
# If Cypress run – overwrite the password for admin and export env variables
|
# If Cypress run – overwrite the password for admin and export env variables
|
||||||
if [ "$CYPRESS_CONFIG" == "true" ]; then
|
if [ "$CYPRESS_CONFIG" == "true" ]; then
|
||||||
ADMIN_PASSWORD="general"
|
ADMIN_PASSWORD="general"
|
||||||
export SUPERSET_CONFIG=tests.superset_test_config
|
export SUPERSET_CONFIG=tests.integration_tests.superset_test_config
|
||||||
export SUPERSET_TESTENV=true
|
export SUPERSET_TESTENV=true
|
||||||
export SUPERSET__SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://superset:superset@db:5432/superset
|
export SUPERSET__SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://superset:superset@db:5432/superset
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
import { getChartAlias, Slice } from 'cypress/utils/vizPlugins';
|
import { getChartAlias, Slice } from 'cypress/utils/vizPlugins';
|
||||||
|
import {
|
||||||
|
dashboardView,
|
||||||
|
editDashboardView,
|
||||||
|
nativeFilters,
|
||||||
|
} from 'cypress/support/directories';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
@ -25,7 +30,13 @@ export const testItems = {
|
||||||
dashboard: 'Cypress Sales Dashboard',
|
dashboard: 'Cypress Sales Dashboard',
|
||||||
dataset: 'Vehicle Sales',
|
dataset: 'Vehicle Sales',
|
||||||
chart: 'Cypress chart',
|
chart: 'Cypress chart',
|
||||||
|
newChart: 'New Cypress Chart',
|
||||||
|
createdDashboard: 'New Dashboard',
|
||||||
defaultNameDashboard: '[ untitled 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 =
|
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 });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -22,7 +22,17 @@ import {
|
||||||
nativeFilters,
|
nativeFilters,
|
||||||
exploreView,
|
exploreView,
|
||||||
} from 'cypress/support/directories';
|
} 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 { DASHBOARD_LIST } from '../dashboard_list/dashboard_list.helper';
|
||||||
import { CHART_LIST } from '../chart_list/chart_list.helper';
|
import { CHART_LIST } from '../chart_list/chart_list.helper';
|
||||||
import { FORM_DATA_DEFAULTS } from '../explore/visualizations/shared.helper';
|
import { FORM_DATA_DEFAULTS } from '../explore/visualizations/shared.helper';
|
||||||
|
@ -39,21 +49,27 @@ const milliseconds = new Date().getTime();
|
||||||
const dashboard = `Test Dashboard${milliseconds}`;
|
const dashboard = `Test Dashboard${milliseconds}`;
|
||||||
|
|
||||||
describe('Nativefilters Sanity test', () => {
|
describe('Nativefilters Sanity test', () => {
|
||||||
before(() => {
|
beforeEach(() => {
|
||||||
cy.login();
|
cy.login();
|
||||||
|
cleanUp();
|
||||||
cy.intercept('/api/v1/dashboard/?q=**').as('dashboardsList');
|
cy.intercept('/api/v1/dashboard/?q=**').as('dashboardsList');
|
||||||
cy.intercept('POST', '**/copy_dash/*').as('copy');
|
cy.intercept('POST', '**/copy_dash/*').as('copy');
|
||||||
cy.intercept('/api/v1/dashboard/*').as('dashboard');
|
cy.intercept('/api/v1/dashboard/*').as('dashboard');
|
||||||
cy.request(
|
cy.intercept('GET', '**/api/v1/dataset/**').as('datasetLoad');
|
||||||
'api/v1/dashboard/?q=(order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:100)',
|
cy.intercept('**/api/v1/dashboard/?q=**').as('dashboardsList');
|
||||||
).then(xhr => {
|
cy.visit('dashboard/list/');
|
||||||
const dashboards = xhr.body.result;
|
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(
|
const worldBankDashboard = dashboards.find(
|
||||||
(d: { dashboard_title: string }) =>
|
(d: { dashboard_title: string }) =>
|
||||||
d.dashboard_title === "World Bank's Data",
|
d.dashboard_title === "World Bank's Data",
|
||||||
);
|
);
|
||||||
cy.visit(worldBankDashboard.url);
|
cy.visit(worldBankDashboard.url);
|
||||||
});
|
});
|
||||||
|
WORLD_HEALTH_CHARTS.forEach(waitForChartLoad);
|
||||||
cy.get(dashboardView.threeDotsMenuIcon).should('be.visible').click();
|
cy.get(dashboardView.threeDotsMenuIcon).should('be.visible').click();
|
||||||
cy.get(dashboardView.saveAsMenuOption).should('be.visible').click();
|
cy.get(dashboardView.saveAsMenuOption).should('be.visible').click();
|
||||||
cy.get(dashboardView.saveModal.dashboardNameInput)
|
cy.get(dashboardView.saveModal.dashboardNameInput)
|
||||||
|
@ -65,19 +81,10 @@ describe('Nativefilters Sanity test', () => {
|
||||||
.its('response.statusCode')
|
.its('response.statusCode')
|
||||||
.should('eq', 200);
|
.should('eq', 200);
|
||||||
});
|
});
|
||||||
beforeEach(() => {
|
afterEach(() => {
|
||||||
cy.login();
|
cleanUp();
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('User can expand / retract native filter sidebar on a dashboard', () => {
|
it('User can expand / retract native filter sidebar on a dashboard', () => {
|
||||||
cy.get(nativeFilters.createFilterButton).should('not.exist');
|
cy.get(nativeFilters.createFilterButton).should('not.exist');
|
||||||
cy.get(nativeFilters.filterFromDashboardView.expand)
|
cy.get(nativeFilters.filterFromDashboardView.expand)
|
||||||
|
@ -390,15 +397,6 @@ describe('Nativefilters Sanity test', () => {
|
||||||
cy.get('.line').within(() => {
|
cy.get('.line').within(() => {
|
||||||
cy.contains('United States').should('be.visible');
|
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', () => {
|
it('User can create a time grain filter', () => {
|
||||||
|
@ -565,6 +563,51 @@ describe('Nativefilters Sanity test', () => {
|
||||||
.should('be.visible', { timeout: 40000 })
|
.should('be.visible', { timeout: 40000 })
|
||||||
.contains('country_name');
|
.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', () => {
|
xdescribe('Nativefilters', () => {
|
||||||
|
|
|
@ -47,6 +47,20 @@ declare namespace Cypress {
|
||||||
querySubstring?: string | RegExp;
|
querySubstring?: string | RegExp;
|
||||||
chartSelector?: JQuery.Selector;
|
chartSelector?: JQuery.Selector;
|
||||||
}): cy;
|
}): cy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get
|
||||||
|
*/
|
||||||
|
getDashboards(): cy;
|
||||||
|
getCharts(): cy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete
|
||||||
|
*/
|
||||||
|
deleteDashboard(id: number): cy;
|
||||||
|
deleteDashboardByName(name: string): cy;
|
||||||
|
deleteChartByName(name: string): cy;
|
||||||
|
deleteChart(id: number): cy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
import '@cypress/code-coverage/support';
|
import '@cypress/code-coverage/support';
|
||||||
|
|
||||||
const BASE_EXPLORE_URL = '/superset/explore/?form_data=';
|
const BASE_EXPLORE_URL = '/superset/explore/?form_data=';
|
||||||
|
const TokenName = Cypress.env('TOKEN_NAME');
|
||||||
|
|
||||||
/* eslint-disable consistent-return */
|
/* eslint-disable consistent-return */
|
||||||
Cypress.on('uncaught:exception', err => {
|
Cypress.on('uncaught:exception', err => {
|
||||||
|
@ -102,3 +103,88 @@ Cypress.Commands.add(
|
||||||
return cy;
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
|
@ -275,7 +275,7 @@
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.9.1",
|
"node": "^16.9.1",
|
||||||
"npm": "^7.5.4"
|
"npm": "^7.5.4 || ^8.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"buildtools/eslint-plugin-theme-colors": {
|
"buildtools/eslint-plugin-theme-colors": {
|
||||||
|
|
Loading…
Reference in New Issue