chore(cypress): Make the e2e tests more behavior-driven (#13784)
* chore(cypress): make the load dashboard test behavior driven * remove bootstrap usage from controls test, + new utils * fix save test to not use bootstrap dat * remove bootstrap usage from the filter test * fix race condition * remove bootstrap from url params test * fix lint * remove unused const * add chart specs for the tab test * attempt removing bootstrap data from tabs tests * fix cypress async/sync confusion * remove redundant assertions * attempt fixing tab test * attempt fixing tabs test * cleanup commented code * better aliases so you can tell what they are in the logs * remove unused imports * get the line chart alias before clicking the tab * simplify getChartGridComponent * wait for all the charts before force refresh * fix tabs test * one more time with the tabs test * another edit to tabs test * fix flaky test Co-authored-by: Phillip Kelley-Dotson <pkelleydotson@yahoo.com>
This commit is contained in:
parent
5ae91e2dd8
commit
a45a5e1060
|
|
@ -16,79 +16,80 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper';
|
||||
import {
|
||||
getChartAliases,
|
||||
isLegacyResponse,
|
||||
DASHBOARD_CHART_ALIAS_PREFIX,
|
||||
} from '../../utils/vizPlugins';
|
||||
WORLD_HEALTH_CHARTS,
|
||||
WORLD_HEALTH_DASHBOARD,
|
||||
waitForChartLoad,
|
||||
ChartSpec,
|
||||
getChartAliasesBySpec,
|
||||
} from './dashboard.helper';
|
||||
import { isLegacyResponse } from '../../utils/vizPlugins';
|
||||
|
||||
describe('Dashboard top-level controls', () => {
|
||||
let mapId: string;
|
||||
let aliases: string[];
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit(WORLD_HEALTH_DASHBOARD);
|
||||
|
||||
cy.get('#app').then(data => {
|
||||
const bootstrapData = JSON.parse(data[0].dataset.bootstrap || '');
|
||||
const dashboard = bootstrapData.dashboard_data;
|
||||
mapId = dashboard.slices.find(
|
||||
(slice: { form_data: { viz_type: string }; slice_id: number }) =>
|
||||
slice.form_data.viz_type === 'world_map',
|
||||
).slice_id;
|
||||
aliases = getChartAliases(dashboard.slices);
|
||||
});
|
||||
});
|
||||
|
||||
// flaky test
|
||||
xit('should allow chart level refresh', () => {
|
||||
cy.wait(aliases);
|
||||
cy.get('[data-test="grid-container"]').find('.world_map').should('exist');
|
||||
cy.get(`#slice_${mapId}-controls`).click();
|
||||
cy.get(`[data-test="slice_${mapId}-menu"]`)
|
||||
.find('[data-test="refresh-chart-menu-item"]')
|
||||
.click({ force: true });
|
||||
cy.get('[data-test="refresh-chart-menu-item"]').should(
|
||||
'have.class',
|
||||
'ant-dropdown-menu-item-disabled',
|
||||
);
|
||||
|
||||
cy.wait(`@${DASHBOARD_CHART_ALIAS_PREFIX}${mapId}`);
|
||||
cy.get('[data-test="refresh-chart-menu-item"]').should(
|
||||
'not.have.class',
|
||||
'ant-dropdown-menu-item-disabled',
|
||||
);
|
||||
const mapSpec = WORLD_HEALTH_CHARTS.find(
|
||||
({ viz }) => viz === 'world_map',
|
||||
) as ChartSpec;
|
||||
waitForChartLoad(mapSpec).then(gridComponent => {
|
||||
const mapId = gridComponent.attr('data-test-chart-id');
|
||||
cy.get('[data-test="grid-container"]').find('.world_map').should('exist');
|
||||
cy.get(`#slice_${mapId}-controls`).click();
|
||||
cy.get(`[data-test="slice_${mapId}-menu"]`)
|
||||
.find('[data-test="refresh-chart-menu-item"]')
|
||||
.click({ force: true });
|
||||
// likely cause for flakiness:
|
||||
// The query completes before this assertion happens.
|
||||
// Solution: pause the network before clicking, assert, then unpause network.
|
||||
cy.get('[data-test="refresh-chart-menu-item"]').should(
|
||||
'have.class',
|
||||
'ant-dropdown-menu-item-disabled',
|
||||
);
|
||||
waitForChartLoad(mapSpec);
|
||||
cy.get('[data-test="refresh-chart-menu-item"]').should(
|
||||
'not.have.class',
|
||||
'ant-dropdown-menu-item-disabled',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow dashboard level force refresh', () => {
|
||||
cy.wait(aliases);
|
||||
// when charts are not start loading, for example, under a secondary tab,
|
||||
// should allow force refresh
|
||||
cy.get('[data-test="more-horiz"]').click();
|
||||
cy.get('[data-test="refresh-dashboard-menu-item"]').should(
|
||||
'not.have.class',
|
||||
'ant-dropdown-menu-item-disabled',
|
||||
);
|
||||
WORLD_HEALTH_CHARTS.forEach(waitForChartLoad);
|
||||
getChartAliasesBySpec(WORLD_HEALTH_CHARTS).then(aliases => {
|
||||
cy.get('[data-test="more-horiz"]').click();
|
||||
cy.get('[data-test="refresh-dashboard-menu-item"]').should(
|
||||
'not.have.class',
|
||||
'ant-dropdown-menu-item-disabled',
|
||||
);
|
||||
|
||||
cy.get('[data-test="refresh-dashboard-menu-item"]').click({ force: true });
|
||||
cy.get('[data-test="refresh-dashboard-menu-item"]').should(
|
||||
'have.class',
|
||||
'ant-dropdown-menu-item-disabled',
|
||||
);
|
||||
cy.get('[data-test="refresh-dashboard-menu-item"]').click({
|
||||
force: true,
|
||||
});
|
||||
cy.get('[data-test="refresh-dashboard-menu-item"]').should(
|
||||
'have.class',
|
||||
'ant-dropdown-menu-item-disabled',
|
||||
);
|
||||
|
||||
// wait all charts force refreshed.
|
||||
cy.wait(aliases).then(xhrs => {
|
||||
xhrs.forEach(async ({ response, request }) => {
|
||||
const responseBody = response?.body;
|
||||
const isCached = isLegacyResponse(responseBody)
|
||||
? responseBody.is_cached
|
||||
: responseBody.result[0].is_cached;
|
||||
// request url should indicate force-refresh operation
|
||||
expect(request.url).to.have.string('force=true');
|
||||
// is_cached in response should be false
|
||||
expect(isCached).to.equal(false);
|
||||
// wait all charts force refreshed.
|
||||
|
||||
cy.wait(aliases).then(xhrs => {
|
||||
xhrs.forEach(async ({ response, request }) => {
|
||||
const responseBody = response?.body;
|
||||
const isCached = isLegacyResponse(responseBody)
|
||||
? responseBody.is_cached
|
||||
: responseBody.result[0].is_cached;
|
||||
// request url should indicate force-refresh operation
|
||||
expect(request.url).to.have.string('force=true');
|
||||
// is_cached in response should be false
|
||||
expect(isCached).to.equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
cy.get('[data-test="more-horiz"]').click();
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { getChartAlias, Slice } from 'cypress/utils/vizPlugins';
|
||||
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
|
|
@ -22,6 +24,73 @@ export const TABBED_DASHBOARD = '/superset/dashboard/tabbed_dash/';
|
|||
export const CHECK_DASHBOARD_FAVORITE_ENDPOINT =
|
||||
'/superset/favstar/Dashboard/*/count';
|
||||
|
||||
export const WORLD_HEALTH_CHARTS = [
|
||||
{ name: '% Rural', viz: 'world_map' },
|
||||
{ name: 'Most Populated Countries', viz: 'table' },
|
||||
{ name: 'Region Filter', viz: 'filter_box' },
|
||||
{ name: "World's Population", viz: 'big_number' },
|
||||
{ name: 'Growth Rate', viz: 'line' },
|
||||
{ name: 'Rural Breakdown', viz: 'sunburst' },
|
||||
{ name: "World's Pop Growth", viz: 'area' },
|
||||
{ name: 'Life Expectancy VS Rural %', viz: 'bubble' },
|
||||
{ name: 'Treemap', viz: 'treemap' },
|
||||
{ name: 'Box plot', viz: 'box_plot' },
|
||||
] as const;
|
||||
|
||||
/** Used to specify charts expected by the test suite */
|
||||
export interface ChartSpec {
|
||||
name: string;
|
||||
viz: string;
|
||||
}
|
||||
|
||||
export function getChartGridComponent({ name, viz }: ChartSpec) {
|
||||
return cy
|
||||
.get(`[data-test="chart-grid-component"][data-test-chart-name="${name}"]`)
|
||||
.should('have.attr', 'data-test-viz-type', viz);
|
||||
}
|
||||
|
||||
export function waitForChartLoad(chart: ChartSpec) {
|
||||
return getChartGridComponent(chart).then(gridComponent => {
|
||||
const chartId = gridComponent.attr('data-test-chart-id');
|
||||
// the chart should load in under half a minute
|
||||
return (
|
||||
cy
|
||||
// this id only becomes visible when the chart is loaded
|
||||
.wrap(gridComponent)
|
||||
.find(`#chart-id-${chartId}`, { timeout: 30000 })
|
||||
.should('be.visible')
|
||||
// return the chart grid component
|
||||
.then(() => gridComponent)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const toSlicelike = ($chart: JQuery<HTMLElement>): Slice => ({
|
||||
slice_id: parseInt($chart.attr('data-test-chart-id')!, 10),
|
||||
form_data: {
|
||||
viz_type: $chart.attr('data-test-viz-type')!,
|
||||
},
|
||||
});
|
||||
|
||||
export function getChartAliasBySpec(chart: ChartSpec) {
|
||||
return getChartGridComponent(chart).then($chart =>
|
||||
cy.wrap(getChartAlias(toSlicelike($chart))),
|
||||
);
|
||||
}
|
||||
|
||||
export function getChartAliasesBySpec(charts: readonly ChartSpec[]) {
|
||||
const aliases: string[] = [];
|
||||
charts.forEach(chart =>
|
||||
getChartAliasBySpec(chart).then(alias => {
|
||||
aliases.push(alias);
|
||||
}),
|
||||
);
|
||||
// Wrapping the aliases is key.
|
||||
// That way callers can chain off this function
|
||||
// and actually get the list of aliases.
|
||||
return cy.wrap(aliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drag an element and drop it to another element.
|
||||
* Usage:
|
||||
|
|
|
|||
|
|
@ -16,77 +16,47 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { isLegacyResponse, parsePostForm } from 'cypress/utils';
|
||||
import {
|
||||
getChartAliases,
|
||||
DASHBOARD_CHART_ALIAS_PREFIX,
|
||||
isLegacyResponse,
|
||||
parsePostForm,
|
||||
} from 'cypress/utils';
|
||||
import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper';
|
||||
|
||||
interface Slice {
|
||||
slice_id: number;
|
||||
form_data: {
|
||||
viz_type: string;
|
||||
[key: string]: JSONValue;
|
||||
};
|
||||
}
|
||||
|
||||
interface DashboardData {
|
||||
slices: Slice[];
|
||||
}
|
||||
WORLD_HEALTH_CHARTS,
|
||||
WORLD_HEALTH_DASHBOARD,
|
||||
getChartAliasesBySpec,
|
||||
waitForChartLoad,
|
||||
} from './dashboard.helper';
|
||||
|
||||
describe('Dashboard filter', () => {
|
||||
let filterId: number;
|
||||
let aliases: string[];
|
||||
|
||||
const getAlias = (id: number) => `@${DASHBOARD_CHART_ALIAS_PREFIX}${id}`;
|
||||
|
||||
beforeEach(() => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
|
||||
cy.visit(WORLD_HEALTH_DASHBOARD);
|
||||
|
||||
cy.get('#app').then(app => {
|
||||
const bootstrapData = app.data('bootstrap');
|
||||
const dashboard = bootstrapData.dashboard_data as DashboardData;
|
||||
const { slices } = dashboard;
|
||||
filterId =
|
||||
dashboard.slices.find(
|
||||
slice => slice.form_data.viz_type === 'filter_box',
|
||||
)?.slice_id || 0;
|
||||
|
||||
aliases = getChartAliases(slices);
|
||||
|
||||
// wait the initial page load requests
|
||||
cy.wait(aliases);
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply filter', () => {
|
||||
cy.get('.Select__placeholder:first').click();
|
||||
WORLD_HEALTH_CHARTS.forEach(waitForChartLoad);
|
||||
getChartAliasesBySpec(
|
||||
WORLD_HEALTH_CHARTS.filter(({ viz }) => viz !== 'filter_box'),
|
||||
).then(nonFilterChartAliases => {
|
||||
cy.get('.Select__placeholder:first').click();
|
||||
|
||||
// should show the filter indicator
|
||||
cy.get('svg[data-test="filter"]:visible').should(nodes => {
|
||||
expect(nodes.length).to.least(9);
|
||||
});
|
||||
// should show the filter indicator
|
||||
cy.get('svg[data-test="filter"]:visible').should(nodes => {
|
||||
expect(nodes.length).to.least(9);
|
||||
});
|
||||
|
||||
cy.get('.Select__control:first input[type=text]').type('So', {
|
||||
force: true,
|
||||
delay: 100,
|
||||
});
|
||||
cy.get('.Select__control:first input[type=text]').type('So', {
|
||||
force: true,
|
||||
delay: 100,
|
||||
});
|
||||
|
||||
cy.get('.Select__menu').first().contains('South Asia').click();
|
||||
cy.get('.Select__menu').first().contains('South Asia').click();
|
||||
|
||||
// should still have all filter indicators
|
||||
cy.get('svg[data-test="filter"]:visible').should(nodes => {
|
||||
expect(nodes.length).to.least(9);
|
||||
});
|
||||
// should still have all filter indicators
|
||||
cy.get('svg[data-test="filter"]:visible').should(nodes => {
|
||||
expect(nodes.length).to.least(9);
|
||||
});
|
||||
|
||||
cy.get('.filter_box button').click({ force: true });
|
||||
cy.wait(aliases.filter(x => x !== getAlias(filterId))).then(requests =>
|
||||
Promise.all(
|
||||
requests.map(async ({ response, request }) => {
|
||||
cy.get('.filter_box button').click({ force: true });
|
||||
cy.wait(nonFilterChartAliases).then(requests => {
|
||||
requests.forEach(({ response, request }) => {
|
||||
const responseBody = response?.body;
|
||||
let requestFilter;
|
||||
if (isLegacyResponse(responseBody)) {
|
||||
|
|
@ -103,9 +73,9 @@ describe('Dashboard filter', () => {
|
|||
op: '==',
|
||||
val: 'South Asia',
|
||||
});
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// TODO add test with South Asia{enter} type action to select filter
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,53 +17,18 @@
|
|||
* under the License.
|
||||
*/
|
||||
import {
|
||||
getChartAliases,
|
||||
isLegacyResponse,
|
||||
getSliceIdFromRequestUrl,
|
||||
JsonObject,
|
||||
} from '../../utils/vizPlugins';
|
||||
import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper';
|
||||
waitForChartLoad,
|
||||
WORLD_HEALTH_CHARTS,
|
||||
WORLD_HEALTH_DASHBOARD,
|
||||
} from './dashboard.helper';
|
||||
|
||||
describe('Dashboard load', () => {
|
||||
let dashboard;
|
||||
let aliases: string[];
|
||||
beforeEach(() => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
|
||||
cy.visit(WORLD_HEALTH_DASHBOARD);
|
||||
|
||||
cy.get('#app').then(nodes => {
|
||||
const bootstrapData = JSON.parse(nodes[0].dataset.bootstrap || '');
|
||||
dashboard = bootstrapData.dashboard_data;
|
||||
const { slices } = dashboard;
|
||||
// then define routes and create alias for each requests
|
||||
aliases = getChartAliases(slices);
|
||||
});
|
||||
});
|
||||
|
||||
it('should load dashboard', () => {
|
||||
// wait and verify one-by-one
|
||||
cy.wait(aliases).then(requests =>
|
||||
Promise.all(
|
||||
requests.map(async ({ response, request }) => {
|
||||
const responseBody = response?.body;
|
||||
let sliceId;
|
||||
if (isLegacyResponse(responseBody)) {
|
||||
expect(responseBody).to.have.property('errors');
|
||||
expect(responseBody.errors.length).to.eq(0);
|
||||
sliceId = responseBody.form_data.slice_id;
|
||||
} else {
|
||||
sliceId = getSliceIdFromRequestUrl(request.url);
|
||||
responseBody.result.forEach((element: JsonObject) => {
|
||||
expect(element).to.have.property('error', null);
|
||||
expect(element).to.have.property('status', 'success');
|
||||
});
|
||||
}
|
||||
cy.get('[data-test="grid-content"]')
|
||||
.find(`#chart-id-${sliceId}`)
|
||||
.should('be.visible');
|
||||
}),
|
||||
),
|
||||
);
|
||||
WORLD_HEALTH_CHARTS.forEach(waitForChartLoad);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@
|
|||
*/
|
||||
|
||||
import shortid from 'shortid';
|
||||
import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper';
|
||||
import {
|
||||
waitForChartLoad,
|
||||
WORLD_HEALTH_CHARTS,
|
||||
WORLD_HEALTH_DASHBOARD,
|
||||
} from './dashboard.helper';
|
||||
|
||||
function openDashboardEditProperties() {
|
||||
cy.get('.dashboard-header [data-test=edit-alt]').click();
|
||||
|
|
@ -31,35 +35,37 @@ describe('Dashboard save action', () => {
|
|||
cy.login();
|
||||
cy.visit(WORLD_HEALTH_DASHBOARD);
|
||||
cy.get('#app').then(data => {
|
||||
const bootstrapData = JSON.parse(data[0].dataset.bootstrap);
|
||||
const dashboard = bootstrapData.dashboard_data;
|
||||
const dashboardId = dashboard.id;
|
||||
cy.intercept('POST', `/superset/copy_dash/${dashboardId}/`).as(
|
||||
'copyRequest',
|
||||
);
|
||||
cy.get('[data-test="dashboard-header"]').then(headerElement => {
|
||||
const dashboardId = headerElement.attr('data-test-id');
|
||||
|
||||
cy.get('[data-test="more-horiz"]').trigger('click', { force: true });
|
||||
cy.get('[data-test="save-as-menu-item"]').trigger('click', {
|
||||
force: true,
|
||||
});
|
||||
cy.get('[data-test="modal-save-dashboard-button"]').trigger('click', {
|
||||
force: true,
|
||||
cy.intercept('POST', `/superset/copy_dash/${dashboardId}/`).as(
|
||||
'copyRequest',
|
||||
);
|
||||
|
||||
cy.get('[data-test="more-horiz"]').trigger('click', { force: true });
|
||||
cy.get('[data-test="save-as-menu-item"]').trigger('click', {
|
||||
force: true,
|
||||
});
|
||||
cy.get('[data-test="modal-save-dashboard-button"]').trigger('click', {
|
||||
force: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// change to what the title should be
|
||||
it('should save as new dashboard', () => {
|
||||
cy.wait('@copyRequest').then(xhr => {
|
||||
expect(xhr.response.body.dashboard_title).to.not.equal(
|
||||
`World Bank's Data`,
|
||||
);
|
||||
cy.get('[data-test="editable-title-input"]').then(element => {
|
||||
const dashboardTitle = element.attr('title');
|
||||
expect(dashboardTitle).to.not.equal(`World Bank's Data`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should save/overwrite dashboard', () => {
|
||||
// should load chart
|
||||
cy.get('.dashboard-grid', { timeout: 30000 });
|
||||
cy.get('.box_plot').should('be.visible');
|
||||
WORLD_HEALTH_CHARTS.forEach(waitForChartLoad);
|
||||
|
||||
// remove box_plot chart from dashboard
|
||||
cy.get('[data-test="edit-alt"]').click({ timeout: 5000 });
|
||||
|
|
|
|||
|
|
@ -16,15 +16,19 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { interceptChart, parsePostForm, Slice } from 'cypress/utils';
|
||||
import { TABBED_DASHBOARD } from './dashboard.helper';
|
||||
import { parsePostForm } from 'cypress/utils';
|
||||
import {
|
||||
TABBED_DASHBOARD,
|
||||
waitForChartLoad,
|
||||
getChartAliasBySpec,
|
||||
} from './dashboard.helper';
|
||||
|
||||
const TREEMAP = { name: 'Treemap', viz: 'treemap' };
|
||||
const FILTER_BOX = { name: 'Region Filter', viz: 'filter_box' };
|
||||
const LINE_CHART = { name: 'Growth Rate', viz: 'line' };
|
||||
const BOX_PLOT = { name: 'Box plot', viz: 'box_plot' };
|
||||
|
||||
describe('Dashboard tabs', () => {
|
||||
let filterId;
|
||||
let treemapId;
|
||||
let linechartId;
|
||||
let boxplotId;
|
||||
|
||||
// cypress can not handle window.scrollTo
|
||||
// https://github.com/cypress-io/cypress/issues/2761
|
||||
// add this exception handler to pass test
|
||||
|
|
@ -38,36 +42,11 @@ describe('Dashboard tabs', () => {
|
|||
cy.login();
|
||||
|
||||
cy.visit(TABBED_DASHBOARD);
|
||||
|
||||
cy.get('#app').then(data => {
|
||||
const bootstrapData = JSON.parse(data[0].dataset.bootstrap || '');
|
||||
const dashboard = bootstrapData.dashboard_data as { slices: Slice[] };
|
||||
filterId = dashboard.slices.find(
|
||||
slice => slice.form_data.viz_type === 'filter_box',
|
||||
)?.slice_id;
|
||||
boxplotId = dashboard.slices.find(
|
||||
slice => slice.form_data.viz_type === 'box_plot',
|
||||
)?.slice_id;
|
||||
treemapId = dashboard.slices.find(
|
||||
slice => slice.form_data.viz_type === 'treemap',
|
||||
)?.slice_id;
|
||||
linechartId = dashboard.slices.find(
|
||||
slice => slice.form_data.viz_type === 'line',
|
||||
)?.slice_id;
|
||||
interceptChart({ sliceId: filterId, legacy: true }).as('filterRequest');
|
||||
interceptChart({ sliceId: treemapId, legacy: true }).as('treemapRequest');
|
||||
interceptChart({ sliceId: linechartId, legacy: true }).as(
|
||||
'linechartRequest',
|
||||
);
|
||||
interceptChart({ sliceId: boxplotId, legacy: false }).as(
|
||||
'boxplotRequest',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should switch active tab on click', () => {
|
||||
cy.wait('@filterRequest');
|
||||
cy.wait('@treemapRequest');
|
||||
waitForChartLoad(FILTER_BOX);
|
||||
waitForChartLoad(TREEMAP);
|
||||
|
||||
cy.get('[data-test="dashboard-component-tabs"]')
|
||||
.first()
|
||||
|
|
@ -93,14 +72,8 @@ describe('Dashboard tabs', () => {
|
|||
|
||||
it('should load charts when tab is visible', () => {
|
||||
// landing in first tab, should see 2 charts
|
||||
cy.wait('@filterRequest');
|
||||
cy.get('[data-test="grid-container"]')
|
||||
.find('.filter_box')
|
||||
.should('be.visible');
|
||||
cy.wait('@treemapRequest');
|
||||
cy.get('[data-test="grid-container"]')
|
||||
.find('.treemap')
|
||||
.should('be.visible');
|
||||
waitForChartLoad(FILTER_BOX);
|
||||
waitForChartLoad(TREEMAP);
|
||||
cy.get('[data-test="grid-container"]')
|
||||
.find('.box_plot')
|
||||
.should('not.exist');
|
||||
|
|
@ -114,7 +87,7 @@ describe('Dashboard tabs', () => {
|
|||
|
||||
cy.get('@row-level-tabs').last().click();
|
||||
|
||||
cy.wait('@linechartRequest');
|
||||
waitForChartLoad(LINE_CHART);
|
||||
cy.get('[data-test="grid-container"]').find('.line').should('be.visible');
|
||||
|
||||
// click top level tab, see 1 more chart
|
||||
|
|
@ -132,30 +105,33 @@ describe('Dashboard tabs', () => {
|
|||
|
||||
it('should send new queries when tab becomes visible', () => {
|
||||
// landing in first tab
|
||||
cy.wait('@filterRequest');
|
||||
cy.wait('@treemapRequest');
|
||||
waitForChartLoad(FILTER_BOX);
|
||||
waitForChartLoad(TREEMAP);
|
||||
|
||||
// apply filter
|
||||
cy.get('.Select__control').first().should('be.visible').click();
|
||||
cy.get('.Select__control input[type=text]').first().focus().type('South');
|
||||
cy.get('.Select__option').contains('South Asia').click();
|
||||
cy.get('.filter_box button:not(:disabled)').contains('Apply').click();
|
||||
getChartAliasBySpec(TREEMAP).then(treemapAlias => {
|
||||
// apply filter
|
||||
cy.get('.Select__control').first().should('be.visible').click();
|
||||
cy.get('.Select__control input[type=text]').first().focus().type('South');
|
||||
cy.get('.Select__option').contains('South Asia').click();
|
||||
cy.get('.filter_box button:not(:disabled)').contains('Apply').click();
|
||||
|
||||
// send new query from same tab
|
||||
cy.wait('@treemapRequest').then(({ request }) => {
|
||||
const requestBody = parsePostForm(request.body);
|
||||
const requestParams = JSON.parse(requestBody.form_data as string);
|
||||
expect(requestParams.extra_filters[0]).deep.eq({
|
||||
col: 'region',
|
||||
op: '==',
|
||||
val: 'South Asia',
|
||||
// send new query from same tab
|
||||
cy.wait(treemapAlias).then(({ request }) => {
|
||||
const requestBody = parsePostForm(request.body);
|
||||
const requestParams = JSON.parse(requestBody.form_data as string);
|
||||
expect(requestParams.extra_filters[0]).deep.eq({
|
||||
col: 'region',
|
||||
op: '==',
|
||||
val: 'South Asia',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
cy.intercept('/superset/explore_json/?*').as('legacyChartData');
|
||||
// click row level tab, send 1 more query
|
||||
cy.get('.ant-tabs-tab').contains('row tab 2').click();
|
||||
|
||||
cy.wait('@linechartRequest').then(({ request }) => {
|
||||
cy.wait('@legacyChartData').then(({ request }) => {
|
||||
const requestBody = parsePostForm(request.body);
|
||||
const requestParams = JSON.parse(requestBody.form_data as string);
|
||||
expect(requestParams.extra_filters[0]).deep.eq({
|
||||
|
|
@ -163,39 +139,41 @@ describe('Dashboard tabs', () => {
|
|||
op: '==',
|
||||
val: 'South Asia',
|
||||
});
|
||||
expect(requestParams.viz_type).eq(LINE_CHART.viz);
|
||||
});
|
||||
|
||||
cy.intercept('POST', '/api/v1/chart/data?*').as('v1ChartData');
|
||||
|
||||
// click top level tab, send 1 more query
|
||||
cy.get('.ant-tabs-tab').contains('Tab B').click();
|
||||
|
||||
cy.wait('@boxplotRequest').then(({ request }) => {
|
||||
const requestBody = request.body;
|
||||
const requestParams = requestBody.queries[0];
|
||||
expect(requestParams.filters[0]).deep.eq({
|
||||
cy.wait('@v1ChartData').then(({ request }) => {
|
||||
expect(request.body.queries[0].filters[0]).deep.eq({
|
||||
col: 'region',
|
||||
op: '==',
|
||||
val: 'South Asia',
|
||||
});
|
||||
});
|
||||
|
||||
// navigate to filter and clear filter
|
||||
cy.get('.ant-tabs-tab').contains('Tab A').click();
|
||||
cy.get('.ant-tabs-tab').contains('row tab 1').click();
|
||||
getChartAliasBySpec(BOX_PLOT).then(boxPlotAlias => {
|
||||
// navigate to filter and clear filter
|
||||
cy.get('.ant-tabs-tab').contains('Tab A').click();
|
||||
cy.get('.ant-tabs-tab').contains('row tab 1').click();
|
||||
|
||||
cy.get('.Select__clear-indicator').click();
|
||||
cy.get('.filter_box button:not(:disabled)').contains('Apply').click();
|
||||
cy.get('.Select__clear-indicator').click();
|
||||
cy.get('.filter_box button:not(:disabled)').contains('Apply').click();
|
||||
|
||||
// trigger 1 new query
|
||||
cy.wait('@treemapRequest');
|
||||
// trigger 1 new query
|
||||
waitForChartLoad(TREEMAP);
|
||||
// make sure query API not requested multiple times
|
||||
cy.on('fail', err => {
|
||||
expect(err.message).to.include('timed out waiting');
|
||||
return false;
|
||||
});
|
||||
|
||||
// make sure query API not requested multiple times
|
||||
cy.on('fail', err => {
|
||||
expect(err.message).to.include('timed out waiting');
|
||||
return false;
|
||||
});
|
||||
|
||||
cy.wait('@boxplotRequest', { timeout: 1000 }).then(() => {
|
||||
throw new Error('Unexpected API call.');
|
||||
cy.wait(boxPlotAlias, { timeout: 1000 }).then(() => {
|
||||
throw new Error('Unexpected API call.');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,52 +16,35 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { parsePostForm, JsonObject } from 'cypress/utils';
|
||||
import {
|
||||
isLegacyResponse,
|
||||
getChartAliases,
|
||||
parsePostForm,
|
||||
Dashboard,
|
||||
JsonObject,
|
||||
} from 'cypress/utils';
|
||||
import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper';
|
||||
WORLD_HEALTH_DASHBOARD,
|
||||
WORLD_HEALTH_CHARTS,
|
||||
waitForChartLoad,
|
||||
} from './dashboard.helper';
|
||||
|
||||
describe('Dashboard form data', () => {
|
||||
const urlParams = { param1: '123', param2: 'abc' };
|
||||
let dashboard: Dashboard;
|
||||
|
||||
beforeEach(() => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
|
||||
cy.visit(WORLD_HEALTH_DASHBOARD, { qs: urlParams });
|
||||
|
||||
cy.get('#app').then(data => {
|
||||
const bootstrapData = JSON.parse(data[0].dataset.bootstrap || '');
|
||||
dashboard = bootstrapData.dashboard_data;
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply url params to slice requests', () => {
|
||||
const aliases = getChartAliases(dashboard.slices);
|
||||
// wait and verify one-by-one
|
||||
cy.wait(aliases, { timeout: 18000 }).then(requests =>
|
||||
Promise.all(
|
||||
requests.map(async ({ response, request }) => {
|
||||
const responseBody = response?.body;
|
||||
if (isLegacyResponse(responseBody)) {
|
||||
const requestParams = JSON.parse(
|
||||
parsePostForm(request.body).form_data as string,
|
||||
);
|
||||
expect(requestParams.url_params).deep.eq(urlParams);
|
||||
} else {
|
||||
// TODO: export url params to chart data API
|
||||
request.body.queries.forEach(
|
||||
(query: { url_params: JsonObject }) => {
|
||||
expect(query.url_params).deep.eq(urlParams);
|
||||
},
|
||||
);
|
||||
}
|
||||
}),
|
||||
),
|
||||
);
|
||||
cy.intercept('/superset/explore_json/*', request => {
|
||||
const requestParams = JSON.parse(
|
||||
parsePostForm(request.body).form_data as string,
|
||||
);
|
||||
expect(requestParams.url_params).deep.eq(urlParams);
|
||||
});
|
||||
cy.intercept('/api/v1/chart/data?*', request => {
|
||||
// TODO: export url params to chart data API
|
||||
request.body.queries.forEach((query: { url_params: JsonObject }) => {
|
||||
expect(query.url_params).deep.eq(urlParams);
|
||||
});
|
||||
});
|
||||
|
||||
WORLD_HEALTH_CHARTS.forEach(waitForChartLoad);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ const V1_PLUGINS = [
|
|||
'pie',
|
||||
'table',
|
||||
];
|
||||
export const DASHBOARD_CHART_ALIAS_PREFIX = 'getJson_';
|
||||
export const DASHBOARD_CHART_ALIAS_PREFIX = 'getChartData_';
|
||||
|
||||
export function isLegacyChart(vizType: string): boolean {
|
||||
return !V1_PLUGINS.includes(vizType);
|
||||
|
|
@ -57,24 +57,25 @@ export function getSliceIdFromRequestUrl(url: string) {
|
|||
return query?.match(/\d+/)?.[0];
|
||||
}
|
||||
|
||||
export function getChartDataRouteForSlice(slice: Slice) {
|
||||
const vizType = slice.form_data.viz_type;
|
||||
const isLegacy = isLegacyChart(vizType);
|
||||
const formData = encodeURIComponent(`{"slice_id":${slice.slice_id}}`);
|
||||
if (isLegacy) {
|
||||
return `/superset/explore_json/?*${formData}*`;
|
||||
}
|
||||
return `/api/v1/chart/data?*${formData}*`;
|
||||
}
|
||||
|
||||
export function getChartAlias(slice: Slice): string {
|
||||
const alias = `${DASHBOARD_CHART_ALIAS_PREFIX}${slice.slice_id}_${slice.form_data.viz_type}`;
|
||||
const route = getChartDataRouteForSlice(slice);
|
||||
cy.intercept('POST', route).as(alias);
|
||||
return `@${alias}`;
|
||||
}
|
||||
|
||||
export function getChartAliases(slices: Slice[]): string[] {
|
||||
const aliases: string[] = [];
|
||||
Array.from(slices).forEach(slice => {
|
||||
const vizType = slice.form_data.viz_type;
|
||||
const isLegacy = isLegacyChart(vizType);
|
||||
const alias = `${DASHBOARD_CHART_ALIAS_PREFIX}${slice.slice_id}`;
|
||||
const formData = encodeURIComponent(`{"slice_id":${slice.slice_id}}`);
|
||||
if (isLegacy) {
|
||||
const route = `/superset/explore_json/?*${formData}*`;
|
||||
cy.intercept('POST', `${route}`).as(alias);
|
||||
aliases.push(`@${alias}`);
|
||||
} else {
|
||||
const route = `/api/v1/chart/data?*${formData}*`;
|
||||
cy.intercept('POST', `${route}`).as(alias);
|
||||
aliases.push(`@${alias}`);
|
||||
}
|
||||
});
|
||||
return aliases;
|
||||
return Array.from(slices).map(getChartAlias);
|
||||
}
|
||||
|
||||
export function interceptChart({
|
||||
|
|
|
|||
|
|
@ -387,6 +387,7 @@ class Header extends React.PureComponent {
|
|||
<StyledDashboardHeader
|
||||
className="dashboard-header"
|
||||
data-test="dashboard-header"
|
||||
data-test-id={`${dashboardInfo.id}`}
|
||||
>
|
||||
<div className="dashboard-component-header header-large">
|
||||
<EditableTitle
|
||||
|
|
|
|||
|
|
@ -288,7 +288,13 @@ export default class Chart extends React.Component {
|
|||
})
|
||||
: {};
|
||||
return (
|
||||
<div className="chart-slice">
|
||||
<div
|
||||
className="chart-slice"
|
||||
data-test="chart-grid-component"
|
||||
data-test-chart-id={id}
|
||||
data-test-viz-type={slice.viz_type}
|
||||
data-test-chart-name={slice.slice_name}
|
||||
>
|
||||
<SliceHeader
|
||||
innerRef={this.setHeaderRef}
|
||||
slice={slice}
|
||||
|
|
|
|||
Loading…
Reference in New Issue