diff --git a/superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx b/superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx
new file mode 100644
index 000000000..9836583b7
--- /dev/null
+++ b/superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import configureStore from 'redux-mock-store';
+import thunk from 'redux-thunk';
+
+import { shallow } from 'enzyme';
+
+import { STATUS_OPTIONS } from '../../../src/SqlLab/constants';
+import { initialState } from './fixtures';
+import SouthPane from '../../../src/SqlLab/components/SouthPane';
+
+describe('SouthPane', () => {
+ const middlewares = [thunk];
+ const mockStore = configureStore(middlewares);
+ const store = mockStore(initialState);
+
+ const mockedProps = {
+ editorQueries: [],
+ dataPreviewQueries: [],
+ actions: {},
+ activeSouthPaneTab: '',
+ height: 1,
+ databases: {},
+ offline: false,
+ };
+
+ const getWrapper = () => (
+ shallow(, {
+ context: { store },
+ }).dive());
+
+ let wrapper;
+ it('should render offline when the state is offline', () => {
+ wrapper = getWrapper();
+ wrapper.setProps({ offline: true });
+ expect(wrapper.find('.m-r-3').render().text()).toBe(STATUS_OPTIONS.offline);
+ });
+});
diff --git a/superset/assets/spec/javascripts/sqllab/SqlEditorLeftBar_spec.jsx b/superset/assets/spec/javascripts/sqllab/SqlEditorLeftBar_spec.jsx
index b233e19d4..9d3c3f64a 100644
--- a/superset/assets/spec/javascripts/sqllab/SqlEditorLeftBar_spec.jsx
+++ b/superset/assets/spec/javascripts/sqllab/SqlEditorLeftBar_spec.jsx
@@ -1,9 +1,11 @@
import React from 'react';
+import configureStore from 'redux-mock-store';
import { shallow } from 'enzyme';
import sinon from 'sinon';
import fetchMock from 'fetch-mock';
+import thunk from 'redux-thunk';
-import { table, defaultQueryEditor, databases, tables } from './fixtures';
+import { table, defaultQueryEditor, databases, initialState, tables } from './fixtures';
import SqlEditorLeftBar from '../../../src/SqlLab/components/SqlEditorLeftBar';
import TableElement from '../../../src/SqlLab/components/TableElement';
@@ -21,11 +23,16 @@ describe('SqlEditorLeftBar', () => {
database: {},
height: 0,
};
+ const middlewares = [thunk];
+ const mockStore = configureStore(middlewares);
+ const store = mockStore(initialState);
let wrapper;
beforeEach(() => {
- wrapper = shallow();
+ wrapper = shallow(, {
+ context: { store },
+ }).dive();
});
it('is valid', () => {
diff --git a/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx b/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx
index 046e2a6ab..33d1e476a 100644
--- a/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx
+++ b/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx
@@ -166,4 +166,10 @@ describe('TabbedSqlEditors', () => {
const lastTab = wrapper.find(Tab).last();
expect(lastTab.props().eventKey).toContain('add_tab');
});
+ it('should disable new tab when offline', () => {
+ wrapper = getWrapper();
+ expect(wrapper.find(Tab).last().props().disabled).toBe(false);
+ wrapper.setProps({ offline: true });
+ expect(wrapper.find(Tab).last().props().disabled).toBe(true);
+ });
});
diff --git a/superset/assets/src/SqlLab/actions.js b/superset/assets/src/SqlLab/actions.js
index 8c9ef2d49..91e848649 100644
--- a/superset/assets/src/SqlLab/actions.js
+++ b/superset/assets/src/SqlLab/actions.js
@@ -34,6 +34,7 @@ export const SET_DATABASES = 'SET_DATABASES';
export const SET_ACTIVE_QUERY_EDITOR = 'SET_ACTIVE_QUERY_EDITOR';
export const SET_ACTIVE_SOUTHPANE_TAB = 'SET_ACTIVE_SOUTHPANE_TAB';
export const REFRESH_QUERIES = 'REFRESH_QUERIES';
+export const SET_USER_OFFLINE = 'SET_USER_OFFLINE';
export const RUN_QUERY = 'RUN_QUERY';
export const START_QUERY = 'START_QUERY';
export const STOP_QUERY = 'STOP_QUERY';
@@ -342,6 +343,10 @@ export function refreshQueries(alteredQueries) {
return { type: REFRESH_QUERIES, alteredQueries };
}
+export function setUserOffline(offline) {
+ return { type: SET_USER_OFFLINE, offline };
+}
+
export function persistEditorHeight(queryEditor, currentHeight) {
return { type: QUERY_EDITOR_PERSIST_HEIGHT, queryEditor, currentHeight };
}
diff --git a/superset/assets/src/SqlLab/components/QueryAutoRefresh.jsx b/superset/assets/src/SqlLab/components/QueryAutoRefresh.jsx
index 0b0936458..ea6e78091 100644
--- a/superset/assets/src/SqlLab/components/QueryAutoRefresh.jsx
+++ b/superset/assets/src/SqlLab/components/QueryAutoRefresh.jsx
@@ -9,6 +9,7 @@ import * as Actions from '../actions';
const QUERY_UPDATE_FREQ = 2000;
const QUERY_UPDATE_BUFFER_MS = 5000;
const MAX_QUERY_AGE_TO_POLL = 21600000;
+const QUERY_TIMEOUT_LIMIT = 7000;
class QueryAutoRefresh extends React.PureComponent {
componentWillMount() {
@@ -44,11 +45,15 @@ class QueryAutoRefresh extends React.PureComponent {
if (this.shouldCheckForQueries()) {
SupersetClient.get({
endpoint: `/superset/queries/${this.props.queriesLastUpdate - QUERY_UPDATE_BUFFER_MS}`,
+ timeout: QUERY_TIMEOUT_LIMIT,
}).then(({ json }) => {
if (Object.keys(json).length > 0) {
this.props.actions.refreshQueries(json);
}
- });
+ this.props.actions.setUserOffline(false);
+ }).catch(() => {
+ this.props.actions.setUserOffline(true);
+ });
}
}
render() {
diff --git a/superset/assets/src/SqlLab/components/QuerySearch.jsx b/superset/assets/src/SqlLab/components/QuerySearch.jsx
index a3d9ddf3a..9e920295a 100644
--- a/superset/assets/src/SqlLab/components/QuerySearch.jsx
+++ b/superset/assets/src/SqlLab/components/QuerySearch.jsx
@@ -227,7 +227,7 @@ class QuerySearch extends React.PureComponent {