mirror of
https://github.com/apache/superset.git
synced 2026-04-21 00:54:44 +00:00
chore: Moves spec files to the src folder - iteration 7 (#16943)
This commit is contained in:
committed by
GitHub
parent
028f6c0d3f
commit
1ab36c94f3
@@ -36,7 +36,7 @@ import { sliceId } from 'spec/fixtures/mockChartQueries';
|
||||
import dashboardInfo from 'spec/fixtures/mockDashboardInfo';
|
||||
import { dashboardLayout as mockLayout } from 'spec/fixtures/mockDashboardLayout';
|
||||
import { sliceEntitiesForChart } from 'spec/fixtures/mockSliceEntities';
|
||||
import { initialState } from 'spec/javascripts/sqllab/fixtures';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
import { nativeFiltersInfo } from '../../fixtures/mockNativeFilters';
|
||||
|
||||
describe('ChartHolder', () => {
|
||||
|
||||
@@ -36,7 +36,7 @@ import WithPopoverMenu from 'src/dashboard/components/menu/WithPopoverMenu';
|
||||
|
||||
import { getMockStore } from 'spec/fixtures/mockStore';
|
||||
import { dashboardLayout as mockLayout } from 'spec/fixtures/mockDashboardLayout';
|
||||
import { initialState } from 'spec/javascripts/sqllab/fixtures';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
|
||||
describe('Column', () => {
|
||||
const columnWithoutChildren = {
|
||||
|
||||
@@ -36,7 +36,7 @@ import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
|
||||
import { getMockStore } from 'spec/fixtures/mockStore';
|
||||
import { dashboardLayout as mockLayout } from 'spec/fixtures/mockDashboardLayout';
|
||||
import { initialState } from 'spec/javascripts/sqllab/fixtures';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
|
||||
describe('Row', () => {
|
||||
const rowWithoutChildren = { ...mockLayout.present.ROW_ID, children: [] };
|
||||
|
||||
@@ -32,7 +32,7 @@ import Tab, {
|
||||
} from 'src/dashboard/components/gridComponents/Tab';
|
||||
import { dashboardLayoutWithTabs } from 'spec/fixtures/mockDashboardLayout';
|
||||
import { getMockStore } from 'spec/fixtures/mockStore';
|
||||
import { initialState } from 'spec/javascripts/sqllab/fixtures';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
|
||||
describe('Tabs', () => {
|
||||
const props = {
|
||||
|
||||
@@ -37,7 +37,7 @@ import emptyDashboardLayout from 'src/dashboard/fixtures/emptyDashboardLayout';
|
||||
import { dashboardLayoutWithTabs } from 'spec/fixtures/mockDashboardLayout';
|
||||
import { getMockStore } from 'spec/fixtures/mockStore';
|
||||
import { nativeFilters } from 'spec/fixtures/mockNativeFilters';
|
||||
import { initialState } from 'spec/javascripts/sqllab/fixtures';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
|
||||
describe('Tabs', () => {
|
||||
fetchMock.post('glob:*/r/shortner/', {});
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import App from 'src/SqlLab/components/App';
|
||||
import TabbedSqlEditors from 'src/SqlLab/components/TabbedSqlEditors';
|
||||
import sqlLabReducer from 'src/SqlLab/reducers/index';
|
||||
|
||||
describe('SqlLab App', () => {
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureStore(middlewares);
|
||||
const store = mockStore(sqlLabReducer(undefined, {}), {});
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<App store={store} />).dive();
|
||||
});
|
||||
|
||||
it('is valid', () => {
|
||||
expect(React.isValidElement(<App />)).toBe(true);
|
||||
});
|
||||
|
||||
it('should render', () => {
|
||||
const inner = wrapper.dive();
|
||||
expect(inner.find('.SqlLab')).toHaveLength(1);
|
||||
expect(inner.find(TabbedSqlEditors)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { styledMount as mount } from 'spec/helpers/theming';
|
||||
import ColumnElement from 'src/SqlLab/components/ColumnElement';
|
||||
|
||||
import { mockedActions, table } from './fixtures';
|
||||
|
||||
describe('ColumnElement', () => {
|
||||
const mockedProps = {
|
||||
actions: mockedActions,
|
||||
column: table.columns[0],
|
||||
};
|
||||
it('is valid with props', () => {
|
||||
expect(React.isValidElement(<ColumnElement {...mockedProps} />)).toBe(true);
|
||||
});
|
||||
it('renders a proper primary key', () => {
|
||||
const wrapper = mount(<ColumnElement column={table.columns[0]} />);
|
||||
expect(wrapper.find('i.fa-key')).toExist();
|
||||
expect(wrapper.find('.col-name').first().text()).toBe('id');
|
||||
});
|
||||
it('renders a multi-key column', () => {
|
||||
const wrapper = mount(<ColumnElement column={table.columns[1]} />);
|
||||
expect(wrapper.find('i.fa-link')).toExist();
|
||||
expect(wrapper.find('i.fa-bookmark')).toExist();
|
||||
expect(wrapper.find('.col-name').first().text()).toBe('first_name');
|
||||
});
|
||||
it('renders a column with no keys', () => {
|
||||
const wrapper = mount(<ColumnElement column={table.columns[2]} />);
|
||||
expect(wrapper.find('i')).not.toExist();
|
||||
expect(wrapper.find('.col-name').first().text()).toBe('last_name');
|
||||
});
|
||||
});
|
||||
@@ -1,192 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import { shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import shortid from 'shortid';
|
||||
import sqlLabReducer from 'src/SqlLab/reducers/index';
|
||||
import ExploreResultsButton from 'src/SqlLab/components/ExploreResultsButton';
|
||||
import * as exploreUtils from 'src/explore/exploreUtils';
|
||||
import Button from 'src/components/Button';
|
||||
|
||||
import { queries, queryWithBadColumns } from './fixtures';
|
||||
|
||||
describe('ExploreResultsButton', () => {
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureStore(middlewares);
|
||||
const database = {
|
||||
allows_subquery: true,
|
||||
};
|
||||
const initialState = {
|
||||
sqlLab: {
|
||||
...sqlLabReducer(undefined, {}),
|
||||
},
|
||||
common: {
|
||||
conf: { SUPERSET_WEBSERVER_TIMEOUT: 45 },
|
||||
},
|
||||
};
|
||||
const store = mockStore(initialState);
|
||||
const mockedProps = {
|
||||
database,
|
||||
show: true,
|
||||
query: queries[0],
|
||||
onClick() {},
|
||||
};
|
||||
const mockColumns = {
|
||||
ds: {
|
||||
is_date: true,
|
||||
name: 'ds',
|
||||
type: 'STRING',
|
||||
},
|
||||
gender: {
|
||||
is_date: false,
|
||||
name: 'gender',
|
||||
type: 'STRING',
|
||||
},
|
||||
};
|
||||
const mockChartTypeBarChart = {
|
||||
label: 'Distribution - Bar Chart',
|
||||
value: 'dist_bar',
|
||||
};
|
||||
const mockChartTypeTB = {
|
||||
label: 'Time Series - Bar Chart',
|
||||
value: 'bar',
|
||||
};
|
||||
const getExploreResultsButtonWrapper = (props = mockedProps) =>
|
||||
shallow(<ExploreResultsButton store={store} {...props} />)
|
||||
.dive()
|
||||
.dive();
|
||||
|
||||
it('renders', () => {
|
||||
expect(React.isValidElement(<ExploreResultsButton />)).toBe(true);
|
||||
});
|
||||
|
||||
it('renders with props', () => {
|
||||
expect(
|
||||
React.isValidElement(<ExploreResultsButton {...mockedProps} />),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('detects bad columns', () => {
|
||||
const wrapper = getExploreResultsButtonWrapper({
|
||||
database,
|
||||
show: true,
|
||||
query: queryWithBadColumns,
|
||||
onClick() {},
|
||||
});
|
||||
|
||||
const badCols = wrapper.instance().getInvalidColumns();
|
||||
expect(badCols).toEqual(['my_dupe_col__2', '__timestamp', '__TIMESTAMP']);
|
||||
|
||||
const msgWrapper = shallow(wrapper.instance().renderInvalidColumnMessage());
|
||||
expect(msgWrapper.find('div')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders a Button', () => {
|
||||
const wrapper = getExploreResultsButtonWrapper();
|
||||
expect(wrapper.find(Button)).toExist();
|
||||
});
|
||||
|
||||
describe('datasourceName', () => {
|
||||
let wrapper;
|
||||
let stub;
|
||||
beforeEach(() => {
|
||||
wrapper = getExploreResultsButtonWrapper();
|
||||
stub = sinon.stub(shortid, 'generate').returns('abcd');
|
||||
});
|
||||
afterEach(() => {
|
||||
stub.restore();
|
||||
});
|
||||
|
||||
it('should generate data source name from query', () => {
|
||||
const sampleQuery = queries[0];
|
||||
const name = wrapper.instance().datasourceName();
|
||||
expect(name).toBe(`${sampleQuery.user}-${sampleQuery.tab}-abcd`);
|
||||
});
|
||||
it('should generate data source name with empty query', () => {
|
||||
wrapper.setProps({ query: {} });
|
||||
const name = wrapper.instance().datasourceName();
|
||||
expect(name).toBe('undefined-abcd');
|
||||
});
|
||||
|
||||
it('should build viz options', () => {
|
||||
wrapper.setState({ chartType: mockChartTypeTB });
|
||||
const spy = sinon.spy(wrapper.instance(), 'buildVizOptions');
|
||||
wrapper.instance().buildVizOptions();
|
||||
expect(spy.returnValues[0]).toEqual({
|
||||
schema: 'test_schema',
|
||||
sql: wrapper.instance().props.query.sql,
|
||||
dbId: wrapper.instance().props.query.dbId,
|
||||
columns: Object.values(mockColumns),
|
||||
templateParams: undefined,
|
||||
datasourceName: 'admin-Demo-abcd',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should build visualize advise for long query', () => {
|
||||
const longQuery = { ...queries[0], endDttm: 1476910666798 };
|
||||
const props = {
|
||||
show: true,
|
||||
query: longQuery,
|
||||
database,
|
||||
onClick() {},
|
||||
};
|
||||
const longQueryWrapper = shallow(
|
||||
<ExploreResultsButton store={store} {...props} />,
|
||||
)
|
||||
.dive()
|
||||
.dive();
|
||||
const inst = longQueryWrapper.instance();
|
||||
expect(inst.getQueryDuration()).toBe(100.7050400390625);
|
||||
});
|
||||
|
||||
describe('visualize', () => {
|
||||
const wrapper = getExploreResultsButtonWrapper();
|
||||
const mockOptions = { attr: 'mockOptions' };
|
||||
wrapper.setState({
|
||||
chartType: mockChartTypeBarChart,
|
||||
datasourceName: 'mockDatasourceName',
|
||||
});
|
||||
|
||||
const visualizeURL = '/superset/sqllab_viz/';
|
||||
const visualizeEndpoint = `glob:*${visualizeURL}`;
|
||||
const visualizationPayload = { table_id: 107 };
|
||||
fetchMock.post(visualizeEndpoint, visualizationPayload);
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(exploreUtils, 'getExploreUrl').callsFake(() => 'mockURL');
|
||||
sinon.spy(exploreUtils, 'exportChart');
|
||||
sinon.spy(exploreUtils, 'exploreChart');
|
||||
sinon
|
||||
.stub(wrapper.instance(), 'buildVizOptions')
|
||||
.callsFake(() => mockOptions);
|
||||
});
|
||||
afterEach(() => {
|
||||
exploreUtils.getExploreUrl.restore();
|
||||
exploreUtils.exploreChart.restore();
|
||||
exploreUtils.exportChart.restore();
|
||||
wrapper.instance().buildVizOptions.restore();
|
||||
fetchMock.reset();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,64 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
|
||||
import HighlightedSql from 'src/SqlLab/components/HighlightedSql';
|
||||
import ModalTrigger from 'src/components/ModalTrigger';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
|
||||
describe('HighlightedSql', () => {
|
||||
const sql =
|
||||
"SELECT * FROM test WHERE something='fkldasjfklajdslfkjadlskfjkldasjfkladsjfkdjsa'";
|
||||
it('renders with props', () => {
|
||||
expect(React.isValidElement(<HighlightedSql sql={sql} />)).toBe(true);
|
||||
});
|
||||
it('renders a ModalTrigger', () => {
|
||||
const wrapper = shallow(<HighlightedSql sql={sql} />);
|
||||
expect(wrapper.find(ModalTrigger)).toExist();
|
||||
});
|
||||
it('renders a ModalTrigger while using shrink', () => {
|
||||
const wrapper = shallow(<HighlightedSql sql={sql} shrink maxWidth={20} />);
|
||||
expect(wrapper.find(ModalTrigger)).toExist();
|
||||
});
|
||||
it('renders two SyntaxHighlighter in modal', () => {
|
||||
const wrapper = mount(
|
||||
<HighlightedSql
|
||||
sql={sql}
|
||||
rawSql="SELECT * FORM foo"
|
||||
shrink
|
||||
maxWidth={5}
|
||||
/>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
const pre = wrapper.find('pre');
|
||||
expect(pre).toHaveLength(1);
|
||||
pre.simulate('click');
|
||||
setTimeout(() => {
|
||||
const modalBody = mount(wrapper.state().modalBody);
|
||||
expect(modalBody.find(SyntaxHighlighter)).toHaveLength(2);
|
||||
}, 10);
|
||||
});
|
||||
});
|
||||
@@ -1,69 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
import thunk from 'redux-thunk';
|
||||
import configureStore from 'redux-mock-store';
|
||||
|
||||
import QueryAutoRefresh from 'src/SqlLab/components/QueryAutoRefresh';
|
||||
import { initialState, runningQuery } from './fixtures';
|
||||
|
||||
describe('QueryAutoRefresh', () => {
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureStore(middlewares);
|
||||
const sqlLab = {
|
||||
...initialState.sqlLab,
|
||||
queries: {
|
||||
ryhMUZCGb: runningQuery,
|
||||
},
|
||||
};
|
||||
const state = {
|
||||
...initialState,
|
||||
sqlLab,
|
||||
};
|
||||
const store = mockStore(state);
|
||||
const getWrapper = () =>
|
||||
shallow(<QueryAutoRefresh store={store} />)
|
||||
.dive()
|
||||
.dive();
|
||||
let wrapper;
|
||||
|
||||
it('shouldCheckForQueries', () => {
|
||||
wrapper = getWrapper();
|
||||
expect(wrapper.instance().shouldCheckForQueries()).toBe(true);
|
||||
});
|
||||
|
||||
it('setUserOffline', () => {
|
||||
wrapper = getWrapper();
|
||||
const spy = sinon.spy(wrapper.instance().props.actions, 'setUserOffline');
|
||||
|
||||
// state not changed
|
||||
wrapper.setState({
|
||||
offline: false,
|
||||
});
|
||||
expect(spy.called).toBe(false);
|
||||
|
||||
// state is changed
|
||||
wrapper.setState({
|
||||
offline: true,
|
||||
});
|
||||
expect(spy.callCount).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -1,140 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 thunk from 'redux-thunk';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import QuerySearch from 'src/SqlLab/components/QuerySearch';
|
||||
import { Provider } from 'react-redux';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
import { fireEvent, render, screen, act } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { user } from './fixtures';
|
||||
|
||||
const mockStore = configureStore([thunk]);
|
||||
const store = mockStore({
|
||||
sqlLab: user,
|
||||
});
|
||||
|
||||
const SEARCH_ENDPOINT = 'glob:*/superset/search_queries?*';
|
||||
const USER_ENDPOINT = 'glob:*/api/v1/query/related/user';
|
||||
const DATABASE_ENDPOINT = 'glob:*/api/v1/database/?*';
|
||||
|
||||
fetchMock.get(SEARCH_ENDPOINT, []);
|
||||
fetchMock.get(USER_ENDPOINT, []);
|
||||
fetchMock.get(DATABASE_ENDPOINT, []);
|
||||
|
||||
describe('QuerySearch', () => {
|
||||
const mockedProps = {
|
||||
actions: { addDangerToast: jest.fn() },
|
||||
displayLimit: 50,
|
||||
};
|
||||
|
||||
it('is valid', () => {
|
||||
expect(
|
||||
React.isValidElement(
|
||||
<ThemeProvider theme={supersetTheme}>
|
||||
<Provider store={store}>
|
||||
<QuerySearch {...mockedProps} />
|
||||
</Provider>
|
||||
</ThemeProvider>,
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
// You need this await function in order to change state in the app. In fact you need it everytime you re-render.
|
||||
await act(async () => {
|
||||
render(
|
||||
<ThemeProvider theme={supersetTheme}>
|
||||
<Provider store={store}>
|
||||
<QuerySearch {...mockedProps} />
|
||||
</Provider>
|
||||
</ThemeProvider>,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have three Selects', () => {
|
||||
expect(screen.getByText(/28 days ago/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/now/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/success/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('updates fromTime on user selects from time', () => {
|
||||
const role = screen.getByText(/28 days ago/i);
|
||||
fireEvent.keyDown(role, { key: 'ArrowDown', keyCode: 40 });
|
||||
userEvent.click(screen.getByText(/1 hour ago/i));
|
||||
expect(screen.getByText(/1 hour ago/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('updates toTime on user selects on time', () => {
|
||||
const role = screen.getByText(/now/i);
|
||||
fireEvent.keyDown(role, { key: 'ArrowDown', keyCode: 40 });
|
||||
userEvent.click(screen.getByText(/1 hour ago/i));
|
||||
expect(screen.getByText(/1 hour ago/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('updates status on user selects status', () => {
|
||||
const role = screen.getByText(/success/i);
|
||||
fireEvent.keyDown(role, { key: 'ArrowDown', keyCode: 40 });
|
||||
userEvent.click(screen.getByText(/failed/i));
|
||||
expect(screen.getByText(/failed/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should have one input for searchText', () => {
|
||||
expect(
|
||||
screen.getByPlaceholderText(/Query search string/i),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('updates search text on user inputs search text', () => {
|
||||
const search = screen.getByPlaceholderText(/Query search string/i);
|
||||
userEvent.type(search, 'text');
|
||||
expect(search.value).toBe('text');
|
||||
});
|
||||
|
||||
it('should have one Button', () => {
|
||||
const button = screen.getAllByRole('button');
|
||||
expect(button.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should call API when search button is pressed', async () => {
|
||||
fetchMock.resetHistory();
|
||||
const button = screen.getByRole('button');
|
||||
await act(async () => {
|
||||
userEvent.click(button);
|
||||
});
|
||||
expect(fetchMock.calls(SEARCH_ENDPOINT)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should call API when (only)enter key is pressed', async () => {
|
||||
fetchMock.resetHistory();
|
||||
const search = screen.getByPlaceholderText(/Query search string/i);
|
||||
await act(async () => {
|
||||
userEvent.type(search, 'a');
|
||||
});
|
||||
expect(fetchMock.calls(SEARCH_ENDPOINT)).toHaveLength(0);
|
||||
await act(async () => {
|
||||
userEvent.type(search, '{enter}');
|
||||
});
|
||||
expect(fetchMock.calls(SEARCH_ENDPOINT)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
@@ -1,40 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { shallow } from 'enzyme';
|
||||
|
||||
import Label from 'src/components/Label';
|
||||
import QueryStateLabel from 'src/SqlLab/components/QueryStateLabel';
|
||||
|
||||
describe('SavedQuery', () => {
|
||||
const mockedProps = {
|
||||
query: {
|
||||
state: 'running',
|
||||
},
|
||||
};
|
||||
it('is valid', () => {
|
||||
expect(React.isValidElement(<QueryStateLabel {...mockedProps} />)).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
it('has an Overlay and a Popover', () => {
|
||||
const wrapper = shallow(<QueryStateLabel {...mockedProps} />);
|
||||
expect(wrapper.find(Label)).toExist();
|
||||
});
|
||||
});
|
||||
@@ -1,58 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 thunk from 'redux-thunk';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import { styledMount as mount } from 'spec/helpers/theming';
|
||||
import QueryTable from 'src/SqlLab/components/QueryTable';
|
||||
import TableView from 'src/components/TableView';
|
||||
import { TableCollection } from 'src/components/dataViewCommon';
|
||||
import { Provider } from 'react-redux';
|
||||
import { queries, user } from './fixtures';
|
||||
|
||||
describe('QueryTable', () => {
|
||||
const mockedProps = {
|
||||
queries,
|
||||
displayLimit: 100,
|
||||
};
|
||||
it('is valid', () => {
|
||||
expect(React.isValidElement(<QueryTable displayLimit={100} />)).toBe(true);
|
||||
});
|
||||
it('is valid with props', () => {
|
||||
expect(React.isValidElement(<QueryTable {...mockedProps} />)).toBe(true);
|
||||
});
|
||||
it('renders a proper table', () => {
|
||||
const mockStore = configureStore([thunk]);
|
||||
const store = mockStore({
|
||||
sqlLab: user,
|
||||
});
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<QueryTable {...mockedProps} />
|
||||
</Provider>,
|
||||
);
|
||||
const tableWrapper = wrapper.find(TableView).find(TableCollection);
|
||||
|
||||
expect(wrapper.find(TableView)).toExist();
|
||||
expect(tableWrapper.find('table')).toExist();
|
||||
expect(tableWrapper.find('table').find('thead').find('tr')).toHaveLength(1);
|
||||
expect(tableWrapper.find('table').find('tbody').find('tr')).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
@@ -1,201 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { shallow } from 'enzyme';
|
||||
import { styledMount } from 'spec/helpers/theming';
|
||||
import { render, screen } from 'spec/helpers/testing-library';
|
||||
import { Provider } from 'react-redux';
|
||||
import sinon from 'sinon';
|
||||
import Alert from 'src/components/Alert';
|
||||
import ProgressBar from 'src/components/ProgressBar';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import FilterableTable from 'src/components/FilterableTable/FilterableTable';
|
||||
import ExploreResultsButton from 'src/SqlLab/components/ExploreResultsButton';
|
||||
import ResultSet from 'src/SqlLab/components/ResultSet';
|
||||
import ErrorMessageWithStackTrace from 'src/components/ErrorMessage/ErrorMessageWithStackTrace';
|
||||
import {
|
||||
cachedQuery,
|
||||
failedQueryWithErrorMessage,
|
||||
failedQueryWithErrors,
|
||||
queries,
|
||||
runningQuery,
|
||||
stoppedQuery,
|
||||
initialState,
|
||||
user,
|
||||
queryWithNoQueryLimit,
|
||||
} from './fixtures';
|
||||
|
||||
const mockStore = configureStore([thunk]);
|
||||
const store = mockStore(initialState);
|
||||
const clearQuerySpy = sinon.spy();
|
||||
const fetchQuerySpy = sinon.spy();
|
||||
const reRunQuerySpy = sinon.spy();
|
||||
const mockedProps = {
|
||||
actions: {
|
||||
clearQueryResults: clearQuerySpy,
|
||||
fetchQueryResults: fetchQuerySpy,
|
||||
reRunQuery: reRunQuerySpy,
|
||||
},
|
||||
cache: true,
|
||||
query: queries[0],
|
||||
height: 140,
|
||||
database: { allows_virtual_table_explore: true },
|
||||
user,
|
||||
defaultQueryLimit: 1000,
|
||||
};
|
||||
const stoppedQueryProps = { ...mockedProps, query: stoppedQuery };
|
||||
const runningQueryProps = { ...mockedProps, query: runningQuery };
|
||||
const cachedQueryProps = { ...mockedProps, query: cachedQuery };
|
||||
const failedQueryWithErrorMessageProps = {
|
||||
...mockedProps,
|
||||
query: failedQueryWithErrorMessage,
|
||||
};
|
||||
const failedQueryWithErrorsProps = {
|
||||
...mockedProps,
|
||||
query: failedQueryWithErrors,
|
||||
};
|
||||
const newProps = {
|
||||
query: {
|
||||
cached: false,
|
||||
resultsKey: 'new key',
|
||||
results: {
|
||||
data: [{ a: 1 }],
|
||||
},
|
||||
},
|
||||
};
|
||||
fetchMock.get('glob:*/api/v1/dataset?*', { result: [] });
|
||||
|
||||
test('is valid', () => {
|
||||
expect(React.isValidElement(<ResultSet {...mockedProps} />)).toBe(true);
|
||||
});
|
||||
|
||||
test('renders a Table', () => {
|
||||
const wrapper = shallow(<ResultSet {...mockedProps} />);
|
||||
expect(wrapper.find(FilterableTable)).toExist();
|
||||
});
|
||||
|
||||
describe('componentDidMount', () => {
|
||||
const propsWithError = {
|
||||
...mockedProps,
|
||||
query: { ...queries[0], errorMessage: 'Your session timed out' },
|
||||
};
|
||||
let spy;
|
||||
beforeEach(() => {
|
||||
reRunQuerySpy.resetHistory();
|
||||
spy = sinon.spy(ResultSet.prototype, 'componentDidMount');
|
||||
});
|
||||
afterEach(() => {
|
||||
spy.restore();
|
||||
});
|
||||
it('should call reRunQuery if timed out', () => {
|
||||
shallow(<ResultSet {...propsWithError} />);
|
||||
expect(reRunQuerySpy.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should not call reRunQuery if no error', () => {
|
||||
shallow(<ResultSet {...mockedProps} />);
|
||||
expect(reRunQuerySpy.callCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('UNSAFE_componentWillReceiveProps', () => {
|
||||
const wrapper = shallow(<ResultSet {...mockedProps} />);
|
||||
let spy;
|
||||
beforeEach(() => {
|
||||
clearQuerySpy.resetHistory();
|
||||
fetchQuerySpy.resetHistory();
|
||||
spy = sinon.spy(ResultSet.prototype, 'UNSAFE_componentWillReceiveProps');
|
||||
});
|
||||
afterEach(() => {
|
||||
spy.restore();
|
||||
});
|
||||
it('should update cached data', () => {
|
||||
wrapper.setProps(newProps);
|
||||
|
||||
expect(wrapper.state().data).toEqual(newProps.query.results.data);
|
||||
expect(clearQuerySpy.callCount).toBe(1);
|
||||
expect(clearQuerySpy.getCall(0).args[0]).toEqual(newProps.query);
|
||||
expect(fetchQuerySpy.callCount).toBe(1);
|
||||
expect(fetchQuerySpy.getCall(0).args[0]).toEqual(newProps.query);
|
||||
});
|
||||
});
|
||||
|
||||
test('should render success query', () => {
|
||||
const wrapper = shallow(<ResultSet {...mockedProps} />);
|
||||
const filterableTable = wrapper.find(FilterableTable);
|
||||
expect(filterableTable.props().data).toBe(mockedProps.query.results.data);
|
||||
expect(wrapper.find(ExploreResultsButton)).toExist();
|
||||
});
|
||||
test('should render empty results', () => {
|
||||
const props = {
|
||||
...mockedProps,
|
||||
query: { ...mockedProps.query, results: { data: [] } },
|
||||
};
|
||||
const wrapper = styledMount(
|
||||
<Provider store={store}>
|
||||
<ResultSet {...props} />
|
||||
</Provider>,
|
||||
);
|
||||
expect(wrapper.find(FilterableTable)).not.toExist();
|
||||
expect(wrapper.find(Alert)).toExist();
|
||||
expect(wrapper.find(Alert).render().text()).toBe(
|
||||
'The query returned no data',
|
||||
);
|
||||
});
|
||||
|
||||
test('should render cached query', () => {
|
||||
const wrapper = shallow(<ResultSet {...cachedQueryProps} />);
|
||||
const cachedData = [{ col1: 'a', col2: 'b' }];
|
||||
wrapper.setState({ data: cachedData });
|
||||
const filterableTable = wrapper.find(FilterableTable);
|
||||
expect(filterableTable.props().data).toBe(cachedData);
|
||||
});
|
||||
|
||||
test('should render stopped query', () => {
|
||||
const wrapper = shallow(<ResultSet {...stoppedQueryProps} />);
|
||||
expect(wrapper.find(Alert)).toExist();
|
||||
});
|
||||
|
||||
test('should render running/pending/fetching query', () => {
|
||||
const wrapper = shallow(<ResultSet {...runningQueryProps} />);
|
||||
expect(wrapper.find(ProgressBar)).toExist();
|
||||
});
|
||||
|
||||
test('should render a failed query with an error message', () => {
|
||||
const wrapper = shallow(<ResultSet {...failedQueryWithErrorMessageProps} />);
|
||||
expect(wrapper.find(ErrorMessageWithStackTrace)).toExist();
|
||||
});
|
||||
|
||||
test('should render a failed query with an errors object', () => {
|
||||
const wrapper = shallow(<ResultSet {...failedQueryWithErrorsProps} />);
|
||||
expect(wrapper.find(ErrorMessageWithStackTrace)).toExist();
|
||||
});
|
||||
|
||||
test('renders if there is no limit in query.results but has queryLimit', () => {
|
||||
render(<ResultSet {...mockedProps} />, { useRedux: true });
|
||||
expect(screen.getByRole('grid')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders if there is a limit in query.results but not queryLimit', () => {
|
||||
const props = { ...mockedProps, query: queryWithNoQueryLimit };
|
||||
render(<ResultSet {...props} />, { useRedux: true });
|
||||
expect(screen.getByRole('grid')).toBeInTheDocument();
|
||||
});
|
||||
@@ -1,59 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { shallow } from 'enzyme';
|
||||
import { Radio } from 'src/components/Radio';
|
||||
import { AutoComplete, Input } from 'src/common/components';
|
||||
import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal';
|
||||
|
||||
describe('SaveDatasetModal', () => {
|
||||
const mockedProps = {
|
||||
visible: false,
|
||||
onOk: () => {},
|
||||
onHide: () => {},
|
||||
handleDatasetNameChange: () => {},
|
||||
handleSaveDatasetRadioBtnState: () => {},
|
||||
saveDatasetRadioBtnState: 1,
|
||||
handleOverwriteCancel: () => {},
|
||||
handleOverwriteDataset: () => {},
|
||||
handleOverwriteDatasetOption: () => {},
|
||||
defaultCreateDatasetValue: 'someDatasets',
|
||||
shouldOverwriteDataset: false,
|
||||
userDatasetOptions: [],
|
||||
disableSaveAndExploreBtn: false,
|
||||
handleSaveDatasetModalSearch: () => Promise,
|
||||
filterAutocompleteOption: () => false,
|
||||
onChangeAutoComplete: () => {},
|
||||
};
|
||||
it('renders a radio group btn', () => {
|
||||
// @ts-ignore
|
||||
const wrapper = shallow(<SaveDatasetModal {...mockedProps} />);
|
||||
expect(wrapper.find(Radio.Group)).toExist();
|
||||
});
|
||||
it('renders a autocomplete', () => {
|
||||
// @ts-ignore
|
||||
const wrapper = shallow(<SaveDatasetModal {...mockedProps} />);
|
||||
expect(wrapper.find(AutoComplete)).toExist();
|
||||
});
|
||||
it('renders an input form', () => {
|
||||
// @ts-ignore
|
||||
const wrapper = shallow(<SaveDatasetModal {...mockedProps} />);
|
||||
expect(wrapper.find(Input)).toExist();
|
||||
});
|
||||
});
|
||||
@@ -1,88 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { shallow } from 'enzyme';
|
||||
import * as sinon from 'sinon';
|
||||
import SaveQuery from 'src/SqlLab/components/SaveQuery';
|
||||
import Modal from 'src/components/Modal';
|
||||
import Button from 'src/components/Button';
|
||||
import { FormItem } from 'src/components/Form';
|
||||
|
||||
describe('SavedQuery', () => {
|
||||
const mockedProps = {
|
||||
query: {
|
||||
dbId: 1,
|
||||
schema: 'main',
|
||||
sql: 'SELECT * FROM t',
|
||||
},
|
||||
defaultLabel: 'untitled',
|
||||
animation: false,
|
||||
};
|
||||
it('is valid', () => {
|
||||
expect(React.isValidElement(<SaveQuery />)).toBe(true);
|
||||
});
|
||||
it('is valid with props', () => {
|
||||
expect(React.isValidElement(<SaveQuery {...mockedProps} />)).toBe(true);
|
||||
});
|
||||
it('has a Modal', () => {
|
||||
const wrapper = shallow(<SaveQuery {...mockedProps} />);
|
||||
expect(wrapper.find(Modal)).toExist();
|
||||
});
|
||||
// TODO: eschutho convert test to RTL
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('has a cancel button', () => {
|
||||
const wrapper = shallow(<SaveQuery {...mockedProps} />);
|
||||
const modal = wrapper.find(Modal);
|
||||
|
||||
expect(modal.find('[data-test="cancel-query"]')).toHaveLength(1);
|
||||
});
|
||||
it('has 2 FormItem', () => {
|
||||
const wrapper = shallow(<SaveQuery {...mockedProps} />);
|
||||
const modal = wrapper.find(Modal);
|
||||
|
||||
expect(modal.find(FormItem)).toHaveLength(2);
|
||||
});
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('has a save button if this is a new query', () => {
|
||||
const saveSpy = sinon.spy();
|
||||
const wrapper = shallow(<SaveQuery {...mockedProps} onSave={saveSpy} />);
|
||||
const modal = wrapper.find(Modal);
|
||||
|
||||
expect(modal.find(Button)).toHaveLength(2);
|
||||
modal.find(Button).at(0).simulate('click');
|
||||
expect(saveSpy.calledOnce).toBe(true);
|
||||
});
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('has an update button if this is an existing query', () => {
|
||||
const updateSpy = sinon.spy();
|
||||
const props = {
|
||||
...mockedProps,
|
||||
query: {
|
||||
...mockedProps.query,
|
||||
remoteId: '42',
|
||||
},
|
||||
};
|
||||
const wrapper = shallow(<SaveQuery {...props} onUpdate={updateSpy} />);
|
||||
const modal = wrapper.find(Modal);
|
||||
|
||||
expect(modal.find(Button)).toHaveLength(3);
|
||||
modal.find(Button).at(0).simulate('click');
|
||||
expect(updateSpy.calledOnce).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,132 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import * as featureFlags from 'src/featureFlags';
|
||||
import { Provider } from 'react-redux';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
import { render, screen, act } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import * as utils from 'src/utils/common';
|
||||
import ShareSqlLabQuery from 'src/SqlLab/components/ShareSqlLabQuery';
|
||||
|
||||
const mockStore = configureStore([thunk]);
|
||||
const store = mockStore({});
|
||||
let isFeatureEnabledMock;
|
||||
|
||||
const standardProvider = ({ children }) => (
|
||||
<ThemeProvider theme={supersetTheme}>
|
||||
<Provider store={store}>{children}</Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
const defaultProps = {
|
||||
queryEditor: {
|
||||
dbId: 0,
|
||||
title: 'query title',
|
||||
schema: 'query_schema',
|
||||
autorun: false,
|
||||
sql: 'SELECT * FROM ...',
|
||||
remoteId: 999,
|
||||
},
|
||||
addDangerToast: jest.fn(),
|
||||
};
|
||||
|
||||
describe('ShareSqlLabQuery', () => {
|
||||
const storeQueryUrl = 'glob:*/kv/store/';
|
||||
const storeQueryMockId = '123';
|
||||
|
||||
beforeEach(async () => {
|
||||
fetchMock.post(storeQueryUrl, () => ({ id: storeQueryMockId }), {
|
||||
overwriteRoutes: true,
|
||||
});
|
||||
fetchMock.resetHistory();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
afterAll(fetchMock.reset);
|
||||
|
||||
describe('via /kv/store', () => {
|
||||
beforeAll(() => {
|
||||
isFeatureEnabledMock = jest
|
||||
.spyOn(featureFlags, 'isFeatureEnabled')
|
||||
.mockImplementation(() => true);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
isFeatureEnabledMock.restore();
|
||||
});
|
||||
|
||||
it('calls storeQuery() with the query when getCopyUrl() is called', async () => {
|
||||
await act(async () => {
|
||||
render(<ShareSqlLabQuery {...defaultProps} />, {
|
||||
wrapper: standardProvider,
|
||||
});
|
||||
});
|
||||
const button = screen.getByRole('button');
|
||||
const storeQuerySpy = jest.spyOn(utils, 'storeQuery');
|
||||
userEvent.click(button);
|
||||
expect(storeQuerySpy.mock.calls).toHaveLength(1);
|
||||
storeQuerySpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('via saved query', () => {
|
||||
beforeAll(() => {
|
||||
isFeatureEnabledMock = jest
|
||||
.spyOn(featureFlags, 'isFeatureEnabled')
|
||||
.mockImplementation(() => false);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
isFeatureEnabledMock.restore();
|
||||
});
|
||||
|
||||
it('does not call storeQuery() with the query when getCopyUrl() is called and feature is not enabled', async () => {
|
||||
await act(async () => {
|
||||
render(<ShareSqlLabQuery {...defaultProps} />, {
|
||||
wrapper: standardProvider,
|
||||
});
|
||||
});
|
||||
const storeQuerySpy = jest.spyOn(utils, 'storeQuery');
|
||||
const button = screen.getByRole('button');
|
||||
userEvent.click(button);
|
||||
expect(storeQuerySpy.mock.calls).toHaveLength(0);
|
||||
storeQuerySpy.mockRestore();
|
||||
});
|
||||
|
||||
it('button is disabled and there is a request to save the query', async () => {
|
||||
const updatedProps = {
|
||||
queryEditor: {
|
||||
...defaultProps.queryEditor,
|
||||
remoteId: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
render(<ShareSqlLabQuery {...updatedProps} />, {
|
||||
wrapper: standardProvider,
|
||||
});
|
||||
const button = await screen.findByRole('button', { name: /copy link/i });
|
||||
expect(button).toBeDisabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,97 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import { styledShallow as shallow } from 'spec/helpers/theming';
|
||||
import SouthPaneContainer from 'src/SqlLab/components/SouthPane/state';
|
||||
import ResultSet from 'src/SqlLab/components/ResultSet';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import { STATUS_OPTIONS } from 'src/SqlLab/constants';
|
||||
import { initialState } from './fixtures';
|
||||
|
||||
const mockedProps = {
|
||||
editorQueries: [
|
||||
{
|
||||
cached: false,
|
||||
changedOn: Date.now(),
|
||||
db: 'main',
|
||||
dbId: 1,
|
||||
id: 'LCly_kkIN',
|
||||
startDttm: Date.now(),
|
||||
},
|
||||
{
|
||||
cached: false,
|
||||
changedOn: 1559238500401,
|
||||
db: 'main',
|
||||
dbId: 1,
|
||||
id: 'lXJa7F9_r',
|
||||
startDttm: 1559238500401,
|
||||
},
|
||||
{
|
||||
cached: false,
|
||||
changedOn: 1559238506925,
|
||||
db: 'main',
|
||||
dbId: 1,
|
||||
id: '2g2_iRFMl',
|
||||
startDttm: 1559238506925,
|
||||
},
|
||||
{
|
||||
cached: false,
|
||||
changedOn: 1559238516395,
|
||||
db: 'main',
|
||||
dbId: 1,
|
||||
id: 'erWdqEWPm',
|
||||
startDttm: 1559238516395,
|
||||
},
|
||||
],
|
||||
latestQueryId: 'LCly_kkIN',
|
||||
dataPreviewQueries: [],
|
||||
actions: {},
|
||||
activeSouthPaneTab: '',
|
||||
height: 1,
|
||||
displayLimit: 1,
|
||||
databases: {},
|
||||
offline: false,
|
||||
};
|
||||
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureStore(middlewares);
|
||||
const store = mockStore(initialState);
|
||||
|
||||
describe('SouthPane', () => {
|
||||
const getWrapper = () =>
|
||||
shallow(<SouthPaneContainer store={store} {...mockedProps} />).dive();
|
||||
|
||||
let wrapper;
|
||||
|
||||
it('should render offline when the state is offline', () => {
|
||||
wrapper = getWrapper().dive();
|
||||
wrapper.setProps({ offline: true });
|
||||
expect(wrapper.childAt(0).text()).toBe(STATUS_OPTIONS.offline);
|
||||
});
|
||||
|
||||
it('should pass latest query down to ResultSet component', () => {
|
||||
wrapper = getWrapper().dive();
|
||||
expect(wrapper.find(ResultSet)).toExist();
|
||||
expect(wrapper.find(ResultSet).props().query.id).toEqual(
|
||||
mockedProps.latestQueryId,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,119 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 configureStore from 'redux-mock-store';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import { shallow } from 'enzyme';
|
||||
import { render, screen } from 'spec/helpers/testing-library';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { Provider } from 'react-redux';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import thunk from 'redux-thunk';
|
||||
import SqlEditorLeftBar from 'src/SqlLab/components/SqlEditorLeftBar';
|
||||
import TableElement from 'src/SqlLab/components/TableElement';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
import {
|
||||
table,
|
||||
initialState,
|
||||
databases,
|
||||
defaultQueryEditor,
|
||||
mockedActions,
|
||||
} from './fixtures';
|
||||
|
||||
const mockedProps = {
|
||||
actions: mockedActions,
|
||||
tables: [table],
|
||||
queryEditor: defaultQueryEditor,
|
||||
database: databases,
|
||||
height: 0,
|
||||
};
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureStore(middlewares);
|
||||
const store = mockStore(initialState);
|
||||
fetchMock.get('glob:*/api/v1/database/*/schemas/?*', { result: [] });
|
||||
describe('SqlEditorLeftBar', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<SqlEditorLeftBar {...mockedProps} />, {
|
||||
context: { store },
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('is valid', () => {
|
||||
expect(React.isValidElement(<SqlEditorLeftBar {...mockedProps} />)).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('renders a TableElement', () => {
|
||||
expect(wrapper.find(TableElement)).toExist();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Left Panel Expansion', () => {
|
||||
it('table should be visible when expanded is true', () => {
|
||||
const { container } = render(
|
||||
<ThemeProvider theme={supersetTheme}>
|
||||
<Provider store={store}>
|
||||
<SqlEditorLeftBar {...mockedProps} />
|
||||
</Provider>
|
||||
</ThemeProvider>,
|
||||
);
|
||||
const dbSelect = screen.getByRole('combobox', {
|
||||
name: 'Select database or type database name',
|
||||
});
|
||||
const schemaSelect = screen.getByRole('combobox', {
|
||||
name: 'Select schema or type schema name',
|
||||
});
|
||||
const dropdown = screen.getByText(/Select table or type table name/i);
|
||||
const abUser = screen.getByText(/ab_user/i);
|
||||
expect(dbSelect).toBeInTheDocument();
|
||||
expect(schemaSelect).toBeInTheDocument();
|
||||
expect(dropdown).toBeInTheDocument();
|
||||
expect(abUser).toBeInTheDocument();
|
||||
expect(
|
||||
container.querySelector('.ant-collapse-content-active'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should toggle the table when the header is clicked', async () => {
|
||||
const collapseMock = jest.fn();
|
||||
render(
|
||||
<ThemeProvider theme={supersetTheme}>
|
||||
<Provider store={store}>
|
||||
<SqlEditorLeftBar
|
||||
actions={{ ...mockedActions, collapseTable: collapseMock }}
|
||||
tables={[table]}
|
||||
queryEditor={defaultQueryEditor}
|
||||
database={databases}
|
||||
height={0}
|
||||
/>
|
||||
</Provider>
|
||||
</ThemeProvider>,
|
||||
);
|
||||
const header = screen.getByText(/ab_user/);
|
||||
userEvent.click(header);
|
||||
expect(collapseMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,131 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 } from 'enzyme';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
import { Provider } from 'react-redux';
|
||||
import thunk from 'redux-thunk';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import {
|
||||
SQL_EDITOR_GUTTER_HEIGHT,
|
||||
SQL_EDITOR_GUTTER_MARGIN,
|
||||
SQL_TOOLBAR_HEIGHT,
|
||||
} from 'src/SqlLab/constants';
|
||||
import AceEditorWrapper from 'src/SqlLab/components/AceEditorWrapper';
|
||||
import ConnectedSouthPane from 'src/SqlLab/components/SouthPane/state';
|
||||
import SqlEditor from 'src/SqlLab/components/SqlEditor';
|
||||
import SqlEditorLeftBar from 'src/SqlLab/components/SqlEditorLeftBar';
|
||||
import { Dropdown } from 'src/common/components';
|
||||
import {
|
||||
queryEditorSetFunctionNames,
|
||||
queryEditorSetSelectedText,
|
||||
queryEditorSetSchemaOptions,
|
||||
} from 'src/SqlLab/actions/sqlLab';
|
||||
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
|
||||
import { initialState, queries, table } from './fixtures';
|
||||
|
||||
const MOCKED_SQL_EDITOR_HEIGHT = 500;
|
||||
|
||||
fetchMock.get('glob:*/api/v1/database/*', { result: [] });
|
||||
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureStore(middlewares);
|
||||
const store = mockStore(initialState);
|
||||
|
||||
describe('SqlEditor', () => {
|
||||
const mockedProps = {
|
||||
actions: {
|
||||
queryEditorSetFunctionNames,
|
||||
queryEditorSetSelectedText,
|
||||
queryEditorSetSchemaOptions,
|
||||
addDangerToast: jest.fn(),
|
||||
},
|
||||
database: {},
|
||||
queryEditorId: initialState.sqlLab.queryEditors[0].id,
|
||||
latestQuery: queries[0],
|
||||
tables: [table],
|
||||
getHeight: () => '100px',
|
||||
editorQueries: [],
|
||||
dataPreviewQueries: [],
|
||||
defaultQueryLimit: 1000,
|
||||
maxRow: 100000,
|
||||
displayLimit: 100,
|
||||
};
|
||||
|
||||
const buildWrapper = (props = {}) =>
|
||||
mount(
|
||||
<Provider store={store}>
|
||||
<SqlEditor {...mockedProps} {...props} />
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: { theme: supersetTheme },
|
||||
},
|
||||
);
|
||||
|
||||
it('render a SqlEditorLeftBar', async () => {
|
||||
const wrapper = buildWrapper();
|
||||
await waitForComponentToPaint(wrapper);
|
||||
expect(wrapper.find(SqlEditorLeftBar)).toExist();
|
||||
});
|
||||
it('render an AceEditorWrapper', async () => {
|
||||
const wrapper = buildWrapper();
|
||||
await waitForComponentToPaint(wrapper);
|
||||
expect(wrapper.find(AceEditorWrapper)).toExist();
|
||||
});
|
||||
it('render a SouthPane', async () => {
|
||||
const wrapper = buildWrapper();
|
||||
await waitForComponentToPaint(wrapper);
|
||||
expect(wrapper.find(ConnectedSouthPane)).toExist();
|
||||
});
|
||||
// TODO eschutho convert tests to RTL
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('does not overflow the editor window', async () => {
|
||||
const wrapper = buildWrapper();
|
||||
await waitForComponentToPaint(wrapper);
|
||||
const totalSize =
|
||||
parseFloat(wrapper.find(AceEditorWrapper).props().height) +
|
||||
wrapper.find(ConnectedSouthPane).props().height +
|
||||
SQL_TOOLBAR_HEIGHT +
|
||||
SQL_EDITOR_GUTTER_MARGIN * 2 +
|
||||
SQL_EDITOR_GUTTER_HEIGHT;
|
||||
expect(totalSize).toEqual(MOCKED_SQL_EDITOR_HEIGHT);
|
||||
});
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('does not overflow the editor window after resizing', async () => {
|
||||
const wrapper = buildWrapper();
|
||||
wrapper.setState({ height: 450 });
|
||||
await waitForComponentToPaint(wrapper);
|
||||
const totalSize =
|
||||
parseFloat(wrapper.find(AceEditorWrapper).props().height) +
|
||||
wrapper.find(ConnectedSouthPane).props().height +
|
||||
SQL_TOOLBAR_HEIGHT +
|
||||
SQL_EDITOR_GUTTER_MARGIN * 2 +
|
||||
SQL_EDITOR_GUTTER_HEIGHT;
|
||||
expect(totalSize).toEqual(450);
|
||||
});
|
||||
it('render a Limit Dropdown', async () => {
|
||||
const defaultQueryLimit = 101;
|
||||
const updatedProps = { ...mockedProps, defaultQueryLimit };
|
||||
const wrapper = buildWrapper(updatedProps);
|
||||
await waitForComponentToPaint(wrapper);
|
||||
expect(wrapper.find(Dropdown)).toExist();
|
||||
});
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 sinon from 'sinon';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import TabStatusIcon from 'src/SqlLab/components/TabStatusIcon';
|
||||
|
||||
function setup() {
|
||||
const onClose = sinon.spy();
|
||||
const wrapper = shallow(
|
||||
<TabStatusIcon onClose={onClose} tabState="running" />,
|
||||
);
|
||||
return { wrapper, onClose };
|
||||
}
|
||||
|
||||
describe('TabStatusIcon', () => {
|
||||
it('renders a circle without an x when hovered', () => {
|
||||
const { wrapper } = setup();
|
||||
expect(wrapper.find('div.circle')).toExist();
|
||||
expect(wrapper.text()).toBe('');
|
||||
});
|
||||
});
|
||||
@@ -1,231 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import URI from 'urijs';
|
||||
import { Provider } from 'react-redux';
|
||||
import { shallow, mount } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
import { EditableTabs } from 'src/components/Tabs';
|
||||
import TabbedSqlEditors from 'src/SqlLab/components/TabbedSqlEditors';
|
||||
import SqlEditor from 'src/SqlLab/components/SqlEditor';
|
||||
|
||||
import { table, initialState } from './fixtures';
|
||||
|
||||
fetchMock.get('glob:*/api/v1/database/*', {});
|
||||
fetchMock.get('glob:*/savedqueryviewapi/api/get/*', {});
|
||||
fetchMock.get('glob:*/kv/*', {});
|
||||
|
||||
describe('TabbedSqlEditors', () => {
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureStore(middlewares);
|
||||
const store = mockStore(initialState);
|
||||
|
||||
const tabHistory = ['dfsadfs', 'newEditorId'];
|
||||
|
||||
const tables = [
|
||||
{ ...table, dataPreviewQueryId: 'B1-VQU1zW', queryEditorId: 'newEditorId' },
|
||||
];
|
||||
|
||||
const queryEditors = [
|
||||
{
|
||||
autorun: false,
|
||||
dbId: 1,
|
||||
id: 'newEditorId',
|
||||
latestQueryId: 'B1-VQU1zW',
|
||||
schema: null,
|
||||
selectedText: null,
|
||||
sql: 'SELECT ds...',
|
||||
title: 'Untitled Query',
|
||||
},
|
||||
];
|
||||
const queries = {
|
||||
'B1-VQU1zW': {
|
||||
id: 'B1-VQU1zW',
|
||||
sqlEditorId: 'newEditorId',
|
||||
tableName: 'ab_user',
|
||||
state: 'success',
|
||||
},
|
||||
};
|
||||
const mockedProps = {
|
||||
actions: {},
|
||||
databases: {},
|
||||
tables: [],
|
||||
queries: {},
|
||||
queryEditors: initialState.sqlLab.queryEditors,
|
||||
tabHistory: initialState.sqlLab.tabHistory,
|
||||
editorHeight: '',
|
||||
getHeight: () => '100px',
|
||||
database: {},
|
||||
defaultQueryLimit: 1000,
|
||||
maxRow: 100000,
|
||||
};
|
||||
const getWrapper = () =>
|
||||
shallow(<TabbedSqlEditors store={store} {...mockedProps} />)
|
||||
.dive()
|
||||
.dive();
|
||||
|
||||
const mountWithAct = async () =>
|
||||
act(async () => {
|
||||
mount(
|
||||
<Provider store={store}>
|
||||
<TabbedSqlEditors {...mockedProps} />
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: { theme: supersetTheme },
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
let wrapper;
|
||||
it('is valid', () => {
|
||||
expect(React.isValidElement(<TabbedSqlEditors {...mockedProps} />)).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
describe('componentDidMount', () => {
|
||||
let uriStub;
|
||||
beforeEach(() => {
|
||||
sinon.stub(window.history, 'replaceState');
|
||||
uriStub = sinon.stub(URI.prototype, 'search');
|
||||
});
|
||||
afterEach(() => {
|
||||
window.history.replaceState.restore();
|
||||
uriStub.restore();
|
||||
});
|
||||
it('should handle id', async () => {
|
||||
uriStub.returns({ id: 1 });
|
||||
await mountWithAct();
|
||||
expect(window.history.replaceState.getCall(0).args[2]).toBe(
|
||||
'/superset/sqllab',
|
||||
);
|
||||
});
|
||||
it('should handle savedQueryId', async () => {
|
||||
uriStub.returns({ savedQueryId: 1 });
|
||||
await mountWithAct();
|
||||
expect(window.history.replaceState.getCall(0).args[2]).toBe(
|
||||
'/superset/sqllab',
|
||||
);
|
||||
});
|
||||
it('should handle sql', async () => {
|
||||
uriStub.returns({ sql: 1, dbid: 1 });
|
||||
await mountWithAct();
|
||||
expect(window.history.replaceState.getCall(0).args[2]).toBe(
|
||||
'/superset/sqllab',
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('UNSAFE_componentWillReceiveProps', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = getWrapper();
|
||||
wrapper.setProps({ queryEditors, queries, tabHistory, tables });
|
||||
});
|
||||
it('should update queriesArray and dataPreviewQueries', () => {
|
||||
expect(wrapper.state().queriesArray.slice(-1)[0]).toBe(
|
||||
queries['B1-VQU1zW'],
|
||||
);
|
||||
expect(wrapper.state().dataPreviewQueries.slice(-1)[0]).toEqual(
|
||||
queries['B1-VQU1zW'],
|
||||
);
|
||||
});
|
||||
});
|
||||
it('should rename Tab', () => {
|
||||
global.prompt = () => 'new title';
|
||||
wrapper = getWrapper();
|
||||
sinon.stub(wrapper.instance().props.actions, 'queryEditorSetTitle');
|
||||
|
||||
wrapper.instance().renameTab(queryEditors[0]);
|
||||
expect(
|
||||
wrapper.instance().props.actions.queryEditorSetTitle.getCall(0).args[1],
|
||||
).toBe('new title');
|
||||
|
||||
delete global.prompt;
|
||||
});
|
||||
it('should removeQueryEditor', () => {
|
||||
wrapper = getWrapper();
|
||||
sinon.stub(wrapper.instance().props.actions, 'removeQueryEditor');
|
||||
|
||||
wrapper.instance().removeQueryEditor(queryEditors[0]);
|
||||
expect(
|
||||
wrapper.instance().props.actions.removeQueryEditor.getCall(0).args[0],
|
||||
).toBe(queryEditors[0]);
|
||||
});
|
||||
it('should add new query editor', () => {
|
||||
wrapper = getWrapper();
|
||||
sinon.stub(wrapper.instance().props.actions, 'addQueryEditor');
|
||||
|
||||
wrapper.instance().newQueryEditor();
|
||||
expect(
|
||||
wrapper.instance().props.actions.addQueryEditor.getCall(0).args[0].title,
|
||||
).toContain('Untitled Query');
|
||||
});
|
||||
it('should duplicate query editor', () => {
|
||||
wrapper = getWrapper();
|
||||
sinon.stub(wrapper.instance().props.actions, 'cloneQueryToNewTab');
|
||||
|
||||
wrapper.instance().duplicateQueryEditor(queryEditors[0]);
|
||||
expect(
|
||||
wrapper.instance().props.actions.cloneQueryToNewTab.getCall(0).args[0],
|
||||
).toBe(queryEditors[0]);
|
||||
});
|
||||
it('should handle select', () => {
|
||||
const mockEvent = {
|
||||
target: {
|
||||
getAttribute: () => null,
|
||||
},
|
||||
};
|
||||
wrapper = getWrapper();
|
||||
sinon.stub(wrapper.instance().props.actions, 'switchQueryEditor');
|
||||
|
||||
// cannot switch to current tab, switchQueryEditor never gets called
|
||||
wrapper.instance().handleSelect('dfsadfs', mockEvent);
|
||||
expect(
|
||||
wrapper.instance().props.actions.switchQueryEditor.callCount,
|
||||
).toEqual(0);
|
||||
});
|
||||
it('should handle add tab', () => {
|
||||
wrapper = getWrapper();
|
||||
sinon.spy(wrapper.instance(), 'newQueryEditor');
|
||||
|
||||
wrapper.instance().handleEdit('1', 'add');
|
||||
expect(wrapper.instance().newQueryEditor.callCount).toBe(1);
|
||||
wrapper.instance().newQueryEditor.restore();
|
||||
});
|
||||
it('should render', () => {
|
||||
wrapper = getWrapper();
|
||||
wrapper.setState({ hideLeftBar: true });
|
||||
|
||||
const firstTab = wrapper.find(EditableTabs.TabPane).first();
|
||||
expect(firstTab.props()['data-key']).toContain(
|
||||
initialState.sqlLab.queryEditors[0].id,
|
||||
);
|
||||
expect(firstTab.find(SqlEditor)).toHaveLength(1);
|
||||
});
|
||||
it('should disable new tab when offline', () => {
|
||||
wrapper = getWrapper();
|
||||
expect(wrapper.find(EditableTabs).props().hideAdd).toBe(false);
|
||||
wrapper.setProps({ offline: true });
|
||||
expect(wrapper.find(EditableTabs).props().hideAdd).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,149 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { Provider } from 'react-redux';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
import Collapse from 'src/components/Collapse';
|
||||
|
||||
import { IconTooltip } from 'src/components/IconTooltip';
|
||||
import TableElement from 'src/SqlLab/components/TableElement';
|
||||
import ColumnElement from 'src/SqlLab/components/ColumnElement';
|
||||
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
|
||||
import { mockedActions, table } from './fixtures';
|
||||
|
||||
describe('TableElement', () => {
|
||||
const mockStore = configureStore([]);
|
||||
const store = mockStore({});
|
||||
const mockedProps = {
|
||||
actions: mockedActions,
|
||||
table,
|
||||
timeout: 0,
|
||||
};
|
||||
it('renders', () => {
|
||||
expect(React.isValidElement(<TableElement />)).toBe(true);
|
||||
});
|
||||
it('renders with props', () => {
|
||||
expect(React.isValidElement(<TableElement {...mockedProps} />)).toBe(true);
|
||||
});
|
||||
it('has 5 IconTooltip elements', () => {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TableElement {...mockedProps} />
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(wrapper.find(IconTooltip)).toHaveLength(4);
|
||||
});
|
||||
it('has 14 columns', () => {
|
||||
const wrapper = shallow(<TableElement {...mockedProps} />);
|
||||
expect(wrapper.find(ColumnElement)).toHaveLength(14);
|
||||
});
|
||||
it('mounts', () => {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TableElement {...mockedProps} />
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(wrapper.find(TableElement)).toHaveLength(1);
|
||||
});
|
||||
it('fades table', async () => {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TableElement {...mockedProps} />
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(wrapper.find('[data-test="fade"]').first().props().hovered).toBe(
|
||||
false,
|
||||
);
|
||||
wrapper.find('.header-container').hostNodes().simulate('mouseEnter');
|
||||
await waitForComponentToPaint(wrapper, 300);
|
||||
expect(wrapper.find('[data-test="fade"]').first().props().hovered).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
it('sorts columns', () => {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<Collapse>
|
||||
<TableElement {...mockedProps} />
|
||||
</Collapse>
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(
|
||||
wrapper.find(IconTooltip).at(1).hasClass('fa-sort-alpha-asc'),
|
||||
).toEqual(true);
|
||||
expect(
|
||||
wrapper.find(IconTooltip).at(1).hasClass('fa-sort-numeric-asc'),
|
||||
).toEqual(false);
|
||||
wrapper.find('.header-container').hostNodes().simulate('click');
|
||||
expect(wrapper.find(ColumnElement).first().props().column.name).toBe('id');
|
||||
wrapper.find('.header-container').simulate('mouseEnter');
|
||||
wrapper.find('.sort-cols').hostNodes().simulate('click');
|
||||
expect(
|
||||
wrapper.find(IconTooltip).at(1).hasClass('fa-sort-numeric-asc'),
|
||||
).toEqual(true);
|
||||
expect(
|
||||
wrapper.find(IconTooltip).at(1).hasClass('fa-sort-alpha-asc'),
|
||||
).toEqual(false);
|
||||
expect(wrapper.find(ColumnElement).first().props().column.name).toBe(
|
||||
'active',
|
||||
);
|
||||
});
|
||||
it('removes the table', () => {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TableElement {...mockedProps} />
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
wrapper.find('.table-remove').hostNodes().simulate('click');
|
||||
expect(mockedActions.removeDataPreview.called).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,57 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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, { ReactNode } from 'react';
|
||||
import {
|
||||
render,
|
||||
fireEvent,
|
||||
getByText,
|
||||
waitFor,
|
||||
} from 'spec/helpers/testing-library';
|
||||
import brace from 'brace';
|
||||
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
||||
|
||||
import TemplateParamsEditor from 'src/SqlLab/components/TemplateParamsEditor';
|
||||
|
||||
const ThemeWrapper = ({ children }: { children: ReactNode }) => (
|
||||
<ThemeProvider theme={supersetTheme}>{children}</ThemeProvider>
|
||||
);
|
||||
|
||||
describe('TemplateParamsEditor', () => {
|
||||
it('should render with a title', () => {
|
||||
const { container } = render(
|
||||
<TemplateParamsEditor code="FOO" language="json" onChange={() => {}} />,
|
||||
{ wrapper: ThemeWrapper },
|
||||
);
|
||||
expect(container.querySelector('div[role="button"]')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should open a modal with the ace editor', async () => {
|
||||
const { container, baseElement } = render(
|
||||
<TemplateParamsEditor code="FOO" language="json" onChange={() => {}} />,
|
||||
{ wrapper: ThemeWrapper },
|
||||
);
|
||||
fireEvent.click(getByText(container, 'Parameters'));
|
||||
const spy = jest.spyOn(brace, 'acequire');
|
||||
spy.mockReturnValue({ setCompleters: () => 'foo' });
|
||||
await waitFor(() => {
|
||||
expect(baseElement.querySelector('#brace-editor')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,876 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
*/
|
||||
/* eslint no-unused-expressions: 0 */
|
||||
import sinon from 'sinon';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import shortid from 'shortid';
|
||||
import * as featureFlags from 'src/featureFlags';
|
||||
import { ADD_TOAST } from 'src/components/MessageToasts/actions';
|
||||
import * as actions from 'src/SqlLab/actions/sqlLab';
|
||||
import { defaultQueryEditor, query } from '../fixtures';
|
||||
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureMockStore(middlewares);
|
||||
|
||||
describe('async actions', () => {
|
||||
const mockBigNumber = '9223372036854775807';
|
||||
const queryEditor = {
|
||||
id: 'abcd',
|
||||
autorun: false,
|
||||
dbId: null,
|
||||
latestQueryId: null,
|
||||
selectedText: null,
|
||||
sql: 'SELECT *\nFROM\nWHERE',
|
||||
title: 'Untitled Query',
|
||||
schemaOptions: [{ value: 'main', label: 'main', title: 'main' }],
|
||||
};
|
||||
|
||||
let dispatch;
|
||||
|
||||
beforeEach(() => {
|
||||
dispatch = sinon.spy();
|
||||
});
|
||||
|
||||
afterEach(fetchMock.resetHistory);
|
||||
|
||||
const fetchQueryEndpoint = 'glob:*/superset/results/*';
|
||||
fetchMock.get(
|
||||
fetchQueryEndpoint,
|
||||
JSON.stringify({ data: mockBigNumber, query: { sqlEditorId: 'dfsadfs' } }),
|
||||
);
|
||||
|
||||
const runQueryEndpoint = 'glob:*/superset/sql_json/*';
|
||||
fetchMock.post(runQueryEndpoint, `{ "data": ${mockBigNumber} }`);
|
||||
|
||||
describe('saveQuery', () => {
|
||||
const saveQueryEndpoint = 'glob:*/savedqueryviewapi/api/create';
|
||||
fetchMock.post(saveQueryEndpoint, { results: { json: {} } });
|
||||
|
||||
const makeRequest = () => {
|
||||
const request = actions.saveQuery(query);
|
||||
return request(dispatch);
|
||||
};
|
||||
|
||||
it('posts to the correct url', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(actions.saveQuery(query)).then(() => {
|
||||
expect(fetchMock.calls(saveQueryEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('posts the correct query object', () => {
|
||||
const store = mockStore({});
|
||||
return store.dispatch(actions.saveQuery(query)).then(() => {
|
||||
const call = fetchMock.calls(saveQueryEndpoint)[0];
|
||||
const formData = call[1].body;
|
||||
Object.keys(query).forEach(key => {
|
||||
expect(formData.get(key)).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('calls 3 dispatch actions', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
return makeRequest().then(() => {
|
||||
expect(dispatch.callCount).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls QUERY_EDITOR_SAVED after making a request', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
return makeRequest().then(() => {
|
||||
expect(dispatch.args[0][0].type).toBe(actions.QUERY_EDITOR_SAVED);
|
||||
});
|
||||
});
|
||||
|
||||
it('onSave calls QUERY_EDITOR_SAVED and QUERY_EDITOR_SET_TITLE', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
const store = mockStore({});
|
||||
const expectedActionTypes = [
|
||||
actions.QUERY_EDITOR_SAVED,
|
||||
ADD_TOAST,
|
||||
actions.QUERY_EDITOR_SET_TITLE,
|
||||
];
|
||||
return store.dispatch(actions.saveQuery(query)).then(() => {
|
||||
expect(store.getActions().map(a => a.type)).toEqual(
|
||||
expectedActionTypes,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchQueryResults', () => {
|
||||
const makeRequest = () => {
|
||||
const request = actions.fetchQueryResults(query);
|
||||
return request(dispatch);
|
||||
};
|
||||
|
||||
it('makes the fetch request', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
return makeRequest().then(() => {
|
||||
expect(fetchMock.calls(fetchQueryEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls requestQueryResults', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
return makeRequest().then(() => {
|
||||
expect(dispatch.args[0][0].type).toBe(actions.REQUEST_QUERY_RESULTS);
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('parses large number result without losing precision', () =>
|
||||
makeRequest().then(() => {
|
||||
expect(fetchMock.calls(fetchQueryEndpoint)).toHaveLength(1);
|
||||
expect(dispatch.callCount).toBe(2);
|
||||
expect(dispatch.getCall(1).lastArg.results.data.toString()).toBe(
|
||||
mockBigNumber,
|
||||
);
|
||||
}));
|
||||
|
||||
it('calls querySuccess on fetch success', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
const store = mockStore({});
|
||||
const expectedActionTypes = [
|
||||
actions.REQUEST_QUERY_RESULTS,
|
||||
actions.QUERY_SUCCESS,
|
||||
];
|
||||
return store.dispatch(actions.fetchQueryResults(query)).then(() => {
|
||||
expect(store.getActions().map(a => a.type)).toEqual(
|
||||
expectedActionTypes,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls queryFailed on fetch error', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
fetchMock.get(
|
||||
fetchQueryEndpoint,
|
||||
{ throws: { message: 'error text' } },
|
||||
{ overwriteRoutes: true },
|
||||
);
|
||||
|
||||
const store = mockStore({});
|
||||
const expectedActionTypes = [
|
||||
actions.REQUEST_QUERY_RESULTS,
|
||||
actions.QUERY_FAILED,
|
||||
];
|
||||
return store.dispatch(actions.fetchQueryResults(query)).then(() => {
|
||||
expect(store.getActions().map(a => a.type)).toEqual(
|
||||
expectedActionTypes,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('runQuery', () => {
|
||||
const makeRequest = () => {
|
||||
const request = actions.runQuery(query);
|
||||
return request(dispatch);
|
||||
};
|
||||
|
||||
it('makes the fetch request', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
return makeRequest().then(() => {
|
||||
expect(fetchMock.calls(runQueryEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls startQuery', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
return makeRequest().then(() => {
|
||||
expect(dispatch.args[0][0].type).toBe(actions.START_QUERY);
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('parses large number result without losing precision', () =>
|
||||
makeRequest().then(() => {
|
||||
expect(fetchMock.calls(runQueryEndpoint)).toHaveLength(1);
|
||||
expect(dispatch.callCount).toBe(2);
|
||||
expect(dispatch.getCall(1).lastArg.results.data.toString()).toBe(
|
||||
mockBigNumber,
|
||||
);
|
||||
}));
|
||||
|
||||
it('calls querySuccess on fetch success', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
const store = mockStore({});
|
||||
const expectedActionTypes = [actions.START_QUERY, actions.QUERY_SUCCESS];
|
||||
return store.dispatch(actions.runQuery(query)).then(() => {
|
||||
expect(store.getActions().map(a => a.type)).toEqual(
|
||||
expectedActionTypes,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls queryFailed on fetch error', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
fetchMock.post(
|
||||
runQueryEndpoint,
|
||||
{ throws: { message: 'error text' } },
|
||||
{ overwriteRoutes: true },
|
||||
);
|
||||
|
||||
const store = mockStore({});
|
||||
const expectedActionTypes = [actions.START_QUERY, actions.QUERY_FAILED];
|
||||
return store.dispatch(actions.runQuery(query)).then(() => {
|
||||
expect(store.getActions().map(a => a.type)).toEqual(
|
||||
expectedActionTypes,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('reRunQuery', () => {
|
||||
let stub;
|
||||
beforeEach(() => {
|
||||
stub = sinon.stub(shortid, 'generate').returns('abcd');
|
||||
});
|
||||
afterEach(() => {
|
||||
stub.restore();
|
||||
});
|
||||
|
||||
it('creates new query with a new id', () => {
|
||||
const id = 'id';
|
||||
const state = {
|
||||
sqlLab: {
|
||||
tabHistory: [id],
|
||||
queryEditors: [{ id, title: 'Dummy query editor' }],
|
||||
},
|
||||
};
|
||||
const store = mockStore(state);
|
||||
store.dispatch(actions.reRunQuery(query));
|
||||
expect(store.getActions()[0].query.id).toEqual('abcd');
|
||||
});
|
||||
});
|
||||
|
||||
describe('postStopQuery', () => {
|
||||
const stopQueryEndpoint = 'glob:*/superset/stop_query/*';
|
||||
fetchMock.post(stopQueryEndpoint, {});
|
||||
|
||||
const makeRequest = () => {
|
||||
const request = actions.postStopQuery(query);
|
||||
return request(dispatch);
|
||||
};
|
||||
|
||||
it('makes the fetch request', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
return makeRequest().then(() => {
|
||||
expect(fetchMock.calls(stopQueryEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls stopQuery', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
return makeRequest().then(() => {
|
||||
expect(dispatch.getCall(0).args[0].type).toBe(actions.STOP_QUERY);
|
||||
});
|
||||
});
|
||||
|
||||
it('sends the correct data', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
return makeRequest().then(() => {
|
||||
const call = fetchMock.calls(stopQueryEndpoint)[0];
|
||||
expect(call[1].body.get('client_id')).toBe(query.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('cloneQueryToNewTab', () => {
|
||||
let stub;
|
||||
beforeEach(() => {
|
||||
stub = sinon.stub(shortid, 'generate').returns('abcd');
|
||||
});
|
||||
afterEach(() => {
|
||||
stub.restore();
|
||||
});
|
||||
|
||||
it('creates new query editor', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
const id = 'id';
|
||||
const state = {
|
||||
sqlLab: {
|
||||
tabHistory: [id],
|
||||
queryEditors: [{ id, title: 'Dummy query editor' }],
|
||||
},
|
||||
};
|
||||
const store = mockStore(state);
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.ADD_QUERY_EDITOR,
|
||||
queryEditor: {
|
||||
title: 'Copy of Dummy query editor',
|
||||
dbId: 1,
|
||||
schema: null,
|
||||
autorun: true,
|
||||
sql: 'SELECT * FROM something',
|
||||
queryLimit: undefined,
|
||||
maxRow: undefined,
|
||||
id: 'abcd',
|
||||
},
|
||||
},
|
||||
];
|
||||
return store
|
||||
.dispatch(actions.cloneQueryToNewTab(query, true))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('addQueryEditor', () => {
|
||||
let stub;
|
||||
beforeEach(() => {
|
||||
stub = sinon.stub(shortid, 'generate').returns('abcd');
|
||||
});
|
||||
afterEach(() => {
|
||||
stub.restore();
|
||||
});
|
||||
|
||||
it('creates new query editor', () => {
|
||||
expect.assertions(1);
|
||||
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.ADD_QUERY_EDITOR,
|
||||
queryEditor,
|
||||
},
|
||||
];
|
||||
return store
|
||||
.dispatch(actions.addQueryEditor(defaultQueryEditor))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('backend sync', () => {
|
||||
const updateTabStateEndpoint = 'glob:*/tabstateview/*';
|
||||
fetchMock.put(updateTabStateEndpoint, {});
|
||||
fetchMock.delete(updateTabStateEndpoint, {});
|
||||
fetchMock.post(updateTabStateEndpoint, JSON.stringify({ id: 1 }));
|
||||
|
||||
const updateTableSchemaEndpoint = 'glob:*/tableschemaview/*';
|
||||
fetchMock.put(updateTableSchemaEndpoint, {});
|
||||
fetchMock.delete(updateTableSchemaEndpoint, {});
|
||||
fetchMock.post(updateTableSchemaEndpoint, JSON.stringify({ id: 1 }));
|
||||
|
||||
const getTableMetadataEndpoint = 'glob:*/api/v1/database/*';
|
||||
fetchMock.get(getTableMetadataEndpoint, {});
|
||||
const getExtraTableMetadataEndpoint =
|
||||
'glob:*/superset/extra_table_metadata/*';
|
||||
fetchMock.get(getExtraTableMetadataEndpoint, {});
|
||||
|
||||
let isFeatureEnabledMock;
|
||||
|
||||
beforeEach(() => {
|
||||
isFeatureEnabledMock = jest
|
||||
.spyOn(featureFlags, 'isFeatureEnabled')
|
||||
.mockImplementation(
|
||||
feature => feature === 'SQLLAB_BACKEND_PERSISTENCE',
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
isFeatureEnabledMock.mockRestore();
|
||||
});
|
||||
|
||||
afterEach(fetchMock.resetHistory);
|
||||
|
||||
describe('querySuccess', () => {
|
||||
it('updates the tab state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const store = mockStore({});
|
||||
const results = { query: { sqlEditorId: 'abcd' } };
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.QUERY_SUCCESS,
|
||||
query,
|
||||
results,
|
||||
},
|
||||
];
|
||||
return store.dispatch(actions.querySuccess(query, results)).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchQueryResults', () => {
|
||||
it('updates the tab state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const results = {
|
||||
data: mockBigNumber,
|
||||
query: { sqlEditorId: 'abcd' },
|
||||
query_id: 'efgh',
|
||||
};
|
||||
fetchMock.get(fetchQueryEndpoint, JSON.stringify(results), {
|
||||
overwriteRoutes: true,
|
||||
});
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.REQUEST_QUERY_RESULTS,
|
||||
query,
|
||||
},
|
||||
// missing below
|
||||
{
|
||||
type: actions.QUERY_SUCCESS,
|
||||
query,
|
||||
results,
|
||||
},
|
||||
];
|
||||
return store.dispatch(actions.fetchQueryResults(query)).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('addQueryEditor', () => {
|
||||
it('updates the tab state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.ADD_QUERY_EDITOR,
|
||||
queryEditor: { ...queryEditor, id: '1' },
|
||||
},
|
||||
];
|
||||
return store.dispatch(actions.addQueryEditor(queryEditor)).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setActiveQueryEditor', () => {
|
||||
it('updates the tab state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.SET_ACTIVE_QUERY_EDITOR,
|
||||
queryEditor,
|
||||
},
|
||||
];
|
||||
return store
|
||||
.dispatch(actions.setActiveQueryEditor(queryEditor))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeQueryEditor', () => {
|
||||
it('updates the tab state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.REMOVE_QUERY_EDITOR,
|
||||
queryEditor,
|
||||
},
|
||||
];
|
||||
return store
|
||||
.dispatch(actions.removeQueryEditor(queryEditor))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryEditorSetDb', () => {
|
||||
it('updates the tab state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const dbId = 42;
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.QUERY_EDITOR_SETDB,
|
||||
queryEditor,
|
||||
dbId,
|
||||
},
|
||||
];
|
||||
return store
|
||||
.dispatch(actions.queryEditorSetDb(queryEditor, dbId))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryEditorSetSchema', () => {
|
||||
it('updates the tab state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const schema = 'schema';
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.QUERY_EDITOR_SET_SCHEMA,
|
||||
queryEditor,
|
||||
schema,
|
||||
},
|
||||
];
|
||||
return store
|
||||
.dispatch(actions.queryEditorSetSchema(queryEditor, schema))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryEditorSetAutorun', () => {
|
||||
it('updates the tab state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const autorun = true;
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.QUERY_EDITOR_SET_AUTORUN,
|
||||
queryEditor,
|
||||
autorun,
|
||||
},
|
||||
];
|
||||
return store
|
||||
.dispatch(actions.queryEditorSetAutorun(queryEditor, autorun))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryEditorSetTitle', () => {
|
||||
it('updates the tab state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const title = 'title';
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.QUERY_EDITOR_SET_TITLE,
|
||||
queryEditor,
|
||||
title,
|
||||
},
|
||||
];
|
||||
return store
|
||||
.dispatch(actions.queryEditorSetTitle(queryEditor, title))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryEditorSetSql', () => {
|
||||
const sql = 'SELECT * ';
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.QUERY_EDITOR_SET_SQL,
|
||||
queryEditor,
|
||||
sql,
|
||||
},
|
||||
];
|
||||
describe('with backend persistence flag on', () => {
|
||||
it('updates the tab state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const store = mockStore({});
|
||||
|
||||
return store
|
||||
.dispatch(actions.queryEditorSetSql(queryEditor, sql))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('with backend persistence flag off', () => {
|
||||
it('does not update the tab state in the backend', () => {
|
||||
const backendPersistenceOffMock = jest
|
||||
.spyOn(featureFlags, 'isFeatureEnabled')
|
||||
.mockImplementation(
|
||||
feature => !(feature === 'SQLLAB_BACKEND_PERSISTENCE'),
|
||||
);
|
||||
|
||||
const store = mockStore({});
|
||||
|
||||
store.dispatch(actions.queryEditorSetSql(queryEditor, sql));
|
||||
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(0);
|
||||
backendPersistenceOffMock.mockRestore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryEditorSetQueryLimit', () => {
|
||||
it('updates the tab state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const queryLimit = 10;
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.QUERY_EDITOR_SET_QUERY_LIMIT,
|
||||
queryEditor,
|
||||
queryLimit,
|
||||
},
|
||||
];
|
||||
return store
|
||||
.dispatch(actions.queryEditorSetQueryLimit(queryEditor, queryLimit))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryEditorSetTemplateParams', () => {
|
||||
it('updates the tab state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const templateParams = '{"foo": "bar"}';
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.QUERY_EDITOR_SET_TEMPLATE_PARAMS,
|
||||
queryEditor,
|
||||
templateParams,
|
||||
},
|
||||
];
|
||||
return store
|
||||
.dispatch(
|
||||
actions.queryEditorSetTemplateParams(queryEditor, templateParams),
|
||||
)
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('addTable', () => {
|
||||
it('updates the table schema state in the backend', () => {
|
||||
expect.assertions(5);
|
||||
|
||||
const results = {
|
||||
data: mockBigNumber,
|
||||
query: { sqlEditorId: 'null' },
|
||||
query_id: 'efgh',
|
||||
};
|
||||
fetchMock.post(runQueryEndpoint, JSON.stringify(results), {
|
||||
overwriteRoutes: true,
|
||||
});
|
||||
|
||||
const tableName = 'table';
|
||||
const schemaName = 'schema';
|
||||
const store = mockStore({});
|
||||
const expectedActionTypes = [
|
||||
actions.MERGE_TABLE, // addTable
|
||||
actions.MERGE_TABLE, // getTableMetadata
|
||||
actions.START_QUERY, // runQuery (data preview)
|
||||
actions.MERGE_TABLE, // getTableExtendedMetadata
|
||||
actions.QUERY_SUCCESS, // querySuccess
|
||||
actions.MERGE_TABLE, // addTable
|
||||
];
|
||||
return store
|
||||
.dispatch(actions.addTable(query, tableName, schemaName))
|
||||
.then(() => {
|
||||
expect(store.getActions().map(a => a.type)).toEqual(
|
||||
expectedActionTypes,
|
||||
);
|
||||
expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1);
|
||||
expect(fetchMock.calls(getTableMetadataEndpoint)).toHaveLength(1);
|
||||
expect(fetchMock.calls(getExtraTableMetadataEndpoint)).toHaveLength(
|
||||
1,
|
||||
);
|
||||
|
||||
// tab state is not updated, since the query is a data preview
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('expandTable', () => {
|
||||
it('updates the table schema state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const table = { id: 1 };
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.EXPAND_TABLE,
|
||||
table,
|
||||
},
|
||||
];
|
||||
return store.dispatch(actions.expandTable(table)).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('collapseTable', () => {
|
||||
it('updates the table schema state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const table = { id: 1 };
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.COLLAPSE_TABLE,
|
||||
table,
|
||||
},
|
||||
];
|
||||
return store.dispatch(actions.collapseTable(table)).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeTable', () => {
|
||||
it('updates the table schema state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const table = { id: 1 };
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.REMOVE_TABLE,
|
||||
table,
|
||||
},
|
||||
];
|
||||
return store.dispatch(actions.removeTable(table)).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('migrateQueryEditorFromLocalStorage', () => {
|
||||
it('updates the tab state in the backend', () => {
|
||||
expect.assertions(3);
|
||||
|
||||
const results = {
|
||||
data: mockBigNumber,
|
||||
query: { sqlEditorId: 'null' },
|
||||
query_id: 'efgh',
|
||||
};
|
||||
fetchMock.post(runQueryEndpoint, JSON.stringify(results), {
|
||||
overwriteRoutes: true,
|
||||
});
|
||||
|
||||
const tables = [
|
||||
{ id: 'one', dataPreviewQueryId: 'previewOne' },
|
||||
{ id: 'two', dataPreviewQueryId: 'previewTwo' },
|
||||
];
|
||||
const queries = [
|
||||
{ ...query, id: 'previewOne' },
|
||||
{ ...query, id: 'previewTwo' },
|
||||
];
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.MIGRATE_QUERY_EDITOR,
|
||||
oldQueryEditor: queryEditor,
|
||||
// new qe has a different id
|
||||
newQueryEditor: { ...queryEditor, id: '1' },
|
||||
},
|
||||
{
|
||||
type: actions.MIGRATE_TAB_HISTORY,
|
||||
newId: '1',
|
||||
oldId: 'abcd',
|
||||
},
|
||||
{
|
||||
type: actions.MIGRATE_TABLE,
|
||||
oldTable: tables[0],
|
||||
// new table has a different id and points to new query editor
|
||||
newTable: { ...tables[0], id: 1, queryEditorId: '1' },
|
||||
},
|
||||
{
|
||||
type: actions.MIGRATE_TABLE,
|
||||
oldTable: tables[1],
|
||||
// new table has a different id and points to new query editor
|
||||
newTable: { ...tables[1], id: 1, queryEditorId: '1' },
|
||||
},
|
||||
{
|
||||
type: actions.MIGRATE_QUERY,
|
||||
queryId: 'previewOne',
|
||||
queryEditorId: '1',
|
||||
},
|
||||
{
|
||||
type: actions.MIGRATE_QUERY,
|
||||
queryId: 'previewTwo',
|
||||
queryEditorId: '1',
|
||||
},
|
||||
];
|
||||
return store
|
||||
.dispatch(
|
||||
actions.migrateQueryEditorFromLocalStorage(
|
||||
queryEditor,
|
||||
tables,
|
||||
queries,
|
||||
),
|
||||
)
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(3);
|
||||
|
||||
// query editor has 2 tables loaded in the schema viewer
|
||||
expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,574 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 sinon from 'sinon';
|
||||
import * as actions from 'src/SqlLab/actions/sqlLab';
|
||||
import { ColumnKeyTypeType } from 'src/SqlLab/components/ColumnElement';
|
||||
|
||||
export const mockedActions = sinon.stub({ ...actions });
|
||||
|
||||
export const alert = { bsStyle: 'danger', msg: 'Ooops', id: 'lksvmcx32' };
|
||||
export const table = {
|
||||
dbId: 1,
|
||||
selectStar: 'SELECT * FROM ab_user',
|
||||
queryEditorId: 'rJ-KP47a',
|
||||
schema: 'superset',
|
||||
name: 'ab_user',
|
||||
id: 'r11Vgt60',
|
||||
dataPreviewQueryId: null,
|
||||
partitions: {
|
||||
cols: ['username'],
|
||||
latest: 'bob',
|
||||
partitionQuery: 'SHOW PARTITIONS FROM ab_user',
|
||||
},
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
column_names: ['username'],
|
||||
type: 'UNIQUE',
|
||||
name: 'username',
|
||||
},
|
||||
{
|
||||
unique: true,
|
||||
column_names: ['email'],
|
||||
type: 'UNIQUE',
|
||||
name: 'email',
|
||||
},
|
||||
{
|
||||
unique: false,
|
||||
column_names: ['created_by_fk'],
|
||||
name: 'created_by_fk',
|
||||
},
|
||||
{
|
||||
unique: false,
|
||||
column_names: ['changed_by_fk'],
|
||||
name: 'changed_by_fk',
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
indexed: false,
|
||||
longType: 'INTEGER(11)',
|
||||
type: 'INTEGER',
|
||||
name: 'id',
|
||||
keys: [
|
||||
{
|
||||
column_names: ['id'],
|
||||
type: 'pk' as ColumnKeyTypeType,
|
||||
name: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
longType: 'VARCHAR(64)',
|
||||
type: 'VARCHAR',
|
||||
name: 'first_name',
|
||||
keys: [
|
||||
{
|
||||
column_names: ['first_name'],
|
||||
name: 'slices_ibfk_1',
|
||||
referred_columns: ['id'],
|
||||
referred_table: 'datasources',
|
||||
type: 'fk' as ColumnKeyTypeType,
|
||||
referred_schema: 'carapal',
|
||||
options: {},
|
||||
},
|
||||
{
|
||||
unique: false,
|
||||
column_names: ['druid_datasource_id'],
|
||||
type: 'index' as ColumnKeyTypeType,
|
||||
name: 'druid_datasource_id',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
longType: 'VARCHAR(64)',
|
||||
type: 'VARCHAR',
|
||||
name: 'last_name',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
longType: 'VARCHAR(64)',
|
||||
type: 'VARCHAR',
|
||||
name: 'username',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
longType: 'VARCHAR(256)',
|
||||
type: 'VARCHAR',
|
||||
name: 'password',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
longType: 'TINYINT(1)',
|
||||
type: 'TINYINT',
|
||||
name: 'active',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
longType: 'VARCHAR(64)',
|
||||
type: 'VARCHAR',
|
||||
name: 'email',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
longType: 'DATETIME',
|
||||
type: 'DATETIME',
|
||||
name: 'last_login',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
longType: 'INTEGER(11)',
|
||||
type: 'INTEGER',
|
||||
name: 'login_count',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
longType: 'INTEGER(11)',
|
||||
type: 'INTEGER',
|
||||
name: 'fail_login_count',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
longType: 'DATETIME',
|
||||
type: 'DATETIME',
|
||||
name: 'created_on',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
longType: 'DATETIME',
|
||||
type: 'DATETIME',
|
||||
name: 'changed_on',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
longType: 'INTEGER(11)',
|
||||
type: 'INTEGER',
|
||||
name: 'created_by_fk',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
longType: 'INTEGER(11)',
|
||||
type: 'INTEGER',
|
||||
name: 'changed_by_fk',
|
||||
},
|
||||
],
|
||||
expanded: true,
|
||||
};
|
||||
|
||||
export const defaultQueryEditor = {
|
||||
id: 'dfsadfs',
|
||||
autorun: false,
|
||||
dbId: null,
|
||||
latestQueryId: null,
|
||||
selectedText: null,
|
||||
sql: 'SELECT *\nFROM\nWHERE',
|
||||
title: 'Untitled Query',
|
||||
schemaOptions: [
|
||||
{
|
||||
value: 'main',
|
||||
label: 'main',
|
||||
title: 'main',
|
||||
},
|
||||
],
|
||||
};
|
||||
export const queries = [
|
||||
{
|
||||
dbId: 1,
|
||||
sql: 'SELECT * FROM superset.slices',
|
||||
sqlEditorId: 'SJ8YO72R',
|
||||
tab: 'Demo',
|
||||
runAsync: false,
|
||||
ctas: false,
|
||||
cached: false,
|
||||
id: 'BkA1CLrJg',
|
||||
progress: 100,
|
||||
startDttm: 1476910566092.96,
|
||||
state: 'success',
|
||||
changedOn: 1476910566000,
|
||||
tempTable: null,
|
||||
userId: 1,
|
||||
executedSql: null,
|
||||
changed_on: '2016-10-19T20:56:06',
|
||||
rows: 42,
|
||||
queryLimit: 100,
|
||||
endDttm: 1476910566798,
|
||||
limit_reached: false,
|
||||
schema: 'test_schema',
|
||||
errorMessage: null,
|
||||
db: 'main',
|
||||
user: 'admin',
|
||||
limit: 1000,
|
||||
serverId: 141,
|
||||
resultsKey: null,
|
||||
results: {
|
||||
columns: [
|
||||
{
|
||||
is_date: true,
|
||||
name: 'ds',
|
||||
type: 'STRING',
|
||||
},
|
||||
{
|
||||
is_date: false,
|
||||
name: 'gender',
|
||||
type: 'STRING',
|
||||
},
|
||||
],
|
||||
selected_columns: [
|
||||
{
|
||||
is_date: true,
|
||||
name: 'ds',
|
||||
type: 'STRING',
|
||||
},
|
||||
{
|
||||
is_date: false,
|
||||
name: 'gender',
|
||||
type: 'STRING',
|
||||
},
|
||||
],
|
||||
data: [
|
||||
{ col1: 0, col2: 1 },
|
||||
{ col1: 2, col2: 3 },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
dbId: 1,
|
||||
sql: 'SELECT *FROM superset.slices',
|
||||
sqlEditorId: 'SJ8YO72R',
|
||||
tab: 'Demo',
|
||||
runAsync: true,
|
||||
ctas: false,
|
||||
cached: false,
|
||||
id: 'S1zeAISkx',
|
||||
progress: 100,
|
||||
startDttm: 1476910570802.2,
|
||||
state: 'success',
|
||||
changedOn: 1476910572000,
|
||||
tempTable: null,
|
||||
userId: 1,
|
||||
executedSql:
|
||||
'SELECT * \nFROM (SELECT created_on, changed_on, id, slice_name, ' +
|
||||
'druid_datasource_id, table_id, datasource_type, datasource_name, ' +
|
||||
'viz_type, params, created_by_fk, changed_by_fk, description, ' +
|
||||
'cache_timeout, perm\nFROM superset.slices) AS inner_qry \n LIMIT 1000',
|
||||
changed_on: '2016-10-19T20:56:12',
|
||||
rows: 42,
|
||||
endDttm: 1476910579693,
|
||||
limit_reached: false,
|
||||
schema: null,
|
||||
errorMessage: null,
|
||||
db: 'main',
|
||||
user: 'admin',
|
||||
limit: 1000,
|
||||
serverId: 142,
|
||||
resultsKey: '417149f4-cd27-4f80-91f3-c45c871003f7',
|
||||
results: null,
|
||||
},
|
||||
];
|
||||
export const queryWithNoQueryLimit = {
|
||||
dbId: 1,
|
||||
sql: 'SELECT * FROM superset.slices',
|
||||
sqlEditorId: 'SJ8YO72R',
|
||||
tab: 'Demo',
|
||||
runAsync: false,
|
||||
ctas: false,
|
||||
cached: false,
|
||||
id: 'BkA1CLrJg',
|
||||
progress: 100,
|
||||
startDttm: 1476910566092.96,
|
||||
state: 'success',
|
||||
changedOn: 1476910566000,
|
||||
tempTable: null,
|
||||
userId: 1,
|
||||
executedSql: null,
|
||||
changed_on: '2016-10-19T20:56:06',
|
||||
rows: 42,
|
||||
endDttm: 1476910566798,
|
||||
limit_reached: false,
|
||||
schema: 'test_schema',
|
||||
errorMessage: null,
|
||||
db: 'main',
|
||||
user: 'admin',
|
||||
limit: 1000,
|
||||
serverId: 141,
|
||||
resultsKey: null,
|
||||
results: {
|
||||
columns: [
|
||||
{
|
||||
is_date: true,
|
||||
name: 'ds',
|
||||
type: 'STRING',
|
||||
},
|
||||
{
|
||||
is_date: false,
|
||||
name: 'gender',
|
||||
type: 'STRING',
|
||||
},
|
||||
],
|
||||
selected_columns: [
|
||||
{
|
||||
is_date: true,
|
||||
name: 'ds',
|
||||
type: 'STRING',
|
||||
},
|
||||
{
|
||||
is_date: false,
|
||||
name: 'gender',
|
||||
type: 'STRING',
|
||||
},
|
||||
],
|
||||
data: [
|
||||
{ col1: 0, col2: 1 },
|
||||
{ col1: 2, col2: 3 },
|
||||
],
|
||||
query: {
|
||||
limit: 100,
|
||||
},
|
||||
},
|
||||
};
|
||||
export const queryWithBadColumns = {
|
||||
...queries[0],
|
||||
results: {
|
||||
data: queries[0].results?.data,
|
||||
selected_columns: [
|
||||
{
|
||||
is_date: true,
|
||||
name: 'COUNT(*)',
|
||||
type: 'STRING',
|
||||
},
|
||||
{
|
||||
is_date: false,
|
||||
name: 'this_col_is_ok',
|
||||
type: 'STRING',
|
||||
},
|
||||
{
|
||||
is_date: false,
|
||||
name: 'a',
|
||||
type: 'STRING',
|
||||
},
|
||||
{
|
||||
is_date: false,
|
||||
name: '1',
|
||||
type: 'STRING',
|
||||
},
|
||||
{
|
||||
is_date: false,
|
||||
name: '123',
|
||||
type: 'STRING',
|
||||
},
|
||||
{
|
||||
is_date: false,
|
||||
name: 'CASE WHEN 1=1 THEN 1 ELSE 0 END',
|
||||
type: 'STRING',
|
||||
},
|
||||
{
|
||||
is_date: true,
|
||||
name: '_TIMESTAMP',
|
||||
type: 'TIMESTAMP',
|
||||
},
|
||||
{
|
||||
is_date: true,
|
||||
name: '__TIME',
|
||||
type: 'TIMESTAMP',
|
||||
},
|
||||
{
|
||||
is_date: false,
|
||||
name: 'my_dupe_col__2',
|
||||
type: 'STRING',
|
||||
},
|
||||
{
|
||||
is_date: true,
|
||||
name: '__timestamp',
|
||||
type: 'TIMESTAMP',
|
||||
},
|
||||
{
|
||||
is_date: true,
|
||||
name: '__TIMESTAMP',
|
||||
type: 'TIMESTAMP',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
export const databases = {
|
||||
result: [
|
||||
{
|
||||
allow_ctas: true,
|
||||
allow_dml: true,
|
||||
allow_run_async: false,
|
||||
database_name: 'main',
|
||||
expose_in_sqllab: true,
|
||||
force_ctas_schema: '',
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
allow_ctas: true,
|
||||
allow_dml: false,
|
||||
allow_run_async: true,
|
||||
database_name: 'Presto - Gold',
|
||||
expose_in_sqllab: true,
|
||||
force_ctas_schema: 'tmp',
|
||||
id: 208,
|
||||
},
|
||||
],
|
||||
};
|
||||
export const tables = {
|
||||
options: [
|
||||
{
|
||||
value: 'birth_names',
|
||||
schema: 'main',
|
||||
label: 'birth_names',
|
||||
title: 'birth_names',
|
||||
},
|
||||
{
|
||||
value: 'energy_usage',
|
||||
schema: 'main',
|
||||
label: 'energy_usage',
|
||||
title: 'energy_usage',
|
||||
},
|
||||
{
|
||||
value: 'wb_health_population',
|
||||
schema: 'main',
|
||||
label: 'wb_health_population',
|
||||
title: 'wb_health_population',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const stoppedQuery = {
|
||||
dbId: 1,
|
||||
cached: false,
|
||||
ctas: false,
|
||||
id: 'ryhMUZCGb',
|
||||
progress: 0,
|
||||
results: [],
|
||||
runAsync: false,
|
||||
schema: 'main',
|
||||
sql: 'SELECT ...',
|
||||
sqlEditorId: 'rJaf5u9WZ',
|
||||
startDttm: 1497400851936,
|
||||
state: 'stopped',
|
||||
tab: 'Untitled Query 2',
|
||||
tempTable: '',
|
||||
};
|
||||
|
||||
export const failedQueryWithErrorMessage = {
|
||||
dbId: 1,
|
||||
cached: false,
|
||||
ctas: false,
|
||||
errorMessage: 'Something went wrong',
|
||||
id: 'ryhMUZCGb',
|
||||
progress: 0,
|
||||
results: [],
|
||||
runAsync: false,
|
||||
schema: 'main',
|
||||
sql: 'SELECT ...',
|
||||
sqlEditorId: 'rJaf5u9WZ',
|
||||
startDttm: 1497400851936,
|
||||
state: 'failed',
|
||||
tab: 'Untitled Query 2',
|
||||
tempTable: '',
|
||||
};
|
||||
|
||||
export const failedQueryWithErrors = {
|
||||
dbId: 1,
|
||||
cached: false,
|
||||
ctas: false,
|
||||
errors: [
|
||||
{
|
||||
message: 'Something went wrong',
|
||||
error_type: 'TEST_ERROR',
|
||||
level: 'error',
|
||||
extra: null,
|
||||
},
|
||||
],
|
||||
id: 'ryhMUZCGb',
|
||||
progress: 0,
|
||||
results: [],
|
||||
runAsync: false,
|
||||
schema: 'main',
|
||||
sql: 'SELECT ...',
|
||||
sqlEditorId: 'rJaf5u9WZ',
|
||||
startDttm: 1497400851936,
|
||||
state: 'failed',
|
||||
tab: 'Untitled Query 2',
|
||||
tempTable: '',
|
||||
};
|
||||
|
||||
export const runningQuery = {
|
||||
dbId: 1,
|
||||
cached: false,
|
||||
ctas: false,
|
||||
id: 'ryhMUZCGb',
|
||||
progress: 90,
|
||||
state: 'running',
|
||||
startDttm: Date.now() - 500,
|
||||
};
|
||||
export const cachedQuery = { ...queries[0], cached: true };
|
||||
|
||||
export const user = {
|
||||
createdOn: '2021-04-27T18:12:38.952304',
|
||||
email: 'admin',
|
||||
firstName: 'admin',
|
||||
isActive: true,
|
||||
lastName: 'admin',
|
||||
permissions: {},
|
||||
roles: { Admin: Array(173) },
|
||||
userId: 1,
|
||||
username: 'admin',
|
||||
};
|
||||
|
||||
export const initialState = {
|
||||
sqlLab: {
|
||||
offline: false,
|
||||
alerts: [],
|
||||
queries: {},
|
||||
databases: {},
|
||||
queryEditors: [defaultQueryEditor],
|
||||
tabHistory: [defaultQueryEditor.id],
|
||||
tables: [],
|
||||
workspaceQueries: [],
|
||||
queriesLastUpdate: 0,
|
||||
activeSouthPaneTab: 'Results',
|
||||
user: { user },
|
||||
},
|
||||
messageToasts: [],
|
||||
common: {
|
||||
conf: {
|
||||
DEFAULT_SQLLAB_LIMIT: 1000,
|
||||
SQL_MAX_ROW: 100000,
|
||||
DISPLAY_MAX_ROW: 100,
|
||||
SQLALCHEMY_DOCS_URL: 'test_SQLALCHEMY_DOCS_URL',
|
||||
SQLALCHEMY_DISPLAY_TEXT: 'test_SQLALCHEMY_DISPLAY_TEXT',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const query = {
|
||||
id: 'clientId2353',
|
||||
dbId: 1,
|
||||
sql: 'SELECT * FROM something',
|
||||
sqlEditorId: defaultQueryEditor.id,
|
||||
tab: 'unimportant',
|
||||
tempTable: null,
|
||||
runAsync: false,
|
||||
ctas: false,
|
||||
cached: false,
|
||||
};
|
||||
@@ -1,252 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 sqlLabReducer from 'src/SqlLab/reducers/sqlLab';
|
||||
import * as actions from 'src/SqlLab/actions/sqlLab';
|
||||
import { now } from 'src/modules/dates';
|
||||
|
||||
import { table, initialState as mockState } from '../fixtures';
|
||||
|
||||
const initialState = mockState.sqlLab;
|
||||
|
||||
describe('sqlLabReducer', () => {
|
||||
describe('Query editors actions', () => {
|
||||
let newState;
|
||||
let defaultQueryEditor;
|
||||
let qe;
|
||||
beforeEach(() => {
|
||||
newState = { ...initialState };
|
||||
defaultQueryEditor = newState.queryEditors[0];
|
||||
const action = {
|
||||
type: actions.ADD_QUERY_EDITOR,
|
||||
queryEditor: { ...initialState.queryEditors[0], id: 'abcd' },
|
||||
};
|
||||
newState = sqlLabReducer(newState, action);
|
||||
qe = newState.queryEditors.find(e => e.id === 'abcd');
|
||||
});
|
||||
it('should add a query editor', () => {
|
||||
expect(newState.queryEditors).toHaveLength(2);
|
||||
});
|
||||
it('should remove a query editor', () => {
|
||||
expect(newState.queryEditors).toHaveLength(2);
|
||||
const action = {
|
||||
type: actions.REMOVE_QUERY_EDITOR,
|
||||
queryEditor: qe,
|
||||
};
|
||||
newState = sqlLabReducer(newState, action);
|
||||
expect(newState.queryEditors).toHaveLength(1);
|
||||
});
|
||||
it('should set q query editor active', () => {
|
||||
const addQueryEditorAction = {
|
||||
type: actions.ADD_QUERY_EDITOR,
|
||||
queryEditor: { ...initialState.queryEditors[0], id: 'abcd' },
|
||||
};
|
||||
newState = sqlLabReducer(newState, addQueryEditorAction);
|
||||
const setActiveQueryEditorAction = {
|
||||
type: actions.SET_ACTIVE_QUERY_EDITOR,
|
||||
queryEditor: defaultQueryEditor,
|
||||
};
|
||||
newState = sqlLabReducer(newState, setActiveQueryEditorAction);
|
||||
expect(newState.tabHistory[newState.tabHistory.length - 1]).toBe(
|
||||
defaultQueryEditor.id,
|
||||
);
|
||||
});
|
||||
it('should not fail while setting DB', () => {
|
||||
const dbId = 9;
|
||||
const action = {
|
||||
type: actions.QUERY_EDITOR_SETDB,
|
||||
queryEditor: qe,
|
||||
dbId,
|
||||
};
|
||||
newState = sqlLabReducer(newState, action);
|
||||
expect(newState.queryEditors[1].dbId).toBe(dbId);
|
||||
});
|
||||
it('should not fail while setting schema', () => {
|
||||
const schema = 'foo';
|
||||
const action = {
|
||||
type: actions.QUERY_EDITOR_SET_SCHEMA,
|
||||
queryEditor: qe,
|
||||
schema,
|
||||
};
|
||||
newState = sqlLabReducer(newState, action);
|
||||
expect(newState.queryEditors[1].schema).toBe(schema);
|
||||
});
|
||||
it('should not fail while setting autorun', () => {
|
||||
const action = {
|
||||
type: actions.QUERY_EDITOR_SET_AUTORUN,
|
||||
queryEditor: qe,
|
||||
};
|
||||
newState = sqlLabReducer(newState, { ...action, autorun: false });
|
||||
expect(newState.queryEditors[1].autorun).toBe(false);
|
||||
newState = sqlLabReducer(newState, { ...action, autorun: true });
|
||||
expect(newState.queryEditors[1].autorun).toBe(true);
|
||||
});
|
||||
it('should not fail while setting title', () => {
|
||||
const title = 'a new title';
|
||||
const action = {
|
||||
type: actions.QUERY_EDITOR_SET_TITLE,
|
||||
queryEditor: qe,
|
||||
title,
|
||||
};
|
||||
newState = sqlLabReducer(newState, action);
|
||||
expect(newState.queryEditors[1].title).toBe(title);
|
||||
});
|
||||
it('should not fail while setting Sql', () => {
|
||||
const sql = 'SELECT nothing from dev_null';
|
||||
const action = {
|
||||
type: actions.QUERY_EDITOR_SET_SQL,
|
||||
queryEditor: qe,
|
||||
sql,
|
||||
};
|
||||
newState = sqlLabReducer(newState, action);
|
||||
expect(newState.queryEditors[1].sql).toBe(sql);
|
||||
});
|
||||
it('should not fail while setting queryLimit', () => {
|
||||
const queryLimit = 101;
|
||||
const action = {
|
||||
type: actions.QUERY_EDITOR_SET_QUERY_LIMIT,
|
||||
queryEditor: qe,
|
||||
queryLimit,
|
||||
};
|
||||
newState = sqlLabReducer(newState, action);
|
||||
expect(newState.queryEditors[1].queryLimit).toEqual(queryLimit);
|
||||
});
|
||||
it('should set selectedText', () => {
|
||||
const selectedText = 'TEST';
|
||||
const action = {
|
||||
type: actions.QUERY_EDITOR_SET_SELECTED_TEXT,
|
||||
queryEditor: newState.queryEditors[0],
|
||||
sql: selectedText,
|
||||
};
|
||||
expect(newState.queryEditors[0].selectedText).toBeNull();
|
||||
newState = sqlLabReducer(newState, action);
|
||||
expect(newState.queryEditors[0].selectedText).toBe(selectedText);
|
||||
});
|
||||
});
|
||||
describe('Tables', () => {
|
||||
let newState;
|
||||
let newTable;
|
||||
beforeEach(() => {
|
||||
newTable = { ...table };
|
||||
const action = {
|
||||
type: actions.MERGE_TABLE,
|
||||
table: newTable,
|
||||
};
|
||||
newState = sqlLabReducer(initialState, action);
|
||||
newTable = newState.tables[0];
|
||||
});
|
||||
it('should add a table', () => {
|
||||
// Testing that beforeEach actually added the table
|
||||
expect(newState.tables).toHaveLength(1);
|
||||
expect(newState.tables[0].expanded).toBe(true);
|
||||
});
|
||||
it('should merge the table attributes', () => {
|
||||
// Merging the extra attribute
|
||||
newTable.extra = true;
|
||||
const action = {
|
||||
type: actions.MERGE_TABLE,
|
||||
table: newTable,
|
||||
};
|
||||
newState = sqlLabReducer(newState, action);
|
||||
expect(newState.tables).toHaveLength(1);
|
||||
expect(newState.tables[0].extra).toBe(true);
|
||||
});
|
||||
it('should expand and collapse a table', () => {
|
||||
const collapseTableAction = {
|
||||
type: actions.COLLAPSE_TABLE,
|
||||
table: newTable,
|
||||
};
|
||||
newState = sqlLabReducer(newState, collapseTableAction);
|
||||
expect(newState.tables[0].expanded).toBe(false);
|
||||
const expandTableAction = {
|
||||
type: actions.EXPAND_TABLE,
|
||||
table: newTable,
|
||||
};
|
||||
newState = sqlLabReducer(newState, expandTableAction);
|
||||
expect(newState.tables[0].expanded).toBe(true);
|
||||
});
|
||||
it('should remove a table', () => {
|
||||
const action = {
|
||||
type: actions.REMOVE_TABLE,
|
||||
table: newTable,
|
||||
};
|
||||
newState = sqlLabReducer(newState, action);
|
||||
expect(newState.tables).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
describe('Run Query', () => {
|
||||
let newState;
|
||||
let query;
|
||||
beforeEach(() => {
|
||||
newState = { ...initialState };
|
||||
query = {
|
||||
id: 'abcd',
|
||||
progress: 0,
|
||||
startDttm: now(),
|
||||
state: 'running',
|
||||
cached: false,
|
||||
sqlEditorId: 'dfsadfs',
|
||||
};
|
||||
});
|
||||
it('should start a query', () => {
|
||||
const action = {
|
||||
type: actions.START_QUERY,
|
||||
query: {
|
||||
id: 'abcd',
|
||||
progress: 0,
|
||||
startDttm: now(),
|
||||
state: 'running',
|
||||
cached: false,
|
||||
sqlEditorId: 'dfsadfs',
|
||||
},
|
||||
};
|
||||
newState = sqlLabReducer(newState, action);
|
||||
expect(Object.keys(newState.queries)).toHaveLength(1);
|
||||
});
|
||||
it('should stop the query', () => {
|
||||
const startQueryAction = {
|
||||
type: actions.START_QUERY,
|
||||
query,
|
||||
};
|
||||
newState = sqlLabReducer(newState, startQueryAction);
|
||||
const stopQueryAction = {
|
||||
type: actions.STOP_QUERY,
|
||||
query,
|
||||
};
|
||||
newState = sqlLabReducer(newState, stopQueryAction);
|
||||
const q = newState.queries[Object.keys(newState.queries)[0]];
|
||||
expect(q.state).toBe('stopped');
|
||||
});
|
||||
it('should remove a query', () => {
|
||||
const startQueryAction = {
|
||||
type: actions.START_QUERY,
|
||||
query,
|
||||
};
|
||||
newState = sqlLabReducer(newState, startQueryAction);
|
||||
const removeQueryAction = {
|
||||
type: actions.REMOVE_QUERY,
|
||||
query,
|
||||
};
|
||||
newState = sqlLabReducer(newState, removeQueryAction);
|
||||
expect(Object.keys(newState.queries)).toHaveLength(0);
|
||||
});
|
||||
it('should refresh queries when polling returns empty', () => {
|
||||
newState = sqlLabReducer(newState, actions.refreshQueries({}));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,55 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 {
|
||||
emptyQueryResults,
|
||||
clearQueryEditors,
|
||||
} from 'src/SqlLab/utils/reduxStateToLocalStorageHelper';
|
||||
import { LOCALSTORAGE_MAX_QUERY_AGE_MS } from 'src/SqlLab/constants';
|
||||
import { queries, defaultQueryEditor } from '../fixtures';
|
||||
|
||||
describe('reduxStateToLocalStorageHelper', () => {
|
||||
const queriesObj = {};
|
||||
beforeEach(() => {
|
||||
queries.forEach(q => {
|
||||
queriesObj[q.id] = q;
|
||||
});
|
||||
});
|
||||
|
||||
it('should empty query.results if query.startDttm is > LOCALSTORAGE_MAX_QUERY_AGE_MS', () => {
|
||||
// make sure sample data contains old query
|
||||
const oldQuery = queries[0];
|
||||
const { id, startDttm } = oldQuery;
|
||||
expect(Date.now() - startDttm).toBeGreaterThan(
|
||||
LOCALSTORAGE_MAX_QUERY_AGE_MS,
|
||||
);
|
||||
expect(Object.keys(oldQuery.results)).toContain('data');
|
||||
|
||||
const emptiedQuery = emptyQueryResults(queriesObj);
|
||||
expect(emptiedQuery[id].startDttm).toBe(startDttm);
|
||||
expect(emptiedQuery[id].results).toEqual({});
|
||||
});
|
||||
|
||||
it('should only return selected keys for query editor', () => {
|
||||
const queryEditors = [defaultQueryEditor];
|
||||
expect(Object.keys(queryEditors[0])).toContain('schemaOptions');
|
||||
|
||||
const clearedQueryEditors = clearQueryEditors(queryEditors);
|
||||
expect(Object.keys(clearedQueryEditors)[0]).not.toContain('schemaOptions');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user