feat(editor): SQL editor overhaul (#6282)

* Draft setup
*  Implemented expression evaluation in Postgres node, minor SQL editor UI improvements, minor refacring
*  Added initial version of expression preview for SQL editor
*  Linking npm package for codemirror sql grammar instead of a local file
*  Moving expression editor wrapper elements to the component
*  Using expression preview in SQL editor
* Use SQL parser skipping whitespace
*  Added support for custom skipped segments specification
*  Fixing highlight problems with dots and expressions that resolve to zero
* 👕 Fixing linting error
*  Added current item support
*  Added expression support to more nodes with sql editor
*  Added expression support for other nodes
*  Implemented different SQL dialect support
* 🐛 Fixing hard-coded parameter names for editors
*  Fixing preview for nested queries, updating query when input data changes, adding keyboard shortcut to toggle comments
*  Adding a custom automcomplete notice for different editors
*  Updating SQL autocomplete notice
*  Added unit tests for SQL editor
*  Using latest grammar
* 🐛 Fixing code node editor rendering
* 💄 SQL preview dropdown matches editor width. Removing unnecessary css
*  Addressing PR review feedback
* 👌 Addressing PR review feedback pt2
* 👌 Added path alias for utils in nodes-base package
* 👌 Addressing more PR review feedback
*  Adding tests for `getResolvables` utility function
* Fixing lodash imports
* 👌 Better focus handling, adding more plugins to the editor, other minor imrovements
*  Not showing SQL autocomplete suggestions inside expressions
*  Using npm package for sql grammar
*  Removing autocomplete notice, adding line highlight on syntax error
* 👌 Addressing code review feedback
---------
Co-authored-by: Milorad Filipovic <milorad@n8n.io>
This commit is contained in:
Iván Ovejero
2023-06-22 16:47:28 +02:00
committed by GitHub
parent d431117c9e
commit beedfb609c
68 changed files with 653 additions and 287 deletions

View File

@@ -0,0 +1,112 @@
import { render } from '@testing-library/vue';
import { PiniaVuePlugin } from 'pinia';
import { SETTINGS_STORE_DEFAULT_STATE, waitAllPromises } from '@/__tests__/utils';
import { STORES } from '@/constants';
import { createTestingPinia } from '@pinia/testing';
import SqlEditor from '@/components/SqlEditor/SqlEditor.vue';
import { expressionManager } from '@/mixins/expressionManager';
import type { TargetItem } from '@/Interface';
const EXPRESSION_OUTPUT_TEST_ID = 'inline-expression-editor-output';
const RESOLVABLES: { [key: string]: string | number | boolean } = {
'{{ $json.schema }}': 'public',
'{{ $json.table }}': 'users',
'{{ $json.id }}': 'id',
'{{ $json.limit - 10 }}': 0,
'{{ $json.active }}': false,
};
const DEFAULT_SETUP = {
pinia: createTestingPinia({
initialState: {
[STORES.SETTINGS]: {
settings: SETTINGS_STORE_DEFAULT_STATE.settings,
},
},
}),
props: {
dialect: 'PostgreSQL',
isReadOnly: false,
},
};
const renderComponent = (renderOptions: Parameters<typeof render>[1] = {}) =>
render(SqlEditor, { ...DEFAULT_SETUP, ...renderOptions }, (vue) => {
vue.use(PiniaVuePlugin);
});
describe('SQL Editor Preview Tests', () => {
beforeEach(() => {
vi.spyOn(expressionManager.methods, 'resolve').mockImplementation(
(resolvable: string, _targetItem?: TargetItem) => {
return { resolved: RESOLVABLES[resolvable] };
},
);
});
afterEach(() => {
vi.clearAllMocks();
});
it('renders basic query', async () => {
const { getByTestId } = renderComponent({
props: {
query: 'SELECT * FROM users',
},
});
await waitAllPromises();
expect(getByTestId(EXPRESSION_OUTPUT_TEST_ID)).toHaveTextContent('SELECT * FROM users');
});
it('renders basic query with expression', async () => {
const { getByTestId } = renderComponent({
props: {
query: 'SELECT * FROM {{ $json.table }}',
},
});
await waitAllPromises();
expect(getByTestId(EXPRESSION_OUTPUT_TEST_ID)).toHaveTextContent('SELECT * FROM users');
});
it('renders resolved expressions with dot between resolvables', async () => {
const { getByTestId } = renderComponent({
props: {
query: 'SELECT * FROM {{ $json.schema }}.{{ $json.table }}',
},
});
await waitAllPromises();
expect(getByTestId(EXPRESSION_OUTPUT_TEST_ID)).toHaveTextContent('SELECT * FROM public.users');
});
it('renders resolved expressions which resolve to 0', async () => {
const { getByTestId } = renderComponent({
props: {
query:
'SELECT * FROM {{ $json.schema }}.{{ $json.table }} WHERE {{ $json.id }} > {{ $json.limit - 10 }}',
},
});
await waitAllPromises();
expect(getByTestId(EXPRESSION_OUTPUT_TEST_ID)).toHaveTextContent(
'SELECT * FROM public.users WHERE id > 0',
);
});
it('keeps query formatting in rendered output', async () => {
const { getByTestId } = renderComponent({
props: {
query:
'SELECT * FROM {{ $json.schema }}.{{ $json.table }}\n WHERE id > {{ $json.limit - 10 }}\n AND active = {{ $json.active }};',
},
});
await waitAllPromises();
expect(getByTestId(EXPRESSION_OUTPUT_TEST_ID)).toHaveTextContent(
'SELECT * FROM public.users WHERE id > 0 AND active = false;',
);
// Output should have the same number of lines as the input
expect(getByTestId('sql-editor-container').getElementsByClassName('cm-line').length).toEqual(
getByTestId(EXPRESSION_OUTPUT_TEST_ID).getElementsByClassName('cm-line').length,
);
});
});