blob: 33936d751648b586232b22b051f6622b24e8e25e [file] [log] [blame]
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
import React from 'react';
import {mount, shallow} from 'enzyme';
import sinon from 'sinon';
import FauxtonAPI from '../../../../core/api';
import Views from '../components';
import '../../base';
FauxtonAPI.router = new FauxtonAPI.Router([]);
describe('ReduceEditor', () => {
describe('getReduceValue', () => {
const defaultProps = {
reduceOptions: [],
hasReduce: false,
hasCustomReduce: false,
reduce: null,
reduceSelectedOption: 'NONE',
customReducerSupported: true,
updateReduceCode: () => {},
selectReduceChanged: () => {}
};
it('returns null for none', () => {
const reduceEl = shallow(<Views.ReduceEditor
{...defaultProps}
/>);
expect(reduceEl.instance().getReduceValue()).toBeNull();
});
it('returns built in for built in reduce', () => {
const reduceEl = shallow(<Views.ReduceEditor
{...defaultProps}
reduce='_sum'
hasReduce={true}
/>);
expect(reduceEl.instance().getReduceValue()).toBe('_sum');
});
it('shows warning when custom reduce is not supported', () => {
const reduceEl = shallow(<Views.ReduceEditor
{...defaultProps}
reduce='function() {}'
hasReduce={true}
customReducerSupported={false}
/>);
expect(reduceEl.find('div.reduce-editor-warning').exists()).toBe(true);
});
});
});
describe('DesignDocSelector component', () => {
const defaultProps = {
designDocList: ['_design/test-doc', '_design/test-doc2'],
newDesignDocName: '',
selectedDesignDocPartitioned: false,
isDbPartitioned: false,
newDesignDocPartitioned: false,
onChangeNewDesignDocName: () => {},
onSelectDesignDoc: () => {}
};
let selectorEl;
it('calls onSelectDesignDoc on change', () => {
const spy = sinon.spy();
selectorEl = mount(
<Views.DesignDocSelector
{...defaultProps}
selectedDDocName={'new-doc'}
onSelectDesignDoc={spy}
/>);
selectorEl.find('.styled-select select').first().simulate('change', {
target: {
value: '_design/test-doc'
}
});
sinon.assert.calledOnce(spy);
});
it('shows new design doc field when set to new-doc', () => {
selectorEl = mount(
<Views.DesignDocSelector
{...defaultProps}
selectedDesignDocName={'new-doc'}
/>);
expect(selectorEl.find('#new-ddoc-section').length).toBe(1);
});
it('hides new design doc field when design doc selected', () => {
selectorEl = mount(
<Views.DesignDocSelector
{...defaultProps}
selectedDesignDocName={'_design/test-doc'}
/>);
expect(selectorEl.find('#new-ddoc-section').length).toBe(0);
});
it('always passes validation when design doc selected', () => {
selectorEl = mount(
<Views.DesignDocSelector
{...defaultProps}
selectedDesignDocName={'_design/test-doc'}
/>);
expect(selectorEl.instance().validate()).toBe(true);
});
it('fails validation if new doc name entered/not entered', () => {
selectorEl = mount(
<Views.DesignDocSelector
{...defaultProps}
selectedDesignDocName={'new-doc'}
newDesignDocName=''
/>);
// it shouldn't validate at this point: no new design doc name has been entered
expect(selectorEl.instance().validate()).toBe(false);
});
it('passes validation if new doc name entered/not entered', () => {
selectorEl = mount(
<Views.DesignDocSelector
{...defaultProps}
selectedDesignDocName={'new-doc'}
newDesignDocName='new-doc-name'
/>);
expect(selectorEl.instance().validate()).toBe(true);
});
it('omits doc URL when not supplied', () => {
selectorEl = mount(
<Views.DesignDocSelector
{...defaultProps}
selectedDesignDocName={'new-doc'}
/>);
expect(selectorEl.find('.help-link').length).toBe(0);
});
it('includes help doc link when supplied', () => {
const docLink = 'http://docs.com';
selectorEl = mount(
<Views.DesignDocSelector
{...defaultProps}
selectedDesignDocName={'new-doc'}
docLink={docLink}
/>);
expect(selectorEl.find('.help-link').length).toBe(1);
expect(selectorEl.find('.help-link').prop('href')).toBe(docLink);
});
it('shows Partitioned checkbox only when db is partitioned', () => {
selectorEl = mount(
<Views.DesignDocSelector
{...defaultProps}
selectedDesignDocName={'new-doc'}
/>);
expect(selectorEl.find('div.ddoc-selector-partitioned').exists()).toBe(false);
selectorEl.setProps({isDbPartitioned: true});
expect(selectorEl.find('div.ddoc-selector-partitioned').exists()).toBe(true);
});
it('calls onChangeNewDesignDocPartitioned when partitioned option changes', () => {
const spy = sinon.stub();
selectorEl = mount(
<Views.DesignDocSelector
{...defaultProps}
selectedDesignDocName={'new-doc'}
isDbPartitioned={true}
onChangeNewDesignDocPartitioned={spy}
/>);
selectorEl.find('input#js-ddoc-selector-partitioned').simulate('change');
sinon.assert.called(spy);
});
});
describe('IndexEditor', () => {
const defaultProps = {
isLoading: false,
isNewView: false,
isNewDesignDoc: false,
isDbPartitioned: false,
viewName: '',
database: { safeID: () => 'test_db' },
newDesignDocName: '',
newDesignDocPartitioned: false,
originalViewName: '',
originalDesignDocName: '',
designDoc: {},
designDocId: '',
designDocPartitioned: false,
designDocList: [],
map: '',
reduce: '',
designDocs: {},
updateNewDesignDocName: () => {},
updateMapCode: () => {},
selectDesignDoc: () => {},
onChangeNewDesignDocName: () => {},
changeViewName: () => {},
reduceOptions: [],
reduceSelectedOption: 'NONE',
hasReduce: false,
hasCustomReduce: false,
updateReduceCode: () => {},
selectReduceChanged: () => {}
};
afterEach(() => {
sinon.restore();
});
it('calls changeViewName on view name change', () => {
const spy = sinon.spy();
const editorEl = mount(<Views.IndexEditor
{...defaultProps}
viewName='new-name'
changeViewName={spy}
/>);
editorEl.find('#index-name').simulate('change', {
target: {
value: 'newViewName'
}
});
sinon.assert.calledWith(spy, 'newViewName');
});
it('generates the correct cancel link when db, ddoc and views have special chars', () => {
const editorEl = mount(<Views.IndexEditor
{...defaultProps}
database={{ id: 'db%$1' }}
designDocId={'_design/doc/1$2'}
viewName={'v?abc/123'}
/>);
const expectedUrl = `/${encodeURIComponent('db%$1')}/_design/${encodeURIComponent('doc/1$2')}/_view/${encodeURIComponent('v?abc/123')}`;
expect(editorEl.find('a.index-cancel-link').prop('href')).toMatch(expectedUrl);
});
it('shows warning when trying to save a partitioned view with custom reduce', () => {
sinon.stub(FauxtonAPI, 'addNotification');
const editorEl = mount(<Views.IndexEditor
{...defaultProps}
viewName='new-name'
designDocId='new-doc'
newDesignDocName='test_ddoc'
newDesignDocPartitioned={true}
isDbPartitioned={true}
reduce='function() { /*custom reduce*/ }'
/>);
editorEl.find('form.view-query-save').simulate('submit', {
preventDefault: () => {}
});
sinon.assert.calledWithMatch(FauxtonAPI.addNotification, { msg: 'Partitioned views do not support custom reduce functions.' });
});
});