@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.
Files changed (74) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/CypherEditor.d.ts +60 -2
  3. package/dist/CypherEditor.js +115 -20
  4. package/dist/CypherEditor.js.map +1 -1
  5. package/dist/e2e_tests/autoCompletion.spec.js +112 -17
  6. package/dist/e2e_tests/autoCompletion.spec.js.map +1 -1
  7. package/dist/e2e_tests/configuration.spec.d.ts +1 -0
  8. package/dist/e2e_tests/configuration.spec.js +83 -0
  9. package/dist/e2e_tests/configuration.spec.js.map +1 -0
  10. package/dist/e2e_tests/debounce.spec.d.ts +1 -0
  11. package/dist/e2e_tests/debounce.spec.js +63 -0
  12. package/dist/e2e_tests/debounce.spec.js.map +1 -0
  13. package/dist/e2e_tests/e2eUtils.js +9 -1
  14. package/dist/e2e_tests/e2eUtils.js.map +1 -1
  15. package/dist/e2e_tests/extraKeybindings.spec.js +0 -1
  16. package/dist/e2e_tests/extraKeybindings.spec.js.map +1 -1
  17. package/dist/e2e_tests/historyNavigation.spec.js +107 -16
  18. package/dist/e2e_tests/historyNavigation.spec.js.map +1 -1
  19. package/dist/e2e_tests/sanityChecks.spec.js +0 -10
  20. package/dist/e2e_tests/sanityChecks.spec.js.map +1 -1
  21. package/dist/e2e_tests/signatureHelp.spec.js +43 -15
  22. package/dist/e2e_tests/signatureHelp.spec.js.map +1 -1
  23. package/dist/e2e_tests/snippets.spec.d.ts +1 -0
  24. package/dist/e2e_tests/snippets.spec.js +62 -0
  25. package/dist/e2e_tests/snippets.spec.js.map +1 -0
  26. package/dist/e2e_tests/syntaxHighlighting.spec.js +0 -1
  27. package/dist/e2e_tests/syntaxHighlighting.spec.js.map +1 -1
  28. package/dist/e2e_tests/syntaxValidation.spec.js +3 -3
  29. package/dist/e2e_tests/syntaxValidation.spec.js.map +1 -1
  30. package/dist/historyNavigation.js +1 -1
  31. package/dist/historyNavigation.js.map +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.js +1 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/lang-cypher/autocomplete.d.ts +4 -1
  36. package/dist/lang-cypher/autocomplete.js +79 -17
  37. package/dist/lang-cypher/autocomplete.js.map +1 -1
  38. package/dist/lang-cypher/contants.test.js +2 -2
  39. package/dist/lang-cypher/contants.test.js.map +1 -1
  40. package/dist/lang-cypher/createCypherTheme.js +34 -2
  41. package/dist/lang-cypher/createCypherTheme.js.map +1 -1
  42. package/dist/lang-cypher/langCypher.d.ts +5 -0
  43. package/dist/lang-cypher/langCypher.js +11 -5
  44. package/dist/lang-cypher/langCypher.js.map +1 -1
  45. package/dist/lang-cypher/signatureHelp.js +39 -22
  46. package/dist/lang-cypher/signatureHelp.js.map +1 -1
  47. package/dist/lang-cypher/utils.d.ts +2 -0
  48. package/dist/lang-cypher/utils.js +10 -0
  49. package/dist/lang-cypher/utils.js.map +1 -0
  50. package/dist/neo4jSetup.js +35 -1
  51. package/dist/neo4jSetup.js.map +1 -1
  52. package/dist/tsconfig.tsbuildinfo +1 -1
  53. package/package.json +9 -9
  54. package/src/CypherEditor.tsx +233 -31
  55. package/src/e2e_tests/autoCompletion.spec.tsx +189 -18
  56. package/src/e2e_tests/configuration.spec.tsx +111 -0
  57. package/src/e2e_tests/debounce.spec.tsx +100 -0
  58. package/src/e2e_tests/e2eUtils.ts +11 -1
  59. package/src/e2e_tests/extraKeybindings.spec.tsx +0 -2
  60. package/src/e2e_tests/historyNavigation.spec.tsx +136 -17
  61. package/src/e2e_tests/sanityChecks.spec.tsx +0 -16
  62. package/src/e2e_tests/signatureHelp.spec.tsx +86 -18
  63. package/src/e2e_tests/snippets.spec.tsx +92 -0
  64. package/src/e2e_tests/syntaxHighlighting.spec.tsx +0 -2
  65. package/src/e2e_tests/syntaxValidation.spec.tsx +3 -3
  66. package/src/historyNavigation.ts +1 -1
  67. package/src/index.ts +4 -1
  68. package/src/lang-cypher/autocomplete.ts +95 -19
  69. package/src/lang-cypher/contants.test.ts +5 -2
  70. package/src/lang-cypher/createCypherTheme.ts +34 -2
  71. package/src/lang-cypher/langCypher.ts +17 -5
  72. package/src/lang-cypher/signatureHelp.ts +61 -30
  73. package/src/lang-cypher/utils.ts +9 -0
  74. package/src/neo4jSetup.tsx +51 -1
@@ -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 { DbSchema } from '@neo4j-cypher/language-support';
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
- * The editor history navigateable via up/down arrow keys. Order newest to oldest.
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 = (onExecute?: (cmd: string) => void) =>
108
- onExecute
109
- ? [
110
- {
111
- key: 'Ctrl-Enter',
112
- mac: 'Mod-Enter',
113
- preventDefault: true,
114
- run: (view: EditorView) => {
115
- const doc = view.state.doc.toString();
116
- if (doc.trim() !== '') {
117
- onExecute(doc);
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
- return true;
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(this.props.onChange, 200)
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([...executeKeybinding(onExecute), ...extraKeybindings]),
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
- lineNumbers({
253
- formatNumber: (a, state) => {
254
- if (state.doc.lines === 1 && this.props.prompt !== undefined) {
255
- return this.props.prompt;
256
- }
257
-
258
- return a.toString();
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 (this.props.value !== undefined && currentCmValue !== this.props.value) {
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(this.props.onExecute),
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
- test('can complete pattern snippet', async ({ page, mount }) => {
239
- await mount(<CypherEditor />);
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 textField.fill('MATCH ()-[]->()');
271
+ await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible();
243
272
 
244
- await page.locator('.cm-tooltip-autocomplete').getByText('-[]->()').click();
245
- await expect(page.locator('.cm-tooltip-autocomplete')).not.toBeVisible();
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
- await textField.press('Tab');
248
- await textField.press('Tab');
249
- await textField.press('Tab');
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(textField).toHaveText('MATCH ()-[]->()-[ ]->( )');
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('does not automatically open completion panel for expressions after snippet trigger char', async ({
303
+ test('shows deprecated procedures as strikethrough on auto-completion', async ({
255
304
  page,
256
305
  mount,
257
306
  }) => {
258
- await mount(<CypherEditor />);
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
- await textField.fill('RETURN (1)');
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
- // expect the panel to not show up
264
- await expect(page.locator('.cm-tooltip-autocomplete')).not.toBeVisible();
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
  });