import { GqlEnum, gql, GqlVariable, GqlFragment, type GqlTemplateValue } from '../../dist/gql/template/index.js'; import { template } from '../../dist/gql/template/build.js'; import { GQL_TEMPLATE } from '../../dist/gql/template/symbols.js'; const query = (t: TemplateStringsArray, ...args: GqlTemplateValue[]) => { const ret = template('query', t, args); if (ret.variables == null) delete ret.variables; return ret; }; test('create enum', () => { // @ts-expect-error test invalid type expect(() => GqlEnum()).toThrow(); expect(() => GqlEnum('')).toThrow(); // @ts-expect-error test invalid type expect(() => GqlEnum(null)).toThrow(); expect(() => GqlEnum(' xx ')).toThrow(); expect(() => GqlEnum(' ')).toThrow(); }); test('create literal', () => { expect(gql('xx').parts).toEqual(['xx']); expect(gql`xx`.parts).toEqual(['xx']); expect(gql('').parts).toEqual(['']); expect(gql`xx`.parts).toEqual(['xx']); expect(gql``.parts).toEqual(['']); // @ts-expect-error test invalid type expect(gql(null).parts).toEqual(['']); expect(gql(undefined).parts).toEqual(['']); expect(gql(1).parts).toEqual(['1']); expect(gql(Number.NaN).parts).toEqual(['NaN']); expect(gql(true).parts).toEqual(['true']); expect(gql(false).parts).toEqual(['false']); expect(gql(1n).parts).toEqual(['1']); // @ts-expect-error test invalid type expect(() => gql([])).toThrow(/Invalid type of value/); // @ts-expect-error test invalid type expect(() => gql(1, 1)).toThrow(/Invalid additional arguments/); expect(() => // @ts-expect-error test invalid type gql(() => { // noop }), ).toThrow(/Invalid type of value/); const l = [undefined]; // @ts-expect-error test invalid type l.raw = ['xx']; // @ts-expect-error test invalid type expect(() => gql(l, 1)).toThrow(/Query string length does not match args./); const l2 = [undefined]; // @ts-expect-error test invalid type l.raw = 1; // @ts-expect-error test invalid type expect(() => gql(l2)).toThrow(/Invalid type of value/); }); test('template empty', () => { expect(query``).toEqual({ query: 'query{}' }); expect(query` `).toEqual({ query: 'query{}' }); }); test('template simple', () => { expect(query`{ a : 1 }`).toEqual({ query: 'query{{a:1}}' }); expect(query` { a : 1 }`).toEqual({ query: 'query{{a:1}}' }); expect(query`{ a : 1 } `).toEqual({ query: 'query{{a:1}}' }); }); test('template string', () => { expect(query`data(input: ${' a string \n '}) { field }`).toEqual({ query: String.raw`query{data(input: " a string \n "){field}}`, }); }); test('template in string', () => { expect(() => query`data(input: "template ${'in string'}") { field }`).toThrow(/Invalid GQL template/); }); test('literal string', () => { expect(query`data(input: "literal string") { field }`).toEqual({ query: String.raw`query{data(input: "literal string") { field }}`, }); }); test('template number', () => { expect(query`data(input: ${123}) { field }`).toEqual({ query: String.raw`query{data(input: 123){field}}`, }); expect(query`data(input: ${123.5}) { field }`).toEqual({ query: String.raw`query{data(input: 123.5){field}}`, }); }); test('template boolean', () => { expect(query`data(input: ${true}) { field }`).toEqual({ query: String.raw`query{data(input: true){field}}`, }); expect(query`data(input: ${false}) { field }`).toEqual({ query: String.raw`query{data(input: false){field}}`, }); }); test('template null', () => { expect(query`data(input: ${null}) { field }`).toEqual({ query: String.raw`query{data(input: null){field}}`, }); // @ts-expect-error test invalid type expect(query`data(input: ${undefined}) { field }`).toEqual({ query: String.raw`query{data(input: null){field}}`, }); }); test('template bigint', () => { expect(query`data(input: ${123n}) { field }`).toEqual({ query: String.raw`query{data(input: 123){field}}`, }); expect(query`data(input: ${111_111_111_111_111_111_111_111_111_111_111_111n}) { field }`).toEqual({ query: String.raw`query{data(input: 111111111111111111111111111111111111){field}}`, }); }); test('template enum', () => { expect(query`data(input: ${GqlEnum('ENUM')}) { field }`).toEqual({ query: String.raw`query{data(input: ENUM){field}}`, }); // @ts-expect-error test invalid type expect(() => query`data(input: ${Symbol()})`).toThrow(); }); test('template literal', () => { expect(query`${gql`data`}(input: ${'an input'}) { field }`).toEqual({ query: String.raw`query{data(input: "an input"){field}}`, }); expect(query`${gql(`data`)}(input: ${'an input'}) { field }`).toEqual({ query: String.raw`query{data(input: "an input"){field}}`, }); expect(query`${gql`da`}ta(input: ${'an input'}) { field }`).toEqual({ query: String.raw`query{data(input: "an input"){field}}`, }); expect(query`${gql(`da`)}ta(input: ${'an input'}) { field }`).toEqual({ query: String.raw`query{data(input: "an input"){field}}`, }); expect(query`${[]}`).toEqual({ query: String.raw`query{}`, }); expect(query`${[1, 2, 3].map((n) => gql`result${gql(n)}: data(input:${n.toString()}){field}`)}`).toEqual({ query: String.raw`query{result1:data(input:"1"){field}result2:data(input:"2"){field}result3:data(input:"3"){field}}`, }); expect( query`${gql`da`}ta(input: ${'an input'}) { field ${gql`field2`} } `, ).toEqual({ query: String.raw`query{data(input: "an input"){field field2 }}`, }); }); test('template huge', () => { const q = query` repository(owner: ${'octocat'}, name:${'Hello World'}) { issues(last: ${20}, states: ${GqlEnum('CLOSED')}) { edges { node { title url labels(first:5) { edges { node { name } } } } } } } `; const expected = 'query{repository(owner: "octocat",name:"Hello World"){issues(last: 20,states: CLOSED){edges{node{title url labels(first:5){edges{node{name}}}}}}}}'; expect(q).toEqual({ query: expected }); }); test('template variable', () => { expect(query`data(input: ${GqlVariable({ x: 1 }, 'Input!')}) { field }`).toEqual({ query: String.raw`query($_a:Input!){data(input: $_a){field}}`, variables: { _a: { x: 1 } }, }); expect( query`data(input: ${GqlVariable({ x: 1 }, 'Input!')}, input2: ${GqlVariable({ y: 1 }, 'Input2')}) { field }`, ).toEqual({ query: String.raw`query($_a:Input!,$_b:Input2){data(input: $_a,input2: $_b){field}}`, variables: { _a: { x: 1 }, _b: { y: 1 } }, }); }); test('template invalid [error]', () => { expect( () => // @ts-expect-error test invalid type query`${() => { // noop }}`, ).toThrow(); // @ts-expect-error test invalid type expect(() => query`${new Date()}`).toThrow(); }); test('template reuse variable', () => { const v = GqlVariable({ x: 1 }, 'Input!'); expect( query`data(input: ${v}, input2: ${v}) { field }`, ).toEqual({ query: String.raw`query($_a:Input!){data(input: $_a,input2: $_a){field}}`, variables: { _a: { x: 1 } }, }); }); test('template symbol variable', () => { const v = GqlVariable(GqlEnum('ENUM'), 'Input!'); expect(query`data(input: ${v}) { field }`).toEqual({ query: String.raw`query($_a:Input!){data(input: $_a){field}}`, variables: { _a: 'ENUM' }, }); }); test('template undefined variable', () => { expect(query`data(input: ${GqlVariable(null, 'Input!')}) { field }`).toEqual({ query: String.raw`query($_a:Input!){data(input: $_a){field}}`, variables: { _a: null }, }); expect(query`data(input: ${GqlVariable(undefined, 'Input!')}) { field }`).toEqual({ query: String.raw`query($_a:Input!){data(input: $_a){field}}`, variables: { _a: null }, }); }); test('template a lot variable', () => { const variables = Array.from({ length: 100 }) .fill(0) .map((_, i) => GqlVariable(i, `Var${i}`)); const temp = Array.from({ length: 101 }).fill(' '); // @ts-expect-error test invalid type temp.raw = temp; // @ts-expect-error test invalid type const q = query(temp, ...variables); const e = { query: `query($_a:Var0,$_b:Var1,$_c:Var2,$_d:Var3,$_e:Var4,$_f:Var5,$_g:Var6,$_h:Var7,$_i:Var8,$_j:Var9,$_k:Var10,$_l:Var11,$_m:Var12,$_n:Var13,$_o:Var14,$_p:Var15,$_q:Var16,$_r:Var17,$_s:Var18,$_t:Var19,$_u:Var20,$_v:Var21,$_w:Var22,$_x:Var23,$_y:Var24,$_z:Var25,$_aa:Var26,$_ab:Var27,$_ac:Var28,$_ad:Var29,$_ae:Var30,$_af:Var31,$_ag:Var32,$_ah:Var33,$_ai:Var34,$_aj:Var35,$_ak:Var36,$_al:Var37,$_am:Var38,$_an:Var39,$_ao:Var40,$_ap:Var41,$_aq:Var42,$_ar:Var43,$_as:Var44,$_at:Var45,$_au:Var46,$_av:Var47,$_aw:Var48,$_ax:Var49,$_ay:Var50,$_az:Var51,$_ba:Var52,$_bb:Var53,$_bc:Var54,$_bd:Var55,$_be:Var56,$_bf:Var57,$_bg:Var58,$_bh:Var59,$_bi:Var60,$_bj:Var61,$_bk:Var62,$_bl:Var63,$_bm:Var64,$_bn:Var65,$_bo:Var66,$_bp:Var67,$_bq:Var68,$_br:Var69,$_bs:Var70,$_bt:Var71,$_bu:Var72,$_bv:Var73,$_bw:Var74,$_bx:Var75,$_by:Var76,$_bz:Var77,$_ca:Var78,$_cb:Var79,$_cc:Var80,$_cd:Var81,$_ce:Var82,$_cf:Var83,$_cg:Var84,$_ch:Var85,$_ci:Var86,$_cj:Var87,$_ck:Var88,$_cl:Var89,$_cm:Var90,$_cn:Var91,$_co:Var92,$_cp:Var93,$_cq:Var94,$_cr:Var95,$_cs:Var96,$_ct:Var97,$_cu:Var98,$_cv:Var99){$_a $_b $_c $_d $_e $_f $_g $_h $_i $_j $_k $_l $_m $_n $_o $_p $_q $_r $_s $_t $_u $_v $_w $_x $_y $_z $_aa $_ab $_ac $_ad $_ae $_af $_ag $_ah $_ai $_aj $_ak $_al $_am $_an $_ao $_ap $_aq $_ar $_as $_at $_au $_av $_aw $_ax $_ay $_az $_ba $_bb $_bc $_bd $_be $_bf $_bg $_bh $_bi $_bj $_bk $_bl $_bm $_bn $_bo $_bp $_bq $_br $_bs $_bt $_bu $_bv $_bw $_bx $_by $_bz $_ca $_cb $_cc $_cd $_ce $_cf $_cg $_ch $_ci $_cj $_ck $_cl $_cm $_cn $_co $_cp $_cq $_cr $_cs $_ct $_cu $_cv}`, variables: { _a: 0, _b: 1, _c: 2, _d: 3, _e: 4, _f: 5, _g: 6, _h: 7, _i: 8, _j: 9, _k: 10, _l: 11, _m: 12, _n: 13, _o: 14, _p: 15, _q: 16, _r: 17, _s: 18, _t: 19, _u: 20, _v: 21, _w: 22, _x: 23, _y: 24, _z: 25, _aa: 26, _ab: 27, _ac: 28, _ad: 29, _ae: 30, _af: 31, _ag: 32, _ah: 33, _ai: 34, _aj: 35, _ak: 36, _al: 37, _am: 38, _an: 39, _ao: 40, _ap: 41, _aq: 42, _ar: 43, _as: 44, _at: 45, _au: 46, _av: 47, _aw: 48, _ax: 49, _ay: 50, _az: 51, _ba: 52, _bb: 53, _bc: 54, _bd: 55, _be: 56, _bf: 57, _bg: 58, _bh: 59, _bi: 60, _bj: 61, _bk: 62, _bl: 63, _bm: 64, _bn: 65, _bo: 66, _bp: 67, _bq: 68, _br: 69, _bs: 70, _bt: 71, _bu: 72, _bv: 73, _bw: 74, _bx: 75, _by: 76, _bz: 77, _ca: 78, _cb: 79, _cc: 80, _cd: 81, _ce: 82, _cf: 83, _cg: 84, _ch: 85, _ci: 86, _cj: 87, _ck: 88, _cl: 89, _cm: 90, _cn: 91, _co: 92, _cp: 93, _cq: 94, _cr: 95, _cs: 96, _ct: 97, _cu: 98, _cv: 99, }, }; expect(q).toEqual(e); }); test('create simple fragment', () => { // @ts-expect-error test invalid type expect(() => GqlFragment()).toThrow(/type must be a string/); // @ts-expect-error test invalid type expect(() => GqlFragment(1, 2)).toThrow(/type must be a string/); expect(() => GqlFragment('x', '')).toThrow(/fragment must not be empty/); expect(() => GqlFragment('x', ' ')).toThrow(/fragment must not be empty/); expect(() => GqlFragment('x', [])).toThrow(/fields must not be empty/); expect(() => GqlFragment('x', [' '])).toThrow(/fields must not be empty/); // @ts-expect-error test invalid type expect(() => GqlFragment('x', {})).toThrow(/fragment must be a string or iterable/); expect(() => GqlFragment('[A]', 'xx')).toThrow(/type must not contain array/); // @ts-expect-error test invalid type expect(() => GqlFragment(1, 'xx')).toThrow(/type must be a string/); expect(() => GqlFragment(' ', 'xx')).toThrow(/type must not be empty/); expect(() => GqlFragment('A!', 'xx')).toThrow(/type must not contain non-null assertion/); expect(GqlFragment('A', 'xx').fragment).toEqual('xx'); expect(GqlFragment('A', ' xx ').fragment).toEqual('xx'); expect(GqlFragment('A', ' xx yy ').fragment).toEqual('xx yy'); expect(GqlFragment('A', ['a', 'b', 'c', 'a']).fragment).toEqual('a b c'); expect(GqlFragment('A', ['a', 'b', 'c', ' ']).fragment).toEqual('a b c'); expect(GqlFragment('A', ['a', 'b', 'c', ' a ']).fragment).toEqual('a b c'); expect(GqlFragment('A', ['a', 'b', 'c', ' d e ']).fragment).toEqual('a b c d e'); }); test('create complex fragment', () => { // @ts-expect-error test invalid type expect(() => GqlFragment(1)`xx`).toThrow(/type must be a string/); expect(() => GqlFragment(' ')`xx`).toThrow(/type must not be empty/); expect(() => GqlFragment('A!')`xx`).toThrow(/type must not contain non-null assertion/); expect(() => GqlFragment('A')` `).toThrow(/fragment must not be empty/); // @ts-expect-error test invalid type expect(() => GqlFragment('A')` ${GqlVariable(1, 'xx')} `).toThrow(/fragment cannot contain variables/); expect( () => GqlFragment('A')` ${gql` ${GqlVariable(1, 'xx')} `} `, ).toThrow(/fragment cannot contain variables/); expect(() => { // @ts-expect-error test invalid type GqlFragment('A')({ raw: ['1', '2'] }, { [GQL_TEMPLATE]: [{}], parts: [1, 2] }); }).toThrow(/Expected a GqlTemplate/); expect(GqlFragment('A')`xx`.fragment).toEqual('xx'); expect(GqlFragment('A')` xx `.fragment).toEqual('xx'); expect(GqlFragment('A')` xx yy `.fragment).toEqual('xx yy'); expect(GqlFragment('A')` xx ${gql(123)} `.fragment).toEqual('xx 123'); expect(GqlFragment('A')` xx ...${GqlFragment('Y', 'yy')} `.fragment).toEqual('xx...on Y{yy}'); expect(GqlFragment('A')` xx v(t:${'1 2 3'})`.fragment).toEqual('xx v(t:"1 2 3")'); expect(GqlFragment('A')` xx ${[]}`.fragment).toEqual('xx'); expect(GqlFragment('A')` xx ${[gql`yy`]}`.fragment).toEqual('xx yy'); expect(GqlFragment('A')` xx access(role: ${GqlEnum('OWNER')})`.fragment).toEqual('xx access(role: OWNER)'); }); test('template with fragment', () => { const f = GqlFragment( 'Post', ` id title content author { id name } `, ); expect( query` data(input: ${GqlVariable(undefined, 'Input!')}) { field ...${f} } data2: data(input: ${GqlVariable(undefined, 'Input!')}) { field ...${f} }`, ).toEqual({ query: String.raw`fragment _a on Post{id title content author{id name}}query($_a:Input!,$_b:Input!){data(input: $_a){field..._a }data2:data(input: $_b){field..._a }}`, variables: { _a: null, _b: null }, }); });