Mirror: The small sibling of the graphql package, slimmed down for client-side libraries.
at v16.6.0-2 47 kB view raw
1// See: https://github.com/graphql/graphql-js/blob/976d64b/src/language/__tests__/visitor-test.ts 2 3import { describe, it, expect } from 'vitest'; 4import { Kind, parse, print } from 'graphql'; 5import { visit, visitInParallel, BREAK } from '../visitor'; 6 7const kitchenSinkQuery = String.raw` 8query queryName($foo: ComplexType, $site: Site = MOBILE) @onQuery { 9 whoever123is: node(id: [123, 456]) { 10 id 11 ... on User @onInlineFragment { 12 field2 { 13 id 14 alias: field1(first: 10, after: $foo) @include(if: $foo) { 15 id 16 ...frag @onFragmentSpread 17 } 18 } 19 } 20 ... @skip(unless: $foo) { 21 id 22 } 23 ... { 24 id 25 } 26 } 27} 28mutation likeStory @onMutation { 29 like(story: 123) @onField { 30 story { 31 id @onField 32 } 33 } 34} 35subscription StoryLikeSubscription( 36 $input: StoryLikeSubscribeInput @onVariableDefinition 37) 38 @onSubscription { 39 storyLikeSubscribe(input: $input) { 40 story { 41 likers { 42 count 43 } 44 likeSentence { 45 text 46 } 47 } 48 } 49} 50fragment frag on Friend @onFragmentDefinition { 51 foo( 52 size: $size 53 bar: $b 54 obj: { 55 key: "value" 56 block: """ 57 block string uses \""" 58 """ 59 } 60 ) 61} 62{ 63 unnamed(truthy: true, falsy: false, nullish: null) 64 query 65} 66query { 67 __typename 68} 69`; 70 71function checkVisitorFnArgs(ast, args, isEdited = false) { 72 const [node, key, parent, path, ancestors] = args; 73 74 expect(node).toBeInstanceOf(Object); 75 expect(Object.values(Kind)).toContain(node.kind); 76 77 const isRoot = key === undefined; 78 if (isRoot) { 79 if (!isEdited) { 80 expect(node).toEqual(ast); 81 } 82 expect(parent).toEqual(undefined); 83 expect(path).toEqual([]); 84 expect(ancestors).toEqual([]); 85 return; 86 } 87 88 expect(typeof key).toMatch(/number|string/); 89 90 expect(parent).toHaveProperty([key]); 91 92 expect(path).toBeInstanceOf(Array); 93 expect(path[path.length - 1]).toEqual(key); 94 95 expect(ancestors).toBeInstanceOf(Array); 96 expect(ancestors.length).toEqual(path.length - 1); 97 98 if (!isEdited) { 99 let currentNode = ast; 100 for (let i = 0; i < ancestors.length; ++i) { 101 expect(ancestors[i]).toEqual(currentNode); 102 103 currentNode = currentNode[path[i]]; 104 expect(currentNode).not.toEqual(undefined); 105 } 106 107 // expect(parent).toEqual(currentNode); 108 // expect(parent[key]).toEqual(node); 109 } 110} 111 112function isNode(node) { 113 return node != null && typeof node.kind === 'string'; 114} 115 116function getValue(node) { 117 return 'value' in node ? node.value : undefined; 118} 119 120describe('Visitor', () => { 121 it('handles empty visitor', () => { 122 const ast = parse('{ a }', { noLocation: true }); 123 expect(() => visit(ast, {})).not.toThrow(); 124 }); 125 126 it('validates path argument', () => { 127 const visited = []; 128 129 const ast = parse('{ a }', { noLocation: true }); 130 131 visit(ast, { 132 enter(_node, _key, _parent, path) { 133 checkVisitorFnArgs(ast, arguments); 134 visited.push(['enter', path.slice()]); 135 }, 136 leave(_node, _key, _parent, path) { 137 checkVisitorFnArgs(ast, arguments); 138 visited.push(['leave', path.slice()]); 139 }, 140 }); 141 142 expect(visited).toEqual([ 143 ['enter', []], 144 ['enter', ['definitions', 0]], 145 ['enter', ['definitions', 0, 'selectionSet']], 146 ['enter', ['definitions', 0, 'selectionSet', 'selections', 0]], 147 ['enter', ['definitions', 0, 'selectionSet', 'selections', 0, 'name']], 148 ['leave', ['definitions', 0, 'selectionSet', 'selections', 0, 'name']], 149 ['leave', ['definitions', 0, 'selectionSet', 'selections', 0]], 150 ['leave', ['definitions', 0, 'selectionSet']], 151 ['leave', ['definitions', 0]], 152 ['leave', []], 153 ]); 154 }); 155 156 it('validates ancestors argument', () => { 157 const ast = parse('{ a }', { noLocation: true }); 158 const visitedNodes = []; 159 160 visit(ast, { 161 enter(node, key, parent, _path, ancestors) { 162 const inArray = typeof key === 'number'; 163 if (inArray) { 164 visitedNodes.push(parent); 165 } 166 visitedNodes.push(node); 167 168 const expectedAncestors = visitedNodes.slice(0, -2); 169 expect(ancestors).toEqual(expectedAncestors); 170 }, 171 leave(_node, key, _parent, _path, ancestors) { 172 const expectedAncestors = visitedNodes.slice(0, -2); 173 expect(ancestors).toEqual(expectedAncestors); 174 175 const inArray = typeof key === 'number'; 176 if (inArray) { 177 visitedNodes.pop(); 178 } 179 visitedNodes.pop(); 180 }, 181 }); 182 }); 183 184 it('allows editing a node both on enter and on leave', () => { 185 const ast = parse('{ a, b, c { a, b, c } }', { noLocation: true }); 186 187 let selectionSet; 188 189 const editedAST = visit(ast, { 190 OperationDefinition: { 191 enter(node) { 192 checkVisitorFnArgs(ast, arguments); 193 selectionSet = node.selectionSet; 194 return { 195 ...node, 196 selectionSet: { 197 kind: 'SelectionSet', 198 selections: [], 199 }, 200 didEnter: true, 201 }; 202 }, 203 leave(node) { 204 checkVisitorFnArgs(ast, arguments, /* isEdited */ true); 205 return { 206 ...node, 207 selectionSet, 208 didLeave: true, 209 }; 210 }, 211 }, 212 }); 213 214 expect(editedAST).toEqual({ 215 ...ast, 216 definitions: [ 217 { 218 ...ast.definitions[0], 219 didEnter: true, 220 didLeave: true, 221 }, 222 ], 223 }); 224 }); 225 226 it('allows editing the root node on enter and on leave', () => { 227 const ast = parse('{ a, b, c { a, b, c } }', { noLocation: true }); 228 229 const { definitions } = ast; 230 231 const editedAST = visit(ast, { 232 Document: { 233 enter(node) { 234 checkVisitorFnArgs(ast, arguments); 235 return { 236 ...node, 237 definitions: [], 238 didEnter: true, 239 }; 240 }, 241 leave(node) { 242 checkVisitorFnArgs(ast, arguments, /* isEdited */ true); 243 return { 244 ...node, 245 definitions, 246 didLeave: true, 247 }; 248 }, 249 }, 250 }); 251 252 expect(editedAST).toEqual({ 253 ...ast, 254 didEnter: true, 255 didLeave: true, 256 }); 257 }); 258 259 it('allows for editing on enter', () => { 260 const ast = parse('{ a, b, c { a, b, c } }', { noLocation: true }); 261 const editedAST = visit(ast, { 262 enter(node) { 263 checkVisitorFnArgs(ast, arguments); 264 if (node.kind === 'Field' && node.name.value === 'b') { 265 return null; 266 } 267 }, 268 }); 269 270 expect(ast).toEqual(parse('{ a, b, c { a, b, c } }', { noLocation: true })); 271 272 expect(editedAST).toEqual(parse('{ a, c { a, c } }', { noLocation: true })); 273 }); 274 275 it('allows for editing on leave', () => { 276 const ast = parse('{ a, b, c { a, b, c } }', { noLocation: true }); 277 const editedAST = visit(ast, { 278 leave(node) { 279 checkVisitorFnArgs(ast, arguments, /* isEdited */ true); 280 if (node.kind === 'Field' && node.name.value === 'b') { 281 return null; 282 } 283 }, 284 }); 285 286 expect(ast).toEqual(parse('{ a, b, c { a, b, c } }', { noLocation: true })); 287 288 expect(editedAST).toEqual(parse('{ a, c { a, c } }', { noLocation: true })); 289 }); 290 291 it('ignores false returned on leave', () => { 292 const ast = parse('{ a, b, c { a, b, c } }', { noLocation: true }); 293 const returnedAST = visit(ast, { 294 leave() { 295 return false; 296 }, 297 }); 298 299 expect(returnedAST).toEqual(parse('{ a, b, c { a, b, c } }', { noLocation: true })); 300 }); 301 302 it('visits edited node', () => { 303 const addedField = { 304 kind: 'Field', 305 name: { 306 kind: 'Name', 307 value: '__typename', 308 }, 309 }; 310 311 let didVisitAddedField; 312 313 const ast = parse('{ a { x } }', { noLocation: true }); 314 visit(ast, { 315 enter(node) { 316 checkVisitorFnArgs(ast, arguments, /* isEdited */ true); 317 if (node.kind === 'Field' && node.name.value === 'a') { 318 return { 319 kind: 'Field', 320 selectionSet: [addedField, node.selectionSet], 321 }; 322 } 323 if (node === addedField) { 324 didVisitAddedField = true; 325 } 326 }, 327 }); 328 329 expect(didVisitAddedField).toEqual(true); 330 }); 331 332 it('allows skipping a sub-tree', () => { 333 const visited = []; 334 335 const ast = parse('{ a, b { x }, c }', { noLocation: true }); 336 visit(ast, { 337 enter(node) { 338 checkVisitorFnArgs(ast, arguments); 339 visited.push(['enter', node.kind, getValue(node)]); 340 if (node.kind === 'Field' && node.name.value === 'b') { 341 return false; 342 } 343 }, 344 345 leave(node) { 346 checkVisitorFnArgs(ast, arguments); 347 visited.push(['leave', node.kind, getValue(node)]); 348 }, 349 }); 350 351 expect(visited).toEqual([ 352 ['enter', 'Document', undefined], 353 ['enter', 'OperationDefinition', undefined], 354 ['enter', 'SelectionSet', undefined], 355 ['enter', 'Field', undefined], 356 ['enter', 'Name', 'a'], 357 ['leave', 'Name', 'a'], 358 ['leave', 'Field', undefined], 359 ['enter', 'Field', undefined], 360 ['enter', 'Field', undefined], 361 ['enter', 'Name', 'c'], 362 ['leave', 'Name', 'c'], 363 ['leave', 'Field', undefined], 364 ['leave', 'SelectionSet', undefined], 365 ['leave', 'OperationDefinition', undefined], 366 ['leave', 'Document', undefined], 367 ]); 368 }); 369 370 it('allows early exit while visiting', () => { 371 const visited = []; 372 373 const ast = parse('{ a, b { x }, c }', { noLocation: true }); 374 visit(ast, { 375 enter(node) { 376 checkVisitorFnArgs(ast, arguments); 377 visited.push(['enter', node.kind, getValue(node)]); 378 if (node.kind === 'Name' && node.value === 'x') { 379 return BREAK; 380 } 381 }, 382 leave(node) { 383 checkVisitorFnArgs(ast, arguments); 384 visited.push(['leave', node.kind, getValue(node)]); 385 }, 386 }); 387 388 expect(visited).toEqual([ 389 ['enter', 'Document', undefined], 390 ['enter', 'OperationDefinition', undefined], 391 ['enter', 'SelectionSet', undefined], 392 ['enter', 'Field', undefined], 393 ['enter', 'Name', 'a'], 394 ['leave', 'Name', 'a'], 395 ['leave', 'Field', undefined], 396 ['enter', 'Field', undefined], 397 ['enter', 'Name', 'b'], 398 ['leave', 'Name', 'b'], 399 ['enter', 'SelectionSet', undefined], 400 ['enter', 'Field', undefined], 401 ['enter', 'Name', 'x'], 402 ]); 403 }); 404 405 it('allows early exit while leaving', () => { 406 const visited = []; 407 408 const ast = parse('{ a, b { x }, c }', { noLocation: true }); 409 visit(ast, { 410 enter(node) { 411 checkVisitorFnArgs(ast, arguments); 412 visited.push(['enter', node.kind, getValue(node)]); 413 }, 414 415 leave(node) { 416 checkVisitorFnArgs(ast, arguments); 417 visited.push(['leave', node.kind, getValue(node)]); 418 if (node.kind === 'Name' && node.value === 'x') { 419 return BREAK; 420 } 421 }, 422 }); 423 424 expect(visited).toEqual([ 425 ['enter', 'Document', undefined], 426 ['enter', 'OperationDefinition', undefined], 427 ['enter', 'SelectionSet', undefined], 428 ['enter', 'Field', undefined], 429 ['enter', 'Name', 'a'], 430 ['leave', 'Name', 'a'], 431 ['leave', 'Field', undefined], 432 ['enter', 'Field', undefined], 433 ['enter', 'Name', 'b'], 434 ['leave', 'Name', 'b'], 435 ['enter', 'SelectionSet', undefined], 436 ['enter', 'Field', undefined], 437 ['enter', 'Name', 'x'], 438 ['leave', 'Name', 'x'], 439 ]); 440 }); 441 442 it('allows a named functions visitor API', () => { 443 const visited = []; 444 445 const ast = parse('{ a, b { x }, c }', { noLocation: true }); 446 visit(ast, { 447 Name(node) { 448 checkVisitorFnArgs(ast, arguments); 449 visited.push(['enter', node.kind, getValue(node)]); 450 }, 451 SelectionSet: { 452 enter(node) { 453 checkVisitorFnArgs(ast, arguments); 454 visited.push(['enter', node.kind, getValue(node)]); 455 }, 456 leave(node) { 457 checkVisitorFnArgs(ast, arguments); 458 visited.push(['leave', node.kind, getValue(node)]); 459 }, 460 }, 461 }); 462 463 expect(visited).toEqual([ 464 ['enter', 'SelectionSet', undefined], 465 ['enter', 'Name', 'a'], 466 ['enter', 'Name', 'b'], 467 ['enter', 'SelectionSet', undefined], 468 ['enter', 'Name', 'x'], 469 ['leave', 'SelectionSet', undefined], 470 ['enter', 'Name', 'c'], 471 ['leave', 'SelectionSet', undefined], 472 ]); 473 }); 474 475 it('handles deep immutable edits correctly when using "enter"', () => { 476 const formatNode = node => { 477 if ( 478 node.selectionSet && 479 !node.selectionSet.selections.some( 480 node => node.kind === Kind.FIELD && node.name.value === '__typename' && !node.alias 481 ) 482 ) { 483 return { 484 ...node, 485 selectionSet: { 486 ...node.selectionSet, 487 selections: [ 488 ...node.selectionSet.selections, 489 { 490 kind: Kind.FIELD, 491 name: { 492 kind: Kind.NAME, 493 value: '__typename', 494 }, 495 }, 496 ], 497 }, 498 }; 499 } 500 }; 501 const ast = parse('{ players { nodes { id } } }'); 502 const expected = parse('{ players { nodes { id __typename } __typename } }'); 503 const visited = visit(ast, { 504 Field: formatNode, 505 InlineFragment: formatNode, 506 }); 507 508 expect(print(visited)).toEqual(print(expected)); 509 }); 510 511 it('visits kitchen sink', () => { 512 const ast = parse(kitchenSinkQuery); 513 const visited = []; 514 const argsStack = []; 515 516 visit(ast, { 517 enter(node, key, parent) { 518 visited.push(['enter', node.kind, key, isNode(parent) ? parent.kind : undefined]); 519 520 checkVisitorFnArgs(ast, arguments); 521 argsStack.push([...arguments]); 522 }, 523 524 leave(node, key, parent) { 525 visited.push(['leave', node.kind, key, isNode(parent) ? parent.kind : undefined]); 526 527 expect(argsStack.pop()).toEqual([...arguments]); 528 }, 529 }); 530 531 expect(argsStack).toEqual([]); 532 expect(visited).toEqual([ 533 ['enter', 'Document', undefined, undefined], 534 ['enter', 'OperationDefinition', 0, undefined], 535 ['enter', 'Name', 'name', 'OperationDefinition'], 536 ['leave', 'Name', 'name', 'OperationDefinition'], 537 ['enter', 'VariableDefinition', 0, undefined], 538 ['enter', 'Variable', 'variable', 'VariableDefinition'], 539 ['enter', 'Name', 'name', 'Variable'], 540 ['leave', 'Name', 'name', 'Variable'], 541 ['leave', 'Variable', 'variable', 'VariableDefinition'], 542 ['enter', 'NamedType', 'type', 'VariableDefinition'], 543 ['enter', 'Name', 'name', 'NamedType'], 544 ['leave', 'Name', 'name', 'NamedType'], 545 ['leave', 'NamedType', 'type', 'VariableDefinition'], 546 ['leave', 'VariableDefinition', 0, undefined], 547 ['enter', 'VariableDefinition', 1, undefined], 548 ['enter', 'Variable', 'variable', 'VariableDefinition'], 549 ['enter', 'Name', 'name', 'Variable'], 550 ['leave', 'Name', 'name', 'Variable'], 551 ['leave', 'Variable', 'variable', 'VariableDefinition'], 552 ['enter', 'NamedType', 'type', 'VariableDefinition'], 553 ['enter', 'Name', 'name', 'NamedType'], 554 ['leave', 'Name', 'name', 'NamedType'], 555 ['leave', 'NamedType', 'type', 'VariableDefinition'], 556 ['enter', 'EnumValue', 'defaultValue', 'VariableDefinition'], 557 ['leave', 'EnumValue', 'defaultValue', 'VariableDefinition'], 558 ['leave', 'VariableDefinition', 1, undefined], 559 ['enter', 'Directive', 0, undefined], 560 ['enter', 'Name', 'name', 'Directive'], 561 ['leave', 'Name', 'name', 'Directive'], 562 ['leave', 'Directive', 0, undefined], 563 ['enter', 'SelectionSet', 'selectionSet', 'OperationDefinition'], 564 ['enter', 'Field', 0, undefined], 565 ['enter', 'Name', 'alias', 'Field'], 566 ['leave', 'Name', 'alias', 'Field'], 567 ['enter', 'Name', 'name', 'Field'], 568 ['leave', 'Name', 'name', 'Field'], 569 ['enter', 'Argument', 0, undefined], 570 ['enter', 'Name', 'name', 'Argument'], 571 ['leave', 'Name', 'name', 'Argument'], 572 ['enter', 'ListValue', 'value', 'Argument'], 573 ['enter', 'IntValue', 0, undefined], 574 ['leave', 'IntValue', 0, undefined], 575 ['enter', 'IntValue', 1, undefined], 576 ['leave', 'IntValue', 1, undefined], 577 ['leave', 'ListValue', 'value', 'Argument'], 578 ['leave', 'Argument', 0, undefined], 579 ['enter', 'SelectionSet', 'selectionSet', 'Field'], 580 ['enter', 'Field', 0, undefined], 581 ['enter', 'Name', 'name', 'Field'], 582 ['leave', 'Name', 'name', 'Field'], 583 ['leave', 'Field', 0, undefined], 584 ['enter', 'InlineFragment', 1, undefined], 585 ['enter', 'NamedType', 'typeCondition', 'InlineFragment'], 586 ['enter', 'Name', 'name', 'NamedType'], 587 ['leave', 'Name', 'name', 'NamedType'], 588 ['leave', 'NamedType', 'typeCondition', 'InlineFragment'], 589 ['enter', 'Directive', 0, undefined], 590 ['enter', 'Name', 'name', 'Directive'], 591 ['leave', 'Name', 'name', 'Directive'], 592 ['leave', 'Directive', 0, undefined], 593 ['enter', 'SelectionSet', 'selectionSet', 'InlineFragment'], 594 ['enter', 'Field', 0, undefined], 595 ['enter', 'Name', 'name', 'Field'], 596 ['leave', 'Name', 'name', 'Field'], 597 ['enter', 'SelectionSet', 'selectionSet', 'Field'], 598 ['enter', 'Field', 0, undefined], 599 ['enter', 'Name', 'name', 'Field'], 600 ['leave', 'Name', 'name', 'Field'], 601 ['leave', 'Field', 0, undefined], 602 ['enter', 'Field', 1, undefined], 603 ['enter', 'Name', 'alias', 'Field'], 604 ['leave', 'Name', 'alias', 'Field'], 605 ['enter', 'Name', 'name', 'Field'], 606 ['leave', 'Name', 'name', 'Field'], 607 ['enter', 'Argument', 0, undefined], 608 ['enter', 'Name', 'name', 'Argument'], 609 ['leave', 'Name', 'name', 'Argument'], 610 ['enter', 'IntValue', 'value', 'Argument'], 611 ['leave', 'IntValue', 'value', 'Argument'], 612 ['leave', 'Argument', 0, undefined], 613 ['enter', 'Argument', 1, undefined], 614 ['enter', 'Name', 'name', 'Argument'], 615 ['leave', 'Name', 'name', 'Argument'], 616 ['enter', 'Variable', 'value', 'Argument'], 617 ['enter', 'Name', 'name', 'Variable'], 618 ['leave', 'Name', 'name', 'Variable'], 619 ['leave', 'Variable', 'value', 'Argument'], 620 ['leave', 'Argument', 1, undefined], 621 ['enter', 'Directive', 0, undefined], 622 ['enter', 'Name', 'name', 'Directive'], 623 ['leave', 'Name', 'name', 'Directive'], 624 ['enter', 'Argument', 0, undefined], 625 ['enter', 'Name', 'name', 'Argument'], 626 ['leave', 'Name', 'name', 'Argument'], 627 ['enter', 'Variable', 'value', 'Argument'], 628 ['enter', 'Name', 'name', 'Variable'], 629 ['leave', 'Name', 'name', 'Variable'], 630 ['leave', 'Variable', 'value', 'Argument'], 631 ['leave', 'Argument', 0, undefined], 632 ['leave', 'Directive', 0, undefined], 633 ['enter', 'SelectionSet', 'selectionSet', 'Field'], 634 ['enter', 'Field', 0, undefined], 635 ['enter', 'Name', 'name', 'Field'], 636 ['leave', 'Name', 'name', 'Field'], 637 ['leave', 'Field', 0, undefined], 638 ['enter', 'FragmentSpread', 1, undefined], 639 ['enter', 'Name', 'name', 'FragmentSpread'], 640 ['leave', 'Name', 'name', 'FragmentSpread'], 641 ['enter', 'Directive', 0, undefined], 642 ['enter', 'Name', 'name', 'Directive'], 643 ['leave', 'Name', 'name', 'Directive'], 644 ['leave', 'Directive', 0, undefined], 645 ['leave', 'FragmentSpread', 1, undefined], 646 ['leave', 'SelectionSet', 'selectionSet', 'Field'], 647 ['leave', 'Field', 1, undefined], 648 ['leave', 'SelectionSet', 'selectionSet', 'Field'], 649 ['leave', 'Field', 0, undefined], 650 ['leave', 'SelectionSet', 'selectionSet', 'InlineFragment'], 651 ['leave', 'InlineFragment', 1, undefined], 652 ['enter', 'InlineFragment', 2, undefined], 653 ['enter', 'Directive', 0, undefined], 654 ['enter', 'Name', 'name', 'Directive'], 655 ['leave', 'Name', 'name', 'Directive'], 656 ['enter', 'Argument', 0, undefined], 657 ['enter', 'Name', 'name', 'Argument'], 658 ['leave', 'Name', 'name', 'Argument'], 659 ['enter', 'Variable', 'value', 'Argument'], 660 ['enter', 'Name', 'name', 'Variable'], 661 ['leave', 'Name', 'name', 'Variable'], 662 ['leave', 'Variable', 'value', 'Argument'], 663 ['leave', 'Argument', 0, undefined], 664 ['leave', 'Directive', 0, undefined], 665 ['enter', 'SelectionSet', 'selectionSet', 'InlineFragment'], 666 ['enter', 'Field', 0, undefined], 667 ['enter', 'Name', 'name', 'Field'], 668 ['leave', 'Name', 'name', 'Field'], 669 ['leave', 'Field', 0, undefined], 670 ['leave', 'SelectionSet', 'selectionSet', 'InlineFragment'], 671 ['leave', 'InlineFragment', 2, undefined], 672 ['enter', 'InlineFragment', 3, undefined], 673 ['enter', 'SelectionSet', 'selectionSet', 'InlineFragment'], 674 ['enter', 'Field', 0, undefined], 675 ['enter', 'Name', 'name', 'Field'], 676 ['leave', 'Name', 'name', 'Field'], 677 ['leave', 'Field', 0, undefined], 678 ['leave', 'SelectionSet', 'selectionSet', 'InlineFragment'], 679 ['leave', 'InlineFragment', 3, undefined], 680 ['leave', 'SelectionSet', 'selectionSet', 'Field'], 681 ['leave', 'Field', 0, undefined], 682 ['leave', 'SelectionSet', 'selectionSet', 'OperationDefinition'], 683 ['leave', 'OperationDefinition', 0, undefined], 684 ['enter', 'OperationDefinition', 1, undefined], 685 ['enter', 'Name', 'name', 'OperationDefinition'], 686 ['leave', 'Name', 'name', 'OperationDefinition'], 687 ['enter', 'Directive', 0, undefined], 688 ['enter', 'Name', 'name', 'Directive'], 689 ['leave', 'Name', 'name', 'Directive'], 690 ['leave', 'Directive', 0, undefined], 691 ['enter', 'SelectionSet', 'selectionSet', 'OperationDefinition'], 692 ['enter', 'Field', 0, undefined], 693 ['enter', 'Name', 'name', 'Field'], 694 ['leave', 'Name', 'name', 'Field'], 695 ['enter', 'Argument', 0, undefined], 696 ['enter', 'Name', 'name', 'Argument'], 697 ['leave', 'Name', 'name', 'Argument'], 698 ['enter', 'IntValue', 'value', 'Argument'], 699 ['leave', 'IntValue', 'value', 'Argument'], 700 ['leave', 'Argument', 0, undefined], 701 ['enter', 'Directive', 0, undefined], 702 ['enter', 'Name', 'name', 'Directive'], 703 ['leave', 'Name', 'name', 'Directive'], 704 ['leave', 'Directive', 0, undefined], 705 ['enter', 'SelectionSet', 'selectionSet', 'Field'], 706 ['enter', 'Field', 0, undefined], 707 ['enter', 'Name', 'name', 'Field'], 708 ['leave', 'Name', 'name', 'Field'], 709 ['enter', 'SelectionSet', 'selectionSet', 'Field'], 710 ['enter', 'Field', 0, undefined], 711 ['enter', 'Name', 'name', 'Field'], 712 ['leave', 'Name', 'name', 'Field'], 713 ['enter', 'Directive', 0, undefined], 714 ['enter', 'Name', 'name', 'Directive'], 715 ['leave', 'Name', 'name', 'Directive'], 716 ['leave', 'Directive', 0, undefined], 717 ['leave', 'Field', 0, undefined], 718 ['leave', 'SelectionSet', 'selectionSet', 'Field'], 719 ['leave', 'Field', 0, undefined], 720 ['leave', 'SelectionSet', 'selectionSet', 'Field'], 721 ['leave', 'Field', 0, undefined], 722 ['leave', 'SelectionSet', 'selectionSet', 'OperationDefinition'], 723 ['leave', 'OperationDefinition', 1, undefined], 724 ['enter', 'OperationDefinition', 2, undefined], 725 ['enter', 'Name', 'name', 'OperationDefinition'], 726 ['leave', 'Name', 'name', 'OperationDefinition'], 727 ['enter', 'VariableDefinition', 0, undefined], 728 ['enter', 'Variable', 'variable', 'VariableDefinition'], 729 ['enter', 'Name', 'name', 'Variable'], 730 ['leave', 'Name', 'name', 'Variable'], 731 ['leave', 'Variable', 'variable', 'VariableDefinition'], 732 ['enter', 'NamedType', 'type', 'VariableDefinition'], 733 ['enter', 'Name', 'name', 'NamedType'], 734 ['leave', 'Name', 'name', 'NamedType'], 735 ['leave', 'NamedType', 'type', 'VariableDefinition'], 736 ['enter', 'Directive', 0, undefined], 737 ['enter', 'Name', 'name', 'Directive'], 738 ['leave', 'Name', 'name', 'Directive'], 739 ['leave', 'Directive', 0, undefined], 740 ['leave', 'VariableDefinition', 0, undefined], 741 ['enter', 'Directive', 0, undefined], 742 ['enter', 'Name', 'name', 'Directive'], 743 ['leave', 'Name', 'name', 'Directive'], 744 ['leave', 'Directive', 0, undefined], 745 ['enter', 'SelectionSet', 'selectionSet', 'OperationDefinition'], 746 ['enter', 'Field', 0, undefined], 747 ['enter', 'Name', 'name', 'Field'], 748 ['leave', 'Name', 'name', 'Field'], 749 ['enter', 'Argument', 0, undefined], 750 ['enter', 'Name', 'name', 'Argument'], 751 ['leave', 'Name', 'name', 'Argument'], 752 ['enter', 'Variable', 'value', 'Argument'], 753 ['enter', 'Name', 'name', 'Variable'], 754 ['leave', 'Name', 'name', 'Variable'], 755 ['leave', 'Variable', 'value', 'Argument'], 756 ['leave', 'Argument', 0, undefined], 757 ['enter', 'SelectionSet', 'selectionSet', 'Field'], 758 ['enter', 'Field', 0, undefined], 759 ['enter', 'Name', 'name', 'Field'], 760 ['leave', 'Name', 'name', 'Field'], 761 ['enter', 'SelectionSet', 'selectionSet', 'Field'], 762 ['enter', 'Field', 0, undefined], 763 ['enter', 'Name', 'name', 'Field'], 764 ['leave', 'Name', 'name', 'Field'], 765 ['enter', 'SelectionSet', 'selectionSet', 'Field'], 766 ['enter', 'Field', 0, undefined], 767 ['enter', 'Name', 'name', 'Field'], 768 ['leave', 'Name', 'name', 'Field'], 769 ['leave', 'Field', 0, undefined], 770 ['leave', 'SelectionSet', 'selectionSet', 'Field'], 771 ['leave', 'Field', 0, undefined], 772 ['enter', 'Field', 1, undefined], 773 ['enter', 'Name', 'name', 'Field'], 774 ['leave', 'Name', 'name', 'Field'], 775 ['enter', 'SelectionSet', 'selectionSet', 'Field'], 776 ['enter', 'Field', 0, undefined], 777 ['enter', 'Name', 'name', 'Field'], 778 ['leave', 'Name', 'name', 'Field'], 779 ['leave', 'Field', 0, undefined], 780 ['leave', 'SelectionSet', 'selectionSet', 'Field'], 781 ['leave', 'Field', 1, undefined], 782 ['leave', 'SelectionSet', 'selectionSet', 'Field'], 783 ['leave', 'Field', 0, undefined], 784 ['leave', 'SelectionSet', 'selectionSet', 'Field'], 785 ['leave', 'Field', 0, undefined], 786 ['leave', 'SelectionSet', 'selectionSet', 'OperationDefinition'], 787 ['leave', 'OperationDefinition', 2, undefined], 788 ['enter', 'FragmentDefinition', 3, undefined], 789 ['enter', 'Name', 'name', 'FragmentDefinition'], 790 ['leave', 'Name', 'name', 'FragmentDefinition'], 791 ['enter', 'NamedType', 'typeCondition', 'FragmentDefinition'], 792 ['enter', 'Name', 'name', 'NamedType'], 793 ['leave', 'Name', 'name', 'NamedType'], 794 ['leave', 'NamedType', 'typeCondition', 'FragmentDefinition'], 795 ['enter', 'Directive', 0, undefined], 796 ['enter', 'Name', 'name', 'Directive'], 797 ['leave', 'Name', 'name', 'Directive'], 798 ['leave', 'Directive', 0, undefined], 799 ['enter', 'SelectionSet', 'selectionSet', 'FragmentDefinition'], 800 ['enter', 'Field', 0, undefined], 801 ['enter', 'Name', 'name', 'Field'], 802 ['leave', 'Name', 'name', 'Field'], 803 ['enter', 'Argument', 0, undefined], 804 ['enter', 'Name', 'name', 'Argument'], 805 ['leave', 'Name', 'name', 'Argument'], 806 ['enter', 'Variable', 'value', 'Argument'], 807 ['enter', 'Name', 'name', 'Variable'], 808 ['leave', 'Name', 'name', 'Variable'], 809 ['leave', 'Variable', 'value', 'Argument'], 810 ['leave', 'Argument', 0, undefined], 811 ['enter', 'Argument', 1, undefined], 812 ['enter', 'Name', 'name', 'Argument'], 813 ['leave', 'Name', 'name', 'Argument'], 814 ['enter', 'Variable', 'value', 'Argument'], 815 ['enter', 'Name', 'name', 'Variable'], 816 ['leave', 'Name', 'name', 'Variable'], 817 ['leave', 'Variable', 'value', 'Argument'], 818 ['leave', 'Argument', 1, undefined], 819 ['enter', 'Argument', 2, undefined], 820 ['enter', 'Name', 'name', 'Argument'], 821 ['leave', 'Name', 'name', 'Argument'], 822 ['enter', 'ObjectValue', 'value', 'Argument'], 823 ['enter', 'ObjectField', 0, undefined], 824 ['enter', 'Name', 'name', 'ObjectField'], 825 ['leave', 'Name', 'name', 'ObjectField'], 826 ['enter', 'StringValue', 'value', 'ObjectField'], 827 ['leave', 'StringValue', 'value', 'ObjectField'], 828 ['leave', 'ObjectField', 0, undefined], 829 ['enter', 'ObjectField', 1, undefined], 830 ['enter', 'Name', 'name', 'ObjectField'], 831 ['leave', 'Name', 'name', 'ObjectField'], 832 ['enter', 'StringValue', 'value', 'ObjectField'], 833 ['leave', 'StringValue', 'value', 'ObjectField'], 834 ['leave', 'ObjectField', 1, undefined], 835 ['leave', 'ObjectValue', 'value', 'Argument'], 836 ['leave', 'Argument', 2, undefined], 837 ['leave', 'Field', 0, undefined], 838 ['leave', 'SelectionSet', 'selectionSet', 'FragmentDefinition'], 839 ['leave', 'FragmentDefinition', 3, undefined], 840 ['enter', 'OperationDefinition', 4, undefined], 841 ['enter', 'SelectionSet', 'selectionSet', 'OperationDefinition'], 842 ['enter', 'Field', 0, undefined], 843 ['enter', 'Name', 'name', 'Field'], 844 ['leave', 'Name', 'name', 'Field'], 845 ['enter', 'Argument', 0, undefined], 846 ['enter', 'Name', 'name', 'Argument'], 847 ['leave', 'Name', 'name', 'Argument'], 848 ['enter', 'BooleanValue', 'value', 'Argument'], 849 ['leave', 'BooleanValue', 'value', 'Argument'], 850 ['leave', 'Argument', 0, undefined], 851 ['enter', 'Argument', 1, undefined], 852 ['enter', 'Name', 'name', 'Argument'], 853 ['leave', 'Name', 'name', 'Argument'], 854 ['enter', 'BooleanValue', 'value', 'Argument'], 855 ['leave', 'BooleanValue', 'value', 'Argument'], 856 ['leave', 'Argument', 1, undefined], 857 ['enter', 'Argument', 2, undefined], 858 ['enter', 'Name', 'name', 'Argument'], 859 ['leave', 'Name', 'name', 'Argument'], 860 ['enter', 'NullValue', 'value', 'Argument'], 861 ['leave', 'NullValue', 'value', 'Argument'], 862 ['leave', 'Argument', 2, undefined], 863 ['leave', 'Field', 0, undefined], 864 ['enter', 'Field', 1, undefined], 865 ['enter', 'Name', 'name', 'Field'], 866 ['leave', 'Name', 'name', 'Field'], 867 ['leave', 'Field', 1, undefined], 868 ['leave', 'SelectionSet', 'selectionSet', 'OperationDefinition'], 869 ['leave', 'OperationDefinition', 4, undefined], 870 ['enter', 'OperationDefinition', 5, undefined], 871 ['enter', 'SelectionSet', 'selectionSet', 'OperationDefinition'], 872 ['enter', 'Field', 0, undefined], 873 ['enter', 'Name', 'name', 'Field'], 874 ['leave', 'Name', 'name', 'Field'], 875 ['leave', 'Field', 0, undefined], 876 ['leave', 'SelectionSet', 'selectionSet', 'OperationDefinition'], 877 ['leave', 'OperationDefinition', 5, undefined], 878 ['leave', 'Document', undefined, undefined], 879 ]); 880 }); 881 882 // TODO: Verify discrepancies 883 describe('visitInParallel', () => { 884 // Note: nearly identical to the above test of the same test but 885 // using visitInParallel. 886 it('allows skipping a sub-tree', () => { 887 const visited = []; 888 889 const ast = parse('{ a, b { x }, c }'); 890 visit( 891 ast, 892 visitInParallel([ 893 { 894 enter(node) { 895 checkVisitorFnArgs(ast, arguments); 896 visited.push(['enter', node.kind, getValue(node)]); 897 if (node.kind === 'Field' && node.name.value === 'b') { 898 return false; 899 } 900 }, 901 902 leave(node) { 903 checkVisitorFnArgs(ast, arguments); 904 visited.push(['leave', node.kind, getValue(node)]); 905 }, 906 }, 907 ]) 908 ); 909 910 expect(visited).toEqual([ 911 ['enter', 'Document', undefined], 912 ['enter', 'OperationDefinition', undefined], 913 ['enter', 'SelectionSet', undefined], 914 ['enter', 'Field', undefined], 915 ['enter', 'Name', 'a'], 916 ['leave', 'Name', 'a'], 917 ['leave', 'Field', undefined], 918 ['enter', 'Field', undefined], 919 ['enter', 'Field', undefined], 920 ['enter', 'Name', 'c'], 921 ['leave', 'Name', 'c'], 922 ['leave', 'Field', undefined], 923 ['leave', 'SelectionSet', undefined], 924 ['leave', 'OperationDefinition', undefined], 925 ['leave', 'Document', undefined], 926 ]); 927 }); 928 929 it('allows skipping different sub-trees', () => { 930 const visited = []; 931 932 const ast = parse('{ a { x }, b { y} }'); 933 visit( 934 ast, 935 visitInParallel([ 936 { 937 enter(node) { 938 checkVisitorFnArgs(ast, arguments); 939 visited.push(['no-a', 'enter', node.kind, getValue(node)]); 940 if (node.kind === 'Field' && node.name.value === 'a') { 941 return false; 942 } 943 }, 944 leave(node) { 945 checkVisitorFnArgs(ast, arguments); 946 visited.push(['no-a', 'leave', node.kind, getValue(node)]); 947 }, 948 }, 949 { 950 enter(node) { 951 checkVisitorFnArgs(ast, arguments); 952 visited.push(['no-b', 'enter', node.kind, getValue(node)]); 953 if (node.kind === 'Field' && node.name.value === 'b') { 954 return false; 955 } 956 }, 957 leave(node) { 958 checkVisitorFnArgs(ast, arguments); 959 visited.push(['no-b', 'leave', node.kind, getValue(node)]); 960 }, 961 }, 962 ]) 963 ); 964 965 expect(visited).toEqual([ 966 ['no-a', 'enter', 'Document', undefined], 967 ['no-b', 'enter', 'Document', undefined], 968 ['no-a', 'enter', 'OperationDefinition', undefined], 969 ['no-b', 'enter', 'OperationDefinition', undefined], 970 ['no-a', 'enter', 'SelectionSet', undefined], 971 ['no-b', 'enter', 'SelectionSet', undefined], 972 ['no-a', 'enter', 'Field', undefined], 973 ['no-b', 'enter', 'Field', undefined], 974 ['no-b', 'enter', 'Name', 'a'], 975 ['no-b', 'leave', 'Name', 'a'], 976 ['no-b', 'enter', 'SelectionSet', undefined], 977 ['no-b', 'enter', 'Field', undefined], 978 ['no-b', 'enter', 'Name', 'x'], 979 ['no-b', 'leave', 'Name', 'x'], 980 ['no-b', 'leave', 'Field', undefined], 981 ['no-b', 'leave', 'SelectionSet', undefined], 982 ['no-b', 'leave', 'Field', undefined], 983 ['no-a', 'enter', 'Field', undefined], 984 ['no-b', 'enter', 'Field', undefined], 985 ['no-a', 'enter', 'Name', 'b'], 986 ['no-a', 'leave', 'Name', 'b'], 987 ['no-a', 'enter', 'SelectionSet', undefined], 988 ['no-a', 'enter', 'Field', undefined], 989 ['no-a', 'enter', 'Name', 'y'], 990 ['no-a', 'leave', 'Name', 'y'], 991 ['no-a', 'leave', 'Field', undefined], 992 ['no-a', 'leave', 'SelectionSet', undefined], 993 ['no-a', 'leave', 'Field', undefined], 994 ['no-a', 'leave', 'SelectionSet', undefined], 995 ['no-b', 'leave', 'SelectionSet', undefined], 996 ['no-a', 'leave', 'OperationDefinition', undefined], 997 ['no-b', 'leave', 'OperationDefinition', undefined], 998 ['no-a', 'leave', 'Document', undefined], 999 ['no-b', 'leave', 'Document', undefined], 1000 ]); 1001 }); 1002 1003 // Note: nearly identical to the above test of the same test but 1004 // using visitInParallel. 1005 it('allows early exit while visiting', () => { 1006 const visited = []; 1007 1008 const ast = parse('{ a, b { x }, c }'); 1009 visit( 1010 ast, 1011 visitInParallel([ 1012 { 1013 enter(node) { 1014 checkVisitorFnArgs(ast, arguments); 1015 visited.push(['enter', node.kind, getValue(node)]); 1016 if (node.kind === 'Name' && node.value === 'x') { 1017 return BREAK; 1018 } 1019 }, 1020 leave(node) { 1021 checkVisitorFnArgs(ast, arguments); 1022 visited.push(['leave', node.kind, getValue(node)]); 1023 }, 1024 }, 1025 ]) 1026 ); 1027 1028 expect(visited).toEqual([ 1029 ['enter', 'Document', undefined], 1030 ['enter', 'OperationDefinition', undefined], 1031 ['enter', 'SelectionSet', undefined], 1032 ['enter', 'Field', undefined], 1033 ['enter', 'Name', 'a'], 1034 ['leave', 'Name', 'a'], 1035 ['leave', 'Field', undefined], 1036 ['enter', 'Field', undefined], 1037 ['enter', 'Name', 'b'], 1038 ['leave', 'Name', 'b'], 1039 ['enter', 'SelectionSet', undefined], 1040 ['enter', 'Field', undefined], 1041 ['enter', 'Name', 'x'], 1042 ]); 1043 }); 1044 1045 it('allows early exit from different points', () => { 1046 const visited = []; 1047 1048 const ast = parse('{ a { y }, b { x } }'); 1049 visit( 1050 ast, 1051 visitInParallel([ 1052 { 1053 enter(node) { 1054 checkVisitorFnArgs(ast, arguments); 1055 visited.push(['break-a', 'enter', node.kind, getValue(node)]); 1056 if (node.kind === 'Name' && node.value === 'a') { 1057 return BREAK; 1058 } 1059 }, 1060 // istanbul ignore next (Never called and used as a placeholder) 1061 leave() { 1062 expect.fail('Should not be called'); 1063 }, 1064 }, 1065 { 1066 enter(node) { 1067 checkVisitorFnArgs(ast, arguments); 1068 visited.push(['break-b', 'enter', node.kind, getValue(node)]); 1069 if (node.kind === 'Name' && node.value === 'b') { 1070 return BREAK; 1071 } 1072 }, 1073 leave(node) { 1074 checkVisitorFnArgs(ast, arguments); 1075 visited.push(['break-b', 'leave', node.kind, getValue(node)]); 1076 }, 1077 }, 1078 ]) 1079 ); 1080 1081 expect(visited).toEqual([ 1082 ['break-a', 'enter', 'Document', undefined], 1083 ['break-b', 'enter', 'Document', undefined], 1084 ['break-a', 'enter', 'OperationDefinition', undefined], 1085 ['break-b', 'enter', 'OperationDefinition', undefined], 1086 ['break-a', 'enter', 'SelectionSet', undefined], 1087 ['break-b', 'enter', 'SelectionSet', undefined], 1088 ['break-a', 'enter', 'Field', undefined], 1089 ['break-b', 'enter', 'Field', undefined], 1090 ['break-a', 'enter', 'Name', 'a'], 1091 ['break-b', 'enter', 'Name', 'a'], 1092 ['break-b', 'leave', 'Name', 'a'], 1093 ['break-b', 'enter', 'SelectionSet', undefined], 1094 ['break-b', 'enter', 'Field', undefined], 1095 ['break-b', 'enter', 'Name', 'y'], 1096 ['break-b', 'leave', 'Name', 'y'], 1097 ['break-b', 'leave', 'Field', undefined], 1098 ['break-b', 'leave', 'SelectionSet', undefined], 1099 ['break-b', 'leave', 'Field', undefined], 1100 ['break-b', 'enter', 'Field', undefined], 1101 ['break-b', 'enter', 'Name', 'b'], 1102 ]); 1103 }); 1104 1105 // Note: nearly identical to the above test of the same test but 1106 // using visitInParallel. 1107 it('allows early exit while leaving', () => { 1108 const visited = []; 1109 1110 const ast = parse('{ a, b { x }, c }'); 1111 visit( 1112 ast, 1113 visitInParallel([ 1114 { 1115 enter(node) { 1116 checkVisitorFnArgs(ast, arguments); 1117 visited.push(['enter', node.kind, getValue(node)]); 1118 }, 1119 leave(node) { 1120 checkVisitorFnArgs(ast, arguments); 1121 visited.push(['leave', node.kind, getValue(node)]); 1122 if (node.kind === 'Name' && node.value === 'x') { 1123 return BREAK; 1124 } 1125 }, 1126 }, 1127 ]) 1128 ); 1129 1130 expect(visited).toEqual([ 1131 ['enter', 'Document', undefined], 1132 ['enter', 'OperationDefinition', undefined], 1133 ['enter', 'SelectionSet', undefined], 1134 ['enter', 'Field', undefined], 1135 ['enter', 'Name', 'a'], 1136 ['leave', 'Name', 'a'], 1137 ['leave', 'Field', undefined], 1138 ['enter', 'Field', undefined], 1139 ['enter', 'Name', 'b'], 1140 ['leave', 'Name', 'b'], 1141 ['enter', 'SelectionSet', undefined], 1142 ['enter', 'Field', undefined], 1143 ['enter', 'Name', 'x'], 1144 ['leave', 'Name', 'x'], 1145 ]); 1146 }); 1147 1148 it('allows early exit from leaving different points', () => { 1149 const visited = []; 1150 1151 const ast = parse('{ a { y }, b { x } }'); 1152 visit( 1153 ast, 1154 visitInParallel([ 1155 { 1156 enter(node) { 1157 checkVisitorFnArgs(ast, arguments); 1158 visited.push(['break-a', 'enter', node.kind, getValue(node)]); 1159 }, 1160 leave(node) { 1161 checkVisitorFnArgs(ast, arguments); 1162 visited.push(['break-a', 'leave', node.kind, getValue(node)]); 1163 if (node.kind === 'Field' && node.name.value === 'a') { 1164 return BREAK; 1165 } 1166 }, 1167 }, 1168 { 1169 enter(node) { 1170 checkVisitorFnArgs(ast, arguments); 1171 visited.push(['break-b', 'enter', node.kind, getValue(node)]); 1172 }, 1173 leave(node) { 1174 checkVisitorFnArgs(ast, arguments); 1175 visited.push(['break-b', 'leave', node.kind, getValue(node)]); 1176 if (node.kind === 'Field' && node.name.value === 'b') { 1177 return BREAK; 1178 } 1179 }, 1180 }, 1181 ]) 1182 ); 1183 1184 expect(visited).toEqual([ 1185 ['break-a', 'enter', 'Document', undefined], 1186 ['break-b', 'enter', 'Document', undefined], 1187 ['break-a', 'enter', 'OperationDefinition', undefined], 1188 ['break-b', 'enter', 'OperationDefinition', undefined], 1189 ['break-a', 'enter', 'SelectionSet', undefined], 1190 ['break-b', 'enter', 'SelectionSet', undefined], 1191 ['break-a', 'enter', 'Field', undefined], 1192 ['break-b', 'enter', 'Field', undefined], 1193 ['break-a', 'enter', 'Name', 'a'], 1194 ['break-b', 'enter', 'Name', 'a'], 1195 ['break-a', 'leave', 'Name', 'a'], 1196 ['break-b', 'leave', 'Name', 'a'], 1197 ['break-a', 'enter', 'SelectionSet', undefined], 1198 ['break-b', 'enter', 'SelectionSet', undefined], 1199 ['break-a', 'enter', 'Field', undefined], 1200 ['break-b', 'enter', 'Field', undefined], 1201 ['break-a', 'enter', 'Name', 'y'], 1202 ['break-b', 'enter', 'Name', 'y'], 1203 ['break-a', 'leave', 'Name', 'y'], 1204 ['break-b', 'leave', 'Name', 'y'], 1205 ['break-a', 'leave', 'Field', undefined], 1206 ['break-b', 'leave', 'Field', undefined], 1207 ['break-a', 'leave', 'SelectionSet', undefined], 1208 ['break-b', 'leave', 'SelectionSet', undefined], 1209 ['break-a', 'leave', 'Field', undefined], 1210 ['break-b', 'leave', 'Field', undefined], 1211 ['break-b', 'enter', 'Field', undefined], 1212 ['break-b', 'enter', 'Name', 'b'], 1213 ['break-b', 'leave', 'Name', 'b'], 1214 ['break-b', 'enter', 'SelectionSet', undefined], 1215 ['break-b', 'enter', 'Field', undefined], 1216 ['break-b', 'enter', 'Name', 'x'], 1217 ['break-b', 'leave', 'Name', 'x'], 1218 ['break-b', 'leave', 'Field', undefined], 1219 ['break-b', 'leave', 'SelectionSet', undefined], 1220 ['break-b', 'leave', 'Field', undefined], 1221 ]); 1222 }); 1223 1224 it('allows for editing on enter', () => { 1225 const visited = []; 1226 1227 const ast = parse('{ a, b, c { a, b, c } }', { noLocation: true }); 1228 const editedAST = visit( 1229 ast, 1230 visitInParallel([ 1231 { 1232 enter(node) { 1233 checkVisitorFnArgs(ast, arguments); 1234 if (node.kind === 'Field' && node.name.value === 'b') { 1235 return null; 1236 } 1237 }, 1238 }, 1239 { 1240 enter(node) { 1241 checkVisitorFnArgs(ast, arguments); 1242 visited.push(['enter', node.kind, getValue(node)]); 1243 }, 1244 leave(node) { 1245 checkVisitorFnArgs(ast, arguments, /* isEdited */ true); 1246 visited.push(['leave', node.kind, getValue(node)]); 1247 }, 1248 }, 1249 ]) 1250 ); 1251 1252 expect(ast).toEqual(parse('{ a, b, c { a, b, c } }', { noLocation: true })); 1253 1254 expect(editedAST).toEqual(parse('{ a, c { a, c } }', { noLocation: true })); 1255 1256 expect(visited).toEqual([ 1257 ['enter', 'Document', undefined], 1258 ['enter', 'OperationDefinition', undefined], 1259 ['enter', 'SelectionSet', undefined], 1260 ['enter', 'Field', undefined], 1261 ['enter', 'Name', 'a'], 1262 ['leave', 'Name', 'a'], 1263 ['leave', 'Field', undefined], 1264 ['enter', 'Field', undefined], 1265 ['enter', 'Name', 'c'], 1266 ['leave', 'Name', 'c'], 1267 ['enter', 'SelectionSet', undefined], 1268 ['enter', 'Field', undefined], 1269 ['enter', 'Name', 'a'], 1270 ['leave', 'Name', 'a'], 1271 ['leave', 'Field', undefined], 1272 ['enter', 'Field', undefined], 1273 ['enter', 'Name', 'c'], 1274 ['leave', 'Name', 'c'], 1275 ['leave', 'Field', undefined], 1276 ['leave', 'SelectionSet', undefined], 1277 ['leave', 'Field', undefined], 1278 ['leave', 'SelectionSet', undefined], 1279 ['leave', 'OperationDefinition', undefined], 1280 ['leave', 'Document', undefined], 1281 ]); 1282 }); 1283 1284 it('allows for editing on leave', () => { 1285 const visited = []; 1286 1287 const ast = parse('{ a, b, c { a, b, c } }', { noLocation: true }); 1288 const editedAST = visit( 1289 ast, 1290 visitInParallel([ 1291 { 1292 leave(node) { 1293 checkVisitorFnArgs(ast, arguments, /* isEdited */ true); 1294 if (node.kind === 'Field' && node.name.value === 'b') { 1295 return null; 1296 } 1297 }, 1298 }, 1299 { 1300 enter(node) { 1301 checkVisitorFnArgs(ast, arguments); 1302 visited.push(['enter', node.kind, getValue(node)]); 1303 }, 1304 leave(node) { 1305 checkVisitorFnArgs(ast, arguments, /* isEdited */ true); 1306 visited.push(['leave', node.kind, getValue(node)]); 1307 }, 1308 }, 1309 ]) 1310 ); 1311 1312 expect(ast).toEqual(parse('{ a, b, c { a, b, c } }', { noLocation: true })); 1313 1314 expect(editedAST).toEqual(parse('{ a, c { a, c } }', { noLocation: true })); 1315 1316 expect(visited).toEqual([ 1317 ['enter', 'Document', undefined], 1318 ['enter', 'OperationDefinition', undefined], 1319 ['enter', 'SelectionSet', undefined], 1320 ['enter', 'Field', undefined], 1321 ['enter', 'Name', 'a'], 1322 ['leave', 'Name', 'a'], 1323 ['leave', 'Field', undefined], 1324 ['enter', 'Field', undefined], 1325 ['enter', 'Name', 'b'], 1326 ['leave', 'Name', 'b'], 1327 ['enter', 'Field', undefined], 1328 ['enter', 'Name', 'c'], 1329 ['leave', 'Name', 'c'], 1330 ['enter', 'SelectionSet', undefined], 1331 ['enter', 'Field', undefined], 1332 ['enter', 'Name', 'a'], 1333 ['leave', 'Name', 'a'], 1334 ['leave', 'Field', undefined], 1335 ['enter', 'Field', undefined], 1336 ['enter', 'Name', 'b'], 1337 ['leave', 'Name', 'b'], 1338 ['enter', 'Field', undefined], 1339 ['enter', 'Name', 'c'], 1340 ['leave', 'Name', 'c'], 1341 ['leave', 'Field', undefined], 1342 ['leave', 'SelectionSet', undefined], 1343 ['leave', 'Field', undefined], 1344 ['leave', 'SelectionSet', undefined], 1345 ['leave', 'OperationDefinition', undefined], 1346 ['leave', 'Document', undefined], 1347 ]); 1348 }); 1349 }); 1350});