@malloydata/malloy-tests 0.0.73 → 0.0.74-dev230815162117
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/dist/render/render.spec.js +51 -100
- package/dist/render/render.spec.js.map +1 -1
- package/dist/tags.spec.js +90 -152
- package/dist/tags.spec.js.map +1 -1
- package/package.json +6 -6
- package/src/render/render.spec.ts +52 -101
- package/src/tags.spec.ts +93 -164
package/src/tags.spec.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import {
|
|
24
|
+
import {TagDict, Tag} from '@malloydata/malloy';
|
|
25
25
|
import {runtimeFor} from './runtimes';
|
|
26
26
|
|
|
27
27
|
declare global {
|
|
@@ -64,138 +64,6 @@ expect.extend({
|
|
|
64
64
|
|
|
65
65
|
const runtime = runtimeFor('duckdb');
|
|
66
66
|
|
|
67
|
-
interface TestAnnotation {
|
|
68
|
-
inherits?: TestAnnotation;
|
|
69
|
-
blockNotes?: string[];
|
|
70
|
-
notes: string[];
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function unTestify(ta: TestAnnotation): Annotation {
|
|
74
|
-
const dloc = {
|
|
75
|
-
url: __filename,
|
|
76
|
-
range: {start: {character: 0, line: 0}, end: {character: 0, line: 0}},
|
|
77
|
-
};
|
|
78
|
-
const ret: Annotation = {
|
|
79
|
-
notes: ta.notes.map(t => {
|
|
80
|
-
return {text: t, at: dloc};
|
|
81
|
-
}),
|
|
82
|
-
};
|
|
83
|
-
if (ta.blockNotes) {
|
|
84
|
-
ret.blockNotes = ta.blockNotes.map(t => {
|
|
85
|
-
return {text: t, at: dloc};
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
if (ta.inherits) {
|
|
89
|
-
ret.inherits = unTestify(ta.inherits);
|
|
90
|
-
}
|
|
91
|
-
return ret;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
class TestTags extends Tags {
|
|
95
|
-
constructor(ta: TestAnnotation) {
|
|
96
|
-
super(unTestify(ta));
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function tstTaglist(a: TestAnnotation): string[] {
|
|
101
|
-
return new TestTags(a).getTagList();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function tstTagParse(s: string): MalloyTags {
|
|
105
|
-
return new TestTags({notes: [s]}).getMalloyTags();
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
describe('tag utilities', () => {
|
|
109
|
-
test('own/block ordering is correct', () => {
|
|
110
|
-
const testA = tstTaglist({
|
|
111
|
-
notes: ['#tag'],
|
|
112
|
-
blockNotes: ['#blockTag'],
|
|
113
|
-
});
|
|
114
|
-
expect(testA).toEqual(['#blockTag', '#tag']);
|
|
115
|
-
});
|
|
116
|
-
test('inherit ordering is correct', () => {
|
|
117
|
-
const testA = tstTaglist({
|
|
118
|
-
inherits: {notes: ['#inherited']},
|
|
119
|
-
notes: ['#tag'],
|
|
120
|
-
});
|
|
121
|
-
expect(testA).toEqual(['#inherited', '#tag']);
|
|
122
|
-
});
|
|
123
|
-
test.todo('check that results are annotated');
|
|
124
|
-
test('doc annotations', () => {
|
|
125
|
-
const a = tstTagParse('#" This is a doc string');
|
|
126
|
-
expect(a).toMatchObject({docStrings: ['This is a doc string']});
|
|
127
|
-
});
|
|
128
|
-
test('simple property', () => {
|
|
129
|
-
const a = tstTagParse('# linechart');
|
|
130
|
-
expect(a.properties).toHaveProperty('linechart', true);
|
|
131
|
-
});
|
|
132
|
-
test('negated property', () => {
|
|
133
|
-
const a = tstTagParse('# linechart -linechart');
|
|
134
|
-
expect(a.properties).not.toHaveProperty('linechart', true);
|
|
135
|
-
});
|
|
136
|
-
test('simple quoted property', () => {
|
|
137
|
-
const a = tstTagParse('# "linechart"');
|
|
138
|
-
expect(a.properties).toHaveProperty('linechart', true);
|
|
139
|
-
});
|
|
140
|
-
test('quoted property with " and space', () => {
|
|
141
|
-
const annotation = '# "a \\"chart\\""';
|
|
142
|
-
const a = tstTagParse(annotation);
|
|
143
|
-
expect(a.properties).toHaveProperty('a "chart"', true);
|
|
144
|
-
});
|
|
145
|
-
test('escaped non-quote', () => {
|
|
146
|
-
const annotation = '# "\\x"';
|
|
147
|
-
const a = tstTagParse(annotation);
|
|
148
|
-
expect(a.properties).toHaveProperty('x', true);
|
|
149
|
-
});
|
|
150
|
-
test('non-terminated string', () => {
|
|
151
|
-
const annotation = '# x="\\"';
|
|
152
|
-
const a = tstTagParse(annotation);
|
|
153
|
-
expect(a.properties).not.toHaveProperty('x', true);
|
|
154
|
-
});
|
|
155
|
-
test('quoted property with value', () => {
|
|
156
|
-
const a = tstTagParse('# "linechart"=yes');
|
|
157
|
-
expect(a.properties).toHaveProperty('linechart', 'yes');
|
|
158
|
-
});
|
|
159
|
-
test('property with simple value', () => {
|
|
160
|
-
const a = tstTagParse('# chart=line');
|
|
161
|
-
expect(a.properties).toHaveProperty('chart', 'line');
|
|
162
|
-
});
|
|
163
|
-
test('property with quoted value', () => {
|
|
164
|
-
const a = tstTagParse('# chart="line"');
|
|
165
|
-
expect(a.properties).toHaveProperty('chart', 'line');
|
|
166
|
-
});
|
|
167
|
-
test('spaces ignored', () => {
|
|
168
|
-
const a = tstTagParse('# chart = line ');
|
|
169
|
-
expect(a.properties).toHaveProperty('chart', 'line');
|
|
170
|
-
});
|
|
171
|
-
test('= with no value', () => {
|
|
172
|
-
expect(tstTagParse('# name = ')).toMatchObject({properties: {}});
|
|
173
|
-
});
|
|
174
|
-
test('multiple = with no value', () => {
|
|
175
|
-
expect(tstTagParse('# name = = me')).toMatchObject({properties: {}});
|
|
176
|
-
});
|
|
177
|
-
test('missing quote', () => {
|
|
178
|
-
expect(tstTagParse('# name = "no quote')).toMatchObject({properties: {}});
|
|
179
|
-
});
|
|
180
|
-
test('leading =', () => {
|
|
181
|
-
expect(tstTagParse('# =name ')).toMatchObject({properties: {}});
|
|
182
|
-
});
|
|
183
|
-
test('complex line', () => {
|
|
184
|
-
const a = tstTagParse('# a b=c "d"=e f="g" "h"="i" "j" k -a');
|
|
185
|
-
expect(a).toBeDefined();
|
|
186
|
-
if (a) {
|
|
187
|
-
expect(a.properties).toEqual({
|
|
188
|
-
b: 'c',
|
|
189
|
-
d: 'e',
|
|
190
|
-
f: 'g',
|
|
191
|
-
h: 'i',
|
|
192
|
-
j: true,
|
|
193
|
-
k: true,
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
|
|
199
67
|
type TagTestTuple = [string, TagDict];
|
|
200
68
|
describe('tagParse to Tag', () => {
|
|
201
69
|
const tagTests: TagTestTuple[] = [
|
|
@@ -343,6 +211,50 @@ describe('Tag access', () => {
|
|
|
343
211
|
const n = a?.numeric();
|
|
344
212
|
expect(n).toBeUndefined();
|
|
345
213
|
});
|
|
214
|
+
test('full text array', () => {
|
|
215
|
+
const strToParse = 'a=[b,c]';
|
|
216
|
+
const getTags = Tag.fromTagline(strToParse, undefined);
|
|
217
|
+
expect(getTags.log).toEqual([]);
|
|
218
|
+
const a = getTags.tag.tag('a');
|
|
219
|
+
expect(a).toBeDefined();
|
|
220
|
+
const ais = a?.textArray();
|
|
221
|
+
expect(ais).toEqual(['b', 'c']);
|
|
222
|
+
});
|
|
223
|
+
test('filtered text array', () => {
|
|
224
|
+
const strToParse = 'a=[b,c,{d}]';
|
|
225
|
+
const getTags = Tag.fromTagline(strToParse, undefined);
|
|
226
|
+
expect(getTags.log).toEqual([]);
|
|
227
|
+
const a = getTags.tag.tag('a');
|
|
228
|
+
expect(a).toBeDefined();
|
|
229
|
+
const ais = a?.textArray();
|
|
230
|
+
expect(ais).toEqual(['b', 'c']);
|
|
231
|
+
});
|
|
232
|
+
test('full numeric array', () => {
|
|
233
|
+
const strToParse = 'a=[1,2]';
|
|
234
|
+
const getTags = Tag.fromTagline(strToParse, undefined);
|
|
235
|
+
expect(getTags.log).toEqual([]);
|
|
236
|
+
const a = getTags.tag.tag('a');
|
|
237
|
+
expect(a).toBeDefined();
|
|
238
|
+
const ais = a?.numericArray();
|
|
239
|
+
expect(ais).toEqual([1, 2]);
|
|
240
|
+
});
|
|
241
|
+
test('filtered numeric array', () => {
|
|
242
|
+
const strToParse = 'a=[1,2,three]';
|
|
243
|
+
const getTags = Tag.fromTagline(strToParse, undefined);
|
|
244
|
+
expect(getTags.log).toEqual([]);
|
|
245
|
+
const a = getTags.tag.tag('a');
|
|
246
|
+
expect(a).toBeDefined();
|
|
247
|
+
const ais = a?.numericArray();
|
|
248
|
+
expect(ais).toEqual([1, 2]);
|
|
249
|
+
});
|
|
250
|
+
test('has', () => {
|
|
251
|
+
const strToParse = 'a b.d';
|
|
252
|
+
const getTags = Tag.fromTagline(strToParse, undefined);
|
|
253
|
+
expect(getTags.log).toEqual([]);
|
|
254
|
+
expect(getTags.tag.has('a')).toBeTruthy();
|
|
255
|
+
expect(getTags.tag.has('b', 'd')).toBeTruthy();
|
|
256
|
+
expect(getTags.tag.has('c')).toBeFalsy();
|
|
257
|
+
});
|
|
346
258
|
});
|
|
347
259
|
|
|
348
260
|
describe('## top level', () => {
|
|
@@ -355,41 +267,37 @@ describe('## top level', () => {
|
|
|
355
267
|
`
|
|
356
268
|
)
|
|
357
269
|
.getModel();
|
|
358
|
-
const modelDesc = model.getTags().getMalloyTags();
|
|
359
|
-
expect(modelDesc).toEqual({
|
|
360
|
-
properties: {propertyTag: true},
|
|
361
|
-
docStrings: ['Doc String\n'],
|
|
362
|
-
});
|
|
363
270
|
const modelTagLine = model.tagParse().tag;
|
|
364
271
|
expect(modelTagLine.has('propertyTag')).toBeTruthy();
|
|
272
|
+
expect(model.getTaglines(/^##"/)).toEqual(['##" Doc String\n']);
|
|
365
273
|
});
|
|
366
274
|
});
|
|
367
275
|
describe('tags in results', () => {
|
|
368
276
|
test('nameless query', async () => {
|
|
369
277
|
const loaded = runtime.loadQuery(
|
|
370
278
|
`
|
|
279
|
+
## modelDef=ok
|
|
371
280
|
sql: one is {connection: "duckdb" select: """SELECT 1 as one"""}
|
|
372
281
|
# b4query
|
|
373
|
-
query: # afterQuery
|
|
282
|
+
query: # afterQuery import=$(modelDef)
|
|
374
283
|
from_sql(one) -> { project: * }`
|
|
375
284
|
);
|
|
285
|
+
const qTag = {b4query: {}, afterQuery: {}, import: {eq: 'ok'}};
|
|
376
286
|
const query = await loaded.getPreparedQuery();
|
|
377
287
|
expect(query).toBeDefined();
|
|
378
|
-
|
|
379
|
-
expect(query.getTags().getTagList()).toEqual(wantTags);
|
|
288
|
+
expect(query.tagParse().tag).tagsAre(qTag);
|
|
380
289
|
const result = await loaded.run();
|
|
381
|
-
expect(result.
|
|
382
|
-
const queryTags = result.tagParse().tag;
|
|
383
|
-
expect(queryTags).tagsAre({b4query: {}, afterQuery: {}});
|
|
290
|
+
expect(result.tagParse().tag).tagsAre(qTag);
|
|
384
291
|
});
|
|
385
|
-
const
|
|
386
|
-
const wantTag = {BQ: {}, AQ: {}, Bis: {}, Ais: {}};
|
|
292
|
+
const wantTag = {BQ: {}, AQ: {}, Bis: {}, Ais: {}, import: {eq: 'ok'}};
|
|
387
293
|
test('named query', async () => {
|
|
388
294
|
const loaded = runtime.loadQuery(
|
|
389
295
|
`
|
|
390
296
|
sql: one is {connection: "duckdb" select: """SELECT 1 as one"""}
|
|
297
|
+
## modelDef=ok
|
|
391
298
|
# BQ
|
|
392
299
|
query: # AQ
|
|
300
|
+
# import=$(modelDef)
|
|
393
301
|
theName
|
|
394
302
|
# Bis
|
|
395
303
|
is
|
|
@@ -399,17 +307,17 @@ describe('tags in results', () => {
|
|
|
399
307
|
);
|
|
400
308
|
const query = await loaded.getPreparedQuery();
|
|
401
309
|
expect(query).toBeDefined();
|
|
402
|
-
expect(query.
|
|
310
|
+
expect(query.tagParse().tag).tagsAre(wantTag);
|
|
403
311
|
const result = await loaded.run();
|
|
404
|
-
expect(result.getTags().getTagList()).toEqual(wantTags);
|
|
405
312
|
expect(result.tagParse().tag).tagsAre(wantTag);
|
|
406
313
|
});
|
|
407
314
|
test('turtle query', async () => {
|
|
408
315
|
const loaded = runtime.loadQuery(
|
|
409
316
|
`
|
|
317
|
+
## modelDef=ok
|
|
410
318
|
sql: one is {connection: "duckdb" select: """SELECT 1 as one"""}
|
|
411
319
|
query: from_sql(one) + {
|
|
412
|
-
# BQ
|
|
320
|
+
# BQ import=$(modelDef)
|
|
413
321
|
query: # AQ
|
|
414
322
|
in_one
|
|
415
323
|
# Bis
|
|
@@ -421,20 +329,18 @@ describe('tags in results', () => {
|
|
|
421
329
|
);
|
|
422
330
|
const query = await loaded.getPreparedQuery();
|
|
423
331
|
expect(query).toBeDefined();
|
|
424
|
-
expect(query.getTags().getTagList()).toEqual(wantTags);
|
|
425
332
|
expect(query.tagParse().tag).tagsAre(wantTag);
|
|
426
333
|
const result = await loaded.run();
|
|
427
|
-
const tl = result.getTags().getTagList();
|
|
428
|
-
expect(tl).toEqual(wantTags);
|
|
429
334
|
expect(result.tagParse().tag).tagsAre(wantTag);
|
|
430
335
|
});
|
|
431
336
|
test('atomic field has tag', async () => {
|
|
432
337
|
const loaded = runtime.loadQuery(
|
|
433
338
|
`
|
|
339
|
+
## modelDef=ok
|
|
434
340
|
sql: one is {connection: "duckdb" select: """SELECT 1 as one"""}
|
|
435
341
|
query: from_sql(one) -> {
|
|
436
342
|
project:
|
|
437
|
-
# note1
|
|
343
|
+
# note1 import=$(modelDef)
|
|
438
344
|
one
|
|
439
345
|
}`
|
|
440
346
|
);
|
|
@@ -442,12 +348,14 @@ describe('tags in results', () => {
|
|
|
442
348
|
const shape = result.resultExplore;
|
|
443
349
|
const one = shape.getFieldByName('one');
|
|
444
350
|
expect(one).toBeDefined();
|
|
445
|
-
|
|
446
|
-
expect(
|
|
351
|
+
const tp = one.tagParse();
|
|
352
|
+
expect(tp.log).toEqual([]);
|
|
353
|
+
expect(tp.tag).tagsAre({note1: {}, import: {eq: 'ok'}});
|
|
447
354
|
});
|
|
448
355
|
test('nested query has tag', async () => {
|
|
449
356
|
const loaded = runtime.loadQuery(
|
|
450
357
|
`
|
|
358
|
+
## modelDef=ok
|
|
451
359
|
sql: one is {connection: "duckdb" select: """SELECT 1 as one"""}
|
|
452
360
|
source: malloy_one is from_sql(one) + {
|
|
453
361
|
query: in_one is {
|
|
@@ -457,7 +365,7 @@ describe('tags in results', () => {
|
|
|
457
365
|
group_by: one
|
|
458
366
|
# note1
|
|
459
367
|
nest:
|
|
460
|
-
# note2
|
|
368
|
+
# note2 import=$(modelDef)
|
|
461
369
|
in_one
|
|
462
370
|
}
|
|
463
371
|
}
|
|
@@ -467,8 +375,11 @@ describe('tags in results', () => {
|
|
|
467
375
|
const shape = result.resultExplore;
|
|
468
376
|
const one = shape.getFieldByName('in_one');
|
|
469
377
|
expect(one).toBeDefined();
|
|
470
|
-
expect(one.
|
|
471
|
-
|
|
378
|
+
expect(one.tagParse().tag).tagsAre({
|
|
379
|
+
note1: {},
|
|
380
|
+
note2: {},
|
|
381
|
+
import: {eq: 'ok'},
|
|
382
|
+
});
|
|
472
383
|
});
|
|
473
384
|
test('render usage test case', async () => {
|
|
474
385
|
const loaded = runtime.loadQuery(
|
|
@@ -501,14 +412,32 @@ describe('tags in results', () => {
|
|
|
501
412
|
const height = shape.getFieldByName('height');
|
|
502
413
|
const age = shape.getFieldByName('age');
|
|
503
414
|
const name = shape.getFieldByName('name');
|
|
504
|
-
expect(height.getTags().getMalloyTags()).toMatchObject({
|
|
505
|
-
properties: {barchart: true},
|
|
506
|
-
});
|
|
507
415
|
expect(height.tagParse().tag).tagsAre({barchart: {}});
|
|
508
|
-
expect(age.getTags().getMalloyTags()).toMatchObject({
|
|
509
|
-
properties: {barchart: true},
|
|
510
|
-
});
|
|
511
416
|
expect(age.tagParse().tag).tagsAre({barchart: {}});
|
|
512
417
|
expect(name.tagParse().tag).tagsAre({name: {}});
|
|
513
418
|
});
|
|
419
|
+
test('User defines scopes nest properly', async () => {
|
|
420
|
+
const loaded = runtime.loadQuery(
|
|
421
|
+
`
|
|
422
|
+
## scope=model
|
|
423
|
+
sql: one is {connection: "duckdb" select: """SELECT 1 as one"""}
|
|
424
|
+
query: from_sql(one) -> {
|
|
425
|
+
project:
|
|
426
|
+
# valueFrom=$(scope)
|
|
427
|
+
one
|
|
428
|
+
}`
|
|
429
|
+
);
|
|
430
|
+
const result = await loaded.run();
|
|
431
|
+
const shape = result.resultExplore;
|
|
432
|
+
const field = shape.getFieldByName('one');
|
|
433
|
+
expect(field).toBeDefined();
|
|
434
|
+
let tp = field.tagParse().tag;
|
|
435
|
+
expect(tp).tagsAre({valueFrom: {eq: 'model'}});
|
|
436
|
+
const sessionScope = Tag.fromTagline('# scope=session', undefined).tag;
|
|
437
|
+
tp = field.tagParse({scopes: [sessionScope]}).tag;
|
|
438
|
+
expect(tp).tagsAre({valueFrom: {eq: 'session'}});
|
|
439
|
+
const globalScope = Tag.fromTagline('# scope=global', undefined).tag;
|
|
440
|
+
tp = field.tagParse({scopes: [globalScope, sessionScope]}).tag;
|
|
441
|
+
expect(tp).tagsAre({valueFrom: {eq: 'global'}});
|
|
442
|
+
});
|
|
514
443
|
});
|