Skip to content

Commit

Permalink
[superset-client] replace misc ajax calls (#6135)
Browse files Browse the repository at this point in the history
* [superset-client][misc] replace ajax calls in DashboardTable, TableLoader, utils, common

* [superset-client][misc] replace ajax calls in AsyncSelect, HeaderActions, Deck.gl

* [superset-client][misc] fix tests

* [superset-client] remove unneeded functional setState calls

* [superset-client] make welcome a redux app for toasts

* [superset-client] make Profile a redux app for toasts

* [superset-client] TableLoader don't pass toast props to dom nodes

* tweak deckgl Multi syntax
  • Loading branch information
williaster authored Oct 19, 2018
1 parent 96228ad commit a71e6eb
Show file tree
Hide file tree
Showing 16 changed files with 300 additions and 147 deletions.
104 changes: 71 additions & 33 deletions superset/assets/spec/javascripts/components/AsyncSelect_spec.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import React from 'react';
import Select from 'react-select';
import { shallow } from 'enzyme';
import sinon from 'sinon';
import fetchMock from 'fetch-mock';

import AsyncSelect from '../../../src/components/AsyncSelect';

describe('AsyncSelect', () => {
afterAll(fetchMock.reset);
afterEach(fetchMock.resetHistory);

const dataEndpoint = '/chart/api/read';
const dataGlob = 'glob:*/chart/api/read';
fetchMock.get(dataGlob, []);
fetchMock.resetHistory();

const mockedProps = {
dataEndpoint: '/chart/api/read',
onChange: sinon.spy(),
dataEndpoint,
onChange: () => {},
placeholder: 'Select...',
mutator: () => [
{ value: 1, label: 'main' },
{ value: 2, label: 'another' },
],
valueRenderer: opt => opt.label,
};

it('is valid element', () => {
expect(
React.isValidElement(<AsyncSelect {...mockedProps} />),
Expand All @@ -30,52 +39,81 @@ describe('AsyncSelect', () => {
});

it('calls onChange on select change', () => {
const onChangeSpy = jest.fn();
const wrapper = shallow(
<AsyncSelect {...mockedProps} />,
<AsyncSelect {...mockedProps} onChange={onChangeSpy} />,
);

wrapper.find(Select).simulate('change', { value: 1 });
expect(mockedProps.onChange).toHaveProperty('callCount', 1);
expect(onChangeSpy.mock.calls).toHaveLength(1);
});

describe('auto select', () => {
let server;
beforeEach(() => {
server = sinon.fakeServer.create();
server.respondWith([
200, { 'Content-Type': 'application/json' }, JSON.stringify({}),
]);
});
afterEach(() => {
server.restore();
it('should not call onChange if autoSelect=false', (done) => {
expect.assertions(2);

const onChangeSpy = jest.fn();
shallow(
<AsyncSelect {...mockedProps} onChange={onChangeSpy} />,
);

setTimeout(() => {
expect(fetchMock.calls(dataGlob)).toHaveLength(1);
expect(onChangeSpy.mock.calls).toHaveLength(0);
done();
});
});
it('should be off by default', () => {

it('should auto select the first option if autoSelect=true', (done) => {
expect.assertions(3);

const onChangeSpy = jest.fn();
const wrapper = shallow(
<AsyncSelect {...mockedProps} />,
<AsyncSelect {...mockedProps} onChange={onChangeSpy} autoSelect />,
);
wrapper.instance().fetchOptions();
const spy = sinon.spy(wrapper.instance(), 'onChange');
expect(spy.callCount).toBe(0);

setTimeout(() => {
expect(fetchMock.calls(dataGlob)).toHaveLength(1);
expect(onChangeSpy.mock.calls).toHaveLength(1);
expect(onChangeSpy).toBeCalledWith(wrapper.instance().state.options[0]);
done();
});
});
it('should auto select first option', () => {

it('should not auto select when value prop is set and autoSelect=true', (done) => {
expect.assertions(3);

const onChangeSpy = jest.fn();
const wrapper = shallow(
<AsyncSelect {...mockedProps} autoSelect />,
<AsyncSelect {...mockedProps} value={2} onChange={onChangeSpy} autoSelect />,
);
const spy = sinon.spy(wrapper.instance(), 'onChange');
server.respond();

expect(spy.callCount).toBe(1);
expect(spy.calledWith(wrapper.instance().state.options[0])).toBe(true);
setTimeout(() => {
expect(fetchMock.calls(dataGlob)).toHaveLength(1);
expect(onChangeSpy.mock.calls).toHaveLength(0);
expect(wrapper.find(Select)).toHaveLength(1);
done();
});
});
it('should not auto select when value prop is set', () => {
const wrapper = shallow(
<AsyncSelect {...mockedProps} value={2} autoSelect />,

it('should call onAsyncError if there is an error fetching options', (done) => {
expect.assertions(3);

const errorEndpoint = 'async/error/';
const errorGlob = 'glob:*async/error/';
fetchMock.get(errorGlob, { throws: 'error' });

const onAsyncError = jest.fn();
shallow(
<AsyncSelect {...mockedProps} dataEndpoint={errorEndpoint} onAsyncError={onAsyncError} />,
);
const spy = sinon.spy(wrapper.instance(), 'onChange');
wrapper.instance().fetchOptions();
server.respond();

expect(spy.callCount).toBe(0);
expect(wrapper.find(Select)).toHaveLength(1);
setTimeout(() => {
expect(fetchMock.calls(errorGlob)).toHaveLength(1);
expect(onAsyncError.mock.calls).toHaveLength(1);
expect(onAsyncError).toBeCalledWith('error');
done();
});
});
});
});
8 changes: 5 additions & 3 deletions superset/assets/spec/javascripts/profile/App_spec.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { Col, Row, Tab } from 'react-bootstrap';
import { mount } from 'enzyme';
import { shallow } from 'enzyme';

import { user } from './fixtures';
import App from '../../../src/profile/components/App';
Expand All @@ -14,13 +14,15 @@ describe('App', () => {
React.isValidElement(<App {...mockedProps} />),
).toBe(true);
});

it('renders 2 Col', () => {
const wrapper = mount(<App {...mockedProps} />);
const wrapper = shallow(<App {...mockedProps} />);
expect(wrapper.find(Row)).toHaveLength(1);
expect(wrapper.find(Col)).toHaveLength(2);
});

it('renders 4 Tabs', () => {
const wrapper = mount(<App {...mockedProps} />);
const wrapper = shallow(<App {...mockedProps} />);
expect(wrapper.find(Tab)).toHaveLength(4);
});
});
19 changes: 11 additions & 8 deletions superset/assets/spec/javascripts/profile/CreatedContent_spec.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
import React from 'react';
import { mount } from 'enzyme';
import { shallow } from 'enzyme';
import thunk from 'redux-thunk';
import configureStore from 'redux-mock-store';

import { user } from './fixtures';
import CreatedContent from '../../../src/profile/components/CreatedContent';
import TableLoader from '../../../src/components/TableLoader';

// store needed for withToasts(TableLoader)
const mockStore = configureStore([thunk]);
const store = mockStore({});

describe('CreatedContent', () => {
const mockedProps = {
user,
};
it('is valid', () => {
expect(
React.isValidElement(<CreatedContent {...mockedProps} />),
).toBe(true);
});

it('renders 2 TableLoader', () => {
const wrapper = mount(<CreatedContent {...mockedProps} />);
const wrapper = shallow(<CreatedContent {...mockedProps} />, { context: { store } });
expect(wrapper.find(TableLoader)).toHaveLength(2);
});

it('renders 2 titles', () => {
const wrapper = mount(<CreatedContent {...mockedProps} />);
const wrapper = shallow(<CreatedContent {...mockedProps} />, { context: { store } });
expect(wrapper.find('h3')).toHaveLength(2);
});
});
19 changes: 11 additions & 8 deletions superset/assets/spec/javascripts/profile/Favorites_spec.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
import React from 'react';
import { mount } from 'enzyme';
import { shallow } from 'enzyme';
import thunk from 'redux-thunk';
import configureStore from 'redux-mock-store';

import { user } from './fixtures';
import Favorites from '../../../src/profile/components/Favorites';
import TableLoader from '../../../src/components/TableLoader';

// store needed for withToasts(TableLoader)
const mockStore = configureStore([thunk]);
const store = mockStore({});

describe('Favorites', () => {
const mockedProps = {
user,
};
it('is valid', () => {
expect(
React.isValidElement(<Favorites {...mockedProps} />),
).toBe(true);
});

it('renders 2 TableLoader', () => {
const wrapper = mount(<Favorites {...mockedProps} />);
const wrapper = shallow(<Favorites {...mockedProps} />, { context: { store } });
expect(wrapper.find(TableLoader)).toHaveLength(2);
});

it('renders 2 titles', () => {
const wrapper = mount(<Favorites {...mockedProps} />);
const wrapper = shallow(<Favorites {...mockedProps} />, { context: { store } });
expect(wrapper.find('h3')).toHaveLength(2);
});
});
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React from 'react';
import { mount } from 'enzyme';
import { shallow } from 'enzyme';

import { user } from './fixtures';
import RecentActivity from '../../../src/profile/components/RecentActivity';
import TableLoader from '../../../src/components/TableLoader';


describe('RecentActivity', () => {
const mockedProps = {
user,
Expand All @@ -15,8 +14,9 @@ describe('RecentActivity', () => {
React.isValidElement(<RecentActivity {...mockedProps} />),
).toBe(true);
});

it('renders a TableLoader', () => {
const wrapper = mount(<RecentActivity {...mockedProps} />);
const wrapper = shallow(<RecentActivity {...mockedProps} />);
expect(wrapper.find(TableLoader)).toHaveLength(1);
});
});
53 changes: 35 additions & 18 deletions superset/assets/spec/javascripts/welcome/DashboardTable_spec.jsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,47 @@
import React from 'react';
import { mount } from 'enzyme';
import sinon from 'sinon';
import thunk from 'redux-thunk';
import configureStore from 'redux-mock-store';
import fetchMock from 'fetch-mock';
import { Table } from 'reactable';

import DashboardTable from '../../../src/welcome/DashboardTable';
import Loading from '../../../src/components/Loading';

const $ = window.$ = require('jquery');
// store needed for withToasts(TableLoader)
const mockStore = configureStore([thunk]);
const store = mockStore({});

const dashboardsEndpoint = 'glob:*/dashboardasync/api/read*';
const mockDashboards = [
{ id: 1, url: 'url', dashboard_title: 'title' },
];

fetchMock.get(dashboardsEndpoint, { result: mockDashboards });

function setup() {
// use mount because data fetching is triggered on mount
return mount(<DashboardTable />, { context: { store } });
}

describe('DashboardTable', () => {
const mockedProps = {};
let stub;
beforeEach(() => {
stub = sinon.stub($, 'getJSON');
});
afterEach(() => {
stub.restore();
});
afterEach(fetchMock.resetHistory);

it('is valid', () => {
expect(
React.isValidElement(<DashboardTable {...mockedProps} />),
).toBe(true);
it('renders a Loading initially', () => {
const wrapper = setup();
expect(wrapper.find(Loading)).toHaveLength(1);
});
it('renders', () => {
const wrapper = mount(<DashboardTable {...mockedProps} />);
expect(stub.callCount).toBe(1);
expect(wrapper.find('img')).toHaveLength(1);

it('fetches dashboards and renders a Table', (done) => {
const wrapper = setup();

setTimeout(() => {
expect(fetchMock.calls(dashboardsEndpoint)).toHaveLength(1);
// there's a delay between response and updating state, so manually set it
// rather than adding a timeout which could introduce flakiness
wrapper.setState({ dashaboards: mockDashboards });
expect(wrapper.find(Table)).toHaveLength(1);
done();
});
});
});
8 changes: 4 additions & 4 deletions superset/assets/src/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ $(document).ready(function () {
$('#language-picker a').click(function (ev) {
ev.preventDefault();

const targetUrl = ev.currentTarget.href;
$.ajax(targetUrl).then(() => {
location.reload();
});
SupersetClient.get({ endpoint: ev.currentTarget.href })
.then(() => {
location.reload();
});
});
});

Expand Down
Loading

0 comments on commit a71e6eb

Please sign in to comment.