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 const queryWithNullabilityFields = parse('query { id?, name! }'); 59 expect(print(queryWithNullabilityFields)).toBe(dedent` 60 { 61 id? 62 name! 63 } 64 `); 65 }); 66 67 it('prints query with variable directives', () => { 68 const queryASTWithVariableDirective = parse( 69 'query ($foo: TestType = {a: 123} @testDirective(if: true) @test) { id }' 70 ); 71 expect(print(queryASTWithVariableDirective)).toBe(dedent` 72 query ($foo: TestType = {a: 123} @testDirective(if: true) @test) { 73 id 74 } 75 `); 76 }); 77 78 it('keeps arguments on one line if line is short (<= 80 chars)', () => { 79 const printed = print( 80 parse('{trip(wheelchair:false arriveBy:false){dateTime}}') 81 ); 82 83 expect(printed).toBe( 84 dedent` 85 { 86 trip(wheelchair: false, arriveBy: false) { 87 dateTime 88 } 89 } 90 ` 91 ); 92 }); 93 94 it('prints kitchen sink without altering ast', () => { 95 const ast = parse(kitchenSinkQuery, { noLocation: true }); 96 97 const astBeforePrintCall = JSON.stringify(ast); 98 const printed = print(ast); 99 const printedAST = parse(printed, { noLocation: true }); 100 101 expect(printedAST).toEqual(ast); 102 expect(JSON.stringify(ast)).toBe(astBeforePrintCall); 103 104 expect(printed).toBe( 105 dedentString(String.raw` 106 query queryName($foo: ComplexType, $site: Site = MOBILE) @onQuery { 107 whoever123is: node(id: [123, 456]) { 108 id 109 ... on User @onInlineFragment { 110 field2 { 111 id 112 alias: field1(first: 10, after: $foo) @include(if: $foo) { 113 id 114 ...frag @onFragmentSpread 115 } 116 } 117 } 118 ... @skip(unless: $foo) { 119 id 120 } 121 ... { 122 id 123 } 124 } 125 } 126 127 mutation likeStory @onMutation { 128 like(story: 123) @onField { 129 story { 130 id @onField 131 } 132 } 133 } 134 135 subscription StoryLikeSubscription($input: StoryLikeSubscribeInput @onVariableDefinition) @onSubscription { 136 storyLikeSubscribe(input: $input) { 137 story { 138 likers { 139 count 140 } 141 likeSentence { 142 text 143 } 144 } 145 } 146 } 147 148 fragment frag on Friend @onFragmentDefinition { 149 foo(size: $size, bar: $b, obj: {key: "value"}) 150 } 151 152 { 153 unnamed(truthy: true, falsy: false, nullish: null) 154 query 155 } 156 157 { 158 __typename 159 } 160 `) + '\n' 161 ); 162 }); 163}); 164 165const kitchenSinkQuery = String.raw` 166query queryName($foo: ComplexType, $site: Site = MOBILE) @onQuery { 167 whoever123is: node(id: [123, 456]) { 168 id 169 ... on User @onInlineFragment { 170 field2 { 171 id 172 alias: field1(first: 10, after: $foo) @include(if: $foo) { 173 id 174 ...frag @onFragmentSpread 175 } 176 } 177 } 178 ... @skip(unless: $foo) { 179 id 180 } 181 ... { 182 id 183 } 184 } 185} 186mutation likeStory @onMutation { 187 like(story: 123) @onField { 188 story { 189 id @onField 190 } 191 } 192} 193subscription StoryLikeSubscription( 194 $input: StoryLikeSubscribeInput @onVariableDefinition 195) 196 @onSubscription { 197 storyLikeSubscribe(input: $input) { 198 story { 199 likers { 200 count 201 } 202 likeSentence { 203 text 204 } 205 } 206 } 207} 208fragment frag on Friend @onFragmentDefinition { 209 foo( 210 size: $size 211 bar: $b 212 obj: { key: "value" } 213 ) 214} 215{ 216 unnamed(truthy: true, falsy: false, nullish: null) 217 query 218} 219query { 220 __typename 221} 222`; 223 224function dedentString(string) { 225 const trimmedStr = string 226 .replace(/^\n*/m, '') // remove leading newline 227 .replace(/[ \t\n]*$/, ''); // remove trailing spaces and tabs 228 // fixes indentation by removing leading spaces and tabs from each line 229 let indent = ''; 230 for (const char of trimmedStr) { 231 if (char !== ' ' && char !== '\t') { 232 break; 233 } 234 indent += char; 235 } 236 237 return trimmedStr.replace(RegExp('^' + indent, 'mg'), ''); // remove indent 238} 239 240function dedent(strings, ...values) { 241 let str = strings[0]; 242 for (let i = 1; i < strings.length; ++i) str += values[i - 1] + strings[i]; // interpolation 243 return dedentString(str) + '\n'; 244}