|
4 | 4 | * This source code is licensed under the MIT license found in the
|
5 | 5 | * LICENSE file in the root directory of this source tree.
|
6 | 6 | *
|
7 |
| - * @noflow |
| 7 | + * @flow strict |
8 | 8 | */
|
9 | 9 |
|
10 | 10 | import { describe, it } from 'mocha';
|
11 | 11 | import { expect } from 'chai';
|
12 | 12 | import dedent from '../../jsutils/dedent';
|
| 13 | +import invariant from '../../jsutils/invariant'; |
13 | 14 | import { buildClientSchema } from '../buildClientSchema';
|
14 | 15 | import { introspectionFromSchema } from '../introspectionFromSchema';
|
15 | 16 | import {
|
16 | 17 | buildSchema,
|
17 | 18 | printSchema,
|
18 | 19 | graphqlSync,
|
| 20 | + isEnumType, |
19 | 21 | GraphQLSchema,
|
20 | 22 | GraphQLObjectType,
|
21 | 23 | GraphQLEnumType,
|
@@ -323,7 +325,7 @@ describe('Type System: build schema from introspection', () => {
|
323 | 325 | const clientFoodEnum = clientSchema.getType('Food');
|
324 | 326 |
|
325 | 327 | // It's also an Enum type on the client.
|
326 |
| - expect(clientFoodEnum).to.be.an.instanceOf(GraphQLEnumType); |
| 328 | + invariant(isEnumType(clientFoodEnum)); |
327 | 329 |
|
328 | 330 | // Client types do not get server-only values, so `value` mirrors `name`,
|
329 | 331 | // rather than using the integers defined in the "server" schema.
|
@@ -430,31 +432,21 @@ describe('Type System: build schema from introspection', () => {
|
430 | 432 | });
|
431 | 433 |
|
432 | 434 | it('builds a schema with legacy names', () => {
|
433 |
| - const introspection = { |
434 |
| - __schema: { |
435 |
| - queryType: { |
436 |
| - name: 'Query', |
437 |
| - }, |
438 |
| - types: [ |
439 |
| - { |
440 |
| - name: 'Query', |
441 |
| - kind: 'OBJECT', |
442 |
| - fields: [ |
443 |
| - { |
444 |
| - name: '__badName', |
445 |
| - args: [], |
446 |
| - type: { name: 'String' }, |
447 |
| - }, |
448 |
| - ], |
449 |
| - interfaces: [], |
450 |
| - }, |
451 |
| - ], |
452 |
| - }, |
453 |
| - }; |
454 |
| - const schema = buildClientSchema(introspection, { |
455 |
| - allowedLegacyNames: ['__badName'], |
| 435 | + const sdl = dedent` |
| 436 | + type Query { |
| 437 | + __badName: String |
| 438 | + } |
| 439 | + `; |
| 440 | + const allowedLegacyNames = ['__badName']; |
| 441 | + const schema = buildSchema(sdl, { allowedLegacyNames }); |
| 442 | + |
| 443 | + const introspection = introspectionFromSchema(schema); |
| 444 | + const clientSchema = buildClientSchema(introspection, { |
| 445 | + allowedLegacyNames, |
456 | 446 | });
|
| 447 | + |
457 | 448 | expect(schema.__allowedLegacyNames).to.deep.equal(['__badName']);
|
| 449 | + expect(printSchema(clientSchema)).to.equal(sdl); |
458 | 450 | });
|
459 | 451 |
|
460 | 452 | it('builds a schema aware of deprecation', () => {
|
@@ -510,73 +502,74 @@ describe('Type System: build schema from introspection', () => {
|
510 | 502 | });
|
511 | 503 |
|
512 | 504 | describe('throws when given incomplete introspection', () => {
|
| 505 | + const dummySchema = buildSchema(` |
| 506 | + type Query { |
| 507 | + foo: String |
| 508 | + } |
| 509 | +
|
| 510 | + directive @Foo on QUERY |
| 511 | + `); |
| 512 | + |
513 | 513 | it('throws when given empty types', () => {
|
514 |
| - const incompleteIntrospection = { |
515 |
| - __schema: { |
516 |
| - queryType: { name: 'QueryType' }, |
517 |
| - types: [], |
518 |
| - }, |
519 |
| - }; |
| 514 | + const introspection = introspectionFromSchema(dummySchema); |
| 515 | + |
| 516 | + // $DisableFlowOnNegativeTest |
| 517 | + introspection.__schema.types = []; |
520 | 518 |
|
521 |
| - expect(() => buildClientSchema(incompleteIntrospection)).to.throw( |
522 |
| - 'Invalid or incomplete schema, unknown type: QueryType. Ensure ' + |
523 |
| - 'that a full introspection query is used in order to build a ' + |
524 |
| - 'client schema.', |
| 519 | + expect(() => buildClientSchema(introspection)).to.throw( |
| 520 | + 'Invalid or incomplete schema, unknown type: Query. Ensure that a ' + |
| 521 | + 'full introspection query is used in order to build a client schema.', |
525 | 522 | );
|
526 | 523 | });
|
527 | 524 |
|
528 | 525 | it('throws when missing kind', () => {
|
529 |
| - const incompleteIntrospection = { |
530 |
| - __schema: { |
531 |
| - queryType: { name: 'QueryType' }, |
532 |
| - types: [{ name: 'QueryType' }], |
533 |
| - }, |
534 |
| - }; |
| 526 | + const introspection = introspectionFromSchema(dummySchema); |
| 527 | + |
| 528 | + const queryTypeIntrospection = introspection.__schema.types[0]; |
| 529 | + expect(queryTypeIntrospection).to.deep.include({ |
| 530 | + name: 'Query', |
| 531 | + kind: 'OBJECT', |
| 532 | + }); |
| 533 | + // $DisableFlowOnNegativeTest |
| 534 | + delete queryTypeIntrospection.kind; |
535 | 535 |
|
536 |
| - expect(() => buildClientSchema(incompleteIntrospection)).to.throw( |
| 536 | + expect(() => buildClientSchema(introspection)).to.throw( |
537 | 537 | 'Invalid or incomplete introspection result. Ensure that a full ' +
|
538 | 538 | 'introspection query is used in order to build a client schema',
|
539 | 539 | );
|
540 | 540 | });
|
541 | 541 |
|
542 | 542 | it('throws when missing interfaces', () => {
|
543 |
| - const nullInterfaceIntrospection = { |
544 |
| - __schema: { |
545 |
| - queryType: { name: 'QueryType' }, |
546 |
| - types: [ |
547 |
| - { |
548 |
| - kind: 'OBJECT', |
549 |
| - name: 'QueryType', |
550 |
| - fields: [ |
551 |
| - { |
552 |
| - name: 'aString', |
553 |
| - args: [], |
554 |
| - type: { kind: 'SCALAR', name: 'String', ofType: null }, |
555 |
| - isDeprecated: false, |
556 |
| - }, |
557 |
| - ], |
558 |
| - }, |
559 |
| - ], |
560 |
| - }, |
561 |
| - }; |
| 543 | + const introspection = introspectionFromSchema(dummySchema); |
562 | 544 |
|
563 |
| - expect(() => buildClientSchema(nullInterfaceIntrospection)).to.throw( |
| 545 | + const queryTypeIntrospection = introspection.__schema.types[0]; |
| 546 | + expect(queryTypeIntrospection).to.deep.include({ |
| 547 | + name: 'Query', |
| 548 | + interfaces: [], |
| 549 | + }); |
| 550 | + // $DisableFlowOnNegativeTest |
| 551 | + delete queryTypeIntrospection.interfaces; |
| 552 | + |
| 553 | + expect(() => buildClientSchema(introspection)).to.throw( |
564 | 554 | 'Introspection result missing interfaces: ' +
|
565 |
| - '{ kind: "OBJECT", name: "QueryType", fields: [{ name: "aString", args: [], type: { kind: "SCALAR", name: "String", ofType: null }, isDeprecated: false }] }', |
| 555 | + '{ kind: "OBJECT", name: "Query", description: null, fields: [{ name: "foo", description: null, args: [], type: { kind: "SCALAR", name: "String", ofType: null }, isDeprecated: false, deprecationReason: null }], inputFields: null, enumValues: null, possibleTypes: null }', |
566 | 556 | );
|
567 | 557 | });
|
568 | 558 |
|
569 | 559 | it('throws when missing directive locations', () => {
|
570 |
| - const introspection = { |
571 |
| - __schema: { |
572 |
| - types: [], |
573 |
| - directives: [{ name: 'test', args: [] }], |
574 |
| - }, |
575 |
| - }; |
| 560 | + const introspection = introspectionFromSchema(dummySchema); |
| 561 | + |
| 562 | + const fooDirectiveIntrospection = introspection.__schema.directives[0]; |
| 563 | + expect(fooDirectiveIntrospection).to.deep.include({ |
| 564 | + name: 'Foo', |
| 565 | + locations: ['QUERY'], |
| 566 | + }); |
| 567 | + // $DisableFlowOnNegativeTest |
| 568 | + delete fooDirectiveIntrospection.locations; |
576 | 569 |
|
577 | 570 | expect(() => buildClientSchema(introspection)).to.throw(
|
578 | 571 | 'Introspection result missing directive locations: ' +
|
579 |
| - '{ name: "test", args: [] }', |
| 572 | + '{ name: "Foo", description: null, args: [] }', |
580 | 573 | );
|
581 | 574 | });
|
582 | 575 | });
|
@@ -622,35 +615,44 @@ describe('Type System: build schema from introspection', () => {
|
622 | 615 |
|
623 | 616 | describe('prevents infinite recursion on invalid introspection', () => {
|
624 | 617 | it('recursive interfaces', () => {
|
625 |
| - const introspection = { |
626 |
| - __schema: { |
627 |
| - types: [ |
628 |
| - { |
629 |
| - name: 'Foo', |
630 |
| - kind: 'OBJECT', |
631 |
| - fields: [], |
632 |
| - interfaces: [{ name: 'Foo' }], |
633 |
| - }, |
634 |
| - ], |
635 |
| - }, |
636 |
| - }; |
| 618 | + const sdl = ` |
| 619 | + type Query { |
| 620 | + foo: Foo |
| 621 | + } |
| 622 | +
|
| 623 | + type Foo implements Foo { |
| 624 | + foo: String |
| 625 | + } |
| 626 | + `; |
| 627 | + const schema = buildSchema(sdl, { assumeValid: true }); |
| 628 | + const introspection = introspectionFromSchema(schema); |
| 629 | + |
| 630 | + expect(introspection.__schema.types[1]).to.deep.include({ |
| 631 | + name: 'Foo', |
| 632 | + interfaces: [{ kind: 'OBJECT', name: 'Foo', ofType: null }], |
| 633 | + }); |
| 634 | + |
637 | 635 | expect(() => buildClientSchema(introspection)).to.throw(
|
638 | 636 | 'Expected Foo to be a GraphQL Interface type.',
|
639 | 637 | );
|
640 | 638 | });
|
641 | 639 |
|
642 | 640 | it('recursive union', () => {
|
643 |
| - const introspection = { |
644 |
| - __schema: { |
645 |
| - types: [ |
646 |
| - { |
647 |
| - name: 'Foo', |
648 |
| - kind: 'UNION', |
649 |
| - possibleTypes: [{ name: 'Foo' }], |
650 |
| - }, |
651 |
| - ], |
652 |
| - }, |
653 |
| - }; |
| 641 | + const sdl = ` |
| 642 | + type Query { |
| 643 | + foo: Foo |
| 644 | + } |
| 645 | +
|
| 646 | + union Foo = Foo |
| 647 | + `; |
| 648 | + const schema = buildSchema(sdl, { assumeValid: true }); |
| 649 | + const introspection = introspectionFromSchema(schema); |
| 650 | + |
| 651 | + expect(introspection.__schema.types[1]).to.deep.include({ |
| 652 | + name: 'Foo', |
| 653 | + possibleTypes: [{ kind: 'UNION', name: 'Foo', ofType: null }], |
| 654 | + }); |
| 655 | + |
654 | 656 | expect(() => buildClientSchema(introspection)).to.throw(
|
655 | 657 | 'Expected Foo to be a GraphQL Object type.',
|
656 | 658 | );
|
|
0 commit comments