diff --git a/superset/assets/cypress/integration/explore/control.test.js b/superset/assets/cypress/integration/explore/control.test.js index a742d6d0a9..2efa7b9b8d 100644 --- a/superset/assets/cypress/integration/explore/control.test.js +++ b/superset/assets/cypress/integration/explore/control.test.js @@ -17,18 +17,20 @@ describe('Groupby', () => { cy.get('.VirtualizedSelectFocusedOption').click(); }); cy.get('button.query').click(); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); }); }); -describe('SimpleAdhocMetric', () => { - it('Clear metric and set simple adhoc metric', () => { - cy.server(); +describe('AdhocMetrics', () => { + beforeEach(() => { cy.login(); + cy.server(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); + }); + it('Clear metric and set simple adhoc metric', () => { const metricName = 'Girl Births'; - cy.route('POST', '/superset/explore_json/**').as('getJson'); cy.visitChartByName('Num Births Trend'); cy.verifySliceSuccess({ waitAlias: '@getJson' }); @@ -52,11 +54,216 @@ describe('SimpleAdhocMetric', () => { }); cy.get('button.query').click(); - cy.wait(['@getJson']).then((data) => { - expect(data.status).to.eq(200); - expect(data.response.body).to.have.property('error', null); - expect(data.response.body.data[0].key).to.equal(metricName); - cy.get('.slice_container'); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + querySubstring: metricName, + chartSelector: 'svg', + }); + }); + + it('Clear metric and set custom sql adhoc metric', () => { + const metric = 'SUM(num)/COUNT(DISTINCT name)'; + + cy.visitChartByName('Num Births Trend'); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + + cy.get('[data-test=metrics]').within(() => { + cy.get('.select-clear').click(); + cy.get('.Select-control').click({ force: true }); + cy.get('input').type('num', { force: true }); + cy.get('.VirtualizedSelectFocusedOption') + .trigger('mousedown') + .click(); + }); + + cy.get('#metrics-edit-popover').within(() => { + cy.get('#adhoc-metric-edit-tabs-tab-SQL').click(); + cy.get('.ace_content').click(); + cy.get('.ace_text-input') + .type(`{selectall}{backspace}${metric}`, { force: true }); + cy.get('button').contains('Save').click(); + }); + + cy.get('button.query').click(); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + querySubstring: metric, + chartSelector: 'svg', + }); + }); + + it('Switch between simple and custom sql tabs', () => { + cy.visitChartByName('Num Births Trend'); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + + cy.get('[data-test=metrics]').within(() => { + cy.get('.select-clear').click(); + cy.get('.Select-control').click({ force: true }); + cy.get('input').type('sum_girls', { force: true }); + cy.get('.VirtualizedSelectFocusedOption') + .trigger('mousedown') + .click(); + }); + + cy.get('#metrics-edit-popover').within(() => { + cy.get('#adhoc-metric-edit-tabs-tab-SQL').click(); + cy.get('.ace_identifier').contains('sum_girls'); + cy.get('.ace_content').click(); + cy.get('.ace_text-input') + .type('{selectall}{backspace}SUM(num)', { force: true }); + cy.get('#adhoc-metric-edit-tabs-tab-SIMPLE').click(); + cy.get('.select-value-label').contains('num'); + cy.get('button').contains('Save').click(); + }); + + cy.get('button.query').click(); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + chartSelector: 'svg', }); }); }); + +describe('AdhocFilters', () => { + beforeEach(() => { + cy.login(); + cy.server(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); + }); + + it('Set simple adhoc filter', () => { + cy.visitChartByName('Num Births Trend'); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + + cy.get('[data-test=adhoc_filters]').within(() => { + cy.get('.Select-control').click({ force: true }); + cy.get('input').type('name', { force: true }); + cy.get('.VirtualizedSelectFocusedOption') + .trigger('mousedown') + .click(); + }); + + cy.get('#filter-edit-popover').within(() => { + cy.get('[data-test=adhoc-filter-simple-value]').within(() => { + cy.get('div.select-input').click({ force: true }); + cy.get('input.select-input').type('Amy', { force: true }); + cy.get('.VirtualizedSelectFocusedOption') + .trigger('mousedown') + .click(); + }); + cy.get('button') + .contains('Save') + .click(); + }); + + cy.get('button.query').click(); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + chartSelector: 'svg', + }); + }); + + it('Set custom adhoc filter', () => { + cy.visitChartByName('Num Births Trend'); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + + cy.get('[data-test=adhoc_filters]').within(() => { + cy.get('.Select-control').click({ force: true }); + cy.get('input').type('name', { force: true }); + cy.get('.VirtualizedSelectFocusedOption') + .trigger('mousedown') + .click(); + }); + + cy.get('#filter-edit-popover').within(() => { + cy.get('#adhoc-filter-edit-tabs-tab-SQL').click(); + cy.get('.ace_content').click(); + cy.get('.ace_text-input') + .type("'Amy' OR name = 'Bob'", { force: true }); + cy.get('button') + .contains('Save') + .click(); + }); + + cy.get('button.query').click(); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + chartSelector: 'svg', + }); + }); +}); + + +describe('Advanced analytics', () => { + beforeEach(() => { + cy.login(); + cy.server(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); + }); + + it('Create custom time compare', () => { + cy.visitChartByName('Num Births Trend'); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + + cy.get('span') + .contains('Advanced Analytics') + .siblings() + .first() + .click(); + + cy.get('[data-test=time_compare]').within(() => { + cy.get('.Select-control').click({ force: true }); + cy.get('input').type('364 days', { force: true }); + cy.get('.VirtualizedSelectOption') + .trigger('mousedown') + .click(); + }); + + cy.get('button.query').click(); + cy.wait('@getJson'); + cy.reload(); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + chartSelector: 'svg', + }); + + cy.get('[data-test=time_compare]').within(() => { + cy.get('.select-value-label').contains('364 days'); + }); + }); +}); + +describe('Annotations', () => { + beforeEach(() => { + cy.login(); + cy.server(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); + }); + + it('Create formula annotation y-axis goal line', () => { + cy.visitChartByName('Num Births Trend'); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + + cy.get('[data-test=annotation_layers]').within(() => { + cy.get('button').click(); + }); + + cy.get('.popover-content').within(() => { + cy.get('[data-test=annotation-layer-name-header]').siblings().first().within(() => { + cy.get('input').type('Goal line'); + }); + cy.get('[data-test=annotation-layer-value-header]').siblings().first().within(() => { + cy.get('input').type('y=1400000'); + }); + cy.get('button').contains('OK').click(); + }); + + cy.get('button.query').click(); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + chartSelector: 'svg', + }); + + cy.get('.nv-legend-text').should('have.length', 2); + }); +}); diff --git a/superset/assets/cypress/integration/explore/link.test.js b/superset/assets/cypress/integration/explore/link.test.js new file mode 100644 index 0000000000..dc84e624d7 --- /dev/null +++ b/superset/assets/cypress/integration/explore/link.test.js @@ -0,0 +1,140 @@ +// *********************************************** +// Tests for links in the explore UI +// *********************************************** + +import { HEALTH_POP_FORM_DATA_DEFAULTS } from './visualizations/shared.helper'; + +describe('Test explore links', () => { + beforeEach(() => { + cy.login(); + cy.server(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); + }); + + it('Open and close view query modal', () => { + cy.visitChartByName('Growth Rate'); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + + cy.get('button#query').click(); + cy.get('span').contains('View query').parent().click(); + cy.wait('@getJson').then(() => { + cy.get('code'); + }); + cy.get('.modal-header').within(() => { + cy.get('button.close').first().click({ force: true }); + }); + }); + + it('Visit short link', () => { + cy.visitChartByName('Growth Rate'); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + + cy.get('[data-test=short-link-button]').click(); + cy.get('#shorturl-popover').within(() => { + cy.get('i[title="Copy to clipboard"]') + .siblings() + .first() + .invoke('text') + .then((text) => { + cy.visit(text); + }); + }); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + }); + + it('Test iframe link', () => { + cy.visitChartByName('Growth Rate'); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + + cy.get('[data-test=embed-code-button]').click(); + cy.get('#embed-code-popover').within(() => { + cy.get('textarea[name=embedCode]').contains('iframe'); + }); + }); + + it('Test chart save as', () => { + const formData = { + ...HEALTH_POP_FORM_DATA_DEFAULTS, + viz_type: 'table', + metrics: ['sum__SP_POP_TOTL'], + groupby: ['country_name'], + }; + const newChartName = 'Test chart'; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.url().then((url) => { + cy.get('button[data-target="#save_modal"]').click(); + cy.get('.modal-content').within(() => { + cy.get('input[name=new_slice_name]').type(newChartName); + cy.get('button#btn_modal_save').click(); + }); + cy.url().should('eq', url); + + cy.visitChartByName(newChartName); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + }); + }); + + it('Test chart save', () => { + const chartName = 'Test chart'; + cy.visitChartByName(chartName); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + + cy.get('[data-test=groupby]').within(() => { + cy.get('span.select-clear-zone').click(); + }); + cy.get('button[data-target="#save_modal"]').click(); + cy.get('.modal-content').within(() => { + cy.get('button#btn_modal_save').click(); + }); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.request(`/chart/api/read?_flt_3_slice_name=${chartName}`).then((response) => { + cy.request('DELETE', `/chart/api/delete/${response.body.pks[0]}`); + }); + }); + + it('Test chart save as and add to new dashboard', () => { + cy.visitChartByName('Growth Rate'); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + + const dashboardTitle = 'Test dashboard'; + cy.get('button[data-target="#save_modal"]').click(); + cy.get('.modal-content').within(() => { + cy.get('input[name=new_slice_name]').type('New Growth Rate'); + cy.get('input[data-test=add-to-new-dashboard]').check(); + cy.get('input[placeholder="[dashboard name]"]').type(dashboardTitle); + cy.get('button#btn_modal_save').click(); + }); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.request(`/dashboard/api/read?_flt_3_dashboard_title=${dashboardTitle}`).then((response) => { + expect(response.body.pks[0]).not.equals(null); + }); + }); + + it('Test chart save as and add to existing dashboard', () => { + cy.visitChartByName('Most Populated Countries'); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + const chartName = 'New Most Populated Countries'; + const dashboardTitle = 'Test dashboard'; + + cy.get('button[data-target="#save_modal"]').click(); + cy.get('.modal-content').within(() => { + cy.get('input[name=new_slice_name]').type(chartName); + cy.get('input[data-test=add-to-existing-dashboard]').check(); + cy.get('.select.save-modal-selector').click().within(() => { + cy.get('input').type(dashboardTitle, { force: true }); + cy.get('.select-option.is-focused') + .trigger('mousedown'); + }); + cy.get('button#btn_modal_save').click(); + }); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.request(`/chart/api/read?_flt_3_slice_name=${chartName}`).then((response) => { + cy.request('DELETE', `/chart/api/delete/${response.body.pks[0]}`); + }); + cy.request(`/dashboard/api/read?_flt_3_dashboard_title=${dashboardTitle}`).then((response) => { + cy.request('DELETE', `/dashboard/api/delete/${response.body.pks[0]}`); + }); + }); +}); diff --git a/superset/assets/cypress/integration/explore/visualizations/shared.helper.js b/superset/assets/cypress/integration/explore/visualizations/shared.helper.js index 796173a956..e98c7ff715 100644 --- a/superset/assets/cypress/integration/explore/visualizations/shared.helper.js +++ b/superset/assets/cypress/integration/explore/visualizations/shared.helper.js @@ -15,6 +15,13 @@ export const FORM_DATA_DEFAULTS = { contribution: false, }; +export const HEALTH_POP_FORM_DATA_DEFAULTS = { + datasource: '2__table', + granularity_sqla: 'ds', + time_grain_sqla: 'P1D', + time_range: '1960-01-01+:+2014-01-02', +}; + export const NUM_METRIC = { expressionType: 'SIMPLE', column: { diff --git a/superset/assets/cypress/support/commands.js b/superset/assets/cypress/support/commands.js index a6c6daeb7b..a8d432e1b6 100644 --- a/superset/assets/cypress/support/commands.js +++ b/superset/assets/cypress/support/commands.js @@ -29,7 +29,7 @@ const BASE_EXPLORE_URL = '/superset/explore/?form_data='; Cypress.Commands.add('login', () => { cy.request({ method: 'POST', - url: 'http://localhost:8081/login/', + url: '/login/', body: { username: 'admin', password: 'general' }, }).then((response) => { expect(response.status).to.eq(200); @@ -37,7 +37,7 @@ Cypress.Commands.add('login', () => { }); Cypress.Commands.add('visitChartByName', (name) => { - cy.request(`http://localhost:8081/chart/api/read?_flt_3_slice_name=${name}`).then((response) => { + cy.request(`/chart/api/read?_flt_3_slice_name=${name}`).then((response) => { cy.visit(`${BASE_EXPLORE_URL}{"slice_id": ${response.body.pks[0]}}`); }); }); diff --git a/superset/assets/src/components/URLShortLinkButton.jsx b/superset/assets/src/components/URLShortLinkButton.jsx index 19c77ab332..86c1b598a5 100644 --- a/superset/assets/src/components/URLShortLinkButton.jsx +++ b/superset/assets/src/components/URLShortLinkButton.jsx @@ -58,7 +58,7 @@ class URLShortLinkButton extends React.Component { onEnter={this.getCopyUrl} overlay={this.renderPopover()} > - +   diff --git a/superset/assets/src/explore/components/AdhocFilterEditPopoverSimpleTabContent.jsx b/superset/assets/src/explore/components/AdhocFilterEditPopoverSimpleTabContent.jsx index 6c670b6d7c..4921647a2f 100644 --- a/superset/assets/src/explore/components/AdhocFilterEditPopoverSimpleTabContent.jsx +++ b/superset/assets/src/explore/components/AdhocFilterEditPopoverSimpleTabContent.jsx @@ -257,7 +257,7 @@ export default class AdhocFilterEditPopoverSimpleTabContent extends React.Compon - + { ( MULTI_OPERATORS.indexOf(adhocFilter.operator) >= 0 || diff --git a/superset/assets/src/explore/components/ControlHeader.jsx b/superset/assets/src/explore/components/ControlHeader.jsx index 3dd884ce21..9dcdc61889 100644 --- a/superset/assets/src/explore/components/ControlHeader.jsx +++ b/superset/assets/src/explore/components/ControlHeader.jsx @@ -5,6 +5,7 @@ import InfoTooltipWithTrigger from '../../components/InfoTooltipWithTrigger'; import { t } from '../../locales'; const propTypes = { + name: PropTypes.string, label: PropTypes.string, description: PropTypes.string, validationErrors: PropTypes.array, @@ -22,6 +23,7 @@ const defaultProps = { validationErrors: [], renderTrigger: false, hovered: false, + name: undefined, }; export default class ControlHeader extends React.Component { @@ -63,6 +65,7 @@ export default class ControlHeader extends React.Component { return (
diff --git a/superset/assets/src/explore/components/EmbedCodeButton.jsx b/superset/assets/src/explore/components/EmbedCodeButton.jsx index a09a5335ad..e6cccacce5 100644 --- a/superset/assets/src/explore/components/EmbedCodeButton.jsx +++ b/superset/assets/src/explore/components/EmbedCodeButton.jsx @@ -113,7 +113,7 @@ export default class EmbedCodeButton extends React.Component { placement="left" overlay={this.renderPopover()} > - +   diff --git a/superset/assets/src/explore/components/SaveModal.jsx b/superset/assets/src/explore/components/SaveModal.jsx index 7748cac19e..522ce0404c 100644 --- a/superset/assets/src/explore/components/SaveModal.jsx +++ b/superset/assets/src/explore/components/SaveModal.jsx @@ -188,6 +188,7 @@ class SaveModal extends React.Component { disabled={canNotSaveToDash} checked={this.state.addToDash === 'existing'} onChange={this.changeDash.bind(this, 'existing')} + data-test="add-to-existing-dashboard" > {t('Add chart to existing dashboard')} @@ -206,6 +207,7 @@ class SaveModal extends React.Component { checked={this.state.addToDash === 'new'} onChange={this.changeDash.bind(this, 'new')} disabled={canNotSaveToDash} + data-test="add-to-new-dashboard" > {t('Add to new dashboard')}