@malloydata/malloy 0.0.190 → 0.0.191-dev240925232958

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 (90) hide show
  1. package/dist/lang/ast/expressions/constant-expression.js +4 -1
  2. package/dist/lang/ast/expressions/expr-aggregate-function.js +25 -14
  3. package/dist/lang/ast/expressions/expr-alternation-tree.js +1 -3
  4. package/dist/lang/ast/expressions/expr-cast.js +1 -1
  5. package/dist/lang/ast/expressions/expr-coalesce.js +1 -1
  6. package/dist/lang/ast/expressions/expr-func.js +20 -31
  7. package/dist/lang/ast/expressions/expr-granular-time.js +1 -1
  8. package/dist/lang/ast/expressions/expr-id-reference.js +1 -3
  9. package/dist/lang/ast/expressions/expr-props.js +8 -8
  10. package/dist/lang/ast/expressions/expr-time-extract.js +7 -13
  11. package/dist/lang/ast/expressions/expr-ungroup.js +4 -8
  12. package/dist/lang/ast/expressions/for-range.js +2 -3
  13. package/dist/lang/ast/expressions/function-ordering.js +6 -6
  14. package/dist/lang/ast/expressions/partial-compare.js +1 -3
  15. package/dist/lang/ast/expressions/pick-when.js +30 -20
  16. package/dist/lang/ast/expressions/range.js +1 -1
  17. package/dist/lang/ast/expressions/top-by.js +1 -1
  18. package/dist/lang/ast/field-space/dynamic-space.js +1 -1
  19. package/dist/lang/ast/field-space/index-field-space.js +6 -6
  20. package/dist/lang/ast/field-space/parameter-space.js +14 -3
  21. package/dist/lang/ast/field-space/project-field-space.js +1 -1
  22. package/dist/lang/ast/field-space/query-spaces.d.ts +2 -1
  23. package/dist/lang/ast/field-space/query-spaces.js +7 -6
  24. package/dist/lang/ast/field-space/reference-field.js +1 -1
  25. package/dist/lang/ast/field-space/refined-space.js +3 -3
  26. package/dist/lang/ast/field-space/static-space.js +11 -2
  27. package/dist/lang/ast/parameters/has-parameter.js +4 -4
  28. package/dist/lang/ast/query-builders/index-builder.js +4 -4
  29. package/dist/lang/ast/query-builders/project-builder.js +2 -2
  30. package/dist/lang/ast/query-builders/reduce-builder.js +5 -5
  31. package/dist/lang/ast/query-elements/anonymous-query.js +1 -1
  32. package/dist/lang/ast/query-elements/query-reference.js +2 -2
  33. package/dist/lang/ast/query-items/field-declaration.js +7 -4
  34. package/dist/lang/ast/query-items/field-references.js +1 -1
  35. package/dist/lang/ast/query-items/typecheck_utils.js +31 -11
  36. package/dist/lang/ast/query-properties/filters.js +4 -4
  37. package/dist/lang/ast/query-properties/ordering.js +3 -3
  38. package/dist/lang/ast/query-properties/qop-desc.js +2 -2
  39. package/dist/lang/ast/query-properties/top.js +5 -5
  40. package/dist/lang/ast/query-utils.js +1 -1
  41. package/dist/lang/ast/source-elements/named-source.d.ts +2 -2
  42. package/dist/lang/ast/source-elements/named-source.js +12 -13
  43. package/dist/lang/ast/source-elements/refined-source.js +9 -7
  44. package/dist/lang/ast/source-elements/sql-source.js +5 -5
  45. package/dist/lang/ast/source-elements/table-source.js +2 -2
  46. package/dist/lang/ast/source-properties/joins.js +6 -6
  47. package/dist/lang/ast/source-properties/renames.js +3 -3
  48. package/dist/lang/ast/source-query-elements/source-query-element.d.ts +2 -1
  49. package/dist/lang/ast/source-query-elements/source-query-element.js +3 -2
  50. package/dist/lang/ast/source-query-elements/sq-arrow.js +2 -2
  51. package/dist/lang/ast/source-query-elements/sq-extend.js +1 -1
  52. package/dist/lang/ast/source-query-elements/sq-reference.js +5 -5
  53. package/dist/lang/ast/source-query-elements/sq-refine.js +2 -2
  54. package/dist/lang/ast/source-query-elements/sq-source.js +1 -1
  55. package/dist/lang/ast/sql-elements/sql-string.js +2 -2
  56. package/dist/lang/ast/statements/define-query.js +2 -2
  57. package/dist/lang/ast/statements/define-source.js +3 -3
  58. package/dist/lang/ast/statements/import-statement.js +7 -7
  59. package/dist/lang/ast/struct-utils.js +1 -1
  60. package/dist/lang/ast/types/expression-def.js +21 -22
  61. package/dist/lang/ast/types/lookup-result.d.ts +5 -1
  62. package/dist/lang/ast/types/malloy-element.d.ts +8 -5
  63. package/dist/lang/ast/types/malloy-element.js +33 -27
  64. package/dist/lang/ast/view-elements/qop-desc-view.js +3 -3
  65. package/dist/lang/ast/view-elements/reference-view.js +6 -6
  66. package/dist/lang/ast/view-elements/refine-utils.js +6 -6
  67. package/dist/lang/ast/view-elements/view-arrow.js +1 -1
  68. package/dist/lang/ast/view-elements/view-refine.js +1 -1
  69. package/dist/lang/malloy-to-ast.d.ts +7 -7
  70. package/dist/lang/malloy-to-ast.js +56 -55
  71. package/dist/lang/parse-log.d.ts +323 -6
  72. package/dist/lang/parse-log.js +62 -16
  73. package/dist/lang/parse-malloy.d.ts +5 -3
  74. package/dist/lang/parse-malloy.js +22 -31
  75. package/dist/lang/test/annotation.spec.js +4 -4
  76. package/dist/lang/test/expressions.spec.js +93 -74
  77. package/dist/lang/test/imports.spec.js +5 -5
  78. package/dist/lang/test/lenses.spec.js +18 -18
  79. package/dist/lang/test/literals.spec.js +1 -1
  80. package/dist/lang/test/locations.spec.js +5 -5
  81. package/dist/lang/test/parameters.spec.js +34 -34
  82. package/dist/lang/test/parse-expects.d.ts +10 -28
  83. package/dist/lang/test/parse-expects.js +38 -33
  84. package/dist/lang/test/parse.spec.js +49 -49
  85. package/dist/lang/test/query.spec.js +82 -82
  86. package/dist/lang/test/source.spec.js +6 -6
  87. package/dist/lang/test/test-translator.d.ts +19 -0
  88. package/dist/lang/test/test-translator.js +20 -1
  89. package/dist/tags.js +4 -2
  90. package/package.json +1 -1
@@ -73,7 +73,7 @@ describe('lenses', () => {
73
73
  view: d is { group_by: n }
74
74
  }
75
75
  run: x -> d + d
76
- `).translationToFailWith('overlapping fields in refinement: n');
76
+ `).toLog((0, test_translator_1.errorMessage)('overlapping fields in refinement: n'));
77
77
  });
78
78
  test('cannot override limit', () => {
79
79
  expect((0, test_translator_1.markSource) `
@@ -82,7 +82,7 @@ describe('lenses', () => {
82
82
  view: d2 is { group_by: n2 is 2; limit: 20 }
83
83
  }
84
84
  run: x -> d1 + d2
85
- `).translationToFailWith('refinement cannot override existing limit');
85
+ `).toLog((0, test_translator_1.errorMessage)('refinement cannot override existing limit'));
86
86
  });
87
87
  test('cannot override ordering', () => {
88
88
  expect((0, test_translator_1.markSource) `
@@ -91,7 +91,7 @@ describe('lenses', () => {
91
91
  view: d2 is { group_by: n2 is 1; order_by: n2 }
92
92
  }
93
93
  run: x -> d1 + d2
94
- `).translationToFailWith('refinement cannot override existing ordering');
94
+ `).toLog((0, test_translator_1.errorMessage)('refinement cannot override existing ordering'));
95
95
  });
96
96
  test('weird issue with order by constant group by', () => {
97
97
  expect((0, test_translator_1.markSource) `
@@ -122,7 +122,7 @@ describe('lenses', () => {
122
122
  run: x -> proj + grp
123
123
  run: x -> idx + grp
124
124
  run: x -> idx + proj
125
- `).translationToFailWith('cannot refine reduce view with project view', 'cannot refine reduce view with index view', 'cannot refine project view with index view', 'cannot refine project view with reduce view', 'cannot refine index view with reduce view', 'cannot refine index view with project view');
125
+ `).toLog((0, test_translator_1.errorMessage)('cannot refine reduce view with project view'), (0, test_translator_1.errorMessage)('cannot refine reduce view with index view'), (0, test_translator_1.errorMessage)('cannot refine project view with index view'), (0, test_translator_1.errorMessage)('cannot refine project view with reduce view'), (0, test_translator_1.errorMessage)('cannot refine index view with reduce view'), (0, test_translator_1.errorMessage)('cannot refine index view with project view'));
126
126
  });
127
127
  test('can reference dimension at head of query when experiment is enabled', () => {
128
128
  expect((0, test_translator_1.markSource) `
@@ -158,7 +158,7 @@ describe('lenses', () => {
158
158
  view: two_stage is { group_by: a is 1 } -> { group_by: a }
159
159
  }
160
160
  run: x -> one_stage + two_stage
161
- `).translationToFailWith('named refinement `two_stage` must have exactly one stage');
161
+ `).toLog((0, test_translator_1.errorMessage)('named refinement `two_stage` must have exactly one stage'));
162
162
  });
163
163
  test('cannot refine with literal multi-stage', () => {
164
164
  expect((0, test_translator_1.markSource) `
@@ -167,7 +167,7 @@ describe('lenses', () => {
167
167
  view: two_stage is { group_by: a is 1 } -> { group_by: a }
168
168
  }
169
169
  run: x -> one_stage + ({ group_by: a is 1 } -> { group_by: a })
170
- `).translationToFailWith('A multi-segment view cannot be used as a refinement');
170
+ `).toLog((0, test_translator_1.errorMessage)('A multi-segment view cannot be used as a refinement'));
171
171
  });
172
172
  test('can reference dimension in refinement when experiment is enabled', () => {
173
173
  expect((0, test_translator_1.markSource) `
@@ -217,7 +217,7 @@ describe('lenses', () => {
217
217
  join_one: y is a on true
218
218
  view: m is { aggregate: c is count() }
219
219
  }
220
- run: x -> m + y + { limit: 1 }`).translationToFailWith('named refinement `y` must be a view, found a struct');
220
+ run: x -> m + y + { limit: 1 }`).toLog((0, test_translator_1.errorMessage)('named refinement `y` must be a view, found a struct'));
221
221
  });
222
222
  test('cannot use view from join as whole pipeline', () => {
223
223
  expect((0, test_translator_1.markSource) `
@@ -227,7 +227,7 @@ describe('lenses', () => {
227
227
  } on true
228
228
  }
229
229
  run: x -> y.z
230
- `).translationToFailWith('Cannot use view from join');
230
+ `).toLog((0, test_translator_1.errorMessage)('Cannot use view from join'));
231
231
  });
232
232
  test('cannot use view from join in nest', () => {
233
233
  expect((0, test_translator_1.markSource) `
@@ -237,7 +237,7 @@ describe('lenses', () => {
237
237
  } on true
238
238
  }
239
239
  run: x -> { nest: y.z }
240
- `).translationToFailWith('Cannot use view from join');
240
+ `).toLog((0, test_translator_1.errorMessage)('Cannot use view from join'));
241
241
  });
242
242
  test('cannot use view from join as nest view head', () => {
243
243
  expect((0, test_translator_1.markSource) `
@@ -247,7 +247,7 @@ describe('lenses', () => {
247
247
  } on true
248
248
  }
249
249
  run: x -> { nest: y.z + { limit: 1 } }
250
- `).translationToFailWith('Cannot use view from join');
250
+ `).toLog((0, test_translator_1.errorMessage)('Cannot use view from join'));
251
251
  });
252
252
  test('cannot use view from join as lens in query', () => {
253
253
  expect((0, test_translator_1.markSource) `
@@ -257,7 +257,7 @@ describe('lenses', () => {
257
257
  } on true
258
258
  }
259
259
  run: x -> ai + y.z
260
- `).translationToFailWith('Cannot use view from join as refinement');
260
+ `).toLog((0, test_translator_1.errorMessage)('Cannot use view from join as refinement'));
261
261
  });
262
262
  test('cannot use view from join as lens in nest', () => {
263
263
  expect((0, test_translator_1.markSource) `
@@ -267,7 +267,7 @@ describe('lenses', () => {
267
267
  } on true
268
268
  }
269
269
  run: x -> { nest: ai + y.z }
270
- `).translationToFailWith('Cannot use view from join as refinement');
270
+ `).toLog((0, test_translator_1.errorMessage)('Cannot use view from join as refinement'));
271
271
  });
272
272
  test('can nest dimension with refinement when experiment is enabled', () => {
273
273
  expect((0, test_translator_1.markSource) `
@@ -286,7 +286,7 @@ describe('lenses', () => {
286
286
  view: d is { group_by: n }
287
287
  }
288
288
  run: x -> d + b
289
- `).translationToFailWith('named refinement `b` must be a view, found a struct');
289
+ `).toLog((0, test_translator_1.errorMessage)('named refinement `b` must be a view, found a struct'));
290
290
  });
291
291
  test('cannot reference field in LHS of refinement in group_by', () => {
292
292
  expect((0, test_translator_1.markSource) `
@@ -294,7 +294,7 @@ describe('lenses', () => {
294
294
  view: v is { group_by: i is 1 }
295
295
  }
296
296
  run: x -> v + { group_by: j is ${'i'} }
297
- `).translationToFailWith("'i' is not defined");
297
+ `).toLog((0, test_translator_1.errorMessage)("'i' is not defined"));
298
298
  });
299
299
  test('cannot named-refine multi-stage query', () => {
300
300
  expect((0, test_translator_1.markSource) `
@@ -306,7 +306,7 @@ describe('lenses', () => {
306
306
  }
307
307
  run: x -> multi + d
308
308
  run: x -> d + multi
309
- `).translationToFailWith('Named refinements of multi-stage views are not supported', 'named refinement `multi` must have exactly one stage');
309
+ `).toLog((0, test_translator_1.errorMessage)('Named refinements of multi-stage views are not supported'), (0, test_translator_1.errorMessage)('named refinement `multi` must have exactly one stage'));
310
310
  });
311
311
  });
312
312
  describe('partial views', () => {
@@ -342,7 +342,7 @@ describe('partial views', () => {
342
342
  run: a -> {
343
343
  nest: { group_by: astr }
344
344
  }
345
- `).translationToFailWith('`nest:` view requires a name (add `nest_name is ...`)');
345
+ `).toLog((0, test_translator_1.errorMessage)('`nest:` view requires a name (add `nest_name is ...`)'));
346
346
  });
347
347
  test.skip('partial with index', () => {
348
348
  expect((0, test_translator_1.markSource) `
@@ -358,7 +358,7 @@ describe('partial views', () => {
358
358
  view: bad1 is { where: true }
359
359
  view: bad2 is { where: true } + { where: false }
360
360
  }
361
- `).translationToFailWith("Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)", "Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)");
361
+ `).toLog((0, test_translator_1.error)('ambiguous-view-type'), (0, test_translator_1.error)('ambiguous-view-type'));
362
362
  });
363
363
  test('disallow chains that have no fields in multi-stage', () => {
364
364
  expect((0, test_translator_1.markSource) `
@@ -369,7 +369,7 @@ describe('partial views', () => {
369
369
  }
370
370
  run: x -> v -> { where: true }
371
371
  run: x -> { where: true } -> { group_by: undef }
372
- `).translationToFailWith("Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)", "'undef' is not defined", "Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)", "Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)", "'undef' is not defined", "Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)");
372
+ `).toLog((0, test_translator_1.error)('ambiguous-view-type'), (0, test_translator_1.errorMessage)("'undef' is not defined"), (0, test_translator_1.error)('ambiguous-view-type'), (0, test_translator_1.error)('ambiguous-view-type'), (0, test_translator_1.errorMessage)("'undef' is not defined"), (0, test_translator_1.error)('ambiguous-view-type'));
373
373
  });
374
374
  test('copy of view with refinement should work', () => {
375
375
  expect((0, test_translator_1.markSource) `
@@ -219,7 +219,7 @@ describe('literals', () => {
219
219
  expect(m).toParse();
220
220
  });
221
221
  test('timezone with illegal query', () => {
222
- expect(`run: a->{timezone: """${tz}%{ab->aturtle}"""; select: *}`).translationToFailWith('%{ query } illegal in this string');
222
+ expect(`run: a->{timezone: """${tz}%{ab->aturtle}"""; select: *}`).toLog((0, test_translator_1.errorMessage)('%{ query } illegal in this string'));
223
223
  });
224
224
  test('table single quote', () => {
225
225
  const m = new test_translator_1.TestTranslator("source: n is bigquery.table('n')");
@@ -210,19 +210,19 @@ describe('source locations', () => {
210
210
  expect(y.location).toMatchObject(source.locations[0]);
211
211
  });
212
212
  test('undefined query location', () => {
213
- expect((0, test_translator_1.model) `run: ${'xyz'}`).translationToFailWith("Reference to undefined object 'xyz'");
213
+ expect((0, test_translator_1.model) `run: ${'xyz'}`).toLog((0, test_translator_1.errorMessage)("Reference to undefined object 'xyz'"));
214
214
  });
215
215
  test('undefined field reference', () => {
216
- expect((0, test_translator_1.model) `run: a -> { group_by: ${'xyz'} }`).translationToFailWith("'xyz' is not defined");
216
+ expect((0, test_translator_1.model) `run: a -> { group_by: ${'xyz'} }`).toLog((0, test_translator_1.errorMessage)("'xyz' is not defined"));
217
217
  });
218
218
  test('bad query', () => {
219
- expect((0, test_translator_1.model) `run: a -> { group_by: astr; ${'select: *'} }`).translationToFailWith(/Not legal in grouping query/);
219
+ expect((0, test_translator_1.model) `run: a -> { group_by: astr; ${'select: *'} }`).toLog((0, test_translator_1.errorMessage)(/Not legal in grouping query/));
220
220
  });
221
221
  test.skip('undefined field reference in top', () => {
222
- expect((0, test_translator_1.model) `run: a -> { group_by: one is 1; top: 1 by ${'xyz'} }`).translationToFailWith("'xyz' is not defined");
222
+ expect((0, test_translator_1.model) `run: a -> { group_by: one is 1; top: 1 by ${'xyz'} }`).toLog((0, test_translator_1.errorMessage)("'xyz' is not defined"));
223
223
  });
224
224
  test.skip('undefined field reference in order_by', () => {
225
- expect((0, test_translator_1.model) `run: a -> { group_by: one is 1; order_by: ${'xyz'} }`).translationToFailWith("'xyz' is not defined");
225
+ expect((0, test_translator_1.model) `run: a -> { group_by: one is 1; order_by: ${'xyz'} }`).toLog((0, test_translator_1.errorMessage)("'xyz' is not defined"));
226
226
  });
227
227
  });
228
228
  describe('source references', () => {
@@ -31,19 +31,19 @@ describe('parameters', () => {
31
31
  expect((0, test_translator_1.markSource) `
32
32
  ##! experimental.parameters
33
33
  source: ab_new(param::number is ${'"hello"'}) is ab
34
- `).translationToFailWith('Default value for parameter does not match declared type `number`');
34
+ `).toLog((0, test_translator_1.errorMessage)('Default value for parameter does not match declared type `number`'));
35
35
  });
36
36
  test('error if paramter has no type or value', () => {
37
37
  expect((0, test_translator_1.markSource) `
38
38
  ##! experimental.parameters
39
39
  source: ab_new(param) is ab
40
- `).translationToFailWith('Parameter must have default value or declared type');
40
+ `).toLog((0, test_translator_1.errorMessage)('Parameter must have default value or declared type'));
41
41
  });
42
42
  test('error if paramter type is null', () => {
43
43
  expect((0, test_translator_1.markSource) `
44
44
  ##! experimental.parameters
45
45
  source: ab_new(param is null) is ab
46
- `).translationToFailWith('Default value cannot have type `null` unless parameter type is also specified');
46
+ `).toLog((0, test_translator_1.errorMessage)('Default value cannot have type `null` unless parameter type is also specified'));
47
47
  });
48
48
  test('allowed to write null::string', () => {
49
49
  expect(`
@@ -70,13 +70,13 @@ describe('parameters', () => {
70
70
  expect((0, test_translator_1.markSource) `
71
71
  ##! experimental.parameters
72
72
  source: ab_new(param is 10 to 20) is ab
73
- `).translationToFailWith('A Range is not a value');
73
+ `).toLog((0, test_translator_1.errorMessage)('A Range is not a value'));
74
74
  });
75
75
  test('no additional error if default value type is error', () => {
76
76
  expect((0, test_translator_1.markSource) `
77
77
  ##! experimental.parameters
78
78
  source: ab_new(param::number is 1 + ${'"foo"'}) is ab
79
- `).translationToFailWith("The '+' operator requires a number, not a 'string'");
79
+ `).toLog((0, test_translator_1.errorMessage)("The '+' operator requires a number, not a 'string'"));
80
80
  });
81
81
  test('can declare parameter with inferred type', () => {
82
82
  expect(`
@@ -242,21 +242,21 @@ describe('parameters', () => {
242
242
  view: all_fields is { select: * }
243
243
  }
244
244
  run: ab_new(param is 1) -> all_fields -> { select: ${'param'} }
245
- `).translationToFailWith("'param' is not defined");
245
+ `).toLog((0, test_translator_1.errorMessage)("'param' is not defined"));
246
246
  });
247
247
  test('cannot reference renamed param in query against source', () => {
248
248
  expect((0, test_translator_1.markSource) `
249
249
  ##! experimental.parameters
250
250
  source: ab_new(param::number) is ab
251
251
  run: ab_new(param is 1) -> { select: p is ${'param'} }
252
- `).translationToFailWith("'param' is not defined");
252
+ `).toLog((0, test_translator_1.errorMessage)("'param' is not defined"));
253
253
  });
254
254
  test('cannot reference param in query against source', () => {
255
255
  expect((0, test_translator_1.markSource) `
256
256
  ##! experimental.parameters
257
257
  source: ab_new(param::number) is ab
258
258
  run: ab_new(param is 1) -> { select: ${'param'} }
259
- `).translationToFailWith("'param' is not defined");
259
+ `).toLog((0, test_translator_1.errorMessage)("'param' is not defined"));
260
260
  });
261
261
  test('cannot reference param in source extension', () => {
262
262
  expect((0, test_translator_1.markSource) `
@@ -265,7 +265,7 @@ describe('parameters', () => {
265
265
  source: x is ab_new(param is 1) extend {
266
266
  dimension: param_copy is ${'param'}
267
267
  }
268
- `).translationToFailWith("'param' is not defined");
268
+ `).toLog((0, test_translator_1.errorMessage)("'param' is not defined"));
269
269
  });
270
270
  test('cannot reference param in in-query source extension', () => {
271
271
  expect((0, test_translator_1.markSource) `
@@ -277,14 +277,14 @@ describe('parameters', () => {
277
277
  }
278
278
  group_by: param_copy
279
279
  }
280
- `).translationToFailWith("'param' is not defined");
280
+ `).toLog((0, test_translator_1.errorMessage)("'param' is not defined"));
281
281
  });
282
282
  test('can reference field in source in argument', () => {
283
283
  expect((0, test_translator_1.markSource) `
284
284
  ##! experimental.parameters
285
285
  source: ab_new(param::number) is ab
286
286
  run: ab_new(param is ${'ai'}) -> { select: * }
287
- `).translationToFailWith('`ai` is not defined');
287
+ `).toLog((0, test_translator_1.errorMessage)('`ai` is not defined'));
288
288
  });
289
289
  test('can pass through parameter to joined source (shorthand)', () => {
290
290
  expect(`
@@ -384,7 +384,7 @@ describe('parameters', () => {
384
384
  ##! experimental.parameters
385
385
  source: ab_new(param::number) is ab
386
386
  run: ab_new(param is 1) -> { select: ${'param'} }
387
- `).translationToFailWith("'param' is not defined");
387
+ `).toLog((0, test_translator_1.errorMessage)("'param' is not defined"));
388
388
  });
389
389
  test('can reference param in view in source', () => {
390
390
  expect(`
@@ -407,13 +407,13 @@ describe('parameters', () => {
407
407
  ##! experimental.parameters
408
408
  source: ab_new(param::number) is ab
409
409
  run: ab_new(param is 1) -> { select: p is ${'param'} }
410
- `).translationToFailWith("'param' is not defined");
410
+ `).toLog((0, test_translator_1.errorMessage)("'param' is not defined"));
411
411
  });
412
412
  test('error when declaring parameter twice', () => {
413
413
  expect((0, test_translator_1.markSource) `
414
414
  ##! experimental.parameters
415
415
  source: ab_new(param::number, ${'param::number'}) is ab
416
- `).translationToFailWith('Cannot redefine parameter `param`');
416
+ `).toLog((0, test_translator_1.errorMessage)('Cannot redefine parameter `param`'));
417
417
  });
418
418
  // This behavior will likely change in the future; but in the meantime, this
419
419
  // safeguards against some confusion about parameter scoping
@@ -423,7 +423,7 @@ describe('parameters', () => {
423
423
  source: ab_new(ai::string) is ab extend {
424
424
  dimension: foo is upper(ai)
425
425
  }
426
- `).translationToFailWith('No matching overload for function upper(number)', 'Illegal shadowing of field `ai` by parameter with the same name');
426
+ `).toLog((0, test_translator_1.errorMessage)('No matching overload for function upper(number)'), (0, test_translator_1.errorMessage)('Illegal shadowing of field `ai` by parameter with the same name'));
427
427
  });
428
428
  test('can shadow field that is excepted', () => {
429
429
  expect(`
@@ -438,7 +438,7 @@ describe('parameters', () => {
438
438
  expect((0, test_translator_1.markSource) `
439
439
  ##! experimental.parameters
440
440
  source: ab_new(${'ai::string'}) is ab
441
- `).translationToFailWith('Illegal shadowing of field `ai` by parameter with the same name');
441
+ `).toLog((0, test_translator_1.errorMessage)('Illegal shadowing of field `ai` by parameter with the same name'));
442
442
  });
443
443
  test('do not inherit parameters from base source', () => {
444
444
  expect((0, test_translator_1.markSource) `
@@ -446,7 +446,7 @@ describe('parameters', () => {
446
446
  source: ab_new(param::number) is ab
447
447
  source: ab_new_new is ab_new(param is 1)
448
448
  run: ab_new_new(${'param'} is 2) -> { select: * }
449
- `).translationToFailWith('`ab_new_new` has no declared parameter named `param`');
449
+ `).toLog((0, test_translator_1.errorMessage)('`ab_new_new` has no declared parameter named `param`'));
450
450
  });
451
451
  test('error when declaring field with same name as parameter', () => {
452
452
  expect((0, test_translator_1.markSource) `
@@ -454,12 +454,12 @@ describe('parameters', () => {
454
454
  source: ab_new(param::number) is ab extend {
455
455
  dimension: param is 1
456
456
  }
457
- `).translationToFailWith("Cannot redefine 'param'");
457
+ `).toLog((0, test_translator_1.errorMessage)("Cannot redefine 'param'"));
458
458
  });
459
459
  test('error when declaring parameter without experiment enabled', () => {
460
460
  expect((0, test_translator_1.markSource) `
461
461
  source: ab_new(param::number) is ab
462
- `).translationToFailWith("Experimental flag 'parameters' required to enable this feature");
462
+ `).toLog((0, test_translator_1.error)('experiment-not-enabled', { experimentId: 'parameters' }));
463
463
  });
464
464
  test('cannot except parameter from extended source', () => {
465
465
  expect((0, test_translator_1.markSource) `
@@ -468,7 +468,7 @@ describe('parameters', () => {
468
468
  source: ab_new_new(param_b::number) is ab_new(param_a is 1) extend {
469
469
  except: param_a
470
470
  }
471
- `).translationToFailWith('`param_a` is not defined');
471
+ `).toLog((0, test_translator_1.errorMessage)('`param_a` is not defined'));
472
472
  });
473
473
  test('cannot except parameter in direct extend', () => {
474
474
  expect((0, test_translator_1.markSource) `
@@ -476,7 +476,7 @@ describe('parameters', () => {
476
476
  source: ab_new(param::number) is ab extend {
477
477
  except: param
478
478
  }
479
- `).translationToFailWith('Illegal `except:` of parameter');
479
+ `).toLog((0, test_translator_1.errorMessage)('Illegal `except:` of parameter'));
480
480
  });
481
481
  test('cannot accept parameter', () => {
482
482
  expect((0, test_translator_1.markSource) `
@@ -484,19 +484,19 @@ describe('parameters', () => {
484
484
  source: ab_new(param::number) is ab extend {
485
485
  accept: param
486
486
  }
487
- `).translationToFailWith('Illegal `accept:` of parameter');
487
+ `).toLog((0, test_translator_1.errorMessage)('Illegal `accept:` of parameter'));
488
488
  });
489
489
  test('error when using parameter without experiment enabled', () => {
490
490
  expect((0, test_translator_1.markSource) `
491
491
  run: ab_new${'(param is param)'} -> { select: * }
492
- `).translationToFailWith("Experimental flag 'parameters' required to enable this feature");
492
+ `).toLog((0, test_translator_1.error)('experiment-not-enabled', { experimentId: 'parameters' }));
493
493
  });
494
494
  test('parameters cannot reference themselves', () => {
495
495
  expect((0, test_translator_1.markSource) `
496
496
  ##! experimental.parameters
497
497
  source: ab_new(param::number) is ab
498
498
  run: ab_new(param is ${'param'}) -> { select: * }
499
- `).translationToFailWith('`param` is not defined');
499
+ `).toLog((0, test_translator_1.errorMessage)('`param` is not defined'));
500
500
  });
501
501
  // This just looks like circular referencing--in reality, you cannot reference other
502
502
  // parameters in parameter arguments, hence just "xxx is not defined"
@@ -505,42 +505,42 @@ describe('parameters', () => {
505
505
  ##! experimental.parameters
506
506
  source: ab_new(p_a::number, p_b::number) is ab
507
507
  run: ab_new(p_a is ${'p_b'}, p_b is ${'p_a'}) -> { select: * }
508
- `).translationToFailWith('`p_b` is not defined', '`p_a` is not defined');
508
+ `).toLog((0, test_translator_1.errorMessage)('`p_b` is not defined'), (0, test_translator_1.errorMessage)('`p_a` is not defined'));
509
509
  });
510
510
  test('error when passing param with no name', () => {
511
511
  expect((0, test_translator_1.markSource) `
512
512
  ##! experimental.parameters
513
513
  source: ab_new(param::number) is ab
514
514
  run: ab_new(${'1'}) -> { select: * }
515
- `).translationToFailWith('Parameterized source arguments must be named with `parameter_name is`', 'Argument not provided for required parameter `param`');
515
+ `).toLog((0, test_translator_1.errorMessage)('Parameterized source arguments must be named with `parameter_name is`'), (0, test_translator_1.errorMessage)('Argument not provided for required parameter `param`'));
516
516
  });
517
517
  test('error when passing param with incorrect name', () => {
518
518
  expect((0, test_translator_1.markSource) `
519
519
  ##! experimental.parameters
520
520
  source: ab_new(param::number) is ab
521
521
  run: ab_new(${'wrong_name'} is 1, param is 2) -> { select: * }
522
- `).translationToFailWith('`ab_new` has no declared parameter named `wrong_name`');
522
+ `).toLog((0, test_translator_1.errorMessage)('`ab_new` has no declared parameter named `wrong_name`'));
523
523
  });
524
524
  test('error when passing param multiple times', () => {
525
525
  expect((0, test_translator_1.markSource) `
526
526
  ##! experimental.parameters
527
527
  source: ab_new(param::number) is ab
528
528
  run: ab_new(param is 1, ${'param is 2'}) -> { select: * }
529
- `).translationToFailWith('Cannot pass argument for `param` more than once');
529
+ `).toLog((0, test_translator_1.errorMessage)('Cannot pass argument for `param` more than once'));
530
530
  });
531
531
  test('error when not specifying argument for param with parentheses', () => {
532
532
  expect((0, test_translator_1.markSource) `
533
533
  ##! experimental.parameters
534
534
  source: ab_new(param::number) is ab
535
535
  run: ${'ab_new'}() -> { select: * }
536
- `).translationToFailWith('Argument not provided for required parameter `param`');
536
+ `).toLog((0, test_translator_1.errorMessage)('Argument not provided for required parameter `param`'));
537
537
  });
538
538
  test('error when not specifying argument for param without parentheses', () => {
539
539
  expect((0, test_translator_1.markSource) `
540
540
  ##! experimental.parameters
541
541
  source: ab_new(param::number) is ab
542
542
  run: ${'ab_new'} -> { select: * }
543
- `).translationToFailWith('Argument not provided for required parameter `param`');
543
+ `).toLog((0, test_translator_1.errorMessage)('Argument not provided for required parameter `param`'));
544
544
  });
545
545
  test('error when not specifying argument for param second time', () => {
546
546
  expect((0, test_translator_1.markSource) `
@@ -548,7 +548,7 @@ describe('parameters', () => {
548
548
  source: ab_new(param::number) is ab
549
549
  run: ab_new(param is 1) -> { select: * }
550
550
  run: ${'ab_new'} -> { select: * }
551
- `).translationToFailWith('Argument not provided for required parameter `param`');
551
+ `).toLog((0, test_translator_1.errorMessage)('Argument not provided for required parameter `param`'));
552
552
  });
553
553
  test('error when referencing parameter that does not exist in join definition', () => {
554
554
  expect((0, test_translator_1.markSource) `
@@ -557,13 +557,13 @@ describe('parameters', () => {
557
557
  source: ab_new_2(param_2::number) is ab extend {
558
558
  join_one: ab_join is ab_new_1(param_1 is ${'param_3'})
559
559
  }
560
- `).translationToFailWith('`param_3` is not defined');
560
+ `).toLog((0, test_translator_1.errorMessage)('`param_3` is not defined'));
561
561
  });
562
562
  test('error when referencing identifier in default param value', () => {
563
563
  expect((0, test_translator_1.markSource) `
564
564
  ##! experimental.parameters
565
565
  source: ab_new_1(param_1 is ${'ident'}) is ab
566
- `).translationToFailWith('Only constants allowed in parameter default values');
566
+ `).toLog((0, test_translator_1.errorMessage)('Only constants allowed in parameter default values'));
567
567
  });
568
568
  test.skip('can use param in multi-stage query', () => {
569
569
  expect(`
@@ -582,7 +582,7 @@ describe('parameters', () => {
582
582
  ##! experimental.parameters
583
583
  source: ab_new(param::number) is ab
584
584
  source: ab_new_new(param::number) is ab_new(${'param'}) -> { select: * }
585
- `).translationToFailWith('`param` is not defined');
585
+ `).toLog((0, test_translator_1.errorMessage)('`param` is not defined'));
586
586
  });
587
587
  test.skip('can add an annotation to a param', () => {
588
588
  expect(`
@@ -1,11 +1,16 @@
1
1
  import { DocumentLocation } from '../../model';
2
2
  import { LogSeverity } from '../parse-log';
3
- type SimpleProblemSpec = string | RegExp;
4
- type ComplexProblemSpec = {
3
+ type MessageProblemSpec = {
5
4
  severity: LogSeverity;
6
- message: SimpleProblemSpec;
5
+ message: string | RegExp;
6
+ data?: any;
7
7
  };
8
- type ProblemSpec = SimpleProblemSpec | ComplexProblemSpec;
8
+ type CodeProblemSpec = {
9
+ severity: LogSeverity;
10
+ code: string;
11
+ data?: any;
12
+ };
13
+ type ProblemSpec = CodeProblemSpec | MessageProblemSpec;
9
14
  declare global {
10
15
  namespace jest {
11
16
  interface Matchers<R> {
@@ -26,31 +31,8 @@ declare global {
26
31
  * X can be a MarkedSource, a string, or a model.
27
32
  */
28
33
  toTranslate(): R;
29
- /**
30
- * expect(X).toTranslateWithWarnings(expectedWarnings)
31
- *
32
- * Passes if the source compiles to code which could be used to
33
- * generate SQL, and the specified warnings appear. If X is a marked
34
- * source, the warnings which are found must match the locations of
35
- * the markings.
36
- *
37
- * X can be a MarkedSource, a string, or a model. If it is a marked
38
- * source, the errors which are found must match the locations of
39
- * the markings.
40
- */
41
- toTranslateWithWarnings(...expectedWarnings: SimpleProblemSpec[]): R;
42
34
  toReturnType(tp: string): R;
43
- /**
44
- * expect(X).translateToFailWith(expectedErrors)
45
- *
46
- * X can be a MarkedSource, a string, or a model. If it is a marked
47
- * source, the errors which are found must match the locations of
48
- * the markings.
49
- *
50
- * @param expectedErrors varargs list of strings which must match
51
- * exactly, or regular expressions.
52
- */
53
- translationToFailWith(...expectedErrors: ProblemSpec[]): R;
35
+ toLog(...expectedErrors: ProblemSpec[]): R;
54
36
  isLocationIn(at: DocumentLocation, txt: string): R;
55
37
  /**
56
38
  * expect(X).compilesTo('expression-string')
@@ -102,19 +102,6 @@ function highlightError(dl, txt) {
102
102
  }
103
103
  return output.join('\n');
104
104
  }
105
- function normalizeProblemSpec(defaultSeverity) {
106
- return function (spec) {
107
- if (typeof spec === 'string') {
108
- return { severity: defaultSeverity, message: spec };
109
- }
110
- else if (spec instanceof RegExp) {
111
- return { severity: defaultSeverity, message: spec };
112
- }
113
- else {
114
- return spec;
115
- }
116
- };
117
- }
118
105
  function isMarkedSource(ts) {
119
106
  return typeof ts !== 'string' && !(ts instanceof test_translator_1.TestTranslator);
120
107
  }
@@ -207,11 +194,9 @@ expect.extend({
207
194
  const msg = `Expression type ${d.dataType} ${pass ? '=' : '!='} $[returnType`;
208
195
  return { pass, message: () => msg };
209
196
  },
210
- translationToFailWith: function (s, ...msgs) {
211
- return checkForProblems(this, false, s, 'error', ...msgs);
212
- },
213
- toTranslateWithWarnings: function (s, ...msgs) {
214
- return checkForProblems(this, true, s, 'warn', ...msgs);
197
+ toLog: function (s, ...msgs) {
198
+ const expectCompiles = !msgs.some(m => m.severity === 'error');
199
+ return checkForProblems(this, expectCompiles, s, ...msgs);
215
200
  },
216
201
  isLocationIn: function (checkAt, at, text) {
217
202
  if (this.equals(at, checkAt)) {
@@ -263,13 +248,14 @@ expect.extend({
263
248
  return { pass, message: () => `${msg}` };
264
249
  },
265
250
  });
266
- function checkForProblems(context, expectCompiles, s, defaultSeverity, ...msgs) {
251
+ function problemSpecSummary(s) {
252
+ return `${s.severity} '${'message' in s ? s.message : s.code}' ${s.data !== undefined ? (0, test_translator_1.pretty)(s.data) : ''}`;
253
+ }
254
+ function checkForProblems(context, expectCompiles, s, ...msgs) {
267
255
  var _a;
268
256
  let emsg = `Expected ${expectCompiles ? 'to' : 'to not'} compile with: `;
269
257
  const mSrc = isMarkedSource(s) ? s : undefined;
270
- const normalize = normalizeProblemSpec(defaultSeverity);
271
- const normMsgs = msgs.map(normalize);
272
- const qmsgs = normMsgs.map(s => `${s.severity} '${s.message}'`);
258
+ const qmsgs = msgs.map(problemSpecSummary);
273
259
  if (msgs.length === 1) {
274
260
  emsg += ` ${qmsgs[0]}`;
275
261
  }
@@ -297,25 +283,44 @@ function checkForProblems(context, expectCompiles, s, defaultSeverity, ...msgs)
297
283
  const explain = [];
298
284
  const errList = m.problemResponse().problems;
299
285
  let i;
300
- for (i = 0; i < normMsgs.length && errList[i]; i += 1) {
301
- const msg = normMsgs[i];
286
+ for (i = 0; i < msgs.length && errList[i]; i += 1) {
287
+ const msg = msgs[i];
302
288
  const err = errList[i];
303
- const matched = typeof msg.message === 'string'
304
- ? msg.message === err.message
305
- : err.message.match(msg.message);
289
+ let matched = true;
290
+ if ('message' in msg) {
291
+ if (typeof msg.message === 'string') {
292
+ if (msg.message !== err.message) {
293
+ explain.push(`Expected: ${msg.message}\nGot: ${err.message}`);
294
+ matched = false;
295
+ }
296
+ }
297
+ else {
298
+ if (!err.message.match(msg.message)) {
299
+ explain.push(`Expected: ${msg.message}\nGot: ${err.message}`);
300
+ matched = false;
301
+ }
302
+ }
303
+ }
304
+ else {
305
+ if (msg.code !== err.code) {
306
+ matched = false;
307
+ explain.push(`Expected: ${msg.code}\nGot: ${err.code}`);
308
+ }
309
+ }
306
310
  if (err.severity !== msg.severity) {
307
311
  explain.push(`Expected ${msg.severity}, got ${err.severity} ${err.message}`);
308
312
  }
309
- if (!matched) {
310
- explain.push(`Expected: ${msg.message}\nGot: ${err.message}`);
311
- }
312
- else {
313
+ if (matched) {
313
314
  if (mSrc === null || mSrc === void 0 ? void 0 : mSrc.locations[i]) {
314
315
  const have = (_a = err.at) === null || _a === void 0 ? void 0 : _a.range;
315
316
  const want = mSrc.locations[i].range;
316
317
  if (!context.equals(have, want)) {
317
- explain.push(`Expected '${msg.message}' at location: ${rangeToStr(want)}\n` +
318
- `Actual location: ${rangeToStr(have)}`);
318
+ explain.push(`Expected ${err.code} (${err.message}) at location: ${rangeToStr(want)}\n` + `Actual location: ${rangeToStr(have)}`);
319
+ }
320
+ }
321
+ if (msg.data !== undefined) {
322
+ if (JSON.stringify(msg.data) !== JSON.stringify(err.data)) {
323
+ explain.push(`Expected log data ${(0, test_translator_1.pretty)(msg.data)}\nGot ${(0, test_translator_1.pretty)(err.data)}`);
319
324
  }
320
325
  }
321
326
  }