@neo4j-cypher/react-codemirror 2.0.0-next.7 → 2.0.0-next.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/dist/CypherEditor.d.ts +60 -2
- package/dist/CypherEditor.js +115 -20
- package/dist/CypherEditor.js.map +1 -1
- package/dist/e2e_tests/autoCompletion.spec.js +112 -17
- package/dist/e2e_tests/autoCompletion.spec.js.map +1 -1
- package/dist/e2e_tests/configuration.spec.d.ts +1 -0
- package/dist/e2e_tests/configuration.spec.js +83 -0
- package/dist/e2e_tests/configuration.spec.js.map +1 -0
- package/dist/e2e_tests/debounce.spec.d.ts +1 -0
- package/dist/e2e_tests/debounce.spec.js +63 -0
- package/dist/e2e_tests/debounce.spec.js.map +1 -0
- package/dist/e2e_tests/e2eUtils.js +9 -1
- package/dist/e2e_tests/e2eUtils.js.map +1 -1
- package/dist/e2e_tests/extraKeybindings.spec.js +0 -1
- package/dist/e2e_tests/extraKeybindings.spec.js.map +1 -1
- package/dist/e2e_tests/historyNavigation.spec.js +107 -16
- package/dist/e2e_tests/historyNavigation.spec.js.map +1 -1
- package/dist/e2e_tests/sanityChecks.spec.js +0 -10
- package/dist/e2e_tests/sanityChecks.spec.js.map +1 -1
- package/dist/e2e_tests/signatureHelp.spec.js +43 -15
- package/dist/e2e_tests/signatureHelp.spec.js.map +1 -1
- package/dist/e2e_tests/snippets.spec.d.ts +1 -0
- package/dist/e2e_tests/snippets.spec.js +62 -0
- package/dist/e2e_tests/snippets.spec.js.map +1 -0
- package/dist/e2e_tests/syntaxHighlighting.spec.js +0 -1
- package/dist/e2e_tests/syntaxHighlighting.spec.js.map +1 -1
- package/dist/e2e_tests/syntaxValidation.spec.js +3 -3
- package/dist/e2e_tests/syntaxValidation.spec.js.map +1 -1
- package/dist/historyNavigation.js +1 -1
- package/dist/historyNavigation.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lang-cypher/autocomplete.d.ts +4 -1
- package/dist/lang-cypher/autocomplete.js +79 -17
- package/dist/lang-cypher/autocomplete.js.map +1 -1
- package/dist/lang-cypher/contants.test.js +2 -2
- package/dist/lang-cypher/contants.test.js.map +1 -1
- package/dist/lang-cypher/createCypherTheme.js +34 -2
- package/dist/lang-cypher/createCypherTheme.js.map +1 -1
- package/dist/lang-cypher/langCypher.d.ts +5 -0
- package/dist/lang-cypher/langCypher.js +11 -5
- package/dist/lang-cypher/langCypher.js.map +1 -1
- package/dist/lang-cypher/signatureHelp.js +39 -22
- package/dist/lang-cypher/signatureHelp.js.map +1 -1
- package/dist/lang-cypher/utils.d.ts +2 -0
- package/dist/lang-cypher/utils.js +10 -0
- package/dist/lang-cypher/utils.js.map +1 -0
- package/dist/neo4jSetup.js +35 -1
- package/dist/neo4jSetup.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/CypherEditor.tsx +233 -31
- package/src/e2e_tests/autoCompletion.spec.tsx +189 -18
- package/src/e2e_tests/configuration.spec.tsx +111 -0
- package/src/e2e_tests/debounce.spec.tsx +100 -0
- package/src/e2e_tests/e2eUtils.ts +11 -1
- package/src/e2e_tests/extraKeybindings.spec.tsx +0 -2
- package/src/e2e_tests/historyNavigation.spec.tsx +136 -17
- package/src/e2e_tests/sanityChecks.spec.tsx +0 -16
- package/src/e2e_tests/signatureHelp.spec.tsx +86 -18
- package/src/e2e_tests/snippets.spec.tsx +92 -0
- package/src/e2e_tests/syntaxHighlighting.spec.tsx +0 -2
- package/src/e2e_tests/syntaxValidation.spec.tsx +3 -3
- package/src/historyNavigation.ts +1 -1
- package/src/index.ts +4 -1
- package/src/lang-cypher/autocomplete.ts +95 -19
- package/src/lang-cypher/contants.test.ts +5 -2
- package/src/lang-cypher/createCypherTheme.ts +34 -2
- package/src/lang-cypher/langCypher.ts +17 -5
- package/src/lang-cypher/signatureHelp.ts +61 -30
- package/src/lang-cypher/utils.ts +9 -0
- package/src/neo4jSetup.tsx +51 -1
package/src/CypherEditor.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { insertNewline } from '@codemirror/commands';
|
|
1
2
|
import {
|
|
2
3
|
Annotation,
|
|
3
4
|
Compartment,
|
|
@@ -9,9 +10,10 @@ import {
|
|
|
9
10
|
KeyBinding,
|
|
10
11
|
keymap,
|
|
11
12
|
lineNumbers,
|
|
13
|
+
placeholder,
|
|
12
14
|
ViewUpdate,
|
|
13
15
|
} from '@codemirror/view';
|
|
14
|
-
import type
|
|
16
|
+
import { type DbSchema } from '@neo4j-cypher/language-support';
|
|
15
17
|
import debounce from 'lodash.debounce';
|
|
16
18
|
import { Component, createRef } from 'react';
|
|
17
19
|
import {
|
|
@@ -23,6 +25,7 @@ import { cleanupWorkers } from './lang-cypher/syntaxValidation';
|
|
|
23
25
|
import { basicNeo4jSetup } from './neo4jSetup';
|
|
24
26
|
import { getThemeExtension } from './themes';
|
|
25
27
|
|
|
28
|
+
type DomEventHandlers = Parameters<typeof EditorView.domEventHandlers>[0];
|
|
26
29
|
export interface CypherEditorProps {
|
|
27
30
|
/**
|
|
28
31
|
* The prompt to show on single line editors
|
|
@@ -42,7 +45,14 @@ export interface CypherEditorProps {
|
|
|
42
45
|
*/
|
|
43
46
|
onExecute?: (cmd: string) => void;
|
|
44
47
|
/**
|
|
45
|
-
*
|
|
48
|
+
* If true, pressing enter will add a new line to the editor and cmd/ctrl + enter will execute the query.
|
|
49
|
+
* Otherwise pressing enter on a single line will execute the query.
|
|
50
|
+
*
|
|
51
|
+
* @default false
|
|
52
|
+
*/
|
|
53
|
+
newLineOnEnter?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The editor history navigable via up/down arrow keys. Order newest to oldest.
|
|
46
56
|
* Add to this list with the `onExecute` callback for REPL style history.
|
|
47
57
|
*/
|
|
48
58
|
history?: string[];
|
|
@@ -75,6 +85,21 @@ export interface CypherEditorProps {
|
|
|
75
85
|
* @default true
|
|
76
86
|
*/
|
|
77
87
|
lint?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Whether the signature help tooltip should be shown below the text.
|
|
90
|
+
* If false, it will be shown above.
|
|
91
|
+
*
|
|
92
|
+
* @default true
|
|
93
|
+
*/
|
|
94
|
+
showSignatureTooltipBelow?: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Internal feature flags for the editor. Don't use in production
|
|
97
|
+
*
|
|
98
|
+
*/
|
|
99
|
+
featureFlags?: {
|
|
100
|
+
consoleCommands?: boolean;
|
|
101
|
+
signatureInfoOnAutoCompletions?: boolean;
|
|
102
|
+
};
|
|
78
103
|
/**
|
|
79
104
|
* The schema to use for autocompletion and linting.
|
|
80
105
|
*
|
|
@@ -102,29 +127,122 @@ export interface CypherEditorProps {
|
|
|
102
127
|
* @param {ViewUpdate} viewUpdate - the view update from codemirror
|
|
103
128
|
*/
|
|
104
129
|
onChange?(value: string, viewUpdate: ViewUpdate): void;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Map of event handlers to add to the editor.
|
|
133
|
+
*
|
|
134
|
+
* Note that the props are compared by reference, meaning object defined inline
|
|
135
|
+
* will cause the editor to re-render (much like the style prop does in this example:
|
|
136
|
+
* <div style={{}} />
|
|
137
|
+
*
|
|
138
|
+
* Memoize the object if you want/need to avoid this.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* // listen to blur events
|
|
142
|
+
* <CypherEditor domEventHandlers={{blur: () => console.log("blur event fired")}} />
|
|
143
|
+
*/
|
|
144
|
+
domEventHandlers?: DomEventHandlers;
|
|
145
|
+
/**
|
|
146
|
+
* Placeholder text to display when the editor is empty.
|
|
147
|
+
*/
|
|
148
|
+
placeholder?: string;
|
|
149
|
+
/**
|
|
150
|
+
* Whether the editor should show line numbers.
|
|
151
|
+
*
|
|
152
|
+
* @default true
|
|
153
|
+
*/
|
|
154
|
+
lineNumbers?: boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Whether the editor is read-only.
|
|
157
|
+
*
|
|
158
|
+
* @default false
|
|
159
|
+
*/
|
|
160
|
+
readonly?: boolean;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* String value to assign to the aria-label attribute of the editor
|
|
164
|
+
*/
|
|
165
|
+
ariaLabel?: string;
|
|
105
166
|
}
|
|
106
167
|
|
|
107
|
-
const executeKeybinding = (
|
|
108
|
-
onExecute
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
168
|
+
const executeKeybinding = (
|
|
169
|
+
onExecute?: (cmd: string) => void,
|
|
170
|
+
newLineOnEnter?: boolean,
|
|
171
|
+
flush?: () => void,
|
|
172
|
+
) => {
|
|
173
|
+
const keybindings: Record<string, KeyBinding> = {
|
|
174
|
+
'Shift-Enter': {
|
|
175
|
+
key: 'Shift-Enter',
|
|
176
|
+
run: insertNewline,
|
|
177
|
+
},
|
|
178
|
+
'Ctrl-Enter': {
|
|
179
|
+
key: 'Ctrl-Enter',
|
|
180
|
+
run: () => true,
|
|
181
|
+
},
|
|
182
|
+
Enter: {
|
|
183
|
+
key: 'Enter',
|
|
184
|
+
run: insertNewline,
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
if (onExecute) {
|
|
189
|
+
keybindings['Ctrl-Enter'] = {
|
|
190
|
+
key: 'Ctrl-Enter',
|
|
191
|
+
mac: 'Mod-Enter',
|
|
192
|
+
preventDefault: true,
|
|
193
|
+
run: (view: EditorView) => {
|
|
194
|
+
const doc = view.state.doc.toString();
|
|
195
|
+
if (doc.trim() !== '') {
|
|
196
|
+
flush?.();
|
|
197
|
+
onExecute(doc);
|
|
198
|
+
}
|
|
119
199
|
|
|
120
|
-
|
|
121
|
-
|
|
200
|
+
return true;
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
if (!newLineOnEnter) {
|
|
205
|
+
keybindings['Enter'] = {
|
|
206
|
+
key: 'Enter',
|
|
207
|
+
preventDefault: true,
|
|
208
|
+
run: (view: EditorView) => {
|
|
209
|
+
const doc = view.state.doc.toString();
|
|
210
|
+
if (doc.includes('\n')) {
|
|
211
|
+
// Returning false means the event will mark the event
|
|
212
|
+
// as not handled and the default behavior will be executed
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (doc.trim() !== '') {
|
|
217
|
+
flush?.();
|
|
218
|
+
onExecute(doc);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return true;
|
|
122
222
|
},
|
|
123
|
-
|
|
124
|
-
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return Object.values(keybindings);
|
|
228
|
+
};
|
|
125
229
|
|
|
126
230
|
const themeCompartment = new Compartment();
|
|
127
231
|
const keyBindingCompartment = new Compartment();
|
|
232
|
+
const lineNumbersCompartment = new Compartment();
|
|
233
|
+
const readOnlyCompartment = new Compartment();
|
|
234
|
+
const placeholderCompartment = new Compartment();
|
|
235
|
+
const domEventHandlerCompartment = new Compartment();
|
|
236
|
+
|
|
237
|
+
const formatLineNumber =
|
|
238
|
+
(prompt?: string) => (a: number, state: EditorState) => {
|
|
239
|
+
if (state.doc.lines === 1 && prompt !== undefined) {
|
|
240
|
+
return prompt;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return a.toString();
|
|
244
|
+
};
|
|
245
|
+
|
|
128
246
|
type CypherEditorState = { cypherSupportEnabled: boolean };
|
|
129
247
|
|
|
130
248
|
const ExternalEdit = Annotation.define<boolean>();
|
|
@@ -147,6 +265,8 @@ export class CypherEditor extends Component<
|
|
|
147
265
|
editorView: React.MutableRefObject<EditorView> = createRef();
|
|
148
266
|
private schemaRef: React.MutableRefObject<CypherConfig> = createRef();
|
|
149
267
|
|
|
268
|
+
private latestDispatchedValue: string | undefined;
|
|
269
|
+
|
|
150
270
|
/**
|
|
151
271
|
* Focus the editor
|
|
152
272
|
*/
|
|
@@ -185,13 +305,22 @@ export class CypherEditor extends Component<
|
|
|
185
305
|
schema: {},
|
|
186
306
|
overrideThemeBackgroundColor: false,
|
|
187
307
|
lineWrap: false,
|
|
308
|
+
showSignatureTooltipBelow: true,
|
|
188
309
|
extraKeybindings: [],
|
|
189
310
|
history: [],
|
|
190
311
|
theme: 'light',
|
|
312
|
+
lineNumbers: true,
|
|
313
|
+
newLineOnEnter: false,
|
|
191
314
|
};
|
|
192
315
|
|
|
193
316
|
private debouncedOnChange = this.props.onChange
|
|
194
|
-
? debounce(
|
|
317
|
+
? debounce(
|
|
318
|
+
((value, viewUpdate) => {
|
|
319
|
+
this.latestDispatchedValue = value;
|
|
320
|
+
this.props.onChange(value, viewUpdate);
|
|
321
|
+
}) satisfies CypherEditorProps['onChange'],
|
|
322
|
+
200,
|
|
323
|
+
)
|
|
195
324
|
: undefined;
|
|
196
325
|
|
|
197
326
|
componentDidMount(): void {
|
|
@@ -202,12 +331,20 @@ export class CypherEditor extends Component<
|
|
|
202
331
|
overrideThemeBackgroundColor,
|
|
203
332
|
schema,
|
|
204
333
|
lint,
|
|
334
|
+
showSignatureTooltipBelow,
|
|
335
|
+
featureFlags,
|
|
205
336
|
onExecute,
|
|
337
|
+
newLineOnEnter,
|
|
206
338
|
} = this.props;
|
|
207
339
|
|
|
208
340
|
this.schemaRef.current = {
|
|
209
341
|
schema,
|
|
210
342
|
lint,
|
|
343
|
+
showSignatureTooltipBelow,
|
|
344
|
+
featureFlags: {
|
|
345
|
+
consoleCommands: true,
|
|
346
|
+
...featureFlags,
|
|
347
|
+
},
|
|
211
348
|
useLightVersion: false,
|
|
212
349
|
setUseLightVersion: (newVal) => {
|
|
213
350
|
if (this.schemaRef.current !== undefined) {
|
|
@@ -237,10 +374,17 @@ export class CypherEditor extends Component<
|
|
|
237
374
|
]
|
|
238
375
|
: [];
|
|
239
376
|
|
|
377
|
+
this.latestDispatchedValue = this.props.value;
|
|
378
|
+
|
|
240
379
|
this.editorState.current = EditorState.create({
|
|
241
380
|
extensions: [
|
|
242
381
|
keyBindingCompartment.of(
|
|
243
|
-
keymap.of([
|
|
382
|
+
keymap.of([
|
|
383
|
+
...executeKeybinding(onExecute, newLineOnEnter, () =>
|
|
384
|
+
this.debouncedOnChange.flush(),
|
|
385
|
+
),
|
|
386
|
+
...extraKeybindings,
|
|
387
|
+
]),
|
|
244
388
|
),
|
|
245
389
|
historyNavigation(this.props),
|
|
246
390
|
basicNeo4jSetup(),
|
|
@@ -249,15 +393,25 @@ export class CypherEditor extends Component<
|
|
|
249
393
|
cypher(this.schemaRef.current),
|
|
250
394
|
lineWrap ? EditorView.lineWrapping : [],
|
|
251
395
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
396
|
+
lineNumbersCompartment.of(
|
|
397
|
+
this.props.lineNumbers
|
|
398
|
+
? lineNumbers({ formatNumber: formatLineNumber(this.props.prompt) })
|
|
399
|
+
: [],
|
|
400
|
+
),
|
|
401
|
+
readOnlyCompartment.of(EditorState.readOnly.of(this.props.readonly)),
|
|
402
|
+
placeholderCompartment.of(
|
|
403
|
+
this.props.placeholder ? placeholder(this.props.placeholder) : [],
|
|
404
|
+
),
|
|
405
|
+
domEventHandlerCompartment.of(
|
|
406
|
+
this.props.domEventHandlers
|
|
407
|
+
? EditorView.domEventHandlers(this.props.domEventHandlers)
|
|
408
|
+
: [],
|
|
409
|
+
),
|
|
410
|
+
this.props.ariaLabel
|
|
411
|
+
? EditorView.contentAttributes.of({
|
|
412
|
+
'aria-label': this.props.ariaLabel,
|
|
413
|
+
})
|
|
414
|
+
: [],
|
|
261
415
|
],
|
|
262
416
|
doc: this.props.value,
|
|
263
417
|
});
|
|
@@ -285,7 +439,11 @@ export class CypherEditor extends Component<
|
|
|
285
439
|
// Handle externally set value
|
|
286
440
|
const currentCmValue = this.editorView.current.state?.doc.toString() ?? '';
|
|
287
441
|
|
|
288
|
-
if (
|
|
442
|
+
if (
|
|
443
|
+
this.props.value !== undefined &&
|
|
444
|
+
this.props.value !== this.latestDispatchedValue
|
|
445
|
+
) {
|
|
446
|
+
this.debouncedOnChange?.cancel();
|
|
289
447
|
this.editorView.current.dispatch({
|
|
290
448
|
changes: {
|
|
291
449
|
from: 0,
|
|
@@ -313,6 +471,35 @@ export class CypherEditor extends Component<
|
|
|
313
471
|
});
|
|
314
472
|
}
|
|
315
473
|
|
|
474
|
+
if (
|
|
475
|
+
prevProps.lineNumbers !== this.props.lineNumbers ||
|
|
476
|
+
prevProps.prompt !== this.props.prompt
|
|
477
|
+
) {
|
|
478
|
+
this.editorView.current.dispatch({
|
|
479
|
+
effects: lineNumbersCompartment.reconfigure(
|
|
480
|
+
this.props.lineNumbers
|
|
481
|
+
? lineNumbers({ formatNumber: formatLineNumber(this.props.prompt) })
|
|
482
|
+
: [],
|
|
483
|
+
),
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (prevProps.readonly !== this.props.readonly) {
|
|
488
|
+
this.editorView.current.dispatch({
|
|
489
|
+
effects: readOnlyCompartment.reconfigure(
|
|
490
|
+
EditorState.readOnly.of(this.props.readonly),
|
|
491
|
+
),
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (prevProps.placeholder !== this.props.placeholder) {
|
|
496
|
+
this.editorView.current.dispatch({
|
|
497
|
+
effects: placeholderCompartment.reconfigure(
|
|
498
|
+
this.props.placeholder ? placeholder(this.props.placeholder) : [],
|
|
499
|
+
),
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
316
503
|
if (
|
|
317
504
|
prevProps.extraKeybindings !== this.props.extraKeybindings ||
|
|
318
505
|
prevProps.onExecute !== this.props.onExecute
|
|
@@ -320,13 +507,27 @@ export class CypherEditor extends Component<
|
|
|
320
507
|
this.editorView.current.dispatch({
|
|
321
508
|
effects: keyBindingCompartment.reconfigure(
|
|
322
509
|
keymap.of([
|
|
323
|
-
...executeKeybinding(
|
|
510
|
+
...executeKeybinding(
|
|
511
|
+
this.props.onExecute,
|
|
512
|
+
this.props.newLineOnEnter,
|
|
513
|
+
() => this.debouncedOnChange.flush(),
|
|
514
|
+
),
|
|
324
515
|
...this.props.extraKeybindings,
|
|
325
516
|
]),
|
|
326
517
|
),
|
|
327
518
|
});
|
|
328
519
|
}
|
|
329
520
|
|
|
521
|
+
if (prevProps.domEventHandlers !== this.props.domEventHandlers) {
|
|
522
|
+
this.editorView.current.dispatch({
|
|
523
|
+
effects: domEventHandlerCompartment.reconfigure(
|
|
524
|
+
this.props.domEventHandlers
|
|
525
|
+
? EditorView.domEventHandlers(this.props.domEventHandlers)
|
|
526
|
+
: [],
|
|
527
|
+
),
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
|
|
330
531
|
// This component rerenders on every keystroke and comparing the
|
|
331
532
|
// full lists of editor strings on every render could be expensive.
|
|
332
533
|
const didChangeHistoryEstimate =
|
|
@@ -346,6 +547,7 @@ export class CypherEditor extends Component<
|
|
|
346
547
|
*/
|
|
347
548
|
this.schemaRef.current.schema = this.props.schema;
|
|
348
549
|
this.schemaRef.current.lint = this.props.lint;
|
|
550
|
+
this.schemaRef.current.featureFlags = this.props.featureFlags;
|
|
349
551
|
}
|
|
350
552
|
|
|
351
553
|
componentWillUnmount(): void {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { testData } from '@neo4j-cypher/language-support';
|
|
2
2
|
import { expect, test } from '@playwright/experimental-ct-react';
|
|
3
|
+
import type { Page } from '@playwright/test';
|
|
3
4
|
import { CypherEditor } from '../CypherEditor';
|
|
4
5
|
|
|
5
|
-
test.use({ viewport: { width: 500, height: 500 } });
|
|
6
|
-
|
|
7
6
|
test('hello world end 2 end test', async ({ mount }) => {
|
|
8
7
|
const component = await mount(<CypherEditor value="hello world" />);
|
|
9
8
|
await expect(component).toContainText('hello world');
|
|
@@ -235,35 +234,207 @@ test('completes allShortestPaths correctly', async ({ page, mount }) => {
|
|
|
235
234
|
);
|
|
236
235
|
});
|
|
237
236
|
|
|
238
|
-
|
|
239
|
-
|
|
237
|
+
async function getInfoTooltip(page: Page, methodName: string) {
|
|
238
|
+
const infoTooltip = page.locator('.cm-completionInfo');
|
|
239
|
+
const firstOption = page.locator('li[aria-selected="true"]');
|
|
240
|
+
let selectedOption = firstOption;
|
|
241
|
+
|
|
242
|
+
while (!(await infoTooltip.textContent()).includes(methodName)) {
|
|
243
|
+
await page.keyboard.press('ArrowDown');
|
|
244
|
+
const currentSelected = page.locator('li[aria-selected="true"]');
|
|
245
|
+
expect(currentSelected).not.toBe(selectedOption);
|
|
246
|
+
expect(currentSelected).not.toBe(firstOption);
|
|
247
|
+
selectedOption = currentSelected;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return infoTooltip;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
test('shows signature help information on auto-completion for procedures', async ({
|
|
254
|
+
page,
|
|
255
|
+
mount,
|
|
256
|
+
}) => {
|
|
257
|
+
await mount(
|
|
258
|
+
<CypherEditor
|
|
259
|
+
schema={testData.mockSchema}
|
|
260
|
+
featureFlags={{
|
|
261
|
+
signatureInfoOnAutoCompletions: true,
|
|
262
|
+
}}
|
|
263
|
+
/>,
|
|
264
|
+
);
|
|
265
|
+
const procName = 'apoc.periodic.iterate';
|
|
266
|
+
const procedure = testData.mockSchema.procedures[procName];
|
|
267
|
+
|
|
240
268
|
const textField = page.getByRole('textbox');
|
|
269
|
+
await textField.fill('CALL apoc.periodic.');
|
|
241
270
|
|
|
242
|
-
await
|
|
271
|
+
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible();
|
|
243
272
|
|
|
244
|
-
await page
|
|
245
|
-
await expect(
|
|
273
|
+
const infoTooltip = await getInfoTooltip(page, procName);
|
|
274
|
+
await expect(infoTooltip).toContainText(procedure.signature);
|
|
275
|
+
await expect(infoTooltip).toContainText(procedure.description);
|
|
276
|
+
});
|
|
246
277
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
278
|
+
test('shows signature help information on auto-completion for functions', async ({
|
|
279
|
+
page,
|
|
280
|
+
mount,
|
|
281
|
+
}) => {
|
|
282
|
+
await mount(
|
|
283
|
+
<CypherEditor
|
|
284
|
+
schema={testData.mockSchema}
|
|
285
|
+
featureFlags={{
|
|
286
|
+
signatureInfoOnAutoCompletions: true,
|
|
287
|
+
}}
|
|
288
|
+
/>,
|
|
289
|
+
);
|
|
290
|
+
const fnName = 'apoc.coll.combinations';
|
|
291
|
+
const fn = testData.mockSchema.functions[fnName];
|
|
292
|
+
|
|
293
|
+
const textField = page.getByRole('textbox');
|
|
294
|
+
await textField.fill('RETURN apoc.coll.');
|
|
250
295
|
|
|
251
|
-
await expect(
|
|
296
|
+
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible();
|
|
297
|
+
|
|
298
|
+
const infoTooltip = await getInfoTooltip(page, fnName);
|
|
299
|
+
await expect(infoTooltip).toContainText(fn.signature);
|
|
300
|
+
await expect(infoTooltip).toContainText(fn.description);
|
|
252
301
|
});
|
|
253
302
|
|
|
254
|
-
test('
|
|
303
|
+
test('shows deprecated procedures as strikethrough on auto-completion', async ({
|
|
255
304
|
page,
|
|
256
305
|
mount,
|
|
257
306
|
}) => {
|
|
258
|
-
|
|
307
|
+
const procName = 'apoc.trigger.resume';
|
|
308
|
+
|
|
309
|
+
await mount(
|
|
310
|
+
<CypherEditor
|
|
311
|
+
schema={{
|
|
312
|
+
procedures: { [procName]: testData.mockSchema.procedures[procName] },
|
|
313
|
+
}}
|
|
314
|
+
featureFlags={{
|
|
315
|
+
signatureInfoOnAutoCompletions: true,
|
|
316
|
+
}}
|
|
317
|
+
/>,
|
|
318
|
+
);
|
|
259
319
|
const textField = page.getByRole('textbox');
|
|
320
|
+
await textField.fill('CALL apoc.trigger.');
|
|
260
321
|
|
|
261
|
-
|
|
322
|
+
// We need to assert on the element having the right class
|
|
323
|
+
// and trusting the CSS is making this truly strikethrough
|
|
324
|
+
await expect(page.locator('.cm-deprecated-completion')).toBeVisible();
|
|
325
|
+
});
|
|
262
326
|
|
|
263
|
-
|
|
264
|
-
|
|
327
|
+
test('shows deprecated function as strikethrough on auto-completion', async ({
|
|
328
|
+
page,
|
|
329
|
+
mount,
|
|
330
|
+
}) => {
|
|
331
|
+
const fnName = 'apoc.create.uuid';
|
|
332
|
+
|
|
333
|
+
await mount(
|
|
334
|
+
<CypherEditor
|
|
335
|
+
schema={{
|
|
336
|
+
functions: { [fnName]: testData.mockSchema.functions[fnName] },
|
|
337
|
+
}}
|
|
338
|
+
featureFlags={{
|
|
339
|
+
signatureInfoOnAutoCompletions: true,
|
|
340
|
+
}}
|
|
341
|
+
/>,
|
|
342
|
+
);
|
|
343
|
+
const textField = page.getByRole('textbox');
|
|
344
|
+
await textField.fill('RETURN apoc.create.');
|
|
345
|
+
|
|
346
|
+
// We need to assert on the element having the right class
|
|
347
|
+
// and trusting the CSS is making this truly strikethrough
|
|
348
|
+
await expect(page.locator('.cm-deprecated-completion')).toBeVisible();
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test('does not signature help information on auto-completion if flag not enabled explicitly', async ({
|
|
352
|
+
page,
|
|
353
|
+
mount,
|
|
354
|
+
}) => {
|
|
355
|
+
await mount(<CypherEditor schema={testData.mockSchema} />);
|
|
356
|
+
|
|
357
|
+
const textField = page.getByRole('textbox');
|
|
358
|
+
await textField.fill('CALL apoc.periodic.');
|
|
359
|
+
|
|
360
|
+
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible();
|
|
361
|
+
await expect(page.locator('.cm-completionInfo')).not.toBeVisible();
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test('does not signature help information on auto-completion if docs and signature are empty', async ({
|
|
365
|
+
page,
|
|
366
|
+
mount,
|
|
367
|
+
}) => {
|
|
368
|
+
await mount(
|
|
369
|
+
<CypherEditor
|
|
370
|
+
schema={testData.mockSchema}
|
|
371
|
+
featureFlags={{
|
|
372
|
+
signatureInfoOnAutoCompletions: true,
|
|
373
|
+
}}
|
|
374
|
+
/>,
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
const textField = page.getByRole('textbox');
|
|
378
|
+
await textField.fill('C');
|
|
379
|
+
|
|
380
|
+
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible();
|
|
381
|
+
await expect(page.locator('.cm-completionInfo')).not.toBeVisible();
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test('shows signature help information on auto-completion if description is not empty, signature is', async ({
|
|
385
|
+
page,
|
|
386
|
+
mount,
|
|
387
|
+
}) => {
|
|
388
|
+
await mount(
|
|
389
|
+
<CypherEditor
|
|
390
|
+
schema={{
|
|
391
|
+
procedures: {
|
|
392
|
+
'db.ping': {
|
|
393
|
+
...testData.emptyProcedure,
|
|
394
|
+
description: 'foo',
|
|
395
|
+
signature: '',
|
|
396
|
+
name: 'db.ping',
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
}}
|
|
400
|
+
featureFlags={{
|
|
401
|
+
signatureInfoOnAutoCompletions: true,
|
|
402
|
+
}}
|
|
403
|
+
/>,
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
const textField = page.getByRole('textbox');
|
|
407
|
+
await textField.fill('CALL db.');
|
|
408
|
+
|
|
409
|
+
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible();
|
|
410
|
+
await expect(page.locator('.cm-completionInfo')).toBeVisible();
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test('shows signature help information on auto-completion if signature is not empty, description is', async ({
|
|
414
|
+
page,
|
|
415
|
+
mount,
|
|
416
|
+
}) => {
|
|
417
|
+
await mount(
|
|
418
|
+
<CypherEditor
|
|
419
|
+
schema={{
|
|
420
|
+
procedures: {
|
|
421
|
+
'db.ping': {
|
|
422
|
+
...testData.emptyProcedure,
|
|
423
|
+
description: '',
|
|
424
|
+
signature: 'foo',
|
|
425
|
+
name: 'db.ping',
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
}}
|
|
429
|
+
featureFlags={{
|
|
430
|
+
signatureInfoOnAutoCompletions: true,
|
|
431
|
+
}}
|
|
432
|
+
/>,
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
const textField = page.getByRole('textbox');
|
|
436
|
+
await textField.fill('CALL db.');
|
|
265
437
|
|
|
266
|
-
// unless manually triggered
|
|
267
|
-
await textField.press('Control+ ');
|
|
268
438
|
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible();
|
|
439
|
+
await expect(page.locator('.cm-completionInfo')).toBeVisible();
|
|
269
440
|
});
|