Mirror: The spec-compliant minimum of client-side GraphQL.
at v1.2.0 8.2 kB view raw
1import { describe, it, expect } from 'vitest'; 2import { parse } from '../parser'; 3import { print } from '../printer'; 4import type { 5 OperationDefinitionNode, 6 VariableDefinitionNode, 7 FragmentDefinitionNode, 8} from '../ast'; 9 10describe('GraphQL descriptions', () => { 11 describe('OperationDefinition descriptions', () => { 12 it('parses operation with description', () => { 13 const source = ` 14 """ 15 Request the current status of a time machine and its operator. 16 """ 17 query GetTimeMachineStatus { 18 timeMachine { 19 id 20 status 21 } 22 } 23 `; 24 25 const doc = parse(source, { noLocation: true }); 26 const operation = doc.definitions[0] as OperationDefinitionNode; 27 28 expect(operation.description).toBeDefined(); 29 expect(operation.description?.value).toBe( 30 'Request the current status of a time machine and its operator.' 31 ); 32 expect(operation.description?.block).toBe(true); 33 }); 34 35 it('parses operation with single-line description', () => { 36 const source = ` 37 "Simple query description" 38 query SimpleQuery { 39 field 40 } 41 `; 42 43 const doc = parse(source, { noLocation: true }); 44 const operation = doc.definitions[0] as OperationDefinitionNode; 45 46 expect(operation.description).toBeDefined(); 47 expect(operation.description?.value).toBe('Simple query description'); 48 expect(operation.description?.block).toBe(false); 49 }); 50 51 it('does not allow description on anonymous operations', () => { 52 const source = ` 53 "This should fail" 54 { 55 field 56 } 57 `; 58 59 expect(() => parse(source)).toThrow(); 60 }); 61 62 it('parses mutation with description', () => { 63 const source = ` 64 """ 65 Create a new time machine entry. 66 """ 67 mutation CreateTimeMachine($input: TimeMachineInput!) { 68 createTimeMachine(input: $input) { 69 id 70 } 71 } 72 `; 73 74 const doc = parse(source, { noLocation: true }); 75 const operation = doc.definitions[0] as OperationDefinitionNode; 76 77 expect(operation.description).toBeDefined(); 78 expect(operation.description?.value).toBe('Create a new time machine entry.'); 79 }); 80 }); 81 82 describe('VariableDefinition descriptions', () => { 83 it('parses variable with description', () => { 84 const source = ` 85 query GetTimeMachineStatus( 86 "The unique serial number of the time machine to inspect." 87 $machineId: ID! 88 89 """ 90 The year to check the status for. 91 **Warning:** certain years may trigger an anomaly in the space-time continuum. 92 """ 93 $year: Int 94 ) { 95 timeMachine(id: $machineId) { 96 status(year: $year) 97 } 98 } 99 `; 100 101 const doc = parse(source, { noLocation: true }); 102 const operation = doc.definitions[0] as OperationDefinitionNode; 103 const variables = operation.variableDefinitions as VariableDefinitionNode[]; 104 105 expect(variables[0].description).toBeDefined(); 106 expect(variables[0].description?.value).toBe( 107 'The unique serial number of the time machine to inspect.' 108 ); 109 expect(variables[0].description?.block).toBe(false); 110 111 expect(variables[1].description).toBeDefined(); 112 expect(variables[1].description?.value).toBe( 113 'The year to check the status for.\n**Warning:** certain years may trigger an anomaly in the space-time continuum.' 114 ); 115 expect(variables[1].description?.block).toBe(true); 116 }); 117 118 it('parses mixed variables with and without descriptions', () => { 119 const source = ` 120 query Mixed( 121 "Described variable" 122 $described: String 123 $undescribed: Int 124 ) { 125 field 126 } 127 `; 128 129 const doc = parse(source, { noLocation: true }); 130 const operation = doc.definitions[0] as OperationDefinitionNode; 131 const variables = operation.variableDefinitions as VariableDefinitionNode[]; 132 133 expect(variables[0].description).toBeDefined(); 134 expect(variables[0].description?.value).toBe('Described variable'); 135 expect(variables[1].description).toBeUndefined(); 136 }); 137 }); 138 139 describe('FragmentDefinition descriptions', () => { 140 it('parses fragment with description', () => { 141 const source = ` 142 "Time machine details." 143 fragment TimeMachineDetails on TimeMachine { 144 id 145 model 146 lastMaintenance 147 } 148 `; 149 150 const doc = parse(source, { noLocation: true }); 151 const fragment = doc.definitions[0] as FragmentDefinitionNode; 152 153 expect(fragment.description).toBeDefined(); 154 expect(fragment.description?.value).toBe('Time machine details.'); 155 expect(fragment.description?.block).toBe(false); 156 }); 157 158 it('parses fragment with block description', () => { 159 const source = ` 160 """ 161 Comprehensive time machine information 162 including maintenance history and operational status. 163 """ 164 fragment FullTimeMachineInfo on TimeMachine { 165 id 166 model 167 lastMaintenance 168 operationalStatus 169 } 170 `; 171 172 const doc = parse(source, { noLocation: true }); 173 const fragment = doc.definitions[0] as FragmentDefinitionNode; 174 175 expect(fragment.description).toBeDefined(); 176 expect(fragment.description?.value).toBe( 177 'Comprehensive time machine information\nincluding maintenance history and operational status.' 178 ); 179 expect(fragment.description?.block).toBe(true); 180 }); 181 }); 182 183 describe('print with descriptions', () => { 184 it('prints operation description correctly', () => { 185 const source = `""" 186Request the current status of a time machine and its operator. 187""" 188query GetTimeMachineStatus { 189 timeMachine { 190 id 191 } 192}`; 193 194 const doc = parse(source, { noLocation: true }); 195 const printed = print(doc); 196 197 expect(printed).toContain('"""'); 198 expect(printed).toContain('Request the current status of a time machine and its operator.'); 199 }); 200 201 it('prints variable descriptions correctly', () => { 202 const source = `query GetStatus( 203 "Machine ID" 204 $id: ID! 205) { 206 field 207}`; 208 209 const doc = parse(source, { noLocation: true }); 210 const printed = print(doc); 211 212 expect(printed).toContain('"Machine ID"'); 213 }); 214 215 it('prints fragment description correctly', () => { 216 const source = `"Details fragment" 217fragment Details on Type { 218 field 219}`; 220 221 const doc = parse(source, { noLocation: true }); 222 const printed = print(doc); 223 224 expect(printed).toContain('"Details fragment"'); 225 }); 226 }); 227 228 describe('roundtrip parsing and printing', () => { 229 it('maintains descriptions through parse and print cycle', () => { 230 const source = `""" 231Request the current status of a time machine and its operator. 232""" 233query GetTimeMachineStatus( 234 "The unique serial number of the time machine to inspect." 235 $machineId: ID! 236 237 """ 238 The year to check the status for. 239 **Warning:** certain years may trigger an anomaly in the space-time continuum. 240 """ 241 $year: Int 242) { 243 timeMachine(id: $machineId) { 244 ...TimeMachineDetails 245 operator { 246 name 247 licenseLevel 248 } 249 status(year: $year) 250 } 251} 252 253"Time machine details." 254fragment TimeMachineDetails on TimeMachine { 255 id 256 model 257 lastMaintenance 258}`; 259 260 const doc = parse(source, { noLocation: true }); 261 const printed = print(doc); 262 const reparsed = parse(printed, { noLocation: true }); 263 264 const operation = doc.definitions[0] as OperationDefinitionNode; 265 const reparsedOperation = reparsed.definitions[0] as OperationDefinitionNode; 266 267 // The printed/reparsed cycle may have slightly different formatting but same content 268 expect(reparsedOperation.description?.value?.trim()).toBe( 269 operation.description?.value?.trim() 270 ); 271 272 const fragment = doc.definitions[1] as FragmentDefinitionNode; 273 const reparsedFragment = reparsed.definitions[1] as FragmentDefinitionNode; 274 275 expect(reparsedFragment.description?.value).toBe(fragment.description?.value); 276 }); 277 }); 278});