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