1
+ import { complete } from './complete' ;
2
+
3
+ const categories = {
4
+ 'a' : { type : 'enum' , values : [ 'a1a' , 'a1b' , 'a2a' , 'a2b' ] } ,
5
+ 'b' : { type : 'enum' , values : [ 'b1' , 'b2' ] } ,
6
+ 'c' : { type : 'numeric' }
7
+ } ;
8
+ const areas = [ 'pqr' , 'xyz' ] ;
9
+
10
+ describe ( "complete" , ( ) => {
11
+ test ( "complete at end of expression" , ( ) => {
12
+ test_complete ( 'a == ' , 5 , [ 'a1a' , 'a1b' , 'a2a' , 'a2b' ] , [ 5 , 5 ] ) ;
13
+ test_complete ( 'b == ' , 5 , [ 'b1' , 'b2' ] , [ 5 , 5 ] ) ;
14
+ test_complete ( 'b == ' , 5 , [ 'b1' , 'b2' ] , [ 5 , 5 ] ) ;
15
+ test_complete ( '' , 12 , [ 'a' , 'b' , 'c' , 'in_pqr' , 'in_xyz' , 'true' , 'false' ] , [ 12 , 12 ] ) ;
16
+ test_complete ( ' ' , 12 , [ 'a' , 'b' , 'c' , 'in_pqr' , 'in_xyz' , 'true' , 'false' ] , [ 12 , 12 ] ) ;
17
+ test_complete ( '\t\n' , 12 , [ 'a' , 'b' , 'c' , 'in_pqr' , 'in_xyz' , 'true' , 'false' ] , [ 12 , 12 ] ) ;
18
+ test_complete ( ' ' , 0 , [ 'a' , 'b' , 'c' , 'in_pqr' , 'in_xyz' , 'true' , 'false' ] , [ 0 , 0 ] ) ;
19
+ test_complete ( ' ' , 1 , [ 'a' , 'b' , 'c' , 'in_pqr' , 'in_xyz' , 'true' , 'false' ] , [ 1 , 1 ] ) ;
20
+ test_complete ( ' ' , 2 , [ 'a' , 'b' , 'c' , 'in_pqr' , 'in_xyz' , 'true' , 'false' ] , [ 2 , 2 ] ) ;
21
+ test_complete ( 'b == ' , 4 , [ 'b1' , 'b2' ] , [ 4 , 4 ] ) ;
22
+ test_complete ( 'b ==' , 4 , [ 'b1' , 'b2' ] , [ 4 , 4 ] ) ;
23
+ test_complete ( 'b ==' , 9 , [ 'b1' , 'b2' ] , [ 9 , 9 ] ) ;
24
+ test_complete ( 'b == ' , 9 , [ 'b1' , 'b2' ] , [ 9 , 9 ] ) ;
25
+ } ) ;
26
+
27
+ test ( "complete at end of expression, incomplete token" , ( ) => {
28
+ // if we complete at the end of the (non-whitespace part of the) expression we get completion suggestions
29
+ // that consider the characters that were entered already
30
+ test_complete ( 'a == a1 ' , 7 , [ 'a1a' , 'a1b' ] , [ 5 , 7 ] ) ;
31
+ test_complete ( 'a == x1 ' , 7 , [ ] , null ) ;
32
+ // here we get no suggestions, because the 'previous' expression is invalid (we evaluate 'a == a1 x')
33
+ test_complete ( 'a == a1 ' , 8 , [ ] , null ) ;
34
+ } ) ;
35
+
36
+ test ( "complete at end of expression, with previous error" , ( ) => {
37
+ // in case the expression contains an error that is found before the position our cursor is at we do not
38
+ // get any suggestions, because without further work they are simply not available! we do *not* want to get
39
+ // suggestions to fix the first error (here a/b instead of xyz)!
40
+ test_complete ( 'xyz == a1 && a == ' , 20 , [ ] , null ) ;
41
+ // here we get suggestions despite the error (unmatched opening '('). this is because even though the opening
42
+ // parentheses comes first in the expression the error is not detected before the position we are editing is
43
+ // inspected
44
+ test_complete ( '( a == ' , 8 , [ 'a1a' , 'a1b' , 'a2a' , 'a2b' ] , [ 8 , 8 ] ) ;
45
+ } ) ;
46
+
47
+ test ( "complete at whitespace within expression" , ( ) => {
48
+ test_complete ( 'a == a1 && b == b1' , 1 , [ 'a' ] , [ 0 , 1 ] ) ;
49
+ test_complete ( 'a == a1 && b == b1' , 4 , [ ] , null ) ;
50
+ test_complete ( 'a == a1 && b == b1' , 5 , [ 'a1a' , 'a1b' , 'a2a' , 'a2b' ] , [ 5 , 7 ] ) ;
51
+ test_complete ( 'a == a1 && b == b1' , 7 , [ 'a1a' , 'a1b' ] , [ 5 , 7 ] ) ;
52
+ test_complete ( 'a == x1a && b == b1' , 5 , [ 'a1a' , 'a1b' , 'a2a' , 'a2b' ] , [ 5 , 8 ] ) ;
53
+ // no completions when there is a previous error
54
+ test_complete ( 'a == x1a && b == b1' , 8 , [ ] , null ) ;
55
+ } ) ;
56
+
57
+ test ( "complete at token within expression" , ( ) => {
58
+ test_complete ( 'a == a1a && b != b1' , 0 , [ 'a' , 'b' , 'c' , 'in_pqr' , 'in_xyz' , 'true' , 'false' ] , [ 0 , 1 ] ) ;
59
+ test_complete ( 'a == a1a && b != b2' , 2 , [ '==' , '!=' ] , [ 2 , 4 ] ) ;
60
+ test_complete ( 'a == a1b && b == b1' , 5 , [ 'a1a' , 'a1b' , 'a2a' , 'a2b' ] , [ 5 , 8 ] ) ;
61
+ test_complete ( 'a == a2a && b == b2' , 6 , [ 'a1a' , 'a1b' , 'a2a' , 'a2b' ] , [ 5 , 8 ] ) ;
62
+ test_complete ( 'a == x2b && b == b1 || a == a1a' , 5 , [ 'a1a' , 'a1b' , 'a2a' , 'a2b' ] , [ 5 , 8 ] ) ;
63
+ // here we filter out some of the options due to the current cursor position
64
+ test_complete ( 'a == a2b && b == b1' , 7 , [ 'a2a' , 'a2b' ] , [ 5 , 8 ] ) ;
65
+ test_complete ( 'a == a2b && b == b1' , 8 , [ 'a2b' ] , [ 5 , 8 ] ) ;
66
+ test_complete ( 'a == a && b == b1' , 5 , [ 'a1a' , 'a1b' , 'a2a' , 'a2b' ] , [ 5 , 6 ] ) ;
67
+ test_complete ( 'a == a && b == b1' , 6 , [ 'a1a' , 'a1b' , 'a2a' , 'a2b' ] , [ 5 , 6 ] ) ;
68
+ test_complete ( 'a == a2 && b == b1' , 6 , [ 'a1a' , 'a1b' , 'a2a' , 'a2b' ] , [ 5 , 7 ] ) ;
69
+ test_complete ( 'a == a2 && b == b1' , 7 , [ 'a2a' , 'a2b' ] , [ 5 , 7 ] ) ;
70
+ // no completions when there is a previous error
71
+ test_complete ( 'a == x2b && b == b1 || a == a1a' , 12 , [ ] , null ) ;
72
+ test_complete ( 'a == x2b && b == b1 || a == a1a' , 17 , [ ] , null ) ;
73
+ } ) ;
74
+
75
+ test ( "complete areas" , ( ) => {
76
+ test_complete ( 'in_ && a == a1a' , 2 , [ 'in_pqr' , 'in_xyz' ] , [ 0 , 3 ] ) ;
77
+ test_complete ( 'in_x && a == a1a' , 4 , [ 'in_xyz' ] , [ 0 , 4 ] ) ;
78
+ } ) ;
79
+
80
+ test ( "complete update" , ( ) => {
81
+ test_complete ( `c < 9` , 4 , [ `__hint__type a number` ] , [ 4 , 5 ] ) ;
82
+ // 'continuing' a number does not work because the __hint__ suggestion is filtered when it is compared with
83
+ // the existing digits. should we change this?
84
+ test_complete ( `c < 9 || b == b1` , 5 , [ ] , null ) ;
85
+ test_complete ( `c < 9 ` , 5 , [ ] , null ) ;
86
+ test_complete ( `c < 9` , 5 , [ ] , null ) ;
87
+ } ) ;
88
+ } ) ;
89
+
90
+ function test_complete ( expression , pos , suggestions , range ) {
91
+ const completion = complete ( expression , pos , categories , areas )
92
+ try {
93
+ expect ( completion . suggestions ) . toStrictEqual ( suggestions ) ;
94
+ expect ( completion . range ) . toStrictEqual ( range ) ;
95
+ } catch ( e ) {
96
+ Error . captureStackTrace ( e , test_complete ) ;
97
+ throw e ;
98
+ }
99
+ }
0 commit comments