Skip to content

Commit

Permalink
improve test coverage for explore view components:
Browse files Browse the repository at this point in the history
- SaveModal component
- URLShortLinkButton
  • Loading branch information
Grace Guo committed Jul 8, 2017
1 parent fa3df04 commit dfdc2d4
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 39 deletions.
Expand Up @@ -4,24 +4,30 @@ import { Checkbox } from 'react-bootstrap';
import sinon from 'sinon';
import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { mount } from 'enzyme';
import { shallow } from 'enzyme';

import CheckboxControl from '../../../../javascripts/explore/components/controls/CheckboxControl';
import ControlHeader from '../../../../javascripts/explore/components/ControlHeader';

const defaultProps = {
name: 'show_legend',
onChange: sinon.spy(),
value: false,
label: 'checkbox label',
};

describe('CheckboxControl', () => {
let wrapper;

beforeEach(() => {
wrapper = mount(<CheckboxControl {...defaultProps} />);
wrapper = shallow(<CheckboxControl {...defaultProps} />);
});

it('renders a Checkbox', () => {
expect(wrapper.find(Checkbox)).to.have.lengthOf(1);
const controlHeader = wrapper.find(ControlHeader);
expect(controlHeader).to.have.lengthOf(1);

const headerWrapper = controlHeader.shallow();
expect(headerWrapper.find(Checkbox)).to.have.length(1);
});
});
221 changes: 185 additions & 36 deletions superset/assets/spec/javascripts/explore/components/SaveModal_spec.jsx
@@ -1,76 +1,225 @@
import React from 'react';
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';

import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { shallow } from 'enzyme';
import { shallow, mount } from 'enzyme';
import { Modal, Button, Radio } from 'react-bootstrap';
import sinon from 'sinon';

import { defaultFormData } from '../../../../javascripts/explore/stores/store';
import { SaveModal } from '../../../../javascripts/explore/components/SaveModal';

const defaultProps = {
can_edit: true,
onHide: () => ({}),
actions: {
saveSlice: sinon.spy(),
},
form_data: defaultFormData,
user_id: '1',
dashboards: [],
slice: {},
};
import * as exploreUtils from '../../../../javascripts/explore/exploreUtils';
import * as saveModalActions from '../../../../javascripts/explore/actions/saveModalActions';
import SaveModal from '../../../../javascripts/explore/components/SaveModal';

const $ = window.$ = require('jquery');

describe('SaveModal', () => {
let wrapper;
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
const initialState = {
chart: {},
saveModal: {
dashboards: [],
},
explore: {
can_overwrite: true,
user_id: '1',
datasource: {},
slice: {
slice_id: 1,
slice_name: 'title',
},
alert: null,
},
};
const store = mockStore(initialState);

beforeEach(() => {
wrapper = shallow(<SaveModal {...defaultProps} />);
});
const defaultProps = {
onHide: () => ({}),
actions: saveModalActions,
form_data: {},
};
const mockEvent = {
target: {
value: 'mock event target',
},
value: 'mock value',
};
const getWrapper = () => (shallow(<SaveModal {...defaultProps} />, {
context: { store },
}).dive());

it('renders a Modal with 7 inputs and 2 buttons', () => {
const wrapper = getWrapper();
expect(wrapper.find(Modal)).to.have.lengthOf(1);
expect(wrapper.find('input')).to.have.lengthOf(2);
expect(wrapper.find(Button)).to.have.lengthOf(2);
expect(wrapper.find(Radio)).to.have.lengthOf(5);
});

it('does not show overwrite option for new slice', () => {
defaultProps.slice = null;
const wrapperNewSlice = shallow(<SaveModal {...defaultProps} />);
const wrapperNewSlice = getWrapper();
wrapperNewSlice.setProps({ slice: null });
expect(wrapperNewSlice.find('#overwrite-radio')).to.have.lengthOf(0);
expect(wrapperNewSlice.find('#saveas-radio')).to.have.lengthOf(1);
});

it('disable overwrite option for non-owner', () => {
defaultProps.slice = {};
defaultProps.can_overwrite = false;
const wrapperForNonOwner = shallow(<SaveModal {...defaultProps} />);
const wrapperForNonOwner = getWrapper();
wrapperForNonOwner.setProps({ can_overwrite: false });
const overwriteRadio = wrapperForNonOwner.find('#overwrite-radio');
expect(overwriteRadio).to.have.lengthOf(1);
expect(overwriteRadio.prop('disabled')).to.equal(true);
});

it('saves a new slice', () => {
defaultProps.slice = {
slice_id: 1,
slice_name: 'title',
};
defaultProps.can_overwrite = false;
const wrapperForNewSlice = shallow(<SaveModal {...defaultProps} />);
const wrapperForNewSlice = getWrapper();
wrapperForNewSlice.setProps({ can_overwrite: false });
wrapperForNewSlice.instance().changeAction('saveas');
const saveasRadio = wrapperForNewSlice.find('#saveas-radio');
saveasRadio.simulate('click');
expect(wrapperForNewSlice.state().action).to.equal('saveas');
});

it('overwrite a slice', () => {
defaultProps.slice = {
slice_id: 1,
slice_name: 'title',
};
defaultProps.can_overwrite = true;
const wrapperForOverwrite = shallow(<SaveModal {...defaultProps} />);
const wrapperForOverwrite = getWrapper();
const overwriteRadio = wrapperForOverwrite.find('#overwrite-radio');
overwriteRadio.simulate('click');
expect(wrapperForOverwrite.state().action).to.equal('overwrite');
});

it('componentDidMount', () => {
sinon.spy(SaveModal.prototype, 'componentDidMount');
sinon.spy(saveModalActions, 'fetchDashboards');
mount(<SaveModal {...defaultProps} />, {
context: { store },
});
expect(SaveModal.prototype.componentDidMount.calledOnce).to.equal(true);
expect(saveModalActions.fetchDashboards.calledOnce).to.equal(true);

SaveModal.prototype.componentDidMount.restore();
saveModalActions.fetchDashboards.restore();
});

it('onChange', () => {
const wrapper = getWrapper();

wrapper.instance().onChange('newSliceName', mockEvent);
expect(wrapper.state().newSliceName).to.equal(mockEvent.target.value);

wrapper.instance().onChange('saveToDashboardId', mockEvent);
expect(wrapper.state().saveToDashboardId).to.equal(mockEvent.value);

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

describe('saveOrOverwrite', () => {
beforeEach(() => {
sinon.stub(exploreUtils, 'getExploreUrl').callsFake(() => ('mockURL'));
sinon.stub(saveModalActions, 'saveSlice').callsFake(() => {
const d = $.Deferred();
d.resolve('done');
return d.promise();
});
});
afterEach(() => {
exploreUtils.getExploreUrl.restore();
saveModalActions.saveSlice.restore();
});

it('should save slice', () => {
const wrapper = getWrapper();
wrapper.instance().saveOrOverwrite(true);
expect(saveModalActions.saveSlice.getCall(0).args[0]).to.equal('mockURL');
});
it('existing dashboard', () => {
const wrapper = getWrapper();
const saveToDashboardId = 100;

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

wrapper.setState({ saveToDashboardId });
wrapper.instance().saveOrOverwrite(true);
const args = exploreUtils.getExploreUrl.getCall(0).args;
expect(args[4].save_to_dashboard_id).to.equal(saveToDashboardId);
});
it('new dashboard', () => {
const wrapper = getWrapper();
const newDashboardName = 'new dashboard name';

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

wrapper.setState({ newDashboardName });
wrapper.instance().saveOrOverwrite(true);
const args = exploreUtils.getExploreUrl.getCall(0).args;
expect(args[4].new_dashboard_name).to.equal(newDashboardName);
});
});

describe('should fetchDashboards', () => {
let dispatch;
let request;
let ajaxStub;
const userID = 1;
beforeEach(() => {
dispatch = sinon.spy();
ajaxStub = sinon.stub($, 'ajax');
});
afterEach(() => {
ajaxStub.restore();
});
const mockDashboardData = {
pks: ['value'],
result: [
{ dashboard_title: 'dashboard title' },
],
};
const makeRequest = () => {
request = saveModalActions.fetchDashboards(userID);
request(dispatch);
};

it('makes the ajax request', () => {
makeRequest();
expect(ajaxStub.callCount).to.equal(1);
});

it('calls correct url', () => {
const url = '/dashboardmodelviewasync/api/read?_flt_0_owners=' + userID;
makeRequest();
expect(ajaxStub.getCall(0).args[0].url).to.be.equal(url);
});

it('calls correct actions on error', () => {
ajaxStub.yieldsTo('error', { responseJSON: { error: 'error text' } });
makeRequest();
expect(dispatch.callCount).to.equal(1);
expect(dispatch.getCall(0).args[0].type).to.equal(saveModalActions.FETCH_DASHBOARDS_FAILED);
});

it('calls correct actions on success', () => {
ajaxStub.yieldsTo('success', mockDashboardData);
makeRequest();
expect(dispatch.callCount).to.equal(1);
expect(dispatch.getCall(0).args[0].type)
.to.equal(saveModalActions.FETCH_DASHBOARDS_SUCCEEDED);
});
});

it('removeAlert', () => {
sinon.spy(saveModalActions, 'removeSaveModalAlert');
const wrapper = getWrapper();
wrapper.setProps({ alert: 'old alert' });

wrapper.instance().removeAlert();
expect(saveModalActions.removeSaveModalAlert.callCount).to.equal(1);
expect(wrapper.state().alert).to.be.a('null');
saveModalActions.removeSaveModalAlert.restore();
});
});
@@ -1,7 +1,9 @@
import React from 'react';
import { expect } from 'chai';
import { describe, it } from 'mocha';
import { shallow } from 'enzyme';

import { OverlayTrigger } from 'react-bootstrap';
import URLShortLinkButton from '../../../../javascripts/explore/components/URLShortLinkButton';

describe('URLShortLinkButton', () => {
Expand All @@ -14,4 +16,8 @@ describe('URLShortLinkButton', () => {
it('renders', () => {
expect(React.isValidElement(<URLShortLinkButton {...defaultProps} />)).to.equal(true);
});
it('renders OverlayTrigger', () => {
const wrapper = shallow(<URLShortLinkButton {...defaultProps} />);
expect(wrapper.find(OverlayTrigger)).have.length(1);
});
});

0 comments on commit dfdc2d4

Please sign in to comment.