1/// <reference types="cypress" />
2
3import * as React from 'react';
4import { mount } from '@cypress/react';
5import { Provider, createClient, useQuery, debugExchange } from 'urql';
6import { executeExchange } from '@urql/exchange-execute';
7import { buildSchema, introspectionFromSchema } from 'graphql';
8
9import { cacheExchange } from '../src';
10
11const schema = buildSchema(`
12 type Query {
13 movie: Movie
14 }
15
16 type Movie {
17 id: String
18 title: String
19 metadata: Metadata
20 }
21
22 type Metadata {
23 uri: String
24 }
25`);
26
27const rootValue = {
28 movie: async () => {
29 await new Promise(resolve => setTimeout(resolve, 50));
30 return {
31 id: 'foo',
32 title: 'title',
33 metadata: () => {
34 throw new Error('Test');
35 },
36 };
37 },
38};
39
40describe('Graphcache Queries', () => {
41 it('should not loop with no schema present', () => {
42 const client = createClient({
43 url: 'https://trygql.formidable.dev/graphql/basic-pokedex',
44 exchanges: [
45 cacheExchange({}),
46 debugExchange,
47 executeExchange({ schema, rootValue }),
48 ],
49 });
50
51 const FirstComponent = () => {
52 const [{ fetching, error }] = useQuery({
53 query: `{
54 movie {
55 id
56 title
57 metadata {
58 uri
59 }
60 }
61 }`,
62 });
63
64 return (
65 <div>
66 {fetching === true ? (
67 'loading'
68 ) : (
69 <div>
70 <div>First Component</div>
71 <div id="first-error">{`Error: ${error?.message}`}</div>
72 </div>
73 )}
74 </div>
75 );
76 };
77
78 const SecondComponent = () => {
79 const [{ error, fetching }] = useQuery({
80 query: `{
81 movie {
82 id
83 metadata {
84 uri
85 }
86 }
87 }`,
88 });
89
90 if (fetching) {
91 return <div>Loading...</div>;
92 }
93
94 return (
95 <div>
96 <div>Second Component</div>
97 <div id="second-error">{`Error: ${error?.message}`}</div>
98 </div>
99 );
100 };
101
102 mount(
103 <Provider value={client}>
104 <FirstComponent />
105 <SecondComponent />
106 </Provider>
107 );
108
109 cy.get('#first-error').should('have.text', 'Error: [GraphQL] Test');
110 cy.get('#second-error').should('have.text', 'Error: [GraphQL] Test');
111 });
112
113 it('should not loop with schema present', () => {
114 const client = createClient({
115 url: 'https://trygql.formidable.dev/graphql/basic-pokedex',
116 exchanges: [
117 cacheExchange({ schema: introspectionFromSchema(schema) }),
118 debugExchange,
119 executeExchange({ schema, rootValue }),
120 ],
121 });
122
123 const FirstComponent = () => {
124 const [{ fetching, data, error, stale }] = useQuery({
125 query: `{
126 movie {
127 id
128 title
129 metadata {
130 uri
131 }
132 }
133 }`,
134 });
135
136 return (
137 <div>
138 {fetching === true ? (
139 'loading'
140 ) : (
141 <div>
142 <div>First Component</div>
143 <div id="first-data">{`Data: ${data.movie?.title}`}</div>
144 <div id="first-error">{`Error: ${error?.message}`}</div>
145 <div id="first-stale">{`Stale: ${!!stale}`}</div>
146 </div>
147 )}
148 </div>
149 );
150 };
151
152 const SecondComponent = () => {
153 const [{ error, data, fetching, stale }] = useQuery({
154 query: `{
155 movie {
156 id
157 metadata {
158 uri
159 }
160 }
161 }`,
162 });
163
164 if (fetching) {
165 return <div>Loading...</div>;
166 }
167
168 return (
169 <div>
170 <div>Second Component</div>
171 <div id="second-data">{`Data: ${data.movie.id}`}</div>
172 <div id="second-error">{`Error: ${error?.message}`}</div>
173 <div id="second-stale">{`Stale: ${!!stale}`}</div>
174 </div>
175 );
176 };
177
178 mount(
179 <Provider value={client}>
180 <FirstComponent />
181 <SecondComponent />
182 </Provider>
183 );
184
185 cy.get('#first-data').should('have.text', 'Data: title');
186 cy.get('#second-data').should('have.text', 'Data: foo');
187 cy.get('#second-stale').should('have.text', 'Stale: false');
188 // TODO: ideally we would be able to keep the error here but...
189 // cy.get('#first-error').should('have.text', 'Error: [GraphQL] Test');
190 // cy.get('#second-error').should('have.text', 'Error: [GraphQL] Test');
191 });
192});