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:
112
packages/editor-ui/src/components/__tests__/SQLEditor.test.ts
Normal file
112
packages/editor-ui/src/components/__tests__/SQLEditor.test.ts
Normal 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,
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user