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 <[email protected]> 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"
