github.com/99designs/gqlgen@v0.17.45/integration/src/__test__/integration.spec.ts (about)

     1  import { afterAll, describe, expect, it } from "vitest";
     2  
     3  import fetch from 'cross-fetch';
     4  import {
     5      ApolloClient,
     6      ApolloLink,
     7      FetchResult,
     8      InMemoryCache,
     9      NormalizedCacheObject,
    10      Observable,
    11      Operation,
    12  } from '@apollo/client/core';
    13  import {HttpLink} from '@apollo/client/link/http';
    14  
    15  import { print } from "graphql";
    16  import { GraphQLWsLink } from "@apollo/client/link/subscriptions";
    17  import { WebSocket } from "ws";
    18  import { createClient as createClientWS } from "graphql-ws";
    19  import {
    20      Client as ClientSSE,
    21      ClientOptions as ClientOptionsSSE,
    22      createClient as createClientSSE,
    23  } from "graphql-sse";
    24  import {
    25      CoercionDocument,
    26      ComplexityDocument,
    27      DateDocument,
    28      ErrorDocument,
    29      ErrorType,
    30      JsonEncodingDocument,
    31      PathDocument,
    32      UserFragmentFragmentDoc,
    33      ViewerDocument,
    34  } from "../generated/graphql.ts";
    35  import {
    36      cacheExchange,
    37      Client,
    38      dedupExchange,
    39      subscriptionExchange,
    40  } from "urql";
    41  import { isFragmentReady, useFragment } from "../generated";
    42  import { readFileSync } from "fs";
    43  import { join } from "path";
    44  
    45  const uri = process.env.VITE_SERVER_URL || "http://localhost:8080/query";
    46  
    47  function test(client: ApolloClient<NormalizedCacheObject>) {
    48      describe("Json", () => {
    49          it("should follow json escaping rules", async () => {
    50              const res = await client.query({
    51                  query: JsonEncodingDocument,
    52              });
    53  
    54              expect(res.data.jsonEncoding).toBe("󾓭");
    55              expect(res.errors).toBe(undefined);
    56  
    57              return null;
    58          });
    59      });
    60  
    61      describe("Input defaults", () => {
    62          it("should pass default values to resolver", async () => {
    63              const res = await client.query({
    64                  query: DateDocument,
    65                  variables: {
    66                      filter: {
    67                          value: "asdf",
    68                      },
    69                  },
    70              });
    71  
    72              expect(res.data.date).toBe(true);
    73              expect(res.errors).toBe(undefined);
    74              return null;
    75          });
    76      });
    77  
    78      describe("Complexity", () => {
    79          it("should fail when complexity is too high", async () => {
    80              const res = await client.query({
    81                  query: ComplexityDocument,
    82                  variables: {
    83                      value: 2000,
    84                  },
    85              });
    86  
    87              expect(res.errors).toBeDefined();
    88              if (res.errors) {
    89                  expect(res.errors[0].message).toBe(
    90                      "operation has complexity 2000, which exceeds the limit of 1000"
    91                  );
    92              }
    93              return null;
    94          });
    95  
    96          it("should succeed when complexity is not too high", async () => {
    97              const res = await client.query({
    98                  query: ComplexityDocument,
    99                  variables: {
   100                      value: 1000,
   101                  },
   102              });
   103  
   104              expect(res.data.complexity).toBe(true);
   105              expect(res.errors).toBe(undefined);
   106              return null;
   107          });
   108      });
   109  
   110      describe("List Coercion", () => {
   111          it("should succeed when nested single values are passed", async () => {
   112              const res = await client.query({
   113                  query: CoercionDocument,
   114                  variables: {
   115                      value: {
   116                          enumVal: ErrorType.Custom,
   117                          strVal: "test",
   118                          intVal: 1,
   119                      },
   120                  },
   121              });
   122  
   123              expect(res.data.coercion).toBe(true);
   124              return null;
   125          });
   126  
   127          it("should succeed when nested array of values are passed", async () => {
   128              const res = await client.query({
   129                  query: CoercionDocument,
   130                  variables: {
   131                      value: {
   132                          enumVal: [ErrorType.Custom],
   133                          strVal: ["test"],
   134                          intVal: [1],
   135                      },
   136                  },
   137              });
   138  
   139              expect(res.data.coercion).toBe(true);
   140              return null;
   141          });
   142  
   143          it("should succeed when single value is passed", async () => {
   144              const res = await client.query({
   145                  query: CoercionDocument,
   146                  variables: {
   147                      value: {
   148                          enumVal: ErrorType.Custom,
   149                      },
   150                  },
   151              });
   152  
   153              expect(res.data.coercion).toBe(true);
   154              return null;
   155          });
   156  
   157          it("should succeed when single scalar value is passed", async () => {
   158              const res = await client.query({
   159                  query: CoercionDocument,
   160                  variables: {
   161                      value: [
   162                          {
   163                              scalarVal: {
   164                                  key: "someValue",
   165                              },
   166                          },
   167                      ],
   168                  },
   169              });
   170  
   171              expect(res.data.coercion).toBe(true);
   172              return null;
   173          });
   174  
   175          it("should succeed when multiple values are passed", async () => {
   176              const res = await client.query({
   177                  query: CoercionDocument,
   178                  variables: {
   179                      value: [
   180                          {
   181                              enumVal: [ErrorType.Custom, ErrorType.Normal],
   182                          },
   183                      ],
   184                  },
   185              });
   186  
   187              expect(res.data.coercion).toBe(true);
   188              return null;
   189          });
   190      });
   191  
   192      describe("Errors", () => {
   193          it("should respond with correct paths", async () => {
   194              const res = await client.query({
   195                  query: PathDocument,
   196              });
   197  
   198              expect(res.errors).toBeDefined();
   199              if (res.errors) {
   200                  expect(res.errors[0].path).toEqual(["path", 0, "cc", "error"]);
   201                  expect(res.errors[1].path).toEqual(["path", 1, "cc", "error"]);
   202                  expect(res.errors[2].path).toEqual(["path", 2, "cc", "error"]);
   203                  expect(res.errors[3].path).toEqual(["path", 3, "cc", "error"]);
   204              }
   205              return null;
   206          });
   207  
   208          it("should use the error presenter for custom errors", async () => {
   209              let res = await client.query({
   210                  query: ErrorDocument,
   211                  variables: {
   212                      type: ErrorType.Custom,
   213                  },
   214              });
   215  
   216              expect(res.errors).toBeDefined();
   217              if (res.errors) {
   218                  expect(res.errors[0].message).toEqual("User message");
   219              }
   220              return null;
   221          });
   222  
   223          it("should pass through for other errors", async () => {
   224              const res = await client.query({
   225                  query: ErrorDocument,
   226                  variables: {
   227                      type: ErrorType.Normal,
   228                  },
   229              });
   230  
   231              expect(res.errors).toBeDefined();
   232              if (res.errors) {
   233                  expect(res.errors[0]?.message).toEqual("normal error");
   234              }
   235              return null;
   236          });
   237      });
   238  }
   239  
   240  describe("HTTP client", () => {
   241      const client = new ApolloClient({
   242          uri: uri,
   243          cache: new InMemoryCache(),
   244          defaultOptions: {
   245              watchQuery: {
   246                  fetchPolicy: "network-only",
   247                  errorPolicy: "ignore",
   248              },
   249              query: {
   250                  fetchPolicy: "network-only",
   251                  errorPolicy: "all",
   252              },
   253          },
   254      });
   255  
   256      test(client);
   257  
   258      afterAll(() => {
   259          client.stop();
   260      });
   261  });
   262  
   263  describe("Schema Introspection", () => {
   264      const schemaJson = readFileSync(
   265          join(__dirname, "../generated/schema-introspection.json"),
   266          "utf-8"
   267      );
   268      const schema = JSON.parse(schemaJson);
   269  
   270      it("User.phoneNumber is deprecated and deprecationReason has the default value: `No longer supported`", async () => {
   271          const userType = schema.__schema.types.find(
   272              (type: any) => type.name === "User"
   273          );
   274  
   275          expect(userType).toBeDefined();
   276  
   277          const phoneNumberField = userType.fields.find(
   278              (field: any) => field.name === "phoneNumber"
   279          );
   280          expect(phoneNumberField).toBeDefined();
   281  
   282          expect(phoneNumberField.isDeprecated).toBe(true);
   283          expect(phoneNumberField.deprecationReason).toBe("No longer supported");
   284      });
   285  });
   286  
   287  describe("Websocket client", () => {
   288      const client = new ApolloClient({
   289          link: new GraphQLWsLink(
   290              createClientWS({
   291                  url: uri
   292                      .replace("http://", "ws://")
   293                      .replace("https://", "wss://"),
   294                  webSocketImpl: WebSocket,
   295              })
   296          ),
   297          cache: new InMemoryCache(),
   298          defaultOptions: {
   299              watchQuery: {
   300                  fetchPolicy: "network-only",
   301                  errorPolicy: "ignore",
   302              },
   303              query: {
   304                  fetchPolicy: "network-only",
   305                  errorPolicy: "all",
   306              },
   307          },
   308      });
   309  
   310      test(client);
   311  
   312      afterAll(() => {
   313          client.stop();
   314      });
   315  });
   316  
   317  describe("SSE client", () => {
   318      class SSELink extends ApolloLink {
   319          private client: ClientSSE;
   320  
   321          constructor(options: ClientOptionsSSE) {
   322              super();
   323              this.client = createClientSSE(options);
   324          }
   325  
   326          public request(operation: Operation): Observable<FetchResult> {
   327              return new Observable((sink) => {
   328                  return this.client.subscribe<FetchResult>(
   329                      { ...operation, query: print(operation.query) },
   330                      {
   331                          next: sink.next.bind(sink),
   332                          complete: sink.complete.bind(sink),
   333                          error: sink.error.bind(sink),
   334                      }
   335                  );
   336              });
   337          }
   338      }
   339  
   340      const client = new ApolloClient({
   341          link: new SSELink({
   342              url: uri,
   343          }),
   344          cache: new InMemoryCache(),
   345          defaultOptions: {
   346              watchQuery: {
   347                  fetchPolicy: "network-only",
   348                  errorPolicy: "ignore",
   349              },
   350              query: {
   351                  fetchPolicy: "network-only",
   352                  errorPolicy: "all",
   353              },
   354          },
   355      });
   356  
   357      test(client);
   358  
   359      afterAll(() => {
   360          client.stop();
   361      });
   362  });
   363  
   364  describe("URQL SSE client", () => {
   365      const wsClient = createClientWS({
   366          url: uri.replace("http://", "ws://").replace("https://", "wss://"),
   367          webSocketImpl: WebSocket,
   368      });
   369  
   370      const client = new Client({
   371          url: uri,
   372          exchanges: [
   373              dedupExchange,
   374              cacheExchange,
   375              subscriptionExchange({
   376                  enableAllOperations: true,
   377                  forwardSubscription(request) {
   378                      const input = { ...request, query: request.query || "" };
   379                      return {
   380                          subscribe(sink) {
   381                              const unsubscribe = wsClient.subscribe(input, sink);
   382                              return { unsubscribe };
   383                          },
   384                      };
   385                  },
   386              }),
   387          ],
   388      });
   389  
   390      describe("Defer", () => {
   391          it("test using defer", async () => {
   392              const res = await client.query(ViewerDocument, {});
   393  
   394              expect(res.error).toBe(undefined);
   395              expect(res.data).toBeDefined();
   396              expect(res.data?.viewer).toBeDefined();
   397              expect(res.data?.viewer?.user).toBeDefined();
   398              expect(res.data?.viewer?.user?.name).toBe("Bob");
   399              expect(res.data?.viewer?.user?.query?.jsonEncoding).toBe("󾓭");
   400              let ready: boolean;
   401              if (
   402                  (ready = isFragmentReady(
   403                      ViewerDocument,
   404                      UserFragmentFragmentDoc,
   405                      res.data?.viewer?.user
   406                  ))
   407              ) {
   408                  const userFragment = useFragment(
   409                      UserFragmentFragmentDoc,
   410                      res.data?.viewer?.user
   411                  );
   412                  expect(userFragment).toBeDefined();
   413                  expect(userFragment?.likes).toStrictEqual(["Alice"]);
   414              }
   415              expect(ready).toBeTruthy();
   416              return null;
   417          });
   418      });
   419  });