@neo4j-cypher/react-codemirror 2.0.0-next.15 → 2.0.0-next.17

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 (51) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/CypherEditor.d.ts +5 -1
  3. package/dist/CypherEditor.js +20 -0
  4. package/dist/CypherEditor.js.map +1 -1
  5. package/dist/e2e_tests/autoCompletion.spec.js +82 -37
  6. package/dist/e2e_tests/autoCompletion.spec.js.map +1 -1
  7. package/dist/e2e_tests/debounce.spec.js +2 -1
  8. package/dist/e2e_tests/debounce.spec.js.map +1 -1
  9. package/dist/e2e_tests/e2eUtils.d.ts +1 -0
  10. package/dist/e2e_tests/e2eUtils.js +13 -2
  11. package/dist/e2e_tests/e2eUtils.js.map +1 -1
  12. package/dist/e2e_tests/performanceTest.spec.js +1 -1
  13. package/dist/e2e_tests/performanceTest.spec.js.map +1 -1
  14. package/dist/e2e_tests/signatureHelp.spec.js +59 -13
  15. package/dist/e2e_tests/signatureHelp.spec.js.map +1 -1
  16. package/dist/e2e_tests/snippets.spec.js +2 -2
  17. package/dist/e2e_tests/snippets.spec.js.map +1 -1
  18. package/dist/e2e_tests/syntaxValidation.spec.js +33 -4
  19. package/dist/e2e_tests/syntaxValidation.spec.js.map +1 -1
  20. package/dist/lang-cypher/autocomplete.js +10 -5
  21. package/dist/lang-cypher/autocomplete.js.map +1 -1
  22. package/dist/lang-cypher/constants.d.ts +2 -0
  23. package/dist/lang-cypher/constants.js +4 -0
  24. package/dist/lang-cypher/constants.js.map +1 -1
  25. package/dist/lang-cypher/createCypherTheme.js +1 -1
  26. package/dist/lang-cypher/createCypherTheme.js.map +1 -1
  27. package/dist/lang-cypher/langCypher.d.ts +1 -0
  28. package/dist/lang-cypher/langCypher.js +1 -8
  29. package/dist/lang-cypher/langCypher.js.map +1 -1
  30. package/dist/lang-cypher/lintWorker.d.ts +8 -4
  31. package/dist/lang-cypher/lintWorker.js +12 -2
  32. package/dist/lang-cypher/lintWorker.js.map +1 -1
  33. package/dist/lang-cypher/syntaxValidation.d.ts +0 -1
  34. package/dist/lang-cypher/syntaxValidation.js +15 -31
  35. package/dist/lang-cypher/syntaxValidation.js.map +1 -1
  36. package/dist/tsconfig.tsbuildinfo +1 -1
  37. package/package.json +2 -2
  38. package/src/CypherEditor.tsx +32 -4
  39. package/src/e2e_tests/autoCompletion.spec.tsx +121 -56
  40. package/src/e2e_tests/debounce.spec.tsx +37 -32
  41. package/src/e2e_tests/e2eUtils.ts +14 -2
  42. package/src/e2e_tests/performanceTest.spec.tsx +1 -1
  43. package/src/e2e_tests/signatureHelp.spec.tsx +74 -13
  44. package/src/e2e_tests/snippets.spec.tsx +2 -2
  45. package/src/e2e_tests/syntaxValidation.spec.tsx +76 -4
  46. package/src/lang-cypher/autocomplete.ts +14 -8
  47. package/src/lang-cypher/constants.ts +4 -0
  48. package/src/lang-cypher/createCypherTheme.ts +1 -1
  49. package/src/lang-cypher/langCypher.ts +3 -12
  50. package/src/lang-cypher/lintWorker.ts +24 -7
  51. package/src/lang-cypher/syntaxValidation.ts +15 -45
@@ -13,7 +13,11 @@ import {
13
13
  placeholder,
14
14
  ViewUpdate,
15
15
  } from '@codemirror/view';
16
- import { type DbSchema } from '@neo4j-cypher/language-support';
16
+ import {
17
+ formatQuery,
18
+ _internalFeatureFlags,
19
+ type DbSchema,
20
+ } from '@neo4j-cypher/language-support';
17
21
  import debounce from 'lodash.debounce';
18
22
  import { Component, createRef } from 'react';
19
23
  import { DEBOUNCE_TIME } from './constants';
@@ -99,7 +103,7 @@ export interface CypherEditorProps {
99
103
  */
100
104
  featureFlags?: {
101
105
  consoleCommands?: boolean;
102
- signatureInfoOnAutoCompletions?: boolean;
106
+ cypher25?: boolean;
103
107
  };
104
108
  /**
105
109
  * The schema to use for autocompletion and linting.
@@ -176,6 +180,22 @@ export interface CypherEditorProps {
176
180
  moveFocusOnTab?: boolean;
177
181
  }
178
182
 
183
+ const format = (view: EditorView): void => {
184
+ const doc = view.state.doc.toString();
185
+ const { formattedString, newCursorPos } = formatQuery(
186
+ doc,
187
+ view.state.selection.main.anchor,
188
+ );
189
+ view.dispatch({
190
+ changes: {
191
+ from: 0,
192
+ to: doc.length,
193
+ insert: formattedString,
194
+ },
195
+ selection: { anchor: newCursorPos },
196
+ });
197
+ }
198
+
179
199
  const executeKeybinding = (
180
200
  onExecute?: (cmd: string) => void,
181
201
  newLineOnEnter?: boolean,
@@ -195,7 +215,6 @@ const executeKeybinding = (
195
215
  run: insertNewline,
196
216
  },
197
217
  };
198
-
199
218
  if (onExecute) {
200
219
  keybindings['Ctrl-Enter'] = {
201
220
  key: 'Ctrl-Enter',
@@ -276,6 +295,13 @@ export class CypherEditor extends Component<
276
295
  editorView: React.MutableRefObject<EditorView> = createRef();
277
296
  private schemaRef: React.MutableRefObject<CypherConfig> = createRef();
278
297
 
298
+ /**
299
+ * Format Cypher query
300
+ */
301
+ format() {
302
+ format(this.editorView.current)
303
+ }
304
+
279
305
  /**
280
306
  * Focus the editor
281
307
  */
@@ -346,6 +372,8 @@ export class CypherEditor extends Component<
346
372
  newLineOnEnter,
347
373
  } = this.props;
348
374
 
375
+ _internalFeatureFlags.cypher25 = featureFlags?.cypher25 ?? false;
376
+
349
377
  this.schemaRef.current = {
350
378
  schema,
351
379
  lint,
@@ -382,7 +410,7 @@ export class CypherEditor extends Component<
382
410
  }),
383
411
  ]
384
412
  : [];
385
-
413
+
386
414
  this.editorState.current = EditorState.create({
387
415
  extensions: [
388
416
  keyBindingCompartment.of(
@@ -194,14 +194,60 @@ test('can complete rel types', async ({ page, mount }) => {
194
194
  await expect(component).toContainText('MATCH (n)-[:KNOWS');
195
195
  });
196
196
 
197
+ test('can complete YIELD clauses without manual trigger', async ({
198
+ page,
199
+ mount,
200
+ }) => {
201
+ const component = await mount(
202
+ <CypherEditor
203
+ schema={{
204
+ procedures: testData.mockSchema.procedures,
205
+ }}
206
+ />,
207
+ );
208
+
209
+ const textField = page.getByRole('textbox');
210
+
211
+ await textField.fill('CALL dbms.components() YIELD ');
212
+
213
+ await page.locator('.cm-tooltip-autocomplete').getByText('edition').click();
214
+ await expect(page.locator('.cm-tooltip-autocomplete')).not.toBeVisible();
215
+
216
+ await expect(component).toContainText('CALL dbms.components() YIELD edition');
217
+ });
218
+
219
+ test('automatic yield trigger is not case sensitive', async ({
220
+ page,
221
+ mount,
222
+ }) => {
223
+ const component = await mount(
224
+ <CypherEditor
225
+ schema={{
226
+ procedures: testData.mockSchema.procedures,
227
+ }}
228
+ />,
229
+ );
230
+
231
+ const textField = page.getByRole('textbox');
232
+
233
+ await textField.fill('CALL dbms.components() yIeLd ');
234
+
235
+ await page.locator('.cm-tooltip-autocomplete').getByText('edition').click();
236
+ await expect(page.locator('.cm-tooltip-autocomplete')).not.toBeVisible();
237
+
238
+ await expect(component).toContainText('CALL dbms.components() yIeLd edition');
239
+ });
240
+
197
241
  test('can complete functions', async ({ page, mount }) => {
198
242
  const component = await mount(
199
243
  <CypherEditor
200
244
  schema={{
201
245
  functions: {
202
- function123: {
203
- ...testData.emptyFunction,
204
- name: 'function123',
246
+ 'CYPHER 5': {
247
+ function123: {
248
+ ...testData.emptyFunction,
249
+ name: 'function123',
250
+ },
205
251
  },
206
252
  },
207
253
  }}
@@ -227,7 +273,9 @@ test('can complete procedures', async ({ page, mount }) => {
227
273
  <CypherEditor
228
274
  schema={{
229
275
  procedures: {
230
- 'db.ping': { ...testData.emptyProcedure, name: 'db.ping' },
276
+ 'CYPHER 5': {
277
+ 'db.ping': { ...testData.emptyProcedure, name: 'db.ping' },
278
+ },
231
279
  },
232
280
  }}
233
281
  />,
@@ -308,16 +356,9 @@ test('shows signature help information on auto-completion for procedures', async
308
356
  page,
309
357
  mount,
310
358
  }) => {
311
- await mount(
312
- <CypherEditor
313
- schema={testData.mockSchema}
314
- featureFlags={{
315
- signatureInfoOnAutoCompletions: true,
316
- }}
317
- />,
318
- );
359
+ await mount(<CypherEditor schema={testData.mockSchema} />);
319
360
  const procName = 'apoc.periodic.iterate';
320
- const procedure = testData.mockSchema.procedures[procName];
361
+ const procedure = testData.mockSchema.procedures['CYPHER 5'][procName];
321
362
 
322
363
  const textField = page.getByRole('textbox');
323
364
  await textField.fill('CALL apoc.periodic.');
@@ -333,16 +374,9 @@ test('shows signature help information on auto-completion for functions', async
333
374
  page,
334
375
  mount,
335
376
  }) => {
336
- await mount(
337
- <CypherEditor
338
- schema={testData.mockSchema}
339
- featureFlags={{
340
- signatureInfoOnAutoCompletions: true,
341
- }}
342
- />,
343
- );
377
+ await mount(<CypherEditor schema={testData.mockSchema} />);
344
378
  const fnName = 'apoc.coll.combinations';
345
- const fn = testData.mockSchema.functions[fnName];
379
+ const fn = testData.mockSchema.functions['CYPHER 5'][fnName];
346
380
 
347
381
  const textField = page.getByRole('textbox');
348
382
  await textField.fill('RETURN apoc.coll.');
@@ -363,10 +397,11 @@ test('shows deprecated procedures as strikethrough on auto-completion', async ({
363
397
  await mount(
364
398
  <CypherEditor
365
399
  schema={{
366
- procedures: { [procName]: testData.mockSchema.procedures[procName] },
367
- }}
368
- featureFlags={{
369
- signatureInfoOnAutoCompletions: true,
400
+ procedures: {
401
+ 'CYPHER 5': {
402
+ [procName]: testData.mockSchema.procedures['CYPHER 5'][procName],
403
+ },
404
+ },
370
405
  }}
371
406
  />,
372
407
  );
@@ -375,7 +410,7 @@ test('shows deprecated procedures as strikethrough on auto-completion', async ({
375
410
 
376
411
  // We need to assert on the element having the right class
377
412
  // and trusting the CSS is making this truly strikethrough
378
- await expect(page.locator('.cm-deprecated-completion')).toBeVisible();
413
+ await expect(page.locator('.cm-deprecated-element')).toBeVisible();
379
414
  });
380
415
 
381
416
  test('shows deprecated function as strikethrough on auto-completion', async ({
@@ -387,10 +422,11 @@ test('shows deprecated function as strikethrough on auto-completion', async ({
387
422
  await mount(
388
423
  <CypherEditor
389
424
  schema={{
390
- functions: { [fnName]: testData.mockSchema.functions[fnName] },
391
- }}
392
- featureFlags={{
393
- signatureInfoOnAutoCompletions: true,
425
+ functions: {
426
+ 'CYPHER 5': {
427
+ [fnName]: testData.mockSchema.functions['CYPHER 5'][fnName],
428
+ },
429
+ },
394
430
  }}
395
431
  />,
396
432
  );
@@ -399,21 +435,14 @@ test('shows deprecated function as strikethrough on auto-completion', async ({
399
435
 
400
436
  // We need to assert on the element having the right class
401
437
  // and trusting the CSS is making this truly strikethrough
402
- await expect(page.locator('.cm-deprecated-completion')).toBeVisible();
438
+ await expect(page.locator('.cm-deprecated-element')).toBeVisible();
403
439
  });
404
440
 
405
441
  test('does not signature help information on auto-completion if docs and signature are empty', async ({
406
442
  page,
407
443
  mount,
408
444
  }) => {
409
- await mount(
410
- <CypherEditor
411
- schema={testData.mockSchema}
412
- featureFlags={{
413
- signatureInfoOnAutoCompletions: true,
414
- }}
415
- />,
416
- );
445
+ await mount(<CypherEditor schema={testData.mockSchema} />);
417
446
 
418
447
  const textField = page.getByRole('textbox');
419
448
  await textField.fill('C');
@@ -430,17 +459,16 @@ test('shows signature help information on auto-completion if description is not
430
459
  <CypherEditor
431
460
  schema={{
432
461
  procedures: {
433
- 'db.ping': {
434
- ...testData.emptyProcedure,
435
- description: 'foo',
436
- signature: '',
437
- name: 'db.ping',
462
+ 'CYPHER 5': {
463
+ 'db.ping': {
464
+ ...testData.emptyProcedure,
465
+ description: 'foo',
466
+ signature: '',
467
+ name: 'db.ping',
468
+ },
438
469
  },
439
470
  },
440
471
  }}
441
- featureFlags={{
442
- signatureInfoOnAutoCompletions: true,
443
- }}
444
472
  />,
445
473
  );
446
474
 
@@ -459,17 +487,16 @@ test('shows signature help information on auto-completion if signature is not em
459
487
  <CypherEditor
460
488
  schema={{
461
489
  procedures: {
462
- 'db.ping': {
463
- ...testData.emptyProcedure,
464
- description: '',
465
- signature: 'foo',
466
- name: 'db.ping',
490
+ 'CYPHER 5': {
491
+ 'db.ping': {
492
+ ...testData.emptyProcedure,
493
+ description: '',
494
+ signature: 'foo',
495
+ name: 'db.ping',
496
+ },
467
497
  },
468
498
  },
469
499
  }}
470
- featureFlags={{
471
- signatureInfoOnAutoCompletions: true,
472
- }}
473
500
  />,
474
501
  );
475
502
 
@@ -479,3 +506,41 @@ test('shows signature help information on auto-completion if signature is not em
479
506
  await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible();
480
507
  await expect(page.locator('.cm-completionInfo')).toBeVisible();
481
508
  });
509
+
510
+ test('completions depend on the Cypher version', async ({ page, mount }) => {
511
+ await mount(
512
+ <CypherEditor
513
+ schema={{
514
+ functions: {
515
+ 'CYPHER 5': {
516
+ cypher5Function: {
517
+ ...testData.emptyFunction,
518
+ name: 'cypher5Function',
519
+ },
520
+ },
521
+ 'CYPHER 25': {
522
+ cypher25Function: {
523
+ ...testData.emptyFunction,
524
+ name: 'cypher25Function',
525
+ },
526
+ },
527
+ },
528
+ }}
529
+ featureFlags={{ cypher25: true }}
530
+ />,
531
+ );
532
+
533
+ const textField = page.getByRole('textbox');
534
+
535
+ await textField.fill('CYPHER 5 RETURN cypher');
536
+
537
+ await expect(
538
+ page.locator('.cm-tooltip-autocomplete').getByText('cypher5Function'),
539
+ ).toBeVisible();
540
+
541
+ await textField.fill('CYPHER 25 RETURN cypher');
542
+
543
+ await expect(
544
+ page.locator('.cm-tooltip-autocomplete').getByText('cypher25Function'),
545
+ ).toBeVisible();
546
+ });
@@ -27,43 +27,48 @@ test.fail(
27
27
  },
28
28
  );
29
29
 
30
- test('onExecute updates should override debounce updates', async ({
31
- mount,
32
- page,
33
- }) => {
34
- const editorPage = new CypherEditorPage(page);
35
- let value = '';
30
+ // TODO Fix this test
31
+ test.fixme(
32
+ 'onExecute updates should override debounce updates',
33
+ async ({ mount, page }) => {
34
+ const editorPage = new CypherEditorPage(page);
35
+ let value = '';
36
36
 
37
- const onExecute = () => {
38
- value = '';
39
- void component.update(
40
- <CypherEditor value={value} onChange={onChange} onExecute={onExecute} />,
41
- );
42
- };
37
+ const onExecute = () => {
38
+ value = '';
39
+ void component.update(
40
+ <CypherEditor
41
+ value={value}
42
+ onChange={onChange}
43
+ onExecute={onExecute}
44
+ />,
45
+ );
46
+ };
43
47
 
44
- const onChange = (val: string) => {
45
- value = val;
46
- void component.update(
47
- <CypherEditor value={val} onChange={onChange} onExecute={onExecute} />,
48
- );
49
- };
48
+ const onChange = (val: string) => {
49
+ value = val;
50
+ void component.update(
51
+ <CypherEditor value={val} onChange={onChange} onExecute={onExecute} />,
52
+ );
53
+ };
50
54
 
51
- const component = await mount(
52
- <CypherEditor value={value} onChange={onChange} onExecute={onExecute} />,
53
- );
55
+ const component = await mount(
56
+ <CypherEditor value={value} onChange={onChange} onExecute={onExecute} />,
57
+ );
54
58
 
55
- await editorPage.getEditor().pressSequentially('RETURN 1');
56
- await editorPage.getEditor().press('Enter');
57
- await page.waitForTimeout(DEBOUNCE_TIME_WITH_MARGIN);
58
- await expect(component).not.toContainText('RETURN 1');
59
+ await editorPage.getEditor().pressSequentially('RETURN 1');
60
+ await editorPage.getEditor().press('Enter');
61
+ await page.waitForTimeout(DEBOUNCE_TIME_WITH_MARGIN);
62
+ await expect(component).not.toContainText('RETURN 1');
59
63
 
60
- await editorPage.getEditor().pressSequentially('RETURN 1');
61
- await editorPage.getEditor().pressSequentially('');
62
- await editorPage.getEditor().pressSequentially('RETURN 1');
63
- await editorPage.getEditor().press('Enter');
64
- await page.waitForTimeout(DEBOUNCE_TIME_WITH_MARGIN);
65
- await expect(component).not.toContainText('RETURN 1');
66
- });
64
+ await editorPage.getEditor().pressSequentially('RETURN 1');
65
+ await editorPage.getEditor().pressSequentially('');
66
+ await editorPage.getEditor().pressSequentially('RETURN 1');
67
+ await editorPage.getEditor().press('Enter');
68
+ await page.waitForTimeout(DEBOUNCE_TIME_WITH_MARGIN);
69
+ await expect(component).not.toContainText('RETURN 1');
70
+ },
71
+ );
67
72
 
68
73
  test('onExecute should fire after debounced updates', async ({
69
74
  mount,
@@ -59,17 +59,29 @@ export class CypherEditorPage {
59
59
  return this.checkNotificationMessage('warning', queryChunk, expectedMsg);
60
60
  }
61
61
 
62
+ async checkNoNotificationMessage(type: 'error' | 'warning') {
63
+ await this.page.waitForTimeout(10000);
64
+ await expect(this.page.locator('.cm-lintRange-' + type)).toHaveCount(0, {
65
+ timeout: 10000,
66
+ });
67
+ await expect(this.page.locator('.cm-lintPoint-' + type)).toHaveCount(0, {
68
+ timeout: 10000,
69
+ });
70
+ }
71
+
62
72
  private async checkNotificationMessage(
63
73
  type: 'error' | 'warning',
64
74
  queryChunk: string,
65
75
  expectedMsg: string,
66
76
  ) {
67
77
  await expect(this.page.locator('.cm-lintRange-' + type).last()).toBeVisible(
68
- { timeout: 3000 },
78
+ { timeout: 10000 },
69
79
  );
70
80
 
71
81
  await this.page.getByText(queryChunk, { exact: true }).hover();
72
- await expect(this.page.locator('.cm-tooltip-hover').last()).toBeVisible();
82
+ await expect(this.page.locator('.cm-tooltip-hover').last()).toBeVisible({
83
+ timeout: 10000,
84
+ });
73
85
  await expect(this.page.getByText(expectedMsg)).toBeVisible();
74
86
  /* Return the mouse to the beginning of the query and
75
87
  This is because if for example we have an overlay with a
@@ -100,7 +100,7 @@ test('benchmarking & performance test session', async ({
100
100
 
101
101
  await editorPage.checkErrorMessage(
102
102
  'RETRN',
103
- 'Unexpected token. Did you mean RETURN?',
103
+ `Invalid input 'RETRN': expected a graph pattern, 'FOREACH', ',', 'ORDER BY', 'CALL', 'CREATE', 'LOAD CSV', 'DELETE', 'DETACH', 'FINISH', 'INSERT', 'LIMIT', 'MATCH', 'MERGE', 'NODETACH', 'OFFSET', 'OPTIONAL', 'REMOVE', 'RETURN', 'SET', 'SKIP', 'UNION', 'UNWIND', 'USE', 'USING', 'WHERE', 'WITH' or <EOF>`,
104
104
  );
105
105
 
106
106
  await editorPage
@@ -11,7 +11,8 @@ type TooltipExpectations = {
11
11
  excludes?: string[];
12
12
  };
13
13
 
14
- const importCsvProc = testData.mockSchema.procedures['apoc.import.csv'];
14
+ const importCsvProc =
15
+ testData.mockSchema.procedures['CYPHER 5']['apoc.import.csv'];
15
16
 
16
17
  function testTooltip(tooltip: Locator, expectations: TooltipExpectations) {
17
18
  const includes = expectations.includes ?? [];
@@ -20,7 +21,7 @@ function testTooltip(tooltip: Locator, expectations: TooltipExpectations) {
20
21
  const included = Promise.all(
21
22
  includes.map((text) => {
22
23
  return expect(tooltip).toContainText(text, {
23
- timeout: 2000,
24
+ timeout: 10000,
24
25
  });
25
26
  }),
26
27
  );
@@ -28,7 +29,7 @@ function testTooltip(tooltip: Locator, expectations: TooltipExpectations) {
28
29
  const excluded = Promise.all(
29
30
  excludes.map((text) => {
30
31
  return expect(tooltip).not.toContainText(text, {
31
- timeout: 2000,
32
+ timeout: 10000,
32
33
  });
33
34
  }),
34
35
  );
@@ -48,7 +49,7 @@ test('Signature help works for functions', async ({ page, mount }) => {
48
49
  );
49
50
 
50
51
  await expect(page.locator('.cm-signature-help-panel')).toBeVisible({
51
- timeout: 2000,
52
+ timeout: 10000,
52
53
  });
53
54
  });
54
55
 
@@ -64,7 +65,7 @@ test('Signature help works for procedures', async ({ page, mount }) => {
64
65
  );
65
66
 
66
67
  await expect(page.locator('.cm-signature-help-panel')).toBeVisible({
67
- timeout: 2000,
68
+ timeout: 10000,
68
69
  });
69
70
  });
70
71
 
@@ -86,9 +87,9 @@ test('Signature help shows the description for the first argument', async ({
86
87
 
87
88
  await testTooltip(tooltip, {
88
89
  includes: [
89
- testData.mockSchema.procedures['apoc.import.csv'].argumentDescription[0]
90
- .description,
91
- testData.mockSchema.procedures['apoc.import.csv'].description,
90
+ testData.mockSchema.procedures['CYPHER 5']['apoc.import.csv']
91
+ .argumentDescription[0].description,
92
+ testData.mockSchema.procedures['CYPHER 5']['apoc.import.csv'].description,
92
93
  ],
93
94
  });
94
95
  });
@@ -289,7 +290,7 @@ test('Signature help does not show any help when method finished', async ({
289
290
  );
290
291
 
291
292
  await expect(page.locator('.cm-signature-help-panel')).not.toBeVisible({
292
- timeout: 2000,
293
+ timeout: 10000,
293
294
  });
294
295
  });
295
296
 
@@ -308,7 +309,7 @@ test('Signature help does not blow up on empty query', async ({
308
309
  );
309
310
 
310
311
  await expect(page.locator('.cm-signature-help-panel')).not.toBeVisible({
311
- timeout: 2000,
312
+ timeout: 10000,
312
313
  });
313
314
  });
314
315
 
@@ -331,7 +332,7 @@ test('Signature help is shown below the text by default', async ({
331
332
  await expect(
332
333
  page.locator('.cm-signature-help-panel.cm-tooltip-below'),
333
334
  ).toBeVisible({
334
- timeout: 2000,
335
+ timeout: 10000,
335
336
  });
336
337
  });
337
338
 
@@ -355,7 +356,7 @@ test('Setting showSignatureTooltipBelow to true shows the signature help above t
355
356
  await expect(
356
357
  page.locator('.cm-signature-help-panel.cm-tooltip-below'),
357
358
  ).toBeVisible({
358
- timeout: 2000,
359
+ timeout: 10000,
359
360
  });
360
361
  });
361
362
 
@@ -379,6 +380,66 @@ test('Setting showSignatureTooltipBelow to false shows the signature help above
379
380
  await expect(
380
381
  page.locator('.cm-signature-help-panel.cm-tooltip-above'),
381
382
  ).toBeVisible({
382
- timeout: 2000,
383
+ timeout: 10000,
384
+ });
385
+ });
386
+
387
+ test('Signature help depends on the Cypher version', async ({
388
+ page,
389
+ mount,
390
+ }) => {
391
+ const cypher5ArgDescription = 'The Cypher 5 statement to run.';
392
+ const cypher25ArgDescription = 'The Cypher 25 statement to run.';
393
+
394
+ await mount(
395
+ <CypherEditor
396
+ schema={{
397
+ functions: {
398
+ 'CYPHER 5': {
399
+ cypher5Function: {
400
+ ...testData.emptyFunction,
401
+ argumentDescription: [
402
+ {
403
+ isDeprecated: false,
404
+ description: cypher5ArgDescription,
405
+ name: 'statement',
406
+ type: 'STRING',
407
+ },
408
+ ],
409
+ name: 'cypher5Function',
410
+ },
411
+ },
412
+ 'CYPHER 25': {
413
+ cypher25Function: {
414
+ ...testData.emptyFunction,
415
+ argumentDescription: [
416
+ {
417
+ isDeprecated: false,
418
+ description: cypher25ArgDescription,
419
+ name: 'statement',
420
+ type: 'STRING',
421
+ },
422
+ ],
423
+ name: 'cypher25Function',
424
+ },
425
+ },
426
+ },
427
+ }}
428
+ featureFlags={{ cypher25: true }}
429
+ />,
430
+ );
431
+
432
+ const textField = page.getByRole('textbox');
433
+ await textField.fill('CYPHER 5 RETURN cypher5Function(');
434
+ const tooltip = page.locator('.cm-signature-help-panel');
435
+
436
+ await testTooltip(tooltip, {
437
+ includes: [cypher5ArgDescription],
438
+ });
439
+
440
+ await textField.fill('CYPHER 25 RETURN cypher25Function(');
441
+
442
+ await testTooltip(tooltip, {
443
+ includes: [cypher25ArgDescription],
383
444
  });
384
445
  });
@@ -65,11 +65,11 @@ test('can accept completion inside pattern snippet', async ({
65
65
  page.locator('.cm-tooltip-autocomplete').getByText('City'),
66
66
  ).toBeVisible();
67
67
 
68
- await textField.press('Tab');
68
+ await textField.press('Tab', { delay: 300 });
69
69
  await expect(page.locator('.cm-tooltip-autocomplete')).not.toBeVisible();
70
70
 
71
71
  // tab out of the snippet
72
- await textField.press('Tab');
72
+ await textField.press('Tab', { delay: 300 });
73
73
 
74
74
  await expect(textField).toHaveText('MATCH ()-[]->()-[ ]->(:City)');
75
75
  });