You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by di...@apache.org on 2022/09/02 16:55:16 UTC
[superset] 03/05: Refactor edit/save dashboard tests
This is an automated email from the ASF dual-hosted git repository.
diegopucci pushed a commit to branch chore/cypress-runtime-enhancements
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 984baf6bf9af0715619e6ad32c46c15ef5b81596
Author: geido <di...@gmail.com>
AuthorDate: Fri Sep 2 14:52:36 2022 +0300
Refactor edit/save dashboard tests
---
.../cypress-base/cypress/fixtures/dashboards.json | 12 +-
.../integration/dashboard/dashboard.helper.ts | 24 --
.../integration/dashboard/edit_mode.test.js | 98 --------
.../integration/dashboard/edit_properties.test.ts | 263 --------------------
.../cypress/integration/dashboard/editsave.test.ts | 272 +++++++++++++++++++++
.../cypress/integration/dashboard/markdown.test.ts | 4 +-
.../integration/dashboard/nativeFilters.test.ts | 2 +-
.../cypress/integration/dashboard/save.test.js | 163 ------------
.../cypress-base/cypress/support/index.d.ts | 3 +
.../cypress-base/cypress/support/index.ts | 58 ++---
.../cypress-base/cypress/utils/index.ts | 23 ++
.../cypress-base/cypress/utils/urls.ts | 3 +-
.../src/components/PageHeaderWithActions/index.tsx | 1 +
.../components/BuilderComponentPane/index.tsx | 1 +
.../src/dashboard/components/Header/index.jsx | 2 +-
.../dashboard/components/PropertiesModal/index.tsx | 1 +
16 files changed, 345 insertions(+), 585 deletions(-)
diff --git a/superset-frontend/cypress-base/cypress/fixtures/dashboards.json b/superset-frontend/cypress-base/cypress/fixtures/dashboards.json
index 6972cb0552..68a9984e41 100644
--- a/superset-frontend/cypress-base/cypress/fixtures/dashboards.json
+++ b/superset-frontend/cypress-base/cypress/fixtures/dashboards.json
@@ -1,14 +1,18 @@
[
{
- "dashboard_title": "1 - Sample dashboard"
+ "dashboard_title": "1 - Sample dashboard",
+ "slug": "1-sample-dashboard"
},
{
- "dashboard_title": "2 - Sample dashboard"
+ "dashboard_title": "2 - Sample dashboard",
+ "slug": "2-sample-dashboard"
},
{
- "dashboard_title": "3 - Sample dashboard"
+ "dashboard_title": "3 - Sample dashboard",
+ "slug": "3-sample-dashboard"
},
{
- "dashboard_title": "4 - Sample dashboard"
+ "dashboard_title": "4 - Sample dashboard",
+ "slug": "4-sample-dashboard"
}
]
\ No newline at end of file
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 39b7fc40c2..9868eb5b5e 100644
--- a/superset-frontend/cypress-base/cypress/integration/dashboard/dashboard.helper.ts
+++ b/superset-frontend/cypress-base/cypress/integration/dashboard/dashboard.helper.ts
@@ -67,30 +67,6 @@ export const ECHARTS_CHARTS = [
{ name: 'Energy Force Layout', viz: 'graph_chart' },
] as const;
-/**
- * Drag an element and drop it to another element.
- * Usage:
- * drag(source).to(target);
- */
-export function drag(selector: string, content: string | number | RegExp) {
- const dataTransfer = { data: {} };
- return {
- to(target: string | Cypress.Chainable) {
- cy.get('.dragdroppable')
- .contains(selector, content)
- .trigger('mousedown', { which: 1 })
- .trigger('dragstart', { dataTransfer })
- .trigger('drag', {});
-
- (typeof target === 'string' ? cy.get(target) : target)
- .trigger('dragover', { dataTransfer })
- .trigger('drop', { dataTransfer })
- .trigger('dragend', { dataTransfer })
- .trigger('mouseup', { which: 1 });
- },
- };
-}
-
export function resize(selector: string) {
return {
to(cordX: number, cordY: number) {
diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js b/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js
deleted file mode 100644
index 0f646cce3d..0000000000
--- a/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/**
- * 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.
- */
-import { drag } from './dashboard.helper';
-import { WORLD_HEALTH_DASHBOARD } from 'cypress/utils/urls';
-
-describe('Dashboard edit mode', () => {
- beforeEach(() => {
- cy.login();
- cy.visit(WORLD_HEALTH_DASHBOARD);
- cy.get('.header-with-actions')
- .find('[aria-label="Edit dashboard"]')
- .click();
- });
-
- it('remove, and add chart flow', () => {
- // wait for box plot to appear
- cy.get('[data-test="grid-container"]').find('.box_plot', {
- timeout: 10000,
- });
- const elementsCount = 10;
-
- cy.get('[data-test="dashboard-component-chart-holder"]')
- .find('[data-test="dashboard-delete-component-button"]')
- .last()
- .then($el => {
- cy.wrap($el).invoke('show').click();
- // box plot should be gone
- cy.get('[data-test="grid-container"]')
- .find('.box_plot')
- .should('not.exist');
- });
-
- // find box plot is available from list
- cy.get('[data-test="dashboard-charts-filter-search-input"]').type(
- 'Box plot',
- );
- cy.get('[data-test="card-title"]').should('have.length', 1);
-
- drag('[data-test="card-title"]', 'Box plot').to(
- '.grid-row.background--transparent:last',
- );
-
- // add back to dashboard
- cy.get('[data-test="grid-container"]')
- .find('.box_plot')
- .should('be.visible');
-
- // should show Save changes button
- cy.get('[data-test="header-save-button"]').should('be.visible');
-
- // undo first step and expect deleted item
- cy.get('[data-test="undo-action"]').click();
- cy.get('[data-test="grid-container"]')
- .find('[data-test="chart-container"]')
- .should('have.length', elementsCount - 1);
-
- // Box plot chart should be gone
- cy.get('[data-test="grid-container"]')
- .find('.box_plot')
- .should('not.exist');
-
- // undo second step and expect initial items count
- cy.get('[data-test="undo-action"]').click();
- cy.get('[data-test="grid-container"]')
- .find('[data-test="chart-container"]')
- .should('have.length', elementsCount);
- cy.get('[data-test="card-title"]').contains('Box plot', { timeout: 5000 });
-
- // save changes button should be disabled
- cy.get('[data-test="header-save-button"]').should('be.disabled');
-
- // no changes, can switch to view mode
- cy.get('[data-test="dashboard-edit-actions"]')
- .find('[data-test="discard-changes-button"]')
- .should('be.visible')
- .click();
- cy.get('.header-with-actions').within(() => {
- cy.get('[data-test="dashboard-edit-actions"]').should('not.be.visible');
- cy.get('[aria-label="Edit dashboard"]').should('be.visible');
- });
- });
-});
diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/edit_properties.test.ts b/superset-frontend/cypress-base/cypress/integration/dashboard/edit_properties.test.ts
deleted file mode 100644
index 57839ebc2c..0000000000
--- a/superset-frontend/cypress-base/cypress/integration/dashboard/edit_properties.test.ts
+++ /dev/null
@@ -1,263 +0,0 @@
-/**
- * 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 * as ace from 'brace';
-import * as shortid from 'shortid';
-import { USA_BIRTH_NAMES_DASHBOARD } from './dashboard.helper';
-
-function selectColorScheme(color: string) {
- // open color scheme dropdown
- cy.get('.ant-modal-body')
- .contains('Color scheme')
- .parents('.ControlHeader')
- .next('.ant-select')
- .click()
- .then($colorSelect => {
- // select a new color scheme
- cy.wrap($colorSelect).find(`[data-test="${color}"]`).click();
- });
-}
-
-function assertMetadata(text: string) {
- const regex = new RegExp(text);
- cy.get('.ant-modal-body')
- .find('#json_metadata')
- .should('be.visible')
- .then(() => {
- const metadata = cy.$$('#json_metadata')[0];
-
- // cypress can read this locally, but not in ci
- // so we have to use the ace module directly to fetch the value
- expect(ace.edit(metadata).getValue()).to.match(regex);
- });
-}
-function clear(input: string) {
- cy.get(input).type('{selectall}{backspace}');
-}
-function type(input: string, text: string) {
- cy.get(input).type(text, { parseSpecialCharSequences: false });
-}
-
-function openAdvancedProperties() {
- return cy
- .get('.ant-modal-body')
- .contains('Advanced')
- .should('be.visible')
- .click();
-}
-
-function openDashboardEditProperties() {
- // open dashboard properties edit modal
- cy.get(
- '.header-with-actions .right-button-panel .ant-dropdown-trigger',
- ).trigger('click', {
- force: true,
- });
- cy.get('[data-test=header-actions-menu]')
- .contains('Edit properties')
- .click({ force: true });
-}
-
-describe('Dashboard edit action', () => {
- beforeEach(() => {
- cy.login();
- cy.visit(USA_BIRTH_NAMES_DASHBOARD);
- cy.intercept(`/api/v1/dashboard/births`).as('dashboardGet');
- cy.get('.dashboard-grid', { timeout: 50000 })
- .should('be.visible') // wait for 50 secs to load dashboard
- .then(() => {
- cy.get('.header-with-actions [aria-label="Edit dashboard"]')
- .should('be.visible')
- .click();
- openDashboardEditProperties();
- });
- });
-
- it('should update the title', () => {
- const dashboardTitle = `Test dashboard [${shortid.generate()}]`;
-
- // update title
- cy.get('.ant-modal-body')
- .should('be.visible')
- .contains('Title')
- .get('[data-test="dashboard-title-input"]')
- .type(`{selectall}{backspace}${dashboardTitle}`);
-
- // save edit changes
- cy.get('.ant-modal-footer')
- .contains('Apply')
- .click()
- .then(() => {
- // assert that modal edit window has closed
- cy.get('.ant-modal-body').should('not.exist');
-
- // assert title has been updated
- cy.get('[data-test="editable-title-input"]').should(
- 'have.value',
- dashboardTitle,
- );
- });
- });
- describe('the color picker is changed', () => {
- describe('the metadata has a color scheme', () => {
- describe('the advanced tab is open', () => {
- it('should overwrite the color scheme', () => {
- openAdvancedProperties();
- selectColorScheme('d3Category20b');
- assertMetadata('d3Category20b');
- });
- });
- describe('the advanced tab is not open', () => {
- it('should overwrite the color scheme', () => {
- selectColorScheme('bnbColors');
- openAdvancedProperties();
- assertMetadata('bnbColors');
- });
- });
- });
- });
- describe('a valid colorScheme is entered', () => {
- it('should save json metadata color change to dropdown', () => {
- // edit json metadata
- openAdvancedProperties().then(() => {
- clear('#json_metadata');
- type('#json_metadata', '{"color_scheme":"d3Category20"}');
- });
-
- // save edit changes
- cy.get('.ant-modal-footer')
- .contains('Apply')
- .click()
- .then(() => {
- // assert that modal edit window has closed
- cy.get('.ant-modal-body').should('not.exist');
-
- // assert color has been updated
- openDashboardEditProperties();
- openAdvancedProperties().then(() => {
- assertMetadata('d3Category20');
- });
- cy.get('.ant-select-selection-item .color-scheme-option').should(
- 'have.attr',
- 'data-test',
- 'd3Category20',
- );
- });
- });
- });
- describe('an invalid colorScheme is entered', () => {
- it('should throw an error', () => {
- // edit json metadata
- openAdvancedProperties().then(() => {
- clear('#json_metadata');
- type('#json_metadata', '{"color_scheme":"THIS_DOES_NOT_WORK"}');
- });
-
- // save edit changes
- cy.get('.ant-modal-footer')
- .contains('Apply')
- .click()
- .then(() => {
- // assert that modal edit window has closed
- cy.get('.ant-modal-body')
- .contains('A valid color scheme is required')
- .should('be.visible');
- });
-
- cy.on('uncaught:exception', err => {
- expect(err.message).to.include('something about the error');
-
- // return false to prevent the error from
- // failing this test
- return false;
- });
- });
- });
- describe.skip('the color scheme affects the chart colors', () => {
- it('should change the chart colors', () => {
- openAdvancedProperties().then(() => {
- clear('#json_metadata');
- type(
- '#json_metadata',
- '{"color_scheme":"lyftColors", "label_colors": {}}',
- );
- });
-
- cy.get('.ant-modal-footer')
- .contains('Apply')
- .click()
- .then(() => {
- cy.get('.ant-modal-body').should('not.exist');
- // assert that the chart has changed colors
- cy.get('.line .nv-legend-symbol')
- .first()
- .should('have.css', 'fill', 'rgb(117, 96, 170)');
- });
- });
- it('the label colors should take precedence over the scheme', () => {
- openAdvancedProperties().then(() => {
- clear('#json_metadata');
- type(
- '#json_metadata',
- '{"color_scheme":"lyftColors","label_colors":{"Amanda":"red"}}',
- );
- });
-
- cy.get('.ant-modal-footer')
- .contains('Apply')
- .click()
- .then(() => {
- cy.get('.ant-modal-body').should('not.exist');
- // assert that the chart has changed colors
- cy.get('.line .nv-legend-symbol')
- .first()
- .should('have.css', 'fill', 'rgb(255, 0, 0)');
- });
- });
- it('the shared label colors and label colors are applied correctly', () => {
- openAdvancedProperties().then(() => {
- clear('#json_metadata');
- type(
- '#json_metadata',
- '{"color_scheme":"lyftColors","label_colors":{"Amanda":"red"}}',
- );
- });
-
- cy.get('.ant-modal-footer')
- .contains('Apply')
- .click()
- .then(() => {
- cy.get('.ant-modal-body').should('not.exist');
- // assert that the chart has changed colors
- cy.get('.line .nv-legend-symbol')
- .first()
- .should('have.css', 'fill', 'rgb(255, 0, 0)'); // label: amanda
- cy.get('.line .nv-legend-symbol')
- .eq(11)
- .should('have.css', 'fill', 'rgb(234, 11, 140)'); // label: jennifer
- cy.get('.word_cloud')
- .first()
- .find('svg text')
- .first()
- .should('have.css', 'fill', 'rgb(234, 11, 140)'); // label: jennifer
- });
- });
- });
-});
diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/editsave.test.ts b/superset-frontend/cypress-base/cypress/integration/dashboard/editsave.test.ts
new file mode 100644
index 0000000000..f456d9473a
--- /dev/null
+++ b/superset-frontend/cypress-base/cypress/integration/dashboard/editsave.test.ts
@@ -0,0 +1,272 @@
+/**
+ * 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.
+ */
+import { SAMPLE_DASHBOARD_1 } from 'cypress/utils/urls';
+import { drag } from 'cypress/utils';
+import {
+ interceptUpdate,
+} from './utils';
+import * as ace from 'brace';
+import {interceptFiltering as interceptCharts} from '../explore/utils';
+
+function editDashboard() {
+ cy.getBySel('edit-dashboard-button').click();
+}
+
+function closeModal() {
+ cy.getBySel('properties-modal-cancel-button').click({force: true});
+}
+
+function openProperties() {
+ cy.get('body')
+ .then($body => {
+ if ($body.find('[data-test="properties-modal-cancel-button"]').length) {
+ closeModal();
+ }
+ });
+ cy.getBySel('actions-trigger').click({ force: true} );
+ cy.getBySel('header-actions-menu').contains('Edit properties').click({ force: true} );
+ cy.wait(500);
+}
+
+function openAdvancedProperties() {
+ return cy
+ .get('.ant-modal-body')
+ .contains('Advanced')
+ .should('be.visible')
+ .click({ force: true });
+}
+
+function dragChart(chart = 'Unicode Cloud') {
+ drag('[data-test="card-title"]', chart).to(
+ '[data-test="grid-content"] [data-test="dragdroppable-object"]',
+ );
+}
+
+function discardChanges() {
+ cy.getBySel('undo-action').click({ force: true });
+}
+
+function visitEdit() {
+ interceptCharts();
+ cy.visit(SAMPLE_DASHBOARD_1);
+ editDashboard();
+ cy.wait('@filtering');
+}
+
+function selectColorScheme(color: string) {
+ cy.get('[data-test="dashboard-edit-properties-form"] [aria-label="Select color scheme"]').first().click();
+ cy.getBySel(color).click();
+}
+
+function applyChanges() {
+ cy.getBySel('properties-modal-apply-button').click();
+}
+
+function saveChanges() {
+ interceptUpdate();
+ cy.getBySel('header-save-button').click();
+ cy.wait('@update');
+}
+
+function assertMetadata(text: string) {
+ const regex = new RegExp(text);
+ cy.get('.ant-modal-body')
+ .find('#json_metadata')
+ .should('be.visible')
+ .then(() => {
+ const metadata = cy.$$('#json_metadata')[0];
+
+ // cypress can read this locally, but not in ci
+ // so we have to use the ace module directly to fetch the value
+ expect(ace.edit(metadata).getValue()).to.match(regex);
+ });
+}
+function clearAll(input: string) {
+ return cy.get(input).type('{selectall}{backspace}');
+}
+
+describe('Dashboard edit', () => {
+ beforeEach(() => {
+ cy.preserveLogin();
+ });
+
+ describe('Edit mode', () => {
+ before(() => {
+ cy.createSampleDashboards();
+ visitEdit();
+ });
+
+ beforeEach(() => {
+ discardChanges();
+ });
+
+ it('should enable edit mode', () => {
+ cy.getBySel('dashboard-builder-sidepane').should('be.visible');
+ });
+
+ it('should edit the title inline', () => {
+ cy.getBySel('editable-title-input').clear().type('Edited title{enter}');
+ cy.getBySel('header-save-button').should('be.enabled');
+ });
+
+ it('should filter charts', () => {
+ interceptCharts();
+ cy.getBySel('dashboard-charts-filter-search-input').type('Unicode');
+ cy.wait('@filtering');
+ cy.getBySel('chart-card').should('have.length', 1).contains('Unicode Cloud');
+ cy.getBySel('dashboard-charts-filter-search-input').clear();
+ });
+
+ });
+
+ describe('Components', () => {
+ before(() => {
+ cy.createSampleDashboards();
+ });
+
+ beforeEach(() => {
+ visitEdit();
+ });
+
+ it('should add charts', () => {
+ dragChart();
+ cy.getBySel('dashboard-component-chart-holder').should('have.length', 1);
+ });
+
+ it('should remove added charts', () => {
+ dragChart('% Rural');
+ cy.getBySel('dashboard-component-chart-holder').should('have.length', 1);
+ cy.getBySel('dashboard-delete-component-button').click();
+ cy.getBySel('dashboard-component-chart-holder').should('have.length', 0);
+ });
+ });
+
+ describe('Edit properties', () => {
+ before(() => {
+ cy.createSampleDashboards();
+ visitEdit();
+ });
+
+ beforeEach(() => {
+ openProperties();
+ });
+
+ it('should overwrite the color scheme when advanced is closed', () => {
+ selectColorScheme('d3Category20b');
+ openAdvancedProperties();
+ assertMetadata('d3Category20b');
+ applyChanges();
+ });
+
+ it('should overwrite the color scheme when advanced is open', () => {
+ openAdvancedProperties();
+ selectColorScheme('googleCategory10c');
+ assertMetadata('googleCategory10c');
+ applyChanges();
+ });
+
+ it('should accept a valid color scheme', () => {
+ openAdvancedProperties();
+ clearAll('#json_metadata').then(() => {
+ cy.get('#json_metadata').type('{"color_scheme":"lyftColors"}', { parseSpecialCharSequences: false })
+ applyChanges();
+ openProperties();
+ openAdvancedProperties();
+ assertMetadata('lyftColors');
+ applyChanges();
+ })
+
+ });
+
+ it('should not accept an invalid color scheme', () => {
+ openAdvancedProperties();
+ clearAll('#json_metadata').then(() => {
+ cy.get('#json_metadata').type('{"color_scheme":"wrongcolorscheme"}', { parseSpecialCharSequences: false })
+ applyChanges();
+ cy.get('.ant-modal-body')
+ .contains('A valid color scheme is required')
+ .should('be.visible');
+ })
+ });
+
+ it('should edit the title', () => {
+ cy.getBySel('dashboard-title-input').clear().type('Edited title');
+ applyChanges();
+ cy.getBySel('editable-title-input').should('have.value', 'Edited title');
+ });
+ });
+
+ describe('Color schemes', () => {
+ beforeEach(() => {
+ cy.createSampleDashboards();
+ visitEdit();
+ });
+
+ it('should apply a valid color scheme', () => {
+ dragChart('Top 10 California Names Timeseries');
+ openProperties();
+ selectColorScheme('lyftColors');
+ applyChanges();
+ saveChanges();
+ cy.get('.line .nv-legend-symbol')
+ .first()
+ .should('have.css', 'fill', 'rgb(234, 11, 140)');
+ });
+
+ it('label colors should take the precedence', () => {
+ dragChart('Top 10 California Names Timeseries');
+ openProperties();
+ openAdvancedProperties();
+ clearAll('#json_metadata').then(() => {
+ cy.get('#json_metadata').type('{"color_scheme":"lyftColors","label_colors":{"Anthony":"red"}}', { parseSpecialCharSequences: false })
+ applyChanges();
+ saveChanges();
+ cy.get('.line .nv-legend-symbol')
+ .first()
+ .should('have.css', 'fill', 'rgb(255, 0, 0)');
+ });
+ });
+ });
+
+ describe('Save', () => {
+ before(() => {
+ cy.createSampleDashboards();
+ visitEdit();
+ });
+
+ beforeEach(() => {
+ discardChanges();
+ })
+
+ it('should disable saving when undoing', () => {
+ dragChart();
+ cy.getBySel('header-save-button').should('be.enabled');
+ discardChanges();
+ cy.getBySel('header-save-button').should('be.disabled');
+ });
+
+ it('should save', () => {
+ dragChart();
+ cy.getBySel('header-save-button').should('be.enabled');
+ saveChanges();
+ cy.getBySel('dashboard-component-chart-holder').should('have.length', 1);
+ cy.getBySel('edit-dashboard-button').should('be.visible');
+ });
+ });
+});
diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/markdown.test.ts b/superset-frontend/cypress-base/cypress/integration/dashboard/markdown.test.ts
index a27382933b..5d7d818508 100644
--- a/superset-frontend/cypress-base/cypress/integration/dashboard/markdown.test.ts
+++ b/superset-frontend/cypress-base/cypress/integration/dashboard/markdown.test.ts
@@ -16,8 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { TABBED_DASHBOARD, drag, resize } from './dashboard.helper';
-
+import { TABBED_DASHBOARD, resize } from './dashboard.helper';
+import { drag } from 'cypress/utils';
describe('Dashboard edit markdown', () => {
beforeEach(() => {
cy.login();
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 024f7b6fa3..8b8c048862 100644
--- a/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts
+++ b/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts
@@ -553,7 +553,7 @@ xdescribe('Nativefilters', () => {
cy.get('[data-test="Treemap-list-chart-title"]')
.should('be.visible', { timeout: 5000 })
.click();
- cy.get('[data-test="query-save-button"]').click();
+ cy.get('[data-test="edit-dashboard-button"]').click();
cy.get('[data-test="save-chart-modal-select-dashboard-form"]')
.find('input[aria-label="Select a dashboard"]')
.type(`${dashboard}`, { force: true });
diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/save.test.js b/superset-frontend/cypress-base/cypress/integration/dashboard/save.test.js
deleted file mode 100644
index 07be4ef626..0000000000
--- a/superset-frontend/cypress-base/cypress/integration/dashboard/save.test.js
+++ /dev/null
@@ -1,163 +0,0 @@
-/**
- * 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.
- */
-
-import shortid from 'shortid';
-import {
- waitForChartLoad,
-} from 'cypress/utils';
-import { WORLD_HEALTH_CHARTS } from './utils';
-import { WORLD_HEALTH_DASHBOARD } from 'cypress/utils/urls';
-
-function openDashboardEditProperties() {
- // open dashboard properties edit modal
- cy.get('.header-with-actions [aria-label="Edit dashboard"]')
- .should('be.visible')
- .click();
- cy.get(
- '.header-with-actions .right-button-panel .ant-dropdown-trigger',
- ).trigger('click', {
- force: true,
- });
- cy.get('[data-test=header-actions-menu]')
- .contains('Edit properties')
- .click({ force: true });
-}
-
-describe('Dashboard save action', () => {
- beforeEach(() => {
- cy.login();
- cy.visit(WORLD_HEALTH_DASHBOARD);
- cy.get('#app').then(() => {
- cy.get('.dashboard-header-container').then(headerContainerElement => {
- const dashboardId = headerContainerElement.attr('data-test-id');
-
- cy.intercept('POST', `/superset/copy_dash/${dashboardId}/`).as(
- 'copyRequest',
- );
-
- cy.get('[aria-label="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(() => {
- cy.get('[data-test="editable-title"]').then(element => {
- const dashboardTitle = element.attr('title');
- expect(dashboardTitle).to.not.equal(`World Bank's Data`);
- });
- });
- });
-
- it('should save/overwrite dashboard', () => {
- // should load chart
- WORLD_HEALTH_CHARTS.forEach(waitForChartLoad);
-
- // remove box_plot chart from dashboard
- cy.get('[aria-label="Edit dashboard"]').click({ timeout: 5000 });
- cy.get('[data-test="dashboard-delete-component-button"]')
- .last()
- .trigger('mouseenter')
- .click();
-
- cy.get('[data-test="grid-container"]')
- .find('.box_plot')
- .should('not.exist');
-
- cy.intercept('PUT', '/api/v1/dashboard/**').as('putDashboardRequest');
- cy.get('.header-with-actions')
- .find('[data-test="header-save-button"]')
- .contains('Save')
- .click();
-
- // go back to view mode
- cy.wait('@putDashboardRequest');
- cy.get('.header-with-actions')
- .find('[aria-label="Edit dashboard"]')
- .click();
-
- // deleted boxplot should still not exist
- cy.get('[data-test="grid-container"]')
- .find('.box_plot', { timeout: 20000 })
- .should('not.exist');
- });
-
- it('should save after edit', () => {
- cy.get('.dashboard-grid', { timeout: 50000 }) // wait for 50 secs to load dashboard
- .then(() => {
- const dashboardTitle = `Test dashboard [${shortid.generate()}]`;
-
- openDashboardEditProperties();
-
- // open color scheme dropdown
- cy.get('.ant-modal-body')
- .contains('Color scheme')
- .parents('.ControlHeader')
- .next('.ant-select')
- .click()
- .then(() => {
- // select a new color scheme
- cy.get('.ant-modal-body')
- .find('.ant-select-item-option-active')
- .first()
- .click();
- });
-
- // remove json metadata
- cy.get('.ant-modal-body')
- .contains('Advanced')
- .click()
- .then(() => {
- cy.get('#json_metadata').type('{selectall}{backspace}');
- });
-
- // update title
- cy.get('[data-test="dashboard-title-input"]').type(
- `{selectall}{backspace}${dashboardTitle}`,
- );
-
- // save edit changes
- cy.get('.ant-modal-footer')
- .contains('Apply')
- .click()
- .then(() => {
- // assert that modal edit window has closed
- cy.get('.ant-modal-body').should('not.exist');
-
- // save dashboard changes
- cy.get('.header-with-actions').contains('Save').click();
-
- // assert success flash
- cy.contains('saved successfully').should('be.visible');
-
- // assert title has been updated
- cy.get(
- '.header-with-actions .title-panel [data-test="editable-title"]',
- ).should('have.text', dashboardTitle);
- });
- });
- });
-});
diff --git a/superset-frontend/cypress-base/cypress/support/index.d.ts b/superset-frontend/cypress-base/cypress/support/index.d.ts
index fc065563d3..a5011090ee 100644
--- a/superset-frontend/cypress-base/cypress/support/index.d.ts
+++ b/superset-frontend/cypress-base/cypress/support/index.d.ts
@@ -38,6 +38,9 @@ declare namespace Cypress {
getBySel(selector: string): cy;
getBySelLike(selector: string): cy;
+ getSampleData(): void;
+ cleanCharts(): void;
+ cleanDashboards(): void;
visitChartByParams(params: string | Record<string, unknown>): cy;
visitChartByName(name: string): cy;
diff --git a/superset-frontend/cypress-base/cypress/support/index.ts b/superset-frontend/cypress-base/cypress/support/index.ts
index 5f201200b5..3c24d167c3 100644
--- a/superset-frontend/cypress-base/cypress/support/index.ts
+++ b/superset-frontend/cypress-base/cypress/support/index.ts
@@ -26,36 +26,36 @@ const TokenName = Cypress.env('TOKEN_NAME');
let SAMPLE_DASHBOARDS: Record<string, any>[] = [];
let SAMPLE_CHARTS: Record<string, any>[] = [];
-function resetSamples() {
- cy.login();
- cy.fixture('dashboards.json').then(dashboards => {
- dashboards.forEach((d: { dashboard_title: string }) => {
- cy.deleteDashboardByName(d.dashboard_title, false);
- });
- });
- cy.fixture('charts.json').then(charts => {
- charts.forEach((c: { slice_name: string }) => {
- cy.deleteChartByName(c.slice_name, false);
- });
- });
-}
-
-function loadSampleData() {
- cy.login();
+Cypress.Commands.add('getSampleData', () =>
cy.getCharts().then((slices: any) => {
SAMPLE_CHARTS = slices;
- });
- cy.getDashboards().then((dashboards: any) => {
- SAMPLE_DASHBOARDS = dashboards;
- });
-}
+ cy.getDashboards().then((dashboards: any) => {
+ SAMPLE_DASHBOARDS = dashboards;
+ });
+ })
+);
-before(() => {
- loadSampleData();
-});
+Cypress.Commands.add('cleanDashboards', () =>
+ cy.fixture('dashboards.json').then(dashboards => {
+ for (let i = 0; i < dashboards.length; i += 1) {
+ cy.deleteDashboardByName(dashboards[i].dashboard_title, false)
+ }
+ })
+);
-beforeEach(() => {
- resetSamples();
+Cypress.Commands.add('cleanCharts', () =>
+ cy.fixture('charts.json').then(charts => {
+ for (let i = 0; i < charts.length; i += 1) {
+ cy.deleteChartByName(charts[i].slice_name, false)
+ }
+ })
+);
+
+before(() => {
+ cy.login();
+ cy.cleanDashboards();
+ cy.cleanCharts();
+ cy.getSampleData();
});
Cypress.Commands.add('getBySel', (selector, ...args) =>
@@ -189,6 +189,7 @@ Cypress.Commands.add(
Cypress.Commands.add('createSampleDashboards', () => {
const requests: any = [];
+ cy.cleanDashboards();
cy.fixture('dashboards.json').then(dashboards => {
for (let i = 0; i < dashboards.length; i += 1) {
requests.push(
@@ -208,11 +209,12 @@ Cypress.Commands.add('createSampleDashboards', () => {
}),
);
}
- return Promise.all(requests).then(() => loadSampleData());
+ return Promise.all(requests).then(() => cy.getSampleData());
});
});
Cypress.Commands.add('createSampleCharts', () => {
+ cy.cleanCharts();
const requests: any = [];
return cy.fixture('charts.json').then(charts => {
for (let i = 0; i < charts.length; i += 1) {
@@ -233,7 +235,7 @@ Cypress.Commands.add('createSampleCharts', () => {
}),
);
}
- return Promise.all(requests).then(() => loadSampleData());
+ return Promise.all(requests).then(() => cy.getSampleData());
});
});
diff --git a/superset-frontend/cypress-base/cypress/utils/index.ts b/superset-frontend/cypress-base/cypress/utils/index.ts
index 3bdbb0f122..949817c84a 100644
--- a/superset-frontend/cypress-base/cypress/utils/index.ts
+++ b/superset-frontend/cypress-base/cypress/utils/index.ts
@@ -86,3 +86,26 @@ export function waitForChartLoad(chart: ChartSpec) {
});
}
+/**
+ * Drag an element and drop it to another element.
+ * Usage:
+ * drag(source).to(target);
+ */
+ export function drag(selector: string, content: string | number | RegExp) {
+ const dataTransfer = { data: {} };
+ return {
+ to(target: string | Cypress.Chainable) {
+ cy.get('.dragdroppable')
+ .contains(selector, content)
+ .trigger('mousedown', { which: 1, force: true })
+ .trigger('dragstart', { dataTransfer, force: true })
+ .trigger('drag', {force: true});
+
+ (typeof target === 'string' ? cy.get(target) : target)
+ .trigger('dragover', { dataTransfer, force: true })
+ .trigger('drop', { dataTransfer, force: true })
+ .trigger('dragend', { dataTransfer, force: true })
+ .trigger('mouseup', { which: 1, force: true });
+ },
+ };
+ }
\ No newline at end of file
diff --git a/superset-frontend/cypress-base/cypress/utils/urls.ts b/superset-frontend/cypress-base/cypress/utils/urls.ts
index 2f3cc4d56c..40eb51a170 100644
--- a/superset-frontend/cypress-base/cypress/utils/urls.ts
+++ b/superset-frontend/cypress-base/cypress/utils/urls.ts
@@ -19,4 +19,5 @@
export const DASHBOARD_LIST = '/dashboard/list/';
export const CHART_LIST = '/chart/list/';
-export const WORLD_HEALTH_DASHBOARD = '/superset/dashboard/world_health/';
\ No newline at end of file
+export const WORLD_HEALTH_DASHBOARD = '/superset/dashboard/world_health/';
+export const SAMPLE_DASHBOARD_1 = '/superset/dashboard/1-sample-dashboard/';
\ No newline at end of file
diff --git a/superset-frontend/src/components/PageHeaderWithActions/index.tsx b/superset-frontend/src/components/PageHeaderWithActions/index.tsx
index e85ccdfc82..19f1ded4a7 100644
--- a/superset-frontend/src/components/PageHeaderWithActions/index.tsx
+++ b/superset-frontend/src/components/PageHeaderWithActions/index.tsx
@@ -152,6 +152,7 @@ export const PageHeaderWithActions = ({
css={menuTriggerStyles}
buttonStyle="tertiary"
aria-label={t('Menu actions trigger')}
+ data-test="actions-trigger"
>
<Icons.MoreHoriz
iconColor={theme.colors.primary.dark2}
diff --git a/superset-frontend/src/dashboard/components/BuilderComponentPane/index.tsx b/superset-frontend/src/dashboard/components/BuilderComponentPane/index.tsx
index 7a1019a0e2..39e7fd4bc4 100644
--- a/superset-frontend/src/dashboard/components/BuilderComponentPane/index.tsx
+++ b/superset-frontend/src/dashboard/components/BuilderComponentPane/index.tsx
@@ -64,6 +64,7 @@ const BuilderComponentPane: React.FC<BCPProps> = ({
<DashboardBuilderSidepane
topOffset={topOffset}
className="dashboard-builder-sidepane"
+ data-test="dashboard-builder-sidepane"
>
<ParentSize>
{({ height }) => (
diff --git a/superset-frontend/src/dashboard/components/Header/index.jsx b/superset-frontend/src/dashboard/components/Header/index.jsx
index 19b7c21ce2..354ee81bbc 100644
--- a/superset-frontend/src/dashboard/components/Header/index.jsx
+++ b/superset-frontend/src/dashboard/components/Header/index.jsx
@@ -618,7 +618,7 @@ class Header extends React.PureComponent {
<Button
buttonStyle="secondary"
onClick={this.toggleEditMode}
- data-test="query-save-button"
+ data-test="edit-dashboard-button"
className="action-button"
css={editButtonStyle}
aria-label={t('Edit dashboard')}
diff --git a/superset-frontend/src/dashboard/components/PropertiesModal/index.tsx b/superset-frontend/src/dashboard/components/PropertiesModal/index.tsx
index ebdc753226..f05b4081a5 100644
--- a/superset-frontend/src/dashboard/components/PropertiesModal/index.tsx
+++ b/superset-frontend/src/dashboard/components/PropertiesModal/index.tsx
@@ -538,6 +538,7 @@ const PropertiesModal = ({
{t('Cancel')}
</Button>
<Button
+ data-test="properties-modal-apply-button"
onClick={form.submit}
buttonSize="small"
buttonStyle="primary"