github.com/geneva/gqlgen@v0.17.7-0.20230801155730-7b9317164836/integration/src/__test__/integration.spec.ts (about)

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