-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest-complete.ts
More file actions
193 lines (166 loc) · 6.38 KB
/
test-complete.ts
File metadata and controls
193 lines (166 loc) · 6.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// @ts-expect-error TS2307
import { expressionlanguage } from "@valtzu/codemirror-lang-el";
import { EditorState } from "@codemirror/state";
import { Completion, CompletionContext, CompletionSource } from "@codemirror/autocomplete";
import ist from "ist";
const operatorKeywords = ['starts with', 'ends with', 'contains', 'matches', 'in', 'not', 'or', 'xor', 'and'];
async function get(doc: string, { explicit } = { explicit: false }) {
const cur = doc.indexOf("‸");
doc = doc.slice(0, cur) + doc.slice(cur + "‸".length);
const state = EditorState.create({
doc,
selection: { anchor: cur },
extensions: [expressionlanguage({
types: {
"custom44": {
identifiers: [
{name: "property11", type: ["any"]},
{name: "property22", type: ["any"]},
],
functions: [
{name: "firstMethod", args: [], returnType: ["custom44"]},
]
}
},
identifiers: [
{name: "foobar"},
{name: "foobaz"},
{name: "obj", type: ["custom44"]}
],
functions: [
{name: "smh", returnType: ["string"]},
{name: "smash_my_head", args: [{name: "object", type: ["custom44"]}]},
{name: "getObject", returnType: ["custom44"]},
],
})],
});
return (await state.languageDataAt<CompletionSource>("autocomplete", cur)[0](new CompletionContext(state, cur, explicit)))?.options;
}
describe("Expression language completion", () => {
it("completes when explicitly requested", async () => {
const c = await get("‸", {explicit: true}) ?? [];
ist(c.length, 0, '>');
ist(!c.some(o => operatorKeywords.includes(o.label)));
});
it("completes when explicitly requested, even when non-empty", async () => {
const c = await get("foo > 10 and ‸", {explicit: true}) ?? [];
ist(c.length, 0, '>');
ist(!c.some(o => operatorKeywords.includes(o.label)));
});
it("completes operators when explicitly requested", async () => {
const c = await get("foo > 10 ‸", {explicit: true}) ?? [];
ist(c.length, 0, '>');
ist(c.some(o => operatorKeywords.includes(o.label)));
});
it("completes variables when explicitly requested, even mid-word", async () => {
const c = await get("foo > 10 and foo‸", {explicit: true}) ?? [];
ist(c.length, 0, '>');
ist(!c.some(o => operatorKeywords.includes(o.label)));
});
it("completes variables", async () => {
const c = await get("foo‸") ?? [];
ist(c.length, 0, '>');
ist(c.map(x => x.label).includes('foobar'));
ist(c.map(x => x.label).includes('foobaz'));
});
it("completes parameterless functions", async () => {
const c = (await get("sm‸"))?.find(x => x.label === 'smh()');
ist(!!c);
ist("string", c?.detail);
ist("smh()", c?.label);
});
it("completes functions with params", async () => {
const c = (await get("smash‸"))?.find(x => x.label === 'smash_my_head(object)');
ist(!!c);
ist(undefined, c?.detail);
ist("smash_my_head(object)", c?.label);
});
it("completes operator keywords after identifiers", async () => {
const c = await get("smh s‸") ?? [];
ist(c.some(x => x.label === 'starts with'));
});
it("completes operator keywords after parenthesis", async () => {
const c = await get("smh() en‸") ?? [];
ist(c.some(x => x.label === 'ends with'));
});
it("completes operator keywords after string", async () => {
const c = await get("'foobar' s‸") ?? [];
ist(c.some(x => x.label === 'starts with'));
});
it("does not complete anything when there's open string", async () => {
ist(null, await get("'foobar s‸"));
});
it("does not complete operators when identifier is expected", async () => {
const c = await get("smash_my_head(a‸)");
ist(!c?.some(x => x.label === 'and'));
});
it("completes object properties and methods", async () => {
const c = await get("obj.‸") ?? [];
ist(c.length, 3);
ist("property11", c[0].label);
ist("property22", c[1].label);
ist("firstMethod()", c[2].label);
});
it("completes object members with partial key", async () => {
const c = await get("obj.property1‸") ?? [];
ist(c.some(x => x.label === 'property11'));
});
it("completes object members after function call", async () => {
const c = await get("getObject().‸") ?? [];
ist(c.some(x => x.label === 'property11'));
ist(c.some(x => x.label === 'firstMethod()'));
});
it("completes only operators after method call", async () => {
const c = await get("obj.firstMethod() ‸") ?? [];
ist(!c.find(x => x.label === 'firstMethod()'));
ist(!c.find(x => x.label === 'obj'));
ist(c.find((x: Completion) => x.label === 'starts with'));
});
it("completes object members after method call", async () => {
const c = await get("obj.firstMethod().‸") ?? [];
ist(c.length, 3);
ist("property11", c[0].label);
ist("property22", c[1].label);
ist("firstMethod()", c[2].label);
});
it("completes object members after complex expression", async () => {
const c = await get("smash_my_head(obj.firstMethod()) + obj.‸") ?? [];
ist(c.length, 3);
ist("property11", c[0].label);
ist("property22", c[1].label);
ist("firstMethod()", c[2].label);
});
it("does not complete right after numbers", async () => {
ist(null, await get("123‸"));
});
it("does not complete right after operator keywords", async () => {
ist(null, await get("1 and‸"));
});
it("does not complete right after closing bracket", async () => {
ist(null, await get("(1)‸"));
});
it("does complete after ternary expression", async () => {
const c = await get("(foobar ? obj : false).‸") ?? [];
ist(c.length, 3);
ist("property11", c[0].label);
ist("property22", c[1].label);
ist("firstMethod()", c[2].label);
});
it("does complete after ternary expression shortcut", async () => {
const c = await get("(foobar ? obj).‸") ?? []
ist(c.length, 3);
ist("property11", c[0].label);
ist("property22", c[1].label);
ist("firstMethod()", c[2].label);
});
it("does complete after ternary expression shortcut 2", async () => {
const c = await get("(foobar ?: obj).‸") ?? [];
ist(c.length, 3);
ist("property11", c[0].label);
ist("property22", c[1].label);
ist("firstMethod()", c[2].label);
});
it("no completion inside comment", async () => {
ist(null, await get("1 /* o‸ */"));
});
});