From dfdc2d44ca4009d6fed8da383f08b1a2e6229f11 Mon Sep 17 00:00:00 2001 From: Grace Guo Date: Fri, 7 Jul 2017 14:23:43 -0700 Subject: [PATCH] improve test coverage for explore view components: - SaveModal component - URLShortLinkButton --- .../components/CheckboxControl_spec.jsx | 12 +- .../explore/components/SaveModal_spec.jsx | 221 +++++++++++++++--- .../components/URLShortLinkButton_spec.jsx | 6 + 3 files changed, 200 insertions(+), 39 deletions(-) diff --git a/superset/assets/spec/javascripts/explore/components/CheckboxControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/CheckboxControl_spec.jsx index 5512b96722a5..0b0839778a23 100644 --- a/superset/assets/spec/javascripts/explore/components/CheckboxControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/CheckboxControl_spec.jsx @@ -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(); + wrapper = shallow(); }); 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); }); }); diff --git a/superset/assets/spec/javascripts/explore/components/SaveModal_spec.jsx b/superset/assets/spec/javascripts/explore/components/SaveModal_spec.jsx index e0a1a845130d..e548d21a60a0 100644 --- a/superset/assets/spec/javascripts/explore/components/SaveModal_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/SaveModal_spec.jsx @@ -1,33 +1,57 @@ 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(); - }); + const defaultProps = { + onHide: () => ({}), + actions: saveModalActions, + form_data: {}, + }; + const mockEvent = { + target: { + value: 'mock event target', + }, + value: 'mock value', + }; + const getWrapper = () => (shallow(, { + 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); @@ -35,42 +59,167 @@ describe('SaveModal', () => { }); it('does not show overwrite option for new slice', () => { - defaultProps.slice = null; - const wrapperNewSlice = shallow(); + 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(); + 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(); + 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(); + 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(, { + 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(); + }); }); diff --git a/superset/assets/spec/javascripts/explore/components/URLShortLinkButton_spec.jsx b/superset/assets/spec/javascripts/explore/components/URLShortLinkButton_spec.jsx index f2729db1163f..74d0d041a87b 100644 --- a/superset/assets/spec/javascripts/explore/components/URLShortLinkButton_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/URLShortLinkButton_spec.jsx @@ -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', () => { @@ -14,4 +16,8 @@ describe('URLShortLinkButton', () => { it('renders', () => { expect(React.isValidElement()).to.equal(true); }); + it('renders OverlayTrigger', () => { + const wrapper = shallow(); + expect(wrapper.find(OverlayTrigger)).have.length(1); + }); });