Skip to content

Commit

Permalink
feat: SIP-34 explore save modal (apache#10355)
Browse files Browse the repository at this point in the history
* feat: SIP-34 explore save modal

* using a const for the session storage key

* backend changes

* minor tweaks

* more tweaks

* radio cosmetics

* styles

* fix tests

* CreatableSelect\!

* Fix cypress & lint

* fix unit

* lint
  • Loading branch information
mistercrunch authored and auxten committed Nov 20, 2020
1 parent 88c2f44 commit a1604e3
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 235 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,14 @@
// Tests for links in the explore UI
// ***********************************************

import rison from 'rison';
import shortid from 'shortid';
import { HEALTH_POP_FORM_DATA_DEFAULTS } from './visualizations/shared.helper';

const apiURL = (endpoint, queryObject) => {
return `${endpoint}?q=${rison.encode(queryObject)}`;
};

describe('Test explore links', () => {
beforeEach(() => {
cy.login();
Expand Down Expand Up @@ -73,98 +79,120 @@ describe('Test explore links', () => {
});
});

xit('Test chart save as', () => {
it('Test chart save as AND overwrite', () => {
const formData = {
...HEALTH_POP_FORM_DATA_DEFAULTS,
viz_type: 'table',
metrics: ['sum__SP_POP_TOTL'],
groupby: ['country_name'],
};
const newChartName = 'Test chart';
const newChartName = `Test chart [${shortid.generate()}]`;

cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@postJson' });
cy.url().then(url => {
cy.get('button[data-target="#save_modal"]').click();
cy.get('.modal-content').within(() => {
cy.get('#saveas-radio').check();
cy.get('input[name=new_slice_name]').type(newChartName);
cy.get('button#btn_modal_save').click();
});
cy.url().should('eq', url);

cy.visitChartByName(newChartName);
cy.verifySliceSuccess({ waitAlias: '@postJson' });
});
});

xit('Test chart save', () => {
const chartName = 'Test chart';
cy.visitChartByName(chartName);
cy.verifySliceSuccess({ waitAlias: '@postJson' });
cy.visitChartByName(newChartName);

cy.get('[data-test=groupby]').within(() => {
cy.get('.Select__clear-indicator').click();
});
cy.get('button[data-target="#save_modal"]').click();
cy.get('.modal-content').within(() => {
cy.get('button#btn_modal_save').click();
// Overwriting!
cy.get('button[data-target="#save_modal"]').click();
cy.get('.modal-content').within(() => {
cy.get('#overwrite-radio').check();
cy.get('button#btn_modal_save').click();
});
cy.verifySliceSuccess({ waitAlias: '@postJson' });
const query = {
filters: [
{
col: 'slice_name',
opr: 'eq',
value: newChartName,
},
],
};
cy.request(apiURL('/api/v1/chart/', query)).then(response => {
expect(response.body.count).equals(1);
cy.request('DELETE', `/api/v1/chart/${response.body.ids[0]}`);
});
});
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]}`);
},
);
});

it('Test chart save as and add to new dashboard', () => {
cy.visitChartByName('Growth Rate');
const chartName = 'Growth Rate';
const newChartName = `${chartName} [${shortid.generate()}]`;
const dashboardTitle = `Test dashboard [${shortid.generate()}]`;

cy.visitChartByName(chartName);
cy.verifySliceSuccess({ waitAlias: '@postJson' });

const dashboardTitle = 'Test dashboard';
cy.get('button[data-target="#save_modal"]').click();
cy.get('.modal-content').within(() => {
cy.get('input[name=new_slice_name]').type('New Growth Rate');
cy.get('input[data-test=add-to-new-dashboard]').check();
cy.get('input[placeholder="[dashboard name]"]').type(dashboardTitle);
cy.get('#saveas-radio').check();
cy.get('input[name=new_slice_name]').click().clear().type(newChartName);
// Add a new option using the "CreatableSelect" feature
cy.get('#dashboard-creatable-select').type(
`${dashboardTitle}{enter}{enter}`,
);
cy.get('button#btn_modal_save').click();
});
cy.verifySliceSuccess({ waitAlias: '@postJson' });
cy.request(
`/dashboard/api/read?_flt_3_dashboard_title=${dashboardTitle}`,
).then(response => {
expect(response.body.pks[0]).not.equals(null);
let query = {
filters: [
{
col: 'dashboard_title',
opr: 'eq',
value: dashboardTitle,
},
],
};
cy.request(apiURL('/api/v1/dashboard/', query)).then(response => {
expect(response.body.count).equals(1);
});
});

it('Test chart save as and add to existing dashboard', () => {
cy.visitChartByName('Most Populated Countries');
cy.visitChartByName(newChartName);
cy.verifySliceSuccess({ waitAlias: '@postJson' });
const chartName = 'New Most Populated Countries';
const dashboardTitle = 'Test dashboard';

cy.get('button[data-target="#save_modal"]').click();
cy.get('.modal-content').within(() => {
cy.get('input[name=new_slice_name]').type(chartName);
cy.get('input[data-test=add-to-existing-dashboard]').check();
cy.get('.save-modal-selector')
.click()
.within(() => {
cy.get('input').type(dashboardTitle);
cy.get('.Select__option--is-focused').trigger('mousedown');
});
cy.get('#overwrite-radio').check();
cy.get('input[name=new_slice_name]').click().clear().type(newChartName);
// This time around, typing the same dashboard name
// will select the existing one
cy.get('#dashboard-creatable-select').type(
`${dashboardTitle}{enter}{enter}`,
);
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('DELETE', `/dashboard/api/delete/${response.body.pks[0]}`);
query = {
filters: [
{
col: 'slice_name',
opr: 'eq',
value: chartName,
},
],
};
cy.request(apiURL('/api/v1/chart/', query)).then(response => {
expect(response.body.count).equals(1);
});
query = {
filters: [
{
col: 'dashboard_title',
opr: 'eq',
value: dashboardTitle,
},
],
};
cy.request(apiURL('/api/v1/dashboard/', query)).then(response => {
expect(response.body.count).equals(1);
});
});
});
5 changes: 5 additions & 0 deletions superset-frontend/cypress-base/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions superset-frontend/cypress-base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
"author": "Apcahe",
"license": "Apache-2.0",
"dependencies": {
"shortid": "^2.2.15",
"@cypress/code-coverage": "^3.8.1"
"@cypress/code-coverage": "^3.8.1",
"rison": "^0.1.1",
"shortid": "^2.2.15"
},
"devDependencies": {
"cypress": "4.9.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import thunk from 'redux-thunk';
import { bindActionCreators } from 'redux';

import { shallow, mount } from 'enzyme';
import { Modal, Button, Radio } from 'react-bootstrap';
import { FormControl, Modal, Button, Radio } from 'react-bootstrap';
import sinon from 'sinon';
import fetchMock from 'fetch-mock';

Expand Down Expand Up @@ -65,27 +65,26 @@ describe('SaveModal', () => {
target: {
value: 'mock event target',
},
value: 'mock value',
value: 10,
};

const getWrapper = () =>
shallow(<SaveModal {...defaultProps} />, {
context: { store },
}).dive();

it('renders a Modal with 7 inputs and 2 buttons', () => {
it('renders a Modal with the right set of components', () => {
const wrapper = getWrapper();
expect(wrapper.find(Modal)).toHaveLength(1);
expect(wrapper.find('input')).toHaveLength(2);
expect(wrapper.find(Button)).toHaveLength(2);
expect(wrapper.find(Radio)).toHaveLength(5);
expect(wrapper.find(FormControl)).toHaveLength(1);
expect(wrapper.find(Button)).toHaveLength(3);
expect(wrapper.find(Radio)).toHaveLength(2);
});

it('does not show overwrite option for new slice', () => {
const wrapperNewSlice = getWrapper();
wrapperNewSlice.setProps({ slice: null });
expect(wrapperNewSlice.find('#overwrite-radio')).toHaveLength(0);
expect(wrapperNewSlice.find('#saveas-radio')).toHaveLength(1);
it('overwrite radio button is disabled for new slice', () => {
const wrapper = getWrapper();
wrapper.setProps({ slice: null });
expect(wrapper.find('#overwrite-radio').prop('disabled')).toBe(true);
});

it('disable overwrite option for non-owner', () => {
Expand Down Expand Up @@ -128,14 +127,11 @@ describe('SaveModal', () => {
it('onChange', () => {
const wrapper = getWrapper();

wrapper.instance().onChange('newSliceName', mockEvent);
wrapper.instance().onSliceNameChange(mockEvent);
expect(wrapper.state().newSliceName).toBe(mockEvent.target.value);

wrapper.instance().onChange('saveToDashboardId', mockEvent);
wrapper.instance().onDashboardSelectChange(mockEvent);
expect(wrapper.state().saveToDashboardId).toBe(mockEvent.value);

wrapper.instance().onChange('newDashboardName', mockEvent);
expect(wrapper.state().newDashboardName).toBe(mockEvent.target.value);
});

describe('saveOrOverwrite', () => {
Expand All @@ -145,7 +141,7 @@ describe('SaveModal', () => {
sinon.stub(defaultProps.actions, 'saveSlice').callsFake(() =>
Promise.resolve({
data: {
dashboard: '/mock_dashboard/',
dashboard_url: 'http://localhost/mock_dashboard/',
slice: { slice_url: '/mock_slice/' },
},
}),
Expand All @@ -168,10 +164,6 @@ describe('SaveModal', () => {
const wrapper = getWrapper();
const saveToDashboardId = 100;

wrapper.setState({ addToDash: 'existing' });
wrapper.instance().saveOrOverwrite(true);
expect(wrapper.state().alert).toBe('Please select a dashboard');

wrapper.setState({ saveToDashboardId });
wrapper.instance().saveOrOverwrite(true);
const args = defaultProps.actions.saveSlice.getCall(0).args;
Expand All @@ -182,10 +174,6 @@ describe('SaveModal', () => {
const wrapper = getWrapper();
const newDashboardName = 'new dashboard name';

wrapper.setState({ addToDash: 'new' });
wrapper.instance().saveOrOverwrite(true);
expect(wrapper.state().alert).toBe('Please enter a dashboard name');

wrapper.setState({ newDashboardName });
wrapper.instance().saveOrOverwrite(true);
const args = defaultProps.actions.saveSlice.getCall(0).args;
Expand Down
Loading

0 comments on commit a1604e3

Please sign in to comment.