Mirror: The spec-compliant minimum of client-side GraphQL.
1import { describe, it, expect } from 'vitest'; 2import * as graphql16 from 'graphql16'; 3 4import { parse } from '../parser'; 5import { print, printString, printBlockString } from '../printer'; 6import kitchenSinkAST from './fixtures/kitchen_sink.json'; 7 8function dedentString(string: string) { 9 const trimmedStr = string 10 .replace(/^\n*/m, '') // remove leading newline 11 .replace(/[ \t\n]*$/, ''); // remove trailing spaces and tabs 12 // fixes indentation by removing leading spaces and tabs from each line 13 let indent = ''; 14 for (const char of trimmedStr) { 15 if (char !== ' ' && char !== '\t') { 16 break; 17 } 18 indent += char; 19 } 20 21 return trimmedStr.replace(RegExp('^' + indent, 'mg'), ''); // remove indent 22} 23 24function dedent(strings: readonly string[], ...values: unknown[]) { 25 let str = strings[0]; 26 for (let i = 1; i < strings.length; ++i) str += values[i - 1] + strings[i]; // interpolation 27 return dedentString(str); 28} 29 30describe('printString', () => { 31 it('prints strings as expected', () => { 32 expect(printString('test')).toEqual('"test"'); 33 expect(printString('\n')).toEqual('"\\n"'); 34 }); 35}); 36 37describe('printBlockString', () => { 38 it('prints block strings as expected', () => { 39 expect(printBlockString('test')).toEqual('"""\ntest\n"""'); 40 expect(printBlockString('\n')).toEqual('"""\n\n\n"""'); 41 expect(printBlockString('"""')).toEqual('"""\n\\"""\n"""'); 42 }); 43}); 44 45describe('print', () => { 46 it('prints the kitchen sink document like graphql.js does', () => { 47 const doc = print(kitchenSinkAST); 48 expect(doc).toMatchSnapshot(); 49 expect(doc).toEqual(graphql16.print(kitchenSinkAST)); 50 }); 51 52 it('prints minimal ast', () => { 53 expect( 54 print({ 55 kind: 'Field', 56 name: { kind: 'Name', value: 'foo' }, 57 } as any) 58 ).toBe('foo'); 59 60 expect( 61 print({ 62 kind: 'Name', 63 value: 'foo', 64 } as any) 65 ).toBe('foo'); 66 67 expect( 68 print({ 69 kind: 'Document', 70 definitions: [], 71 } as any) 72 ).toBe(''); 73 }); 74 75 it('prints integers and floats', () => { 76 expect( 77 print({ 78 kind: 'IntValue', 79 value: '12', 80 } as any) 81 ).toBe('12'); 82 83 expect( 84 print({ 85 kind: 'FloatValue', 86 value: '12e2', 87 } as any) 88 ).toBe('12e2'); 89 }); 90 91 it('prints lists of values', () => { 92 expect( 93 print({ 94 kind: 'ListValue', 95 values: [{ kind: 'NullValue' }], 96 } as any) 97 ).toBe('[null]'); 98 }); 99 100 it('prints types', () => { 101 expect( 102 print({ 103 kind: 'ListType', 104 type: { 105 kind: 'NonNullType', 106 type: { 107 kind: 'NamedType', 108 name: { 109 kind: 'Name', 110 value: 'Type', 111 }, 112 }, 113 }, 114 } as any) 115 ).toBe('[Type!]'); 116 }); 117 118 // NOTE: The shim won't throw for invalid AST nodes 119 it('returns empty strings for invalid AST', () => { 120 const badAST = { random: 'Data' }; 121 expect(print(badAST as any)).toBe(''); 122 }); 123 124 it('correctly prints non-query operations without name', () => { 125 const queryASTShorthanded = parse('query { id, name }'); 126 expect(print(queryASTShorthanded)).toBe(dedent` 127 { 128 id 129 name 130 } 131 `); 132 133 const mutationAST = parse('mutation { id, name }'); 134 expect(print(mutationAST)).toBe(dedent` 135 mutation { 136 id 137 name 138 } 139 `); 140 141 const queryASTWithArtifacts = parse('query ($foo: TestType) @testDirective { id, name }'); 142 expect(print(queryASTWithArtifacts)).toBe(dedent` 143 query ($foo: TestType) @testDirective { 144 id 145 name 146 } 147 `); 148 149 const mutationASTWithArtifacts = parse('mutation ($foo: TestType) @testDirective { id, name }'); 150 expect(print(mutationASTWithArtifacts)).toBe(dedent` 151 mutation ($foo: TestType) @testDirective { 152 id 153 name 154 } 155 `); 156 }); 157 158 it('prints query with variable directives', () => { 159 const queryASTWithVariableDirective = parse( 160 'query ($foo: TestType = {a: 123} @testDirective(if: true) @test) { id }' 161 ); 162 expect(print(queryASTWithVariableDirective)).toBe(dedent` 163 query ($foo: TestType = {a: 123} @testDirective(if: true) @test) { 164 id 165 } 166 `); 167 }); 168 169 it('keeps arguments on one line if line is short (<= 80 chars)', () => { 170 const printed = print(parse('{trip(wheelchair:false arriveBy:false){dateTime}}')); 171 172 expect(printed).toBe( 173 dedent` 174 { 175 trip(wheelchair: false, arriveBy: false) { 176 dateTime 177 } 178 } 179 ` 180 ); 181 }); 182});