File size: 1,834 Bytes
aa89cb5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import {QueryParser} from '../src/parser/dsl';

function queryApi() {
  const calls: Array<[string, ...unknown[]]> = [];
  const request = {
    selectName() { calls.push(['selectName']); return this; },
    withNameIn(...values: string[]) { calls.push(['withNameIn', ...values]); return this; },
    selectCatalogWith(value: unknown) { calls.push(['selectCatalogWith', value]); return this; },
    limit(value: number) { calls.push(['limit', value]); return this; },
  };
  const nested = {
    selectName() { calls.push(['nested.selectName']); return this; },
  };
  return {
    calls,
    entryPoint: {
      releasesWithMinimalFields: () => request,
      catalogsWithMinimalFields: () => nested,
    },
    request,
    nested,
  };
}

describe('QueryParser controlled grammar', () => {
  test('parses generated query chains, scalar arguments, and nested requests', () => {
    const api = queryApi();
    const result = QueryParser.parse(`
      Q.releasesWithMinimalFields()
        .selectName()
        .withNameIn("Blue", 'Green')
        .selectCatalogWith(Q.catalogsWithMinimalFields().selectName())
        .limit(10)
    `, api.entryPoint);

    expect(result).toBe(api.request);
    expect(api.calls).toEqual([
      ['selectName'],
      ['withNameIn', 'Blue', 'Green'],
      ['nested.selectName'],
      ['selectCatalogWith', api.nested],
      ['limit', 10],
    ]);
  });

  test.each([
    'Q.releasesWithMinimalFields(); globalThis.alert(1)',
    'Q["releasesWithMinimalFields"]()',
    'Q.releasesWithMinimalFields().constructor("return globalThis")',
    'Q.releasesWithMinimalFields().unknownMethod()',
    'Other.releasesWithMinimalFields()',
  ])('rejects source outside the controlled query grammar: %s', source => {
    expect(() => QueryParser.parse(source, queryApi().entryPoint)).toThrow();
  });
});