Mirror: The small sibling of the graphql package, slimmed down for client-side libraries.
1// See: https://github.com/graphql/graphql-js/blob/976d64b/src/language/__tests__/printer-test.ts 2 3import { parse } from '../parser'; 4import { print } from '../printer'; 5 6describe('Printer: Query document', () => { 7 it('prints minimal ast', () => { 8 const ast = { 9 kind: 'Field', 10 name: { kind: 'Name', value: 'foo' }, 11 }; 12 expect(print(ast)).toBe('foo'); 13 }); 14 15 // NOTE: The shim won't throw for invalid AST nodes 16 it('returns empty strings for invalid AST', () => { 17 const badAST = { random: 'Data' }; 18 expect(print(badAST)).toBe(''); 19 }); 20 21 it('correctly prints non-query operations without name', () => { 22 const queryASTShorthanded = parse('query { id, name }'); 23 expect(print(queryASTShorthanded)).toBe(dedent` 24 { 25 id 26 name 27 } 28 `); 29 30 const mutationAST = parse('mutation { id, name }'); 31 expect(print(mutationAST)).toBe(dedent` 32 mutation { 33 id 34 name 35 } 36 `); 37 38 const queryASTWithArtifacts = parse( 39 'query ($foo: TestType) @testDirective { id, name }' 40 ); 41 expect(print(queryASTWithArtifacts)).toBe(dedent` 42 query ($foo: TestType) @testDirective { 43 id 44 name 45 } 46 `); 47 48 const mutationASTWithArtifacts = parse( 49 'mutation ($foo: TestType) @testDirective { id, name }' 50 ); 51 expect(print(mutationASTWithArtifacts)).toBe(dedent` 52 mutation ($foo: TestType) @testDirective { 53 id 54 name 55 } 56 `); 57 }); 58 59 it('prints query with variable directives', () => { 60 const queryASTWithVariableDirective = parse( 61 'query ($foo: TestType = {a: 123} @testDirective(if: true) @test) { id }' 62 ); 63 expect(print(queryASTWithVariableDirective)).toBe(dedent` 64 query ($foo: TestType = {a: 123} @testDirective(if: true) @test) { 65 id 66 } 67 `); 68 }); 69 70 it('keeps arguments on one line if line is short (<= 80 chars)', () => { 71 const printed = print( 72 parse('{trip(wheelchair:false arriveBy:false){dateTime}}') 73 ); 74 75 expect(printed).toBe( 76 dedent` 77 { 78 trip(wheelchair: false, arriveBy: false) { 79 dateTime 80 } 81 } 82 ` 83 ); 84 }); 85 86 it('prints kitchen sink without altering ast', () => { 87 const ast = parse(kitchenSinkQuery, { noLocation: true }); 88 89 const astBeforePrintCall = JSON.stringify(ast); 90 const printed = print(ast); 91 const printedAST = parse(printed, { noLocation: true }); 92 93 expect(printedAST).toEqual(ast); 94 expect(JSON.stringify(ast)).toBe(astBeforePrintCall); 95 96 expect(printed).toBe( 97 dedentString(String.raw` 98 query queryName($foo: ComplexType, $site: Site = MOBILE) @onQuery { 99 whoever123is: node(id: [123, 456]) { 100 id 101 ... on User @onInlineFragment { 102 field2 { 103 id 104 alias: field1(first: 10, after: $foo) @include(if: $foo) { 105 id 106 ...frag @onFragmentSpread 107 } 108 } 109 } 110 ... @skip(unless: $foo) { 111 id 112 } 113 ... { 114 id 115 } 116 } 117 } 118 119 mutation likeStory @onMutation { 120 like(story: 123) @onField { 121 story { 122 id @onField 123 } 124 } 125 } 126 127 subscription StoryLikeSubscription($input: StoryLikeSubscribeInput @onVariableDefinition) @onSubscription { 128 storyLikeSubscribe(input: $input) { 129 story { 130 likers { 131 count 132 } 133 likeSentence { 134 text 135 } 136 } 137 } 138 } 139 140 fragment frag on Friend @onFragmentDefinition { 141 foo(size: $size, bar: $b, obj: {key: "value"}) 142 } 143 144 { 145 unnamed(truthy: true, falsy: false, nullish: null) 146 query 147 } 148 149 { 150 __typename 151 } 152 `) + '\n' 153 ); 154 }); 155}); 156 157const kitchenSinkQuery = String.raw` 158query queryName($foo: ComplexType, $site: Site = MOBILE) @onQuery { 159 whoever123is: node(id: [123, 456]) { 160 id 161 ... on User @onInlineFragment { 162 field2 { 163 id 164 alias: field1(first: 10, after: $foo) @include(if: $foo) { 165 id 166 ...frag @onFragmentSpread 167 } 168 } 169 } 170 ... @skip(unless: $foo) { 171 id 172 } 173 ... { 174 id 175 } 176 } 177} 178mutation likeStory @onMutation { 179 like(story: 123) @onField { 180 story { 181 id @onField 182 } 183 } 184} 185subscription StoryLikeSubscription( 186 $input: StoryLikeSubscribeInput @onVariableDefinition 187) 188 @onSubscription { 189 storyLikeSubscribe(input: $input) { 190 story { 191 likers { 192 count 193 } 194 likeSentence { 195 text 196 } 197 } 198 } 199} 200fragment frag on Friend @onFragmentDefinition { 201 foo( 202 size: $size 203 bar: $b 204 obj: { key: "value" } 205 ) 206} 207{ 208 unnamed(truthy: true, falsy: false, nullish: null) 209 query 210} 211query { 212 __typename 213} 214`; 215 216function dedentString(string) { 217 const trimmedStr = string 218 .replace(/^\n*/m, '') // remove leading newline 219 .replace(/[ \t\n]*$/, ''); // remove trailing spaces and tabs 220 // fixes indentation by removing leading spaces and tabs from each line 221 let indent = ''; 222 for (const char of trimmedStr) { 223 if (char !== ' ' && char !== '\t') { 224 break; 225 } 226 indent += char; 227 } 228 229 return trimmedStr.replace(RegExp('^' + indent, 'mg'), ''); // remove indent 230} 231 232function dedent(strings, ...values) { 233 let str = strings[0]; 234 for (let i = 1; i < strings.length; ++i) str += values[i - 1] + strings[i]; // interpolation 235 return dedentString(str) + '\n'; 236}