diff --git a/superset/assets/.eslintrc b/superset/assets/.eslintrc index c4f14916768..34cbfe9611c 100644 --- a/superset/assets/.eslintrc +++ b/superset/assets/.eslintrc @@ -17,7 +17,7 @@ * under the License. */ { - "extends": "airbnb", + "extends": ["airbnb", "prettier"], "parser": "babel-eslint", "parserOptions": { "ecmaFeatures": { @@ -27,7 +27,7 @@ "env": { "browser": true }, - "plugins": ["react"], + "plugins": ["prettier", "react"], "rules": { "camelcase": [ "error", @@ -77,7 +77,8 @@ "react/no-unused-prop-types": 0, "react/require-default-props": 0, "react/jsx-fragments": 1, - "react/prop-types": 0 + "react/prop-types": 0, + "prettier/prettier": "error" }, "settings": { "import/resolver": "webpack", diff --git a/superset/assets/spec/javascripts/dashboard/.prettierrc b/superset/assets/.prettierrc similarity index 100% rename from superset/assets/spec/javascripts/dashboard/.prettierrc rename to superset/assets/.prettierrc diff --git a/superset/assets/cypress/integration/dashboard/controls.js b/superset/assets/cypress/integration/dashboard/controls.js index 08402bcbbe3..aa04410173a 100644 --- a/superset/assets/cypress/integration/dashboard/controls.js +++ b/superset/assets/cypress/integration/dashboard/controls.js @@ -19,85 +19,105 @@ import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper'; import readResponseBlob from '../../utils/readResponseBlob'; -export default () => describe('top-level controls', () => { - const sliceRequests = []; - const forceRefreshRequests = []; - let mapId; +export default () => + describe('top-level controls', () => { + const sliceRequests = []; + const forceRefreshRequests = []; + let mapId; - beforeEach(() => { - cy.server(); - cy.login(); - cy.visit(WORLD_HEALTH_DASHBOARD); + beforeEach(() => { + cy.server(); + 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 => (slice.form_data.viz_type === 'world_map')).slice_id; + cy.get('#app').then(data => { + const bootstrapData = JSON.parse(data[0].dataset.bootstrap); + const dashboard = bootstrapData.dashboard_data; + mapId = dashboard.slices.find( + slice => slice.form_data.viz_type === 'world_map', + ).slice_id; - dashboard.slices - .forEach((slice) => { + dashboard.slices.forEach(slice => { const sliceRequest = `getJson_${slice.slice_id}`; sliceRequests.push(`@${sliceRequest}`); const formData = `{"slice_id":${slice.slice_id}}`; - cy.route('POST', `/superset/explore_json/?form_data=${formData}`).as(sliceRequest); + cy.route('POST', `/superset/explore_json/?form_data=${formData}`).as( + sliceRequest, + ); const forceRefresh = `postJson_${slice.slice_id}_force`; forceRefreshRequests.push(`@${forceRefresh}`); - cy.route('POST', `/superset/explore_json/?form_data={"slice_id":${slice.slice_id}}&force=true`).as(forceRefresh); - }); - }); - }); - afterEach(() => { - sliceRequests.length = 0; - forceRefreshRequests.length = 0; - }); - - it.skip('should allow chart level refresh', () => { - cy.wait(sliceRequests); - cy.get('.grid-container .world_map').should('be.exist'); - cy.get(`#slice_${mapId}-controls`).click(); - cy.get(`#slice_${mapId}-controls`).next() - .find('.refresh-tooltip').trigger('click', { force: true }); - - // not allow dashboard level force refresh when any chart is loading - cy.get('#save-dash-split-button').trigger('click', { force: true }); - cy.contains('Force refresh dashboard').parent().should('have.class', 'disabled'); - // not allow chart level force refresh when it is loading - cy.get(`#slice_${mapId}-controls`).next() - .find('.refresh-tooltip') - .parent() - .parent() - .should('have.class', 'disabled'); - - cy.wait(`@postJson_${mapId}_force`); - cy.get('#save-dash-split-button').trigger('click'); - cy.contains('Force refresh dashboard').parent().not('have.class', 'disabled'); - }); - - it.skip('should allow dashboard level force refresh', () => { - // when charts are not start loading, for example, under a secondary tab, - // should allow force refresh - cy.get('#save-dash-split-button').trigger('click'); - cy.contains('Force refresh dashboard').parent().not('have.class', 'disabled'); - - // wait the all dash finish loading. - cy.wait(sliceRequests); - cy.get('#save-dash-split-button').trigger('click'); - cy.contains('Force refresh dashboard').trigger('click', { force: true }); - cy.get('#save-dash-split-button').trigger('click'); - cy.contains('Force refresh dashboard').parent().should('have.class', 'disabled'); - - // wait all charts force refreshed - cy.wait(forceRefreshRequests).then((xhrs) => { - // is_cached in response should be false - xhrs.forEach((xhr) => { - readResponseBlob(xhr.response.body).then((responseBody) => { - expect(responseBody.is_cached).to.equal(false); + cy.route( + 'POST', + `/superset/explore_json/?form_data={"slice_id":${slice.slice_id}}&force=true`, + ).as(forceRefresh); }); }); }); + afterEach(() => { + sliceRequests.length = 0; + forceRefreshRequests.length = 0; + }); - cy.get('#save-dash-split-button').trigger('click'); - cy.contains('Force refresh dashboard').parent().not('have.class', 'disabled'); + it.skip('should allow chart level refresh', () => { + cy.wait(sliceRequests); + cy.get('.grid-container .world_map').should('be.exist'); + cy.get(`#slice_${mapId}-controls`).click(); + cy.get(`#slice_${mapId}-controls`) + .next() + .find('.refresh-tooltip') + .trigger('click', { force: true }); + + // not allow dashboard level force refresh when any chart is loading + cy.get('#save-dash-split-button').trigger('click', { force: true }); + cy.contains('Force refresh dashboard') + .parent() + .should('have.class', 'disabled'); + // not allow chart level force refresh when it is loading + cy.get(`#slice_${mapId}-controls`) + .next() + .find('.refresh-tooltip') + .parent() + .parent() + .should('have.class', 'disabled'); + + cy.wait(`@postJson_${mapId}_force`); + cy.get('#save-dash-split-button').trigger('click'); + cy.contains('Force refresh dashboard') + .parent() + .not('have.class', 'disabled'); + }); + + it.skip('should allow dashboard level force refresh', () => { + // when charts are not start loading, for example, under a secondary tab, + // should allow force refresh + cy.get('#save-dash-split-button').trigger('click'); + cy.contains('Force refresh dashboard') + .parent() + .not('have.class', 'disabled'); + + // wait the all dash finish loading. + cy.wait(sliceRequests); + cy.get('#save-dash-split-button').trigger('click'); + cy.contains('Force refresh dashboard').trigger('click', { force: true }); + cy.get('#save-dash-split-button').trigger('click'); + cy.contains('Force refresh dashboard') + .parent() + .should('have.class', 'disabled'); + + // wait all charts force refreshed + cy.wait(forceRefreshRequests).then(xhrs => { + // is_cached in response should be false + xhrs.forEach(xhr => { + readResponseBlob(xhr.response.body).then(responseBody => { + expect(responseBody.is_cached).to.equal(false); + }); + }); + }); + + cy.get('#save-dash-split-button').trigger('click'); + cy.contains('Force refresh dashboard') + .parent() + .not('have.class', 'disabled'); + }); }); -}); diff --git a/superset/assets/cypress/integration/dashboard/dashboard.helper.js b/superset/assets/cypress/integration/dashboard/dashboard.helper.js index 1c16a829012..250eb5e15c0 100644 --- a/superset/assets/cypress/integration/dashboard/dashboard.helper.js +++ b/superset/assets/cypress/integration/dashboard/dashboard.helper.js @@ -19,5 +19,5 @@ export const WORLD_HEALTH_DASHBOARD = '/superset/dashboard/world_health/'; export const TABBED_DASHBOARD = '/superset/dashboard/tabbed_dash/'; -export const CHECK_DASHBOARD_FAVORITE_ENDPOINT = '/superset/favstar/Dashboard/*/count'; - +export const CHECK_DASHBOARD_FAVORITE_ENDPOINT = + '/superset/favstar/Dashboard/*/count'; diff --git a/superset/assets/cypress/integration/dashboard/edit_mode.js b/superset/assets/cypress/integration/dashboard/edit_mode.js index 79198e60af3..c279cc5f093 100644 --- a/superset/assets/cypress/integration/dashboard/edit_mode.js +++ b/superset/assets/cypress/integration/dashboard/edit_mode.js @@ -18,64 +18,83 @@ */ import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper'; -export default () => describe('edit mode', () => { - beforeEach(() => { - cy.server(); - cy.login(); +export default () => + describe('edit mode', () => { + beforeEach(() => { + cy.server(); + 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 boxplotChartId = dashboard.slices.find(slice => (slice.form_data.viz_type === 'box_plot')).slice_id; - const formData = `{"slice_id":${boxplotChartId}}`; - const boxplotRequest = `/superset/explore_json/?form_data=${formData}`; - cy.route('POST', boxplotRequest).as('boxplotRequest'); + cy.visit(WORLD_HEALTH_DASHBOARD); + cy.get('#app').then(data => { + const bootstrapData = JSON.parse(data[0].dataset.bootstrap); + const dashboard = bootstrapData.dashboard_data; + const boxplotChartId = dashboard.slices.find( + slice => slice.form_data.viz_type === 'box_plot', + ).slice_id; + const formData = `{"slice_id":${boxplotChartId}}`; + const boxplotRequest = `/superset/explore_json/?form_data=${formData}`; + cy.route('POST', boxplotRequest).as('boxplotRequest'); + }); + + cy.get('.dashboard-header') + .contains('Edit dashboard') + .click(); }); - cy.get('.dashboard-header').contains('Edit dashboard').click(); - }); + it('remove, and add chart flow', () => { + // wait box_plot data and find box plot + cy.wait('@boxplotRequest'); + cy.get('.grid-container .box_plot').should('be.exist'); - it('remove, and add chart flow', () => { - // wait box_plot data and find box plot - cy.wait('@boxplotRequest'); - cy.get('.grid-container .box_plot').should('be.exist'); + cy.get('.fa.fa-trash') + .last() + .then($el => { + cy.wrap($el) + .invoke('show') + .click(); - cy.get('.fa.fa-trash').last().then(($el) => { - cy.wrap($el).invoke('show').click(); + // box plot should be gone + cy.get('.grid-container .box_plot').should('not.exist'); + }); - // box plot should be gone - cy.get('.grid-container .box_plot').should('not.exist'); + // open charts list + cy.get('.component-layer') + .contains('Your charts & filters') + .click(); + + // find box plot is available from list + cy.get('.slices-layer') + .find('.chart-card-container') + .contains('Box plot'); + + // drag-n-drop + const dataTransfer = { data: {} }; + cy.get('.dragdroppable') + .contains('Box plot') + .trigger('mousedown', { which: 1 }) + .trigger('dragstart', { dataTransfer }) + .trigger('drag', {}); + cy.get('.grid-content .dragdroppable') + .last() + .trigger('dragover', { dataTransfer }) + .trigger('drop', { dataTransfer }) + .trigger('dragend', { dataTransfer }) + .trigger('mouseup', { which: 1 }); + + // add back to dashboard + cy.get('.grid-container .box_plot').should('be.exist'); + + // should show Save changes button + cy.get('.dashboard-header .button-container').contains('Save changes'); + + // undo 2 steps + cy.get('.dashboard-header .undo-action') + .click() + .click(); + + // no changes, can switch to view mode + cy.get('.dashboard-header .button-container') + .contains('Switch to view mode') + .click(); }); - - // open charts list - cy.get('.component-layer').contains('Your charts & filters').click(); - - // find box plot is available from list - cy.get('.slices-layer').find('.chart-card-container').contains('Box plot'); - - // drag-n-drop - const dataTransfer = { data: {} }; - cy.get('.dragdroppable').contains('Box plot') - .trigger('mousedown', { which: 1 }) - .trigger('dragstart', { dataTransfer }) - .trigger('drag', {}); - cy.get('.grid-content .dragdroppable').last() - .trigger('dragover', { dataTransfer }) - .trigger('drop', { dataTransfer }) - .trigger('dragend', { dataTransfer }) - .trigger('mouseup', { which: 1 }); - - // add back to dashboard - cy.get('.grid-container .box_plot').should('be.exist'); - - // should show Save changes button - cy.get('.dashboard-header .button-container').contains('Save changes'); - - // undo 2 steps - cy.get('.dashboard-header .undo-action').click().click(); - - // no changes, can switch to view mode - cy.get('.dashboard-header .button-container').contains('Switch to view mode').click(); }); -}); diff --git a/superset/assets/cypress/integration/dashboard/fav_star.js b/superset/assets/cypress/integration/dashboard/fav_star.js index 27d88fb5d59..2a007f49b14 100644 --- a/superset/assets/cypress/integration/dashboard/fav_star.js +++ b/superset/assets/cypress/integration/dashboard/fav_star.js @@ -16,38 +16,50 @@ * specific language governing permissions and limitations * under the License. */ -import { WORLD_HEALTH_DASHBOARD, CHECK_DASHBOARD_FAVORITE_ENDPOINT } from './dashboard.helper'; +import { + WORLD_HEALTH_DASHBOARD, + CHECK_DASHBOARD_FAVORITE_ENDPOINT, +} from './dashboard.helper'; -export default () => describe('favorite dashboard', () => { - let isFavoriteDashboard = false; +export default () => + describe('favorite dashboard', () => { + let isFavoriteDashboard = false; - beforeEach(() => { - cy.server(); - cy.login(); + beforeEach(() => { + cy.server(); + cy.login(); - cy.route(CHECK_DASHBOARD_FAVORITE_ENDPOINT).as('countFavStar'); - cy.visit(WORLD_HEALTH_DASHBOARD); + cy.route(CHECK_DASHBOARD_FAVORITE_ENDPOINT).as('countFavStar'); + cy.visit(WORLD_HEALTH_DASHBOARD); - cy.wait('@countFavStar').then((xhr) => { - isFavoriteDashboard = xhr.response.body.count === 1; + cy.wait('@countFavStar').then(xhr => { + isFavoriteDashboard = xhr.response.body.count === 1; + }); + }); + + it('should allow favor/unfavor', () => { + if (!isFavoriteDashboard) { + cy.get('.favstar') + .find('i') + .should('have.class', 'fa-star-o'); + cy.get('.favstar').trigger('click'); + cy.get('.favstar') + .find('i') + .should('have.class', 'fa-star') + .and('not.have.class', 'fa-star-o'); + } else { + cy.get('.favstar') + .find('i') + .should('have.class', 'fa-star') + .and('not.have.class', 'fa-star-o'); + cy.get('.favstar').trigger('click'); + cy.get('.fave-unfave-icon') + .find('i') + .should('have.class', 'fa-star-o') + .and('not.have.class', 'fa-star'); + } + + // reset to original fav state + cy.get('.favstar').trigger('click'); }); }); - - it('should allow favor/unfavor', () => { - if (!isFavoriteDashboard) { - cy.get('.favstar').find('i').should('have.class', 'fa-star-o'); - cy.get('.favstar').trigger('click'); - cy.get('.favstar').find('i').should('have.class', 'fa-star') - .and('not.have.class', 'fa-star-o'); - } else { - cy.get('.favstar').find('i').should('have.class', 'fa-star') - .and('not.have.class', 'fa-star-o'); - cy.get('.favstar').trigger('click'); - cy.get('.fave-unfave-icon').find('i').should('have.class', 'fa-star-o') - .and('not.have.class', 'fa-star'); - } - - // reset to original fav state - cy.get('.favstar').trigger('click'); - }); -}); diff --git a/superset/assets/cypress/integration/dashboard/filter.js b/superset/assets/cypress/integration/dashboard/filter.js index 97b40a4150c..6da7de22d3e 100644 --- a/superset/assets/cypress/integration/dashboard/filter.js +++ b/superset/assets/cypress/integration/dashboard/filter.js @@ -18,53 +18,63 @@ */ import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper'; -export default () => describe('dashboard filter', () => { - let sliceIds = []; - let filterId; +export default () => + describe('dashboard filter', () => { + let sliceIds = []; + let filterId; - beforeEach(() => { - cy.server(); - cy.login(); + beforeEach(() => { + cy.server(); + cy.login(); - cy.visit(WORLD_HEALTH_DASHBOARD); + cy.visit(WORLD_HEALTH_DASHBOARD); - cy.get('#app').then((data) => { - const bootstrapData = JSON.parse(data[0].dataset.bootstrap); - const dashboard = bootstrapData.dashboard_data; - sliceIds = dashboard.slices.map(slice => (slice.slice_id)); - filterId = dashboard.slices.find(slice => (slice.form_data.viz_type === 'filter_box')).slice_id; - }); - }); - - it('should apply filter', () => { - const aliases = []; - - const formData = `{"slice_id":${filterId}}`; - const filterRoute = `/superset/explore_json/?form_data=${formData}`; - cy.route('POST', filterRoute).as('fetchFilter'); - cy.wait('@fetchFilter'); - sliceIds - .filter(id => (parseInt(id, 10) !== filterId)) - .forEach((id) => { - const alias = `getJson_${id}`; - aliases.push(`@${alias}`); - - cy.route('POST', `/superset/explore_json/?form_data={"slice_id":${id}}`).as(alias); + cy.get('#app').then(data => { + const bootstrapData = JSON.parse(data[0].dataset.bootstrap); + const dashboard = bootstrapData.dashboard_data; + sliceIds = dashboard.slices.map(slice => slice.slice_id); + filterId = dashboard.slices.find( + slice => slice.form_data.viz_type === 'filter_box', + ).slice_id; }); + }); - // select filter_box and apply - cy.get('.Select-control') - .first().find('input') - .first() - .type('South Asia{enter}', { force: true }); + it('should apply filter', () => { + const aliases = []; - cy.wait(aliases).then((requests) => { - requests.forEach((xhr) => { - const requestFormData = xhr.request.body; - const requestParams = JSON.parse(requestFormData.get('form_data')); - expect(requestParams.extra_filters[0]) - .deep.eq({ col: 'region', op: 'in', val: ['South Asia'] }); + const formData = `{"slice_id":${filterId}}`; + const filterRoute = `/superset/explore_json/?form_data=${formData}`; + cy.route('POST', filterRoute).as('fetchFilter'); + cy.wait('@fetchFilter'); + sliceIds + .filter(id => parseInt(id, 10) !== filterId) + .forEach(id => { + const alias = `getJson_${id}`; + aliases.push(`@${alias}`); + + cy.route( + 'POST', + `/superset/explore_json/?form_data={"slice_id":${id}}`, + ).as(alias); + }); + + // select filter_box and apply + cy.get('.Select-control') + .first() + .find('input') + .first() + .type('South Asia{enter}', { force: true }); + + cy.wait(aliases).then(requests => { + requests.forEach(xhr => { + const requestFormData = xhr.request.body; + const requestParams = JSON.parse(requestFormData.get('form_data')); + expect(requestParams.extra_filters[0]).deep.eq({ + col: 'region', + op: 'in', + val: ['South Asia'], + }); + }); }); }); }); -}); diff --git a/superset/assets/cypress/integration/dashboard/load.js b/superset/assets/cypress/integration/dashboard/load.js index c6427c7320a..6050cbf55c2 100644 --- a/superset/assets/cypress/integration/dashboard/load.js +++ b/superset/assets/cypress/integration/dashboard/load.js @@ -19,37 +19,40 @@ import readResponseBlob from '../../utils/readResponseBlob'; import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper'; -export default () => describe('load', () => { - const aliases = []; +export default () => + describe('load', () => { + const aliases = []; - beforeEach(() => { - cy.server(); - cy.login(); + beforeEach(() => { + cy.server(); + cy.login(); - cy.visit(WORLD_HEALTH_DASHBOARD); + cy.visit(WORLD_HEALTH_DASHBOARD); - cy.get('#app').then((data) => { - const bootstrapData = JSON.parse(data[0].dataset.bootstrap); - const slices = bootstrapData.dashboard_data.slices; - // then define routes and create alias for each requests - slices.forEach((slice) => { - const alias = `getJson_${slice.slice_id}`; - const formData = `{"slice_id":${slice.slice_id}}`; - cy.route('POST', `/superset/explore_json/?form_data=${formData}`).as(alias); - aliases.push(`@${alias}`); + cy.get('#app').then(data => { + const bootstrapData = JSON.parse(data[0].dataset.bootstrap); + const slices = bootstrapData.dashboard_data.slices; + // then define routes and create alias for each requests + slices.forEach(slice => { + const alias = `getJson_${slice.slice_id}`; + const formData = `{"slice_id":${slice.slice_id}}`; + cy.route('POST', `/superset/explore_json/?form_data=${formData}`).as( + alias, + ); + aliases.push(`@${alias}`); + }); + }); + }); + + it('should load dashboard', () => { + // wait and verify one-by-one + cy.wait(aliases).then(requests => { + requests.forEach(async xhr => { + expect(xhr.status).to.eq(200); + const responseBody = await readResponseBlob(xhr.response.body); + expect(responseBody).to.have.property('error', null); + cy.get(`#slice-container-${xhr.response.body.form_data.slice_id}`); + }); }); }); }); - - it('should load dashboard', () => { - // wait and verify one-by-one - cy.wait(aliases).then((requests) => { - requests.forEach(async (xhr) => { - expect(xhr.status).to.eq(200); - const responseBody = await readResponseBlob(xhr.response.body); - expect(responseBody).to.have.property('error', null); - cy.get(`#slice-container-${xhr.response.body.form_data.slice_id}`); - }); - }); - }); -}); diff --git a/superset/assets/cypress/integration/dashboard/save.js b/superset/assets/cypress/integration/dashboard/save.js index 772862d5b87..263fd8addfd 100644 --- a/superset/assets/cypress/integration/dashboard/save.js +++ b/superset/assets/cypress/integration/dashboard/save.js @@ -19,60 +19,73 @@ import readResponseBlob from '../../utils/readResponseBlob'; import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper'; -export default () => describe('save', () => { - let dashboardId; - let boxplotChartId; +export default () => + describe('save', () => { + let dashboardId; + let boxplotChartId; - beforeEach(() => { - cy.server(); - cy.login(); - cy.visit(WORLD_HEALTH_DASHBOARD); + beforeEach(() => { + cy.server(); + 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; - dashboardId = dashboard.id; - boxplotChartId = dashboard.slices.find(slice => (slice.form_data.viz_type === 'box_plot')).slice_id; + cy.get('#app').then(data => { + const bootstrapData = JSON.parse(data[0].dataset.bootstrap); + const dashboard = bootstrapData.dashboard_data; + dashboardId = dashboard.id; + boxplotChartId = dashboard.slices.find( + slice => slice.form_data.viz_type === 'box_plot', + ).slice_id; - cy.route('POST', `/superset/copy_dash/${dashboardId}/`).as('copyRequest'); + cy.route('POST', `/superset/copy_dash/${dashboardId}/`).as( + 'copyRequest', + ); + }); + + cy.get('#save-dash-split-button').trigger('click', { force: true }); + cy.contains('Save as').trigger('click', { force: true }); + cy.get('.modal-footer') + .contains('Save') + .trigger('click', { force: true }); }); - cy.get('#save-dash-split-button').trigger('click', { force: true }); - cy.contains('Save as').trigger('click', { force: true }); - cy.get('.modal-footer').contains('Save').trigger('click', { force: true }); - }); + it('should save as new dashboard', () => { + cy.wait('@copyRequest').then(xhr => { + expect(xhr.status).to.eq(200); - it('should save as new dashboard', () => { - cy.wait('@copyRequest').then((xhr) => { - expect(xhr.status).to.eq(200); - - readResponseBlob(xhr.response.body).then((json) => { - expect(json.id).to.be.gt(dashboardId); + readResponseBlob(xhr.response.body).then(json => { + expect(json.id).to.be.gt(dashboardId); + }); }); }); + + it('should save/overwrite dashboard', () => { + cy.wait('@copyRequest'); + + // should have box_plot chart + const formData = `{"slice_id":${boxplotChartId}}`; + const boxplotRequest = `/superset/explore_json/?form_data=${formData}`; + cy.route('POST', boxplotRequest).as('boxplotRequest'); + cy.wait('@boxplotRequest'); + cy.get('.grid-container .box_plot').should('be.exist'); + + // remove box_plot chart from dashboard + cy.get('.dashboard-header') + .contains('Edit dashboard') + .trigger('click', { force: true }); + cy.get('.fa.fa-trash') + .last() + .trigger('click', { force: true }); + cy.get('.grid-container .box_plot').should('not.exist'); + + cy.route('POST', '/superset/save_dash/**/').as('saveRequest'); + cy.get('.dashboard-header') + .contains('Save changes') + .trigger('click', { force: true }); + + // go back to view mode + cy.wait('@saveRequest'); + cy.get('.dashboard-header').contains('Edit dashboard'); + cy.get('.grid-container .box_plot').should('not.exist'); + }); }); - - it('should save/overwrite dashboard', () => { - cy.wait('@copyRequest'); - - // should have box_plot chart - const formData = `{"slice_id":${boxplotChartId}}`; - const boxplotRequest = `/superset/explore_json/?form_data=${formData}`; - cy.route('POST', boxplotRequest).as('boxplotRequest'); - cy.wait('@boxplotRequest'); - cy.get('.grid-container .box_plot').should('be.exist'); - - // remove box_plot chart from dashboard - cy.get('.dashboard-header').contains('Edit dashboard').trigger('click', { force: true }); - cy.get('.fa.fa-trash').last().trigger('click', { force: true }); - cy.get('.grid-container .box_plot').should('not.exist'); - - cy.route('POST', '/superset/save_dash/**/').as('saveRequest'); - cy.get('.dashboard-header').contains('Save changes').trigger('click', { force: true }); - - // go back to view mode - cy.wait('@saveRequest'); - cy.get('.dashboard-header').contains('Edit dashboard'); - cy.get('.grid-container .box_plot').should('not.exist'); - }); -}); diff --git a/superset/assets/cypress/integration/dashboard/tabs.js b/superset/assets/cypress/integration/dashboard/tabs.js index 273d2ecc584..4a86d3c703f 100644 --- a/superset/assets/cypress/integration/dashboard/tabs.js +++ b/superset/assets/cypress/integration/dashboard/tabs.js @@ -18,161 +18,183 @@ */ import { TABBED_DASHBOARD } from './dashboard.helper'; -export default () => describe('tabs', () => { - let filterId; - let treemapId; - let linechartId; - let boxplotId; +export default () => + describe('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 - const handleException = () => { - // return false to prevent the error from - // failing this test - cy.on('uncaught:exception', () => false); - }; + // cypress can not handle window.scrollTo + // https://github.com/cypress-io/cypress/issues/2761 + // add this exception handler to pass test + const handleException = () => { + // return false to prevent the error from + // failing this test + cy.on('uncaught:exception', () => false); + }; - beforeEach(() => { - cy.server(); - cy.login(); + beforeEach(() => { + cy.server(); + cy.login(); - cy.visit(TABBED_DASHBOARD); + cy.visit(TABBED_DASHBOARD); - cy.get('#app').then((data) => { - const bootstrapData = JSON.parse(data[0].dataset.bootstrap); - const dashboard = bootstrapData.dashboard_data; - 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; + cy.get('#app').then(data => { + const bootstrapData = JSON.parse(data[0].dataset.bootstrap); + const dashboard = bootstrapData.dashboard_data; + 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; - const filterFormdata = { - slice_id: filterId, - }; - const filterRequest = `/superset/explore_json/?form_data=${JSON.stringify(filterFormdata)}`; - cy.route('POST', filterRequest).as('filterRequest'); + const filterFormdata = { + slice_id: filterId, + }; + const filterRequest = `/superset/explore_json/?form_data=${JSON.stringify( + filterFormdata, + )}`; + cy.route('POST', filterRequest).as('filterRequest'); - const treemapFormdata = { - slice_id: treemapId, - }; - const treemapRequest = `/superset/explore_json/?form_data=${JSON.stringify(treemapFormdata)}`; - cy.route('POST', treemapRequest).as('treemapRequest'); + const treemapFormdata = { + slice_id: treemapId, + }; + const treemapRequest = `/superset/explore_json/?form_data=${JSON.stringify( + treemapFormdata, + )}`; + cy.route('POST', treemapRequest).as('treemapRequest'); - const linechartFormdata = { - slice_id: linechartId, - }; - const linechartRequest = `/superset/explore_json/?form_data=${JSON.stringify(linechartFormdata)}`; - cy.route('POST', linechartRequest).as('linechartRequest'); + const linechartFormdata = { + slice_id: linechartId, + }; + const linechartRequest = `/superset/explore_json/?form_data=${JSON.stringify( + linechartFormdata, + )}`; + cy.route('POST', linechartRequest).as('linechartRequest'); - const boxplotFormdata = { - slice_id: boxplotId, - }; - const boxplotRequest = `/superset/explore_json/?form_data=${JSON.stringify(boxplotFormdata)}`; - cy.route('POST', boxplotRequest).as('boxplotRequest'); + const boxplotFormdata = { + slice_id: boxplotId, + }; + const boxplotRequest = `/superset/explore_json/?form_data=${JSON.stringify( + boxplotFormdata, + )}`; + cy.route('POST', boxplotRequest).as('boxplotRequest'); + }); }); - }); - it('should load charts when tab is visible', () => { - // landing in first tab, should see 2 charts - cy.wait('@filterRequest'); - cy.get('.grid-container .filter_box').should('be.exist'); - cy.wait('@treemapRequest'); - cy.get('.grid-container .treemap').should('be.exist'); - cy.get('.grid-container .box_plot').should('not.be.exist'); - cy.get('.grid-container .line').should('not.be.exist'); + it('should load charts when tab is visible', () => { + // landing in first tab, should see 2 charts + cy.wait('@filterRequest'); + cy.get('.grid-container .filter_box').should('be.exist'); + cy.wait('@treemapRequest'); + cy.get('.grid-container .treemap').should('be.exist'); + cy.get('.grid-container .box_plot').should('not.be.exist'); + cy.get('.grid-container .line').should('not.be.exist'); - // click row level tab, see 1 more chart - cy.get('.tab-content ul.nav.nav-tabs li') - .last() - .find('.editable-title input') - .click(); - cy.wait('@linechartRequest'); - cy.get('.grid-container .line').should('be.exist'); + // click row level tab, see 1 more chart + cy.get('.tab-content ul.nav.nav-tabs li') + .last() + .find('.editable-title input') + .click(); + cy.wait('@linechartRequest'); + cy.get('.grid-container .line').should('be.exist'); - // click top level tab, see 1 more chart - handleException(); - cy.get('.dashboard-component-tabs') - .first() - .find('ul.nav.nav-tabs li') - .last() - .find('.editable-title input') - .click(); - cy.wait('@boxplotRequest'); - cy.get('.grid-container .box_plot').should('be.exist'); - }); + // click top level tab, see 1 more chart + handleException(); + cy.get('.dashboard-component-tabs') + .first() + .find('ul.nav.nav-tabs li') + .last() + .find('.editable-title input') + .click(); + cy.wait('@boxplotRequest'); + cy.get('.grid-container .box_plot').should('be.exist'); + }); - it('should send new queries when tab becomes visible', () => { - // landing in first tab - cy.wait('@filterRequest'); - cy.wait('@treemapRequest'); + it('should send new queries when tab becomes visible', () => { + // landing in first tab + cy.wait('@filterRequest'); + cy.wait('@treemapRequest'); - // apply filter - cy.get('.Select-control') - .first() - .find('input') - .first() - .type('South Asia{enter}', { force: true }); + // apply filter + cy.get('.Select-control') + .first() + .find('input') + .first() + .type('South Asia{enter}', { force: true }); - // send new query from same tab - cy.wait('@treemapRequest') - .then((xhr) => { + // send new query from same tab + cy.wait('@treemapRequest').then(xhr => { const requestFormData = xhr.request.body; const requestParams = JSON.parse(requestFormData.get('form_data')); - expect(requestParams.extra_filters[0]) - .deep.eq({ col: 'region', op: 'in', val: ['South Asia'] }); + expect(requestParams.extra_filters[0]).deep.eq({ + col: 'region', + op: 'in', + val: ['South Asia'], + }); }); - // click row level tab, send 1 more query - cy.get('.tab-content ul.nav.nav-tabs li') - .last() - .click(); - cy.wait('@linechartRequest') - .then((xhr) => { + // click row level tab, send 1 more query + cy.get('.tab-content ul.nav.nav-tabs li') + .last() + .click(); + cy.wait('@linechartRequest').then(xhr => { const requestFormData = xhr.request.body; const requestParams = JSON.parse(requestFormData.get('form_data')); - expect(requestParams.extra_filters[0]) - .deep.eq({ col: 'region', op: 'in', val: ['South Asia'] }); + expect(requestParams.extra_filters[0]).deep.eq({ + col: 'region', + op: 'in', + val: ['South Asia'], + }); }); - // click top level tab, send 1 more query - handleException(); - cy.get('.dashboard-component-tabs') - .first() - .find('ul.nav.nav-tabs li') - .last() - .find('.editable-title input') - .click(); - cy.wait('@boxplotRequest') - .then((xhr) => { + // click top level tab, send 1 more query + handleException(); + cy.get('.dashboard-component-tabs') + .first() + .find('ul.nav.nav-tabs li') + .last() + .find('.editable-title input') + .click(); + cy.wait('@boxplotRequest').then(xhr => { const requestFormData = xhr.request.body; const requestParams = JSON.parse(requestFormData.get('form_data')); - expect(requestParams.extra_filters[0]) - .deep.eq({ col: 'region', op: 'in', val: ['South Asia'] }); + expect(requestParams.extra_filters[0]).deep.eq({ + col: 'region', + op: 'in', + val: ['South Asia'], + }); }); - // navigate to filter and clear filter - cy.get('.dashboard-component-tabs') - .first() - .find('ul.nav.nav-tabs li') - .first() - .click(); - cy.get('.tab-content ul.nav.nav-tabs li') - .first() - .click(); - cy.get('span.Select-clear') - .click(); + // navigate to filter and clear filter + cy.get('.dashboard-component-tabs') + .first() + .find('ul.nav.nav-tabs li') + .first() + .click(); + cy.get('.tab-content ul.nav.nav-tabs li') + .first() + .click(); + cy.get('span.Select-clear').click(); - // trigger 1 new query - cy.wait('@treemapRequest'); + // trigger 1 new query + cy.wait('@treemapRequest'); - // no other requests occurred - cy.on('fail', (err) => { - expect(err.message).to.include('Timed out retrying'); - return false; - }); - cy.wait('@boxplotRequest', { timeout: 1000 }).then(() => { - throw new Error('Unexpected API call.'); + // no other requests occurred + cy.on('fail', err => { + expect(err.message).to.include('Timed out retrying'); + return false; + }); + cy.wait('@boxplotRequest', { timeout: 1000 }).then(() => { + throw new Error('Unexpected API call.'); + }); }); }); -}); diff --git a/superset/assets/cypress/integration/dashboard/url_params.js b/superset/assets/cypress/integration/dashboard/url_params.js index b82aea10ed8..d7f983e7c11 100644 --- a/superset/assets/cypress/integration/dashboard/url_params.js +++ b/superset/assets/cypress/integration/dashboard/url_params.js @@ -18,39 +18,41 @@ */ import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper'; -export default () => describe('dashboard url params', () => { - const urlParams = { param1: '123', param2: 'abc' }; - let sliceIds = []; +export default () => + describe('dashboard url params', () => { + const urlParams = { param1: '123', param2: 'abc' }; + let sliceIds = []; - beforeEach(() => { - cy.server(); - cy.login(); + beforeEach(() => { + cy.server(); + cy.login(); - cy.visit(WORLD_HEALTH_DASHBOARD, { qs: urlParams }); + cy.visit(WORLD_HEALTH_DASHBOARD, { qs: urlParams }); - cy.get('#app').then((data) => { - const bootstrapData = JSON.parse(data[0].dataset.bootstrap); - const dashboard = bootstrapData.dashboard_data; - sliceIds = dashboard.slices.map(slice => (slice.slice_id)); + cy.get('#app').then(data => { + const bootstrapData = JSON.parse(data[0].dataset.bootstrap); + const dashboard = bootstrapData.dashboard_data; + sliceIds = dashboard.slices.map(slice => slice.slice_id); + }); }); - }); - it('should apply url params to slice requests', () => { - const aliases = []; - sliceIds - .forEach((id) => { + it('should apply url params to slice requests', () => { + const aliases = []; + sliceIds.forEach(id => { const alias = `getJson_${id}`; aliases.push(`@${alias}`); - cy.route('POST', `/superset/explore_json/?form_data={"slice_id":${id}}`).as(alias); + cy.route( + 'POST', + `/superset/explore_json/?form_data={"slice_id":${id}}`, + ).as(alias); }); - cy.wait(aliases).then((requests) => { - requests.forEach((xhr) => { - const requestFormData = xhr.request.body; - const requestParams = JSON.parse(requestFormData.get('form_data')); - expect(requestParams.url_params) - .deep.eq(urlParams); + cy.wait(aliases).then(requests => { + requests.forEach(xhr => { + const requestFormData = xhr.request.body; + const requestParams = JSON.parse(requestFormData.get('form_data')); + expect(requestParams.url_params).deep.eq(urlParams); + }); }); }); }); -}); diff --git a/superset/assets/cypress/integration/explore/chart.test.js b/superset/assets/cypress/integration/explore/chart.test.js index 9032de9d666..75874617efb 100644 --- a/superset/assets/cypress/integration/explore/chart.test.js +++ b/superset/assets/cypress/integration/explore/chart.test.js @@ -31,19 +31,21 @@ describe('Error', () => { ...FORM_DATA_DEFAULTS, metrics: [NUM_METRIC], viz_type: 'line', - adhoc_filters: [{ - expressionType: 'SIMPLE', - subject: 'state', - operator: 'in', - comparator: ['Fake State'], - clause: 'WHERE', - sqlExpression: null, - fromFormData: true, - }], + adhoc_filters: [ + { + expressionType: 'SIMPLE', + subject: 'state', + operator: 'in', + comparator: ['Fake State'], + clause: 'WHERE', + sqlExpression: null, + fromFormData: true, + }, + ], }; cy.visitChartByParams(JSON.stringify(formData)); - cy.wait('@getJson').then(async (xhr) => { + cy.wait('@getJson').then(async xhr => { expect(xhr.status).to.eq(400); const responseBody = await readResponseBlob(xhr.response.body); diff --git a/superset/assets/cypress/integration/explore/control.test.js b/superset/assets/cypress/integration/explore/control.test.js index d20cb46e21f..7d0da009ec6 100644 --- a/superset/assets/cypress/integration/explore/control.test.js +++ b/superset/assets/cypress/integration/explore/control.test.js @@ -100,9 +100,10 @@ describe('AdhocMetrics', () => { 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('/COUNT(DISTINCT name)', { force: true }); - cy.get('button').contains('Save').click(); + cy.get('.ace_text-input').type('/COUNT(DISTINCT name)', { force: true }); + cy.get('button') + .contains('Save') + .click(); }); cy.get('button.query').click(); @@ -130,11 +131,14 @@ describe('AdhocMetrics', () => { 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('.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') + .contains('Save') + .click(); }); cy.get('button.query').click(); @@ -200,8 +204,7 @@ describe('AdhocFilters', () => { 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('.ace_text-input').type("'Amy' OR name = 'Bob'", { force: true }); cy.get('button') .contains('Save') .click(); @@ -215,7 +218,6 @@ describe('AdhocFilters', () => { }); }); - describe('Advanced analytics', () => { beforeEach(() => { cy.login(); @@ -274,13 +276,21 @@ describe('Annotations', () => { }); 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('[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(); diff --git a/superset/assets/cypress/integration/explore/link.test.js b/superset/assets/cypress/integration/explore/link.test.js index 9f5f82d59fa..22067498632 100644 --- a/superset/assets/cypress/integration/explore/link.test.js +++ b/superset/assets/cypress/integration/explore/link.test.js @@ -35,12 +35,17 @@ describe('Test explore links', () => { cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.get('button#query').click(); - cy.get('span').contains('View query').parent().click(); + cy.get('span') + .contains('View query') + .parent() + .click(); cy.wait('@postJson').then(() => { cy.get('code'); }); cy.get('.modal-header').within(() => { - cy.get('button.close').first().click({ force: true }); + cy.get('button.close') + .first() + .click({ force: true }); }); }); @@ -57,8 +62,9 @@ describe('Test explore links', () => { cy.wait(100); - cy.get('#shorturl-popover [data-test="short-url"]').invoke('text') - .then((text) => { + cy.get('#shorturl-popover [data-test="short-url"]') + .invoke('text') + .then(text => { cy.visit(text); }); cy.verifySliceSuccess({ waitAlias: '@postJson' }); @@ -85,7 +91,7 @@ describe('Test explore links', () => { cy.visitChartByParams(JSON.stringify(formData)); cy.verifySliceSuccess({ waitAlias: '@postJson' }); - cy.url().then((url) => { + 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); @@ -111,9 +117,11 @@ describe('Test explore links', () => { cy.get('button#btn_modal_save').click(); }); cy.verifySliceSuccess({ waitAlias: '@postJson' }); - cy.request(`/chart/api/read?_flt_3_slice_name=${chartName}`).then((response) => { - cy.request('DELETE', `/chart/api/delete/${response.body.pks[0]}`); - }); + 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', () => { @@ -129,7 +137,9 @@ describe('Test explore links', () => { cy.get('button#btn_modal_save').click(); }); cy.verifySliceSuccess({ waitAlias: '@postJson' }); - cy.request(`/dashboard/api/read?_flt_3_dashboard_title=${dashboardTitle}`).then((response) => { + cy.request( + `/dashboard/api/read?_flt_3_dashboard_title=${dashboardTitle}`, + ).then(response => { expect(response.body.pks[0]).not.equals(null); }); }); @@ -144,18 +154,23 @@ describe('Test explore links', () => { 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('.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: '@postJson' }); - 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(`/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/area.js b/superset/assets/cypress/integration/explore/visualizations/area.js index 0e1906c5259..a3c5a33ce51 100644 --- a/superset/assets/cypress/integration/explore/visualizations/area.js +++ b/superset/assets/cypress/integration/explore/visualizations/area.js @@ -18,92 +18,99 @@ */ import readResponseBlob from '../../../utils/readResponseBlob'; -export default () => describe('Area', () => { - const AREA_FORM_DATA = { - datasource: '2__table', - viz_type: 'area', - slice_id: 48, - granularity_sqla: 'year', - time_grain_sqla: 'P1D', - time_range: '1960-01-01 : now', - metrics: ['sum__SP_POP_TOTL'], - adhoc_filters: [], - groupby: [], - limit: '25', - order_desc: true, - contribution: false, - row_limit: 50000, - show_brush: 'auto', - show_legend: true, - line_interpolation: 'linear', - stacked_style: 'stack', - color_scheme: 'bnbColors', - rich_tooltip: true, - show_controls: false, - x_axis_label: '', - bottom_margin: 'auto', - x_ticks_layout: 'auto', - x_axis_format: 'smart_date', - x_axis_showminmax: false, - y_axis_format: '.3s', - y_log_scale: false, - rolling_type: 'None', - comparison_type: 'values', - annotation_layers: [], - }; +export default () => + describe('Area', () => { + const AREA_FORM_DATA = { + datasource: '2__table', + viz_type: 'area', + slice_id: 48, + granularity_sqla: 'year', + time_grain_sqla: 'P1D', + time_range: '1960-01-01 : now', + metrics: ['sum__SP_POP_TOTL'], + adhoc_filters: [], + groupby: [], + limit: '25', + order_desc: true, + contribution: false, + row_limit: 50000, + show_brush: 'auto', + show_legend: true, + line_interpolation: 'linear', + stacked_style: 'stack', + color_scheme: 'bnbColors', + rich_tooltip: true, + show_controls: false, + x_axis_label: '', + bottom_margin: 'auto', + x_ticks_layout: 'auto', + x_axis_format: 'smart_date', + x_axis_showminmax: false, + y_axis_format: '.3s', + y_log_scale: false, + rolling_type: 'None', + comparison_type: 'values', + annotation_layers: [], + }; - function verify(formData) { - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - } + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + } - beforeEach(() => { - cy.server(); - cy.login(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); - - it('should work without groupby', () => { - verify(AREA_FORM_DATA); - cy.get('.nv-area').should('have.length', 1); - }); - - it('should work with group by', () => { - verify({ - ...AREA_FORM_DATA, - groupby: ['region'], + beforeEach(() => { + cy.server(); + cy.login(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); }); - cy.get('.nv-area').should('have.length', 7); - }); - - it('should work with groupby and filter', () => { - cy.visitChartByParams(JSON.stringify({ - ...AREA_FORM_DATA, - groupby: ['region'], - adhoc_filters: [{ - expressionType: 'SIMPLE', - subject: 'region', - operator: 'in', - comparator: ['South Asia', 'North America'], - clause: 'WHERE', - sqlExpression: null, - fromFormData: true, - filterOptionName: 'filter_txje2ikiv6_wxmn0qwd1xo', - }], - })); - - cy.wait('@getJson').then(async (xhr) => { - cy.verifyResponseCodes(xhr); - - const responseBody = await readResponseBlob(xhr.response.body); - - // Make sure data is sorted correctly - const firstRow = responseBody.data[0].values; - const secondRow = responseBody.data[1].values; - expect(firstRow[firstRow.length - 1].y).to.be.greaterThan(secondRow[secondRow.length - 1].y); - cy.verifySliceContainer('svg'); + it('should work without groupby', () => { + verify(AREA_FORM_DATA); + cy.get('.nv-area').should('have.length', 1); + }); + + it('should work with group by', () => { + verify({ + ...AREA_FORM_DATA, + groupby: ['region'], + }); + + cy.get('.nv-area').should('have.length', 7); + }); + + it('should work with groupby and filter', () => { + cy.visitChartByParams( + JSON.stringify({ + ...AREA_FORM_DATA, + groupby: ['region'], + adhoc_filters: [ + { + expressionType: 'SIMPLE', + subject: 'region', + operator: 'in', + comparator: ['South Asia', 'North America'], + clause: 'WHERE', + sqlExpression: null, + fromFormData: true, + filterOptionName: 'filter_txje2ikiv6_wxmn0qwd1xo', + }, + ], + }), + ); + + cy.wait('@getJson').then(async xhr => { + cy.verifyResponseCodes(xhr); + + const responseBody = await readResponseBlob(xhr.response.body); + + // Make sure data is sorted correctly + const firstRow = responseBody.data[0].values; + const secondRow = responseBody.data[1].values; + expect(firstRow[firstRow.length - 1].y).to.be.greaterThan( + secondRow[secondRow.length - 1].y, + ); + cy.verifySliceContainer('svg'); + }); + cy.get('.nv-area').should('have.length', 2); }); - cy.get('.nv-area').should('have.length', 2); }); -}); diff --git a/superset/assets/cypress/integration/explore/visualizations/big_number.js b/superset/assets/cypress/integration/explore/visualizations/big_number.js index a2d84283c7e..61c8fc65426 100644 --- a/superset/assets/cypress/integration/explore/visualizations/big_number.js +++ b/superset/assets/cypress/integration/explore/visualizations/big_number.js @@ -16,66 +16,70 @@ * specific language governing permissions and limitations * under the License. */ -export default () => describe('Big Number', () => { - const BIG_NUMBER_FORM_DATA = { - datasource: '2__table', - viz_type: 'big_number', - slice_id: 42, - granularity_sqla: 'year', - time_grain_sqla: 'P1D', - time_range: '2000+:+2014-01-02', - metric: 'sum__SP_POP_TOTL', - adhoc_filters: [], - compare_lag: '10', - compare_suffix: 'over+10Y', - y_axis_format: '.3s', - show_trend_line: true, - start_y_axis_at_zero: true, - color_picker: { - r: 0, - g: 122, - b: 135, - a: 1, - }, - }; +export default () => + describe('Big Number', () => { + const BIG_NUMBER_FORM_DATA = { + datasource: '2__table', + viz_type: 'big_number', + slice_id: 42, + granularity_sqla: 'year', + time_grain_sqla: 'P1D', + time_range: '2000+:+2014-01-02', + metric: 'sum__SP_POP_TOTL', + adhoc_filters: [], + compare_lag: '10', + compare_suffix: 'over+10Y', + y_axis_format: '.3s', + show_trend_line: true, + start_y_axis_at_zero: true, + color_picker: { + r: 0, + g: 122, + b: 135, + a: 1, + }, + }; - function verify(formData) { - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: '.big_number' }); - } + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + chartSelector: '.big_number', + }); + } - beforeEach(() => { - cy.login(); - cy.server(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); - - it('should work', () => { - verify(BIG_NUMBER_FORM_DATA); - cy.get('.chart-container .header_line'); - cy.get('.chart-container .subheader_line'); - cy.get('.chart-container svg path.vx-linepath'); - }); - - it('should work without subheader', () => { - verify({ - ...BIG_NUMBER_FORM_DATA, - compare_lag: null, + beforeEach(() => { + cy.login(); + cy.server(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); }); - cy.get('.chart-container .header_line'); - cy.get('.chart-container .subheader_line'); - cy.get('.chart-container svg path.vx-linepath'); - }); - it('should not render trendline when hidden', () => { - verify({ - ...BIG_NUMBER_FORM_DATA, - show_trend_line: false, + it('should work', () => { + verify(BIG_NUMBER_FORM_DATA); + cy.get('.chart-container .header_line'); + cy.get('.chart-container .subheader_line'); + cy.get('.chart-container svg path.vx-linepath'); }); - cy.get('.chart-container .header_line'); - cy.get('.chart-container .subheader_line'); - cy.get('.chart-container').then((containers) => { - expect(containers[0].querySelector('svg')).to.equal(null); + + it('should work without subheader', () => { + verify({ + ...BIG_NUMBER_FORM_DATA, + compare_lag: null, + }); + cy.get('.chart-container .header_line'); + cy.get('.chart-container .subheader_line'); + cy.get('.chart-container svg path.vx-linepath'); + }); + + it('should not render trendline when hidden', () => { + verify({ + ...BIG_NUMBER_FORM_DATA, + show_trend_line: false, + }); + cy.get('.chart-container .header_line'); + cy.get('.chart-container .subheader_line'); + cy.get('.chart-container').then(containers => { + expect(containers[0].querySelector('svg')).to.equal(null); + }); }); }); -}); diff --git a/superset/assets/cypress/integration/explore/visualizations/big_number_total.js b/superset/assets/cypress/integration/explore/visualizations/big_number_total.js index e395c77d779..3fa45cf7992 100644 --- a/superset/assets/cypress/integration/explore/visualizations/big_number_total.js +++ b/superset/assets/cypress/integration/explore/visualizations/big_number_total.js @@ -21,52 +21,67 @@ import readResponseBlob from '../../../utils/readResponseBlob'; // Big Number Total -export default () => describe('Big Number Total', () => { - const BIG_NUMBER_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'big_number_total' }; +export default () => + describe('Big Number Total', () => { + const BIG_NUMBER_DEFAULTS = { + ...FORM_DATA_DEFAULTS, + viz_type: 'big_number_total', + }; - beforeEach(() => { - cy.login(); - cy.server(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); + beforeEach(() => { + cy.login(); + cy.server(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); + }); - it('Test big number chart with adhoc metric', () => { - const formData = { ...BIG_NUMBER_DEFAULTS, metric: NUM_METRIC }; + it('Test big number chart with adhoc metric', () => { + const formData = { ...BIG_NUMBER_DEFAULTS, metric: NUM_METRIC }; - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', querySubstring: NUM_METRIC.label }); - }); + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + querySubstring: NUM_METRIC.label, + }); + }); - it('Test big number chart with simple filter', () => { - const filters = [ - { - expressionType: 'SIMPLE', - subject: 'name', - operator: 'in', - comparator: ['Aaron', 'Amy', 'Andrea'], - clause: 'WHERE', - sqlExpression: null, - fromFormData: true, - filterOptionName: 'filter_4y6teao56zs_ebjsvwy48c', - }, - ]; + it('Test big number chart with simple filter', () => { + const filters = [ + { + expressionType: 'SIMPLE', + subject: 'name', + operator: 'in', + comparator: ['Aaron', 'Amy', 'Andrea'], + clause: 'WHERE', + sqlExpression: null, + fromFormData: true, + filterOptionName: 'filter_4y6teao56zs_ebjsvwy48c', + }, + ]; - const formData = { ...BIG_NUMBER_DEFAULTS, metric: 'count', adhoc_filters: filters }; + const formData = { + ...BIG_NUMBER_DEFAULTS, + metric: 'count', + adhoc_filters: filters, + }; - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); - }); + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + }); - it('Test big number chart ignores groupby', () => { - const formData = { ...BIG_NUMBER_DEFAULTS, metric: NUM_METRIC, groupby: ['state'] }; + it('Test big number chart ignores groupby', () => { + const formData = { + ...BIG_NUMBER_DEFAULTS, + metric: NUM_METRIC, + groupby: ['state'], + }; - cy.visitChartByParams(JSON.stringify(formData)); - cy.wait(['@getJson']).then(async (xhr) => { - cy.verifyResponseCodes(xhr); - cy.verifySliceContainer(); + cy.visitChartByParams(JSON.stringify(formData)); + cy.wait(['@getJson']).then(async xhr => { + cy.verifyResponseCodes(xhr); + cy.verifySliceContainer(); - const responseBody = await readResponseBlob(xhr.response.body); - expect(responseBody.query).not.contains(formData.groupby[0]); + const responseBody = await readResponseBlob(xhr.response.body); + expect(responseBody.query).not.contains(formData.groupby[0]); + }); }); }); -}); diff --git a/superset/assets/cypress/integration/explore/visualizations/box_plot.js b/superset/assets/cypress/integration/explore/visualizations/box_plot.js index 582cb162482..86a0ca22ffe 100644 --- a/superset/assets/cypress/integration/explore/visualizations/box_plot.js +++ b/superset/assets/cypress/integration/explore/visualizations/box_plot.js @@ -16,53 +16,61 @@ * specific language governing permissions and limitations * under the License. */ -export default () => describe('Box Plot', () => { - const BOX_PLOT_FORM_DATA = { - datasource: '2__table', - viz_type: 'box_plot', - slice_id: 49, - granularity_sqla: 'year', - time_grain_sqla: 'P1D', - time_range: '1960-01-01+:+now', - metrics: ['sum__SP_POP_TOTL'], - adhoc_filters: [], - groupby: ['region'], - limit: '25', - color_scheme: 'bnbColors', - whisker_options: 'Min/max+(no+outliers)', - }; +export default () => + describe('Box Plot', () => { + const BOX_PLOT_FORM_DATA = { + datasource: '2__table', + viz_type: 'box_plot', + slice_id: 49, + granularity_sqla: 'year', + time_grain_sqla: 'P1D', + time_range: '1960-01-01+:+now', + metrics: ['sum__SP_POP_TOTL'], + adhoc_filters: [], + groupby: ['region'], + limit: '25', + color_scheme: 'bnbColors', + whisker_options: 'Min/max+(no+outliers)', + }; - function verify(formData) { - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - } + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + } - beforeEach(() => { - cy.server(); - cy.login(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); - - it('should work', () => { - verify(BOX_PLOT_FORM_DATA); - cy.get('.chart-container svg rect.nv-boxplot-box').should('have.length', 7); - }); - - it('should work with filter', () => { - verify({ - ...BOX_PLOT_FORM_DATA, - adhoc_filters: [{ - expressionType: 'SIMPLE', - subject: 'region', - operator: '==', - comparator: 'South Asia', - clause: 'WHERE', - sqlExpression: null, - fromFormData: true, - filterOptionName: 'filter_8aqxcf5co1a_x7lm2d1fq0l', - }], + beforeEach(() => { + cy.server(); + cy.login(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); }); - cy.get('.chart-container svg rect.nv-boxplot-box').should('have.length', 1); - }); -}); + it('should work', () => { + verify(BOX_PLOT_FORM_DATA); + cy.get('.chart-container svg rect.nv-boxplot-box').should( + 'have.length', + 7, + ); + }); + + it('should work with filter', () => { + verify({ + ...BOX_PLOT_FORM_DATA, + adhoc_filters: [ + { + expressionType: 'SIMPLE', + subject: 'region', + operator: '==', + comparator: 'South Asia', + clause: 'WHERE', + sqlExpression: null, + fromFormData: true, + filterOptionName: 'filter_8aqxcf5co1a_x7lm2d1fq0l', + }, + ], + }); + cy.get('.chart-container svg rect.nv-boxplot-box').should( + 'have.length', + 1, + ); + }); + }); diff --git a/superset/assets/cypress/integration/explore/visualizations/bubble.js b/superset/assets/cypress/integration/explore/visualizations/bubble.js index 915e97d5a56..563b453300b 100644 --- a/superset/assets/cypress/integration/explore/visualizations/bubble.js +++ b/superset/assets/cypress/integration/explore/visualizations/bubble.js @@ -16,74 +16,78 @@ * specific language governing permissions and limitations * under the License. */ -export default () => describe('Bubble', () => { - const BUBBLE_FORM_DATA = { - datasource: '2__table', - viz_type: 'bubble', - slice_id: 46, - granularity_sqla: 'year', - time_grain_sqla: 'P1D', - time_range: '2011-01-01+:+2011-01-02', - series: 'region', - entity: 'country_name', - x: 'sum__SP_RUR_TOTL_ZS', - y: 'sum__SP_DYN_LE00_IN', - size: 'sum__SP_POP_TOTL', - max_bubble_size: '50', - limit: 0, - color_scheme: 'bnbColors', - show_legend: true, - x_axis_label: '', - left_margin: 'auto', - x_axis_format: '.3s', - x_ticks_layout: 'auto', - x_log_scale: false, - x_axis_showminmax: false, - y_axis_label: '', - bottom_margin: 'auto', - y_axis_format: '.3s', - y_log_scale: false, - y_axis_showminmax: false, - }; +export default () => + describe('Bubble', () => { + const BUBBLE_FORM_DATA = { + datasource: '2__table', + viz_type: 'bubble', + slice_id: 46, + granularity_sqla: 'year', + time_grain_sqla: 'P1D', + time_range: '2011-01-01+:+2011-01-02', + series: 'region', + entity: 'country_name', + x: 'sum__SP_RUR_TOTL_ZS', + y: 'sum__SP_DYN_LE00_IN', + size: 'sum__SP_POP_TOTL', + max_bubble_size: '50', + limit: 0, + color_scheme: 'bnbColors', + show_legend: true, + x_axis_label: '', + left_margin: 'auto', + x_axis_format: '.3s', + x_ticks_layout: 'auto', + x_log_scale: false, + x_axis_showminmax: false, + y_axis_label: '', + bottom_margin: 'auto', + y_axis_format: '.3s', + y_log_scale: false, + y_axis_showminmax: false, + }; - function verify(formData) { - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - } + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + } - beforeEach(() => { - cy.server(); - cy.login(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); - - it('should work', () => { - verify(BUBBLE_FORM_DATA); - cy.get('.chart-container svg circle').should('have.length', 208); - }); - - it('should work with filter', () => { - verify({ - ...BUBBLE_FORM_DATA, - adhoc_filters: [{ - expressionType: 'SIMPLE', - subject: 'region', - operator: '==', - comparator: 'South+Asia', - clause: 'WHERE', - sqlExpression: null, - fromFormData: true, - filterOptionName: 'filter_b2tfg1rs8y_8kmrcyxvsqd', - }], + beforeEach(() => { + cy.server(); + cy.login(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); }); - cy.get('.chart-container svg circle') - .should('have.length', 9) - .then((nodeList) => { - // Check that all circles have same color. - const color = nodeList[0].getAttribute('fill'); - const circles = Array.prototype.slice.call(nodeList); - expect(circles.every(c => c.getAttribute('fill') === color)).to.equal(true); - }); - }); -}); + it('should work', () => { + verify(BUBBLE_FORM_DATA); + cy.get('.chart-container svg circle').should('have.length', 208); + }); + + it('should work with filter', () => { + verify({ + ...BUBBLE_FORM_DATA, + adhoc_filters: [ + { + expressionType: 'SIMPLE', + subject: 'region', + operator: '==', + comparator: 'South+Asia', + clause: 'WHERE', + sqlExpression: null, + fromFormData: true, + filterOptionName: 'filter_b2tfg1rs8y_8kmrcyxvsqd', + }, + ], + }); + cy.get('.chart-container svg circle') + .should('have.length', 9) + .then(nodeList => { + // Check that all circles have same color. + const color = nodeList[0].getAttribute('fill'); + const circles = Array.prototype.slice.call(nodeList); + expect(circles.every(c => c.getAttribute('fill') === color)).to.equal( + true, + ); + }); + }); + }); diff --git a/superset/assets/cypress/integration/explore/visualizations/compare.js b/superset/assets/cypress/integration/explore/visualizations/compare.js index 04f072e5ebf..2693eb26779 100644 --- a/superset/assets/cypress/integration/explore/visualizations/compare.js +++ b/superset/assets/cypress/integration/explore/visualizations/compare.js @@ -16,75 +16,77 @@ * specific language governing permissions and limitations * under the License. */ -export default () => describe('Compare', () => { - const COMPARE_FORM_DATA = { - datasource: '3__table', - viz_type: 'compare', - slice_id: 60, - granularity_sqla: 'ds', - time_grain_sqla: 'P1D', - time_range: '100 years ago : now', - metrics: ['count'], - adhoc_filters: [], - groupby: [], - order_desc: true, - contribution: false, - row_limit: 50000, - color_scheme: 'bnbColors', - x_axis_label: 'Frequency', - bottom_margin: 'auto', - x_ticks_layout: 'auto', - x_axis_format: 'smart_date', - x_axis_showminmax: false, - y_axis_label: 'Num', - left_margin: 'auto', - y_axis_showminmax: false, - y_log_scale: false, - y_axis_format: '.3s', - rolling_type: 'None', - comparison_type: 'values', - annotation_layers: [], - }; +export default () => + describe('Compare', () => { + const COMPARE_FORM_DATA = { + datasource: '3__table', + viz_type: 'compare', + slice_id: 60, + granularity_sqla: 'ds', + time_grain_sqla: 'P1D', + time_range: '100 years ago : now', + metrics: ['count'], + adhoc_filters: [], + groupby: [], + order_desc: true, + contribution: false, + row_limit: 50000, + color_scheme: 'bnbColors', + x_axis_label: 'Frequency', + bottom_margin: 'auto', + x_ticks_layout: 'auto', + x_axis_format: 'smart_date', + x_axis_showminmax: false, + y_axis_label: 'Num', + left_margin: 'auto', + y_axis_showminmax: false, + y_log_scale: false, + y_axis_format: '.3s', + rolling_type: 'None', + comparison_type: 'values', + annotation_layers: [], + }; - function verify(formData) { - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - } + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + } - beforeEach(() => { - cy.server(); - cy.login(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); - - it('should work without groupby', () => { - verify(COMPARE_FORM_DATA); - cy.get('.chart-container .nvd3 path.nv-line').should('have.length', 1); - }); - - it('should with group by', () => { - verify({ - ...COMPARE_FORM_DATA, - groupby: ['gender'], + beforeEach(() => { + cy.server(); + cy.login(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); }); - cy.get('.chart-container .nvd3 path.nv-line').should('have.length', 2); - }); - it('should work with filter', () => { - verify({ - ...COMPARE_FORM_DATA, - adhoc_filters: [{ - expressionType: 'SIMPLE', - subject: 'gender', - operator: '==', - comparator: 'boy', - clause: 'WHERE', - sqlExpression: null, - fromFormData: true, - filterOptionName: 'filter_tqx1en70hh_7nksse7nqic', - }], + it('should work without groupby', () => { + verify(COMPARE_FORM_DATA); + cy.get('.chart-container .nvd3 path.nv-line').should('have.length', 1); }); - cy.get('.chart-container .nvd3 path.nv-line').should('have.length', 1); - }); -}); + it('should with group by', () => { + verify({ + ...COMPARE_FORM_DATA, + groupby: ['gender'], + }); + cy.get('.chart-container .nvd3 path.nv-line').should('have.length', 2); + }); + + it('should work with filter', () => { + verify({ + ...COMPARE_FORM_DATA, + adhoc_filters: [ + { + expressionType: 'SIMPLE', + subject: 'gender', + operator: '==', + comparator: 'boy', + clause: 'WHERE', + sqlExpression: null, + fromFormData: true, + filterOptionName: 'filter_tqx1en70hh_7nksse7nqic', + }, + ], + }); + cy.get('.chart-container .nvd3 path.nv-line').should('have.length', 1); + }); + }); diff --git a/superset/assets/cypress/integration/explore/visualizations/dist_bar.js b/superset/assets/cypress/integration/explore/visualizations/dist_bar.js index 9f8046f0880..7b3a8f0440a 100644 --- a/superset/assets/cypress/integration/explore/visualizations/dist_bar.js +++ b/superset/assets/cypress/integration/explore/visualizations/dist_bar.js @@ -20,60 +20,65 @@ import { FORM_DATA_DEFAULTS, NUM_METRIC } from './shared.helper'; // Dist bar -export default () => describe('Distribution bar chart', () => { - const VIZ_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'dist_bar' }; +export default () => + describe('Distribution bar chart', () => { + const VIZ_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'dist_bar' }; - beforeEach(() => { - cy.login(); - cy.server(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); + beforeEach(() => { + cy.login(); + cy.server(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); + }); - it('Test dist bar with adhoc metric', () => { - const formData = { ...VIZ_DEFAULTS, metrics: NUM_METRIC, groupby: ['state'] }; + it('Test dist bar with adhoc metric', () => { + const formData = { + ...VIZ_DEFAULTS, + metrics: NUM_METRIC, + groupby: ['state'], + }; - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ - waitAlias: '@getJson', - querySubstring: NUM_METRIC.label, - chartSelector: 'svg', + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + querySubstring: NUM_METRIC.label, + chartSelector: 'svg', + }); + }); + + it('Test dist bar with series', () => { + const formData = { + ...VIZ_DEFAULTS, + metrics: NUM_METRIC, + groupby: ['state'], + columns: ['gender'], + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + }); + + it('Test dist bar with row limit', () => { + const formData = { + ...VIZ_DEFAULTS, + metrics: NUM_METRIC, + groupby: ['state'], + row_limit: 10, + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + }); + + it('Test dist bar with contribution', () => { + const formData = { + ...VIZ_DEFAULTS, + metrics: NUM_METRIC, + groupby: ['state'], + columns: ['gender'], + contribution: true, + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); }); }); - - it('Test dist bar with series', () => { - const formData = { - ...VIZ_DEFAULTS, - metrics: NUM_METRIC, - groupby: ['state'], - columns: ['gender'], - }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - }); - - it('Test dist bar with row limit', () => { - const formData = { - ...VIZ_DEFAULTS, - metrics: NUM_METRIC, - groupby: ['state'], - row_limit: 10, - }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - }); - - it('Test dist bar with contribution', () => { - const formData = { - ...VIZ_DEFAULTS, - metrics: NUM_METRIC, - groupby: ['state'], - columns: ['gender'], - contribution: true, - }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - }); -}); diff --git a/superset/assets/cypress/integration/explore/visualizations/dual_line.js b/superset/assets/cypress/integration/explore/visualizations/dual_line.js index 069c96edf04..b86b0b4d5b5 100644 --- a/superset/assets/cypress/integration/explore/visualizations/dual_line.js +++ b/superset/assets/cypress/integration/explore/visualizations/dual_line.js @@ -16,56 +16,57 @@ * specific language governing permissions and limitations * under the License. */ -export default () => describe('Dual Line', () => { - const DUAL_LINE_FORM_DATA = { - datasource: '3__table', - viz_type: 'dual_line', - slice_id: 58, - granularity_sqla: 'ds', - time_grain_sqla: 'P1D', - time_range: '100+years+ago+:+now', - color_scheme: 'bnbColors', - x_axis_format: 'smart_date', - metric: 'avg__num', - y_axis_format: '.3s', - metric_2: 'sum__num', - y_axis_2_format: '.3s', - adhoc_filters: [], - annotation_layers: [], - }; +export default () => + describe('Dual Line', () => { + const DUAL_LINE_FORM_DATA = { + datasource: '3__table', + viz_type: 'dual_line', + slice_id: 58, + granularity_sqla: 'ds', + time_grain_sqla: 'P1D', + time_range: '100+years+ago+:+now', + color_scheme: 'bnbColors', + x_axis_format: 'smart_date', + metric: 'avg__num', + y_axis_format: '.3s', + metric_2: 'sum__num', + y_axis_2_format: '.3s', + adhoc_filters: [], + annotation_layers: [], + }; - function verify(formData) { - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - } + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + } - beforeEach(() => { - cy.server(); - cy.login(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); - - it('should work', () => { - verify(DUAL_LINE_FORM_DATA); - cy.get('.chart-container svg path.nv-line').should('have.length', 2); - }); - - it('should work with filter', () => { - verify({ - ...DUAL_LINE_FORM_DATA, - adhoc_filters: [{ - expressionType: 'SIMPLE', - subject: 'gender', - operator: '==', - comparator: 'girl', - clause: 'WHERE', - sqlExpression: null, - fromFormData: true, - filterOptionName: 'filter_1ep6q50g8vk_48jj6qxdems', - }], + beforeEach(() => { + cy.server(); + cy.login(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); + }); + + it('should work', () => { + verify(DUAL_LINE_FORM_DATA); + cy.get('.chart-container svg path.nv-line').should('have.length', 2); + }); + + it('should work with filter', () => { + verify({ + ...DUAL_LINE_FORM_DATA, + adhoc_filters: [ + { + expressionType: 'SIMPLE', + subject: 'gender', + operator: '==', + comparator: 'girl', + clause: 'WHERE', + sqlExpression: null, + fromFormData: true, + filterOptionName: 'filter_1ep6q50g8vk_48jj6qxdems', + }, + ], + }); + cy.get('.chart-container svg path.nv-line').should('have.length', 2); }); - cy.get('.chart-container svg path.nv-line').should('have.length', 2); }); - -}); - diff --git a/superset/assets/cypress/integration/explore/visualizations/filter_box.js b/superset/assets/cypress/integration/explore/visualizations/filter_box.js index 69654a728f4..f62937de8c5 100644 --- a/superset/assets/cypress/integration/explore/visualizations/filter_box.js +++ b/superset/assets/cypress/integration/explore/visualizations/filter_box.js @@ -18,26 +18,26 @@ */ import { FORM_DATA_DEFAULTS } from './shared.helper'; -export default () => describe('FilterBox', () => { - const VIZ_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'filter_box' }; +export default () => + describe('FilterBox', () => { + const VIZ_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'filter_box' }; - function verify(formData) { - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); - } + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + } - beforeEach(() => { - cy.server(); - cy.login(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); + beforeEach(() => { + cy.server(); + cy.login(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); + }); - it('should work with default date filter', () => { - verify(VIZ_DEFAULTS); - // Filter box should default to having a date filter with no filter selected - cy.get('div.filter_box').within(() => { - cy.get('span').contains('No filter'); + it('should work with default date filter', () => { + verify(VIZ_DEFAULTS); + // Filter box should default to having a date filter with no filter selected + cy.get('div.filter_box').within(() => { + cy.get('span').contains('No filter'); + }); }); }); - -}); diff --git a/superset/assets/cypress/integration/explore/visualizations/histogram.js b/superset/assets/cypress/integration/explore/visualizations/histogram.js index 1898a6d27ed..ec1957f0eb9 100644 --- a/superset/assets/cypress/integration/explore/visualizations/histogram.js +++ b/superset/assets/cypress/integration/explore/visualizations/histogram.js @@ -16,65 +16,67 @@ * specific language governing permissions and limitations * under the License. */ -export default () => describe('Histogram', () => { - const HISTOGRAM_FORM_DATA = { - datasource: '3__table', - viz_type: 'histogram', - slice_id: 60, - granularity_sqla: 'ds', - time_grain_sqla: 'P1D', - time_range: '100 years ago : now', - all_columns_x: ['num'], - adhoc_filters: [], - row_limit: 50000, - groupby: [], - color_scheme: 'bnbColors', - link_length: 5, - x_axis_label: 'Frequency', - y_axis_label: 'Num', - global_opacity: 1, - normalized: false, - }; +export default () => + describe('Histogram', () => { + const HISTOGRAM_FORM_DATA = { + datasource: '3__table', + viz_type: 'histogram', + slice_id: 60, + granularity_sqla: 'ds', + time_grain_sqla: 'P1D', + time_range: '100 years ago : now', + all_columns_x: ['num'], + adhoc_filters: [], + row_limit: 50000, + groupby: [], + color_scheme: 'bnbColors', + link_length: 5, + x_axis_label: 'Frequency', + y_axis_label: 'Num', + global_opacity: 1, + normalized: false, + }; - function verify(formData) { - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - } + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + } - beforeEach(() => { - cy.server(); - cy.login(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); - - it('should work without groupby', () => { - verify(HISTOGRAM_FORM_DATA); - cy.get('.chart-container svg .vx-bar').should('have.length', 6); - }); - - it('should with group by', () => { - verify({ - ...HISTOGRAM_FORM_DATA, - groupby: ['gender'], + beforeEach(() => { + cy.server(); + cy.login(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); }); - cy.get('.chart-container svg .vx-bar').should('have.length', 12); - }); - it('should work with filter', () => { - verify({ - ...HISTOGRAM_FORM_DATA, - adhoc_filters: [{ - expressionType: 'SIMPLE', - subject: 'gender', - operator: '==', - comparator: 'boy', - clause: 'WHERE', - sqlExpression: null, - fromFormData: true, - filterOptionName: 'filter_tqx1en70hh_7nksse7nqic', - }], + it('should work without groupby', () => { + verify(HISTOGRAM_FORM_DATA); + cy.get('.chart-container svg .vx-bar').should('have.length', 6); }); - cy.get('.chart-container svg .vx-bar').should('have.length', 5); - }); -}); + it('should with group by', () => { + verify({ + ...HISTOGRAM_FORM_DATA, + groupby: ['gender'], + }); + cy.get('.chart-container svg .vx-bar').should('have.length', 12); + }); + + it('should work with filter', () => { + verify({ + ...HISTOGRAM_FORM_DATA, + adhoc_filters: [ + { + expressionType: 'SIMPLE', + subject: 'gender', + operator: '==', + comparator: 'boy', + clause: 'WHERE', + sqlExpression: null, + fromFormData: true, + filterOptionName: 'filter_tqx1en70hh_7nksse7nqic', + }, + ], + }); + cy.get('.chart-container svg .vx-bar').should('have.length', 5); + }); + }); diff --git a/superset/assets/cypress/integration/explore/visualizations/line.js b/superset/assets/cypress/integration/explore/visualizations/line.js index 94bc393c493..1292072d395 100644 --- a/superset/assets/cypress/integration/explore/visualizations/line.js +++ b/superset/assets/cypress/integration/explore/visualizations/line.js @@ -18,223 +18,241 @@ */ import { FORM_DATA_DEFAULTS, NUM_METRIC, SIMPLE_FILTER } from './shared.helper'; -export default () => describe('Line', () => { - const LINE_CHART_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'line' }; +export default () => + describe('Line', () => { + const LINE_CHART_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'line' }; - beforeEach(() => { - cy.login(); - cy.server(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); - - it('Test line chart with adhoc metric', () => { - const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - }); - - it('Test line chart with groupby', () => { - const metrics = ['count']; - const groupby = ['gender']; - - const formData = { ...LINE_CHART_DEFAULTS, metrics, groupby }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - }); - - it('Test line chart with simple filter', () => { - const metrics = ['count']; - const filters = [SIMPLE_FILTER]; - - const formData = { ...LINE_CHART_DEFAULTS, metrics, adhoc_filters: filters }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - }); - - it('Test line chart with series limit sort asc', () => { - const formData = { - ...LINE_CHART_DEFAULTS, - metrics: [NUM_METRIC], - limit: 10, - groupby: ['name'], - timeseries_limit_metric: NUM_METRIC, - }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - }); - - it('Test line chart with series limit sort desc', () => { - const formData = { - ...LINE_CHART_DEFAULTS, - metrics: [NUM_METRIC], - limit: 10, - groupby: ['name'], - timeseries_limit_metric: NUM_METRIC, - order_desc: true, - }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - }); - - it('Test line chart with rolling avg', () => { - const metrics = [NUM_METRIC]; - - const formData = { ...LINE_CHART_DEFAULTS, metrics, rolling_type: 'mean', rolling_periods: 10 }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - }); - - it('Test line chart with time shift 1 year', () => { - const metrics = [NUM_METRIC]; - - const formData = { - ...LINE_CHART_DEFAULTS, - metrics, - time_compare: ['1+year'], - comparison_type: 'values', - groupby: ['gender'], - }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - - // Offset color should match original line color - cy.get('.nv-legend-text') - .contains('boy') - .siblings() - .first() - .should('have.attr', 'style') - .then((style) => { - cy.get('.nv-legend-text') - .contains('boy, 1 year offset') - .siblings() - .first() - .should('have.attr', 'style') - .and('eq', style); - }); - - cy.get('.nv-legend-text') - .contains('girl') - .siblings() - .first() - .should('have.attr', 'style') - .then((style) => { - cy.get('.nv-legend-text') - .contains('girl, 1 year offset') - .siblings() - .first() - .should('have.attr', 'style') - .and('eq', style); - }); - }); - - it('Test line chart with time shift yoy', () => { - const metrics = [NUM_METRIC]; - - const formData = { - ...LINE_CHART_DEFAULTS, - metrics, - time_compare: ['1+year'], - comparison_type: 'ratio', - }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - }); - - it('Test line chart with time shift percentage change', () => { - const metrics = [NUM_METRIC]; - - const formData = { - ...LINE_CHART_DEFAULTS, - metrics, - time_compare: ['1+year'], - comparison_type: 'percentage', - }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - }); - - it('Test verbose name shows up in legend', () => { - const formData = { - ...LINE_CHART_DEFAULTS, - metrics: ['count'], - }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - cy.get('text.nv-legend-text').contains('COUNT(*)'); - }); - - it('Test hidden annotation', () => { - const formData = { - ...LINE_CHART_DEFAULTS, - metrics: ['count'], - annotation_layers: [{ - name: 'Goal+line', - annotationType: 'FORMULA', - sourceType: '', - value: 'y=140000', - overrides: { time_range: null }, - show: false, - titleColumn: '', - descriptionColumns: [], - timeColumn: '', - intervalEndColumn: '', - color: null, - opacity: '', - style: 'solid', - width: 1, - showMarkers: false, - hideLine: false, - }], - }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - cy.get('.slice_container').within(() => { - // Goal line annotation doesn't show up in legend - cy.get('.nv-legend-text').should('have.length', 1); + beforeEach(() => { + cy.login(); + cy.server(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); }); - }); - it('Test event annotation time override', () => { - cy.request('/chart/api/read?_flt_3_slice_name=Daily+Totals').then((response) => { - const value = response.body.pks[0]; + it('Test line chart with adhoc metric', () => { + const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + }); + + it('Test line chart with groupby', () => { + const metrics = ['count']; + const groupby = ['gender']; + + const formData = { ...LINE_CHART_DEFAULTS, metrics, groupby }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + }); + + it('Test line chart with simple filter', () => { + const metrics = ['count']; + const filters = [SIMPLE_FILTER]; + + const formData = { + ...LINE_CHART_DEFAULTS, + metrics, + adhoc_filters: filters, + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + }); + + it('Test line chart with series limit sort asc', () => { + const formData = { + ...LINE_CHART_DEFAULTS, + metrics: [NUM_METRIC], + limit: 10, + groupby: ['name'], + timeseries_limit_metric: NUM_METRIC, + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + }); + + it('Test line chart with series limit sort desc', () => { + const formData = { + ...LINE_CHART_DEFAULTS, + metrics: [NUM_METRIC], + limit: 10, + groupby: ['name'], + timeseries_limit_metric: NUM_METRIC, + order_desc: true, + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + }); + + it('Test line chart with rolling avg', () => { + const metrics = [NUM_METRIC]; + + const formData = { + ...LINE_CHART_DEFAULTS, + metrics, + rolling_type: 'mean', + rolling_periods: 10, + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + }); + + it('Test line chart with time shift 1 year', () => { + const metrics = [NUM_METRIC]; + + const formData = { + ...LINE_CHART_DEFAULTS, + metrics, + time_compare: ['1+year'], + comparison_type: 'values', + groupby: ['gender'], + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + + // Offset color should match original line color + cy.get('.nv-legend-text') + .contains('boy') + .siblings() + .first() + .should('have.attr', 'style') + .then(style => { + cy.get('.nv-legend-text') + .contains('boy, 1 year offset') + .siblings() + .first() + .should('have.attr', 'style') + .and('eq', style); + }); + + cy.get('.nv-legend-text') + .contains('girl') + .siblings() + .first() + .should('have.attr', 'style') + .then(style => { + cy.get('.nv-legend-text') + .contains('girl, 1 year offset') + .siblings() + .first() + .should('have.attr', 'style') + .and('eq', style); + }); + }); + + it('Test line chart with time shift yoy', () => { + const metrics = [NUM_METRIC]; + + const formData = { + ...LINE_CHART_DEFAULTS, + metrics, + time_compare: ['1+year'], + comparison_type: 'ratio', + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + }); + + it('Test line chart with time shift percentage change', () => { + const metrics = [NUM_METRIC]; + + const formData = { + ...LINE_CHART_DEFAULTS, + metrics, + time_compare: ['1+year'], + comparison_type: 'percentage', + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + }); + + it('Test verbose name shows up in legend', () => { const formData = { ...LINE_CHART_DEFAULTS, metrics: ['count'], - annotation_layers: [{ - name: 'Yearly date', - annotationType: 'EVENT', - sourceType: 'table', - value, - overrides: { time_range: null }, - show: true, - titleColumn: 'ds', - descriptionColumns: ['ds'], - timeColumn: 'ds', - color: null, - opacity: '', - style: 'solid', - width: 1, - showMarkers: false, - hideLine: false, - }], }; + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + cy.get('text.nv-legend-text').contains('COUNT(*)'); }); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - cy.get('.slice_container').within(() => { - cy.get('.nv-event-annotation-layer-0').children().should('have.length', 44); + it('Test hidden annotation', () => { + const formData = { + ...LINE_CHART_DEFAULTS, + metrics: ['count'], + annotation_layers: [ + { + name: 'Goal+line', + annotationType: 'FORMULA', + sourceType: '', + value: 'y=140000', + overrides: { time_range: null }, + show: false, + titleColumn: '', + descriptionColumns: [], + timeColumn: '', + intervalEndColumn: '', + color: null, + opacity: '', + style: 'solid', + width: 1, + showMarkers: false, + hideLine: false, + }, + ], + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + cy.get('.slice_container').within(() => { + // Goal line annotation doesn't show up in legend + cy.get('.nv-legend-text').should('have.length', 1); + }); + }); + + it('Test event annotation time override', () => { + cy.request('/chart/api/read?_flt_3_slice_name=Daily+Totals').then( + response => { + const value = response.body.pks[0]; + const formData = { + ...LINE_CHART_DEFAULTS, + metrics: ['count'], + annotation_layers: [ + { + name: 'Yearly date', + annotationType: 'EVENT', + sourceType: 'table', + value, + overrides: { time_range: null }, + show: true, + titleColumn: 'ds', + descriptionColumns: ['ds'], + timeColumn: 'ds', + color: null, + opacity: '', + style: 'solid', + width: 1, + showMarkers: false, + hideLine: false, + }, + ], + }; + cy.visitChartByParams(JSON.stringify(formData)); + }, + ); + + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + cy.get('.slice_container').within(() => { + cy.get('.nv-event-annotation-layer-0') + .children() + .should('have.length', 44); + }); }); }); -}); diff --git a/superset/assets/cypress/integration/explore/visualizations/pie.js b/superset/assets/cypress/integration/explore/visualizations/pie.js index 6b404d4e6a8..832b6743f24 100644 --- a/superset/assets/cypress/integration/explore/visualizations/pie.js +++ b/superset/assets/cypress/integration/explore/visualizations/pie.js @@ -16,56 +16,65 @@ * specific language governing permissions and limitations * under the License. */ -export default () => describe('Pie', () => { - const PIE_FORM_DATA = { - datasource: '3__table', - viz_type: 'pie', - slice_id: 55, - granularity_sqla: 'ds', - time_grain_sqla: 'P1D', - time_range: '100 years ago : now', - metric: 'sum__num', - adhoc_filters: [], - groupby: ['gender'], - row_limit: 50000, - pie_label_type: 'key', - donut: false, - show_legend: true, - show_labels: true, - labels_outside: true, - color_scheme: 'bnbColors', - }; +export default () => + describe('Pie', () => { + const PIE_FORM_DATA = { + datasource: '3__table', + viz_type: 'pie', + slice_id: 55, + granularity_sqla: 'ds', + time_grain_sqla: 'P1D', + time_range: '100 years ago : now', + metric: 'sum__num', + adhoc_filters: [], + groupby: ['gender'], + row_limit: 50000, + pie_label_type: 'key', + donut: false, + show_legend: true, + show_labels: true, + labels_outside: true, + color_scheme: 'bnbColors', + }; - function verify(formData) { - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - } + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + } - beforeEach(() => { - cy.server(); - cy.login(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); - - it('should work with ad-hoc metric', () => { - verify(PIE_FORM_DATA); - cy.get('.chart-container .nv-pie .nv-slice path').should('have.length', 2); - }); - - it('should work with simple filter', () => { - verify({ - ...PIE_FORM_DATA, - adhoc_filters: [{ - expressionType: 'SIMPLE', - subject: 'gender', - operator: '==', - comparator: 'boy', - clause: 'WHERE', - sqlExpression: null, - fromFormData: true, - filterOptionName: 'filter_tqx1en70hh_7nksse7nqic', - }], + beforeEach(() => { + cy.server(); + cy.login(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); + }); + + it('should work with ad-hoc metric', () => { + verify(PIE_FORM_DATA); + cy.get('.chart-container .nv-pie .nv-slice path').should( + 'have.length', + 2, + ); + }); + + it('should work with simple filter', () => { + verify({ + ...PIE_FORM_DATA, + adhoc_filters: [ + { + expressionType: 'SIMPLE', + subject: 'gender', + operator: '==', + comparator: 'boy', + clause: 'WHERE', + sqlExpression: null, + fromFormData: true, + filterOptionName: 'filter_tqx1en70hh_7nksse7nqic', + }, + ], + }); + cy.get('.chart-container .nv-pie .nv-slice path').should( + 'have.length', + 1, + ); }); - cy.get('.chart-container .nv-pie .nv-slice path').should('have.length', 1); }); -}); diff --git a/superset/assets/cypress/integration/explore/visualizations/pivot_table.js b/superset/assets/cypress/integration/explore/visualizations/pivot_table.js index 4422d04359d..88746086e96 100644 --- a/superset/assets/cypress/integration/explore/visualizations/pivot_table.js +++ b/superset/assets/cypress/integration/explore/visualizations/pivot_table.js @@ -16,95 +16,114 @@ * specific language governing permissions and limitations * under the License. */ -export default () => describe('Pivot Table', () => { - const PIVOT_TABLE_FORM_DATA = { - datasource: '3__table', - viz_type: 'pivot_table', - slice_id: 61, - granularity_sqla: 'ds', - time_grain_sqla: 'P1D', - time_range: '100+years+ago+:+now', - metrics: ['sum__num'], - adhoc_filters: [], - groupby: ['name'], - columns: ['state'], - row_limit: 50000, - pandas_aggfunc: 'sum', - pivot_margins: true, - number_format: '.3s', - combine_metric: false, - }; +export default () => + describe('Pivot Table', () => { + const PIVOT_TABLE_FORM_DATA = { + datasource: '3__table', + viz_type: 'pivot_table', + slice_id: 61, + granularity_sqla: 'ds', + time_grain_sqla: 'P1D', + time_range: '100+years+ago+:+now', + metrics: ['sum__num'], + adhoc_filters: [], + groupby: ['name'], + columns: ['state'], + row_limit: 50000, + pandas_aggfunc: 'sum', + pivot_margins: true, + number_format: '.3s', + combine_metric: false, + }; - const TEST_METRIC = { - expressionType: 'SIMPLE', - column: { - id: 338, - column_name: 'sum_boys', - expression: '', - filterable: false, - groupby: false, - is_dttm: false, - type: 'BIGINT', - optionName: '_col_sum_boys', - }, - aggregate: 'SUM', - hasCustomLabel: false, - fromFormData: false, - label: 'SUM(sum_boys)', - optionName: 'metric_gvpdjt0v2qf_6hkf56o012', - }; + const TEST_METRIC = { + expressionType: 'SIMPLE', + column: { + id: 338, + column_name: 'sum_boys', + expression: '', + filterable: false, + groupby: false, + is_dttm: false, + type: 'BIGINT', + optionName: '_col_sum_boys', + }, + aggregate: 'SUM', + hasCustomLabel: false, + fromFormData: false, + label: 'SUM(sum_boys)', + optionName: 'metric_gvpdjt0v2qf_6hkf56o012', + }; - function verify(formData) { - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' }); - } + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' }); + } - beforeEach(() => { - cy.server(); - cy.login(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); + beforeEach(() => { + cy.server(); + cy.login(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); + }); - it('should work with single groupby', () => { - verify(PIVOT_TABLE_FORM_DATA); - cy.get('.chart-container tr th').then((ths) => { - expect(ths.find(th => th.innerText.trim() === 'name')).to.not.equal(undefined); + it('should work with single groupby', () => { + verify(PIVOT_TABLE_FORM_DATA); + cy.get('.chart-container tr th').then(ths => { + expect(ths.find(th => th.innerText.trim() === 'name')).to.not.equal( + undefined, + ); + }); + }); + + it('should work with more than one groupby', () => { + verify({ + ...PIVOT_TABLE_FORM_DATA, + groupby: ['name', 'gender'], + }); + cy.get('.chart-container tr th').then(ths => { + expect(ths.find(th => th.innerText.trim() === 'name')).to.not.equal( + undefined, + ); + expect(ths.find(th => th.innerText.trim() === 'gender')).to.not.equal( + undefined, + ); + }); + }); + + it('should work with multiple metrics', () => { + verify({ + ...PIVOT_TABLE_FORM_DATA, + metrics: ['sum__num', TEST_METRIC], + }); + cy.get('.chart-container tr th').then(ths => { + expect(ths.find(th => th.innerText.trim() === 'sum__num')).to.not.equal( + undefined, + ); + expect( + ths.find(th => th.innerText.trim() === 'SUM(sum_boys)'), + ).to.not.equal(undefined); + }); + }); + + it('should work with multiple groupby and multiple metrics', () => { + verify({ + ...PIVOT_TABLE_FORM_DATA, + groupby: ['name', 'gender'], + metrics: ['sum__num', TEST_METRIC], + }); + cy.get('.chart-container tr th').then(ths => { + expect(ths.find(th => th.innerText.trim() === 'name')).to.not.equal( + undefined, + ); + expect(ths.find(th => th.innerText.trim() === 'gender')).to.not.equal( + undefined, + ); + expect(ths.find(th => th.innerText.trim() === 'sum__num')).to.not.equal( + undefined, + ); + expect( + ths.find(th => th.innerText.trim() === 'SUM(sum_boys)'), + ).to.not.equal(undefined); + }); }); }); - - it('should work with more than one groupby', () => { - verify({ - ...PIVOT_TABLE_FORM_DATA, - groupby: ['name', 'gender'], - }); - cy.get('.chart-container tr th').then((ths) => { - expect(ths.find(th => th.innerText.trim() === 'name')).to.not.equal(undefined); - expect(ths.find(th => th.innerText.trim() === 'gender')).to.not.equal(undefined); - }); - }); - - it('should work with multiple metrics', () => { - verify({ - ...PIVOT_TABLE_FORM_DATA, - metrics: ['sum__num', TEST_METRIC], - }); - cy.get('.chart-container tr th').then((ths) => { - expect(ths.find(th => th.innerText.trim() === 'sum__num')).to.not.equal(undefined); - expect(ths.find(th => th.innerText.trim() === 'SUM(sum_boys)')).to.not.equal(undefined); - }); - }); - - it('should work with multiple groupby and multiple metrics', () => { - verify({ - ...PIVOT_TABLE_FORM_DATA, - groupby: ['name', 'gender'], - metrics: ['sum__num', TEST_METRIC], - }); - cy.get('.chart-container tr th').then((ths) => { - expect(ths.find(th => th.innerText.trim() === 'name')).to.not.equal(undefined); - expect(ths.find(th => th.innerText.trim() === 'gender')).to.not.equal(undefined); - expect(ths.find(th => th.innerText.trim() === 'sum__num')).to.not.equal(undefined); - expect(ths.find(th => th.innerText.trim() === 'SUM(sum_boys)')).to.not.equal(undefined); - }); - }); -}); diff --git a/superset/assets/cypress/integration/explore/visualizations/sankey.js b/superset/assets/cypress/integration/explore/visualizations/sankey.js index 4e346364b5e..45b81bc840b 100644 --- a/superset/assets/cypress/integration/explore/visualizations/sankey.js +++ b/superset/assets/cypress/integration/explore/visualizations/sankey.js @@ -16,63 +16,65 @@ * specific language governing permissions and limitations * under the License. */ -export default () => describe('Sankey', () => { - const SANKEY_FORM_DATA = { - datasource: '1__table', - viz_type: 'sankey', - slice_id: 1, - url_params: {}, - granularity_sqla: null, - time_grain_sqla: 'P1D', - time_range: 'Last+week', - groupby: ['source', 'target'], - metric: 'sum__value', - adhoc_filters: [], - row_limit: '5000', - color_scheme: 'bnbColors', - }; +export default () => + describe('Sankey', () => { + const SANKEY_FORM_DATA = { + datasource: '1__table', + viz_type: 'sankey', + slice_id: 1, + url_params: {}, + granularity_sqla: null, + time_grain_sqla: 'P1D', + time_range: 'Last+week', + groupby: ['source', 'target'], + metric: 'sum__value', + adhoc_filters: [], + row_limit: '5000', + color_scheme: 'bnbColors', + }; - function verify(formData) { - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - } + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + } - beforeEach(() => { - cy.server(); - cy.login(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); - - it('should work', () => { - verify(SANKEY_FORM_DATA); - cy.get('.chart-container svg g.node rect').should('have.length', 41); - }); - - it('should work with filter', () => { - verify({ - ...SANKEY_FORM_DATA, - adhoc_filters: [ - { - expressionType: 'SQL', - sqlExpression: 'SUM(value)+>+0', - clause: 'HAVING', - subject: null, - operator: null, - comparator: null, - fromFormData: false, - filterOptionName: 'filter_jbdwe0hayaj_h9jfer8fy58', - }, { - expressionType: 'SIMPLE', - subject: 'source', - operator: '==', - comparator: 'Energy', - clause: 'WHERE', - sqlExpression: null, - fromFormData: true, - filterOptionName: 'filter_8e0otka9uif_vmqri4gmbqc', - }, - ], + beforeEach(() => { + cy.server(); + cy.login(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); + }); + + it('should work', () => { + verify(SANKEY_FORM_DATA); + cy.get('.chart-container svg g.node rect').should('have.length', 41); + }); + + it('should work with filter', () => { + verify({ + ...SANKEY_FORM_DATA, + adhoc_filters: [ + { + expressionType: 'SQL', + sqlExpression: 'SUM(value)+>+0', + clause: 'HAVING', + subject: null, + operator: null, + comparator: null, + fromFormData: false, + filterOptionName: 'filter_jbdwe0hayaj_h9jfer8fy58', + }, + { + expressionType: 'SIMPLE', + subject: 'source', + operator: '==', + comparator: 'Energy', + clause: 'WHERE', + sqlExpression: null, + fromFormData: true, + filterOptionName: 'filter_8e0otka9uif_vmqri4gmbqc', + }, + ], + }); + cy.get('.chart-container svg g.node rect').should('have.length', 6); }); - cy.get('.chart-container svg g.node rect').should('have.length', 6); }); -}); diff --git a/superset/assets/cypress/integration/explore/visualizations/sunburst.js b/superset/assets/cypress/integration/explore/visualizations/sunburst.js index e57c2e19c8e..fca07731b7a 100644 --- a/superset/assets/cypress/integration/explore/visualizations/sunburst.js +++ b/superset/assets/cypress/integration/explore/visualizations/sunburst.js @@ -16,69 +16,71 @@ * specific language governing permissions and limitations * under the License. */ -export default () => describe('Sunburst', () => { - const SUNBURST_FORM_DATA = { - datasource: '2__table', - viz_type: 'sunburst', - slice_id: 47, - granularity_sqla: 'year', - time_grain_sqla: 'P1D', - time_range: '2011-01-01+:+2011-01-01', - groupby: ['region'], - metric: 'sum__SP_POP_TOTL', - adhoc_filters: [], - row_limit: 50000, - color_scheme: 'bnbColors', - }; +export default () => + describe('Sunburst', () => { + const SUNBURST_FORM_DATA = { + datasource: '2__table', + viz_type: 'sunburst', + slice_id: 47, + granularity_sqla: 'year', + time_grain_sqla: 'P1D', + time_range: '2011-01-01+:+2011-01-01', + groupby: ['region'], + metric: 'sum__SP_POP_TOTL', + adhoc_filters: [], + row_limit: 50000, + color_scheme: 'bnbColors', + }; - function verify(formData) { - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - } + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + } - beforeEach(() => { - cy.server(); - cy.login(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); - - it('should work without secondary metric', () => { - verify(SUNBURST_FORM_DATA); - // There should be 7 visible arcs + 1 hidden - cy.get('.chart-container svg g#arcs path').should('have.length', 8); - }); - - it('should work with secondary metric', () => { - verify({ - ...SUNBURST_FORM_DATA, - secondary_metric: 'sum__SP_RUR_TOTL', + beforeEach(() => { + cy.server(); + cy.login(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); }); - cy.get('.chart-container svg g#arcs path').should('have.length', 8); - }); - it('should work with multiple groupbys', () => { - verify({ - ...SUNBURST_FORM_DATA, - groupby: ['region', 'country_name'], + it('should work without secondary metric', () => { + verify(SUNBURST_FORM_DATA); + // There should be 7 visible arcs + 1 hidden + cy.get('.chart-container svg g#arcs path').should('have.length', 8); }); - cy.get('.chart-container svg g#arcs path').should('have.length', 118); - }); - it('should work with filter', () => { - verify({ - ...SUNBURST_FORM_DATA, - adhoc_filters: [{ - expressionType: 'SIMPLE', - subject: 'region', - operator: 'in', - comparator: ['South Asia', 'North America'], - clause: 'WHERE', - sqlExpression: null, - fromFormData: true, - filterOptionName: 'filter_txje2ikiv6_wxmn0qwd1xo', - }], + it('should work with secondary metric', () => { + verify({ + ...SUNBURST_FORM_DATA, + secondary_metric: 'sum__SP_RUR_TOTL', + }); + cy.get('.chart-container svg g#arcs path').should('have.length', 8); }); - cy.get('.chart-container svg g#arcs path').should('have.length', 3); - }); -}); + it('should work with multiple groupbys', () => { + verify({ + ...SUNBURST_FORM_DATA, + groupby: ['region', 'country_name'], + }); + cy.get('.chart-container svg g#arcs path').should('have.length', 118); + }); + + it('should work with filter', () => { + verify({ + ...SUNBURST_FORM_DATA, + adhoc_filters: [ + { + expressionType: 'SIMPLE', + subject: 'region', + operator: 'in', + comparator: ['South Asia', 'North America'], + clause: 'WHERE', + sqlExpression: null, + fromFormData: true, + filterOptionName: 'filter_txje2ikiv6_wxmn0qwd1xo', + }, + ], + }); + cy.get('.chart-container svg g#arcs path').should('have.length', 3); + }); + }); diff --git a/superset/assets/cypress/integration/explore/visualizations/table.js b/superset/assets/cypress/integration/explore/visualizations/table.js index d2088b3a302..b9294ceaeba 100644 --- a/superset/assets/cypress/integration/explore/visualizations/table.js +++ b/superset/assets/cypress/integration/explore/visualizations/table.js @@ -21,129 +21,153 @@ import readResponseBlob from '../../../utils/readResponseBlob'; // Table -export default () => describe('Table chart', () => { - const VIZ_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'table' }; +export default () => + describe('Table chart', () => { + const VIZ_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'table' }; - beforeEach(() => { - cy.login(); - cy.server(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); + beforeEach(() => { + cy.login(); + cy.server(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); + }); - it('Test table with adhoc metric', () => { - const formData = { ...VIZ_DEFAULTS, metrics: NUM_METRIC }; + it('Test table with adhoc metric', () => { + const formData = { ...VIZ_DEFAULTS, metrics: NUM_METRIC }; - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ - waitAlias: '@getJson', - querySubstring: NUM_METRIC.label, - chartSelector: 'table', + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + querySubstring: NUM_METRIC.label, + chartSelector: 'table', + }); + }); + + it('Test table with groupby', () => { + const formData = { + ...VIZ_DEFAULTS, + metrics: NUM_METRIC, + groupby: ['name'], + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + querySubstring: formData.groupby[0], + chartSelector: 'table', + }); + }); + + it('Test table with percent metrics and groupby', () => { + const formData = { + ...VIZ_DEFAULTS, + percent_metrics: NUM_METRIC, + metrics: [], + groupby: ['name'], + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' }); + }); + + it('Test table with groupby order desc', () => { + const formData = { + ...VIZ_DEFAULTS, + metrics: NUM_METRIC, + groupby: ['name'], + order_desc: true, + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' }); + }); + + it('Test table with groupby and limit', () => { + const limit = 10; + + const formData = { + ...VIZ_DEFAULTS, + metrics: NUM_METRIC, + groupby: ['name'], + row_limit: limit, + }; + + cy.visitChartByParams(JSON.stringify(formData)); + + cy.wait('@getJson').then(async xhr => { + cy.verifyResponseCodes(xhr); + cy.verifySliceContainer('table'); + const responseBody = await readResponseBlob(xhr.response.body); + expect(responseBody.data.records.length).to.eq(limit); + }); + cy.get('span.label-danger').contains('10 rows'); + }); + + it('Test table with columns and row limit', () => { + const formData = { + ...VIZ_DEFAULTS, + all_columns: ['name'], + metrics: [], + row_limit: 10, + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' }); + }); + + it('Test table with columns, ordering, and row limit', () => { + const limit = 10; + + const formData = { + ...VIZ_DEFAULTS, + all_columns: ['name', 'state', 'ds', 'num'], + metrics: [], + row_limit: limit, + order_by_cols: ['["num",+false]'], + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.wait('@getJson').then(async xhr => { + cy.verifyResponseCodes(xhr); + cy.verifySliceContainer('table'); + const responseBody = await readResponseBlob(xhr.response.body); + const { records } = responseBody.data; + expect(records[0].num).greaterThan(records[records.length - 1].num); + }); + }); + + it('Test table with simple filter', () => { + const metrics = ['count']; + const filters = [SIMPLE_FILTER]; + + const formData = { ...VIZ_DEFAULTS, metrics, adhoc_filters: filters }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' }); + }); + + it('Tests table number formatting with % in metric name', () => { + const PERCENT_METRIC = { + expressionType: 'SQL', + sqlExpression: 'CAST(SUM(sum_girls)+AS+FLOAT)/SUM(num)', + column: null, + aggregate: null, + hasCustomLabel: true, + fromFormData: true, + label: '%+Girls', + optionName: 'metric_6qwzgc8bh2v_zox7hil1mzs', + }; + const formData = { + ...VIZ_DEFAULTS, + metrics: PERCENT_METRIC, + groupby: ['state'], + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + querySubstring: formData.groupby[0], + chartSelector: 'table', + }); + cy.get('td').contains(/\d*%/); }); }); - - it('Test table with groupby', () => { - const formData = { ...VIZ_DEFAULTS, metrics: NUM_METRIC, groupby: ['name'] }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ - waitAlias: '@getJson', - querySubstring: formData.groupby[0], - chartSelector: 'table', - }); - }); - - it('Test table with percent metrics and groupby', () => { - const formData = { - ...VIZ_DEFAULTS, - percent_metrics: NUM_METRIC, - metrics: [], - groupby: ['name'], - }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' }); - }); - - it('Test table with groupby order desc', () => { - const formData = { ...VIZ_DEFAULTS, metrics: NUM_METRIC, groupby: ['name'], order_desc: true }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' }); - }); - - it('Test table with groupby and limit', () => { - const limit = 10; - - const formData = { ...VIZ_DEFAULTS, metrics: NUM_METRIC, groupby: ['name'], row_limit: limit }; - - cy.visitChartByParams(JSON.stringify(formData)); - - cy.wait('@getJson').then(async (xhr) => { - cy.verifyResponseCodes(xhr); - cy.verifySliceContainer('table'); - const responseBody = await readResponseBlob(xhr.response.body); - expect(responseBody.data.records.length).to.eq(limit); - }); - cy.get('span.label-danger').contains('10 rows'); - }); - - it('Test table with columns and row limit', () => { - const formData = { ...VIZ_DEFAULTS, all_columns: ['name'], metrics: [], row_limit: 10 }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' }); - }); - - it('Test table with columns, ordering, and row limit', () => { - const limit = 10; - - const formData = { - ...VIZ_DEFAULTS, - all_columns: ['name', 'state', 'ds', 'num'], - metrics: [], - row_limit: limit, - order_by_cols: ['["num",+false]'], - }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.wait('@getJson').then(async (xhr) => { - cy.verifyResponseCodes(xhr); - cy.verifySliceContainer('table'); - const responseBody = await readResponseBlob(xhr.response.body); - const { records } = responseBody.data; - expect(records[0].num).greaterThan(records[records.length - 1].num); - }); - }); - - it('Test table with simple filter', () => { - const metrics = ['count']; - const filters = [SIMPLE_FILTER]; - - const formData = { ...VIZ_DEFAULTS, metrics, adhoc_filters: filters }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' }); - }); - - it('Tests table number formatting with % in metric name', () => { - const PERCENT_METRIC = { - expressionType: 'SQL', - sqlExpression: 'CAST(SUM(sum_girls)+AS+FLOAT)/SUM(num)', - column: null, - aggregate: null, - hasCustomLabel: true, - fromFormData: true, - label: '%+Girls', - optionName: 'metric_6qwzgc8bh2v_zox7hil1mzs', - }; - const formData = { ...VIZ_DEFAULTS, metrics: PERCENT_METRIC, groupby: ['state'] }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ - waitAlias: '@getJson', - querySubstring: formData.groupby[0], - chartSelector: 'table', - }); - cy.get('td').contains(/\d*%/); - }); -}); diff --git a/superset/assets/cypress/integration/explore/visualizations/time_table.js b/superset/assets/cypress/integration/explore/visualizations/time_table.js index 84cfe05cb75..6cd4b87f08c 100644 --- a/superset/assets/cypress/integration/explore/visualizations/time_table.js +++ b/superset/assets/cypress/integration/explore/visualizations/time_table.js @@ -20,87 +20,116 @@ import { FORM_DATA_DEFAULTS, NUM_METRIC } from './shared.helper'; // time table viz -export default () => describe('Time Table Viz', () => { - const VIZ_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'time_table' }; +export default () => + describe('Time Table Viz', () => { + const VIZ_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'time_table' }; - beforeEach(() => { - cy.login(); - cy.server(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); - - it('Test time series table multiple metrics last year total', () => { - const formData = { - ...VIZ_DEFAULTS, - metrics: [NUM_METRIC, 'count'], - column_collection: [{ - key: '9g4K-B-YL', - label: 'Last+Year', - colType: 'time', - timeLag: '1', - comparisonType: 'value', - }], - url: '', - }; - - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', querySubstring: NUM_METRIC.label }); - cy.get('.time-table').within(() => { - cy.get('span').contains('Sum(num)'); - cy.get('span').contains('COUNT(*)'); + beforeEach(() => { + cy.login(); + cy.server(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); }); - }); - it('Test time series table metric and group by last year total', () => { - const formData = { - ...VIZ_DEFAULTS, - metrics: [NUM_METRIC], - groupby: ['gender'], - column_collection: [{ - key: '9g4K-B-YL', - label: 'Last+Year', - colType: 'time', - timeLag: '1', - comparisonType: 'value', - }], - url: '', - }; + it('Test time series table multiple metrics last year total', () => { + const formData = { + ...VIZ_DEFAULTS, + metrics: [NUM_METRIC, 'count'], + column_collection: [ + { + key: '9g4K-B-YL', + label: 'Last+Year', + colType: 'time', + timeLag: '1', + comparisonType: 'value', + }, + ], + url: '', + }; - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', querySubstring: NUM_METRIC.label }); - cy.get('.time-table').within(() => { - cy.get('td').contains('boy'); - cy.get('td').contains('girl'); + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + querySubstring: NUM_METRIC.label, + }); + cy.get('.time-table').within(() => { + cy.get('span').contains('Sum(num)'); + cy.get('span').contains('COUNT(*)'); + }); }); - }); - it('Test time series various time columns', () => { - const formData = { - ...VIZ_DEFAULTS, - metrics: [NUM_METRIC, 'count'], - column_collection: [ - { key: 'LHHNPhamU', label: 'Current', colType: 'time', timeLag: 0 }, - { key: '9g4K-B-YL', label: 'Last Year', colType: 'time', timeLag: '1', comparisonType: 'value' }, - { key: 'JVZXtNu7_', label: 'YoY', colType: 'time', timeLag: 1, comparisonType: 'perc', d3format: '%' }, - { key: 'tN5Gba36u', label: 'Trend', colType: 'spark' }, - ], - url: '', - }; + it('Test time series table metric and group by last year total', () => { + const formData = { + ...VIZ_DEFAULTS, + metrics: [NUM_METRIC], + groupby: ['gender'], + column_collection: [ + { + key: '9g4K-B-YL', + label: 'Last+Year', + colType: 'time', + timeLag: '1', + comparisonType: 'value', + }, + ], + url: '', + }; - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', querySubstring: NUM_METRIC.label }); - cy.get('.time-table').within(() => { - cy.get('th').contains('Current'); - cy.get('th').contains('Last Year'); - cy.get('th').contains('YoY'); - cy.get('th').contains('Trend'); + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + querySubstring: NUM_METRIC.label, + }); + cy.get('.time-table').within(() => { + cy.get('td').contains('boy'); + cy.get('td').contains('girl'); + }); + }); - cy.get('span').contains('%'); - cy.get('svg').first().then((charts) => { - const firstChart = charts[0]; - expect(firstChart.clientWidth).greaterThan(0); - expect(firstChart.clientHeight).greaterThan(0); + it('Test time series various time columns', () => { + const formData = { + ...VIZ_DEFAULTS, + metrics: [NUM_METRIC, 'count'], + column_collection: [ + { key: 'LHHNPhamU', label: 'Current', colType: 'time', timeLag: 0 }, + { + key: '9g4K-B-YL', + label: 'Last Year', + colType: 'time', + timeLag: '1', + comparisonType: 'value', + }, + { + key: 'JVZXtNu7_', + label: 'YoY', + colType: 'time', + timeLag: 1, + comparisonType: 'perc', + d3format: '%', + }, + { key: 'tN5Gba36u', label: 'Trend', colType: 'spark' }, + ], + url: '', + }; + + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ + waitAlias: '@getJson', + querySubstring: NUM_METRIC.label, + }); + cy.get('.time-table').within(() => { + cy.get('th').contains('Current'); + cy.get('th').contains('Last Year'); + cy.get('th').contains('YoY'); + cy.get('th').contains('Trend'); + + cy.get('span').contains('%'); + cy.get('svg') + .first() + .then(charts => { + const firstChart = charts[0]; + expect(firstChart.clientWidth).greaterThan(0); + expect(firstChart.clientHeight).greaterThan(0); + }); }); }); }); -}); diff --git a/superset/assets/cypress/integration/explore/visualizations/treemap.js b/superset/assets/cypress/integration/explore/visualizations/treemap.js index 83c1a99c46a..16560aa0ef3 100644 --- a/superset/assets/cypress/integration/explore/visualizations/treemap.js +++ b/superset/assets/cypress/integration/explore/visualizations/treemap.js @@ -16,62 +16,65 @@ * specific language governing permissions and limitations * under the License. */ -export default () => describe('Treemap', () => { - const TREEMAP_FORM_DATA = { - datasource: '2__table', - viz_type: 'treemap', - slice_id: 50, - granularity_sqla: 'year', - time_grain_sqla: 'P1D', - time_range: '1960-01-01+:+now', - metrics: ['sum__SP_POP_TOTL'], - adhoc_filters: [], - groupby: ['country_code'], - row_limit: 50000, - color_scheme: 'bnbColors', - treemap_ratio: 1.618033988749895, - number_format: '.3s', - }; +export default () => + describe('Treemap', () => { + const TREEMAP_FORM_DATA = { + datasource: '2__table', + viz_type: 'treemap', + slice_id: 50, + granularity_sqla: 'year', + time_grain_sqla: 'P1D', + time_range: '1960-01-01+:+now', + metrics: ['sum__SP_POP_TOTL'], + adhoc_filters: [], + groupby: ['country_code'], + row_limit: 50000, + color_scheme: 'bnbColors', + treemap_ratio: 1.618033988749895, + number_format: '.3s', + }; - function verify(formData) { - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - } + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + } - beforeEach(() => { - cy.server(); - cy.login(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); - - it('should work', () => { - verify(TREEMAP_FORM_DATA); - cy.get('.chart-container svg rect.child').should('have.length', 214); - }); - - it('should work with multiple groupby', () => { - verify({ - ...TREEMAP_FORM_DATA, - groupby: ['region', 'country_code'], + beforeEach(() => { + cy.server(); + cy.login(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); }); - cy.get('.chart-container svg rect.parent').should('have.length', 7); - cy.get('.chart-container svg rect.child').should('have.length', 214); - }); - it('should work with filter', () => { - verify({ - ...TREEMAP_FORM_DATA, - adhoc_filters: [{ - expressionType: 'SIMPLE', - subject: 'region', - operator: '==', - comparator: 'South Asia', - clause: 'WHERE', - sqlExpression: null, - fromFormData: true, - filterOptionName: 'filter_8aqxcf5co1a_x7lm2d1fq0l', - }], + it('should work', () => { + verify(TREEMAP_FORM_DATA); + cy.get('.chart-container svg rect.child').should('have.length', 214); + }); + + it('should work with multiple groupby', () => { + verify({ + ...TREEMAP_FORM_DATA, + groupby: ['region', 'country_code'], + }); + cy.get('.chart-container svg rect.parent').should('have.length', 7); + cy.get('.chart-container svg rect.child').should('have.length', 214); + }); + + it('should work with filter', () => { + verify({ + ...TREEMAP_FORM_DATA, + adhoc_filters: [ + { + expressionType: 'SIMPLE', + subject: 'region', + operator: '==', + comparator: 'South Asia', + clause: 'WHERE', + sqlExpression: null, + fromFormData: true, + filterOptionName: 'filter_8aqxcf5co1a_x7lm2d1fq0l', + }, + ], + }); + cy.get('.chart-container svg rect.child').should('have.length', 8); }); - cy.get('.chart-container svg rect.child').should('have.length', 8); }); -}); diff --git a/superset/assets/cypress/integration/explore/visualizations/world_map.js b/superset/assets/cypress/integration/explore/visualizations/world_map.js index 6eac41fb23d..6db348b2d61 100644 --- a/superset/assets/cypress/integration/explore/visualizations/world_map.js +++ b/superset/assets/cypress/integration/explore/visualizations/world_map.js @@ -16,65 +16,71 @@ * specific language governing permissions and limitations * under the License. */ -export default () => describe('World Map', () => { - const WORLD_MAP_FORM_DATA = { - datasource: '2__table', - viz_type: 'world_map', - slice_id: 45, - granularity_sqla: 'year', - time_grain_sqla: 'P1D', - time_range: '2014-01-01 : 2014-01-02', - entity: 'country_code', - country_fieldtype: 'cca3', - metric: 'sum__SP_RUR_TOTL_ZS', - adhoc_filters: [], - row_limit: 50000, - show_bubbles: true, - secondary_metric: 'sum__SP_POP_TOTL', - max_bubble_size: '25', - }; +export default () => + describe('World Map', () => { + const WORLD_MAP_FORM_DATA = { + datasource: '2__table', + viz_type: 'world_map', + slice_id: 45, + granularity_sqla: 'year', + time_grain_sqla: 'P1D', + time_range: '2014-01-01 : 2014-01-02', + entity: 'country_code', + country_fieldtype: 'cca3', + metric: 'sum__SP_RUR_TOTL_ZS', + adhoc_filters: [], + row_limit: 50000, + show_bubbles: true, + secondary_metric: 'sum__SP_POP_TOTL', + max_bubble_size: '25', + }; - function verify(formData) { - cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); - } + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + } - beforeEach(() => { - cy.server(); - cy.login(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); - }); - - it('should work with ad-hoc metric', () => { - verify(WORLD_MAP_FORM_DATA); - cy.get('.bubbles circle.datamaps-bubble').should('have.length', 206); - }); - - it('should work with simple filter', () => { - verify({ - ...WORLD_MAP_FORM_DATA, - metric: 'count', - adhoc_filters: [{ - expressionType: 'SIMPLE', - subject: 'region', - operator: '==', - comparator: 'South Asia', - clause: 'WHERE', - sqlExpression: null, - fromFormData: true, - filterOptionName: 'filter_8aqxcf5co1a_x7lm2d1fq0l', - }], + beforeEach(() => { + cy.server(); + cy.login(); + cy.route('POST', '/superset/explore_json/**').as('getJson'); }); - cy.get('.bubbles circle.datamaps-bubble').should('have.length', 8); - }); - it('should hide bubbles when told so', () => { - verify({ - ...WORLD_MAP_FORM_DATA, - show_bubbles: false, + it('should work with ad-hoc metric', () => { + verify(WORLD_MAP_FORM_DATA); + cy.get('.bubbles circle.datamaps-bubble').should('have.length', 206); }); - cy.get('.slice_container').then((containers) => { - expect(containers[0].querySelectorAll('.bubbles circle.datamaps-bubble').length).to.equal(0); + + it('should work with simple filter', () => { + verify({ + ...WORLD_MAP_FORM_DATA, + metric: 'count', + adhoc_filters: [ + { + expressionType: 'SIMPLE', + subject: 'region', + operator: '==', + comparator: 'South Asia', + clause: 'WHERE', + sqlExpression: null, + fromFormData: true, + filterOptionName: 'filter_8aqxcf5co1a_x7lm2d1fq0l', + }, + ], + }); + cy.get('.bubbles circle.datamaps-bubble').should('have.length', 8); + }); + + it('should hide bubbles when told so', () => { + verify({ + ...WORLD_MAP_FORM_DATA, + show_bubbles: false, + }); + cy.get('.slice_container').then(containers => { + expect( + containers[0].querySelectorAll('.bubbles circle.datamaps-bubble') + .length, + ).to.equal(0); + }); }); }); -}); diff --git a/superset/assets/cypress/integration/sqllab/query.js b/superset/assets/cypress/integration/sqllab/query.js index cf698f46f6a..df34631c4f0 100644 --- a/superset/assets/cypress/integration/sqllab/query.js +++ b/superset/assets/cypress/integration/sqllab/query.js @@ -37,9 +37,9 @@ export default () => { cy.get('#brace-editor textarea') .clear({ force: true }) .type( - `{selectall}{backspace}SELECT ds, gender, name, num FROM main.birth_names LIMIT ${rowLimit}`, - { force: true }, - ); + `{selectall}{backspace}SELECT ds, gender, name, num FROM main.birth_names LIMIT ${rowLimit}`, + { force: true }, + ); cy.get('#js-sql-toolbar button') .eq(0) .click(); @@ -48,7 +48,7 @@ export default () => { selectResultsTab() .eq(0) // ensures results tab in case preview tab exists - .then((tableNodes) => { + .then(tableNodes => { const [header, bodyWrapper] = tableNodes[0].childNodes; const body = bodyWrapper.childNodes[0]; const expectedColCount = header.childNodes.length; @@ -62,7 +62,8 @@ export default () => { cy.route('savedqueryviewapi/**').as('getSavedQuery'); cy.route('superset/tables/**').as('getTables'); - const query = 'SELECT ds, gender, name, num FROM main.birth_names ORDER BY name LIMIT 3'; + const query = + 'SELECT ds, gender, name, num FROM main.birth_names ORDER BY name LIMIT 3'; const savedQueryTitle = `CYPRESS TEST QUERY ${shortid.generate()}`; // we will assert that the results of the query we save, and the saved query are the same @@ -81,7 +82,7 @@ export default () => { cy.wait('@sqlLabQuery'); // Save results to check agains below - selectResultsTab().then((resultsA) => { + selectResultsTab().then(resultsA => { initialResultsTable = resultsA[0]; }); @@ -93,8 +94,8 @@ export default () => { cy.get('.modal-sm input') .clear({ force: true }) .type(`{selectall}{backspace}${savedQueryTitle}`, { - force: true, - }); + force: true, + }); cy.get('.modal-sm .modal-body button') .eq(0) // save @@ -117,10 +118,13 @@ export default () => { cy.wait('@sqlLabQuery'); // assert the results of the saved query match the initial results - selectResultsTab().then((resultsB) => { + selectResultsTab().then(resultsB => { savedQueryResultsTable = resultsB[0]; - assertSQLLabResultsAreEqual(initialResultsTable, savedQueryResultsTable); + assertSQLLabResultsAreEqual( + initialResultsTable, + savedQueryResultsTable, + ); }); }); }); diff --git a/superset/assets/cypress/integration/sqllab/sourcePanel.js b/superset/assets/cypress/integration/sqllab/sourcePanel.js index df15334c2f9..ef977b2f7f5 100644 --- a/superset/assets/cypress/integration/sqllab/sourcePanel.js +++ b/superset/assets/cypress/integration/sqllab/sourcePanel.js @@ -35,7 +35,9 @@ export default () => { cy.get('.sql-toolbar .Select').should('have.length', 3); cy.get('.sql-toolbar .table-schema').should('not.exist'); - cy.get('.SouthPane .tab-content .filterable-table-container').should('not.exist'); + cy.get('.SouthPane .tab-content .filterable-table-container').should( + 'not.exist', + ); cy.get('.sql-toolbar .Select') .eq(0) // database select diff --git a/superset/assets/cypress/integration/sqllab/tabs.js b/superset/assets/cypress/integration/sqllab/tabs.js index 0d9731ef982..2fc102e241d 100644 --- a/superset/assets/cypress/integration/sqllab/tabs.js +++ b/superset/assets/cypress/integration/sqllab/tabs.js @@ -25,7 +25,7 @@ export default () => { }); it('allows you to create a tab', () => { - cy.get('#a11y-query-editor-tabs > ul > li').then((tabList) => { + cy.get('#a11y-query-editor-tabs > ul > li').then(tabList => { const initialTabCount = tabList.length; // add tab @@ -33,23 +33,31 @@ export default () => { .last() .click(); - cy.get('#a11y-query-editor-tabs > ul > li').should('have.length', initialTabCount + 1); + cy.get('#a11y-query-editor-tabs > ul > li').should( + 'have.length', + initialTabCount + 1, + ); }); }); it('allows you to close a tab', () => { - cy.get('#a11y-query-editor-tabs > ul > li').then((tabListA) => { + cy.get('#a11y-query-editor-tabs > ul > li').then(tabListA => { const initialTabCount = tabListA.length; // open the tab dropdown to remove - cy.get('#a11y-query-editor-tabs > ul > li:first button:nth-child(2)').click(); + cy.get( + '#a11y-query-editor-tabs > ul > li:first button:nth-child(2)', + ).click(); // first item is close cy.get('#a11y-query-editor-tabs > ul > li:first ul li a') .eq(0) .click(); - cy.get('#a11y-query-editor-tabs > ul > li').should('have.length', initialTabCount - 1); + cy.get('#a11y-query-editor-tabs > ul > li').should( + 'have.length', + initialTabCount - 1, + ); }); }); }); diff --git a/superset/assets/cypress/support/commands.js b/superset/assets/cypress/support/commands.js index dd3be98216f..1c12e4fb20d 100644 --- a/superset/assets/cypress/support/commands.js +++ b/superset/assets/cypress/support/commands.js @@ -51,26 +51,26 @@ Cypress.Commands.add('login', () => { method: 'POST', url: '/login/', body: { username: 'admin', password: 'general' }, - }).then((response) => { + }).then(response => { expect(response.status).to.eq(200); }); }); -Cypress.Commands.add('visitChartByName', (name) => { - cy.request(`/chart/api/read?_flt_3_slice_name=${name}`).then((response) => { +Cypress.Commands.add('visitChartByName', name => { + cy.request(`/chart/api/read?_flt_3_slice_name=${name}`).then(response => { cy.visit(`${BASE_EXPLORE_URL}{"slice_id": ${response.body.pks[0]}}`); }); }); -Cypress.Commands.add('visitChartById', (chartId) => { +Cypress.Commands.add('visitChartById', chartId => { cy.visit(`${BASE_EXPLORE_URL}{"slice_id": ${chartId}}`); }); -Cypress.Commands.add('visitChartByParams', (params) => { +Cypress.Commands.add('visitChartByParams', params => { cy.visit(`${BASE_EXPLORE_URL}${params}`); }); -Cypress.Commands.add('verifyResponseCodes', async (xhr) => { +Cypress.Commands.add('verifyResponseCodes', async xhr => { // After a wait response check for valid response expect(xhr.status).to.eq(200); @@ -81,11 +81,11 @@ Cypress.Commands.add('verifyResponseCodes', async (xhr) => { } }); -Cypress.Commands.add('verifySliceContainer', (chartSelector) => { +Cypress.Commands.add('verifySliceContainer', chartSelector => { // After a wait response check for valid slice container cy.get('.slice_container').within(() => { if (chartSelector) { - cy.get(chartSelector).then((charts) => { + cy.get(chartSelector).then(charts => { const firstChart = charts[0]; expect(firstChart.clientWidth).greaterThan(0); expect(firstChart.clientHeight).greaterThan(0); @@ -94,15 +94,18 @@ Cypress.Commands.add('verifySliceContainer', (chartSelector) => { }); }); -Cypress.Commands.add('verifySliceSuccess', ({ waitAlias, querySubstring, chartSelector }) => { - cy.wait(waitAlias).then(async (xhr) => { - cy.verifyResponseCodes(xhr); +Cypress.Commands.add( + 'verifySliceSuccess', + ({ waitAlias, querySubstring, chartSelector }) => { + cy.wait(waitAlias).then(async xhr => { + cy.verifyResponseCodes(xhr); - const responseBody = await readResponseBlob(xhr.response.body); - if (querySubstring) { - expect(responseBody.query).contains(querySubstring); - } + const responseBody = await readResponseBlob(xhr.response.body); + if (querySubstring) { + expect(responseBody.query).contains(querySubstring); + } - cy.verifySliceContainer(chartSelector); - }); -}); + cy.verifySliceContainer(chartSelector); + }); + }, +); diff --git a/superset/assets/cypress/support/index.js b/superset/assets/cypress/support/index.js index 68b580a2564..9ff4b7b1a48 100644 --- a/superset/assets/cypress/support/index.js +++ b/superset/assets/cypress/support/index.js @@ -36,6 +36,6 @@ import './commands'; // The following is a workaround for Cypress not supporting fetch. // By setting window.fetch = null, we force the fetch polyfill to fall back // to xhr as described here https://github.com/cypress-io/cypress/issues/95 -Cypress.on('window:before:load', (win) => { +Cypress.on('window:before:load', win => { win.fetch = null; // eslint-disable-line no-param-reassign }); diff --git a/superset/assets/cypress/utils/readResponseBlob.js b/superset/assets/cypress/utils/readResponseBlob.js index 429485a8a64..6cf077b37ab 100644 --- a/superset/assets/cypress/utils/readResponseBlob.js +++ b/superset/assets/cypress/utils/readResponseBlob.js @@ -21,7 +21,7 @@ // and that the response can be parsed as JSON. This is needed to read // the value of any fetch-based response. export default function readResponseBlob(blob) { - return new Promise((resolve) => { + return new Promise(resolve => { const reader = new FileReader(); reader.onload = () => resolve(JSON.parse(reader.result)); reader.readAsText(blob); diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index d53f9b7e212..4ab9b1583cd 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -19894,9 +19894,9 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, "prettier": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.15.3.tgz", - "integrity": "sha512-gAU9AGAPMaKb3NNSUUuhhFAS7SCO4ALTN4nRIn6PJ075Qd28Yn2Ig2ahEJWdJwJmlEBTUfC7mMUSFy8MwsOCfg==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", "dev": true }, "pretty-format": { diff --git a/superset/assets/package.json b/superset/assets/package.json index 59c29c58630..b82d2d3c44f 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -200,7 +200,7 @@ "minimist": "^1.2.0", "optimize-css-assets-webpack-plugin": "^5.0.1", "po2json": "^0.4.5", - "prettier": "^1.12.1", + "prettier": "^1.19.1", "react-test-renderer": "^16.9.0", "redux-mock-store": "^1.2.3", "sinon": "^4.5.0", diff --git a/superset/assets/spec/helpers/shim.js b/superset/assets/spec/helpers/shim.js index 06ecc790b6f..b6ca1b92d9f 100644 --- a/superset/assets/spec/helpers/shim.js +++ b/superset/assets/spec/helpers/shim.js @@ -36,7 +36,7 @@ global.document = global.jsdom(''); global.window = document.defaultView; global.HTMLElement = window.HTMLElement; -Object.keys(document.defaultView).forEach((property) => { +Object.keys(document.defaultView).forEach(property => { if (typeof global[property] === 'undefined') { exposedProperties.push(property); global[property] = document.defaultView[property]; diff --git a/superset/assets/spec/javascripts/CRUD/CollectionTable_spec.jsx b/superset/assets/spec/javascripts/CRUD/CollectionTable_spec.jsx index 0feb2cd120c..119947a46d2 100644 --- a/superset/assets/spec/javascripts/CRUD/CollectionTable_spec.jsx +++ b/superset/assets/spec/javascripts/CRUD/CollectionTable_spec.jsx @@ -28,7 +28,6 @@ const props = { }; describe('CollectionTable', () => { - let wrapper; let el; @@ -46,5 +45,4 @@ describe('CollectionTable', () => { expect(wrapper.find('table')).toHaveLength(1); expect(wrapper.find('tbody tr.row')).toHaveLength(length); }); - }); diff --git a/superset/assets/spec/javascripts/addSlice/AddSliceContainer_spec.jsx b/superset/assets/spec/javascripts/addSlice/AddSliceContainer_spec.jsx index e16b9889378..a399d22e782 100644 --- a/superset/assets/spec/javascripts/addSlice/AddSliceContainer_spec.jsx +++ b/superset/assets/spec/javascripts/addSlice/AddSliceContainer_spec.jsx @@ -52,7 +52,12 @@ describe('AddSliceContainer', () => { }); it('renders a disabled button if no datasource is selected', () => { - expect(wrapper.find(Button).dive().find('.btn[disabled=true]')).toHaveLength(1); + expect( + wrapper + .find(Button) + .dive() + .find('.btn[disabled=true]'), + ).toHaveLength(1); }); it('renders an enabled button if datasource is selected', () => { @@ -62,7 +67,12 @@ describe('AddSliceContainer', () => { datasourceId: datasourceValue.split('__')[0], datasourceType: datasourceValue.split('__')[1], }); - expect(wrapper.find(Button).dive().find('.btn[disabled=false]')).toHaveLength(1); + expect( + wrapper + .find(Button) + .dive() + .find('.btn[disabled=false]'), + ).toHaveLength(1); }); it('formats explore url', () => { @@ -72,7 +82,8 @@ describe('AddSliceContainer', () => { datasourceId: datasourceValue.split('__')[0], datasourceType: datasourceValue.split('__')[1], }); - const formattedUrl = '/superset/explore/?form_data=%7B%22viz_type%22%3A%22table%22%2C%22datasource%22%3A%221__table%22%7D'; + const formattedUrl = + '/superset/explore/?form_data=%7B%22viz_type%22%3A%22table%22%2C%22datasource%22%3A%221__table%22%7D'; expect(wrapper.instance().exploreUrl()).toBe(formattedUrl); }); }); diff --git a/superset/assets/spec/javascripts/chart/chartActions_spec.js b/superset/assets/spec/javascripts/chart/chartActions_spec.js index a4a832632e3..56e8cfb6386 100644 --- a/superset/assets/spec/javascripts/chart/chartActions_spec.js +++ b/superset/assets/spec/javascripts/chart/chartActions_spec.js @@ -117,7 +117,9 @@ describe('chart actions', () => { it('should CHART_UPDATE_TIMEOUT action upon query timeout', () => { const unresolvingPromise = new Promise(() => {}); - fetchMock.post(MOCK_URL, () => unresolvingPromise, { overwriteRoutes: true }); + fetchMock.post(MOCK_URL, () => unresolvingPromise, { + overwriteRoutes: true, + }); const timeoutInSec = 1 / 1000; const actionThunk = actions.postChartFormData({}, false, timeoutInSec); @@ -133,7 +135,11 @@ describe('chart actions', () => { }); it('should dispatch CHART_UPDATE_FAILED action upon non-timeout non-abort failure', () => { - fetchMock.post(MOCK_URL, { throws: { statusText: 'misc error' } }, { overwriteRoutes: true }); + fetchMock.post( + MOCK_URL, + { throws: { statusText: 'misc error' } }, + { overwriteRoutes: true }, + ); const timeoutInSec = 1 / 1000; const actionThunk = actions.postChartFormData({}, false, timeoutInSec); diff --git a/superset/assets/spec/javascripts/chart/chartReducers_spec.js b/superset/assets/spec/javascripts/chart/chartReducers_spec.js index fdf2ed768b1..f2f83c8962d 100644 --- a/superset/assets/spec/javascripts/chart/chartReducers_spec.js +++ b/superset/assets/spec/javascripts/chart/chartReducers_spec.js @@ -19,7 +19,6 @@ import chartReducer, { chart } from '../../../src/chart/chartReducer'; import * as actions from '../../../src/chart/chartAction'; - describe('chart reducers', () => { const chartKey = 1; let testChart; @@ -39,7 +38,10 @@ describe('chart reducers', () => { }); it('should update endtime on timeout', () => { - const newState = chartReducer(charts, actions.chartUpdateTimeout('timeout', 60, chartKey)); + const newState = chartReducer( + charts, + actions.chartUpdateTimeout('timeout', 60, chartKey), + ); expect(newState[chartKey].chartUpdateEndTime).toBeGreaterThan(0); expect(newState[chartKey].chartStatus).toEqual('failed'); }); diff --git a/superset/assets/spec/javascripts/components/AlteredSliceTag_spec.jsx b/superset/assets/spec/javascripts/components/AlteredSliceTag_spec.jsx index 51f0002497d..63902ab574c 100644 --- a/superset/assets/spec/javascripts/components/AlteredSliceTag_spec.jsx +++ b/superset/assets/spec/javascripts/components/AlteredSliceTag_spec.jsx @@ -164,24 +164,32 @@ describe('AlteredSliceTag', () => { describe('renderTriggerNode', () => { it('renders a TooltipWrapper', () => { - const triggerNode = shallow(
{wrapper.instance().renderTriggerNode()}
); + const triggerNode = shallow( +
{wrapper.instance().renderTriggerNode()}
, + ); expect(triggerNode.find(TooltipWrapper)).toHaveLength(1); }); }); describe('renderModalBody', () => { it('renders a Table', () => { - const modalBody = shallow(
{wrapper.instance().renderModalBody()}
); + const modalBody = shallow( +
{wrapper.instance().renderModalBody()}
, + ); expect(modalBody.find(Table)).toHaveLength(1); }); it('renders a Thead', () => { - const modalBody = shallow(
{wrapper.instance().renderModalBody()}
); + const modalBody = shallow( +
{wrapper.instance().renderModalBody()}
, + ); expect(modalBody.find(Thead)).toHaveLength(1); }); it('renders Th', () => { - const modalBody = shallow(
{wrapper.instance().renderModalBody()}
); + const modalBody = shallow( +
{wrapper.instance().renderModalBody()}
, + ); const th = modalBody.find(Th); expect(th).toHaveLength(3); ['control', 'before', 'after'].forEach((v, i) => { @@ -190,13 +198,17 @@ describe('AlteredSliceTag', () => { }); it('renders the correct number of Tr', () => { - const modalBody = shallow(
{wrapper.instance().renderModalBody()}
); + const modalBody = shallow( +
{wrapper.instance().renderModalBody()}
, + ); const tr = modalBody.find(Tr); expect(tr).toHaveLength(7); }); it('renders the correct number of Td', () => { - const modalBody = shallow(
{wrapper.instance().renderModalBody()}
); + const modalBody = shallow( +
{wrapper.instance().renderModalBody()}
, + ); const td = modalBody.find(Td); expect(td).toHaveLength(21); ['control', 'before', 'after'].forEach((v, i) => { @@ -225,13 +237,20 @@ describe('AlteredSliceTag', () => { }); it('returns "Max" and "Min" for BoundsControl', () => { - expect(wrapper.instance().formatValue([5, 6], 'y_axis_bounds')).toBe('Min: 5, Max: 6'); + expect(wrapper.instance().formatValue([5, 6], 'y_axis_bounds')).toBe( + 'Min: 5, Max: 6', + ); }); it('returns stringified objects for CollectionControl', () => { - const value = [{ 1: 2, alpha: 'bravo' }, { sent: 'imental', w0ke: 5 }]; + const value = [ + { 1: 2, alpha: 'bravo' }, + { sent: 'imental', w0ke: 5 }, + ]; const expected = '{"1":2,"alpha":"bravo"}, {"sent":"imental","w0ke":5}'; - expect(wrapper.instance().formatValue(value, 'column_collection')).toBe(expected); + expect(wrapper.instance().formatValue(value, 'column_collection')).toBe( + expected, + ); }); it('returns boolean values as string', () => { @@ -278,7 +297,9 @@ describe('AlteredSliceTag', () => { }, ]; const expected = 'a in [1, g, 7, ho], b not in [hu, ho, ha]'; - expect(wrapper.instance().formatValue(filters, 'adhoc_filters')).toBe(expected); + expect(wrapper.instance().formatValue(filters, 'adhoc_filters')).toBe( + expected, + ); }); it('correctly formats filters with string values', () => { @@ -299,7 +320,9 @@ describe('AlteredSliceTag', () => { }, ]; const expected = 'a == gucci, b LIKE moshi moshi'; - expect(wrapper.instance().formatValue(filters, 'adhoc_filters')).toBe(expected); + expect(wrapper.instance().formatValue(filters, 'adhoc_filters')).toBe( + expected, + ); }); }); describe('isEqualish', () => { @@ -318,12 +341,18 @@ describe('AlteredSliceTag', () => { it('considers deeply equal objects as equal', () => { const inst = wrapper.instance(); expect(inst.isEqualish('', '')).toBe(true); - expect(inst.isEqualish({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 })).toBe(true); + expect(inst.isEqualish({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 })).toBe( + true, + ); // Out of order - expect(inst.isEqualish({ a: 1, b: 2, c: 3 }, { b: 2, a: 1, c: 3 })).toBe(true); + expect(inst.isEqualish({ a: 1, b: 2, c: 3 }, { b: 2, a: 1, c: 3 })).toBe( + true, + ); // Actually not equal - expect(inst.isEqualish({ a: 1, b: 2, z: 9 }, { a: 1, b: 2, c: 3 })).toBe(false); + expect(inst.isEqualish({ a: 1, b: 2, z: 9 }, { a: 1, b: 2, c: 3 })).toBe( + false, + ); }); }); }); diff --git a/superset/assets/spec/javascripts/components/AsyncSelect_spec.jsx b/superset/assets/spec/javascripts/components/AsyncSelect_spec.jsx index 6918e1b94cc..cb0281526c0 100644 --- a/superset/assets/spec/javascripts/components/AsyncSelect_spec.jsx +++ b/superset/assets/spec/javascripts/components/AsyncSelect_spec.jsx @@ -44,15 +44,11 @@ describe('AsyncSelect', () => { }; it('is valid element', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('has one select', () => { - const wrapper = shallow( - , - ); + const wrapper = shallow(); expect(wrapper.find(Select)).toHaveLength(1); }); @@ -67,13 +63,11 @@ describe('AsyncSelect', () => { }); describe('auto select', () => { - it('should not call onChange if autoSelect=false', (done) => { + it('should not call onChange if autoSelect=false', done => { expect.assertions(2); const onChangeSpy = jest.fn(); - shallow( - , - ); + shallow(); setTimeout(() => { expect(fetchMock.calls(dataGlob)).toHaveLength(1); @@ -82,7 +76,7 @@ describe('AsyncSelect', () => { }); }); - it('should auto select the first option if autoSelect=true', (done) => { + it('should auto select the first option if autoSelect=true', done => { expect.assertions(3); const onChangeSpy = jest.fn(); @@ -98,12 +92,17 @@ describe('AsyncSelect', () => { }); }); - it('should not auto select when value prop is set and autoSelect=true', (done) => { + it('should not auto select when value prop is set and autoSelect=true', done => { expect.assertions(3); const onChangeSpy = jest.fn(); const wrapper = shallow( - , + , ); setTimeout(() => { @@ -114,7 +113,7 @@ describe('AsyncSelect', () => { }); }); - it('should call onAsyncError if there is an error fetching options', (done) => { + it('should call onAsyncError if there is an error fetching options', done => { expect.assertions(3); const errorEndpoint = 'async/error/'; @@ -123,7 +122,11 @@ describe('AsyncSelect', () => { const onAsyncError = jest.fn(); shallow( - , + , ); setTimeout(() => { diff --git a/superset/assets/spec/javascripts/components/CachedLabel_spec.jsx b/superset/assets/spec/javascripts/components/CachedLabel_spec.jsx index 235d55e8336..1e9cbb0caaa 100644 --- a/superset/assets/spec/javascripts/components/CachedLabel_spec.jsx +++ b/superset/assets/spec/javascripts/components/CachedLabel_spec.jsx @@ -29,14 +29,10 @@ describe('CachedLabel', () => { }; it('is valid', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('renders', () => { - const wrapper = shallow( - , - ); + const wrapper = shallow(); expect(wrapper.find(Label)).toHaveLength(1); }); }); diff --git a/superset/assets/spec/javascripts/components/Checkbox_spec.jsx b/superset/assets/spec/javascripts/components/Checkbox_spec.jsx index 72edb7c5f54..6a8573eca6c 100644 --- a/superset/assets/spec/javascripts/components/Checkbox_spec.jsx +++ b/superset/assets/spec/javascripts/components/Checkbox_spec.jsx @@ -29,7 +29,7 @@ describe('Checkbox', () => { }; let wrapper; - const factory = (o) => { + const factory = o => { const props = Object.assign({}, defaultProps, o); return shallow(); }; @@ -49,7 +49,10 @@ describe('Checkbox', () => { }); it('unchecks when clicked', () => { expect(wrapper.find('i.fa-check.text-transparent')).toHaveLength(0); - wrapper.find('i').first().simulate('click'); + wrapper + .find('i') + .first() + .simulate('click'); expect(defaultProps.onChange.calledOnce).toBe(true); }); }); diff --git a/superset/assets/spec/javascripts/components/ColumnOption_spec.jsx b/superset/assets/spec/javascripts/components/ColumnOption_spec.jsx index 584304921b8..26650da62cb 100644 --- a/superset/assets/spec/javascripts/components/ColumnOption_spec.jsx +++ b/superset/assets/spec/javascripts/components/ColumnOption_spec.jsx @@ -60,17 +60,24 @@ describe('ColumnOption', () => { it('shows a label with column_name when no verbose_name', () => { props.column.verbose_name = null; wrapper = shallow(factory(props)); - expect(wrapper.find('.option-label').first().text()).toBe('foo'); + expect( + wrapper + .find('.option-label') + .first() + .text(), + ).toBe('foo'); }); it('shows a column type label when showType is true', () => { - wrapper = shallow(factory({ - ...props, - showType: true, - column: { - expression: null, - type: 'str', - }, - })); + wrapper = shallow( + factory({ + ...props, + showType: true, + column: { + expression: null, + type: 'str', + }, + }), + ); expect(wrapper.find(ColumnTypeLabel)).toHaveLength(1); }); it('column with expression has correct column label if showType is true', () => { @@ -80,14 +87,16 @@ describe('ColumnOption', () => { expect(wrapper.find(ColumnTypeLabel).props().type).toBe('expression'); }); it('shows no column type label when type is null', () => { - wrapper = shallow(factory({ - ...props, - showType: true, - column: { - expression: null, - type: null, - }, - })); + wrapper = shallow( + factory({ + ...props, + showType: true, + column: { + expression: null, + type: null, + }, + }), + ); expect(wrapper.find(ColumnTypeLabel)).toHaveLength(0); }); it('dttm column has correct column label if showType is true', () => { diff --git a/superset/assets/spec/javascripts/components/ColumnTypeLabel_spec.jsx b/superset/assets/spec/javascripts/components/ColumnTypeLabel_spec.jsx index ea199e3bed7..8c5f9ea4933 100644 --- a/superset/assets/spec/javascripts/components/ColumnTypeLabel_spec.jsx +++ b/superset/assets/spec/javascripts/components/ColumnTypeLabel_spec.jsx @@ -34,7 +34,9 @@ describe('ColumnOption', () => { } it('is a valid element', () => { - expect(React.isValidElement()).toBe(true); + expect(React.isValidElement()).toBe( + true, + ); }); it('string type shows ABC icon', () => { const lbl = getWrapper({}).find('.type-label'); diff --git a/superset/assets/spec/javascripts/components/CopyToClipboard_spec.jsx b/superset/assets/spec/javascripts/components/CopyToClipboard_spec.jsx index 90f48ce9e6d..4b81ac9f754 100644 --- a/superset/assets/spec/javascripts/components/CopyToClipboard_spec.jsx +++ b/superset/assets/spec/javascripts/components/CopyToClipboard_spec.jsx @@ -26,8 +26,8 @@ describe('CopyToClipboard', () => { }; it('renders', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe( + true, + ); }); }); diff --git a/superset/assets/spec/javascripts/components/FilterableTable/FilterableTable_spec.jsx b/superset/assets/spec/javascripts/components/FilterableTable/FilterableTable_spec.jsx index d792f479734..32bd4314307 100644 --- a/superset/assets/spec/javascripts/components/FilterableTable/FilterableTable_spec.jsx +++ b/superset/assets/spec/javascripts/components/FilterableTable/FilterableTable_spec.jsx @@ -18,7 +18,9 @@ */ import React from 'react'; import { mount } from 'enzyme'; -import FilterableTable, { MAX_COLUMNS_FOR_TABLE } from '../../../../src/components/FilterableTable/FilterableTable'; +import FilterableTable, { + MAX_COLUMNS_FOR_TABLE, +} from '../../../../src/components/FilterableTable/FilterableTable'; describe('FilterableTable', () => { const mockedProps = { @@ -35,7 +37,9 @@ describe('FilterableTable', () => { wrapper = mount(); }); it('is valid element', () => { - expect(React.isValidElement()).toBe(true); + expect(React.isValidElement()).toBe( + true, + ); }); it('renders a grid with 2 Table rows', () => { expect(wrapper.find('.ReactVirtualized__Grid')).toHaveLength(1); @@ -44,13 +48,22 @@ describe('FilterableTable', () => { it('renders a grid with 2 Grid rows for wide tables', () => { const wideTableColumns = MAX_COLUMNS_FOR_TABLE + 1; const wideTableMockedProps = { - orderedColumnKeys: Array.from(Array(wideTableColumns), (_, x) => `col_${x}`), + orderedColumnKeys: Array.from( + Array(wideTableColumns), + (_, x) => `col_${x}`, + ), data: [ - Object.assign(...Array.from(Array(wideTableColumns)).map((val, x) => ({ [`col_${x}`]: x }))), + Object.assign( + ...Array.from(Array(wideTableColumns)).map((val, x) => ({ + [`col_${x}`]: x, + })), + ), ], height: 500, }; - const wideTableWrapper = mount(); + const wideTableWrapper = mount( + , + ); expect(wideTableWrapper.find('.ReactVirtualized__Grid')).toHaveLength(2); }); it('filters on a string', () => { diff --git a/superset/assets/spec/javascripts/components/FormRow_spec.jsx b/superset/assets/spec/javascripts/components/FormRow_spec.jsx index 7c54a47b46c..9b2db4ef1d7 100644 --- a/superset/assets/spec/javascripts/components/FormRow_spec.jsx +++ b/superset/assets/spec/javascripts/components/FormRow_spec.jsx @@ -55,5 +55,4 @@ describe('FormRow', () => { expect(wrapper.find(Row)).toHaveLength(1); expect(wrapper.find(Col)).toHaveLength(2); }); - }); diff --git a/superset/assets/spec/javascripts/components/MetricOption_spec.jsx b/superset/assets/spec/javascripts/components/MetricOption_spec.jsx index 8da21d6466f..cd9c0e8de60 100644 --- a/superset/assets/spec/javascripts/components/MetricOption_spec.jsx +++ b/superset/assets/spec/javascripts/components/MetricOption_spec.jsx @@ -61,7 +61,12 @@ describe('MetricOption', () => { it('shows a label with metric_name when no verbose_name', () => { props.metric.verbose_name = null; wrapper = shallow(factory(props)); - expect(wrapper.find('.option-label').first().text()).toBe('foo'); + expect( + wrapper + .find('.option-label') + .first() + .text(), + ).toBe('foo'); }); it('shows only 1 InfoTooltipWithTrigger when no descr and no warning', () => { props.metric.warning_text = null; diff --git a/superset/assets/spec/javascripts/components/ModalTrigger_spec.jsx b/superset/assets/spec/javascripts/components/ModalTrigger_spec.jsx index 77af49b7a62..1b3066bf8be 100644 --- a/superset/assets/spec/javascripts/components/ModalTrigger_spec.jsx +++ b/superset/assets/spec/javascripts/components/ModalTrigger_spec.jsx @@ -28,8 +28,6 @@ describe('ModalTrigger', () => { }; it('is a valid element', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); }); diff --git a/superset/assets/spec/javascripts/components/OnPasteSelect_spec.jsx b/superset/assets/spec/javascripts/components/OnPasteSelect_spec.jsx index d7e2635bf5e..c5f5742799c 100644 --- a/superset/assets/spec/javascripts/components/OnPasteSelect_spec.jsx +++ b/superset/assets/spec/javascripts/components/OnPasteSelect_spec.jsx @@ -83,8 +83,8 @@ describe('OnPasteSelect', () => { }); it('calls onChange with pasted new line separated values', () => { - evt.clipboardData.getData = sinon.spy(() => - 'United States\nChina\nRussian Federation\nIndia', + evt.clipboardData.getData = sinon.spy( + () => 'United States\nChina\nRussian Federation\nIndia', ); wrapper.instance().onPaste(evt); expected = [ @@ -99,8 +99,8 @@ describe('OnPasteSelect', () => { }); it('calls onChange with pasted tab separated values', () => { - evt.clipboardData.getData = sinon.spy(() => - 'Russian Federation\tMexico\tIndia\tCanada', + evt.clipboardData.getData = sinon.spy( + () => 'Russian Federation\tMexico\tIndia\tCanada', ); wrapper.instance().onPaste(evt); expected = [ @@ -115,8 +115,8 @@ describe('OnPasteSelect', () => { }); it('calls onChange without duplicate values and adds new comma separated values', () => { - evt.clipboardData.getData = sinon.spy(() => - 'China, China, China, China, Mexico, Mexico, Chi na, Mexico, ', + evt.clipboardData.getData = sinon.spy( + () => 'China, China, China, China, Mexico, Mexico, Chi na, Mexico, ', ); expected = [ props.options[1], @@ -132,14 +132,10 @@ describe('OnPasteSelect', () => { }); it('calls onChange without duplicate values and parses new line separated values', () => { - evt.clipboardData.getData = sinon.spy(() => - 'United States\nCanada\nMexico\nUnited States\nCanada', + evt.clipboardData.getData = sinon.spy( + () => 'United States\nCanada\nMexico\nUnited States\nCanada', ); - expected = [ - props.options[0], - props.options[3], - props.options[6], - ]; + expected = [props.options[0], props.options[3], props.options[6]]; wrapper.instance().onPaste(evt); expect(props.onChange.calledWith(expected)).toBe(true); expect(evt.preventDefault.called).toBe(true); @@ -147,25 +143,25 @@ describe('OnPasteSelect', () => { }); it('calls onChange without duplicate values and parses tab separated values', () => { - evt.clipboardData.getData = sinon.spy(() => - 'China\tIndia\tChina\tRussian Federation\tJapan\tJapan', - ); - expected = [ - props.options[1], - props.options[2], - props.options[4], - props.options[5], - ]; - wrapper.instance().onPaste(evt); - expect(props.onChange.calledWith(expected)).toBe(true); - expect(evt.preventDefault.called).toBe(true); - expect(props.isValidNewOption.callCount).toBe(24); - }); + evt.clipboardData.getData = sinon.spy( + () => 'China\tIndia\tChina\tRussian Federation\tJapan\tJapan', + ); + expected = [ + props.options[1], + props.options[2], + props.options[4], + props.options[5], + ]; + wrapper.instance().onPaste(evt); + expect(props.onChange.calledWith(expected)).toBe(true); + expect(evt.preventDefault.called).toBe(true); + expect(props.isValidNewOption.callCount).toBe(24); + }); it('calls onChange with currently selected values and new comma separated values', () => { props.value = ['United States', 'Canada', 'Mexico']; - evt.clipboardData.getData = sinon.spy(() => - 'United States, Canada, Japan, India', + evt.clipboardData.getData = sinon.spy( + () => 'United States, Canada, Japan, India', ); wrapper = shallow(); expected = [ @@ -183,9 +179,7 @@ describe('OnPasteSelect', () => { it('calls onChange with currently selected values and new "new line" separated values', () => { props.value = ['China', 'India', 'Japan']; - evt.clipboardData.getData = sinon.spy(() => - 'Mexico\nJapan\nIndia', - ); + evt.clipboardData.getData = sinon.spy(() => 'Mexico\nJapan\nIndia'); wrapper = shallow(); expected = [ props.options[1], @@ -200,23 +194,23 @@ describe('OnPasteSelect', () => { }); it('calls onChange with currently selected values and new tab separated values', () => { - props.value = ['United States', 'Canada', 'Mexico', 'Russian Federation']; - evt.clipboardData.getData = sinon.spy(() => - 'United States\tCanada\tJapan\tIndia', - ); - wrapper = shallow(); - expected = [ - props.options[0], - props.options[3], - props.options[6], - props.options[4], - props.options[5], - props.options[2], - ]; - wrapper.instance().onPaste(evt); - expect(props.onChange.calledWith(expected)).toBe(true); - expect(evt.preventDefault.called).toBe(true); - expect(props.isValidNewOption.callCount).toBe(29); - }); + props.value = ['United States', 'Canada', 'Mexico', 'Russian Federation']; + evt.clipboardData.getData = sinon.spy( + () => 'United States\tCanada\tJapan\tIndia', + ); + wrapper = shallow(); + expected = [ + props.options[0], + props.options[3], + props.options[6], + props.options[4], + props.options[5], + props.options[2], + ]; + wrapper.instance().onPaste(evt); + expect(props.onChange.calledWith(expected)).toBe(true); + expect(evt.preventDefault.called).toBe(true); + expect(props.isValidNewOption.callCount).toBe(29); + }); }); }); diff --git a/superset/assets/spec/javascripts/components/PopoverSection_spec.jsx b/superset/assets/spec/javascripts/components/PopoverSection_spec.jsx index 4c731e0af62..28fda4ded79 100644 --- a/superset/assets/spec/javascripts/components/PopoverSection_spec.jsx +++ b/superset/assets/spec/javascripts/components/PopoverSection_spec.jsx @@ -31,7 +31,7 @@ describe('PopoverSection', () => { }; let wrapper; - const factory = (overrideProps) => { + const factory = overrideProps => { const props = Object.assign({}, defaultProps, overrideProps || {}); return shallow(); }; @@ -39,7 +39,9 @@ describe('PopoverSection', () => { wrapper = factory(); }); it('renders', () => { - expect(React.isValidElement()).toBe(true); + expect(React.isValidElement()).toBe( + true, + ); }); it('is show an icon when selected', () => { expect(wrapper.find('.fa-check')).toHaveLength(1); diff --git a/superset/assets/spec/javascripts/components/TableSelector_spec.jsx b/superset/assets/spec/javascripts/components/TableSelector_spec.jsx index d1febbf8f88..638afc71e1d 100644 --- a/superset/assets/spec/javascripts/components/TableSelector_spec.jsx +++ b/superset/assets/spec/javascripts/components/TableSelector_spec.jsx @@ -81,12 +81,10 @@ describe('TableSelector', () => { afterAll(fetchMock.reset); it('should handle empty', () => - inst - .getTableNamesBySubStr('') - .then((data) => { - expect(data).toEqual({ options: [] }); - return Promise.resolve(); - })); + inst.getTableNamesBySubStr('').then(data => { + expect(data).toEqual({ options: [] }); + return Promise.resolve(); + })); it('should handle table name', () => { fetchMock.get(GET_TABLE_NAMES_GLOB, tables, { overwriteRoutes: true }); @@ -94,28 +92,29 @@ describe('TableSelector', () => { return wrapper .instance() .getTableNamesBySubStr('my table') - .then((data) => { + .then(data => { expect(fetchMock.calls(GET_TABLE_NAMES_GLOB)).toHaveLength(1); expect(data).toEqual({ options: [ - { - value: 'birth_names', - schema: 'main', - label: 'birth_names', - title: 'birth_names', - }, - { - value: 'energy_usage', - schema: 'main', - label: 'energy_usage', - title: 'energy_usage', - }, - { - value: 'wb_health_population', - schema: 'main', - label: 'wb_health_population', - title: 'wb_health_population', - }], + { + value: 'birth_names', + schema: 'main', + label: 'birth_names', + title: 'birth_names', + }, + { + value: 'energy_usage', + schema: 'main', + label: 'energy_usage', + title: 'energy_usage', + }, + { + value: 'wb_health_population', + schema: 'main', + label: 'wb_health_population', + title: 'wb_health_population', + }, + ], }); return Promise.resolve(); }); @@ -130,8 +129,9 @@ describe('TableSelector', () => { .instance() .getTableNamesBySubStr('slashed/table') .then(() => { - expect(fetchMock.lastUrl(GET_TABLE_GLOB)) - .toContain('/slashed%252Fschema/slashed%252Ftable'); + expect(fetchMock.lastUrl(GET_TABLE_GLOB)).toContain( + '/slashed%252Fschema/slashed%252Ftable', + ); return Promise.resolve(); }); }); @@ -149,37 +149,39 @@ describe('TableSelector', () => { it('should fetch table options', () => { fetchMock.get(FETCH_TABLES_GLOB, tables, { overwriteRoutes: true }); - return inst - .fetchTables(true, 'birth_names') - .then(() => { - expect(wrapper.state().tableOptions).toHaveLength(3); - expect(wrapper.state().tableOptions).toEqual([ - { - value: 'birth_names', - schema: 'main', - label: 'birth_names', - title: 'birth_names', - }, - { - value: 'energy_usage', - schema: 'main', - label: 'energy_usage', - title: 'energy_usage', - }, - { - value: 'wb_health_population', - schema: 'main', - label: 'wb_health_population', - title: 'wb_health_population', - }, - ]); - return Promise.resolve(); - }); + return inst.fetchTables(true, 'birth_names').then(() => { + expect(wrapper.state().tableOptions).toHaveLength(3); + expect(wrapper.state().tableOptions).toEqual([ + { + value: 'birth_names', + schema: 'main', + label: 'birth_names', + title: 'birth_names', + }, + { + value: 'energy_usage', + schema: 'main', + label: 'energy_usage', + title: 'energy_usage', + }, + { + value: 'wb_health_population', + schema: 'main', + label: 'wb_health_population', + title: 'wb_health_population', + }, + ]); + return Promise.resolve(); + }); }); // Test needs to be fixed: Github issue #7768 xit('should dispatch a danger toast on error', () => { - fetchMock.get(FETCH_TABLES_GLOB, { throws: 'error' }, { overwriteRoutes: true }); + fetchMock.get( + FETCH_TABLES_GLOB, + { throws: 'error' }, + { overwriteRoutes: true }, + ); wrapper .instance() @@ -202,7 +204,9 @@ describe('TableSelector', () => { const schemaOptions = { schemas: ['main', 'erf', 'superset'], }; - fetchMock.get(FETCH_SCHEMAS_GLOB, schemaOptions, { overwriteRoutes: true }); + fetchMock.get(FETCH_SCHEMAS_GLOB, schemaOptions, { + overwriteRoutes: true, + }); return wrapper .instance() diff --git a/superset/assets/spec/javascripts/components/URLShortLinkButton_spec.jsx b/superset/assets/spec/javascripts/components/URLShortLinkButton_spec.jsx index a8321357096..c61e91684a8 100644 --- a/superset/assets/spec/javascripts/components/URLShortLinkButton_spec.jsx +++ b/superset/assets/spec/javascripts/components/URLShortLinkButton_spec.jsx @@ -33,7 +33,9 @@ describe('URLShortLinkButton', () => { function setup() { const mockStore = configureStore([]); const store = mockStore({}); - return shallow(, { context: { store } }).dive(); + return shallow(, { + context: { store }, + }).dive(); } it('renders OverlayTrigger', () => { diff --git a/superset/assets/spec/javascripts/components/URLShortLinkModal_spec.jsx b/superset/assets/spec/javascripts/components/URLShortLinkModal_spec.jsx index 9ad52c2e5e5..a1bc94046f2 100644 --- a/superset/assets/spec/javascripts/components/URLShortLinkModal_spec.jsx +++ b/superset/assets/spec/javascripts/components/URLShortLinkModal_spec.jsx @@ -33,7 +33,9 @@ describe('URLShortLinkModal', () => { function setup() { const mockStore = configureStore([]); const store = mockStore({}); - return shallow(, { context: { store } }).dive(); + return shallow(, { + context: { store }, + }).dive(); } it('renders ModalTrigger', () => { diff --git a/superset/assets/spec/javascripts/components/VirtualizedRendererWrap_spec.jsx b/superset/assets/spec/javascripts/components/VirtualizedRendererWrap_spec.jsx index 351e26938b1..fdb7c4ca9cb 100644 --- a/superset/assets/spec/javascripts/components/VirtualizedRendererWrap_spec.jsx +++ b/superset/assets/spec/javascripts/components/VirtualizedRendererWrap_spec.jsx @@ -34,9 +34,7 @@ const defaultProps = { }; function TestOption({ option }) { - return ( - {option.label} - ); + return {option.label}; } TestOption.propTypes = { option: PropTypes.object.isRequired, @@ -75,14 +73,18 @@ describe('VirtualizedRendererWrap', () => { props.option = props.focusedOption; wrapper = shallow(); const optionDiv = wrapper.find('div'); - expect(optionDiv.props().className).toBe('VirtualizedSelectOption VirtualizedSelectFocusedOption'); + expect(optionDiv.props().className).toBe( + 'VirtualizedSelectOption VirtualizedSelectFocusedOption', + ); }); it('renders disabled option with the correct class', () => { props.option.disabled = true; wrapper = shallow(); const optionDiv = wrapper.find('div'); - expect(optionDiv.props().className).toBe('VirtualizedSelectOption VirtualizedSelectDisabledOption'); + expect(optionDiv.props().className).toBe( + 'VirtualizedSelectOption VirtualizedSelectDisabledOption', + ); props.option.disabled = false; }); @@ -90,14 +92,18 @@ describe('VirtualizedRendererWrap', () => { props.valueArray = [props.option, props.focusedOption]; wrapper = shallow(); const optionDiv = wrapper.find('div'); - expect(optionDiv.props().className).toBe('VirtualizedSelectOption VirtualizedSelectSelectedOption'); + expect(optionDiv.props().className).toBe( + 'VirtualizedSelectOption VirtualizedSelectSelectedOption', + ); }); it('renders options with custom classes', () => { props.option.className = 'CustomClass'; wrapper = shallow(); const optionDiv = wrapper.find('div'); - expect(optionDiv.props().className).toBe('VirtualizedSelectOption CustomClass'); + expect(optionDiv.props().className).toBe( + 'VirtualizedSelectOption CustomClass', + ); }); it('calls focusedOption on its own option onMouseEnter', () => { diff --git a/superset/assets/spec/javascripts/datasource/ChangeDatasourceModal_spec.jsx b/superset/assets/spec/javascripts/datasource/ChangeDatasourceModal_spec.jsx index f55ab71b1e7..c1da270b90f 100644 --- a/superset/assets/spec/javascripts/datasource/ChangeDatasourceModal_spec.jsx +++ b/superset/assets/spec/javascripts/datasource/ChangeDatasourceModal_spec.jsx @@ -70,7 +70,7 @@ describe('ChangeDatasourceModal', () => { expect(wrapper.find(Modal)).toHaveLength(1); }); - it('fetches datasources', (done) => { + it('fetches datasources', done => { inst.onEnterModal(); setTimeout(() => { expect(fetchMock.calls(DATASOURCES_ENDPOINT)).toHaveLength(1); @@ -79,12 +79,14 @@ describe('ChangeDatasourceModal', () => { }, 0); }); - it('changes the datasource', (done) => { + it('changes the datasource', done => { fetchMock.get(DATASOURCE_ENDPOINT, DATASOURCE_PAYLOAD); inst.selectDatasource(datasourceData); setTimeout(() => { expect(fetchMock.calls(DATASOURCE_ENDPOINT)).toHaveLength(1); - expect(props.onDatasourceSave.getCall(0).args[0]).toEqual(DATASOURCE_PAYLOAD); + expect(props.onDatasourceSave.getCall(0).args[0]).toEqual( + DATASOURCE_PAYLOAD, + ); fetchMock.reset(); done(); }, 0); diff --git a/superset/assets/spec/javascripts/datasource/DatasourceEditor_spec.jsx b/superset/assets/spec/javascripts/datasource/DatasourceEditor_spec.jsx index c23ff43dbe0..898d76872a0 100644 --- a/superset/assets/spec/javascripts/datasource/DatasourceEditor_spec.jsx +++ b/superset/assets/spec/javascripts/datasource/DatasourceEditor_spec.jsx @@ -70,7 +70,7 @@ describe('DatasourceEditor', () => { expect(wrapper.find(Tabs)).toHaveLength(1); }); - it('makes an async request', (done) => { + it('makes an async request', done => { wrapper.setState({ activeTabKey: 2 }); const syncButton = wrapper.find('.sync-from-source'); expect(syncButton).toHaveLength(1); @@ -93,6 +93,11 @@ describe('DatasourceEditor', () => { it('renders isSqla fields', () => { wrapper.setState({ activeTabKey: 4 }); expect(wrapper.state('isSqla')).toBe(true); - expect(wrapper.find(Field).find({ fieldKey: 'fetch_values_predicate' }).exists()).toBe(true); + expect( + wrapper + .find(Field) + .find({ fieldKey: 'fetch_values_predicate' }) + .exists(), + ).toBe(true); }); }); diff --git a/superset/assets/spec/javascripts/datasource/DatasourceModal_spec.jsx b/superset/assets/spec/javascripts/datasource/DatasourceModal_spec.jsx index 090e82c06e4..720ef980317 100644 --- a/superset/assets/spec/javascripts/datasource/DatasourceModal_spec.jsx +++ b/superset/assets/spec/javascripts/datasource/DatasourceModal_spec.jsx @@ -68,7 +68,7 @@ describe('DatasourceModal', () => { expect(wrapper.find(DatasourceEditor)).toHaveLength(1); }); - it('saves on confirm', (done) => { + it('saves on confirm', done => { inst.onConfirmSave(); setTimeout(() => { expect(fetchMock.calls(SAVE_ENDPOINT)).toHaveLength(1); diff --git a/superset/assets/spec/javascripts/explore/AdhocFilter_spec.js b/superset/assets/spec/javascripts/explore/AdhocFilter_spec.js index 7f8701bd912..a559bd70990 100644 --- a/superset/assets/spec/javascripts/explore/AdhocFilter_spec.js +++ b/superset/assets/spec/javascripts/explore/AdhocFilter_spec.js @@ -16,7 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -import AdhocFilter, { EXPRESSION_TYPES, CLAUSES } from '../../../src/explore/AdhocFilter'; +import AdhocFilter, { + EXPRESSION_TYPES, + CLAUSES, +} from '../../../src/explore/AdhocFilter'; describe('AdhocFilter', () => { it('sets filterOptionName in constructor', () => { @@ -93,7 +96,9 @@ describe('AdhocFilter', () => { sqlExpression: 'value > 10', clause: CLAUSES.WHERE, }); - const adhocFilter4 = adhocFilter3.duplicateWith({ sqlExpression: 'value = 5' }); + const adhocFilter4 = adhocFilter3.duplicateWith({ + sqlExpression: 'value = 5', + }); // eslint-disable-next-line no-unused-expressions expect(adhocFilter3.equals(adhocFilter4)).toBe(false); diff --git a/superset/assets/spec/javascripts/explore/AdhocMetric_spec.js b/superset/assets/spec/javascripts/explore/AdhocMetric_spec.js index a03c42a7ac3..a564412e027 100644 --- a/superset/assets/spec/javascripts/explore/AdhocMetric_spec.js +++ b/superset/assets/spec/javascripts/explore/AdhocMetric_spec.js @@ -16,7 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -import AdhocMetric, { EXPRESSION_TYPES } from '../../../src/explore/AdhocMetric'; +import AdhocMetric, { + EXPRESSION_TYPES, +} from '../../../src/explore/AdhocMetric'; import { AGGREGATES } from '../../../src/explore/constants'; const valueColumn = { type: 'DOUBLE', column_name: 'value' }; @@ -45,7 +47,9 @@ describe('AdhocMetric', () => { column: valueColumn, aggregate: AGGREGATES.SUM, }); - const adhocMetric2 = adhocMetric1.duplicateWith({ aggregate: AGGREGATES.AVG }); + const adhocMetric2 = adhocMetric1.duplicateWith({ + aggregate: AGGREGATES.AVG, + }); expect(adhocMetric1.column).toBe(adhocMetric2.column); expect(adhocMetric1.column).toBe(valueColumn); @@ -83,7 +87,9 @@ describe('AdhocMetric', () => { label: 'old label', hasCustomLabel: true, }); - const adhocMetric4 = adhocMetric3.duplicateWith({ sqlExpression: 'COUNT(1)' }); + const adhocMetric4 = adhocMetric3.duplicateWith({ + sqlExpression: 'COUNT(1)', + }); // eslint-disable-next-line no-unused-expressions expect(adhocMetric3.equals(adhocMetric4)).toBe(false); @@ -94,7 +100,9 @@ describe('AdhocMetric', () => { column: valueColumn, aggregate: AGGREGATES.SUM, }); - const adhocMetric2 = adhocMetric1.duplicateWith({ aggregate: AGGREGATES.AVG }); + const adhocMetric2 = adhocMetric1.duplicateWith({ + aggregate: AGGREGATES.AVG, + }); expect(adhocMetric2.label).toBe('AVG(value)'); }); @@ -106,7 +114,9 @@ describe('AdhocMetric', () => { hasCustomLabel: true, label: 'label1', }); - const adhocMetric2 = adhocMetric1.duplicateWith({ aggregate: AGGREGATES.AVG }); + const adhocMetric2 = adhocMetric1.duplicateWith({ + aggregate: AGGREGATES.AVG, + }); expect(adhocMetric2.label).toBe('label1'); }); @@ -160,51 +170,45 @@ describe('AdhocMetric', () => { expect(adhocMetric5.isValid()).toBe(false); }); - it( - 'can translate back from sql expressions to simple expressions when possible', - () => { - const adhocMetric = new AdhocMetric({ - expressionType: EXPRESSION_TYPES.SQL, - sqlExpression: 'AVG(my_column)', - hasCustomLabel: true, - label: 'label1', - }); - expect(adhocMetric.inferSqlExpressionColumn()).toBe('my_column'); - expect(adhocMetric.inferSqlExpressionAggregate()).toBe('AVG'); + it('can translate back from sql expressions to simple expressions when possible', () => { + const adhocMetric = new AdhocMetric({ + expressionType: EXPRESSION_TYPES.SQL, + sqlExpression: 'AVG(my_column)', + hasCustomLabel: true, + label: 'label1', + }); + expect(adhocMetric.inferSqlExpressionColumn()).toBe('my_column'); + expect(adhocMetric.inferSqlExpressionAggregate()).toBe('AVG'); - const adhocMetric2 = new AdhocMetric({ - expressionType: EXPRESSION_TYPES.SQL, - sqlExpression: 'AVG(SUM(my_column)) / MAX(other_column)', - hasCustomLabel: true, - label: 'label1', - }); - expect(adhocMetric2.inferSqlExpressionColumn()).toBeNull(); - expect(adhocMetric2.inferSqlExpressionAggregate()).toBeNull(); - }, - ); + const adhocMetric2 = new AdhocMetric({ + expressionType: EXPRESSION_TYPES.SQL, + sqlExpression: 'AVG(SUM(my_column)) / MAX(other_column)', + hasCustomLabel: true, + label: 'label1', + }); + expect(adhocMetric2.inferSqlExpressionColumn()).toBeNull(); + expect(adhocMetric2.inferSqlExpressionAggregate()).toBeNull(); + }); - it( - 'will infer columns and aggregates when converting to a simple expression', - () => { - const adhocMetric = new AdhocMetric({ - expressionType: EXPRESSION_TYPES.SQL, - sqlExpression: 'AVG(my_column)', - hasCustomLabel: true, - label: 'label1', - }); - const adhocMetric2 = adhocMetric.duplicateWith({ - expressionType: EXPRESSION_TYPES.SIMPLE, - aggregate: AGGREGATES.SUM, - }); - expect(adhocMetric2.aggregate).toBe(AGGREGATES.SUM); - expect(adhocMetric2.column.column_name).toBe('my_column'); + it('will infer columns and aggregates when converting to a simple expression', () => { + const adhocMetric = new AdhocMetric({ + expressionType: EXPRESSION_TYPES.SQL, + sqlExpression: 'AVG(my_column)', + hasCustomLabel: true, + label: 'label1', + }); + const adhocMetric2 = adhocMetric.duplicateWith({ + expressionType: EXPRESSION_TYPES.SIMPLE, + aggregate: AGGREGATES.SUM, + }); + expect(adhocMetric2.aggregate).toBe(AGGREGATES.SUM); + expect(adhocMetric2.column.column_name).toBe('my_column'); - const adhocMetric3 = adhocMetric.duplicateWith({ - expressionType: EXPRESSION_TYPES.SIMPLE, - column: valueColumn, - }); - expect(adhocMetric3.aggregate).toBe(AGGREGATES.AVG); - expect(adhocMetric3.column.column_name).toBe('value'); - }, - ); + const adhocMetric3 = adhocMetric.duplicateWith({ + expressionType: EXPRESSION_TYPES.SIMPLE, + column: valueColumn, + }); + expect(adhocMetric3.aggregate).toBe(AGGREGATES.AVG); + expect(adhocMetric3.column.column_name).toBe('value'); + }); }); diff --git a/superset/assets/spec/javascripts/explore/components/AdhocFilterControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/AdhocFilterControl_spec.jsx index b879c5b1ba2..25859c3673f 100644 --- a/superset/assets/spec/javascripts/explore/components/AdhocFilterControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/AdhocFilterControl_spec.jsx @@ -21,7 +21,10 @@ import React from 'react'; import sinon from 'sinon'; import { shallow } from 'enzyme'; -import AdhocFilter, { EXPRESSION_TYPES, CLAUSES } from '../../../../src/explore/AdhocFilter'; +import AdhocFilter, { + EXPRESSION_TYPES, + CLAUSES, +} from '../../../../src/explore/AdhocFilter'; import AdhocFilterControl from '../../../../src/explore/components/controls/AdhocFilterControl'; import AdhocMetric from '../../../../src/explore/AdhocMetric'; import { AGGREGATES, OPERATORS } from '../../../../src/explore/constants'; @@ -82,15 +85,17 @@ describe('AdhocFilterControl', () => { const adhocFilter = onChange.lastCall.args[0][0]; expect(adhocFilter instanceof AdhocFilter).toBe(true); - expect(adhocFilter.equals(( - new AdhocFilter({ - expressionType: EXPRESSION_TYPES.SQL, - subject: savedMetric.expression, - operator: OPERATORS['>'], - comparator: 0, - clause: CLAUSES.HAVING, - }) - ))).toBe(true); + expect( + adhocFilter.equals( + new AdhocFilter({ + expressionType: EXPRESSION_TYPES.SQL, + subject: savedMetric.expression, + operator: OPERATORS['>'], + comparator: 0, + clause: CLAUSES.HAVING, + }), + ), + ).toBe(true); }); it('handles adhoc metrics being selected to filter on', () => { @@ -100,15 +105,17 @@ describe('AdhocFilterControl', () => { const adhocFilter = onChange.lastCall.args[0][0]; expect(adhocFilter instanceof AdhocFilter).toBe(true); - expect(adhocFilter.equals(( - new AdhocFilter({ - expressionType: EXPRESSION_TYPES.SQL, - subject: sumValueAdhocMetric.label, - operator: OPERATORS['>'], - comparator: 0, - clause: CLAUSES.HAVING, - }) - ))).toBe(true); + expect( + adhocFilter.equals( + new AdhocFilter({ + expressionType: EXPRESSION_TYPES.SQL, + subject: sumValueAdhocMetric.label, + operator: OPERATORS['>'], + comparator: 0, + clause: CLAUSES.HAVING, + }), + ), + ).toBe(true); }); it('handles columns being selected to filter on', () => { @@ -118,15 +125,17 @@ describe('AdhocFilterControl', () => { const adhocFilter = onChange.lastCall.args[0][0]; expect(adhocFilter instanceof AdhocFilter).toBe(true); - expect(adhocFilter.equals(( - new AdhocFilter({ - expressionType: EXPRESSION_TYPES.SIMPLE, - subject: columns[0].column_name, - operator: OPERATORS['=='], - comparator: '', - clause: CLAUSES.WHERE, - }) - ))).toBe(true); + expect( + adhocFilter.equals( + new AdhocFilter({ + expressionType: EXPRESSION_TYPES.SIMPLE, + subject: columns[0].column_name, + operator: OPERATORS['=='], + comparator: '', + clause: CLAUSES.WHERE, + }), + ), + ).toBe(true); }); it('persists existing filters even when new filters are added', () => { @@ -140,14 +149,16 @@ describe('AdhocFilterControl', () => { const newAdhocFilter = onChange.lastCall.args[0][1]; expect(newAdhocFilter instanceof AdhocFilter).toBe(true); - expect(newAdhocFilter.equals(( - new AdhocFilter({ - expressionType: EXPRESSION_TYPES.SIMPLE, - subject: columns[0].column_name, - operator: OPERATORS['=='], - comparator: '', - clause: CLAUSES.WHERE, - }) - ))).toBe(true); + expect( + newAdhocFilter.equals( + new AdhocFilter({ + expressionType: EXPRESSION_TYPES.SIMPLE, + subject: columns[0].column_name, + operator: OPERATORS['=='], + comparator: '', + clause: CLAUSES.WHERE, + }), + ), + ).toBe(true); }); }); diff --git a/superset/assets/spec/javascripts/explore/components/AdhocFilterEditPopoverSimpleTabContent_spec.jsx b/superset/assets/spec/javascripts/explore/components/AdhocFilterEditPopoverSimpleTabContent_spec.jsx index 67607c74bc2..d5079cead6f 100644 --- a/superset/assets/spec/javascripts/explore/components/AdhocFilterEditPopoverSimpleTabContent_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/AdhocFilterEditPopoverSimpleTabContent_spec.jsx @@ -22,7 +22,10 @@ import sinon from 'sinon'; import { shallow } from 'enzyme'; import { FormGroup } from 'react-bootstrap'; -import AdhocFilter, { EXPRESSION_TYPES, CLAUSES } from '../../../../src/explore/AdhocFilter'; +import AdhocFilter, { + EXPRESSION_TYPES, + CLAUSES, +} from '../../../../src/explore/AdhocFilter'; import AdhocMetric from '../../../../src/explore/AdhocMetric'; import AdhocFilterEditPopoverSimpleTabContent from '../../../../src/explore/components/AdhocFilterEditPopoverSimpleTabContent'; import { AGGREGATES } from '../../../../src/explore/constants'; @@ -68,7 +71,9 @@ function setup(overrides) { datasource: {}, ...overrides, }; - const wrapper = shallow(); + const wrapper = shallow( + , + ); return { wrapper, onChange, onHeightChange }; } @@ -80,23 +85,29 @@ describe('AdhocFilterEditPopoverSimpleTabContent', () => { it('passes the new adhocFilter to onChange after onSubjectChange', () => { const { wrapper, onChange } = setup(); - wrapper.instance().onSubjectChange({ type: 'VARCHAR(255)', column_name: 'source' }); + wrapper + .instance() + .onSubjectChange({ type: 'VARCHAR(255)', column_name: 'source' }); expect(onChange.calledOnce).toBe(true); - expect(onChange.lastCall.args[0].equals(( - simpleAdhocFilter.duplicateWith({ subject: 'source' }) - ))).toBe(true); + expect( + onChange.lastCall.args[0].equals( + simpleAdhocFilter.duplicateWith({ subject: 'source' }), + ), + ).toBe(true); }); it('may alter the clause in onSubjectChange if the old clause is not appropriate', () => { const { wrapper, onChange } = setup(); wrapper.instance().onSubjectChange(sumValueAdhocMetric); expect(onChange.calledOnce).toBe(true); - expect(onChange.lastCall.args[0].equals(( - simpleAdhocFilter.duplicateWith({ - subject: sumValueAdhocMetric.label, - clause: CLAUSES.HAVING, - }) - ))).toBe(true); + expect( + onChange.lastCall.args[0].equals( + simpleAdhocFilter.duplicateWith({ + subject: sumValueAdhocMetric.label, + clause: CLAUSES.HAVING, + }), + ), + ).toBe(true); }); it('will convert from individual comparator to array if the operator changes to multi', () => { @@ -109,21 +120,27 @@ describe('AdhocFilterEditPopoverSimpleTabContent', () => { }); it('will convert from array to individual comparators if the operator changes from multi', () => { - const { wrapper, onChange } = setup({ adhocFilter: simpleMultiAdhocFilter }); + const { wrapper, onChange } = setup({ + adhocFilter: simpleMultiAdhocFilter, + }); wrapper.instance().onOperatorChange({ operator: '<' }); expect(onChange.calledOnce).toBe(true); - expect(onChange.lastCall.args[0].equals(( - simpleAdhocFilter.duplicateWith({ operator: '<', comparator: '10' }) - ))).toBe(true); + expect( + onChange.lastCall.args[0].equals( + simpleAdhocFilter.duplicateWith({ operator: '<', comparator: '10' }), + ), + ).toBe(true); }); it('passes the new adhocFilter to onChange after onComparatorChange', () => { const { wrapper, onChange } = setup(); wrapper.instance().onComparatorChange('20'); expect(onChange.calledOnce).toBe(true); - expect(onChange.lastCall.args[0].equals(( - simpleAdhocFilter.duplicateWith({ comparator: '20' }) - ))).toBe(true); + expect( + onChange.lastCall.args[0].equals( + simpleAdhocFilter.duplicateWith({ comparator: '20' }), + ), + ).toBe(true); }); it('will filter operators for table datasources', () => { @@ -141,8 +158,9 @@ describe('AdhocFilterEditPopoverSimpleTabContent', () => { it('expands when its multi comparator input field expands', () => { const { wrapper, onHeightChange } = setup(); - wrapper.instance().multiComparatorComponent = - { _selectRef: { select: { control: { clientHeight: 57 } } } }; + wrapper.instance().multiComparatorComponent = { + _selectRef: { select: { control: { clientHeight: 57 } } }, + }; wrapper.instance().handleMultiComparatorInputHeightChange(); expect(onHeightChange.calledOnce).toBe(true); diff --git a/superset/assets/spec/javascripts/explore/components/AdhocFilterEditPopoverSqlTabContent_spec.jsx b/superset/assets/spec/javascripts/explore/components/AdhocFilterEditPopoverSqlTabContent_spec.jsx index 4bc739e95ad..eef902778db 100644 --- a/superset/assets/spec/javascripts/explore/components/AdhocFilterEditPopoverSqlTabContent_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/AdhocFilterEditPopoverSqlTabContent_spec.jsx @@ -22,7 +22,10 @@ import sinon from 'sinon'; import { shallow } from 'enzyme'; import { FormGroup } from 'react-bootstrap'; -import AdhocFilter, { EXPRESSION_TYPES, CLAUSES } from '../../../../src/explore/AdhocFilter'; +import AdhocFilter, { + EXPRESSION_TYPES, + CLAUSES, +} from '../../../../src/explore/AdhocFilter'; import AdhocFilterEditPopoverSqlTabContent from '../../../../src/explore/components/AdhocFilterEditPopoverSqlTabContent'; const sqlAdhocFilter = new AdhocFilter({ @@ -54,17 +57,21 @@ describe('AdhocFilterEditPopoverSqlTabContent', () => { const { wrapper, onChange } = setup(); wrapper.instance().onSqlExpressionClauseChange(CLAUSES.HAVING); expect(onChange.calledOnce).toBe(true); - expect(onChange.lastCall.args[0].equals(( - sqlAdhocFilter.duplicateWith({ clause: CLAUSES.HAVING }) - ))).toBe(true); + expect( + onChange.lastCall.args[0].equals( + sqlAdhocFilter.duplicateWith({ clause: CLAUSES.HAVING }), + ), + ).toBe(true); }); it('passes the new query to onChange after onSqlExpressionChange', () => { const { wrapper, onChange } = setup(); wrapper.instance().onSqlExpressionChange('value < 5'); expect(onChange.calledOnce).toBe(true); - expect(onChange.lastCall.args[0].equals(( - sqlAdhocFilter.duplicateWith({ sqlExpression: 'value < 5' }) - ))).toBe(true); + expect( + onChange.lastCall.args[0].equals( + sqlAdhocFilter.duplicateWith({ sqlExpression: 'value < 5' }), + ), + ).toBe(true); }); }); diff --git a/superset/assets/spec/javascripts/explore/components/AdhocFilterEditPopover_spec.jsx b/superset/assets/spec/javascripts/explore/components/AdhocFilterEditPopover_spec.jsx index c38d0ed3657..0f917ab3fac 100644 --- a/superset/assets/spec/javascripts/explore/components/AdhocFilterEditPopover_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/AdhocFilterEditPopover_spec.jsx @@ -22,7 +22,10 @@ import sinon from 'sinon'; import { shallow } from 'enzyme'; import { Button, Popover, Tab, Tabs } from 'react-bootstrap'; -import AdhocFilter, { EXPRESSION_TYPES, CLAUSES } from '../../../../src/explore/AdhocFilter'; +import AdhocFilter, { + EXPRESSION_TYPES, + CLAUSES, +} from '../../../../src/explore/AdhocFilter'; import AdhocMetric from '../../../../src/explore/AdhocMetric'; import AdhocFilterEditPopover from '../../../../src/explore/components/AdhocFilterEditPopover'; import AdhocFilterEditPopoverSimpleTabContent from '../../../../src/explore/components/AdhocFilterEditPopoverSimpleTabContent'; @@ -81,7 +84,9 @@ describe('AdhocFilterEditPopover', () => { expect(wrapper.find(Tabs)).toHaveLength(1); expect(wrapper.find(Tab)).toHaveLength(2); expect(wrapper.find(Button)).toHaveLength(2); - expect(wrapper.find(AdhocFilterEditPopoverSimpleTabContent)).toHaveLength(1); + expect(wrapper.find(AdhocFilterEditPopoverSimpleTabContent)).toHaveLength( + 1, + ); }); it('renders sql tab content when the adhoc filter expressionType is sql', () => { @@ -102,7 +107,9 @@ describe('AdhocFilterEditPopover', () => { it('prevents saving if the filter is invalid', () => { const { wrapper } = setup(); expect(wrapper.find(Button).find({ disabled: true })).toHaveLength(0); - wrapper.instance().onAdhocFilterChange(simpleAdhocFilter.duplicateWith({ operator: null })); + wrapper + .instance() + .onAdhocFilterChange(simpleAdhocFilter.duplicateWith({ operator: null })); expect(wrapper.find(Button).find({ disabled: true })).toHaveLength(1); wrapper.instance().onAdhocFilterChange(sqlAdhocFilter); expect(wrapper.find(Button).find({ disabled: true })).toHaveLength(0); diff --git a/superset/assets/spec/javascripts/explore/components/AdhocFilterOption_spec.jsx b/superset/assets/spec/javascripts/explore/components/AdhocFilterOption_spec.jsx index 2a1830a2c4c..c24a3cf0ef9 100644 --- a/superset/assets/spec/javascripts/explore/components/AdhocFilterOption_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/AdhocFilterOption_spec.jsx @@ -22,7 +22,10 @@ import sinon from 'sinon'; import { shallow } from 'enzyme'; import { Label, OverlayTrigger } from 'react-bootstrap'; -import AdhocFilter, { EXPRESSION_TYPES, CLAUSES } from '../../../../src/explore/AdhocFilter'; +import AdhocFilter, { + EXPRESSION_TYPES, + CLAUSES, +} from '../../../../src/explore/AdhocFilter'; import AdhocFilterOption from '../../../../src/explore/components/AdhocFilterOption'; const simpleAdhocFilter = new AdhocFilter({ diff --git a/superset/assets/spec/javascripts/explore/components/AdhocMetricEditPopoverTitle_spec.jsx b/superset/assets/spec/javascripts/explore/components/AdhocMetricEditPopoverTitle_spec.jsx index 0300f7fb1a8..15416a11430 100644 --- a/superset/assets/spec/javascripts/explore/components/AdhocMetricEditPopoverTitle_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/AdhocMetricEditPopoverTitle_spec.jsx @@ -52,7 +52,12 @@ describe('AdhocMetricEditPopoverTitle', () => { it('renders an OverlayTrigger wrapper with the title', () => { const { wrapper } = setup(); expect(wrapper.find(OverlayTrigger)).toHaveLength(1); - expect(wrapper.find(OverlayTrigger).find('span').text()).toBe('My Metric\xa0'); + expect( + wrapper + .find(OverlayTrigger) + .find('span') + .text(), + ).toBe('My Metric\xa0'); }); it('transfers to edit mode when clicked', () => { diff --git a/superset/assets/spec/javascripts/explore/components/AdhocMetricEditPopover_spec.jsx b/superset/assets/spec/javascripts/explore/components/AdhocMetricEditPopover_spec.jsx index a6106dbb9bf..36733749539 100644 --- a/superset/assets/spec/javascripts/explore/components/AdhocMetricEditPopover_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/AdhocMetricEditPopover_spec.jsx @@ -22,7 +22,9 @@ import sinon from 'sinon'; import { shallow } from 'enzyme'; import { Button, FormGroup, Popover } from 'react-bootstrap'; -import AdhocMetric, { EXPRESSION_TYPES } from '../../../../src/explore/AdhocMetric'; +import AdhocMetric, { + EXPRESSION_TYPES, +} from '../../../../src/explore/AdhocMetric'; import AdhocMetricEditPopover from '../../../../src/explore/components/AdhocMetricEditPopover'; import { AGGREGATES } from '../../../../src/explore/constants'; @@ -68,19 +70,25 @@ describe('AdhocMetricEditPopover', () => { it('overwrites the adhocMetric in state with onColumnChange', () => { const { wrapper } = setup(); wrapper.instance().onColumnChange(columns[0]); - expect(wrapper.state('adhocMetric')).toEqual(sumValueAdhocMetric.duplicateWith({ column: columns[0] })); + expect(wrapper.state('adhocMetric')).toEqual( + sumValueAdhocMetric.duplicateWith({ column: columns[0] }), + ); }); it('overwrites the adhocMetric in state with onAggregateChange', () => { const { wrapper } = setup(); wrapper.instance().onAggregateChange({ aggregate: AGGREGATES.AVG }); - expect(wrapper.state('adhocMetric')).toEqual(sumValueAdhocMetric.duplicateWith({ aggregate: AGGREGATES.AVG })); + expect(wrapper.state('adhocMetric')).toEqual( + sumValueAdhocMetric.duplicateWith({ aggregate: AGGREGATES.AVG }), + ); }); it('overwrites the adhocMetric in state with onSqlExpressionChange', () => { const { wrapper } = setup({ adhocMetric: sqlExpressionAdhocMetric }); wrapper.instance().onSqlExpressionChange('COUNT(1)'); - expect(wrapper.state('adhocMetric')).toEqual(sqlExpressionAdhocMetric.duplicateWith({ sqlExpression: 'COUNT(1)' })); + expect(wrapper.state('adhocMetric')).toEqual( + sqlExpressionAdhocMetric.duplicateWith({ sqlExpression: 'COUNT(1)' }), + ); }); it('overwrites the adhocMetric in state with onLabelChange', () => { diff --git a/superset/assets/spec/javascripts/explore/components/AdhocMetricStaticOption_spec.jsx b/superset/assets/spec/javascripts/explore/components/AdhocMetricStaticOption_spec.jsx index 96e6fabcff5..248468a6ba4 100644 --- a/superset/assets/spec/javascripts/explore/components/AdhocMetricStaticOption_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/AdhocMetricStaticOption_spec.jsx @@ -21,7 +21,9 @@ import React from 'react'; import { shallow } from 'enzyme'; import AdhocMetricStaticOption from '../../../../src/explore/components/AdhocMetricStaticOption'; -import AdhocMetric, { EXPRESSION_TYPES } from '../../../../src/explore/AdhocMetric'; +import AdhocMetric, { + EXPRESSION_TYPES, +} from '../../../../src/explore/AdhocMetric'; import { AGGREGATES } from '../../../../src/explore/constants'; const sumValueAdhocMetric = new AdhocMetric({ @@ -32,7 +34,9 @@ const sumValueAdhocMetric = new AdhocMetric({ describe('AdhocMetricStaticOption', () => { it('renders the adhoc metrics label', () => { - const wrapper = shallow(); + const wrapper = shallow( + , + ); expect(wrapper.text()).toBe('SUM(source)'); }); }); diff --git a/superset/assets/spec/javascripts/explore/components/AggregateOption_spec.jsx b/superset/assets/spec/javascripts/explore/components/AggregateOption_spec.jsx index 5300164370f..61a1912b716 100644 --- a/superset/assets/spec/javascripts/explore/components/AggregateOption_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/AggregateOption_spec.jsx @@ -24,7 +24,9 @@ import AggregateOption from '../../../../src/explore/components/AggregateOption' describe('AggregateOption', () => { it('renders the aggregate', () => { - const wrapper = shallow(); + const wrapper = shallow( + , + ); expect(wrapper.text()).toBe('SUM'); }); }); diff --git a/superset/assets/spec/javascripts/explore/components/BoundsControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/BoundsControl_spec.jsx index 5497d80826d..cc05a7f3488 100644 --- a/superset/assets/spec/javascripts/explore/components/BoundsControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/BoundsControl_spec.jsx @@ -42,13 +42,24 @@ describe('BoundsControl', () => { }); it('errors on non-numeric', () => { - wrapper.find(FormControl).first().simulate('change', { target: { value: 's' } }); + wrapper + .find(FormControl) + .first() + .simulate('change', { target: { value: 's' } }); expect(defaultProps.onChange.calledWith([null, null])).toBe(true); - expect(defaultProps.onChange.getCall(0).args[1][0]).toContain('value should be numeric'); + expect(defaultProps.onChange.getCall(0).args[1][0]).toContain( + 'value should be numeric', + ); }); it('casts to numeric', () => { - wrapper.find(FormControl).first().simulate('change', { target: { value: '1' } }); - wrapper.find(FormControl).last().simulate('change', { target: { value: '5' } }); + wrapper + .find(FormControl) + .first() + .simulate('change', { target: { value: '1' } }); + wrapper + .find(FormControl) + .last() + .simulate('change', { target: { value: '5' } }); expect(defaultProps.onChange.calledWith([1, 5])).toBe(true); }); }); diff --git a/superset/assets/spec/javascripts/explore/components/CheckboxControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/CheckboxControl_spec.jsx index 9e946194818..aafcc979231 100644 --- a/superset/assets/spec/javascripts/explore/components/CheckboxControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/CheckboxControl_spec.jsx @@ -54,7 +54,10 @@ describe('CheckboxControl', () => { fullComponent.instance().forceUpdate(); - fullComponent.find('label span').last().simulate('click'); + fullComponent + .find('label span') + .last() + .simulate('click'); expect(spy.calledOnce).toBe(true); }); diff --git a/superset/assets/spec/javascripts/explore/components/ColorPickerControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/ColorPickerControl_spec.jsx index c372e314a82..c01975eb804 100644 --- a/superset/assets/spec/javascripts/explore/components/ColorPickerControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/ColorPickerControl_spec.jsx @@ -21,14 +21,16 @@ import React from 'react'; import { shallow } from 'enzyme'; import { OverlayTrigger } from 'react-bootstrap'; import { SketchPicker } from 'react-color'; -import { CategoricalScheme, getCategoricalSchemeRegistry } from '@superset-ui/color'; +import { + CategoricalScheme, + getCategoricalSchemeRegistry, +} from '@superset-ui/color'; -import ColorPickerControl from - '../../../../src/explore/components/controls/ColorPickerControl'; +import ColorPickerControl from '../../../../src/explore/components/controls/ColorPickerControl'; import ControlHeader from '../../../../src/explore/components/ControlHeader'; const defaultProps = { - value: { }, + value: {}, }; describe('ColorPickerControl', () => { @@ -36,10 +38,13 @@ describe('ColorPickerControl', () => { let inst; beforeEach(() => { getCategoricalSchemeRegistry() - .registerValue('test', new CategoricalScheme({ - id: 'test', - colors: ['red', 'green', 'blue'], - })) + .registerValue( + 'test', + new CategoricalScheme({ + id: 'test', + colors: ['red', 'green', 'blue'], + }), + ) .setDefaultKey('test'); wrapper = shallow(); inst = wrapper.instance(); diff --git a/superset/assets/spec/javascripts/explore/components/ColorScheme_spec.jsx b/superset/assets/spec/javascripts/explore/components/ColorScheme_spec.jsx index c2b6a96604d..a1187a7e226 100644 --- a/superset/assets/spec/javascripts/explore/components/ColorScheme_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/ColorScheme_spec.jsx @@ -22,11 +22,12 @@ import { mount } from 'enzyme'; import { Creatable } from 'react-select'; import { getCategoricalSchemeRegistry } from '@superset-ui/color'; -import ColorSchemeControl from - '../../../../src/explore/components/controls/ColorSchemeControl'; +import ColorSchemeControl from '../../../../src/explore/components/controls/ColorSchemeControl'; const defaultProps = { - options: getCategoricalSchemeRegistry().keys().map(s => ([s, s])), + options: getCategoricalSchemeRegistry() + .keys() + .map(s => [s, s]), }; describe('ColorSchemeControl', () => { diff --git a/superset/assets/spec/javascripts/explore/components/ControlPanelSection_spec.jsx b/superset/assets/spec/javascripts/explore/components/ControlPanelSection_spec.jsx index c662d274cce..7e4f7478e9f 100644 --- a/superset/assets/spec/javascripts/explore/components/ControlPanelSection_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/ControlPanelSection_spec.jsx @@ -20,11 +20,9 @@ import React from 'react'; import { shallow } from 'enzyme'; import { Panel } from 'react-bootstrap'; -import InfoTooltipWithTrigger from - '../../../../src/components/InfoTooltipWithTrigger'; +import InfoTooltipWithTrigger from '../../../../src/components/InfoTooltipWithTrigger'; -import ControlPanelSection from - '../../../../src/explore/components/ControlPanelSection'; +import ControlPanelSection from '../../../../src/explore/components/ControlPanelSection'; const defaultProps = { children:
a child element
, @@ -57,11 +55,21 @@ describe('ControlPanelSection', () => { }); it('renders a label if present', () => { - expect(wrapper.find(Panel).dive().text()).toContain('my label'); + expect( + wrapper + .find(Panel) + .dive() + .text(), + ).toContain('my label'); }); it('renders a InfoTooltipWithTrigger if label and tooltip is present', () => { - expect(wrapper.find(Panel).dive().find(InfoTooltipWithTrigger)).toHaveLength(1); + expect( + wrapper + .find(Panel) + .dive() + .find(InfoTooltipWithTrigger), + ).toHaveLength(1); }); }); }); diff --git a/superset/assets/spec/javascripts/explore/components/ControlPanelsContainer_spec.jsx b/superset/assets/spec/javascripts/explore/components/ControlPanelsContainer_spec.jsx index 50dbe999dfb..4d9e383f880 100644 --- a/superset/assets/spec/javascripts/explore/components/ControlPanelsContainer_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/ControlPanelsContainer_spec.jsx @@ -29,15 +29,18 @@ import * as featureFlags from 'src/featureFlags'; describe('ControlPanelsContainer', () => { let wrapper; let scopedFilterOn = false; - const isFeatureEnabledMock = jest.spyOn(featureFlags, 'isFeatureEnabled') - .mockImplementation(() => scopedFilterOn); + const isFeatureEnabledMock = jest + .spyOn(featureFlags, 'isFeatureEnabled') + .mockImplementation(() => scopedFilterOn); beforeAll(() => { getChartControlPanelRegistry().registerValue('table', { controlPanelSections: [ { label: t('GROUP BY'), - description: t('Use this section if you want a query that aggregates'), + description: t( + 'Use this section if you want a query that aggregates', + ), expanded: true, controlSetRows: [ ['groupby'], @@ -60,9 +63,7 @@ describe('ControlPanelsContainer', () => { { label: t('Query'), expanded: true, - controlSetRows: [ - ['adhoc_filters'], - ], + controlSetRows: [['adhoc_filters']], }, { label: t('Options'), diff --git a/superset/assets/spec/javascripts/explore/components/DatasourceControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/DatasourceControl_spec.jsx index 47643a11af3..c40f4a150cd 100644 --- a/superset/assets/spec/javascripts/explore/components/DatasourceControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/DatasourceControl_spec.jsx @@ -47,7 +47,9 @@ describe('DatasourceControl', () => { function setup() { const mockStore = configureStore([]); const store = mockStore({}); - return shallow(, { context: { store } }); + return shallow(, { + context: { store }, + }); } it('renders a Modal', () => { diff --git a/superset/assets/spec/javascripts/explore/components/DateFilterControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/DateFilterControl_spec.jsx index 0eef849cc46..09c7d7f7958 100644 --- a/superset/assets/spec/javascripts/explore/components/DateFilterControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/DateFilterControl_spec.jsx @@ -70,7 +70,9 @@ describe('DateFilterControl', () => { label.simulate('click'); setTimeout(() => { expect(wrapper.find('.popover')).toHaveLength(1); - expect(wrapper.find('.ok')).first().simulate('click'); + expect(wrapper.find('.ok')) + .first() + .simulate('click'); setTimeout(() => { expect(wrapper.find('.popover')).toHaveLength(0); }, 10); diff --git a/superset/assets/spec/javascripts/explore/components/DisplayQueryButton_spec.jsx b/superset/assets/spec/javascripts/explore/components/DisplayQueryButton_spec.jsx index a7aec9caf7f..f2403541fab 100644 --- a/superset/assets/spec/javascripts/explore/components/DisplayQueryButton_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/DisplayQueryButton_spec.jsx @@ -37,7 +37,9 @@ describe('DisplayQueryButton', () => { }; it('is valid', () => { - expect(React.isValidElement()).toBe(true); + expect(React.isValidElement()).toBe( + true, + ); }); it('renders a dropdown', () => { const wrapper = mount(); diff --git a/superset/assets/spec/javascripts/explore/components/EmbedCodeButton_spec.jsx b/superset/assets/spec/javascripts/explore/components/EmbedCodeButton_spec.jsx index bf4f1998972..e2353638f78 100644 --- a/superset/assets/spec/javascripts/explore/components/EmbedCodeButton_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/EmbedCodeButton_spec.jsx @@ -30,7 +30,9 @@ describe('EmbedCodeButton', () => { }; it('renders', () => { - expect(React.isValidElement()).toBe(true); + expect(React.isValidElement()).toBe( + true, + ); }); it('renders overlay trigger', () => { @@ -39,13 +41,15 @@ describe('EmbedCodeButton', () => { }); it('returns correct embed code', () => { - const stub = sinon.stub(exploreUtils, 'getExploreLongUrl').callsFake(() => ('endpoint_url')); + const stub = sinon + .stub(exploreUtils, 'getExploreLongUrl') + .callsFake(() => 'endpoint_url'); const wrapper = mount(); wrapper.setState({ height: '1000', width: '2000', }); - const embedHTML = ( + const embedHTML = ' { ' scrolling="no"\n' + ' src="http://localhostendpoint_url&height=1000"\n' + '>\n' + - '' - ); + ''; expect(wrapper.instance().generateEmbedHTML()).toBe(embedHTML); stub.restore(); }); diff --git a/superset/assets/spec/javascripts/explore/components/ExploreActionButtons_spec.jsx b/superset/assets/spec/javascripts/explore/components/ExploreActionButtons_spec.jsx index 2f9297fef45..bc665058c11 100644 --- a/superset/assets/spec/javascripts/explore/components/ExploreActionButtons_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/ExploreActionButtons_spec.jsx @@ -18,8 +18,7 @@ */ import React from 'react'; import { shallow } from 'enzyme'; -import ExploreActionButtons from - '../../../../src/explore/components/ExploreActionButtons'; +import ExploreActionButtons from '../../../../src/explore/components/ExploreActionButtons'; describe('ExploreActionButtons', () => { const defaultProps = { diff --git a/superset/assets/spec/javascripts/explore/components/ExploreChartHeader_spec.jsx b/superset/assets/spec/javascripts/explore/components/ExploreChartHeader_spec.jsx index 583d7b9b023..ddc107ab5ec 100644 --- a/superset/assets/spec/javascripts/explore/components/ExploreChartHeader_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/ExploreChartHeader_spec.jsx @@ -44,9 +44,9 @@ describe('ExploreChartHeader', () => { }); it('is valid', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe( + true, + ); }); it('renders', () => { diff --git a/superset/assets/spec/javascripts/explore/components/ExploreChartPanel_spec.jsx b/superset/assets/spec/javascripts/explore/components/ExploreChartPanel_spec.jsx index fa1666895e4..2b1ad85f79d 100644 --- a/superset/assets/spec/javascripts/explore/components/ExploreChartPanel_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/ExploreChartPanel_spec.jsx @@ -28,8 +28,6 @@ describe('ChartContainer', () => { }; it('renders when vizType is line', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); }); diff --git a/superset/assets/spec/javascripts/explore/components/ExploreViewContainer_spec.jsx b/superset/assets/spec/javascripts/explore/components/ExploreViewContainer_spec.jsx index bdaa6420f64..5af8aad0acd 100644 --- a/superset/assets/spec/javascripts/explore/components/ExploreViewContainer_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/ExploreViewContainer_spec.jsx @@ -37,8 +37,9 @@ describe('ExploreViewContainer', () => { let isFeatureEnabledMock; beforeAll(() => { - isFeatureEnabledMock = jest.spyOn(featureFlags, 'isFeatureEnabled') - .mockReturnValue(false); + isFeatureEnabledMock = jest + .spyOn(featureFlags, 'isFeatureEnabled') + .mockReturnValue(false); const bootstrapData = { common: { @@ -63,9 +64,7 @@ describe('ExploreViewContainer', () => { }); it('renders', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('renders QueryAndSaveButtons', () => { @@ -83,8 +82,14 @@ describe('ExploreViewContainer', () => { describe('componentWillReceiveProps()', () => { it('when controls change, should call resetControls', () => { expect(wrapper.instance().props.controls.viz_type.value).toBe('table'); - const resetControls = sinon.stub(wrapper.instance().props.actions, 'resetControls'); - const triggerQuery = sinon.stub(wrapper.instance().props.actions, 'triggerQuery'); + const resetControls = sinon.stub( + wrapper.instance().props.actions, + 'resetControls', + ); + const triggerQuery = sinon.stub( + wrapper.instance().props.actions, + 'triggerQuery', + ); // triggers componentWillReceiveProps wrapper.setProps({ diff --git a/superset/assets/spec/javascripts/explore/components/FilterBoxItemControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/FilterBoxItemControl_spec.jsx index 4a4e4f8a2ae..77a4e8307bb 100644 --- a/superset/assets/spec/javascripts/explore/components/FilterBoxItemControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/FilterBoxItemControl_spec.jsx @@ -35,7 +35,7 @@ describe('FilterBoxItemControl', () => { let wrapper; let inst; - const getWrapper = (propOverrides) => { + const getWrapper = propOverrides => { const props = { ...defaultProps, ...propOverrides }; return shallow(); }; diff --git a/superset/assets/spec/javascripts/explore/components/FilterDefinitionOption_spec.jsx b/superset/assets/spec/javascripts/explore/components/FilterDefinitionOption_spec.jsx index dd3d13893d0..7ab7f5bf956 100644 --- a/superset/assets/spec/javascripts/explore/components/FilterDefinitionOption_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/FilterDefinitionOption_spec.jsx @@ -23,7 +23,9 @@ import { shallow } from 'enzyme'; import FilterDefinitionOption from '../../../../src/explore/components/FilterDefinitionOption'; import ColumnOption from '../../../../src/components/ColumnOption'; import AdhocMetricStaticOption from '../../../../src/explore/components/AdhocMetricStaticOption'; -import AdhocMetric, { EXPRESSION_TYPES } from '../../../../src/explore/AdhocMetric'; +import AdhocMetric, { + EXPRESSION_TYPES, +} from '../../../../src/explore/AdhocMetric'; import { AGGREGATES } from '../../../../src/explore/constants'; const sumValueAdhocMetric = new AdhocMetric({ @@ -34,19 +36,25 @@ const sumValueAdhocMetric = new AdhocMetric({ describe('FilterDefinitionOption', () => { it('renders a ColumnOption given a column', () => { - const wrapper = shallow(); + const wrapper = shallow( + , + ); expect(wrapper.find(ColumnOption)).toHaveLength(1); }); it('renders a AdhocMetricStaticOption given an adhoc metric', () => { - const wrapper = shallow(); + const wrapper = shallow( + , + ); expect(wrapper.find(AdhocMetricStaticOption)).toHaveLength(1); }); it('renders the metric name given a saved metric', () => { - const wrapper = shallow(( - - )); + const wrapper = shallow( + , + ); expect(wrapper.text()).toBe('my_custom_metric'); }); }); diff --git a/superset/assets/spec/javascripts/explore/components/FixedOrMetricControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/FixedOrMetricControl_spec.jsx index 0a6fc135e91..1ab42435fdf 100644 --- a/superset/assets/spec/javascripts/explore/components/FixedOrMetricControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/FixedOrMetricControl_spec.jsx @@ -20,16 +20,13 @@ import React from 'react'; import { shallow } from 'enzyme'; -import FixedOrMetricControl from - '../../../../src/explore/components/controls/FixedOrMetricControl'; -import TextControl from - '../../../../src/explore/components/controls/TextControl'; -import MetricsControl from - '../../../../src/explore/components/controls/MetricsControl'; +import FixedOrMetricControl from '../../../../src/explore/components/controls/FixedOrMetricControl'; +import TextControl from '../../../../src/explore/components/controls/TextControl'; +import MetricsControl from '../../../../src/explore/components/controls/MetricsControl'; const defaultProps = { - value: { }, - datasource: { }, + value: {}, + datasource: {}, }; describe('FixedOrMetricControl', () => { diff --git a/superset/assets/spec/javascripts/explore/components/MetricDefinitionOption_spec.jsx b/superset/assets/spec/javascripts/explore/components/MetricDefinitionOption_spec.jsx index 470c969b453..f1b55c8f86a 100644 --- a/superset/assets/spec/javascripts/explore/components/MetricDefinitionOption_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/MetricDefinitionOption_spec.jsx @@ -30,7 +30,9 @@ describe('MetricDefinitionOption', () => { const store = mockStore({}); function setup(props) { - return shallow(, { context: { store } }).dive(); + return shallow(, { + context: { store }, + }).dive(); } it('renders a MetricOption given a saved metric', () => { diff --git a/superset/assets/spec/javascripts/explore/components/MetricDefinitionValue_spec.jsx b/superset/assets/spec/javascripts/explore/components/MetricDefinitionValue_spec.jsx index f357991a530..2a326be509c 100644 --- a/superset/assets/spec/javascripts/explore/components/MetricDefinitionValue_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/MetricDefinitionValue_spec.jsx @@ -33,14 +33,19 @@ const sumValueAdhocMetric = new AdhocMetric({ describe('MetricDefinitionValue', () => { it('renders a MetricOption given a saved metric', () => { - const wrapper = shallow(); + const wrapper = shallow( + , + ); expect(wrapper.find(MetricOption)).toHaveLength(1); }); it('renders an AdhocMetricOption given an adhoc metric', () => { - const wrapper = shallow(( - {}} option={sumValueAdhocMetric} /> - )); + const wrapper = shallow( + {}} + option={sumValueAdhocMetric} + />, + ); expect(wrapper.find(AdhocMetricOption)).toHaveLength(1); }); }); diff --git a/superset/assets/spec/javascripts/explore/components/MetricsControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/MetricsControl_spec.jsx index b6fbf1bacd3..a27b5240645 100644 --- a/superset/assets/spec/javascripts/explore/components/MetricsControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/MetricsControl_spec.jsx @@ -24,7 +24,9 @@ import { shallow } from 'enzyme'; import MetricsControl from '../../../../src/explore/components/controls/MetricsControl'; import { AGGREGATES } from '../../../../src/explore/constants'; import OnPasteSelect from '../../../../src/components/OnPasteSelect'; -import AdhocMetric, { EXPRESSION_TYPES } from '../../../../src/explore/AdhocMetric'; +import AdhocMetric, { + EXPRESSION_TYPES, +} from '../../../../src/explore/AdhocMetric'; const defaultProps = { name: 'metrics', @@ -63,33 +65,56 @@ const sumValueAdhocMetric = new AdhocMetric({ }); describe('MetricsControl', () => { - it('renders an OnPasteSelect', () => { const { wrapper } = setup(); expect(wrapper.find(OnPasteSelect)).toHaveLength(1); }); describe('constructor', () => { - it('unifies options for the dropdown select with aggregates', () => { const { wrapper } = setup(); expect(wrapper.state('options')).toEqual([ - { optionName: '_col_source', type: 'VARCHAR(255)', column_name: 'source' }, - { optionName: '_col_target', type: 'VARCHAR(255)', column_name: 'target' }, + { + optionName: '_col_source', + type: 'VARCHAR(255)', + column_name: 'source', + }, + { + optionName: '_col_target', + type: 'VARCHAR(255)', + column_name: 'target', + }, { optionName: '_col_value', type: 'DOUBLE', column_name: 'value' }, - ...Object.keys(AGGREGATES).map( - aggregate => ({ aggregate_name: aggregate, optionName: '_aggregate_' + aggregate }), - ), - { optionName: 'sum__value', metric_name: 'sum__value', expression: 'SUM(energy_usage.value)' }, - { optionName: 'avg__value', metric_name: 'avg__value', expression: 'AVG(energy_usage.value)' }, + ...Object.keys(AGGREGATES).map(aggregate => ({ + aggregate_name: aggregate, + optionName: '_aggregate_' + aggregate, + })), + { + optionName: 'sum__value', + metric_name: 'sum__value', + expression: 'SUM(energy_usage.value)', + }, + { + optionName: 'avg__value', + metric_name: 'avg__value', + expression: 'AVG(energy_usage.value)', + }, ]); }); it('does not show aggregates in options if no columns', () => { const { wrapper } = setup({ columns: [] }); expect(wrapper.state('options')).toEqual([ - { optionName: 'sum__value', metric_name: 'sum__value', expression: 'SUM(energy_usage.value)' }, - { optionName: 'avg__value', metric_name: 'avg__value', expression: 'AVG(energy_usage.value)' }, + { + optionName: 'sum__value', + metric_name: 'sum__value', + expression: 'SUM(energy_usage.value)', + }, + { + optionName: 'avg__value', + metric_name: 'avg__value', + expression: 'AVG(energy_usage.value)', + }, ]); }); @@ -124,11 +149,9 @@ describe('MetricsControl', () => { 'avg__value', ]); }); - }); describe('onChange', () => { - it('handles saved metrics being selected', () => { const { wrapper, onChange } = setup(); const select = wrapper.find(OnPasteSelect); @@ -143,16 +166,20 @@ describe('MetricsControl', () => { const adhocMetric = onChange.lastCall.args[0][0]; expect(adhocMetric instanceof AdhocMetric).toBe(true); - expect(onChange.lastCall.args).toEqual([[{ - expressionType: EXPRESSION_TYPES.SIMPLE, - column: valueColumn, - aggregate: AGGREGATES.SUM, - label: 'SUM(value)', - fromFormData: false, - hasCustomLabel: false, - optionName: adhocMetric.optionName, - sqlExpression: null, - }]]); + expect(onChange.lastCall.args).toEqual([ + [ + { + expressionType: EXPRESSION_TYPES.SIMPLE, + column: valueColumn, + aggregate: AGGREGATES.SUM, + label: 'SUM(value)', + fromFormData: false, + hasCustomLabel: false, + optionName: adhocMetric.optionName, + sqlExpression: null, + }, + ], + ]); }); it('handles aggregates being selected', () => { @@ -171,15 +198,22 @@ describe('MetricsControl', () => { select.simulate('change', [{ aggregate_name: 'SUM', optionName: 'SUM' }]); expect(setInputSpy.calledWith('SUM()')).toBe(true); - expect(handleInputSpy.calledWith({ target: { value: 'SUM()' } })).toBe(true); + expect(handleInputSpy.calledWith({ target: { value: 'SUM()' } })).toBe( + true, + ); expect(onChange.lastCall.args).toEqual([[]]); }); it('preserves existing selected AdhocMetrics', () => { const { wrapper, onChange } = setup(); const select = wrapper.find(OnPasteSelect); - select.simulate('change', [{ metric_name: 'sum__value' }, sumValueAdhocMetric]); - expect(onChange.lastCall.args).toEqual([['sum__value', sumValueAdhocMetric]]); + select.simulate('change', [ + { metric_name: 'sum__value' }, + sumValueAdhocMetric, + ]); + expect(onChange.lastCall.args).toEqual([ + ['sum__value', sumValueAdhocMetric], + ]); }); }); @@ -189,12 +223,12 @@ describe('MetricsControl', () => { value: [sumValueAdhocMetric], }); - const editedMetric = sumValueAdhocMetric.duplicateWith({ aggregate: AGGREGATES.AVG }); + const editedMetric = sumValueAdhocMetric.duplicateWith({ + aggregate: AGGREGATES.AVG, + }); wrapper.instance().onMetricEdit(editedMetric); - expect(onChange.lastCall.args).toEqual([[ - editedMetric, - ]]); + expect(onChange.lastCall.args).toEqual([[editedMetric]]); }); }); @@ -220,118 +254,151 @@ describe('MetricsControl', () => { it('includes user defined metrics', () => { const { wrapper } = setup({ datasourceType: 'druid' }); - expect(!!wrapper.instance().selectFilterOption( - { - metric_name: 'a_metric', - optionName: 'a_metric', - expression: 'SUM(FANCY(metric))', - }, - 'a', - )).toBe(true); + expect( + !!wrapper.instance().selectFilterOption( + { + metric_name: 'a_metric', + optionName: 'a_metric', + expression: 'SUM(FANCY(metric))', + }, + 'a', + ), + ).toBe(true); }); it('includes auto generated avg metrics for druid', () => { const { wrapper } = setup({ datasourceType: 'druid' }); - expect(!!wrapper.instance().selectFilterOption( - { - metric_name: 'avg__metric', - optionName: 'avg__metric', - expression: 'AVG(metric)', - }, - 'a', - )).toBe(true); + expect( + !!wrapper.instance().selectFilterOption( + { + metric_name: 'avg__metric', + optionName: 'avg__metric', + expression: 'AVG(metric)', + }, + 'a', + ), + ).toBe(true); }); it('includes columns and aggregates', () => { const { wrapper } = setup(); - expect(!!wrapper.instance().selectFilterOption( - { type: 'VARCHAR(255)', column_name: 'source', optionName: '_col_source' }, - 'sou', - )).toBe(true); + expect( + !!wrapper.instance().selectFilterOption( + { + type: 'VARCHAR(255)', + column_name: 'source', + optionName: '_col_source', + }, + 'sou', + ), + ).toBe(true); - expect(!!wrapper.instance().selectFilterOption( - { aggregate_name: 'AVG', optionName: '_aggregate_AVG' }, - 'av', - )).toBe(true); + expect( + !!wrapper + .instance() + .selectFilterOption( + { aggregate_name: 'AVG', optionName: '_aggregate_AVG' }, + 'av', + ), + ).toBe(true); }); it('includes columns based on verbose_name', () => { const { wrapper } = setup(); - expect(!!wrapper.instance().selectFilterOption( - { metric_name: 'sum__num', verbose_name: 'babies', optionName: '_col_sum_num' }, - 'bab', - )).toBe(true); + expect( + !!wrapper.instance().selectFilterOption( + { + metric_name: 'sum__num', + verbose_name: 'babies', + optionName: '_col_sum_num', + }, + 'bab', + ), + ).toBe(true); }); it('excludes auto generated avg metrics for sqla', () => { const { wrapper } = setup(); - expect(!!wrapper.instance().selectFilterOption( - { - metric_name: 'avg__metric', - optionName: 'avg__metric', - expression: 'AVG(metric)', - }, - 'a', - )).toBe(false); + expect( + !!wrapper.instance().selectFilterOption( + { + metric_name: 'avg__metric', + optionName: 'avg__metric', + expression: 'AVG(metric)', + }, + 'a', + ), + ).toBe(false); }); it('includes custom made simple saved metrics', () => { const { wrapper } = setup(); - expect(!!wrapper.instance().selectFilterOption( - { - metric_name: 'my_fancy_sum_metric', - optionName: 'my_fancy_sum_metric', - expression: 'SUM(value)', - }, - 'sum', - )).toBe(true); + expect( + !!wrapper.instance().selectFilterOption( + { + metric_name: 'my_fancy_sum_metric', + optionName: 'my_fancy_sum_metric', + expression: 'SUM(value)', + }, + 'sum', + ), + ).toBe(true); }); it('excludes auto generated metrics', () => { const { wrapper } = setup(); - expect(!!wrapper.instance().selectFilterOption( - { - metric_name: 'sum__value', - optionName: 'sum__value', - expression: 'SUM(value)', - }, - 'sum', - )).toBe(false); + expect( + !!wrapper.instance().selectFilterOption( + { + metric_name: 'sum__value', + optionName: 'sum__value', + expression: 'SUM(value)', + }, + 'sum', + ), + ).toBe(false); - expect(!!wrapper.instance().selectFilterOption( - { - metric_name: 'sum__value', - optionName: 'sum__value', - expression: 'SUM("table"."value")', - }, - 'sum', - )).toBe(false); + expect( + !!wrapper.instance().selectFilterOption( + { + metric_name: 'sum__value', + optionName: 'sum__value', + expression: 'SUM("table"."value")', + }, + 'sum', + ), + ).toBe(false); }); it('filters out metrics if the input begins with an aggregate', () => { const { wrapper } = setup(); wrapper.setState({ aggregateInInput: true }); - expect(!!wrapper.instance().selectFilterOption( - { metric_name: 'metric', expression: 'SUM(FANCY(metric))' }, - 'SUM(', - )).toBe(false); + expect( + !!wrapper + .instance() + .selectFilterOption( + { metric_name: 'metric', expression: 'SUM(FANCY(metric))' }, + 'SUM(', + ), + ).toBe(false); }); it('includes columns if the input begins with an aggregate', () => { const { wrapper } = setup(); wrapper.setState({ aggregateInInput: true }); - expect(!!wrapper.instance().selectFilterOption( - { type: 'DOUBLE', column_name: 'value' }, - 'SUM(', - )).toBe(true); + expect( + !!wrapper + .instance() + .selectFilterOption({ type: 'DOUBLE', column_name: 'value' }, 'SUM('), + ).toBe(true); }); it('Removes metrics if savedMetrics changes', () => { diff --git a/superset/assets/spec/javascripts/explore/components/QueryAndSaveBtns_spec.jsx b/superset/assets/spec/javascripts/explore/components/QueryAndSaveBtns_spec.jsx index 14d0c254fab..7ddfb99a801 100644 --- a/superset/assets/spec/javascripts/explore/components/QueryAndSaveBtns_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/QueryAndSaveBtns_spec.jsx @@ -31,7 +31,9 @@ describe('QueryAndSaveButtons', () => { // It must render it('renders', () => { - expect(React.isValidElement()).toBe(true); + expect( + React.isValidElement(), + ).toBe(true); }); // Test the output diff --git a/superset/assets/spec/javascripts/explore/components/RowCountLabel_spec.jsx b/superset/assets/spec/javascripts/explore/components/RowCountLabel_spec.jsx index 5589a549a5c..3d4cff84f22 100644 --- a/superset/assets/spec/javascripts/explore/components/RowCountLabel_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/RowCountLabel_spec.jsx @@ -31,7 +31,9 @@ describe('RowCountLabel', () => { }; it('is valid', () => { - expect(React.isValidElement()).toBe(true); + expect(React.isValidElement()).toBe( + true, + ); }); it('renders a Label and a TooltipWrapper', () => { const wrapper = shallow(); @@ -44,6 +46,11 @@ describe('RowCountLabel', () => { limit: 100, }; const wrapper = shallow(); - expect(wrapper.find(Label).first().props().bsStyle).toBe('danger'); + expect( + wrapper + .find(Label) + .first() + .props().bsStyle, + ).toBe('danger'); }); }); diff --git a/superset/assets/spec/javascripts/explore/components/RunQueryActionButton_spec.jsx b/superset/assets/spec/javascripts/explore/components/RunQueryActionButton_spec.jsx index ab2c93f6697..73221973c8f 100644 --- a/superset/assets/spec/javascripts/explore/components/RunQueryActionButton_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/RunQueryActionButton_spec.jsx @@ -19,8 +19,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import RunQueryActionButton - from '../../../../src/SqlLab/components/RunQueryActionButton'; +import RunQueryActionButton from '../../../../src/SqlLab/components/RunQueryActionButton'; import Button from '../../../../src/components/Button'; describe('RunQueryActionButton', () => { diff --git a/superset/assets/spec/javascripts/explore/components/SaveModal_spec.jsx b/superset/assets/spec/javascripts/explore/components/SaveModal_spec.jsx index d334688a4d0..1353e0e938e 100644 --- a/superset/assets/spec/javascripts/explore/components/SaveModal_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/SaveModal_spec.jsx @@ -53,7 +53,7 @@ describe('SaveModal', () => { const defaultProps = { onHide: () => ({}), - actions: bindActionCreators(saveModalActions, (arg) => { + actions: bindActionCreators(saveModalActions, arg => { if (typeof arg === 'function') { return arg(jest.fn); } @@ -144,11 +144,11 @@ describe('SaveModal', () => { .stub(exploreUtils, 'getExploreUrlAndPayload') .callsFake(() => ({ url: 'mockURL', payload: defaultProps.form_data })); - sinon - .stub(defaultProps.actions, 'saveSlice') - .callsFake(() => - Promise.resolve({ data: { dashboard: '/mock/', slice: { slice_url: '/mock/' } } }), - ); + sinon.stub(defaultProps.actions, 'saveSlice').callsFake(() => + Promise.resolve({ + data: { dashboard: '/mock/', slice: { slice_url: '/mock/' } }, + }), + ); }); afterEach(() => { @@ -223,15 +223,14 @@ describe('SaveModal', () => { return actionThunk(dispatch); }; - it('makes the fetch request', () => ( + it('makes the fetch request', () => makeRequest().then(() => { expect(fetchMock.calls(saveEndpoint)).toHaveLength(1); return Promise.resolve(); - }) - )); + })); - it('calls correct actions on success', () => ( + it('calls correct actions on success', () => makeRequest().then(() => { expect(dispatch.callCount).toBe(1); expect(dispatch.getCall(0).args[0].type).toBe( @@ -239,17 +238,24 @@ describe('SaveModal', () => { ); return Promise.resolve(); - }) - )); + })); it('calls correct actions on error', () => { - fetchMock.get(saveEndpoint, { throws: 'error' }, { overwriteRoutes: true }); + fetchMock.get( + saveEndpoint, + { throws: 'error' }, + { overwriteRoutes: true }, + ); return makeRequest().then(() => { expect(dispatch.callCount).toBe(1); - expect(dispatch.getCall(0).args[0].type).toBe(saveModalActions.FETCH_DASHBOARDS_FAILED); + expect(dispatch.getCall(0).args[0].type).toBe( + saveModalActions.FETCH_DASHBOARDS_FAILED, + ); - fetchMock.get(saveEndpoint, mockDashboardData, { overwriteRoutes: true }); + fetchMock.get(saveEndpoint, mockDashboardData, { + overwriteRoutes: true, + }); return Promise.resolve(); }); diff --git a/superset/assets/spec/javascripts/explore/components/SelectControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/SelectControl_spec.jsx index ce20aab4627..05e47d56c0e 100644 --- a/superset/assets/spec/javascripts/explore/components/SelectControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/SelectControl_spec.jsx @@ -27,7 +27,10 @@ import VirtualizedRendererWrap from '../../../../src/components/VirtualizedRende import SelectControl from '../../../../src/explore/components/controls/SelectControl'; const defaultProps = { - choices: [['1 year ago', '1 year ago'], ['today', 'today']], + choices: [ + ['1 year ago', '1 year ago'], + ['today', 'today'], + ], name: 'row_limit', label: 'Row Limit', valueKey: 'value', // shallow isn't passing SelectControl.defaultProps.valueKey through @@ -115,8 +118,11 @@ describe('SelectControl', () => { valueKey: 'value', }; wrapper.setProps(selectAllProps); - expect(wrapper.instance().getOptions(selectAllProps)) - .toContainEqual({ label: 'Select All', meta: true, value: 'Select All' }); + expect(wrapper.instance().getOptions(selectAllProps)).toContainEqual({ + label: 'Select All', + meta: true, + value: 'Select All', + }); }); it('returns the correct options when freeform is set to true', () => { @@ -145,7 +151,10 @@ describe('SelectControl', () => { { value: 'four', label: 'four' }, ]; const newProps = { - choices: [['three', 'three'], ['four', 'four']], + choices: [ + ['three', 'three'], + ['four', 'four'], + ], name: 'name', freeForm: false, value: null, diff --git a/superset/assets/spec/javascripts/explore/components/ViewportControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/ViewportControl_spec.jsx index ada75ea9d6d..768a05eec34 100644 --- a/superset/assets/spec/javascripts/explore/components/ViewportControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/ViewportControl_spec.jsx @@ -21,10 +21,8 @@ import React from 'react'; import { shallow } from 'enzyme'; import { OverlayTrigger, Label } from 'react-bootstrap'; -import ViewportControl from - '../../../../src/explore/components/controls/ViewportControl'; -import TextControl from - '../../../../src/explore/components/controls/TextControl'; +import ViewportControl from '../../../../src/explore/components/controls/ViewportControl'; +import TextControl from '../../../../src/explore/components/controls/TextControl'; import ControlHeader from '../../../../src/explore/components/ControlHeader'; const defaultProps = { @@ -57,6 +55,12 @@ describe('ViewportControl', () => { }); it('renders a summary in the label', () => { - expect(wrapper.find(Label).first().render().text()).toBe('6° 51\' 8.50" | 31° 13\' 21.56"'); + expect( + wrapper + .find(Label) + .first() + .render() + .text(), + ).toBe('6° 51\' 8.50" | 31° 13\' 21.56"'); }); }); diff --git a/superset/assets/spec/javascripts/explore/components/VizTypeControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/VizTypeControl_spec.jsx index 8941d1bbd12..e266e9f76c5 100644 --- a/superset/assets/spec/javascripts/explore/components/VizTypeControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/VizTypeControl_spec.jsx @@ -35,14 +35,20 @@ describe('VizTypeControl', () => { const registry = getChartMetadataRegistry(); registry - .registerValue('vis1', new ChartMetadata({ - name: 'vis1', - thumbnail: '', - })) - .registerValue('vis2', new ChartMetadata({ - name: 'vis2', - thumbnail: '', - })); + .registerValue( + 'vis1', + new ChartMetadata({ + name: 'vis1', + thumbnail: '', + }), + ) + .registerValue( + 'vis2', + new ChartMetadata({ + name: 'vis2', + thumbnail: '', + }), + ); beforeEach(() => { wrapper = shallow(); diff --git a/superset/assets/spec/javascripts/explore/components/withVerification_spec.jsx b/superset/assets/spec/javascripts/explore/components/withVerification_spec.jsx index bef21406954..f7cd15728dc 100644 --- a/superset/assets/spec/javascripts/explore/components/withVerification_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/withVerification_spec.jsx @@ -42,7 +42,10 @@ const defaultProps = { getEndpoint: controlValues => `valid_metrics?data=${controlValues}`, }; -const VALID_METRIC = { metric_name: 'sum__value', expression: 'SUM(energy_usage.value)' }; +const VALID_METRIC = { + metric_name: 'sum__value', + expression: 'SUM(energy_usage.value)', +}; function setup(overrides, validMetric) { const onChange = sinon.spy(); @@ -51,16 +54,22 @@ function setup(overrides, validMetric) { ...defaultProps, ...overrides, }; - const VerifiedControl = withVerification(MetricsControl, 'metric_name', 'savedMetrics'); + const VerifiedControl = withVerification( + MetricsControl, + 'metric_name', + 'savedMetrics', + ); const wrapper = shallow(); - fetchMock.mock('glob:*/valid_metrics*', validMetric || `["${VALID_METRIC.metric_name}"]`); + fetchMock.mock( + 'glob:*/valid_metrics*', + validMetric || `["${VALID_METRIC.metric_name}"]`, + ); return { props, wrapper, onChange }; } afterEach(fetchMock.restore); describe('VerifiedMetricsControl', () => { - it('Gets valid options', () => { const { wrapper } = setup(); setTimeout(() => { @@ -92,7 +101,9 @@ describe('VerifiedMetricsControl', () => { }); it('Calls endpoint if control values change', () => { - const { props, wrapper } = setup({ controlValues: { metrics: 'sum__value' } }); + const { props, wrapper } = setup({ + controlValues: { metrics: 'sum__value' }, + }); setTimeout(() => { expect(fetchMock.calls(defaultProps.getEndpoint())).toHaveLength(1); fetchMock.reset(); diff --git a/superset/assets/spec/javascripts/explore/controlUtils_spec.jsx b/superset/assets/spec/javascripts/explore/controlUtils_spec.jsx index 12117293ad5..2725a9a975e 100644 --- a/superset/assets/spec/javascripts/explore/controlUtils_spec.jsx +++ b/superset/assets/spec/javascripts/explore/controlUtils_spec.jsx @@ -27,57 +27,55 @@ import { describe('controlUtils', () => { const state = { datasource: { - columns: [ - 'a', 'b', 'c', - ], - metrics: [ - { metric_name: 'first' }, - { metric_name: 'second' }, - ], + columns: ['a', 'b', 'c'], + metrics: [{ metric_name: 'first' }, { metric_name: 'second' }], }, }; beforeAll(() => { - getChartControlPanelRegistry().registerValue('test-chart', { - requiresTime: true, - controlPanelSections: [ - { - label: t('Chart Options'), - expanded: true, - controlSetRows: [ - ['color_scheme', { - name: 'rose_area_proportion', - config: { - type: 'CheckboxControl', - label: t('Use Area Proportions'), - description: t( - 'Check if the Rose Chart should use segment area instead of ' + - 'segment radius for proportioning', - ), - default: false, - renderTrigger: true, - }, - }], - ], + getChartControlPanelRegistry() + .registerValue('test-chart', { + requiresTime: true, + controlPanelSections: [ + { + label: t('Chart Options'), + expanded: true, + controlSetRows: [ + [ + 'color_scheme', + { + name: 'rose_area_proportion', + config: { + type: 'CheckboxControl', + label: t('Use Area Proportions'), + description: t( + 'Check if the Rose Chart should use segment area instead of ' + + 'segment radius for proportioning', + ), + default: false, + renderTrigger: true, + }, + }, + ], + ], + }, + ], + }) + .registerValue('test-chart-override', { + requiresTime: true, + controlPanelSections: [ + { + label: t('Chart Options'), + expanded: true, + controlSetRows: [['color_scheme']], + }, + ], + controlOverrides: { + color_scheme: { + label: t('My beautiful colors'), + }, }, - ], - }).registerValue('test-chart-override', { - requiresTime: true, - controlPanelSections: [ - { - label: t('Chart Options'), - expanded: true, - controlSetRows: [ - ['color_scheme'], - ], - }, - ], - controlOverrides: { - color_scheme: { - label: t('My beautiful colors'), - }, - }, - }); + }); }); afterAll(() => { @@ -101,20 +99,26 @@ describe('controlUtils', () => { expect(control.label).toEqual('My beautiful colors'); }); - it('returns correct control config when control config is defined ' + - 'in the control panel definition', () => { - const roseAreaProportionControlConfig = getControlConfig('rose_area_proportion', 'test-chart'); + it( + 'returns correct control config when control config is defined ' + + 'in the control panel definition', + () => { + const roseAreaProportionControlConfig = getControlConfig( + 'rose_area_proportion', + 'test-chart', + ); expect(roseAreaProportionControlConfig).toEqual({ type: 'CheckboxControl', label: t('Use Area Proportions'), description: t( 'Check if the Rose Chart should use segment area instead of ' + - 'segment radius for proportioning', + 'segment radius for proportioning', ), default: false, renderTrigger: true, - }); - }); + }); + }, + ); }); describe('applyMapStateToPropsToControl,', () => { @@ -129,7 +133,6 @@ describe('controlUtils', () => { control = applyMapStateToPropsToControl(control, state); expect(control.mapStateToProps).toBe(undefined); }); - }); describe('getControlState', () => { diff --git a/superset/assets/spec/javascripts/explore/exploreActions_spec.js b/superset/assets/spec/javascripts/explore/exploreActions_spec.js index 22e02495882..54c39b013e7 100644 --- a/superset/assets/spec/javascripts/explore/exploreActions_spec.js +++ b/superset/assets/spec/javascripts/explore/exploreActions_spec.js @@ -24,12 +24,16 @@ import * as actions from '../../../src/explore/actions/exploreActions'; describe('reducers', () => { it('sets correct control value given a key and value', () => { const newState = exploreReducer( - defaultState, actions.setControlValue('x_axis_label', 'x', [])); + defaultState, + actions.setControlValue('x_axis_label', 'x', []), + ); expect(newState.controls.x_axis_label.value).toBe('x'); }); it('setControlValue works as expected with a checkbox', () => { - const newState = exploreReducer(defaultState, - actions.setControlValue('show_legend', true, [])); + const newState = exploreReducer( + defaultState, + actions.setControlValue('show_legend', true, []), + ); expect(newState.controls.show_legend.value).toBe(true); }); }); diff --git a/superset/assets/spec/javascripts/explore/store_spec.jsx b/superset/assets/spec/javascripts/explore/store_spec.jsx index 470cb162605..80e3d1051b7 100644 --- a/superset/assets/spec/javascripts/explore/store_spec.jsx +++ b/superset/assets/spec/javascripts/explore/store_spec.jsx @@ -37,7 +37,6 @@ describe('store', () => { }); describe('applyDefaultFormData', () => { - window.featureFlags = { SCOPED_FILTER: false, }; @@ -77,6 +76,5 @@ describe('store', () => { const outputFormData = applyDefaultFormData(inputFormData); expect(outputFormData.this_should_no_be_here).toBe(undefined); }); - }); }); diff --git a/superset/assets/spec/javascripts/explore/utils_spec.jsx b/superset/assets/spec/javascripts/explore/utils_spec.jsx index 3845233e2db..4efc21d3d5b 100644 --- a/superset/assets/spec/javascripts/explore/utils_spec.jsx +++ b/superset/assets/spec/javascripts/explore/utils_spec.jsx @@ -19,7 +19,10 @@ import sinon from 'sinon'; import URI from 'urijs'; -import { getExploreUrlAndPayload, getExploreLongUrl } from '../../../src/explore/exploreUtils'; +import { + getExploreUrlAndPayload, + getExploreLongUrl, +} from '../../../src/explore/exploreUtils'; import * as hostNamesConfig from '../../../src/utils/hostNamesConfig'; describe('exploreUtils', () => { @@ -44,10 +47,7 @@ describe('exploreUtils', () => { force: false, curUrl: 'http://superset.com', }); - compareURI( - URI(url), - URI('/superset/explore/'), - ); + compareURI(URI(url), URI('/superset/explore/')); expect(payload).toEqual(formData); }); it('generates proper json url', () => { @@ -57,10 +57,7 @@ describe('exploreUtils', () => { force: false, curUrl: 'http://superset.com', }); - compareURI( - URI(url), - URI('/superset/explore_json/'), - ); + compareURI(URI(url), URI('/superset/explore_json/')); expect(payload).toEqual(formData); }); it('generates proper json forced url', () => { @@ -72,8 +69,7 @@ describe('exploreUtils', () => { }); compareURI( URI(url), - URI('/superset/explore_json/') - .search({ force: 'true' }), + URI('/superset/explore_json/').search({ force: 'true' }), ); expect(payload).toEqual(formData); }); @@ -86,8 +82,7 @@ describe('exploreUtils', () => { }); compareURI( URI(url), - URI('/superset/explore_json/') - .search({ csv: 'true' }), + URI('/superset/explore_json/').search({ csv: 'true' }), ); expect(payload).toEqual(formData); }); @@ -100,8 +95,7 @@ describe('exploreUtils', () => { }); compareURI( URI(url), - URI('/superset/explore/') - .search({ standalone: 'true' }), + URI('/superset/explore/').search({ standalone: 'true' }), ); expect(payload).toEqual(formData); }); @@ -114,8 +108,7 @@ describe('exploreUtils', () => { }); compareURI( URI(url), - URI('/superset/explore_json/') - .search({ foo: 'bar' }), + URI('/superset/explore_json/').search({ foo: 'bar' }), ); expect(payload).toEqual(formData); }); @@ -128,8 +121,7 @@ describe('exploreUtils', () => { }); compareURI( URI(url), - URI('/superset/explore_json/') - .search({ foo: 'bar' }), + URI('/superset/explore_json/').search({ foo: 'bar' }), ); expect(payload).toEqual(formData); }); @@ -142,8 +134,7 @@ describe('exploreUtils', () => { }); compareURI( URI(url), - URI('/superset/explore_json/') - .search({ foo: 'bar' }), + URI('/superset/explore_json/').search({ foo: 'bar' }), ); expect(payload).toEqual(formData); }); @@ -153,10 +144,14 @@ describe('exploreUtils', () => { let stub; const availableDomains = [ 'http://localhost/', - 'domain1.com', 'domain2.com', 'domain3.com', + 'domain1.com', + 'domain2.com', + 'domain3.com', ]; beforeEach(() => { - stub = sinon.stub(hostNamesConfig, 'availableDomains').value(availableDomains); + stub = sinon + .stub(hostNamesConfig, 'availableDomains') + .value(availableDomains); }); afterEach(() => { stub.restore(); diff --git a/superset/assets/spec/javascripts/messageToasts/.prettierrc b/superset/assets/spec/javascripts/messageToasts/.prettierrc deleted file mode 100644 index a20502b7f06..00000000000 --- a/superset/assets/spec/javascripts/messageToasts/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all" -} diff --git a/superset/assets/spec/javascripts/middleware/logger_spec.js b/superset/assets/spec/javascripts/middleware/logger_spec.js index c5f5a4a6c6a..e2b7b9a8a28 100644 --- a/superset/assets/spec/javascripts/middleware/logger_spec.js +++ b/superset/assets/spec/javascripts/middleware/logger_spec.js @@ -70,33 +70,32 @@ describe('logger middleware', () => { clock.tick(2000); expect(SupersetClient.post.callCount).toBe(1); - expect(SupersetClient.post.getCall(0).args[0].endpoint).toMatch('/superset/log/'); + expect(SupersetClient.post.getCall(0).args[0].endpoint).toMatch( + '/superset/log/', + ); }); - it( - 'should include ts, start_offset, event_name, impression_id, source, and source_id in every event', - () => { - const clock = sinon.useFakeTimers(); - logger(mockStore)(next)(action); - clock.tick(2000); + it('should include ts, start_offset, event_name, impression_id, source, and source_id in every event', () => { + const clock = sinon.useFakeTimers(); + logger(mockStore)(next)(action); + clock.tick(2000); - expect(SupersetClient.post.callCount).toBe(1); - const events = SupersetClient.post.getCall(0).args[0].postPayload.events; - const mockEventdata = action.payload.eventData; - const mockEventname = action.payload.eventName; - expect(events[0]).toMatchObject({ - key: mockEventdata.key, - event_name: mockEventname, - impression_id: mockStore.getState().impressionId, - source: 'dashboard', - source_id: mockStore.getState().dashboardInfo.id, - event_type: 'timing', - }); + expect(SupersetClient.post.callCount).toBe(1); + const events = SupersetClient.post.getCall(0).args[0].postPayload.events; + const mockEventdata = action.payload.eventData; + const mockEventname = action.payload.eventName; + expect(events[0]).toMatchObject({ + key: mockEventdata.key, + event_name: mockEventname, + impression_id: mockStore.getState().impressionId, + source: 'dashboard', + source_id: mockStore.getState().dashboardInfo.id, + event_type: 'timing', + }); - expect(typeof events[0].ts).toBe('number'); - expect(typeof events[0].start_offset).toBe('number'); - }, - ); + expect(typeof events[0].ts).toBe('number'); + expect(typeof events[0].start_offset).toBe('number'); + }); it('should debounce a few log requests to one', () => { const clock = sinon.useFakeTimers(); @@ -106,6 +105,8 @@ describe('logger middleware', () => { clock.tick(2000); expect(SupersetClient.post.callCount).toBe(1); - expect(SupersetClient.post.getCall(0).args[0].postPayload.events).toHaveLength(3); + expect( + SupersetClient.post.getCall(0).args[0].postPayload.events, + ).toHaveLength(3); }); }); diff --git a/superset/assets/spec/javascripts/modules/utils_spec.jsx b/superset/assets/spec/javascripts/modules/utils_spec.jsx index 842f54b5045..4db2f0dfe56 100644 --- a/superset/assets/spec/javascripts/modules/utils_spec.jsx +++ b/superset/assets/spec/javascripts/modules/utils_spec.jsx @@ -44,24 +44,15 @@ describe('utils', () => { expect(mainMetric(null)).toBeUndefined(); }); it('prefers the "count" metric when first', () => { - const metrics = [ - { metric_name: 'count' }, - { metric_name: 'foo' }, - ]; + const metrics = [{ metric_name: 'count' }, { metric_name: 'foo' }]; expect(mainMetric(metrics)).toBe('count'); }); it('prefers the "count" metric when not first', () => { - const metrics = [ - { metric_name: 'foo' }, - { metric_name: 'count' }, - ]; + const metrics = [{ metric_name: 'foo' }, { metric_name: 'count' }]; expect(mainMetric(metrics)).toBe('count'); }); it('selects the first metric when "count" is not an option', () => { - const metrics = [ - { metric_name: 'foo' }, - { metric_name: 'not_count' }, - ]; + const metrics = [{ metric_name: 'foo' }, { metric_name: 'not_count' }]; expect(mainMetric(metrics)).toBe('foo'); }); }); diff --git a/superset/assets/spec/javascripts/profile/App_spec.jsx b/superset/assets/spec/javascripts/profile/App_spec.jsx index 7896a4caaf4..7a7224945b3 100644 --- a/superset/assets/spec/javascripts/profile/App_spec.jsx +++ b/superset/assets/spec/javascripts/profile/App_spec.jsx @@ -28,9 +28,7 @@ describe('App', () => { user, }; it('is valid', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('renders 2 Col', () => { diff --git a/superset/assets/spec/javascripts/profile/CreatedContent_spec.jsx b/superset/assets/spec/javascripts/profile/CreatedContent_spec.jsx index af8130235b4..f2deca00223 100644 --- a/superset/assets/spec/javascripts/profile/CreatedContent_spec.jsx +++ b/superset/assets/spec/javascripts/profile/CreatedContent_spec.jsx @@ -35,12 +35,16 @@ describe('CreatedContent', () => { }; it('renders 2 TableLoader', () => { - const wrapper = shallow(, { context: { store } }); + const wrapper = shallow(, { + context: { store }, + }); expect(wrapper.find(TableLoader)).toHaveLength(2); }); it('renders 2 titles', () => { - const wrapper = shallow(, { context: { store } }); + const wrapper = shallow(, { + context: { store }, + }); expect(wrapper.find('h3')).toHaveLength(2); }); }); diff --git a/superset/assets/spec/javascripts/profile/EditableTitle_spec.jsx b/superset/assets/spec/javascripts/profile/EditableTitle_spec.jsx index 4013a636ac9..270dfa0a381 100644 --- a/superset/assets/spec/javascripts/profile/EditableTitle_spec.jsx +++ b/superset/assets/spec/javascripts/profile/EditableTitle_spec.jsx @@ -35,11 +35,11 @@ describe('EditableTitle', () => { }, }; const editableWrapper = shallow(); - const notEditableWrapper = shallow(); + const notEditableWrapper = shallow( + , + ); it('is valid', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('should render title', () => { const titleElement = editableWrapper.find('input'); diff --git a/superset/assets/spec/javascripts/profile/Favorites_spec.jsx b/superset/assets/spec/javascripts/profile/Favorites_spec.jsx index 6248d18efc3..75482da046b 100644 --- a/superset/assets/spec/javascripts/profile/Favorites_spec.jsx +++ b/superset/assets/spec/javascripts/profile/Favorites_spec.jsx @@ -35,12 +35,16 @@ describe('Favorites', () => { }; it('renders 2 TableLoader', () => { - const wrapper = shallow(, { context: { store } }); + const wrapper = shallow(, { + context: { store }, + }); expect(wrapper.find(TableLoader)).toHaveLength(2); }); it('renders 2 titles', () => { - const wrapper = shallow(, { context: { store } }); + const wrapper = shallow(, { + context: { store }, + }); expect(wrapper.find('h3')).toHaveLength(2); }); }); diff --git a/superset/assets/spec/javascripts/profile/RecentActivity_spec.jsx b/superset/assets/spec/javascripts/profile/RecentActivity_spec.jsx index c99db2f58ac..08d6b6f19ba 100644 --- a/superset/assets/spec/javascripts/profile/RecentActivity_spec.jsx +++ b/superset/assets/spec/javascripts/profile/RecentActivity_spec.jsx @@ -28,9 +28,9 @@ describe('RecentActivity', () => { user, }; it('is valid', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe( + true, + ); }); it('renders a TableLoader', () => { diff --git a/superset/assets/spec/javascripts/profile/Security_spec.jsx b/superset/assets/spec/javascripts/profile/Security_spec.jsx index 79d6e878380..50dd541ac51 100644 --- a/superset/assets/spec/javascripts/profile/Security_spec.jsx +++ b/superset/assets/spec/javascripts/profile/Security_spec.jsx @@ -22,15 +22,12 @@ import { mount } from 'enzyme'; import { user, userNoPerms } from './fixtures'; import Security from '../../../src/profile/components/Security'; - describe('Security', () => { const mockedProps = { user, }; it('is valid', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('renders 2 role labels', () => { const wrapper = mount(); diff --git a/superset/assets/spec/javascripts/profile/UserInfo_spec.jsx b/superset/assets/spec/javascripts/profile/UserInfo_spec.jsx index d232ef6a320..0f9c1108d92 100644 --- a/superset/assets/spec/javascripts/profile/UserInfo_spec.jsx +++ b/superset/assets/spec/javascripts/profile/UserInfo_spec.jsx @@ -24,15 +24,12 @@ import { mount } from 'enzyme'; import { user } from './fixtures'; import UserInfo from '../../../src/profile/components/UserInfo'; - describe('UserInfo', () => { const mockedProps = { user, }; it('is valid', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('renders a Gravatar', () => { const wrapper = mount(); diff --git a/superset/assets/spec/javascripts/profile/fixtures.jsx b/superset/assets/spec/javascripts/profile/fixtures.jsx index da907a011e4..2b378c5adb7 100644 --- a/superset/assets/spec/javascripts/profile/fixtures.jsx +++ b/superset/assets/spec/javascripts/profile/fixtures.jsx @@ -20,40 +20,16 @@ export const user = { username: 'alpha', roles: { Alpha: [ - [ - 'can_this_form_post', - 'ResetMyPasswordView', - ], - [ - 'can_this_form_get', - 'ResetMyPasswordView', - ], - [ - 'can_this_form_post', - 'UserInfoEditView', - ], - [ - 'can_this_form_get', - 'UserInfoEditView', - ], + ['can_this_form_post', 'ResetMyPasswordView'], + ['can_this_form_get', 'ResetMyPasswordView'], + ['can_this_form_post', 'UserInfoEditView'], + ['can_this_form_get', 'UserInfoEditView'], ], sql_lab: [ - [ - 'menu_access', - 'SQL Lab', - ], - [ - 'can_sql_json', - 'Superset', - ], - [ - 'can_search_queries', - 'Superset', - ], - [ - 'can_csv', - 'Superset', - ], + ['menu_access', 'SQL Lab'], + ['can_sql_json', 'Superset'], + ['can_search_queries', 'Superset'], + ['can_csv', 'Superset'], ], }, firstName: 'alpha', diff --git a/superset/assets/spec/javascripts/showSavedQuery/utils_spec.jsx b/superset/assets/spec/javascripts/showSavedQuery/utils_spec.jsx index d198a49180f..fd0e9135808 100644 --- a/superset/assets/spec/javascripts/showSavedQuery/utils_spec.jsx +++ b/superset/assets/spec/javascripts/showSavedQuery/utils_spec.jsx @@ -43,7 +43,6 @@ describe('getNestedValue', () => { }); }); - describe('interpolate', () => { it('is a function', () => { expect(typeof interpolate).toBe('function'); diff --git a/superset/assets/spec/javascripts/sqllab/ColumnElement_spec.jsx b/superset/assets/spec/javascripts/sqllab/ColumnElement_spec.jsx index ff7c2671a5b..47d0ffdcb78 100644 --- a/superset/assets/spec/javascripts/sqllab/ColumnElement_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/ColumnElement_spec.jsx @@ -22,31 +22,43 @@ import { mount } from 'enzyme'; import { mockedActions, table } from './fixtures'; import ColumnElement from '../../../src/SqlLab/components/ColumnElement'; - describe('ColumnElement', () => { const mockedProps = { actions: mockedActions, column: table.columns[0], }; it('is valid with props', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('renders a proper primary key', () => { const wrapper = mount(); expect(wrapper.find('i.fa-key')).toHaveLength(1); - expect(wrapper.find('.col-name').first().text()).toBe('id'); + expect( + wrapper + .find('.col-name') + .first() + .text(), + ).toBe('id'); }); it('renders a multi-key column', () => { const wrapper = mount(); expect(wrapper.find('i.fa-link')).toHaveLength(1); expect(wrapper.find('i.fa-bookmark')).toHaveLength(1); - expect(wrapper.find('.col-name').first().text()).toBe('first_name'); + expect( + wrapper + .find('.col-name') + .first() + .text(), + ).toBe('first_name'); }); it('renders a column with no keys', () => { const wrapper = mount(); expect(wrapper.find('i')).toHaveLength(0); - expect(wrapper.find('.col-name').first().text()).toBe('last_name'); + expect( + wrapper + .find('.col-name') + .first() + .text(), + ).toBe('last_name'); }); }); diff --git a/superset/assets/spec/javascripts/sqllab/ExploreResultsButton_spec.jsx b/superset/assets/spec/javascripts/sqllab/ExploreResultsButton_spec.jsx index 70fca920d4f..8e4315f9aba 100644 --- a/superset/assets/spec/javascripts/sqllab/ExploreResultsButton_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/ExploreResultsButton_spec.jsx @@ -99,7 +99,12 @@ describe('ExploreResultsButton', () => { }); const badCols = wrapper.instance().getInvalidColumns(); - expect(badCols).toEqual(['COUNT(*)', '1', '123', 'CASE WHEN 1=1 THEN 1 ELSE 0 END']); + expect(badCols).toEqual([ + 'COUNT(*)', + '1', + '123', + 'CASE WHEN 1=1 THEN 1 ELSE 0 END', + ]); const msgWrapper = shallow(wrapper.instance().renderInvalidColumnMessage()); expect(msgWrapper.find('div')).toHaveLength(1); @@ -175,11 +180,14 @@ describe('ExploreResultsButton', () => { fetchMock.post(visualizeEndpoint, visualizationPayload); beforeEach(() => { - sinon - .stub(exploreUtils, 'getExploreUrlAndPayload') - .callsFake(() => ({ url: 'mockURL', payload: { datasource: '107__table' } })); + sinon.stub(exploreUtils, 'getExploreUrlAndPayload').callsFake(() => ({ + url: 'mockURL', + payload: { datasource: '107__table' }, + })); sinon.spy(exploreUtils, 'exportChart'); - sinon.stub(wrapper.instance(), 'buildVizOptions').callsFake(() => mockOptions); + sinon + .stub(wrapper.instance(), 'buildVizOptions') + .callsFake(() => mockOptions); }); afterEach(() => { exploreUtils.getExploreUrlAndPayload.restore(); @@ -188,7 +196,7 @@ describe('ExploreResultsButton', () => { fetchMock.reset(); }); - it('should build request with correct args', (done) => { + it('should build request with correct args', done => { wrapper.instance().visualize(); setTimeout(() => { @@ -196,7 +204,7 @@ describe('ExploreResultsButton', () => { expect(calls).toHaveLength(1); const formData = calls[0][1].body; - Object.keys(mockOptions).forEach((key) => { + Object.keys(mockOptions).forEach(key => { // eslint-disable-next-line no-unused-expressions expect(formData.get(key)).toBeDefined(); }); @@ -205,7 +213,7 @@ describe('ExploreResultsButton', () => { }); }); - it('should export chart and add an info toast', (done) => { + it('should export chart and add an info toast', done => { const infoToastSpy = sinon.spy(); const datasourceSpy = sinon.stub(); @@ -223,13 +231,15 @@ describe('ExploreResultsButton', () => { setTimeout(() => { expect(datasourceSpy.callCount).toBe(1); expect(exploreUtils.exportChart.callCount).toBe(1); - expect(exploreUtils.exportChart.getCall(0).args[0].datasource).toBe('107__table'); + expect(exploreUtils.exportChart.getCall(0).args[0].datasource).toBe( + '107__table', + ); expect(infoToastSpy.callCount).toBe(1); done(); }); }); - it('should add error toast', (done) => { + it('should add error toast', done => { const dangerToastSpy = sinon.stub(actions, 'addDangerToast'); const datasourceSpy = sinon.stub(); diff --git a/superset/assets/spec/javascripts/sqllab/HighlightedSql_spec.jsx b/superset/assets/spec/javascripts/sqllab/HighlightedSql_spec.jsx index b75f7ee3376..db8794a09dd 100644 --- a/superset/assets/spec/javascripts/sqllab/HighlightedSql_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/HighlightedSql_spec.jsx @@ -23,9 +23,9 @@ import { mount, shallow } from 'enzyme'; import HighlightedSql from '../../../src/SqlLab/components/HighlightedSql'; import ModalTrigger from '../../../src/components/ModalTrigger'; - describe('HighlightedSql', () => { - const sql = "SELECT * FROM test WHERE something='fkldasjfklajdslfkjadlskfjkldasjfkladsjfkdjsa'"; + const sql = + "SELECT * FROM test WHERE something='fkldasjfklajdslfkjadlskfjkldasjfkladsjfkdjsa'"; it('renders with props', () => { expect(React.isValidElement()).toBe(true); }); @@ -39,7 +39,13 @@ describe('HighlightedSql', () => { }); it('renders two SyntaxHighlighter in modal', () => { const wrapper = mount( - ); + , + ); const pre = wrapper.find('pre'); expect(pre).toHaveLength(1); pre.simulate('click'); diff --git a/superset/assets/spec/javascripts/sqllab/LimitControl_spec.jsx b/superset/assets/spec/javascripts/sqllab/LimitControl_spec.jsx index 5e8d743cfbc..c42126b163a 100644 --- a/superset/assets/spec/javascripts/sqllab/LimitControl_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/LimitControl_spec.jsx @@ -36,7 +36,9 @@ describe('LimitControl', () => { wrapper = shallow(factory(defaultProps)); }); it('is a valid element', () => { - expect(React.isValidElement()).toEqual(true); + expect(React.isValidElement()).toEqual( + true, + ); }); it('renders a Label', () => { expect(wrapper.find(Label)).toHaveLength(1); @@ -45,37 +47,68 @@ describe('LimitControl', () => { const value = 100; wrapper = shallow(factory({ ...defaultProps, value })); expect(wrapper.state().textValue).toEqual(value.toString()); - wrapper.find(Label).first().simulate('click'); + wrapper + .find(Label) + .first() + .simulate('click'); expect(wrapper.state().showOverlay).toBe(true); - expect(wrapper.find(ControlHeader).props().validationErrors).toHaveLength(0); + expect(wrapper.find(ControlHeader).props().validationErrors).toHaveLength( + 0, + ); }); it('handles invalid value', () => { - wrapper.find(Label).first().simulate('click'); + wrapper + .find(Label) + .first() + .simulate('click'); wrapper.setState({ textValue: 'invalid' }); - expect(wrapper.find(ControlHeader).props().validationErrors).toHaveLength(1); + expect(wrapper.find(ControlHeader).props().validationErrors).toHaveLength( + 1, + ); }); it('handles negative value', () => { - wrapper.find(Label).first().simulate('click'); + wrapper + .find(Label) + .first() + .simulate('click'); wrapper.setState({ textValue: '-1' }); - expect(wrapper.find(ControlHeader).props().validationErrors).toHaveLength(1); + expect(wrapper.find(ControlHeader).props().validationErrors).toHaveLength( + 1, + ); }); it('handles value above max row', () => { - wrapper.find(Label).first().simulate('click'); + wrapper + .find(Label) + .first() + .simulate('click'); wrapper.setState({ textValue: (defaultProps.maxRow + 1).toString() }); - expect(wrapper.find(ControlHeader).props().validationErrors).toHaveLength(1); + expect(wrapper.find(ControlHeader).props().validationErrors).toHaveLength( + 1, + ); }); it('opens and closes', () => { - wrapper.find(Label).first().simulate('click'); + wrapper + .find(Label) + .first() + .simulate('click'); expect(wrapper.state().showOverlay).toBe(true); - wrapper.find('.ok').first().simulate('click'); + wrapper + .find('.ok') + .first() + .simulate('click'); expect(wrapper.state().showOverlay).toBe(false); }); it('resets and closes', () => { const value = 100; wrapper = shallow(factory({ ...defaultProps, value })); - wrapper.find(Label).first().simulate('click'); + wrapper + .find(Label) + .first() + .simulate('click'); expect(wrapper.state().textValue).toEqual(value.toString()); wrapper.find('.reset').simulate('click'); - expect(wrapper.state().textValue).toEqual(defaultProps.defaultQueryLimit.toString()); + expect(wrapper.state().textValue).toEqual( + defaultProps.defaultQueryLimit.toString(), + ); }); }); diff --git a/superset/assets/spec/javascripts/sqllab/Link_spec.jsx b/superset/assets/spec/javascripts/sqllab/Link_spec.jsx index 89116be1cf4..2df2eb39c6e 100644 --- a/superset/assets/spec/javascripts/sqllab/Link_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/Link_spec.jsx @@ -30,12 +30,10 @@ describe('Link', () => { expect(React.isValidElement(TEST)).toBe(true); }); it('renders with props', () => { - expect( - React.isValidElement(TEST), - ).toBe(true); + expect(React.isValidElement(TEST)).toBe(true); }); it('renders an anchor tag', () => { - const wrapper = shallow(TEST); + const wrapper = shallow(TEST); expect(wrapper.find('a')).toHaveLength(1); }); }); diff --git a/superset/assets/spec/javascripts/sqllab/QueryAutoRefresh_spec.jsx b/superset/assets/spec/javascripts/sqllab/QueryAutoRefresh_spec.jsx index 527ecd68098..77ac0a29373 100644 --- a/superset/assets/spec/javascripts/sqllab/QueryAutoRefresh_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/QueryAutoRefresh_spec.jsx @@ -37,14 +37,13 @@ describe('QueryAutoRefresh', () => { const state = { ...initialState, sqlLab, - }; const store = mockStore(state); - const getWrapper = () => ( + const getWrapper = () => shallow(, { context: { store }, - }).dive()); + }).dive(); let wrapper; diff --git a/superset/assets/spec/javascripts/sqllab/QuerySearch_spec.jsx b/superset/assets/spec/javascripts/sqllab/QuerySearch_spec.jsx index dca3d9972c5..b04a47e1712 100644 --- a/superset/assets/spec/javascripts/sqllab/QuerySearch_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/QuerySearch_spec.jsx @@ -31,9 +31,7 @@ describe('QuerySearch', () => { height: 0, }; it('is valid', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); let wrapper; beforeEach(() => { @@ -45,19 +43,18 @@ describe('QuerySearch', () => { }); it('updates fromTime on user selects from time', () => { - wrapper.find('[name="select-from"]') - .simulate('change', { value: 0 }); + wrapper.find('[name="select-from"]').simulate('change', { value: 0 }); expect(wrapper.state().from).toBe(0); }); it('updates toTime on user selects to time', () => { - wrapper.find('[name="select-to"]') - .simulate('change', { value: 0 }); + wrapper.find('[name="select-to"]').simulate('change', { value: 0 }); expect(wrapper.state().to).toBe(0); }); it('updates status on user selects status', () => { - wrapper.find('[name="select-status"]') + wrapper + .find('[name="select-status"]') .simulate('change', { value: 'success' }); expect(wrapper.state().status).toBe('success'); }); diff --git a/superset/assets/spec/javascripts/sqllab/QueryStateLabel_spec.jsx b/superset/assets/spec/javascripts/sqllab/QueryStateLabel_spec.jsx index 5ff8f975640..4c5e15766ca 100644 --- a/superset/assets/spec/javascripts/sqllab/QueryStateLabel_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/QueryStateLabel_spec.jsx @@ -29,9 +29,9 @@ describe('SavedQuery', () => { }, }; it('is valid', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe( + true, + ); }); it('has an Overlay and a Popover', () => { const wrapper = shallow(); diff --git a/superset/assets/spec/javascripts/sqllab/QueryTable_spec.jsx b/superset/assets/spec/javascripts/sqllab/QueryTable_spec.jsx index c48612cd05f..7f38ceb7275 100644 --- a/superset/assets/spec/javascripts/sqllab/QueryTable_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/QueryTable_spec.jsx @@ -31,14 +31,23 @@ describe('QueryTable', () => { expect(React.isValidElement()).toBe(true); }); it('is valid with props', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('renders a proper table', () => { const wrapper = shallow(); expect(wrapper.find(Table)).toHaveLength(1); - expect(wrapper.find(Table).shallow().find('table')).toHaveLength(1); - expect(wrapper.find(Table).shallow().find('table').find('Tr')).toHaveLength(2); + expect( + wrapper + .find(Table) + .shallow() + .find('table'), + ).toHaveLength(1); + expect( + wrapper + .find(Table) + .shallow() + .find('table') + .find('Tr'), + ).toHaveLength(2); }); }); diff --git a/superset/assets/spec/javascripts/sqllab/ResultSet_spec.jsx b/superset/assets/spec/javascripts/sqllab/ResultSet_spec.jsx index 8aa00a9a989..803083ef209 100644 --- a/superset/assets/spec/javascripts/sqllab/ResultSet_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/ResultSet_spec.jsx @@ -102,13 +102,16 @@ describe('ResultSet', () => { wrapper.setProps({ query: emptyResults }); expect(wrapper.find(FilterableTable)).toHaveLength(0); expect(wrapper.find(Alert)).toHaveLength(1); - expect(wrapper.find(Alert).shallow().text()).toBe('The query returned no data'); + expect( + wrapper + .find(Alert) + .shallow() + .text(), + ).toBe('The query returned no data'); }); it('should render cached query', () => { const wrapper = shallow(); - const cachedData = [ - { col1: 'a', col2: 'b' }, - ]; + const cachedData = [{ col1: 'a', col2: 'b' }]; wrapper.setState({ data: cachedData }); const filterableTable = wrapper.find(FilterableTable); expect(filterableTable.props().data).toBe(cachedData); diff --git a/superset/assets/spec/javascripts/sqllab/SaveQuery_spec.jsx b/superset/assets/spec/javascripts/sqllab/SaveQuery_spec.jsx index 46c215ceca1..508558f84af 100644 --- a/superset/assets/spec/javascripts/sqllab/SaveQuery_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/SaveQuery_spec.jsx @@ -35,14 +35,10 @@ describe('SavedQuery', () => { animation: false, }; it('is valid', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('is valid with props', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('has a ModalTrigger', () => { const wrapper = shallow(); @@ -63,7 +59,10 @@ describe('SavedQuery', () => { const wrapper = shallow(); const modal = shallow(wrapper.instance().renderModalBody()); expect(modal.find(Button)).toHaveLength(2); - modal.find(Button).at(0).simulate('click'); + modal + .find(Button) + .at(0) + .simulate('click'); expect(saveSpy.calledOnce).toBe(true); }); it('has an update button if this is an existing query', () => { @@ -78,7 +77,10 @@ describe('SavedQuery', () => { const wrapper = shallow(); const modal = shallow(wrapper.instance().renderModalBody()); expect(modal.find(Button)).toHaveLength(3); - modal.find(Button).at(0).simulate('click'); + modal + .find(Button) + .at(0) + .simulate('click'); expect(updateSpy.calledOnce).toBe(true); }); }); diff --git a/superset/assets/spec/javascripts/sqllab/ShareSqlLabQuery_spec.jsx b/superset/assets/spec/javascripts/sqllab/ShareSqlLabQuery_spec.jsx index a6c7de9e293..c17295dfc1a 100644 --- a/superset/assets/spec/javascripts/sqllab/ShareSqlLabQuery_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/ShareSqlLabQuery_spec.jsx @@ -54,9 +54,12 @@ describe('ShareSqlLabQuery', () => { }; function setup(overrideProps) { - const wrapper = shallow(, { - context: { store }, - }).dive(); // wrapped in withToasts HOC + const wrapper = shallow( + , + { + context: { store }, + }, + ).dive(); // wrapped in withToasts HOC return wrapper; } @@ -80,7 +83,9 @@ describe('ShareSqlLabQuery', () => { return instance.getCopyUrl().then(() => { expect(storeQuerySpy.mock.calls).toHaveLength(1); expect(fetchMock.calls(storeQueryUrl)).toHaveLength(1); - expect(storeQuerySpy.mock.calls[0][0]).toMatchObject(defaultProps.queryEditor); + expect(storeQuerySpy.mock.calls[0][0]).toMatchObject( + defaultProps.queryEditor, + ); expect(instance.state.shortUrl).toContain(storeQueryMockId); return Promise.resolve(); diff --git a/superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx b/superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx index b67b2e6729e..914ce8f8ecd 100644 --- a/superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx @@ -24,7 +24,9 @@ import { shallow } from 'enzyme'; import { STATUS_OPTIONS } from '../../../src/SqlLab/constants'; import { initialState } from './fixtures'; -import SouthPaneContainer, { SouthPane } from '../../../src/SqlLab/components/SouthPane'; +import SouthPaneContainer, { + SouthPane, +} from '../../../src/SqlLab/components/SouthPane'; import ResultSet from '../../../src/SqlLab/components/ResultSet'; describe('SouthPane', () => { @@ -34,10 +36,38 @@ describe('SouthPane', () => { const mockedProps = { editorQueries: [ - { cached: false, changedOn: Date.now(), db: 'main', dbId: 1, id: 'LCly_kkIN', startDttm: Date.now() }, - { cached: false, changedOn: 1559238500401, db: 'main', dbId: 1, id: 'lXJa7F9_r', startDttm: 1559238500401 }, - { cached: false, changedOn: 1559238506925, db: 'main', dbId: 1, id: '2g2_iRFMl', startDttm: 1559238506925 }, - { cached: false, changedOn: 1559238516395, db: 'main', dbId: 1, id: 'erWdqEWPm', startDttm: 1559238516395 }, + { + cached: false, + changedOn: Date.now(), + db: 'main', + dbId: 1, + id: 'LCly_kkIN', + startDttm: Date.now(), + }, + { + cached: false, + changedOn: 1559238500401, + db: 'main', + dbId: 1, + id: 'lXJa7F9_r', + startDttm: 1559238500401, + }, + { + cached: false, + changedOn: 1559238506925, + db: 'main', + dbId: 1, + id: '2g2_iRFMl', + startDttm: 1559238506925, + }, + { + cached: false, + changedOn: 1559238516395, + db: 'main', + dbId: 1, + id: 'erWdqEWPm', + startDttm: 1559238516395, + }, ], latestQueryId: 'LCly_kkIN', dataPreviewQueries: [], @@ -48,25 +78,34 @@ describe('SouthPane', () => { offline: false, }; - const getWrapper = () => ( + const getWrapper = () => shallow(, { context: { store }, - }).dive()); + }).dive(); let wrapper; beforeAll(() => { - jest.spyOn(SouthPane.prototype, 'getSouthPaneHeight').mockImplementation(() => 500); + jest + .spyOn(SouthPane.prototype, 'getSouthPaneHeight') + .mockImplementation(() => 500); }); it('should render offline when the state is offline', () => { wrapper = getWrapper(); wrapper.setProps({ offline: true }); - expect(wrapper.find('.m-r-3').render().text()).toBe(STATUS_OPTIONS.offline); + expect( + wrapper + .find('.m-r-3') + .render() + .text(), + ).toBe(STATUS_OPTIONS.offline); }); it('should pass latest query down to ResultSet component', () => { wrapper = getWrapper(); expect(wrapper.find(ResultSet)).toHaveLength(1); - expect(wrapper.find(ResultSet).props().query.id).toEqual(mockedProps.latestQueryId); + expect(wrapper.find(ResultSet).props().query.id).toEqual( + mockedProps.latestQueryId, + ); }); }); diff --git a/superset/assets/spec/javascripts/sqllab/SqlEditorLeftBar_spec.jsx b/superset/assets/spec/javascripts/sqllab/SqlEditorLeftBar_spec.jsx index 379bf6616bb..4f420a016c9 100644 --- a/superset/assets/spec/javascripts/sqllab/SqlEditorLeftBar_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/SqlEditorLeftBar_spec.jsx @@ -53,11 +53,12 @@ describe('SqlEditorLeftBar', () => { }); it('is valid', () => { - expect(React.isValidElement()).toBe(true); + expect(React.isValidElement()).toBe( + true, + ); }); it('renders a TableElement', () => { expect(wrapper.find(TableElement)).toHaveLength(1); }); - }); diff --git a/superset/assets/spec/javascripts/sqllab/SqlEditor_spec.jsx b/superset/assets/spec/javascripts/sqllab/SqlEditor_spec.jsx index dcf2609e885..cb1002f5de9 100644 --- a/superset/assets/spec/javascripts/sqllab/SqlEditor_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/SqlEditor_spec.jsx @@ -40,7 +40,7 @@ describe('SqlEditor', () => { queryEditor: initialState.sqlLab.queryEditors[0], latestQuery: queries[0], tables: [table], - getHeight: () => ('100px'), + getHeight: () => '100px', editorQueries: [], dataPreviewQueries: [], defaultQueryLimit: 1000, @@ -48,13 +48,13 @@ describe('SqlEditor', () => { }; beforeAll(() => { - jest.spyOn(SqlEditor.prototype, 'getSqlEditorHeight').mockImplementation(() => MOCKED_SQL_EDITOR_HEIGHT); + jest + .spyOn(SqlEditor.prototype, 'getSqlEditorHeight') + .mockImplementation(() => MOCKED_SQL_EDITOR_HEIGHT); }); it('is valid', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('render a SqlEditorLeftBar', () => { const wrapper = shallow(); @@ -70,21 +70,23 @@ describe('SqlEditor', () => { }); it('does not overflow the editor window', () => { const wrapper = shallow(); - const totalSize = parseFloat(wrapper.find(AceEditorWrapper).props().height) - + wrapper.find(SouthPane).props().height - + SQL_TOOLBAR_HEIGHT - + (SQL_EDITOR_GUTTER_MARGIN * 2) - + SQL_EDITOR_GUTTER_HEIGHT; + const totalSize = + parseFloat(wrapper.find(AceEditorWrapper).props().height) + + wrapper.find(SouthPane).props().height + + SQL_TOOLBAR_HEIGHT + + SQL_EDITOR_GUTTER_MARGIN * 2 + + SQL_EDITOR_GUTTER_HEIGHT; expect(totalSize).toEqual(MOCKED_SQL_EDITOR_HEIGHT); }); it('does not overflow the editor window after resizing', () => { const wrapper = shallow(); wrapper.setState({ height: 450 }); - const totalSize = parseFloat(wrapper.find(AceEditorWrapper).props().height) - + wrapper.find(SouthPane).props().height - + SQL_TOOLBAR_HEIGHT - + (SQL_EDITOR_GUTTER_MARGIN * 2) - + SQL_EDITOR_GUTTER_HEIGHT; + const totalSize = + parseFloat(wrapper.find(AceEditorWrapper).props().height) + + wrapper.find(SouthPane).props().height + + SQL_TOOLBAR_HEIGHT + + SQL_EDITOR_GUTTER_MARGIN * 2 + + SQL_EDITOR_GUTTER_HEIGHT; expect(totalSize).toEqual(450); }); it('render a LimitControl with default limit', () => { @@ -99,6 +101,8 @@ describe('SqlEditor', () => { const updatedProps = { ...mockedProps, queryEditor }; const wrapper = shallow(); expect(wrapper.find(LimitControl)).toHaveLength(1); - expect(wrapper.find(LimitControl).props().value).toEqual(queryEditor.queryLimit); + expect(wrapper.find(LimitControl).props().value).toEqual( + queryEditor.queryLimit, + ); }); }); diff --git a/superset/assets/spec/javascripts/sqllab/TabStatusIcon_spec.jsx b/superset/assets/spec/javascripts/sqllab/TabStatusIcon_spec.jsx index 055aeb9b574..990143eeb6f 100644 --- a/superset/assets/spec/javascripts/sqllab/TabStatusIcon_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/TabStatusIcon_spec.jsx @@ -24,7 +24,9 @@ import TabStatusIcon from '../../../src/SqlLab/components/TabStatusIcon'; function setup() { const onClose = sinon.spy(); - const wrapper = shallow(); + const wrapper = shallow( + , + ); return { wrapper, onClose }; } diff --git a/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx b/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx index 21e0ba331b1..eed1dd921c2 100644 --- a/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx @@ -34,26 +34,27 @@ describe('TabbedSqlEditors', () => { const mockStore = configureStore(middlewares); const store = mockStore(initialState); - const tabHistory = [ - 'dfsadfs', - 'newEditorId', + const tabHistory = ['dfsadfs', 'newEditorId']; + + const tables = [ + Object.assign({}, table, { + dataPreviewQueryId: 'B1-VQU1zW', + queryEditorId: 'newEditorId', + }), ]; - const tables = [Object.assign({}, table, { - dataPreviewQueryId: 'B1-VQU1zW', - queryEditorId: 'newEditorId', - })]; - - const queryEditors = [{ - autorun: false, - dbId: 1, - id: 'newEditorId', - latestQueryId: 'B1-VQU1zW', - schema: null, - selectedText: null, - sql: 'SELECT ds...', - title: 'Untitled Query', - }]; + const queryEditors = [ + { + autorun: false, + dbId: 1, + id: 'newEditorId', + latestQueryId: 'B1-VQU1zW', + schema: null, + selectedText: null, + sql: 'SELECT ds...', + title: 'Untitled Query', + }, + ]; const queries = { 'B1-VQU1zW': { id: 'B1-VQU1zW', @@ -69,21 +70,21 @@ describe('TabbedSqlEditors', () => { queryEditors: initialState.sqlLab.queryEditors, tabHistory: initialState.sqlLab.tabHistory, editorHeight: '', - getHeight: () => ('100px'), + getHeight: () => '100px', database: {}, defaultQueryLimit: 1000, maxRow: 100000, }; - const getWrapper = () => ( + const getWrapper = () => shallow(, { context: { store }, - }).dive()); + }).dive(); let wrapper; it('is valid', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe( + true, + ); }); describe('componentDidMount', () => { let uriStub; @@ -102,24 +103,36 @@ describe('TabbedSqlEditors', () => { wrapper = mount(, { context: { store }, }); - expect(TabbedSqlEditors.prototype.componentDidMount.calledOnce).toBe(true); - expect(window.history.replaceState.getCall(0).args[2]).toBe('/superset/sqllab'); + expect(TabbedSqlEditors.prototype.componentDidMount.calledOnce).toBe( + true, + ); + expect(window.history.replaceState.getCall(0).args[2]).toBe( + '/superset/sqllab', + ); }); it('should handle savedQueryId', () => { uriStub.returns({ savedQueryId: 1 }); wrapper = mount(, { context: { store }, }); - expect(TabbedSqlEditors.prototype.componentDidMount.calledOnce).toBe(true); - expect(window.history.replaceState.getCall(0).args[2]).toBe('/superset/sqllab'); + expect(TabbedSqlEditors.prototype.componentDidMount.calledOnce).toBe( + true, + ); + expect(window.history.replaceState.getCall(0).args[2]).toBe( + '/superset/sqllab', + ); }); it('should handle sql', () => { uriStub.returns({ sql: 1, dbid: 1 }); wrapper = mount(, { context: { store }, }); - expect(TabbedSqlEditors.prototype.componentDidMount.calledOnce).toBe(true); - expect(window.history.replaceState.getCall(0).args[2]).toBe('/superset/sqllab'); + expect(TabbedSqlEditors.prototype.componentDidMount.calledOnce).toBe( + true, + ); + expect(window.history.replaceState.getCall(0).args[2]).toBe( + '/superset/sqllab', + ); }); }); describe('componentWillReceiveProps', () => { @@ -133,17 +146,23 @@ describe('TabbedSqlEditors', () => { spy.restore(); }); it('should update queriesArray and dataPreviewQueries', () => { - expect(wrapper.state().queriesArray.slice(-1)[0]).toBe(queries['B1-VQU1zW']); - expect(wrapper.state().dataPreviewQueries.slice(-1)[0]).toEqual(queries['B1-VQU1zW']); + expect(wrapper.state().queriesArray.slice(-1)[0]).toBe( + queries['B1-VQU1zW'], + ); + expect(wrapper.state().dataPreviewQueries.slice(-1)[0]).toEqual( + queries['B1-VQU1zW'], + ); }); }); it('should rename Tab', () => { - global.prompt = () => ('new title'); + global.prompt = () => 'new title'; wrapper = getWrapper(); sinon.stub(wrapper.instance().props.actions, 'queryEditorSetTitle'); wrapper.instance().renameTab(queryEditors[0]); - expect(wrapper.instance().props.actions.queryEditorSetTitle.getCall(0).args[1]).toBe('new title'); + expect( + wrapper.instance().props.actions.queryEditorSetTitle.getCall(0).args[1], + ).toBe('new title'); delete global.prompt; }); @@ -152,24 +171,27 @@ describe('TabbedSqlEditors', () => { sinon.stub(wrapper.instance().props.actions, 'removeQueryEditor'); wrapper.instance().removeQueryEditor(queryEditors[0]); - expect(wrapper.instance().props.actions.removeQueryEditor.getCall(0).args[0]) - .toBe(queryEditors[0]); + expect( + wrapper.instance().props.actions.removeQueryEditor.getCall(0).args[0], + ).toBe(queryEditors[0]); }); it('should add new query editor', () => { wrapper = getWrapper(); sinon.stub(wrapper.instance().props.actions, 'addQueryEditor'); wrapper.instance().newQueryEditor(); - expect(wrapper.instance().props.actions.addQueryEditor.getCall(0).args[0].title) - .toContain('Untitled Query'); + expect( + wrapper.instance().props.actions.addQueryEditor.getCall(0).args[0].title, + ).toContain('Untitled Query'); }); it('should duplicate query editor', () => { wrapper = getWrapper(); sinon.stub(wrapper.instance().props.actions, 'cloneQueryToNewTab'); wrapper.instance().duplicateQueryEditor(queryEditors[0]); - expect(wrapper.instance().props.actions.cloneQueryToNewTab.getCall(0).args[0]) - .toBe(queryEditors[0]); + expect( + wrapper.instance().props.actions.cloneQueryToNewTab.getCall(0).args[0], + ).toBe(queryEditors[0]); }); it('should handle select', () => { const mockEvent = { @@ -186,7 +208,9 @@ describe('TabbedSqlEditors', () => { // cannot switch to current tab, switchQueryEditor never gets called wrapper.instance().handleSelect('dfsadfs', mockEvent); - expect(wrapper.instance().props.actions.switchQueryEditor.callCount).toEqual(0); + expect( + wrapper.instance().props.actions.switchQueryEditor.callCount, + ).toEqual(0); wrapper.instance().newQueryEditor.restore(); }); it('should render', () => { @@ -194,7 +218,9 @@ describe('TabbedSqlEditors', () => { wrapper.setState({ hideLeftBar: true }); const firstTab = wrapper.find(Tab).first(); - expect(firstTab.props().eventKey).toContain(initialState.sqlLab.queryEditors[0].id); + expect(firstTab.props().eventKey).toContain( + initialState.sqlLab.queryEditors[0].id, + ); expect(firstTab.find(SqlEditor)).toHaveLength(1); const lastTab = wrapper.find(Tab).last(); @@ -202,8 +228,18 @@ describe('TabbedSqlEditors', () => { }); it('should disable new tab when offline', () => { wrapper = getWrapper(); - expect(wrapper.find(Tab).last().props().disabled).toBe(false); + expect( + wrapper + .find(Tab) + .last() + .props().disabled, + ).toBe(false); wrapper.setProps({ offline: true }); - expect(wrapper.find(Tab).last().props().disabled).toBe(true); + expect( + wrapper + .find(Tab) + .last() + .props().disabled, + ).toBe(true); }); }); diff --git a/superset/assets/spec/javascripts/sqllab/TableElement_spec.jsx b/superset/assets/spec/javascripts/sqllab/TableElement_spec.jsx index c01d0660ef4..ae3bd627fd2 100644 --- a/superset/assets/spec/javascripts/sqllab/TableElement_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/TableElement_spec.jsx @@ -31,14 +31,10 @@ describe('TableElement', () => { timeout: 0, }; it('renders', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('renders with props', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('has 2 Link elements', () => { const wrapper = shallow(); @@ -54,10 +50,20 @@ describe('TableElement', () => { it('sorts columns', () => { const wrapper = shallow(); expect(wrapper.state().sortColumns).toBe(false); - expect(wrapper.find(ColumnElement).first().props().column.name).toBe('id'); + expect( + wrapper + .find(ColumnElement) + .first() + .props().column.name, + ).toBe('id'); wrapper.find('.sort-cols').simulate('click'); expect(wrapper.state().sortColumns).toBe(true); - expect(wrapper.find(ColumnElement).first().props().column.name).toBe('active'); + expect( + wrapper + .find(ColumnElement) + .first() + .props().column.name, + ).toBe('active'); }); it('calls the collapseTable action', () => { const wrapper = mount(); diff --git a/superset/assets/spec/javascripts/sqllab/actions/sqlLab_spec.js b/superset/assets/spec/javascripts/sqllab/actions/sqlLab_spec.js index 306a5f9777a..f8fab7579cf 100644 --- a/superset/assets/spec/javascripts/sqllab/actions/sqlLab_spec.js +++ b/superset/assets/spec/javascripts/sqllab/actions/sqlLab_spec.js @@ -78,7 +78,7 @@ describe('async actions', () => { return store.dispatch(actions.saveQuery(query)).then(() => { const call = fetchMock.calls(saveQueryEndpoint)[0]; const formData = call[1].body; - Object.keys(query).forEach((key) => { + Object.keys(query).forEach(key => { expect(formData.get(key)).toBeDefined(); }); }); @@ -111,7 +111,9 @@ describe('async actions', () => { makeRequest().then(() => { expect(fetchMock.calls(fetchQueryEndpoint)).toHaveLength(1); expect(dispatch.callCount).toBe(2); - expect(dispatch.getCall(1).lastArg.results.data.toString()).toBe(mockBigNumber); + expect(dispatch.getCall(1).lastArg.results.data.toString()).toBe( + mockBigNumber, + ); })); it('calls querySuccess on fetch success', () => { @@ -123,7 +125,9 @@ describe('async actions', () => { actions.QUERY_SUCCESS, ]; return store.dispatch(actions.fetchQueryResults(query)).then(() => { - expect(store.getActions().map(a => a.type)).toEqual(expectedActionTypes); + expect(store.getActions().map(a => a.type)).toEqual( + expectedActionTypes, + ); }); }); @@ -142,7 +146,9 @@ describe('async actions', () => { actions.QUERY_FAILED, ]; return store.dispatch(actions.fetchQueryResults(query)).then(() => { - expect(store.getActions().map(a => a.type)).toEqual(expectedActionTypes); + expect(store.getActions().map(a => a.type)).toEqual( + expectedActionTypes, + ); }); }); }); @@ -173,19 +179,20 @@ describe('async actions', () => { makeRequest().then(() => { expect(fetchMock.calls(runQueryEndpoint)).toHaveLength(1); expect(dispatch.callCount).toBe(2); - expect(dispatch.getCall(1).lastArg.results.data.toString()).toBe(mockBigNumber); + expect(dispatch.getCall(1).lastArg.results.data.toString()).toBe( + mockBigNumber, + ); })); it('calls querySuccess on fetch success', () => { expect.assertions(1); const store = mockStore({}); - const expectedActionTypes = [ - actions.START_QUERY, - actions.QUERY_SUCCESS, - ]; + const expectedActionTypes = [actions.START_QUERY, actions.QUERY_SUCCESS]; return store.dispatch(actions.runQuery(query)).then(() => { - expect(store.getActions().map(a => a.type)).toEqual(expectedActionTypes); + expect(store.getActions().map(a => a.type)).toEqual( + expectedActionTypes, + ); }); }); @@ -199,12 +206,11 @@ describe('async actions', () => { ); const store = mockStore({}); - const expectedActionTypes = [ - actions.START_QUERY, - actions.QUERY_FAILED, - ]; + const expectedActionTypes = [actions.START_QUERY, actions.QUERY_FAILED]; return store.dispatch(actions.runQuery(query)).then(() => { - expect(store.getActions().map(a => a.type)).toEqual(expectedActionTypes); + expect(store.getActions().map(a => a.type)).toEqual( + expectedActionTypes, + ); }); }); }); @@ -226,7 +232,6 @@ describe('async actions', () => { }); }); - it('calls stopQuery', () => { expect.assertions(1); @@ -265,22 +270,26 @@ describe('async actions', () => { }, }; const store = mockStore(state); - const expectedActions = [{ - type: actions.ADD_QUERY_EDITOR, - queryEditor: { - title: 'Copy of Dummy query editor', - dbId: 1, - schema: null, - autorun: true, - sql: 'SELECT * FROM something', - queryLimit: undefined, - maxRow: undefined, - id: 'abcd', + const expectedActions = [ + { + type: actions.ADD_QUERY_EDITOR, + queryEditor: { + title: 'Copy of Dummy query editor', + dbId: 1, + schema: null, + autorun: true, + sql: 'SELECT * FROM something', + queryLimit: undefined, + maxRow: undefined, + id: 'abcd', + }, }, - }]; - return store.dispatch(actions.cloneQueryToNewTab(query, true)).then(() => { - expect(store.getActions()).toEqual(expectedActions); - }); + ]; + return store + .dispatch(actions.cloneQueryToNewTab(query, true)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + }); }); }); @@ -297,13 +306,17 @@ describe('async actions', () => { expect.assertions(1); const store = mockStore({}); - const expectedActions = [{ - type: actions.ADD_QUERY_EDITOR, - queryEditor, - }]; - return store.dispatch(actions.addQueryEditor(defaultQueryEditor)).then(() => { - expect(store.getActions()).toEqual(expectedActions); - }); + const expectedActions = [ + { + type: actions.ADD_QUERY_EDITOR, + queryEditor, + }, + ]; + return store + .dispatch(actions.addQueryEditor(defaultQueryEditor)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + }); }); }); @@ -320,14 +333,18 @@ describe('async actions', () => { const getTableMetadataEndpoint = 'glob:*/superset/table/*'; fetchMock.get(getTableMetadataEndpoint, {}); - const getExtraTableMetadataEndpoint = 'glob:*/superset/extra_table_metadata/*'; + const getExtraTableMetadataEndpoint = + 'glob:*/superset/extra_table_metadata/*'; fetchMock.get(getExtraTableMetadataEndpoint, {}); let isFeatureEnabledMock; beforeAll(() => { - isFeatureEnabledMock = jest.spyOn(featureFlags, 'isFeatureEnabled') - .mockImplementation(feature => feature === 'SQLLAB_BACKEND_PERSISTENCE'); + isFeatureEnabledMock = jest + .spyOn(featureFlags, 'isFeatureEnabled') + .mockImplementation( + feature => feature === 'SQLLAB_BACKEND_PERSISTENCE', + ); }); afterAll(() => { @@ -365,11 +382,9 @@ describe('async actions', () => { query: { sqlEditorId: 'abcd' }, query_id: 'efgh', }; - fetchMock.get( - fetchQueryEndpoint, - JSON.stringify(results), - { overwriteRoutes: true }, - ); + fetchMock.get(fetchQueryEndpoint, JSON.stringify(results), { + overwriteRoutes: true, + }); const store = mockStore({}); const expectedActions = [ { @@ -419,10 +434,12 @@ describe('async actions', () => { queryEditor, }, ]; - return store.dispatch(actions.setActiveQueryEditor(queryEditor)).then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); - }); + return store + .dispatch(actions.setActiveQueryEditor(queryEditor)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); + }); }); }); @@ -437,10 +454,12 @@ describe('async actions', () => { queryEditor, }, ]; - return store.dispatch(actions.removeQueryEditor(queryEditor)).then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); - }); + return store + .dispatch(actions.removeQueryEditor(queryEditor)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); + }); }); }); @@ -457,10 +476,12 @@ describe('async actions', () => { dbId, }, ]; - return store.dispatch(actions.queryEditorSetDb(queryEditor, dbId)).then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); - }); + return store + .dispatch(actions.queryEditorSetDb(queryEditor, dbId)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); + }); }); }); @@ -477,10 +498,12 @@ describe('async actions', () => { schema, }, ]; - return store.dispatch(actions.queryEditorSetSchema(queryEditor, schema)).then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); - }); + return store + .dispatch(actions.queryEditorSetSchema(queryEditor, schema)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); + }); }); }); @@ -497,10 +520,12 @@ describe('async actions', () => { autorun, }, ]; - return store.dispatch(actions.queryEditorSetAutorun(queryEditor, autorun)).then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); - }); + return store + .dispatch(actions.queryEditorSetAutorun(queryEditor, autorun)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); + }); }); }); @@ -517,10 +542,12 @@ describe('async actions', () => { title, }, ]; - return store.dispatch(actions.queryEditorSetTitle(queryEditor, title)).then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); - }); + return store + .dispatch(actions.queryEditorSetTitle(queryEditor, title)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); + }); }); }); @@ -537,10 +564,12 @@ describe('async actions', () => { sql, }, ]; - return store.dispatch(actions.queryEditorSetSql(queryEditor, sql)).then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); - }); + return store + .dispatch(actions.queryEditorSetSql(queryEditor, sql)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); + }); }); }); @@ -557,8 +586,8 @@ describe('async actions', () => { queryLimit, }, ]; - return store.dispatch( - actions.queryEditorSetQueryLimit(queryEditor, queryLimit)) + return store + .dispatch(actions.queryEditorSetQueryLimit(queryEditor, queryLimit)) .then(() => { expect(store.getActions()).toEqual(expectedActions); expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); @@ -579,8 +608,10 @@ describe('async actions', () => { templateParams, }, ]; - return store.dispatch( - actions.queryEditorSetTemplateParams(queryEditor, templateParams)) + return store + .dispatch( + actions.queryEditorSetTemplateParams(queryEditor, templateParams), + ) .then(() => { expect(store.getActions()).toEqual(expectedActions); expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1); @@ -597,30 +628,32 @@ describe('async actions', () => { query: { sqlEditorId: 'null' }, query_id: 'efgh', }; - fetchMock.post( - runQueryEndpoint, - JSON.stringify(results), - { overwriteRoutes: true }, - ); + fetchMock.post(runQueryEndpoint, JSON.stringify(results), { + overwriteRoutes: true, + }); const tableName = 'table'; const schemaName = 'schema'; const store = mockStore({}); const expectedActionTypes = [ - actions.MERGE_TABLE, // addTable - actions.MERGE_TABLE, // getTableMetadata - actions.START_QUERY, // runQuery (data preview) - actions.MERGE_TABLE, // getTableExtendedMetadata + actions.MERGE_TABLE, // addTable + actions.MERGE_TABLE, // getTableMetadata + actions.START_QUERY, // runQuery (data preview) + actions.MERGE_TABLE, // getTableExtendedMetadata actions.QUERY_SUCCESS, // querySuccess - actions.MERGE_TABLE, // addTable + actions.MERGE_TABLE, // addTable ]; - return store.dispatch( - actions.addTable(query, tableName, schemaName)) + return store + .dispatch(actions.addTable(query, tableName, schemaName)) .then(() => { - expect(store.getActions().map(a => a.type)).toEqual(expectedActionTypes); + expect(store.getActions().map(a => a.type)).toEqual( + expectedActionTypes, + ); expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1); expect(fetchMock.calls(getTableMetadataEndpoint)).toHaveLength(1); - expect(fetchMock.calls(getExtraTableMetadataEndpoint)).toHaveLength(1); + expect(fetchMock.calls(getExtraTableMetadataEndpoint)).toHaveLength( + 1, + ); // tab state is not updated, since the query is a data preview expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(0); @@ -641,9 +674,9 @@ describe('async actions', () => { }, ]; return store.dispatch(actions.expandTable(table)).then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1); - }); + expect(store.getActions()).toEqual(expectedActions); + expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1); + }); }); }); @@ -660,9 +693,9 @@ describe('async actions', () => { }, ]; return store.dispatch(actions.collapseTable(table)).then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1); - }); + expect(store.getActions()).toEqual(expectedActions); + expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1); + }); }); }); @@ -679,9 +712,9 @@ describe('async actions', () => { }, ]; return store.dispatch(actions.removeTable(table)).then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1); - }); + expect(store.getActions()).toEqual(expectedActions); + expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1); + }); }); }); @@ -694,11 +727,9 @@ describe('async actions', () => { query: { sqlEditorId: 'null' }, query_id: 'efgh', }; - fetchMock.post( - runQueryEndpoint, - JSON.stringify(results), - { overwriteRoutes: true }, - ); + fetchMock.post(runQueryEndpoint, JSON.stringify(results), { + overwriteRoutes: true, + }); const tables = [ { id: 'one', dataPreviewQueryId: 'previewOne' }, @@ -744,8 +775,14 @@ describe('async actions', () => { queryEditorId: '1', }, ]; - return store.dispatch( - actions.migrateQueryEditorFromLocalStorage(queryEditor, tables, queries)) + return store + .dispatch( + actions.migrateQueryEditorFromLocalStorage( + queryEditor, + tables, + queries, + ), + ) .then(() => { expect(store.getActions()).toEqual(expectedActions); expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(3); diff --git a/superset/assets/spec/javascripts/sqllab/fixtures.js b/superset/assets/spec/javascripts/sqllab/fixtures.js index 30cff0b9dc4..cf9cba6677d 100644 --- a/superset/assets/spec/javascripts/sqllab/fixtures.js +++ b/superset/assets/spec/javascripts/sqllab/fixtures.js @@ -244,7 +244,10 @@ export const queries = [ type: 'STRING', }, ], - data: [{ col1: 0, col2: 1 }, { col1: 2, col2: 3 }], + data: [ + { col1: 0, col2: 1 }, + { col1: 2, col2: 3 }, + ], }, }, { diff --git a/superset/assets/spec/javascripts/sqllab/reducers/sqlLab_spec.js b/superset/assets/spec/javascripts/sqllab/reducers/sqlLab_spec.js index d7aa1189573..87a0deb3213 100644 --- a/superset/assets/spec/javascripts/sqllab/reducers/sqlLab_spec.js +++ b/superset/assets/spec/javascripts/sqllab/reducers/sqlLab_spec.js @@ -61,7 +61,9 @@ describe('sqlLabReducer', () => { queryEditor: defaultQueryEditor, }; newState = sqlLabReducer(newState, setActiveQueryEditorAction); - expect(newState.tabHistory[newState.tabHistory.length - 1]).toBe(defaultQueryEditor.id); + expect(newState.tabHistory[newState.tabHistory.length - 1]).toBe( + defaultQueryEditor.id, + ); }); it('should not fail while setting DB', () => { const dbId = 9; diff --git a/superset/assets/spec/javascripts/sqllab/utils/emptyQueryResults_spec.js b/superset/assets/spec/javascripts/sqllab/utils/emptyQueryResults_spec.js index 44806272f33..b380607fa81 100644 --- a/superset/assets/spec/javascripts/sqllab/utils/emptyQueryResults_spec.js +++ b/superset/assets/spec/javascripts/sqllab/utils/emptyQueryResults_spec.js @@ -16,14 +16,17 @@ * specific language governing permissions and limitations * under the License. */ -import { emptyQueryResults, clearQueryEditors } from '../../../../src/SqlLab/utils/reduxStateToLocalStorageHelper'; +import { + emptyQueryResults, + clearQueryEditors, +} from '../../../../src/SqlLab/utils/reduxStateToLocalStorageHelper'; import { LOCALSTORAGE_MAX_QUERY_AGE_MS } from '../../../../src/SqlLab/constants'; import { queries, defaultQueryEditor } from '../fixtures'; describe('reduxStateToLocalStorageHelper', () => { const queriesObj = {}; beforeEach(() => { - queries.forEach((q) => { + queries.forEach(q => { queriesObj[q.id] = q; }); }); @@ -32,7 +35,9 @@ describe('reduxStateToLocalStorageHelper', () => { // make sure sample data contains old query const oldQuery = queries[0]; const { id, startDttm } = oldQuery; - expect(Date.now() - startDttm).toBeGreaterThan(LOCALSTORAGE_MAX_QUERY_AGE_MS); + expect(Date.now() - startDttm).toBeGreaterThan( + LOCALSTORAGE_MAX_QUERY_AGE_MS, + ); expect(Object.keys(oldQuery.results)).toContain('data'); const emptiedQuery = emptyQueryResults(queriesObj); diff --git a/superset/assets/spec/javascripts/utils/common_spec.jsx b/superset/assets/spec/javascripts/utils/common_spec.jsx index c9c34bbeac8..f1a1653fc4e 100644 --- a/superset/assets/spec/javascripts/utils/common_spec.jsx +++ b/superset/assets/spec/javascripts/utils/common_spec.jsx @@ -16,15 +16,27 @@ * specific language governing permissions and limitations * under the License. */ -import { optionFromValue, prepareCopyToClipboardTabularData } from '../../../src/utils/common'; +import { + optionFromValue, + prepareCopyToClipboardTabularData, +} from '../../../src/utils/common'; describe('utils/common', () => { describe('optionFromValue', () => { it('converts values as expected', () => { - expect(optionFromValue(false)).toEqual({ value: false, label: '' }); + expect(optionFromValue(false)).toEqual({ + value: false, + label: '', + }); expect(optionFromValue(true)).toEqual({ value: true, label: '' }); - expect(optionFromValue(null)).toEqual({ value: '', label: '' }); - expect(optionFromValue('')).toEqual({ value: '', label: '' }); + expect(optionFromValue(null)).toEqual({ + value: '', + label: '', + }); + expect(optionFromValue('')).toEqual({ + value: '', + label: '', + }); expect(optionFromValue('foo')).toEqual({ value: 'foo', label: 'foo' }); expect(optionFromValue(5)).toEqual({ value: 5, label: '5' }); }); @@ -36,10 +48,12 @@ describe('utils/common', () => { }); it('converts non empty array', () => { const array = [ - { column1: 'lorem', column2: 'ipsum' }, - { column1: 'dolor', column2: 'sit', column3: 'amet' }, + { column1: 'lorem', column2: 'ipsum' }, + { column1: 'dolor', column2: 'sit', column3: 'amet' }, ]; - expect(prepareCopyToClipboardTabularData(array)).toEqual('lorem\tipsum\ndolor\tsit\tamet\n'); + expect(prepareCopyToClipboardTabularData(array)).toEqual( + 'lorem\tipsum\ndolor\tsit\tamet\n', + ); }); }); }); diff --git a/superset/assets/spec/javascripts/utils/getClientErrorObject_spec.js b/superset/assets/spec/javascripts/utils/getClientErrorObject_spec.js index 7bb739fc8de..647f4e8f8d7 100644 --- a/superset/assets/spec/javascripts/utils/getClientErrorObject_spec.js +++ b/superset/assets/spec/javascripts/utils/getClientErrorObject_spec.js @@ -27,7 +27,7 @@ describe('getClientErrorObject()', () => { it('Returns a Promise that resolves to an object with an error key', () => { const error = 'error'; - return getClientErrorObject(error).then((errorObj) => { + return getClientErrorObject(error).then(errorObj => { expect(errorObj).toMatchObject({ error }); }); }); @@ -36,15 +36,17 @@ describe('getClientErrorObject()', () => { const jsonError = { something: 'something', error: 'Error message' }; const jsonErrorString = JSON.stringify(jsonError); - return getClientErrorObject(new Response(jsonErrorString)).then((errorObj) => { - expect(errorObj).toMatchObject(jsonError); - }); + return getClientErrorObject(new Response(jsonErrorString)).then( + errorObj => { + expect(errorObj).toMatchObject(jsonError); + }, + ); }); it('Handles Response that can be parsed as text', () => { const textError = 'Hello I am a text error'; - return getClientErrorObject(new Response(textError)).then((errorObj) => { + return getClientErrorObject(new Response(textError)).then(errorObj => { expect(errorObj).toMatchObject({ error: textError }); }); }); @@ -52,7 +54,7 @@ describe('getClientErrorObject()', () => { it('Handles plain text as input', () => { const error = 'error'; - return getClientErrorObject(error).then((errorObj) => { + return getClientErrorObject(error).then(errorObj => { expect(errorObj).toMatchObject({ error }); }); }); diff --git a/superset/assets/spec/javascripts/welcome/DashboardTable_spec.jsx b/superset/assets/spec/javascripts/welcome/DashboardTable_spec.jsx index e989f9a207a..42f2dd7d782 100644 --- a/superset/assets/spec/javascripts/welcome/DashboardTable_spec.jsx +++ b/superset/assets/spec/javascripts/welcome/DashboardTable_spec.jsx @@ -31,9 +31,7 @@ const mockStore = configureStore([thunk]); const store = mockStore({}); const dashboardsEndpoint = 'glob:*/dashboardasync/api/read*'; -const mockDashboards = [ - { id: 1, url: 'url', dashboard_title: 'title' }, -]; +const mockDashboards = [{ id: 1, url: 'url', dashboard_title: 'title' }]; fetchMock.get(dashboardsEndpoint, { result: mockDashboards }); @@ -50,7 +48,7 @@ describe('DashboardTable', () => { expect(wrapper.find(Loading)).toHaveLength(1); }); - it('fetches dashboards and renders a Table', (done) => { + it('fetches dashboards and renders a Table', done => { const wrapper = setup(); setTimeout(() => { diff --git a/superset/assets/spec/javascripts/welcome/Welcome_spec.jsx b/superset/assets/spec/javascripts/welcome/Welcome_spec.jsx index 8a18b299955..67eb413f755 100644 --- a/superset/assets/spec/javascripts/welcome/Welcome_spec.jsx +++ b/superset/assets/spec/javascripts/welcome/Welcome_spec.jsx @@ -25,9 +25,7 @@ import Welcome from '../../../src/welcome/Welcome'; describe('Welcome', () => { const mockedProps = {}; it('is valid', () => { - expect( - React.isValidElement(), - ).toBe(true); + expect(React.isValidElement()).toBe(true); }); it('renders 4 Tab, Panel, and Row components', () => { const wrapper = shallow(); diff --git a/superset/assets/src/CRUD/CollectionTable.jsx b/superset/assets/src/CRUD/CollectionTable.jsx index 00b3a0b2b91..2612646bacc 100644 --- a/superset/assets/src/CRUD/CollectionTable.jsx +++ b/superset/assets/src/CRUD/CollectionTable.jsx @@ -49,10 +49,7 @@ const defaultProps = { expandFieldset: null, extraButtons: null, }; -const Frame = props => ( -
- {props.children} -
); +const Frame = props =>
{props.children}
; Frame.propTypes = { children: PropTypes.node }; function createKeyedCollection(arr) { @@ -61,7 +58,7 @@ function createKeyedCollection(arr) { id: o.id || shortid.generate(), })); const map = {}; - newArr.forEach((o) => { + newArr.forEach(o => { map[o.id] = o; }); return map; @@ -97,7 +94,6 @@ export default class CRUDCollection extends React.PureComponent { [col]: val, }, }); - } onAddItem() { let newItem = this.props.itemGenerator(); @@ -135,7 +131,9 @@ export default class CRUDCollection extends React.PureComponent { } effectiveTableColumns() { const { tableColumns, allowDeletes, expandFieldset } = this.props; - const cols = allowDeletes ? tableColumns.concat(['__actions']) : tableColumns; + const cols = allowDeletes + ? tableColumns.concat(['__actions']) + : tableColumns; return expandFieldset ? ['__expand'].concat(cols) : cols; } toggleExpand(id) { @@ -153,7 +151,9 @@ export default class CRUDCollection extends React.PureComponent { {this.props.expandFieldset && } - {cols.map(col => {this.getLabel(col)})} + {cols.map(col => ( + {this.getLabel(col)} + ))} {this.props.allowDeletes && } @@ -161,7 +161,11 @@ export default class CRUDCollection extends React.PureComponent { } renderExpandableSection(item) { const propsGenerator = () => ({ item, onChange: this.onFieldsetChange }); - return recurseReactClone(this.props.expandFieldset, Fieldset, propsGenerator); + return recurseReactClone( + this.props.expandFieldset, + Fieldset, + propsGenerator, + ); } renderCell(record, col) { const renderer = this.props.itemRenderers[col]; @@ -172,20 +176,26 @@ export default class CRUDCollection extends React.PureComponent { renderItem(record) { const { tableColumns, allowDeletes, expandFieldset } = this.props; /* eslint-disable no-underscore-dangle */ - const isExpanded = !!this.state.expandedColumns[record.id] || record.__expanded; + const isExpanded = + !!this.state.expandedColumns[record.id] || record.__expanded; let tds = []; if (expandFieldset) { tds.push( - ); + , + ); } - tds = tds.concat(tableColumns.map(col => ( - {this.renderCell(record, col)} - ))); + tds = tds.concat( + tableColumns.map(col => ( + {this.renderCell(record, col)} + )), + ); if (allowDeletes) { tds.push( @@ -193,43 +203,57 @@ export default class CRUDCollection extends React.PureComponent { className="fa fa-close text-primary pointer" onClick={this.deleteItem.bind(this, record.id)} /> - ); + , + ); } - const trs = [{tds}]; + const trs = [ + + {tds} + , + ]; if (isExpanded) { trs.push( - -
- {this.renderExpandableSection(record)} -
+ +
{this.renderExpandableSection(record)}
- ); + , + ); } return trs; } renderEmptyCell() { - return {this.props.emptyMessage}; + return ( + + {this.props.emptyMessage} + + ); } renderTableBody() { - const data = Object.keys(this.state.collection).map(k => this.state.collection[k]); - const content = data.length ? data.map(d => this.renderItem(d)) : this.renderEmptyCell(); + const data = Object.keys(this.state.collection).map( + k => this.state.collection[k], + ); + const content = data.length + ? data.map(d => this.renderItem(d)) + : this.renderEmptyCell(); return {content}; } render() { return (
- +
{this.renderHeaderRow()} {this.renderTableBody()}
- {this.props.allowAddItem && + {this.props.allowAddItem && ( } + + )} {this.props.extraButtons}
diff --git a/superset/assets/src/CRUD/Field.jsx b/superset/assets/src/CRUD/Field.jsx index 902b68199be..1425085ed80 100644 --- a/superset/assets/src/CRUD/Field.jsx +++ b/superset/assets/src/CRUD/Field.jsx @@ -19,7 +19,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import { - FormGroup, ControlLabel, HelpBlock, FormControl, OverlayTrigger, Tooltip, + FormGroup, + ControlLabel, + HelpBlock, + FormControl, + OverlayTrigger, + Tooltip, } from 'react-bootstrap'; import './crud.less'; @@ -50,29 +55,30 @@ export default class Field extends React.PureComponent { } render() { const { compact, value, label, control, descr, fieldKey } = this.props; - const hookedControl = React.cloneElement(control, { value, onChange: this.onChange }); + const hookedControl = React.cloneElement(control, { + value, + onChange: this.onChange, + }); return ( - + {label || fieldKey} - {compact && descr && + {compact && descr && ( {descr} - } + + {descr} + + } > - } + )} {hookedControl} - {!compact && descr && - {descr} - } + {!compact && descr && {descr}} ); } diff --git a/superset/assets/src/CRUD/Fieldset.jsx b/superset/assets/src/CRUD/Fieldset.jsx index 75a04c6b9c9..f73cd79289b 100644 --- a/superset/assets/src/CRUD/Fieldset.jsx +++ b/superset/assets/src/CRUD/Fieldset.jsx @@ -55,9 +55,7 @@ export default class Fieldset extends React.PureComponent { }); return (
- {title && - {title} - } + {title && {title}} {recurseReactClone(this.props.children, Field, propExtender)}
); diff --git a/superset/assets/src/CRUD/crud.less b/superset/assets/src/CRUD/crud.less index 478e08f8f4d..b0cb324bf54 100644 --- a/superset/assets/src/CRUD/crud.less +++ b/superset/assets/src/CRUD/crud.less @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -@import "../../stylesheets/less/variables.less"; +@import '../../stylesheets/less/variables.less'; .CRUD { .text-right { diff --git a/superset/assets/src/CRUD/utils.js b/superset/assets/src/CRUD/utils.js index 70b0988d11b..2fdcb6ff2b6 100644 --- a/superset/assets/src/CRUD/utils.js +++ b/superset/assets/src/CRUD/utils.js @@ -23,14 +23,18 @@ export function recurseReactClone(children, type, propExtender) { * Clones a React component's children, and injects new props * where the type specified is matched. */ - return React.Children.map(children, (child) => { + return React.Children.map(children, child => { let newChild = child; if (child && child.type.name === type.name) { newChild = React.cloneElement(child, propExtender(child)); } if (newChild && newChild.props.children) { newChild = React.cloneElement(newChild, { - children: recurseReactClone(newChild.props.children, type, propExtender), + children: recurseReactClone( + newChild.props.children, + type, + propExtender, + ), }); } return newChild; diff --git a/superset/assets/src/SqlLab/App.jsx b/superset/assets/src/SqlLab/App.jsx index 058e17dd950..c13314b560a 100644 --- a/superset/assets/src/SqlLab/App.jsx +++ b/superset/assets/src/SqlLab/App.jsx @@ -22,12 +22,19 @@ import { Provider } from 'react-redux'; import thunkMiddleware from 'redux-thunk'; import { hot } from 'react-hot-loader'; -import { initFeatureFlags, isFeatureEnabled, FeatureFlag } from 'src/featureFlags'; +import { + initFeatureFlags, + isFeatureEnabled, + FeatureFlag, +} from 'src/featureFlags'; import getInitialState from './reducers/getInitialState'; import rootReducer from './reducers/index'; import { initEnhancer } from '../reduxUtils'; import App from './components/App'; -import { emptyQueryResults, clearQueryEditors } from './utils/reduxStateToLocalStorageHelper'; +import { + emptyQueryResults, + clearQueryEditors, +} from './utils/reduxStateToLocalStorageHelper'; import { BYTES_PER_CHAR, KB_STORAGE } from './constants'; import setupApp from '../setup/setupApp'; @@ -44,9 +51,9 @@ const initialState = getInitialState(bootstrapData); const sqlLabPersistStateConfig = { paths: ['sqlLab'], config: { - slicer: paths => (state) => { + slicer: paths => state => { const subset = {}; - paths.forEach((path) => { + paths.forEach(path => { // this line is used to remove old data from browser localStorage. // we used to persist all redux state into localStorage, but // it caused configurations passed from server-side got override. @@ -64,7 +71,8 @@ const sqlLabPersistStateConfig = { const data = JSON.stringify(subset); // 2 digit precision - const currentSize = Math.round(data.length * BYTES_PER_CHAR / KB_STORAGE * 100) / 100; + const currentSize = + Math.round(((data.length * BYTES_PER_CHAR) / KB_STORAGE) * 100) / 100; if (state.localStorageUsageInKilobytes !== currentSize) { state.localStorageUsageInKilobytes = currentSize; // eslint-disable-line no-param-reassign } @@ -88,7 +96,8 @@ const store = createStore( // Highlight the navbar menu const menus = document.querySelectorAll('.nav.navbar-nav li.dropdown'); -const sqlLabMenu = Array.prototype.slice.apply(menus) +const sqlLabMenu = Array.prototype.slice + .apply(menus) .find(element => element.innerText.trim() === 'SQL Lab'); if (sqlLabMenu) { const classes = sqlLabMenu.getAttribute('class'); diff --git a/superset/assets/src/SqlLab/actions/sqlLab.js b/superset/assets/src/SqlLab/actions/sqlLab.js index 36df2f4fbc7..870ca91692b 100644 --- a/superset/assets/src/SqlLab/actions/sqlLab.js +++ b/superset/assets/src/SqlLab/actions/sqlLab.js @@ -48,13 +48,15 @@ export const EXPAND_TABLE = 'EXPAND_TABLE'; export const COLLAPSE_TABLE = 'COLLAPSE_TABLE'; export const QUERY_EDITOR_SETDB = 'QUERY_EDITOR_SETDB'; export const QUERY_EDITOR_SET_SCHEMA = 'QUERY_EDITOR_SET_SCHEMA'; -export const QUERY_EDITOR_SET_SCHEMA_OPTIONS = 'QUERY_EDITOR_SET_SCHEMA_OPTIONS'; +export const QUERY_EDITOR_SET_SCHEMA_OPTIONS = + 'QUERY_EDITOR_SET_SCHEMA_OPTIONS'; export const QUERY_EDITOR_SET_TABLE_OPTIONS = 'QUERY_EDITOR_SET_TABLE_OPTIONS'; export const QUERY_EDITOR_SET_TITLE = 'QUERY_EDITOR_SET_TITLE'; export const QUERY_EDITOR_SET_AUTORUN = 'QUERY_EDITOR_SET_AUTORUN'; export const QUERY_EDITOR_SET_SQL = 'QUERY_EDITOR_SET_SQL'; export const QUERY_EDITOR_SET_QUERY_LIMIT = 'QUERY_EDITOR_SET_QUERY_LIMIT'; -export const QUERY_EDITOR_SET_TEMPLATE_PARAMS = 'QUERY_EDITOR_SET_TEMPLATE_PARAMS'; +export const QUERY_EDITOR_SET_TEMPLATE_PARAMS = + 'QUERY_EDITOR_SET_TEMPLATE_PARAMS'; export const QUERY_EDITOR_SET_SELECTED_TEXT = 'QUERY_EDITOR_SET_SELECTED_TEXT'; export const QUERY_EDITOR_PERSIST_HEIGHT = 'QUERY_EDITOR_PERSIST_HEIGHT'; export const MIGRATE_QUERY_EDITOR = 'MIGRATE_QUERY_EDITOR'; @@ -108,7 +110,7 @@ const queryServerMapping = invert(queryClientMapping); // uses a mapping like those above to convert object key names to another style const fieldConverter = mapping => obj => - mapKeys(obj, (value, key) => key in mapping ? mapping[key] : key); + mapKeys(obj, (value, key) => (key in mapping ? mapping[key] : key)); const convertQueryToServer = fieldConverter(queryServerMapping); const convertQueryToClient = fieldConverter(queryClientMapping); @@ -139,7 +141,7 @@ export function saveQuery(query) { postPayload: convertQueryToServer(query), stringify: false, }) - .then((result) => { + .then(result => { dispatch({ type: QUERY_EDITOR_SAVED, query, @@ -147,7 +149,9 @@ export function saveQuery(query) { }); dispatch(addSuccessToast(t('Your query was saved'))); }) - .catch(() => dispatch(addDangerToast(t('Your query could not be saved')))); + .catch(() => + dispatch(addDangerToast(t('Your query could not be saved'))), + ); } export function updateQueryEditor(alterations) { @@ -162,7 +166,9 @@ export function updateSavedQuery(query) { stringify: false, }) .then(() => dispatch(addSuccessToast(t('Your query was updated')))) - .catch(() => dispatch(addDangerToast(t('Your query could not be updated')))) + .catch(() => + dispatch(addDangerToast(t('Your query could not be updated'))), + ) .then(() => dispatch(updateQueryEditor(query))); } @@ -173,29 +179,53 @@ export function scheduleQuery(query) { postPayload: query, stringify: false, }) - .then(() => dispatch(addSuccessToast(t('Your query has been scheduled. To see details of your query, navigate to Saved Queries')))) - .catch(() => dispatch(addDangerToast(t('Your query could not be scheduled')))); + .then(() => + dispatch( + addSuccessToast( + t( + 'Your query has been scheduled. To see details of your query, navigate to Saved Queries', + ), + ), + ), + ) + .catch(() => + dispatch(addDangerToast(t('Your query could not be scheduled'))), + ); } export function estimateQueryCost(query) { const { dbId, schema, sql, templateParams } = query; - const endpoint = schema === null + const endpoint = + schema === null ? `/superset/estimate_query_cost/${dbId}/` : `/superset/estimate_query_cost/${dbId}/${schema}/`; - return dispatch => Promise.all([ - dispatch({ type: COST_ESTIMATE_STARTED, query }), - SupersetClient.post({ - endpoint, - postPayload: { sql, templateParams: JSON.parse(templateParams || '{}') }, - }) - .then(({ json }) => dispatch({ type: COST_ESTIMATE_RETURNED, query, json })) - .catch(response => - getClientErrorObject(response).then((error) => { - const message = error.error || error.statusText || t('Failed at retrieving results'); - return dispatch({ type: COST_ESTIMATE_FAILED, query, error: message }); - }), - ), - ]); + return dispatch => + Promise.all([ + dispatch({ type: COST_ESTIMATE_STARTED, query }), + SupersetClient.post({ + endpoint, + postPayload: { + sql, + templateParams: JSON.parse(templateParams || '{}'), + }, + }) + .then(({ json }) => + dispatch({ type: COST_ESTIMATE_RETURNED, query, json }), + ) + .catch(response => + getClientErrorObject(response).then(error => { + const message = + error.error || + error.statusText || + t('Failed at retrieving results'); + return dispatch({ + type: COST_ESTIMATE_FAILED, + query, + error: message, + }); + }), + ), + ]); } export function startQuery(query) { @@ -210,38 +240,54 @@ export function startQuery(query) { } export function querySuccess(query, results) { - return function (dispatch) { - const sync = (!query.isDataPreview && isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE)) - ? SupersetClient.put({ - endpoint: encodeURI(`/tabstateview/${results.query.sqlEditorId}`), - postPayload: { latest_query_id: query.id }, - }) - : Promise.resolve(); + return function(dispatch) { + const sync = + !query.isDataPreview && + isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) + ? SupersetClient.put({ + endpoint: encodeURI(`/tabstateview/${results.query.sqlEditorId}`), + postPayload: { latest_query_id: query.id }, + }) + : Promise.resolve(); return sync .then(() => dispatch({ type: QUERY_SUCCESS, query, results })) .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while storing the latest query id in the backend. ' + - 'Please contact your administrator if this problem persists.')))); + dispatch( + addDangerToast( + t( + 'An error occurred while storing the latest query id in the backend. ' + + 'Please contact your administrator if this problem persists.', + ), + ), + ), + ); }; } export function queryFailed(query, msg, link) { - return function (dispatch) { - const sync = (!query.isDataPreview && isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE)) - ? SupersetClient.put({ - endpoint: encodeURI(`/tabstateview/${query.sqlEditorId}`), - postPayload: { latest_query_id: query.id }, - }) - : Promise.resolve(); + return function(dispatch) { + const sync = + !query.isDataPreview && + isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) + ? SupersetClient.put({ + endpoint: encodeURI(`/tabstateview/${query.sqlEditorId}`), + postPayload: { latest_query_id: query.id }, + }) + : Promise.resolve(); return sync .then(() => dispatch({ type: QUERY_FAILED, query, msg, link })) .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while storing the latest query id in the backend. ' + - 'Please contact your administrator if this problem persists.')))); + dispatch( + addDangerToast( + t( + 'An error occurred while storing the latest query id in the backend. ' + + 'Please contact your administrator if this problem persists.', + ), + ), + ), + ); }; } @@ -262,7 +308,7 @@ export function requestQueryResults(query) { } export function fetchQueryResults(query, displayLimit) { - return function (dispatch) { + return function(dispatch) { dispatch(requestQueryResults(query)); return SupersetClient.get({ @@ -274,17 +320,20 @@ export function fetchQueryResults(query, displayLimit) { return dispatch(querySuccess(query, bigIntJson)); }) .catch(response => - getClientErrorObject(response).then((error) => { - const message = error.error || error.statusText || t('Failed at retrieving results'); + getClientErrorObject(response).then(error => { + const message = + error.error || + error.statusText || + t('Failed at retrieving results'); return dispatch(queryFailed(query, message, error.link)); }), ); - }; + }; } export function runQuery(query) { - return function (dispatch) { + return function(dispatch) { dispatch(startQuery(query)); const postPayload = { client_id: query.id, @@ -315,7 +364,7 @@ export function runQuery(query) { } }) .catch(response => - getClientErrorObject(response).then((error) => { + getClientErrorObject(response).then(error => { let message = error.error || error.statusText || t('Unknown error'); if (message.includes('CSRF token')) { message = t(COMMON_ERR_MESSAGES.SESSION_TIMED_OUT); @@ -327,7 +376,7 @@ export function runQuery(query) { } export function validateQuery(query) { - return function (dispatch) { + return function(dispatch) { dispatch(startQueryValidation(query)); const postPayload = { @@ -348,7 +397,7 @@ export function validateQuery(query) { }) .then(({ json }) => dispatch(queryValidationReturned(query, json))) .catch(response => - getClientErrorObject(response).then((error) => { + getClientErrorObject(response).then(error => { let message = error.error || error.statusText || t('Unknown error'); if (message.includes('CSRF token')) { message = t(COMMON_ERR_MESSAGES.SESSION_TIMED_OUT); @@ -360,7 +409,7 @@ export function validateQuery(query) { } export function postStopQuery(query) { - return function (dispatch) { + return function(dispatch) { return SupersetClient.post({ endpoint: '/superset/stop_query/', postPayload: { client_id: query.id }, @@ -368,7 +417,11 @@ export function postStopQuery(query) { }) .then(() => dispatch(stopQuery(query))) .then(() => dispatch(addSuccessToast(t('Query was stopped.')))) - .catch(() => dispatch(addDangerToast(t('Failed at stopping query. ') + `'${query.id}'`))); + .catch(() => + dispatch( + addDangerToast(t('Failed at stopping query. ') + `'${query.id}'`), + ), + ); }; } @@ -389,9 +442,16 @@ function migrateTable(table, queryEditorId, dispatch) { }; return dispatch({ type: MIGRATE_TABLE, oldTable: table, newTable }); }) - .catch(() => dispatch(addWarningToast(t( - 'Unable to migrate table schema state to backend. Superset will retry ' + - 'later. Please contact your administrator if this problem persists.')))); + .catch(() => + dispatch( + addWarningToast( + t( + 'Unable to migrate table schema state to backend. Superset will retry ' + + 'later. Please contact your administrator if this problem persists.', + ), + ), + ), + ); } function migrateQuery(queryId, queryEditorId, dispatch) { @@ -400,36 +460,72 @@ function migrateQuery(queryId, queryEditorId, dispatch) { postPayload: { queryId }, }) .then(() => dispatch({ type: MIGRATE_QUERY, queryId, queryEditorId })) - .catch(() => dispatch(addWarningToast(t( - 'Unable to migrate query state to backend. Superset will retry later. ' + - 'Please contact your administrator if this problem persists.')))); + .catch(() => + dispatch( + addWarningToast( + t( + 'Unable to migrate query state to backend. Superset will retry later. ' + + 'Please contact your administrator if this problem persists.', + ), + ), + ), + ); } -export function migrateQueryEditorFromLocalStorage(queryEditor, tables, queries) { - return function (dispatch) { - return SupersetClient.post({ endpoint: '/tabstateview/', postPayload: { queryEditor } }) +export function migrateQueryEditorFromLocalStorage( + queryEditor, + tables, + queries, +) { + return function(dispatch) { + return SupersetClient.post({ + endpoint: '/tabstateview/', + postPayload: { queryEditor }, + }) .then(({ json }) => { const newQueryEditor = { ...queryEditor, id: json.id.toString(), }; - dispatch({ type: MIGRATE_QUERY_EDITOR, oldQueryEditor: queryEditor, newQueryEditor }); - dispatch({ type: MIGRATE_TAB_HISTORY, oldId: queryEditor.id, newId: newQueryEditor.id }); + dispatch({ + type: MIGRATE_QUERY_EDITOR, + oldQueryEditor: queryEditor, + newQueryEditor, + }); + dispatch({ + type: MIGRATE_TAB_HISTORY, + oldId: queryEditor.id, + newId: newQueryEditor.id, + }); return Promise.all([ - ...tables.map(table => migrateTable(table, newQueryEditor.id, dispatch)), - ...queries.map(query => migrateQuery(query.id, newQueryEditor.id, dispatch)), + ...tables.map(table => + migrateTable(table, newQueryEditor.id, dispatch), + ), + ...queries.map(query => + migrateQuery(query.id, newQueryEditor.id, dispatch), + ), ]); }) - .catch(() => dispatch(addWarningToast(t( - 'Unable to migrate query editor state to backend. Superset will retry ' + - 'later. Please contact your administrator if this problem persists.')))); + .catch(() => + dispatch( + addWarningToast( + t( + 'Unable to migrate query editor state to backend. Superset will retry ' + + 'later. Please contact your administrator if this problem persists.', + ), + ), + ), + ); }; } export function addQueryEditor(queryEditor) { - return function (dispatch) { + return function(dispatch) { const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) - ? SupersetClient.post({ endpoint: '/tabstateview/', postPayload: { queryEditor } }) + ? SupersetClient.post({ + endpoint: '/tabstateview/', + postPayload: { queryEditor }, + }) : Promise.resolve({ json: { id: shortid.generate() } }); return sync @@ -438,18 +534,30 @@ export function addQueryEditor(queryEditor) { ...queryEditor, id: json.id.toString(), }; - return dispatch({ type: ADD_QUERY_EDITOR, queryEditor: newQueryEditor }); + return dispatch({ + type: ADD_QUERY_EDITOR, + queryEditor: newQueryEditor, + }); }) - .catch(() => dispatch(addDangerToast(t( - 'Unable to add a new tab to the backend. Please contact your administrator.')))); + .catch(() => + dispatch( + addDangerToast( + t( + 'Unable to add a new tab to the backend. Please contact your administrator.', + ), + ), + ), + ); }; } export function cloneQueryToNewTab(query, autorun) { - return function (dispatch, getState) { + return function(dispatch, getState) { const state = getState(); const { queryEditors, tabHistory } = state.sqlLab; - const sourceQueryEditor = queryEditors.find(qe => qe.id === tabHistory[tabHistory.length - 1]); + const sourceQueryEditor = queryEditors.find( + qe => qe.id === tabHistory[tabHistory.length - 1], + ); const queryEditor = { title: t('Copy of %s', sourceQueryEditor.title), dbId: query.dbId ? query.dbId : null, @@ -465,18 +573,25 @@ export function cloneQueryToNewTab(query, autorun) { } export function setActiveQueryEditor(queryEditor) { - return function (dispatch) { + return function(dispatch) { const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) - ? SupersetClient.post({ endpoint: encodeURI(`/tabstateview/${queryEditor.id}/activate`) }) + ? SupersetClient.post({ + endpoint: encodeURI(`/tabstateview/${queryEditor.id}/activate`), + }) : Promise.resolve(); return sync .then(() => dispatch({ type: SET_ACTIVE_QUERY_EDITOR, queryEditor })) - .catch((response) => { + .catch(response => { if (response.status !== 404) { - return dispatch(addDangerToast(t( - 'An error occurred while setting the active tab. Please contact ' + - 'your administrator.'))); + return dispatch( + addDangerToast( + t( + 'An error occurred while setting the active tab. Please contact ' + + 'your administrator.', + ), + ), + ); } return dispatch({ type: REMOVE_QUERY_EDITOR, queryEditor }); }); @@ -488,7 +603,7 @@ export function loadQueryEditor(queryEditor) { } export function setTables(tableSchemas) { - const tables = tableSchemas.map((tableSchema) => { + const tables = tableSchemas.map(tableSchema => { const { columns, selectStar, @@ -518,8 +633,11 @@ export function setTables(tableSchemas) { } export function switchQueryEditor(queryEditor, displayLimit) { - return function (dispatch) { - if (isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) && !queryEditor.loaded) { + return function(dispatch) { + if ( + isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) && + !queryEditor.loaded + ) { SupersetClient.get({ endpoint: encodeURI(`/tabstateview/${queryEditor.id}`), }) @@ -549,10 +667,11 @@ export function switchQueryEditor(queryEditor, displayLimit) { dispatch(fetchQueryResults(json.latest_query, displayLimit)); } }) - .catch((response) => { + .catch(response => { if (response.status !== 404) { - return dispatch(addDangerToast(t( - 'An error occurred while fetching tab state'))); + return dispatch( + addDangerToast(t('An error occurred while fetching tab state')), + ); } return dispatch({ type: REMOVE_QUERY_EDITOR, queryEditor }); }); @@ -567,43 +686,53 @@ export function setActiveSouthPaneTab(tabId) { } export function removeQueryEditor(queryEditor) { - return function (dispatch) { + return function(dispatch) { const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) - ? SupersetClient.delete({ endpoint: encodeURI(`/tabstateview/${queryEditor.id}`) }) + ? SupersetClient.delete({ + endpoint: encodeURI(`/tabstateview/${queryEditor.id}`), + }) : Promise.resolve(); return sync - .then(() => - dispatch({ type: REMOVE_QUERY_EDITOR, queryEditor }), - ) + .then(() => dispatch({ type: REMOVE_QUERY_EDITOR, queryEditor })) .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while removing tab. Please contact your administrator.'))), + dispatch( + addDangerToast( + t( + 'An error occurred while removing tab. Please contact your administrator.', + ), + ), + ), ); }; } export function removeQuery(query) { - return function (dispatch) { + return function(dispatch) { const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) ? SupersetClient.delete({ - endpoint: encodeURI(`/tabstateview/${query.sqlEditorId}/query/${query.id}`), - }) + endpoint: encodeURI( + `/tabstateview/${query.sqlEditorId}/query/${query.id}`, + ), + }) : Promise.resolve(); return sync - .then(() => - dispatch({ type: REMOVE_QUERY, query }), - ) + .then(() => dispatch({ type: REMOVE_QUERY, query })) .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while removing query. Please contact your administrator.'))), + dispatch( + addDangerToast( + t( + 'An error occurred while removing query. Please contact your administrator.', + ), + ), + ), ); }; } export function queryEditorSetDb(queryEditor, dbId) { - return function (dispatch) { + return function(dispatch) { const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) ? SupersetClient.put({ endpoint: encodeURI(`/tabstateview/${queryEditor.id}`), @@ -614,14 +743,19 @@ export function queryEditorSetDb(queryEditor, dbId) { return sync .then(() => dispatch({ type: QUERY_EDITOR_SETDB, queryEditor, dbId })) .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while setting the tab database ID. Please contact your administrator.'))), + dispatch( + addDangerToast( + t( + 'An error occurred while setting the tab database ID. Please contact your administrator.', + ), + ), + ), ); }; } export function queryEditorSetSchema(queryEditor, schema) { - return function (dispatch) { + return function(dispatch) { const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) ? SupersetClient.put({ endpoint: encodeURI(`/tabstateview/${queryEditor.id}`), @@ -630,10 +764,17 @@ export function queryEditorSetSchema(queryEditor, schema) { : Promise.resolve(); return sync - .then(() => dispatch({ type: QUERY_EDITOR_SET_SCHEMA, queryEditor, schema })) + .then(() => + dispatch({ type: QUERY_EDITOR_SET_SCHEMA, queryEditor, schema }), + ) .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while setting the tab schema. Please contact your administrator.'))), + dispatch( + addDangerToast( + t( + 'An error occurred while setting the tab schema. Please contact your administrator.', + ), + ), + ), ); }; } @@ -647,7 +788,7 @@ export function queryEditorSetTableOptions(queryEditor, options) { } export function queryEditorSetAutorun(queryEditor, autorun) { - return function (dispatch) { + return function(dispatch) { const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) ? SupersetClient.put({ endpoint: encodeURI(`/tabstateview/${queryEditor.id}`), @@ -656,16 +797,23 @@ export function queryEditorSetAutorun(queryEditor, autorun) { : Promise.resolve(); return sync - .then(() => dispatch({ type: QUERY_EDITOR_SET_AUTORUN, queryEditor, autorun })) + .then(() => + dispatch({ type: QUERY_EDITOR_SET_AUTORUN, queryEditor, autorun }), + ) .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while setting the tab autorun. Please contact your administrator.'))), + dispatch( + addDangerToast( + t( + 'An error occurred while setting the tab autorun. Please contact your administrator.', + ), + ), + ), ); }; } export function queryEditorSetTitle(queryEditor, title) { - return function (dispatch) { + return function(dispatch) { const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) ? SupersetClient.put({ endpoint: encodeURI(`/tabstateview/${queryEditor.id}`), @@ -674,16 +822,23 @@ export function queryEditorSetTitle(queryEditor, title) { : Promise.resolve(); return sync - .then(() => dispatch({ type: QUERY_EDITOR_SET_TITLE, queryEditor, title })) + .then(() => + dispatch({ type: QUERY_EDITOR_SET_TITLE, queryEditor, title }), + ) .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while setting the tab title. Please contact your administrator.'))), + dispatch( + addDangerToast( + t( + 'An error occurred while setting the tab title. Please contact your administrator.', + ), + ), + ), ); }; } export function queryEditorSetSql(queryEditor, sql) { - return function (dispatch) { + return function(dispatch) { const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) ? SupersetClient.put({ endpoint: encodeURI(`/tabstateview/${queryEditor.id}`), @@ -694,16 +849,21 @@ export function queryEditorSetSql(queryEditor, sql) { return sync .then(() => dispatch({ type: QUERY_EDITOR_SET_SQL, queryEditor, sql })) .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while storing your query in the backend. To ' + - 'avoid losing your changes, please save your query using the ' + - '"Save Query" button.'))), + dispatch( + addDangerToast( + t( + 'An error occurred while storing your query in the backend. To ' + + 'avoid losing your changes, please save your query using the ' + + '"Save Query" button.', + ), + ), + ), ); }; } export function queryEditorSetQueryLimit(queryEditor, queryLimit) { - return function (dispatch) { + return function(dispatch) { const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) ? SupersetClient.put({ endpoint: encodeURI(`/tabstateview/${queryEditor.id}`), @@ -712,16 +872,27 @@ export function queryEditorSetQueryLimit(queryEditor, queryLimit) { : Promise.resolve(); return sync - .then(() => dispatch({ type: QUERY_EDITOR_SET_QUERY_LIMIT, queryEditor, queryLimit })) + .then(() => + dispatch({ + type: QUERY_EDITOR_SET_QUERY_LIMIT, + queryEditor, + queryLimit, + }), + ) .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while setting the tab title. Please contact your administrator.'))), + dispatch( + addDangerToast( + t( + 'An error occurred while setting the tab title. Please contact your administrator.', + ), + ), + ), ); }; } export function queryEditorSetTemplateParams(queryEditor, templateParams) { - return function (dispatch) { + return function(dispatch) { const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) ? SupersetClient.put({ endpoint: encodeURI(`/tabstateview/${queryEditor.id}`), @@ -730,11 +901,22 @@ export function queryEditorSetTemplateParams(queryEditor, templateParams) { : Promise.resolve(); return sync - .then(() => dispatch({ type: QUERY_EDITOR_SET_TEMPLATE_PARAMS, queryEditor, templateParams })) + .then(() => + dispatch({ + type: QUERY_EDITOR_SET_TEMPLATE_PARAMS, + queryEditor, + templateParams, + }), + ) .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while setting the tab template parameters. ' + - 'Please contact your administrator.'))), + dispatch( + addDangerToast( + t( + 'An error occurred while setting the tab template parameters. ' + + 'Please contact your administrator.', + ), + ), + ), ); }; } @@ -748,8 +930,14 @@ export function mergeTable(table, query) { } function getTableMetadata(table, query, dispatch) { - return SupersetClient.get({ endpoint: encodeURI(`/superset/table/${query.dbId}/` + - `${encodeURIComponent(table.name)}/${encodeURIComponent(table.schema)}/`) }) + return SupersetClient.get({ + endpoint: encodeURI( + `/superset/table/${query.dbId}/` + + `${encodeURIComponent(table.name)}/${encodeURIComponent( + table.schema, + )}/`, + ), + }) .then(({ json }) => { const dataPreviewQuery = { id: shortid.generate(), @@ -783,30 +971,40 @@ function getTableMetadata(table, query, dispatch) { isMetadataLoading: false, }), ), - dispatch(addDangerToast(t('An error occurred while fetching table metadata'))), + dispatch( + addDangerToast(t('An error occurred while fetching table metadata')), + ), ]), ); } function getTableExtendedMetadata(table, query, dispatch) { return SupersetClient.get({ - endpoint: encodeURI(`/superset/extra_table_metadata/${query.dbId}/` + - `${encodeURIComponent(table.name)}/${encodeURIComponent(table.schema)}/`), + endpoint: encodeURI( + `/superset/extra_table_metadata/${query.dbId}/` + + `${encodeURIComponent(table.name)}/${encodeURIComponent( + table.schema, + )}/`, + ), }) .then(({ json }) => { - dispatch(mergeTable({ ...table, ...json, isExtraMetadataLoading: false })); + dispatch( + mergeTable({ ...table, ...json, isExtraMetadataLoading: false }), + ); return json; }) .catch(() => Promise.all([ dispatch(mergeTable({ ...table, isExtraMetadataLoading: false })), - dispatch(addDangerToast(t('An error occurred while fetching table metadata'))), + dispatch( + addDangerToast(t('An error occurred while fetching table metadata')), + ), ]), ); } export function addTable(query, tableName, schemaName) { - return function (dispatch) { + return function(dispatch) { const table = { dbId: query.dbId, queryEditorId: query.id, @@ -825,25 +1023,29 @@ export function addTable(query, tableName, schemaName) { return Promise.all([ getTableMetadata(table, query, dispatch), getTableExtendedMetadata(table, query, dispatch), - ]) - .then(([newTable, json]) => { - const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) - ? SupersetClient.post({ - endpoint: encodeURI('/tableschemaview/'), - postPayload: { table: { ...newTable, ...json } }, - }) - : Promise.resolve({ json: { id: shortid.generate() } }); + ]).then(([newTable, json]) => { + const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) + ? SupersetClient.post({ + endpoint: encodeURI('/tableschemaview/'), + postPayload: { table: { ...newTable, ...json } }, + }) + : Promise.resolve({ json: { id: shortid.generate() } }); - return sync - .then(({ json: resultJson }) => - dispatch(mergeTable({ ...table, id: resultJson.id })), - ) - .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while fetching table metadata. ' + - 'Please contact your administrator.'))), - ); - }); + return sync + .then(({ json: resultJson }) => + dispatch(mergeTable({ ...table, id: resultJson.id })), + ) + .catch(() => + dispatch( + addDangerToast( + t( + 'An error occurred while fetching table metadata. ' + + 'Please contact your administrator.', + ), + ), + ), + ); + }); }; } @@ -852,7 +1054,7 @@ export function changeDataPreviewId(oldQueryId, newQuery) { } export function reFetchQueryResults(query) { - return function (dispatch) { + return function(dispatch) { const newQuery = { id: shortid.generate(), dbId: query.dbId, @@ -871,7 +1073,7 @@ export function reFetchQueryResults(query) { } export function expandTable(table) { - return function (dispatch) { + return function(dispatch) { const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) ? SupersetClient.post({ endpoint: encodeURI(`/tableschemaview/${table.id}/expanded`), @@ -882,15 +1084,20 @@ export function expandTable(table) { return sync .then(() => dispatch({ type: EXPAND_TABLE, table })) .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while expanding the table schema. ' + - 'Please contact your administrator.'))), + dispatch( + addDangerToast( + t( + 'An error occurred while expanding the table schema. ' + + 'Please contact your administrator.', + ), + ), + ), ); }; } export function collapseTable(table) { - return function (dispatch) { + return function(dispatch) { const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) ? SupersetClient.post({ endpoint: encodeURI(`/tableschemaview/${table.id}/expanded`), @@ -901,25 +1108,37 @@ export function collapseTable(table) { return sync .then(() => dispatch({ type: COLLAPSE_TABLE, table })) .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while collapsing the table schema. ' + - 'Please contact your administrator.'))), + dispatch( + addDangerToast( + t( + 'An error occurred while collapsing the table schema. ' + + 'Please contact your administrator.', + ), + ), + ), ); }; } export function removeTable(table) { - return function (dispatch) { + return function(dispatch) { const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) - ? SupersetClient.delete({ endpoint: encodeURI(`/tableschemaview/${table.id}`) }) + ? SupersetClient.delete({ + endpoint: encodeURI(`/tableschemaview/${table.id}`), + }) : Promise.resolve(); return sync .then(() => dispatch({ type: REMOVE_TABLE, table })) .catch(() => - dispatch(addDangerToast(t( - 'An error occurred while removing the table schema. ' + - 'Please contact your administrator.'))), + dispatch( + addDangerToast( + t( + 'An error occurred while removing the table schema. ' + + 'Please contact your administrator.', + ), + ), + ), ); }; } @@ -933,11 +1152,16 @@ export function setUserOffline(offline) { } export function persistEditorHeight(queryEditor, northPercent, southPercent) { - return { type: QUERY_EDITOR_PERSIST_HEIGHT, queryEditor, northPercent, southPercent }; + return { + type: QUERY_EDITOR_PERSIST_HEIGHT, + queryEditor, + northPercent, + southPercent, + }; } export function popStoredQuery(urlId) { - return function (dispatch) { + return function(dispatch) { return SupersetClient.get({ endpoint: `/kv/${urlId}` }) .then(({ json }) => dispatch( @@ -954,8 +1178,10 @@ export function popStoredQuery(urlId) { }; } export function popSavedQuery(saveQueryId) { - return function (dispatch) { - return SupersetClient.get({ endpoint: `/savedqueryviewapi/api/get/${saveQueryId}` }) + return function(dispatch) { + return SupersetClient.get({ + endpoint: `/savedqueryviewapi/api/get/${saveQueryId}`, + }) .then(({ json }) => { const queryEditorProps = { ...convertQueryToClient(json.result), @@ -967,7 +1193,7 @@ export function popSavedQuery(saveQueryId) { }; } export function popDatasourceQuery(datasourceKey, sql) { - return function (dispatch) { + return function(dispatch) { return SupersetClient.get({ endpoint: `/superset/fetch_datasource_metadata?datasourceKey=${datasourceKey}`, }) @@ -982,7 +1208,9 @@ export function popDatasourceQuery(datasourceKey, sql) { }), ), ) - .catch(() => dispatch(addDangerToast(t("The datasource couldn't be loaded")))); + .catch(() => + dispatch(addDangerToast(t("The datasource couldn't be loaded"))), + ); }; } export function createDatasourceStarted() { @@ -997,7 +1225,7 @@ export function createDatasourceFailed(err) { } export function createDatasource(vizOptions) { - return (dispatch) => { + return dispatch => { dispatch(createDatasourceStarted()); return SupersetClient.post({ endpoint: '/superset/sqllab_viz/', @@ -1009,7 +1237,11 @@ export function createDatasource(vizOptions) { return Promise.resolve(json); }) .catch(() => { - dispatch(createDatasourceFailed(t('An error occurred while creating the data source'))); + dispatch( + createDatasourceFailed( + t('An error occurred while creating the data source'), + ), + ); return Promise.reject(); }); diff --git a/superset/assets/src/SqlLab/components/AceEditorWrapper.jsx b/superset/assets/src/SqlLab/components/AceEditorWrapper.jsx index 314cb3189d4..ca142f182de 100644 --- a/superset/assets/src/SqlLab/components/AceEditorWrapper.jsx +++ b/superset/assets/src/SqlLab/components/AceEditorWrapper.jsx @@ -32,21 +32,22 @@ const SCHEMA_AUTOCOMPLETE_SCORE = 60; const TABLE_AUTOCOMPLETE_SCORE = 55; const COLUMN_AUTOCOMPLETE_SCORE = 50; -const keywords = ( +const keywords = 'SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|AND|OR|GROUP|BY|ORDER|LIMIT|OFFSET|HAVING|AS|CASE|' + 'WHEN|THEN|ELSE|END|TYPE|LEFT|RIGHT|JOIN|ON|OUTER|DESC|ASC|UNION|CREATE|TABLE|PRIMARY|KEY|IF|' + 'FOREIGN|NOT|REFERENCES|DEFAULT|NULL|INNER|CROSS|NATURAL|DATABASE|DROP|GRANT|SUM|MAX|MIN|COUNT|' + - 'AVG|DISTINCT' -); + 'AVG|DISTINCT'; -const dataTypes = ( +const dataTypes = 'INT|NUMERIC|DECIMAL|DATE|VARCHAR|CHAR|BIGINT|FLOAT|DOUBLE|BIT|BINARY|TEXT|SET|TIMESTAMP|' + - 'MONEY|REAL|NUMBER|INTEGER' -); + 'MONEY|REAL|NUMBER|INTEGER'; const sqlKeywords = [].concat(keywords.split('|'), dataTypes.split('|')); export const sqlWords = sqlKeywords.map(s => ({ - name: s, value: s, score: SQL_KEYWORD_AUTOCOMPLETE_SCORE, meta: 'sql', + name: s, + value: s, + score: SQL_KEYWORD_AUTOCOMPLETE_SCORE, + meta: 'sql', })); const propTypes = { @@ -58,17 +59,19 @@ const propTypes = { extendedTables: PropTypes.array, queryEditor: PropTypes.object.isRequired, height: PropTypes.string, - hotkeys: PropTypes.arrayOf(PropTypes.shape({ - key: PropTypes.string.isRequired, - descr: PropTypes.string.isRequired, - func: PropTypes.func.isRequired, - })), + hotkeys: PropTypes.arrayOf( + PropTypes.shape({ + key: PropTypes.string.isRequired, + descr: PropTypes.string.isRequired, + func: PropTypes.func.isRequired, + }), + ), onChange: PropTypes.func, }; const defaultProps = { - onBlur: () => { }, - onChange: () => { }, + onBlur: () => {}, + onChange: () => {}, schemas: [], tables: [], extendedTables: [], @@ -89,9 +92,14 @@ class AceEditorWrapper extends React.PureComponent { this.setAutoCompleter(this.props); } UNSAFE_componentWillReceiveProps(nextProps) { - if (!areArraysShallowEqual(this.props.tables, nextProps.tables) || + if ( + !areArraysShallowEqual(this.props.tables, nextProps.tables) || !areArraysShallowEqual(this.props.schemas, nextProps.schemas) || - !areArraysShallowEqual(this.props.extendedTables, nextProps.extendedTables)) { + !areArraysShallowEqual( + this.props.extendedTables, + nextProps.extendedTables, + ) + ) { this.setAutoCompleter(nextProps); } if (nextProps.sql !== this.props.sql) { @@ -112,7 +120,7 @@ class AceEditorWrapper extends React.PureComponent { this.onAltEnter(); }, }); - this.props.hotkeys.forEach((keyConfig) => { + this.props.hotkeys.forEach(keyConfig => { editor.commands.addCommand({ name: keyConfig.name, bindKey: { win: keyConfig.key, mac: keyConfig.key }, @@ -123,10 +131,15 @@ class AceEditorWrapper extends React.PureComponent { editor.selection.on('changeSelection', () => { const selectedText = editor.getSelectedText(); // Backspace trigger 1 character selection, ignoring - if (selectedText !== this.state.selectedText && selectedText.length !== 1) { + if ( + selectedText !== this.state.selectedText && + selectedText.length !== 1 + ) { this.setState({ selectedText }); this.props.actions.queryEditorSetSelectedText( - this.props.queryEditor, selectedText); + this.props.queryEditor, + selectedText, + ); } }); } @@ -162,12 +175,12 @@ class AceEditorWrapper extends React.PureComponent { const columns = {}; const tables = props.tables || []; const extendedTables = props.extendedTables || []; - const tableWords = tables.map((t) => { + const tableWords = tables.map(t => { const tableName = t.value; const extendedTable = extendedTables.find(et => et.name === tableName); - const cols = extendedTable && extendedTable.columns || []; - cols.forEach((col) => { - columns[col.name] = null; // using an object as a unique set + const cols = (extendedTable && extendedTable.columns) || []; + cols.forEach(col => { + columns[col.name] = null; // using an object as a unique set }); return { name: t.label, @@ -177,11 +190,17 @@ class AceEditorWrapper extends React.PureComponent { }; }); - const columnWords = Object.keys(columns).map(col => ( - { name: col, value: col, score: COLUMN_AUTOCOMPLETE_SCORE, meta: 'column' } - )); + const columnWords = Object.keys(columns).map(col => ({ + name: col, + value: col, + score: COLUMN_AUTOCOMPLETE_SCORE, + meta: 'column', + })); - const words = schemaWords.concat(tableWords).concat(columnWords).concat(sqlWords); + const words = schemaWords + .concat(tableWords) + .concat(columnWords) + .concat(sqlWords); this.setState({ words }, () => { const completer = { @@ -194,7 +213,7 @@ class AceEditorWrapper extends React.PureComponent { } getAceAnnotations() { const validationResult = this.props.queryEditor.validationResult; - const resultIsReady = (validationResult && validationResult.completed); + const resultIsReady = validationResult && validationResult.completed; if (resultIsReady && validationResult.errors.length > 0) { const errors = validationResult.errors.map(err => ({ type: 'error', diff --git a/superset/assets/src/SqlLab/components/App.jsx b/superset/assets/src/SqlLab/components/App.jsx index 016a19ecd7f..f1c21be3361 100644 --- a/superset/assets/src/SqlLab/components/App.jsx +++ b/superset/assets/src/SqlLab/components/App.jsx @@ -56,10 +56,13 @@ class App extends React.PureComponent { window.addEventListener('resize', this.handleResize.bind(this)); } componentDidUpdate() { - if (this.props.localStorageUsageInKilobytes >= + if ( + this.props.localStorageUsageInKilobytes >= LOCALSTORAGE_WARNING_THRESHOLD * LOCALSTORAGE_MAX_USAGE_KB ) { - this.showLocalStorageUsageWarning(this.props.localStorageUsageInKilobytes); + this.showLocalStorageUsageWarning( + this.props.localStorageUsageInKilobytes, + ); } } componentWillUnmount() { @@ -75,23 +78,34 @@ class App extends React.PureComponent { const searchHeaderEl = $('#search-header'); const alertEl = $('#sqllab-alerts'); const headerEl = $('header .navbar'); - const headerHeight = headerEl.outerHeight() + parseInt(headerEl.css('marginBottom'), 10); + const headerHeight = + headerEl.outerHeight() + parseInt(headerEl.css('marginBottom'), 10); const searchHeaderHeight = searchHeaderEl.length > 0 - ? searchHeaderEl.outerHeight() + parseInt(searchHeaderEl.css('marginBottom'), 10) + ? searchHeaderEl.outerHeight() + + parseInt(searchHeaderEl.css('marginBottom'), 10) : 0; - const tabsHeight = tabsEl.length > 0 ? tabsEl.outerHeight() : searchHeaderHeight; + const tabsHeight = + tabsEl.length > 0 ? tabsEl.outerHeight() : searchHeaderHeight; const warningHeight = warningEl.length > 0 ? warningEl.outerHeight() : 0; const alertHeight = alertEl.length > 0 ? alertEl.outerHeight() : 0; - return `${window.innerHeight - headerHeight - tabsHeight - warningHeight - alertHeight}px`; + return `${window.innerHeight - + headerHeight - + tabsHeight - + warningHeight - + alertHeight}px`; } showLocalStorageUsageWarning(currentUsage) { this.props.actions.addDangerToast( - t('SQL Lab uses your browser\'s local storage to store queries and results.' + - `\n Currently, you are using ${currentUsage.toFixed(2)} KB out of ${LOCALSTORAGE_MAX_USAGE_KB} KB. storage space.` + - '\n To keep SQL Lab from crashing, please delete some query tabs.' + - '\n You can re-access these queries by using the Save feature before you delete the tab. ' + - 'Note that you will need to close other SQL Lab windows before you do this.'), + t( + "SQL Lab uses your browser's local storage to store queries and results." + + `\n Currently, you are using ${currentUsage.toFixed( + 2, + )} KB out of ${LOCALSTORAGE_MAX_USAGE_KB} KB. storage space.` + + '\n To keep SQL Lab from crashing, please delete some query tabs.' + + '\n You can re-access these queries by using the Save feature before you delete the tab. ' + + 'Note that you will need to close other SQL Lab windows before you do this.', + ), ); } handleResize() { @@ -145,7 +159,4 @@ function mapDispatchToProps(dispatch) { } export { App }; -export default connect( - mapStateToProps, - mapDispatchToProps, -)(App); +export default connect(mapStateToProps, mapDispatchToProps)(App); diff --git a/superset/assets/src/SqlLab/components/ColumnElement.jsx b/superset/assets/src/SqlLab/components/ColumnElement.jsx index c2c0aa9e091..1ee8579e8a1 100644 --- a/superset/assets/src/SqlLab/components/ColumnElement.jsx +++ b/superset/assets/src/SqlLab/components/ColumnElement.jsx @@ -54,7 +54,7 @@ export default function ColumnElement(props) { {JSON.stringify(key, null, ' ')} - } + } > @@ -64,11 +64,13 @@ export default function ColumnElement(props) { return (
- {name}{icons} + {name} + {icons}
{col.type}
-
); + + ); } ColumnElement.propTypes = propTypes; diff --git a/superset/assets/src/SqlLab/components/ExploreResultsButton.jsx b/superset/assets/src/SqlLab/components/ExploreResultsButton.jsx index 4d8de5726e5..260c93366a8 100644 --- a/superset/assets/src/SqlLab/components/ExploreResultsButton.jsx +++ b/superset/assets/src/SqlLab/components/ExploreResultsButton.jsx @@ -48,7 +48,9 @@ class ExploreResultsButton extends React.PureComponent { this.visualize = this.visualize.bind(this); this.onClick = this.onClick.bind(this); this.getInvalidColumns = this.getInvalidColumns.bind(this); - this.renderInvalidColumnMessage = this.renderInvalidColumnMessage.bind(this); + this.renderInvalidColumnMessage = this.renderInvalidColumnMessage.bind( + this, + ); } onClick() { const timeout = this.props.timeout; @@ -64,7 +66,7 @@ class ExploreResultsButton extends React.PureComponent { }), ], bsSize: 'large', - onHide: (dialog) => { + onHide: dialog => { dialog.hide(); }, }); @@ -75,7 +77,7 @@ class ExploreResultsButton extends React.PureComponent { actions: [Dialog.DefaultAction('Ok', () => {}, 'btn-primary')], bsSize: 'large', bsStyle: 'warning', - onHide: (dialog) => { + onHide: dialog => { dialog.hide(); }, }); @@ -85,19 +87,26 @@ class ExploreResultsButton extends React.PureComponent { } getColumns() { const props = this.props; - if (props.query && props.query.results && props.query.results.selected_columns) { + if ( + props.query && + props.query.results && + props.query.results.selected_columns + ) { return props.query.results.selected_columns; } return []; } getQueryDuration() { - return moment.duration(this.props.query.endDttm - this.props.query.startDttm).asSeconds(); + return moment + .duration(this.props.query.endDttm - this.props.query.startDttm) + .asSeconds(); } getInvalidColumns() { - const re1 = /^[A-Za-z_]\w*$/; // starts with char or _, then only alphanum - const re2 = /__\d+$/; // does not finish with __ and then a number which screams dup col name + const re1 = /^[A-Za-z_]\w*$/; // starts with char or _, then only alphanum + const re2 = /__\d+$/; // does not finish with __ and then a number which screams dup col name - return this.props.query.results.selected_columns.map(col => col.name) + return this.props.query.results.selected_columns + .map(col => col.name) .filter(col => !re1.test(col) || re2.test(col)); } datasourceName() { @@ -124,7 +133,7 @@ class ExploreResultsButton extends React.PureComponent { visualize() { this.props.actions .createDatasource(this.buildVizOptions()) - .then((data) => { + .then(data => { const columns = this.getColumns(); const formData = { datasource: `${data.table_id}__table`, @@ -136,22 +145,36 @@ class ExploreResultsButton extends React.PureComponent { row_limit: 1000, }; - this.props.actions.addInfoToast(t('Creating a data source and creating a new tab')); + this.props.actions.addInfoToast( + t('Creating a data source and creating a new tab'), + ); // open new window for data visualization exportChart(formData); }) .catch(() => { - this.props.actions.addDangerToast(this.props.errorMessage || t('An error occurred')); + this.props.actions.addDangerToast( + this.props.errorMessage || t('An error occurred'), + ); }); } renderTimeoutWarning() { return ( - {t('This query took %s seconds to run, ', Math.round(this.getQueryDuration())) + - t('and the explore view times out at %s seconds ', this.props.timeout) + - t('following this flow will most likely lead to your query timing out. ') + - t('We recommend your summarize your data further before following that flow. ') + + {t( + 'This query took %s seconds to run, ', + Math.round(this.getQueryDuration()), + ) + + t( + 'and the explore view times out at %s seconds ', + this.props.timeout, + ) + + t( + 'following this flow will most likely lead to your query timing out. ', + ) + + t( + 'We recommend your summarize your data further before following that flow. ', + ) + t('If activated you can use the ')} CREATE TABLE AS {t('feature to store a summarized data set that you can then explore.')} @@ -173,7 +196,8 @@ class ExploreResultsButton extends React.PureComponent { SELECT count(*) AS my_alias - ){' '} + + ){' '} {t(`limited to alphanumeric characters and underscores. Column aliases ending with double underscores followed by a numeric value are not allowed for reasons discussed in Github issue #5739. @@ -182,7 +206,8 @@ class ExploreResultsButton extends React.PureComponent { ); } render() { - const allowsSubquery = this.props.database && this.props.database.allows_subquery; + const allowsSubquery = + this.props.database && this.props.database.allows_subquery; return ( <> { + ref={el => { this.dialog = el; }} /> diff --git a/superset/assets/src/SqlLab/components/HighlightedSql.jsx b/superset/assets/src/SqlLab/components/HighlightedSql.jsx index 8769575b23a..8d75be72673 100644 --- a/superset/assets/src/SqlLab/components/HighlightedSql.jsx +++ b/superset/assets/src/SqlLab/components/HighlightedSql.jsx @@ -18,7 +18,9 @@ */ import React from 'react'; import PropTypes from 'prop-types'; -import SyntaxHighlighter, { registerLanguage } from 'react-syntax-highlighter/dist/light'; +import SyntaxHighlighter, { + registerLanguage, +} from 'react-syntax-highlighter/dist/light'; import sql from 'react-syntax-highlighter/dist/languages/hljs/sql'; import github from 'react-syntax-highlighter/dist/styles/hljs/github'; import { t } from '@superset-ui/translation'; @@ -55,20 +57,24 @@ class HighlightedSql extends React.Component { lines = lines.slice(0, this.props.maxLines); lines.push('{...}'); } - return lines.map((line) => { - if (line.length > this.props.maxWidth) { - return line.slice(0, this.props.maxWidth) + '{...}'; - } - return line; - }) - .join('\n'); + return lines + .map(line => { + if (line.length > this.props.maxWidth) { + return line.slice(0, this.props.maxWidth) + '{...}'; + } + return line; + }) + .join('\n'); } triggerNode() { - const shownSql = this.props.shrink ? this.shrinkSql(this.props.sql) : this.props.sql; + const shownSql = this.props.shrink + ? this.shrinkSql(this.props.sql) + : this.props.sql; return ( {shownSql} - ); + + ); } generateModal() { let rawSql; diff --git a/superset/assets/src/SqlLab/components/LimitControl.jsx b/superset/assets/src/SqlLab/components/LimitControl.jsx index 185d76a6ad9..c5ec5ba9513 100644 --- a/superset/assets/src/SqlLab/components/LimitControl.jsx +++ b/superset/assets/src/SqlLab/components/LimitControl.jsx @@ -55,14 +55,19 @@ export default class LimitControl extends React.PureComponent { } submitAndClose() { - const value = parseInt(this.state.textValue, 10) || this.props.defaultQueryLimit; + const value = + parseInt(this.state.textValue, 10) || this.props.defaultQueryLimit; this.props.onChange(value); this.setState({ showOverlay: false }); } isValidLimit(limit) { - const value = parseInt(limit, 10); - return !(Number.isNaN(value) || value <= 0 || (this.props.maxRow && value > this.props.maxRow)); + const value = parseInt(limit, 10); + return !( + Number.isNaN(value) || + value <= 0 || + (this.props.maxRow && value > this.props.maxRow) + ); } handleToggle() { @@ -76,8 +81,11 @@ export default class LimitControl extends React.PureComponent { renderPopover() { const textValue = this.state.textValue; const isValid = this.isValidLimit(textValue); - const errorMsg = t('Row limit must be positive integer') + - (this.props.maxRow ? t(' and not greater than %s', this.props.maxRow) : ''); + const errorMsg = + t('Row limit must be positive integer') + + (this.props.maxRow + ? t(' and not greater than %s', this.props.maxRow) + : ''); return (
@@ -107,7 +115,10 @@ export default class LimitControl extends React.PureComponent { @@ -120,10 +131,7 @@ export default class LimitControl extends React.PureComponent { render() { return (
-
@@ -169,8 +186,10 @@ export default class ResultSet extends React.PureComponent { } render() { const query = this.props.query; - const height = Math.max(0, - (this.props.search ? this.props.height - SEARCH_HEIGHT : this.props.height)); + const height = Math.max( + 0, + this.props.search ? this.props.height - SEARCH_HEIGHT : this.props.height, + ); let sql; if (this.props.showSql) { @@ -184,13 +203,14 @@ export default class ResultSet extends React.PureComponent { {query.errorMessage} {query.link && {t('(Request Access)')} } -
); + + ); } else if (query.state === 'success' && query.ctas) { return (
- {t('Table')} [{query.tempTable}] {t('was ' + - 'created')}   + {t('Table')} [{query.tempTable}] {t('was created')}{' '} +  
); + + ); } else if (query.state === 'success' && query.results) { const results = query.results; let data; @@ -226,7 +247,9 @@ export default class ResultSet extends React.PureComponent { ); } else if (data && data.length === 0) { - return {t('The query returned no data')}; + return ( + {t('The query returned no data')} + ); } } if (query.cached || (query.state === 'success' && !query.results)) { @@ -235,7 +258,10 @@ export default class ResultSet extends React.PureComponent { bsSize="sm" className="fetch" bsStyle="primary" - onClick={this.reFetchQueryResults.bind(this, { ...query, isDataPreview: true })} + onClick={this.reFetchQueryResults.bind(this, { + ...query, + isDataPreview: true, + })} > {t('Fetch data preview')} @@ -249,34 +275,34 @@ export default class ResultSet extends React.PureComponent { striped now={query.progress} label={`${query.progress.toFixed(0)}%`} - />); + /> + ); } if (query.trackingUrl) { trackingUrl = ( ); } - const progressMsg = query && query.extra && query.extra.progress ? query.extra.progress : null; + const progressMsg = + query && query.extra && query.extra.progress + ? query.extra.progress + : null; return (
-
- {!progressBar && } -
+
{!progressBar && }
{progressMsg && {progressMsg}}
-
- {progressBar} -
-
- {trackingUrl} -
+
{progressBar}
+
{trackingUrl}
); } diff --git a/superset/assets/src/SqlLab/components/RunQueryActionButton.jsx b/superset/assets/src/SqlLab/components/RunQueryActionButton.jsx index 37337a86577..218d66a76a4 100644 --- a/superset/assets/src/SqlLab/components/RunQueryActionButton.jsx +++ b/superset/assets/src/SqlLab/components/RunQueryActionButton.jsx @@ -37,22 +37,22 @@ const defaultProps = { }; export default function RunQueryActionButton(props) { - const runBtnText = props.selectedText ? t('Run Selected Query') : t('Run Query'); + const runBtnText = props.selectedText + ? t('Run Selected Query') + : t('Run Query'); const btnStyle = props.selectedText ? 'warning' : 'primary'; - const shouldShowStopBtn = ['running', 'pending'].indexOf(props.queryState) > -1; + const shouldShowStopBtn = + ['running', 'pending'].indexOf(props.queryState) > -1; const commonBtnProps = { bsSize: 'small', bsStyle: btnStyle, - disabled: !(props.dbId), + disabled: !props.dbId, }; if (shouldShowStopBtn) { return ( - ); @@ -66,7 +66,8 @@ export default function RunQueryActionButton(props) { disabled={!props.sql.trim()} > {runBtnText} - ); + + ); } return ( } diff --git a/superset/assets/src/SqlLab/components/ScheduleQueryButton.jsx b/superset/assets/src/SqlLab/components/ScheduleQueryButton.jsx index 09a7ddb3b22..207b26e59a8 100644 --- a/superset/assets/src/SqlLab/components/ScheduleQueryButton.jsx +++ b/superset/assets/src/SqlLab/components/ScheduleQueryButton.jsx @@ -58,7 +58,7 @@ function getValidationRules() { function getValidator() { const rules = getValidationRules(); return (formData, errors) => { - rules.forEach((rule) => { + rules.forEach(rule => { const test = validators[rule.name]; const args = rule.arguments.map(name => formData[name]); const container = rule.container || rule.arguments.slice(-1)[0]; @@ -181,7 +181,7 @@ class ScheduleQueryButton extends React.PureComponent { return ( { + ref={ref => { this.saveModal = ref; }} modalTitle={t('Schedule Query')} diff --git a/superset/assets/src/SqlLab/components/ShareSqlLabQuery.jsx b/superset/assets/src/SqlLab/components/ShareSqlLabQuery.jsx index 176a4fc77dd..29313dc2470 100644 --- a/superset/assets/src/SqlLab/components/ShareSqlLabQuery.jsx +++ b/superset/assets/src/SqlLab/components/ShareSqlLabQuery.jsx @@ -52,10 +52,10 @@ class ShareSqlLabQuery extends React.Component { const sharedQuery = { dbId, title, schema, autorun, sql }; return storeQuery(sharedQuery) - .then((shortUrl) => { + .then(shortUrl => { this.setState({ shortUrl }); }) - .catch((response) => { + .catch(response => { getClientErrorObject(response).then(({ error }) => { this.props.addDangerToast(error); this.setState({ shortUrl: t('Error') }); @@ -68,7 +68,9 @@ class ShareSqlLabQuery extends React.Component { } + copyNode={ + + } /> ); diff --git a/superset/assets/src/SqlLab/components/ShowSQL.jsx b/superset/assets/src/SqlLab/components/ShowSQL.jsx index 45da9aa3cd5..10180f76139 100644 --- a/superset/assets/src/SqlLab/components/ShowSQL.jsx +++ b/superset/assets/src/SqlLab/components/ShowSQL.jsx @@ -18,7 +18,9 @@ */ import React from 'react'; import PropTypes from 'prop-types'; -import SyntaxHighlighter, { registerLanguage } from 'react-syntax-highlighter/dist/light'; +import SyntaxHighlighter, { + registerLanguage, +} from 'react-syntax-highlighter/dist/light'; import sql from 'react-syntax-highlighter/dist/languages/hljs/sql'; import github from 'react-syntax-highlighter/dist/styles/hljs/github'; diff --git a/superset/assets/src/SqlLab/components/SouthPane.jsx b/superset/assets/src/SqlLab/components/SouthPane.jsx index 3f747a8af9d..54c0dc34995 100644 --- a/superset/assets/src/SqlLab/components/SouthPane.jsx +++ b/superset/assets/src/SqlLab/components/SouthPane.jsx @@ -28,7 +28,11 @@ import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags'; import * as Actions from '../actions/sqlLab'; import QueryHistory from './QueryHistory'; import ResultSet from './ResultSet'; -import { STATUS_OPTIONS, STATE_BSSTYLE_MAP, LOCALSTORAGE_MAX_QUERY_AGE_MS } from '../constants'; +import { + STATUS_OPTIONS, + STATE_BSSTYLE_MAP, + LOCALSTORAGE_MAX_QUERY_AGE_MS, +} from '../constants'; const TAB_HEIGHT = 44; @@ -69,7 +73,9 @@ export class SouthPane extends React.PureComponent { } // One layer of abstraction for easy spying in unit tests getSouthPaneHeight() { - return this.southPaneRef.current ? this.southPaneRef.current.clientHeight : 0; + return this.southPaneRef.current + ? this.southPaneRef.current.clientHeight + : 0; } switchTab(id) { this.props.actions.setActiveSouthPaneTab(id); @@ -77,26 +83,40 @@ export class SouthPane extends React.PureComponent { render() { if (this.props.offline) { return ( - ); + + ); } const innerTabContentHeight = this.state.height - TAB_HEIGHT; let latestQuery; const props = this.props; if (props.editorQueries.length > 0) { // get the latest query - latestQuery = props.editorQueries.find(q => q.id === this.props.latestQueryId); + latestQuery = props.editorQueries.find( + q => q.id === this.props.latestQueryId, + ); } let results; if (latestQuery) { if ( isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) && latestQuery.state === 'success' && - (!latestQuery.resultsKey && !latestQuery.results) + !latestQuery.resultsKey && + !latestQuery.results + ) { + results = ( + + {t('No stored results found, you need to re-run your query')} + + ); + } else if ( + Date.now() - latestQuery.startDttm <= + LOCALSTORAGE_MAX_QUERY_AGE_MS ) { - results = {t('No stored results found, you need to re-run your query')}; - } else if ((Date.now() - latestQuery.startDttm) <= LOCALSTORAGE_MAX_QUERY_AGE_MS) { results = ( {t('Run a query to display results here')}; + results = ( + {t('Run a query to display results here')} + ); } const dataPreviewTabs = props.dataPreviewQueries.map(query => ( - + {results} - + = 0) { + if ( + this.props.latestQuery && + ['running', 'pending'].indexOf(this.props.latestQuery.state) >= 0 + ) { this.props.actions.postStopQuery(this.props.latestQuery); } } @@ -297,7 +314,10 @@ class SqlEditor extends React.PureComponent { } queryPane() { const hotkeys = this.getHotkeyConfig(); - const { aceEditorHeight, southPaneHeight } = this.getAceEditorAndSouthPaneHeights( + const { + aceEditorHeight, + southPaneHeight, + } = this.getAceEditorAndSouthPaneHeights( this.state.height, this.state.northPercent, this.state.southPercent, @@ -370,15 +390,18 @@ class SqlEditor extends React.PureComponent { const qe = this.props.queryEditor; let limitWarning = null; if ( - this.props.latestQuery - && this.props.latestQuery.results - && this.props.latestQuery.results.displayLimitReached + this.props.latestQuery && + this.props.latestQuery.results && + this.props.latestQuery.results.displayLimitReached ) { const tooltip = ( - {t(`It appears that the number of rows in the query results displayed + {t( + `It appears that the number of rows in the query results displayed was limited on the server side to - the %s limit.`, this.props.latestQuery.rows)} + the %s limit.`, + this.props.latestQuery.rows, + )} ); limitWarning = ( @@ -387,7 +410,8 @@ class SqlEditor extends React.PureComponent { ); } - const successful = this.props.latestQuery && this.props.latestQuery.state === 'success'; + const successful = + this.props.latestQuery && this.props.latestQuery.state === 'success'; const scheduleToolTip = successful ? t('Schedule the query periodically') : t('You must run the query successfully first'); @@ -397,49 +421,56 @@ class SqlEditor extends React.PureComponent {
- { - isFeatureEnabled(FeatureFlag.ESTIMATE_QUERY_COST) && + {isFeatureEnabled(FeatureFlag.ESTIMATE_QUERY_COST) && this.props.database && - this.props.database.allows_cost_estimate && + this.props.database.allows_cost_estimate && ( + + + + )} + {isFeatureEnabled(FeatureFlag.SCHEDULED_QUERIES) && ( - - } - {isFeatureEnabled(FeatureFlag.SCHEDULED_QUERIES) && - - - - } + )} - { ctasControls && ( - - {ctasControls} - - )} + {ctasControls && {ctasControls}} - +
{ + onChange={params => { this.props.actions.queryEditorSetTemplateParams(qe, params); }} code={qe.templateParams} /> {limitWarning} - {this.props.latestQuery && + {this.props.latestQuery && ( - } + )}
); diff --git a/superset/assets/src/SqlLab/components/SqlEditorLeftBar.jsx b/superset/assets/src/SqlLab/components/SqlEditorLeftBar.jsx index bf7afff727b..0f2ac94b03e 100644 --- a/superset/assets/src/SqlLab/components/SqlEditorLeftBar.jsx +++ b/superset/assets/src/SqlLab/components/SqlEditorLeftBar.jsx @@ -54,10 +54,16 @@ export default class SqlEditorLeftBar extends React.PureComponent { this.props.actions.queryEditorSetSchema(this.props.queryEditor, schema); } onSchemasLoad(schemas) { - this.props.actions.queryEditorSetSchemaOptions(this.props.queryEditor, schemas); + this.props.actions.queryEditorSetSchemaOptions( + this.props.queryEditor, + schemas, + ); } onTablesLoad(tables) { - this.props.actions.queryEditorSetTableOptions(this.props.queryEditor, tables); + this.props.actions.queryEditorSetTableOptions( + this.props.queryEditor, + tables, + ); } onDbChange(db) { this.props.actions.queryEditorSetDb(this.props.queryEditor, db.id); @@ -76,7 +82,9 @@ export default class SqlEditorLeftBar extends React.PureComponent { })); this.props.actions.setDatabases(data.result); if (data.result.length === 0) { - this.props.actions.addDangerToast(t("It seems you don't have access to any database")); + this.props.actions.addDangerToast( + t("It seems you don't have access to any database"), + ); } return options; } @@ -118,9 +126,16 @@ export default class SqlEditorLeftBar extends React.PureComponent { />
-
+
{this.props.tables.map(table => ( - + ))}
diff --git a/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx b/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx index 8ffd097f13f..b47d7921f05 100644 --- a/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx +++ b/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx @@ -67,22 +67,36 @@ class TabbedSqlEditors extends React.PureComponent { this.removeQueryEditor = this.removeQueryEditor.bind(this); this.renameTab = this.renameTab.bind(this); this.toggleLeftBar = this.toggleLeftBar.bind(this); - this.removeAllOtherQueryEditors = this.removeAllOtherQueryEditors.bind(this); + this.removeAllOtherQueryEditors = this.removeAllOtherQueryEditors.bind( + this, + ); this.duplicateQueryEditor = this.duplicateQueryEditor.bind(this); } componentDidMount() { // migrate query editor and associated tables state to server if (isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE)) { - const localStorageTables = this.props.tables.filter(table => table.inLocalStorage); - const localStorageQueries = Object.values(this.props.queries) - .filter(query => query.inLocalStorage); - this.props.queryEditors.filter(qe => qe.inLocalStorage).forEach((qe) => { - // get all queries associated with the query editor - const queries = localStorageQueries - .filter(query => query.sqlEditorId === qe.id); - const tables = localStorageTables.filter(table => table.queryEditorId === qe.id); - this.props.actions.migrateQueryEditorFromLocalStorage(qe, tables, queries); - }); + const localStorageTables = this.props.tables.filter( + table => table.inLocalStorage, + ); + const localStorageQueries = Object.values(this.props.queries).filter( + query => query.inLocalStorage, + ); + this.props.queryEditors + .filter(qe => qe.inLocalStorage) + .forEach(qe => { + // get all queries associated with the query editor + const queries = localStorageQueries.filter( + query => query.sqlEditorId === qe.id, + ); + const tables = localStorageTables.filter( + table => table.queryEditorId === qe.id, + ); + this.props.actions.migrateQueryEditorFromLocalStorage( + qe, + tables, + queries, + ); + }); } const query = URI(window.location).search(true); @@ -102,7 +116,7 @@ class TabbedSqlEditors extends React.PureComponent { const databases = this.props.databases; const dbName = query.dbname; if (dbName) { - Object.keys(databases).forEach((db) => { + Object.keys(databases).forEach(db => { if (databases[db].database_name === dbName) { dbId = databases[db].id; } @@ -126,16 +140,21 @@ class TabbedSqlEditors extends React.PureComponent { const latestQuery = this.props.queries[qe.latestQueryId]; if ( isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) && - latestQuery && latestQuery.resultsKey + latestQuery && + latestQuery.resultsKey ) { // when results are not stored in localStorage they need to be // fetched from the results backend (if configured) - this.props.actions.fetchQueryResults(latestQuery, this.props.displayLimit); + this.props.actions.fetchQueryResults( + latestQuery, + this.props.displayLimit, + ); } } } UNSAFE_componentWillReceiveProps(nextProps) { - const nextActiveQeId = nextProps.tabHistory[nextProps.tabHistory.length - 1]; + const nextActiveQeId = + nextProps.tabHistory[nextProps.tabHistory.length - 1]; const queriesArray = []; for (const id in nextProps.queries) { if (nextProps.queries[id].sqlEditorId === nextActiveQeId) { @@ -147,13 +166,22 @@ class TabbedSqlEditors extends React.PureComponent { } const dataPreviewQueries = []; - nextProps.tables.forEach((table) => { + nextProps.tables.forEach(table => { const queryId = table.dataPreviewQueryId; - if (queryId && nextProps.queries[queryId] && table.queryEditorId === nextActiveQeId) { - dataPreviewQueries.push({ ...nextProps.queries[queryId], tableName: table.name }); + if ( + queryId && + nextProps.queries[queryId] && + table.queryEditorId === nextActiveQeId + ) { + dataPreviewQueries.push({ + ...nextProps.queries[queryId], + tableName: table.name, + }); } }); - if (!areArraysShallowEqual(dataPreviewQueries, this.state.dataPreviewQueries)) { + if ( + !areArraysShallowEqual(dataPreviewQueries, this.state.dataPreviewQueries) + ) { this.setState({ dataPreviewQueries }); } } @@ -180,7 +208,8 @@ class TabbedSqlEditors extends React.PureComponent { queryCount++; const activeQueryEditor = this.activeQueryEditor(); const firstDbId = Math.min( - ...Object.values(this.props.databases).map(database => database.id)); + ...Object.values(this.props.databases).map(database => database.id), + ); const warning = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) ? '' : `${t( @@ -191,7 +220,7 @@ class TabbedSqlEditors extends React.PureComponent { dbId: activeQueryEditor && activeQueryEditor.dbId ? activeQueryEditor.dbId - : (this.props.defaultDbId || firstDbId), + : this.props.defaultDbId || firstDbId, schema: activeQueryEditor ? activeQueryEditor.schema : null, autorun: false, sql: `${warning}SELECT ...`, @@ -206,7 +235,10 @@ class TabbedSqlEditors extends React.PureComponent { const qeid = this.props.tabHistory[this.props.tabHistory.length - 1]; if (key !== qeid) { const queryEditor = this.props.queryEditors.find(qe => qe.id === key); - this.props.actions.switchQueryEditor(queryEditor, this.props.displayLimit); + this.props.actions.switchQueryEditor( + queryEditor, + this.props.displayLimit, + ); } } } @@ -214,8 +246,9 @@ class TabbedSqlEditors extends React.PureComponent { this.props.actions.removeQueryEditor(qe); } removeAllOtherQueryEditors(cqe) { - this.props.queryEditors - .forEach(qe => qe !== cqe && this.removeQueryEditor(qe)); + this.props.queryEditors.forEach( + qe => qe !== cqe && this.removeQueryEditor(qe), + ); } duplicateQueryEditor(qe) { this.props.actions.cloneQueryToNewTab(qe, false); @@ -225,7 +258,8 @@ class TabbedSqlEditors extends React.PureComponent { } render() { const editors = this.props.queryEditors.map((qe, i) => { - const isSelected = this.activeQueryEditor() && this.activeQueryEditor().id === qe.id; + const isSelected = + this.activeQueryEditor() && this.activeQueryEditor().id === qe.id; let latestQuery; if (qe.latestQueryId) { @@ -239,7 +273,10 @@ class TabbedSqlEditors extends React.PureComponent { const title = ( <> - this.removeQueryEditor(qe)} tabState={state} />{' '} + this.removeQueryEditor(qe)} + tabState={state} + />{' '} {qe.title}{' '} ); @@ -268,7 +305,10 @@ class TabbedSqlEditors extends React.PureComponent {
{this.state.hideLeftBar ? t('Expand tool bar') : t('Hide tool bar')} - this.removeAllOtherQueryEditors(qe)}> + this.removeAllOtherQueryEditors(qe)} + >
@@ -286,7 +326,9 @@ class TabbedSqlEditors extends React.PureComponent { {isSelected && ( xt.queryEditorId === qe.id)} + tables={this.props.tables.filter( + xt => xt.queryEditorId === qe.id, + )} queryEditor={qe} editorQueries={this.state.queriesArray} dataPreviewQueries={this.state.dataPreviewQueries} @@ -317,7 +359,8 @@ class TabbedSqlEditors extends React.PureComponent { -   + +  
} className="addEditorTab" @@ -355,7 +398,4 @@ function mapDispatchToProps(dispatch) { export { TabbedSqlEditors }; -export default connect( - mapStateToProps, - mapDispatchToProps, -)(TabbedSqlEditors); +export default connect(mapStateToProps, mapDispatchToProps)(TabbedSqlEditors); diff --git a/superset/assets/src/SqlLab/components/TableElement.jsx b/superset/assets/src/SqlLab/components/TableElement.jsx index a1ba5a567ab..c8e96a9960c 100644 --- a/superset/assets/src/SqlLab/components/TableElement.jsx +++ b/superset/assets/src/SqlLab/components/TableElement.jsx @@ -32,7 +32,7 @@ import Loading from '../../components/Loading'; const propTypes = { table: PropTypes.object, actions: PropTypes.object, - timeout: PropTypes.number, // used for tests + timeout: PropTypes.number, // used for tests }; const defaultProps = { @@ -119,7 +119,8 @@ class TableElement extends React.PureComponent {
{t('latest partition:')} {latest} - {partitionClipBoard} + {' '} + {partitionClipBoard}
); @@ -155,31 +156,31 @@ class TableElement extends React.PureComponent { - {table.selectStar && + {table.selectStar && ( - } + copyNode={} text={table.selectStar} shouldShowText={false} tooltipText={t('Copy SELECT statement to the clipboard')} /> - } - {table.view && + )} + {table.view && ( - } + )} { this.toggleTable(e); }} + onClick={e => { + this.toggleTable(e); + }} > - - {table.name} - + {table.name}
- {table.isMetadataLoading || table.isExtraMetadataLoading ? - - : - - {this.renderControls()} - - } + {table.isMetadataLoading || table.isExtraMetadataLoading ? ( + + ) : ( + {this.renderControls()} + )} { this.toggleTable(e); }} - className={( + onClick={e => { + this.toggleTable(e); + }} + className={ 'text-primary pointer m-l-10 ' + 'fa fa-lg ' + `fa-angle-${table.expanded ? 'up' : 'down'}` - )} + } />
@@ -247,16 +244,12 @@ class TableElement extends React.PureComponent { } } const metadata = ( - +
{this.renderWell()}
- {cols && cols.map(col => ( - - ))} + {cols && + cols.map(col => )}
@@ -277,9 +270,7 @@ class TableElement extends React.PureComponent { onMouseLeave={() => this.setHover(false)} > {this.renderHeader()} -
- {this.renderBody()} -
+
{this.renderBody()}
); diff --git a/superset/assets/src/SqlLab/components/TemplateParamsEditor.jsx b/superset/assets/src/SqlLab/components/TemplateParamsEditor.jsx index 07746902f57..c907ce26a15 100644 --- a/superset/assets/src/SqlLab/components/TemplateParamsEditor.jsx +++ b/superset/assets/src/SqlLab/components/TemplateParamsEditor.jsx @@ -91,7 +91,8 @@ export default class TemplateParamsEditor extends React.Component { rel="noopener noreferrer" > Jinja templating - syntax. + {' '} + syntax.

); } @@ -115,26 +116,24 @@ export default class TemplateParamsEditor extends React.Component { ); } render() { - const paramCount = this.state.parsedJSON ? Object.keys(this.state.parsedJSON).length : 0; + const paramCount = this.state.parsedJSON + ? Object.keys(this.state.parsedJSON).length + : 0; return ( + } modalBody={this.renderModalBody(true)} diff --git a/superset/assets/src/SqlLab/index.jsx b/superset/assets/src/SqlLab/index.jsx index f2547e79a30..c257009e64f 100644 --- a/superset/assets/src/SqlLab/index.jsx +++ b/superset/assets/src/SqlLab/index.jsx @@ -20,7 +20,4 @@ import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; -ReactDOM.render( - , - document.getElementById('app'), -); +ReactDOM.render(, document.getElementById('app')); diff --git a/superset/assets/src/SqlLab/main.less b/superset/assets/src/SqlLab/main.less index e06cb333bd5..6ebfcba8a61 100644 --- a/superset/assets/src/SqlLab/main.less +++ b/superset/assets/src/SqlLab/main.less @@ -16,8 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -@import "../../stylesheets/less/cosmo/variables.less"; -@import "../../stylesheets/less/variables.less"; +@import '../../stylesheets/less/cosmo/variables.less'; +@import '../../stylesheets/less/variables.less'; body { overflow: hidden; diff --git a/superset/assets/src/SqlLab/reducers/getInitialState.js b/superset/assets/src/SqlLab/reducers/getInitialState.js index 220d5981375..dbefed7db84 100644 --- a/superset/assets/src/SqlLab/reducers/getInitialState.js +++ b/superset/assets/src/SqlLab/reducers/getInitialState.js @@ -65,7 +65,9 @@ export default function getInitialState({ defaultDbId, ...restBootstrapData }) { title: activeTab.label, sql: activeTab.sql, selectedText: null, - latestQueryId: activeTab.latest_query ? activeTab.latest_query.id : null, + latestQueryId: activeTab.latest_query + ? activeTab.latest_query.id + : null, autorun: activeTab.autorun, templateParams: activeTab.template_params, dbId: activeTab.database_id, @@ -93,7 +95,7 @@ export default function getInitialState({ defaultDbId, ...restBootstrapData }) { const tables = []; if (activeTab) { - activeTab.table_schemas.forEach((tableSchema) => { + activeTab.table_schemas.forEach(tableSchema => { const { columns, selectStar, @@ -128,7 +130,10 @@ export default function getInitialState({ defaultDbId, ...restBootstrapData }) { * hasn't used SQL Lab after it has been turned on, the state will be stored * in the browser's local storage. */ - if (localStorage.getItem('redux') && JSON.parse(localStorage.getItem('redux')).sqlLab) { + if ( + localStorage.getItem('redux') && + JSON.parse(localStorage.getItem('redux')).sqlLab + ) { const sqlLab = JSON.parse(localStorage.getItem('redux')).sqlLab; if (sqlLab.queryEditors.length === 0) { @@ -137,13 +142,17 @@ export default function getInitialState({ defaultDbId, ...restBootstrapData }) { } else { // add query editors and tables to state with a special flag so they can // be migrated if the `SQLLAB_BACKEND_PERSISTENCE` feature flag is on - sqlLab.queryEditors.forEach(qe => queryEditors.push({ - ...qe, - inLocalStorage: true, - loaded: true, - })); - sqlLab.tables.forEach(table => tables.push({ ...table, inLocalStorage: true })); - Object.values(sqlLab.queries).forEach((query) => { + sqlLab.queryEditors.forEach(qe => + queryEditors.push({ + ...qe, + inLocalStorage: true, + loaded: true, + }), + ); + sqlLab.tables.forEach(table => + tables.push({ ...table, inLocalStorage: true }), + ); + Object.values(sqlLab.queries).forEach(query => { queries[query.id] = { ...query, inLocalStorage: true }; }); tabHistory.push(...sqlLab.tabHistory); diff --git a/superset/assets/src/SqlLab/reducers/sqlLab.js b/superset/assets/src/SqlLab/reducers/sqlLab.js index 03547b36f6a..d751917e9f4 100644 --- a/superset/assets/src/SqlLab/reducers/sqlLab.js +++ b/superset/assets/src/SqlLab/reducers/sqlLab.js @@ -42,16 +42,28 @@ export default function sqlLabReducer(state = {}, action) { [actions.QUERY_EDITOR_SAVED]() { const { query, result } = action; const existing = state.queryEditors.find(qe => qe.id === query.id); - return alterInArr(state, 'queryEditors', existing, { - remoteId: result.remoteId, - title: query.title, - }, 'id'); + return alterInArr( + state, + 'queryEditors', + existing, + { + remoteId: result.remoteId, + title: query.title, + }, + 'id', + ); }, [actions.UPDATE_QUERY_EDITOR]() { const id = action.alterations.remoteId; const existing = state.queryEditors.find(qe => qe.remoteId === id); if (existing == null) return state; - return alterInArr(state, 'queryEditors', existing, action.alterations, 'remoteId'); + return alterInArr( + state, + 'queryEditors', + existing, + action.alterations, + 'remoteId', + ); }, [actions.CLONE_QUERY_TO_NEW_TAB]() { const progenitor = state.queryEditors.find( @@ -75,7 +87,7 @@ export default function sqlLabReducer(state = {}, action) { const qeIds = newState.queryEditors.map(qe => qe.id); const queries = {}; - Object.keys(state.queries).forEach((k) => { + Object.keys(state.queries).forEach(k => { const query = state.queries[k]; if (qeIds.indexOf(query.sqlEditorId) > -1) { queries[k] = query; @@ -86,7 +98,9 @@ export default function sqlLabReducer(state = {}, action) { tabHistory = tabHistory.filter(id => qeIds.indexOf(id) > -1); // Remove associated table schemas - const tables = state.tables.filter(table => table.queryEditorId !== action.queryEditor.id); + const tables = state.tables.filter( + table => table.queryEditorId !== action.queryEditor.id, + ); newState = Object.assign({}, newState, { tabHistory, tables, queries }); return newState; @@ -102,7 +116,7 @@ export default function sqlLabReducer(state = {}, action) { [actions.MERGE_TABLE]() { const at = Object.assign({}, action.table); let existingTable; - state.tables.forEach((xt) => { + state.tables.forEach(xt => { if ( xt.dbId === at.dbId && xt.queryEditorId === at.queryEditorId && @@ -122,7 +136,9 @@ export default function sqlLabReducer(state = {}, action) { at.dataPreviewQueryId = null; let newState = addToArr(state, 'tables', at); if (action.query) { - newState = alterInArr(newState, 'tables', at, { dataPreviewQueryId: action.query.id }); + newState = alterInArr(newState, 'tables', at, { + dataPreviewQueryId: action.query.id, + }); } return newState; }, @@ -132,7 +148,9 @@ export default function sqlLabReducer(state = {}, action) { [actions.REMOVE_DATA_PREVIEW]() { const queries = Object.assign({}, state.queries); delete queries[action.table.dataPreviewQueryId]; - const newState = alterInArr(state, 'tables', action.table, { dataPreviewQueryId: null }); + const newState = alterInArr(state, 'tables', action.table, { + dataPreviewQueryId: null, + }); return Object.assign({}, newState, { queries }); }, [actions.CHANGE_DATA_PREVIEW_ID]() { @@ -140,9 +158,11 @@ export default function sqlLabReducer(state = {}, action) { delete queries[action.oldQueryId]; const newTables = []; - state.tables.forEach((xt) => { + state.tables.forEach(xt => { if (xt.dataPreviewQueryId === action.oldQueryId) { - newTables.push(Object.assign({}, xt, { dataPreviewQueryId: action.newQuery.id })); + newTables.push( + Object.assign({}, xt, { dataPreviewQueryId: action.newQuery.id }), + ); } else { newTables.push(xt); } @@ -213,12 +233,14 @@ export default function sqlLabReducer(state = {}, action) { newState = alterInArr(newState, 'queryEditors', sqlEditor, { validationResult: { id: action.query.id, - errors: [{ - line_number: 1, - start_column: 1, - end_column: 1, - message: `The server failed to validate your query.\n${action.message}`, - }], + errors: [ + { + line_number: 1, + start_column: 1, + end_column: 1, + message: `The server failed to validate your query.\n${action.message}`, + }, + ], completed: true, }, }); @@ -265,11 +287,17 @@ export default function sqlLabReducer(state = {}, action) { if (action.query.sqlEditorId) { const qe = getFromArr(state.queryEditors, action.query.sqlEditorId); if (qe.latestQueryId && state.queries[qe.latestQueryId]) { - const newResults = Object.assign({}, state.queries[qe.latestQueryId].results, { - data: [], - query: null, + const newResults = Object.assign( + {}, + state.queries[qe.latestQueryId].results, + { + data: [], + query: null, + }, + ); + const q = Object.assign({}, state.queries[qe.latestQueryId], { + results: newResults, }); - const q = Object.assign({}, state.queries[qe.latestQueryId], { results: newResults }); const queries = Object.assign({}, state.queries, { [q.id]: q }); newState = Object.assign({}, state, { queries }); } @@ -278,18 +306,28 @@ export default function sqlLabReducer(state = {}, action) { } newState = addToObject(newState, 'queries', action.query); const sqlEditor = { id: action.query.sqlEditorId }; - return alterInArr(newState, 'queryEditors', sqlEditor, { latestQueryId: action.query.id }); + return alterInArr(newState, 'queryEditors', sqlEditor, { + latestQueryId: action.query.id, + }); }, [actions.STOP_QUERY]() { - return alterInObject(state, 'queries', action.query, { state: 'stopped', results: [] }); + return alterInObject(state, 'queries', action.query, { + state: 'stopped', + results: [], + }); }, [actions.CLEAR_QUERY_RESULTS]() { const newResults = Object.assign({}, action.query.results); newResults.data = []; - return alterInObject(state, 'queries', action.query, { results: newResults, cached: true }); + return alterInObject(state, 'queries', action.query, { + results: newResults, + cached: true, + }); }, [actions.REQUEST_QUERY_RESULTS]() { - return alterInObject(state, 'queries', action.query, { state: 'fetching' }); + return alterInObject(state, 'queries', action.query, { + state: 'fetching', + }); }, [actions.QUERY_SUCCESS]() { let rows; @@ -322,8 +360,8 @@ export default function sqlLabReducer(state = {}, action) { [actions.SET_ACTIVE_QUERY_EDITOR]() { const qeIds = state.queryEditors.map(qe => qe.id); if ( - (qeIds.indexOf(action.queryEditor.id) > -1) && - (state.tabHistory[state.tabHistory.length - 1] !== action.queryEditor.id) + qeIds.indexOf(action.queryEditor.id) > -1 && + state.tabHistory[state.tabHistory.length - 1] !== action.queryEditor.id ) { const tabHistory = state.tabHistory.slice(); tabHistory.push(action.queryEditor.id); @@ -332,7 +370,9 @@ export default function sqlLabReducer(state = {}, action) { return state; }, [actions.LOAD_QUERY_EDITOR]() { - return alterInArr(state, 'queryEditors', action.queryEditor, { ...action.queryEditor }); + return alterInArr(state, 'queryEditors', action.queryEditor, { + ...action.queryEditor, + }); }, [actions.SET_TABLES]() { return extendArr(state, 'tables', action.tables); @@ -343,16 +383,14 @@ export default function sqlLabReducer(state = {}, action) { [actions.MIGRATE_QUERY_EDITOR]() { // remove migrated query editor from localStorage const sqlLab = JSON.parse(localStorage.getItem('redux')).sqlLab; - sqlLab.queryEditors = sqlLab.queryEditors.filter(qe => qe.id !== action.oldQueryEditor.id); + sqlLab.queryEditors = sqlLab.queryEditors.filter( + qe => qe.id !== action.oldQueryEditor.id, + ); localStorage.setItem('redux', JSON.stringify({ sqlLab })); // replace localStorage query editor with the server backed one return addToArr( - removeFromArr( - state, - 'queryEditors', - action.oldQueryEditor, - ), + removeFromArr(state, 'queryEditors', action.oldQueryEditor), 'queryEditors', action.newQueryEditor, ); @@ -360,16 +398,14 @@ export default function sqlLabReducer(state = {}, action) { [actions.MIGRATE_TABLE]() { // remove migrated table from localStorage const sqlLab = JSON.parse(localStorage.getItem('redux')).sqlLab; - sqlLab.tables = sqlLab.tables.filter(table => table.id !== action.oldTable.id); + sqlLab.tables = sqlLab.tables.filter( + table => table.id !== action.oldTable.id, + ); localStorage.setItem('redux', JSON.stringify({ sqlLab })); // replace localStorage table with the server backed one return addToArr( - removeFromArr( - state, - 'tables', - action.oldTable, - ), + removeFromArr(state, 'tables', action.oldTable), 'tables', action.newTable, ); @@ -377,9 +413,13 @@ export default function sqlLabReducer(state = {}, action) { [actions.MIGRATE_TAB_HISTORY]() { // remove migrated tab from localStorage tabHistory const sqlLab = JSON.parse(localStorage.getItem('redux')).sqlLab; - sqlLab.tabHistory = sqlLab.tabHistory.filter(tabId => tabId !== action.oldId); + sqlLab.tabHistory = sqlLab.tabHistory.filter( + tabId => tabId !== action.oldId, + ); localStorage.setItem('redux', JSON.stringify({ sqlLab })); - const tabHistory = state.tabHistory.filter(tabId => tabId !== action.oldId); + const tabHistory = state.tabHistory.filter( + tabId => tabId !== action.oldId, + ); tabHistory.push(action.newId); return Object.assign({}, state, { tabHistory }); }, @@ -393,25 +433,39 @@ export default function sqlLabReducer(state = {}, action) { return Object.assign({}, state, { queries }); }, [actions.QUERY_EDITOR_SETDB]() { - return alterInArr(state, 'queryEditors', action.queryEditor, { dbId: action.dbId }); + return alterInArr(state, 'queryEditors', action.queryEditor, { + dbId: action.dbId, + }); }, [actions.QUERY_EDITOR_SET_SCHEMA]() { - return alterInArr(state, 'queryEditors', action.queryEditor, { schema: action.schema }); + return alterInArr(state, 'queryEditors', action.queryEditor, { + schema: action.schema, + }); }, [actions.QUERY_EDITOR_SET_SCHEMA_OPTIONS]() { - return alterInArr(state, 'queryEditors', action.queryEditor, { schemaOptions: action.options }); + return alterInArr(state, 'queryEditors', action.queryEditor, { + schemaOptions: action.options, + }); }, [actions.QUERY_EDITOR_SET_TABLE_OPTIONS]() { - return alterInArr(state, 'queryEditors', action.queryEditor, { tableOptions: action.options }); + return alterInArr(state, 'queryEditors', action.queryEditor, { + tableOptions: action.options, + }); }, [actions.QUERY_EDITOR_SET_TITLE]() { - return alterInArr(state, 'queryEditors', action.queryEditor, { title: action.title }); + return alterInArr(state, 'queryEditors', action.queryEditor, { + title: action.title, + }); }, [actions.QUERY_EDITOR_SET_SQL]() { - return alterInArr(state, 'queryEditors', action.queryEditor, { sql: action.sql }); + return alterInArr(state, 'queryEditors', action.queryEditor, { + sql: action.sql, + }); }, [actions.QUERY_EDITOR_SET_QUERY_LIMIT]() { - return alterInArr(state, 'queryEditors', action.queryEditor, { queryLimit: action.queryLimit }); + return alterInArr(state, 'queryEditors', action.queryEditor, { + queryLimit: action.queryLimit, + }); }, [actions.QUERY_EDITOR_SET_TEMPLATE_PARAMS]() { return alterInArr(state, 'queryEditors', action.queryEditor, { @@ -419,10 +473,14 @@ export default function sqlLabReducer(state = {}, action) { }); }, [actions.QUERY_EDITOR_SET_SELECTED_TEXT]() { - return alterInArr(state, 'queryEditors', action.queryEditor, { selectedText: action.sql }); + return alterInArr(state, 'queryEditors', action.queryEditor, { + selectedText: action.sql, + }); }, [actions.QUERY_EDITOR_SET_AUTORUN]() { - return alterInArr(state, 'queryEditors', action.queryEditor, { autorun: action.autorun }); + return alterInArr(state, 'queryEditors', action.queryEditor, { + autorun: action.autorun, + }); }, [actions.QUERY_EDITOR_PERSIST_HEIGHT]() { return alterInArr(state, 'queryEditors', action.queryEditor, { @@ -432,7 +490,7 @@ export default function sqlLabReducer(state = {}, action) { }, [actions.SET_DATABASES]() { const databases = {}; - action.databases.forEach((db) => { + action.databases.forEach(db => { databases[db.id] = db; }); return Object.assign({}, state, { databases }); @@ -445,8 +503,9 @@ export default function sqlLabReducer(state = {}, action) { for (const id in action.alteredQueries) { const changedQuery = action.alteredQueries[id]; if ( - !state.queries.hasOwnProperty(id) - || (state.queries[id].state !== 'stopped' && state.queries[id].state !== 'failed') + !state.queries.hasOwnProperty(id) || + (state.queries[id].state !== 'stopped' && + state.queries[id].state !== 'failed') ) { if (changedQuery.changedOn > queriesLastUpdate) { queriesLastUpdate = changedQuery.changedOn; @@ -458,7 +517,10 @@ export default function sqlLabReducer(state = {}, action) { if (!change) { newQueries = state.queries; } - return Object.assign({}, state, { queries: newQueries, queriesLastUpdate }); + return Object.assign({}, state, { + queries: newQueries, + queriesLastUpdate, + }); }, [actions.SET_USER_OFFLINE]() { return Object.assign({}, state, { offline: action.offline }); diff --git a/superset/assets/src/SqlLab/utils/reduxStateToLocalStorageHelper.js b/superset/assets/src/SqlLab/utils/reduxStateToLocalStorageHelper.js index d0a8f5f2fff..18d03d2102f 100644 --- a/superset/assets/src/SqlLab/utils/reduxStateToLocalStorageHelper.js +++ b/superset/assets/src/SqlLab/utils/reduxStateToLocalStorageHelper.js @@ -36,32 +36,33 @@ const PERSISTENT_QUERY_EDITOR_KEYS = new Set([ ]); export function emptyQueryResults(queries) { - return Object.keys(queries) - .reduce((accu, key) => { - const { startDttm, results } = queries[key]; - const query = { - ...queries[key], - results: Date.now() - startDttm > LOCALSTORAGE_MAX_QUERY_AGE_MS ? - {} : results, - }; + return Object.keys(queries).reduce((accu, key) => { + const { startDttm, results } = queries[key]; + const query = { + ...queries[key], + results: + Date.now() - startDttm > LOCALSTORAGE_MAX_QUERY_AGE_MS ? {} : results, + }; - const updatedQueries = { - ...accu, - [key]: query, - }; - return updatedQueries; - }, {}); + const updatedQueries = { + ...accu, + [key]: query, + }; + return updatedQueries; + }, {}); } export function clearQueryEditors(queryEditors) { - return queryEditors - .map(editor => + return queryEditors.map(editor => // only return selected keys Object.keys(editor) .filter(key => PERSISTENT_QUERY_EDITOR_KEYS.has(key)) - .reduce((accumulator, key) => ({ - ...accumulator, - [key]: editor[key], - }), {}), - ); + .reduce( + (accumulator, key) => ({ + ...accumulator, + [key]: editor[key], + }), + {}, + ), + ); } diff --git a/superset/assets/src/addSlice/AddSliceContainer.jsx b/superset/assets/src/addSlice/AddSliceContainer.jsx index 71dda49c28c..106d33af9ad 100644 --- a/superset/assets/src/addSlice/AddSliceContainer.jsx +++ b/superset/assets/src/addSlice/AddSliceContainer.jsx @@ -25,10 +25,12 @@ import { t } from '@superset-ui/translation'; import VizTypeControl from '../explore/components/controls/VizTypeControl'; const propTypes = { - datasources: PropTypes.arrayOf(PropTypes.shape({ - label: PropTypes.string.isRequired, - value: PropTypes.string.isRequired, - })).isRequired, + datasources: PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.string.isRequired, + value: PropTypes.string.isRequired, + }), + ).isRequired, }; const styleSelectWidth = { width: 600 }; @@ -50,7 +52,8 @@ export default class AddSliceContainer extends React.PureComponent { JSON.stringify({ viz_type: this.state.visType, datasource: this.state.datasourceValue, - })); + }), + ); return `/superset/explore/?form_data=${formData}`; } @@ -96,9 +99,12 @@ export default class AddSliceContainer extends React.PureComponent {

{t( 'If the datasource you are looking for is not ' + - 'available in the list, ' + - 'follow the instructions on the how to add it on the ')} - {t('Superset tutorial')} + 'available in the list, ' + + 'follow the instructions on the how to add it on the ', + )} + + {t('Superset tutorial')} +


@@ -119,7 +125,8 @@ export default class AddSliceContainer extends React.PureComponent { > {t('Create new chart')} -

+
+
); diff --git a/superset/assets/src/addSlice/App.jsx b/superset/assets/src/addSlice/App.jsx index 195512ffdf5..ccdf719f385 100644 --- a/superset/assets/src/addSlice/App.jsx +++ b/superset/assets/src/addSlice/App.jsx @@ -26,10 +26,10 @@ setupApp(); setupPlugins(); const addSliceContainer = document.getElementById('js-add-slice-container'); -const bootstrapData = JSON.parse(addSliceContainer.getAttribute('data-bootstrap')); - -const App = () => ( - +const bootstrapData = JSON.parse( + addSliceContainer.getAttribute('data-bootstrap'), ); +const App = () => ; + export default hot(module)(App); diff --git a/superset/assets/src/addSlice/index.jsx b/superset/assets/src/addSlice/index.jsx index 4f18feedb43..09b6de2291b 100644 --- a/superset/assets/src/addSlice/index.jsx +++ b/superset/assets/src/addSlice/index.jsx @@ -20,7 +20,4 @@ import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; -ReactDOM.render( - , - document.getElementById('js-add-slice-container'), -); +ReactDOM.render(, document.getElementById('js-add-slice-container')); diff --git a/superset/assets/src/chart/Chart.jsx b/superset/assets/src/chart/Chart.jsx index a29079a2ffc..7a4679c6383 100644 --- a/superset/assets/src/chart/Chart.jsx +++ b/superset/assets/src/chart/Chart.jsx @@ -76,7 +76,9 @@ const defaultProps = { class Chart extends React.PureComponent { constructor(props) { super(props); - this.handleRenderContainerFailure = this.handleRenderContainerFailure.bind(this); + this.handleRenderContainerFailure = this.handleRenderContainerFailure.bind( + this, + ); } componentDidMount() { @@ -114,7 +116,11 @@ class Chart extends React.PureComponent { handleRenderContainerFailure(error, info) { const { actions, chartId } = this.props; console.warn(error); // eslint-disable-line - actions.chartRenderingFailed(error.toString(), chartId, info ? info.componentStack : null); + actions.chartRenderingFailed( + error.toString(), + chartId, + info ? info.componentStack : null, + ); actions.logEvent(LOG_ACTIONS_RENDER_CHART_CONTAINER, { slice_id: chartId, @@ -133,7 +139,8 @@ class Chart extends React.PureComponent { message={chartAlert} link={queryResponse ? queryResponse.link : null} stackTrace={chartStackTrace} - />); + /> + ); } render() { @@ -160,12 +167,14 @@ class Chart extends React.PureComponent { return {errorMessage}; } return ( - +
- {isLoading && } {!isLoading && !chartAlert && isFaded && ( diff --git a/superset/assets/src/chart/ChartContainer.jsx b/superset/assets/src/chart/ChartContainer.jsx index 03a4f1a74b7..f509281f3dc 100644 --- a/superset/assets/src/chart/ChartContainer.jsx +++ b/superset/assets/src/chart/ChartContainer.jsx @@ -25,10 +25,13 @@ import Chart from './Chart'; function mapDispatchToProps(dispatch) { return { - actions: bindActionCreators({ - ...actions, - logEvent, - }, dispatch), + actions: bindActionCreators( + { + ...actions, + logEvent, + }, + dispatch, + ), }; } diff --git a/superset/assets/src/chart/ChartRenderer.jsx b/superset/assets/src/chart/ChartRenderer.jsx index 03ab130df12..3ab00741a6d 100644 --- a/superset/assets/src/chart/ChartRenderer.jsx +++ b/superset/assets/src/chart/ChartRenderer.jsx @@ -93,13 +93,15 @@ class ChartRenderer extends React.Component { this.hasQueryResponseChange = nextProps.queryResponse !== this.props.queryResponse; - if (this.hasQueryResponseChange || + if ( + this.hasQueryResponseChange || nextProps.annotationData !== this.props.annotationData || nextProps.height !== this.props.height || nextProps.width !== this.props.width || nextState.tooltip !== this.state.tooltip || nextProps.triggerRender || - nextProps.formData.color_scheme !== this.props.formData.color_scheme) { + nextProps.formData.color_scheme !== this.props.formData.color_scheme + ) { return true; } } @@ -136,7 +138,11 @@ class ChartRenderer extends React.Component { handleRenderFailure(error, info) { const { actions, chartId } = this.props; console.warn(error); // eslint-disable-line - actions.chartRenderingFailed(error.toString(), chartId, info ? info.componentStack : null); + actions.chartRenderingFailed( + error.toString(), + chartId, + info ? info.componentStack : null, + ); // only trigger render log when query is changed if (this.hasQueryResponseChange) { @@ -170,12 +176,15 @@ class ChartRenderer extends React.Component { positionLeft={tooltip.x + 30} arrowOffsetTop={10} > - {typeof (tooltip.content) === 'string' ? + {typeof tooltip.content === 'string' ? (
- : tooltip.content - } + ) : ( + tooltip.content + )} ); } @@ -183,12 +192,7 @@ class ChartRenderer extends React.Component { } render() { - const { - chartAlert, - chartStatus, - vizType, - chartId, - } = this.props; + const { chartAlert, chartStatus, vizType, chartId } = this.props; // Skip chart rendering if (chartStatus === 'loading' || !!chartAlert || chartStatus === null) { diff --git a/superset/assets/src/chart/chart.less b/superset/assets/src/chart/chart.less index 18e8a105098..ce9e7c7a358 100644 --- a/superset/assets/src/chart/chart.less +++ b/superset/assets/src/chart/chart.less @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -@import "../../stylesheets/less/variables.less"; +@import '../../stylesheets/less/variables.less'; .chart-tooltip { opacity: 0.75; diff --git a/superset/assets/src/chart/chartAction.js b/superset/assets/src/chart/chartAction.js index 69a074d9c9b..9859ef4c407 100644 --- a/superset/assets/src/chart/chartAction.js +++ b/superset/assets/src/chart/chartAction.js @@ -21,8 +21,14 @@ import { t } from '@superset-ui/translation'; import { SupersetClient } from '@superset-ui/connection'; import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags'; -import { getExploreUrlAndPayload, getAnnotationJsonUrl } from '../explore/exploreUtils'; -import { requiresQuery, ANNOTATION_SOURCE_TYPES } from '../modules/AnnotationTypes'; +import { + getExploreUrlAndPayload, + getAnnotationJsonUrl, +} from '../explore/exploreUtils'; +import { + requiresQuery, + ANNOTATION_SOURCE_TYPES, +} from '../modules/AnnotationTypes'; import { addDangerToast } from '../messageToasts/actions'; import { logEvent } from '../logger/actions'; import { Logger, LOG_ACTIONS_LOAD_CHART } from '../logger/LogUtils'; @@ -31,7 +37,12 @@ import { allowCrossDomain } from '../utils/hostNamesConfig'; export const CHART_UPDATE_STARTED = 'CHART_UPDATE_STARTED'; export function chartUpdateStarted(queryController, latestQueryFormData, key) { - return { type: CHART_UPDATE_STARTED, queryController, latestQueryFormData, key }; + return { + type: CHART_UPDATE_STARTED, + queryController, + latestQueryFormData, + key, + }; } export const CHART_UPDATE_SUCCEEDED = 'CHART_UPDATE_SUCCEEDED'; @@ -84,11 +95,18 @@ export function annotationQueryFailed(annotation, queryResponse, key) { return { type: ANNOTATION_QUERY_FAILED, annotation, queryResponse, key }; } -export function runAnnotationQuery(annotation, timeout = 60, formData = null, key) { - return function (dispatch, getState) { +export function runAnnotationQuery( + annotation, + timeout = 60, + formData = null, + key, +) { + return function(dispatch, getState) { const sliceKey = key || Object.keys(getState().charts)[0]; // make a copy of formData, not modifying original formData - const fd = { ...(formData || getState().charts[sliceKey].latestQueryFormData) }; + const fd = { + ...(formData || getState().charts[sliceKey].latestQueryFormData), + }; if (!requiresQuery(annotation.sourceType)) { return Promise.resolve(); @@ -114,7 +132,9 @@ export function runAnnotationQuery(annotation, timeout = 60, formData = null, ke if (fd !== null) { const hasExtraFilters = fd.extra_filters && fd.extra_filters.length > 0; - sliceFormData.extra_filters = hasExtraFilters ? fd.extra_filters : undefined; + sliceFormData.extra_filters = hasExtraFilters + ? fd.extra_filters + : undefined; } const isNative = annotation.sourceType === ANNOTATION_SOURCE_TYPES.NATIVE; @@ -129,16 +149,25 @@ export function runAnnotationQuery(annotation, timeout = 60, formData = null, ke signal, timeout: timeout * 1000, }) - .then(({ json }) => dispatch(annotationQuerySuccess(annotation, json, sliceKey))) - .catch(response => getClientErrorObject(response).then((err) => { - if (err.statusText === 'timeout') { - dispatch(annotationQueryFailed(annotation, { error: 'Query Timeout' }, sliceKey)); - } else if ((err.error || '').toLowerCase().includes('no data')) { - dispatch(annotationQuerySuccess(annotation, err, sliceKey)); - } else if (err.statusText !== 'abort') { - dispatch(annotationQueryFailed(annotation, err, sliceKey)); - } - }), + .then(({ json }) => + dispatch(annotationQuerySuccess(annotation, json, sliceKey)), + ) + .catch(response => + getClientErrorObject(response).then(err => { + if (err.statusText === 'timeout') { + dispatch( + annotationQueryFailed( + annotation, + { error: 'Query Timeout' }, + sliceKey, + ), + ); + } else if ((err.error || '').toLowerCase().includes('no data')) { + dispatch(annotationQuerySuccess(annotation, err, sliceKey)); + } else if (err.statusText !== 'abort') { + dispatch(annotationQueryFailed(annotation, err, sliceKey)); + } + }), ); }; } @@ -171,8 +200,14 @@ export function addChart(chart, key) { return { type: ADD_CHART, chart, key }; } -export function exploreJSON(formData, force = false, timeout = 60, key, method) { - return (dispatch) => { +export function exploreJSON( + formData, + force = false, + timeout = 60, + key, + method, +) { + return dispatch => { const { url, payload } = getExploreUrlAndPayload({ formData, endpointType: 'json', @@ -200,46 +235,54 @@ export function exploreJSON(formData, force = false, timeout = 60, key, method) }; } - const clientMethod = method === 'GET' && isFeatureEnabled(FeatureFlag.CLIENT_CACHE) - ? SupersetClient.get - : SupersetClient.post; + const clientMethod = + method === 'GET' && isFeatureEnabled(FeatureFlag.CLIENT_CACHE) + ? SupersetClient.get + : SupersetClient.post; const queryPromise = clientMethod(querySettings) .then(({ json }) => { - dispatch(logEvent(LOG_ACTIONS_LOAD_CHART, { - slice_id: key, - is_cached: json.is_cached, - force_refresh: force, - row_count: json.rowcount, - datasource: formData.datasource, - start_offset: logStart, - ts: new Date().getTime(), - duration: Logger.getTimestamp() - logStart, - has_extra_filters: formData.extra_filters && formData.extra_filters.length > 0, - viz_type: formData.viz_type, - })); - return dispatch(chartUpdateSucceeded(json, key)); - }) - .catch((response) => { - const appendErrorLog = (errorDetails) => { - dispatch(logEvent(LOG_ACTIONS_LOAD_CHART, { + dispatch( + logEvent(LOG_ACTIONS_LOAD_CHART, { slice_id: key, - has_err: true, - error_details: errorDetails, + is_cached: json.is_cached, + force_refresh: force, + row_count: json.rowcount, datasource: formData.datasource, start_offset: logStart, ts: new Date().getTime(), duration: Logger.getTimestamp() - logStart, - })); + has_extra_filters: + formData.extra_filters && formData.extra_filters.length > 0, + viz_type: formData.viz_type, + }), + ); + return dispatch(chartUpdateSucceeded(json, key)); + }) + .catch(response => { + const appendErrorLog = errorDetails => { + dispatch( + logEvent(LOG_ACTIONS_LOAD_CHART, { + slice_id: key, + has_err: true, + error_details: errorDetails, + datasource: formData.datasource, + start_offset: logStart, + ts: new Date().getTime(), + duration: Logger.getTimestamp() - logStart, + }), + ); }; if (response.statusText === 'timeout') { appendErrorLog('timeout'); - return dispatch(chartUpdateTimeout(response.statusText, timeout, key)); + return dispatch( + chartUpdateTimeout(response.statusText, timeout, key), + ); } else if (response.name === 'AbortError') { appendErrorLog('abort'); return dispatch(chartUpdateStopped(key)); } - return getClientErrorObject(response).then((parsedResponse) => { + return getClientErrorObject(response).then(parsedResponse => { appendErrorLog(parsedResponse.error); return dispatch(chartUpdateFailed(parsedResponse, key)); }); @@ -251,7 +294,9 @@ export function exploreJSON(formData, force = false, timeout = 60, key, method) queryPromise, dispatch(triggerQuery(false, key)), dispatch(updateQueryFormData(payload, key)), - ...annotationLayers.map(x => dispatch(runAnnotationQuery(x, timeout, formData, key))), + ...annotationLayers.map(x => + dispatch(runAnnotationQuery(x, timeout, formData, key)), + ), ]); }; } @@ -283,8 +328,11 @@ export function postChartFormData(formData, force = false, timeout = 60, key) { } export function redirectSQLLab(formData) { - return (dispatch) => { - const { url } = getExploreUrlAndPayload({ formData, endpointType: 'query' }); + return dispatch => { + const { url } = getExploreUrlAndPayload({ + formData, + endpointType: 'query', + }); return SupersetClient.post({ url, postPayload: { form_data: formData }, @@ -299,15 +347,22 @@ export function redirectSQLLab(formData) { redirectUrl.searchParams.set('sql', json.query); window.open(redirectUrl.href, '_blank'); }) - .catch(() => dispatch(addDangerToast(t('An error occurred while loading the SQL')))); + .catch(() => + dispatch(addDangerToast(t('An error occurred while loading the SQL'))), + ); }; } export function refreshChart(chart, force, timeout) { - return (dispatch) => { - if (!chart.latestQueryFormData || Object.keys(chart.latestQueryFormData).length === 0) { + return dispatch => { + if ( + !chart.latestQueryFormData || + Object.keys(chart.latestQueryFormData).length === 0 + ) { return; } - dispatch(postChartFormData(chart.latestQueryFormData, force, timeout, chart.id)); + dispatch( + postChartFormData(chart.latestQueryFormData, force, timeout, chart.id), + ); }; } diff --git a/superset/assets/src/chart/chartReducer.js b/superset/assets/src/chart/chartReducer.js index 02df8cb28d1..8ac7f1aefdc 100644 --- a/superset/assets/src/chart/chartReducer.js +++ b/superset/assets/src/chart/chartReducer.js @@ -44,7 +44,8 @@ export default function chartReducer(charts = {}, action) { }; }, [actions.CHART_UPDATE_SUCCEEDED](state) { - return { ...state, + return { + ...state, chartStatus: 'success', queryResponse: action.queryResponse, chartAlert: null, @@ -62,45 +63,57 @@ export default function chartReducer(charts = {}, action) { }; }, [actions.CHART_UPDATE_STOPPED](state) { - return { ...state, + return { + ...state, chartStatus: 'stopped', chartAlert: t('Updating chart was stopped'), chartUpdateEndTime: now(), }; }, [actions.CHART_RENDERING_SUCCEEDED](state) { - return { ...state, - chartStatus: 'rendered', - chartUpdateEndTime: now(), - }; + return { ...state, chartStatus: 'rendered', chartUpdateEndTime: now() }; }, [actions.CHART_RENDERING_FAILED](state) { - return { ...state, + return { + ...state, chartStatus: 'failed', chartStackTrace: action.stackTrace, - chartAlert: t('An error occurred while rendering the visualization: %s', action.error), + chartAlert: t( + 'An error occurred while rendering the visualization: %s', + action.error, + ), }; }, [actions.CHART_UPDATE_TIMEOUT](state) { - return { ...state, + return { + ...state, chartStatus: 'failed', - chartAlert: ( + chartAlert: `${t('Query timeout')} - ` + - t(`visualization queries are set to timeout at ${action.timeout} seconds. `) + - t('Perhaps your data has grown, your database is under unusual load, ' + - 'or you are simply querying a data source that is too large ' + - 'to be processed within the timeout range. ' + - 'If that is the case, we recommend that you summarize your data further.')), + t( + `visualization queries are set to timeout at ${action.timeout} seconds. `, + ) + + t( + 'Perhaps your data has grown, your database is under unusual load, ' + + 'or you are simply querying a data source that is too large ' + + 'to be processed within the timeout range. ' + + 'If that is the case, we recommend that you summarize your data further.', + ), chartUpdateEndTime: now(), }; }, [actions.CHART_UPDATE_FAILED](state) { - return { ...state, + return { + ...state, chartStatus: 'failed', - chartAlert: action.queryResponse ? action.queryResponse.error : t('Network error.'), + chartAlert: action.queryResponse + ? action.queryResponse.error + : t('Network error.'), chartUpdateEndTime: now(), queryResponse: action.queryResponse, - chartStackTrace: action.queryResponse ? action.queryResponse.stacktrace : null, + chartStackTrace: action.queryResponse + ? action.queryResponse.stacktrace + : null, }; }, [actions.TRIGGER_QUERY](state) { @@ -117,8 +130,10 @@ export default function chartReducer(charts = {}, action) { return { ...state, latestQueryFormData: action.value }; }, [actions.ANNOTATION_QUERY_STARTED](state) { - if (state.annotationQuery && - state.annotationQuery[action.annotation.name]) { + if ( + state.annotationQuery && + state.annotationQuery[action.annotation.name] + ) { state.annotationQuery[action.annotation.name].abort(); } const annotationQuery = { @@ -151,8 +166,9 @@ export default function chartReducer(charts = {}, action) { delete annotationData[action.annotation.name]; const annotationError = { ...state.annotationError, - [action.annotation.name]: action.queryResponse ? - action.queryResponse.error : t('Network error.'), + [action.annotation.name]: action.queryResponse + ? action.queryResponse.error + : t('Network error.'), }; const annotationQuery = { ...state.annotationQuery }; delete annotationQuery[action.annotation.name]; diff --git a/superset/assets/src/components/AlteredSliceTag.jsx b/superset/assets/src/components/AlteredSliceTag.jsx index a2746aaacb6..5f274042f15 100644 --- a/superset/assets/src/components/AlteredSliceTag.jsx +++ b/superset/assets/src/components/AlteredSliceTag.jsx @@ -48,9 +48,7 @@ function alterForComparison(value) { return value; } - export default class AlteredSliceTag extends React.Component { - constructor(props) { super(props); const diffs = this.getDiffs(props); @@ -104,12 +102,15 @@ export default class AlteredSliceTag extends React.Component { if (!value.length) { return '[]'; } - return value.map((v) => { - const filterVal = v.comparator && v.comparator.constructor === Array ? - `[${v.comparator.join(', ')}]` : - v.comparator; - return `${v.subject} ${v.operator} ${filterVal}`; - }).join(', '); + return value + .map(v => { + const filterVal = + v.comparator && v.comparator.constructor === Array + ? `[${v.comparator.join(', ')}]` + : v.comparator; + return `${v.subject} ${v.operator} ${filterVal}`; + }) + .join(', '); } else if (controls[key] && controls[key].type === 'BoundsControl') { return `Min: ${value[0]}, Max: ${value[1]}`; } else if (controls[key] && controls[key].type === 'CollectionControl') { @@ -130,7 +131,10 @@ export default class AlteredSliceTag extends React.Component { for (const key in diffs) { rows.push( - + {this.formatValue(diffs[key].before, key)} {this.formatValue(diffs[key].after, key)} , @@ -154,10 +158,7 @@ export default class AlteredSliceTag extends React.Component { renderTriggerNode() { return ( - + - {showShortLinkButton && - } + + {showShortLinkButton && ( + + )} ); } diff --git a/superset/assets/src/components/AsyncSelect.jsx b/superset/assets/src/components/AsyncSelect.jsx index 581fe2ca112..d7de3984f38 100644 --- a/superset/assets/src/components/AsyncSelect.jsx +++ b/superset/assets/src/components/AsyncSelect.jsx @@ -75,11 +75,12 @@ class AsyncSelect extends React.PureComponent { this.onChange(options[0]); } }) - .catch(response => getClientErrorObject(response).then((error) => { - this.props.onAsyncError(error.error || error.statusText || error); - this.setState({ isLoading: false }); - }), - ); + .catch(response => + getClientErrorObject(response).then(error => { + this.props.onAsyncError(error.error || error.statusText || error); + this.setState({ isLoading: false }); + }), + ); } render() { diff --git a/superset/assets/src/components/BootstrapSliderWrapper.less b/superset/assets/src/components/BootstrapSliderWrapper.less index b103ff4da9d..e78a2dd6689 100644 --- a/superset/assets/src/components/BootstrapSliderWrapper.less +++ b/superset/assets/src/components/BootstrapSliderWrapper.less @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -@import "../../stylesheets/less/variables.less"; +@import '../../stylesheets/less/variables.less'; .BootstrapSliderWrapper .slider-selection { background: @gray-bg; diff --git a/superset/assets/src/components/Button.jsx b/superset/assets/src/components/Button.jsx index 29d93b7f827..818040fe46f 100644 --- a/superset/assets/src/components/Button.jsx +++ b/superset/assets/src/components/Button.jsx @@ -19,7 +19,11 @@ import React from 'react'; import PropTypes from 'prop-types'; import { kebabCase } from 'lodash'; -import { Button as BootstrapButton, Tooltip, OverlayTrigger } from 'react-bootstrap'; +import { + Button as BootstrapButton, + Tooltip, + OverlayTrigger, +} from 'react-bootstrap'; const propTypes = { tooltip: PropTypes.node, @@ -40,9 +44,7 @@ export default function Button(props) { delete buttonProps.placement; let button = ( - - {props.children} - + {props.children} ); if (tooltip) { if (props.disabled) { @@ -51,15 +53,16 @@ export default function Button(props) { buttonProps.style = { pointerEvents: 'none' }; button = (
- - {props.children} - -
); + {props.children} +
+ ); } return ( {tooltip}} + overlay={ + {tooltip} + } > {button} diff --git a/superset/assets/src/components/CachedLabel.jsx b/superset/assets/src/components/CachedLabel.jsx index 8c95b8c4b41..2faf9af507e 100644 --- a/superset/assets/src/components/CachedLabel.jsx +++ b/superset/assets/src/components/CachedLabel.jsx @@ -43,8 +43,10 @@ class CacheLabel extends React.PureComponent { {t('Loaded data cached')} {moment.utc(this.props.cachedTimestamp).fromNow()} - ) : - t('Loaded from cache'); + + ) : ( + t('Loaded from cache') + ); const tooltipContent = ( @@ -66,10 +68,7 @@ class CacheLabel extends React.PureComponent { render() { const labelStyle = this.state.hovered ? 'primary' : 'default'; return ( - + - ); + + ); } } CacheLabel.propTypes = propTypes; diff --git a/superset/assets/src/components/ChartIcon.jsx b/superset/assets/src/components/ChartIcon.jsx index d25e4536e39..5fdec9f6ed8 100644 --- a/superset/assets/src/components/ChartIcon.jsx +++ b/superset/assets/src/components/ChartIcon.jsx @@ -19,8 +19,22 @@ import React from 'react'; const ChartIcon = () => ( - - + + diff --git a/superset/assets/src/components/Checkbox.jsx b/superset/assets/src/components/Checkbox.jsx index 0793c7ce91b..d068488f65e 100644 --- a/superset/assets/src/components/Checkbox.jsx +++ b/superset/assets/src/components/Checkbox.jsx @@ -29,7 +29,9 @@ export default function Checkbox({ checked, onChange, style }) { return ( { onChange(!checked); }} @@ -39,6 +41,7 @@ export default function Checkbox({ checked, onChange, style }) { cursor: 'pointer', }} /> - ); + + ); } Checkbox.propTypes = propTypes; diff --git a/superset/assets/src/components/CheckboxIcons.jsx b/superset/assets/src/components/CheckboxIcons.jsx index f26e75255a8..2c94c863fb0 100644 --- a/superset/assets/src/components/CheckboxIcons.jsx +++ b/superset/assets/src/components/CheckboxIcons.jsx @@ -19,22 +19,49 @@ import React from 'react'; export const CheckboxChecked = () => ( - - + + ); export const CheckboxHalfChecked = () => ( - - + + ); export const CheckboxUnchecked = () => ( - - + + ); diff --git a/superset/assets/src/components/ColumnOption.jsx b/superset/assets/src/components/ColumnOption.jsx index 47f731daf08..6196006747b 100644 --- a/superset/assets/src/components/ColumnOption.jsx +++ b/superset/assets/src/components/ColumnOption.jsx @@ -31,7 +31,8 @@ const defaultProps = { }; export default function ColumnOption({ column, showType }) { - const hasExpression = column.expression && column.expression !== column.column_name; + const hasExpression = + column.expression && column.expression !== column.column_name; let columnType = column.type; if (column.is_dttm) { @@ -46,23 +47,24 @@ export default function ColumnOption({ column, showType }) { {column.verbose_name || column.column_name} - {column.description && + {column.description && ( - } - {hasExpression && + )} + {hasExpression && ( - } - ); + )} + + ); } ColumnOption.propTypes = propTypes; ColumnOption.defaultProps = defaultProps; diff --git a/superset/assets/src/components/ColumnTypeLabel.jsx b/superset/assets/src/components/ColumnTypeLabel.jsx index 090dbd89b50..f378fccff77 100644 --- a/superset/assets/src/components/ColumnTypeLabel.jsx +++ b/superset/assets/src/components/ColumnTypeLabel.jsx @@ -31,9 +31,18 @@ export default function ColumnTypeLabel({ type }) { stringIcon = 'Æ’'; } else if (type === 'aggregate') { stringIcon = 'AGG'; - } else if (type.match(/.*char.*/i) || type.match(/string.*/i) || type.match(/.*text.*/i)) { + } else if ( + type.match(/.*char.*/i) || + type.match(/string.*/i) || + type.match(/.*text.*/i) + ) { stringIcon = 'ABC'; - } else if (type.match(/.*int.*/i) || type === 'LONG' || type === 'DOUBLE' || type === 'FLOAT') { + } else if ( + type.match(/.*int.*/i) || + type === 'LONG' || + type === 'DOUBLE' || + type === 'FLOAT' + ) { stringIcon = '#'; } else if (type.match(/.*bool.*/i)) { stringIcon = 'T/F'; @@ -43,13 +52,13 @@ export default function ColumnTypeLabel({ type }) { stringIcon = '?'; } - const typeIcon = stringIcon === 'time' ? - : -
{stringIcon}
; + const typeIcon = + stringIcon === 'time' ? ( + + ) : ( +
{stringIcon}
+ ); - return ( - - {typeIcon} - ); + return {typeIcon}; } ColumnTypeLabel.propTypes = propTypes; diff --git a/superset/assets/src/components/CopyToClipboard.jsx b/superset/assets/src/components/CopyToClipboard.jsx index bd9a07bfa0c..9061c2929f5 100644 --- a/superset/assets/src/components/CopyToClipboard.jsx +++ b/superset/assets/src/components/CopyToClipboard.jsx @@ -62,7 +62,9 @@ export default class CopyToClipboard extends React.Component { onClick() { if (this.props.getText) { - this.props.getText((d) => { this.copyToClipboard(d); }); + this.props.getText(d => { + this.copyToClipboard(d); + }); } else { this.copyToClipboard(this.props.text); } @@ -135,7 +137,9 @@ export default class CopyToClipboard extends React.Component { return ( {this.props.shouldShowText && this.props.text && ( - {this.props.text} + + {this.props.text} + )} + - + {this.props.copyNode} @@ -169,9 +174,7 @@ export default class CopyToClipboard extends React.Component { renderTooltip() { return ( - - {this.tooltipText()} - + {this.tooltipText()} ); } diff --git a/superset/assets/src/components/EditableTitle.jsx b/superset/assets/src/components/EditableTitle.jsx index fc6d47615f8..63c05cdd306 100644 --- a/superset/assets/src/components/EditableTitle.jsx +++ b/superset/assets/src/components/EditableTitle.jsx @@ -31,7 +31,10 @@ const propTypes = { showTooltip: PropTypes.bool, emptyText: PropTypes.node, style: PropTypes.object, - extraClasses: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.string]), + extraClasses: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.string), + PropTypes.string, + ]), }; const defaultProps = { title: t('Title'), @@ -77,8 +80,9 @@ export default class EditableTitle extends React.PureComponent { // For multi-line values, save the actual rendered size of the displayed text. // Later, if a textarea is constructed for editing the value, we'll need this. - const contentBoundingRect = (this.contentRef.current) ? - this.contentRef.current.getBoundingClientRect() : null; + const contentBoundingRect = this.contentRef.current + ? this.contentRef.current.getBoundingClientRect() + : null; this.setState({ isEditing: true, contentBoundingRect }); } @@ -144,8 +148,15 @@ export default class EditableTitle extends React.PureComponent { render() { const { isEditing, title, contentBoundingRect } = this.state; - const { emptyText, multiLine, showTooltip, canEdit, - noPermitTooltip, style, extraClasses } = this.props; + const { + emptyText, + multiLine, + showTooltip, + canEdit, + noPermitTooltip, + style, + extraClasses, + } = this.props; let value; if (title) { @@ -156,43 +167,51 @@ export default class EditableTitle extends React.PureComponent { // Construct an inline style based on previously-saved height of the rendered label. Only // used in multi-line contexts. - const editStyle = (isEditing && contentBoundingRect) ? { height: `${contentBoundingRect.height}px` } : null; + const editStyle = + isEditing && contentBoundingRect + ? { height: `${contentBoundingRect.height}px` } + : null; // Create a textarea when we're editing a multi-line value, otherwise create an input (which may // be text or a button). - let input = multiLine && isEditing ? ( -