Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(sqllab): refactor addQueryEditor for new tab #21711

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions superset-frontend/src/SqlLab/actions/sqlLab.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
} from 'src/components/MessageToasts/actions';
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
import COMMON_ERR_MESSAGES from 'src/utils/errorMessages';
import { newQueryTabName } from '../utils/newQueryTabName';

export const RESET_STATE = 'RESET_STATE';
export const ADD_QUERY_EDITOR = 'ADD_QUERY_EDITOR';
Expand Down Expand Up @@ -590,6 +591,22 @@ export function addQueryEditor(queryEditor) {
};
}

export function addNewQueryEditor(queryEditor) {
return function (dispatch, getState) {
const {
sqlLab: { queryEditors },
} = getState();
const name = newQueryTabName(queryEditors || []);

return dispatch(
addQueryEditor({
...queryEditor,
name,
}),
);
};
}

export function cloneQueryToNewTab(query, autorun) {
return function (dispatch, getState) {
const state = getState();
Expand Down
22 changes: 22 additions & 0 deletions superset-frontend/src/SqlLab/actions/sqlLab.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,28 @@ describe('async actions', () => {
expect(store.getActions()).toEqual(expectedActions);
});
});

describe('addNewQueryEditor', () => {
it('creates new query editor with new tab name', () => {
const store = mockStore(initialState);
const expectedActions = [
{
type: actions.ADD_QUERY_EDITOR,
queryEditor: {
...defaultQueryEditor,
id: 'abcd',
name: `Untitled Query ${
store.getState().sqlLab.queryEditors.length + 1
}`,
},
},
];
const request = actions.addNewQueryEditor(defaultQueryEditor);
return request(store.dispatch, store.getState).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
});
});

describe('backend sync', () => {
Expand Down
13 changes: 2 additions & 11 deletions superset-frontend/src/SqlLab/components/SqlEditor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { Menu } from 'src/components/Menu';
import Icons from 'src/components/Icons';
import { detectOS } from 'src/utils/common';
import {
addQueryEditor,
addNewQueryEditor,
CtasEnum,
estimateQueryCost,
persistEditorHeight,
Expand Down Expand Up @@ -84,7 +84,6 @@ import ShareSqlLabQuery from '../ShareSqlLabQuery';
import SqlEditorLeftBar from '../SqlEditorLeftBar';
import AceEditorWrapper from '../AceEditorWrapper';
import RunQueryActionButton from '../RunQueryActionButton';
import { newQueryTabName } from '../../utils/newQueryTabName';
import QueryLimitSelect from '../QueryLimitSelect';

const appContainer = document.getElementById('app');
Expand Down Expand Up @@ -179,8 +178,6 @@ const SqlEditor = ({
},
);

const queryEditors = useSelector(({ sqlLab }) => sqlLab.queryEditors);

const [height, setHeight] = useState(0);
const [autorun, setAutorun] = useState(queryEditor.autorun);
const [ctas, setCtas] = useState('');
Expand Down Expand Up @@ -274,13 +271,7 @@ const SqlEditor = ({
key: userOS === 'Windows' ? 'ctrl+q' : 'ctrl+t',
descr: t('New tab'),
func: () => {
const name = newQueryTabName(queryEditors || []);
dispatch(
addQueryEditor({
...queryEditor,
name,
}),
);
dispatch(addNewQueryEditor(queryEditor));
},
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import thunk from 'redux-thunk';
import URI from 'urijs';
import { Provider } from 'react-redux';
import { shallow, mount } from 'enzyme';
import { fireEvent, render, waitFor } from 'spec/helpers/testing-library';
import sinon from 'sinon';
import { act } from 'react-dom/test-utils';
import fetchMock from 'fetch-mock';
Expand Down Expand Up @@ -101,6 +102,11 @@ describe('TabbedSqlEditors', () => {
},
);
});
const setup = (props = {}, overridesStore) =>
render(<TabbedSqlEditors {...props} />, {
useRedux: true,
store: overridesStore || store,
});

let wrapper;
it('is valid', () => {
Expand Down Expand Up @@ -163,23 +169,32 @@ describe('TabbedSqlEditors', () => {
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].name,
).toContain('Untitled Query');
it('should add new query editor', async () => {
const { getAllByLabelText } = setup(mockedProps);
fireEvent.click(getAllByLabelText('Add tab')[0]);
const actions = store.getActions();
await waitFor(() =>
expect(actions).toContainEqual({
type: 'ADD_QUERY_EDITOR',
queryEditor: expect.objectContaining({
name: expect.stringMatching(/Untitled Query (\d+)+/),
}),
}),
);
});
it('should properly increment query tab name', () => {
wrapper = getWrapper();
sinon.stub(wrapper.instance().props.actions, 'addQueryEditor');
const newTitle = newQueryTabName(wrapper.instance().props.queryEditors);
wrapper.instance().newQueryEditor();
expect(
wrapper.instance().props.actions.addQueryEditor.getCall(0).args[0].name,
).toContain(newTitle);
it('should properly increment query tab name', async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RTL best practices recommend testing what the user interacts with instead of implementation details. Here's a quote from their site:

You want to write maintainable tests that give you high confidence that your components are working for your users. As a part of this goal, you want your tests to avoid including implementation details so refactors of your components (changes to implementation but not functionality) don’t break your tests and slow you and your team down.

In this case, it would be better if we test what appears on the screen like checking if there's a new tab with the right title. Even if we change the action name or other logic in the future, the expected result would be the same. This is not a blocker for me though, it's just a suggestion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion. However, it's a little complicated to setup entire SqlLab redux state to simulate the behavior in the screen. It'd be better to minimize the redux usage in the future for better screen testing environment.

const { getAllByLabelText } = setup(mockedProps);
const newTitle = newQueryTabName(store.getState().sqlLab.queryEditors);
fireEvent.click(getAllByLabelText('Add tab')[0]);
const actions = store.getActions();
await waitFor(() =>
expect(actions).toContainEqual({
type: 'ADD_QUERY_EDITOR',
queryEditor: expect.objectContaining({
name: newTitle,
}),
}),
);
});
it('should duplicate query editor', () => {
wrapper = getWrapper();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import { Tooltip } from 'src/components/Tooltip';
import { detectOS } from 'src/utils/common';
import * as Actions from 'src/SqlLab/actions/sqlLab';
import { EmptyStateBig } from 'src/components/EmptyState';
import { newQueryTabName } from '../../utils/newQueryTabName';
import SqlEditor from '../SqlEditor';
import SqlEditorTabHeader from '../SqlEditorTabHeader';

Expand Down Expand Up @@ -242,10 +241,7 @@ class TabbedSqlEditors extends React.PureComponent {
'-- Note: Unless you save your query, these tabs will NOT persist if you clear your cookies or change browsers.\n\n',
);

const newTitle = newQueryTabName(this.props.queryEditors || []);

const qe = {
name: newTitle,
dbId:
activeQueryEditor && activeQueryEditor.dbId
? activeQueryEditor.dbId
Expand All @@ -255,7 +251,7 @@ class TabbedSqlEditors extends React.PureComponent {
sql: `${warning}SELECT ...`,
queryLimit: this.props.defaultQueryLimit,
};
this.props.actions.addQueryEditor(qe);
this.props.actions.addNewQueryEditor(qe);
}

handleSelect(key) {
Expand Down