1/* eslint-disable @typescript-eslint/no-var-requires */
2
3import { gql, CombinedError } from '@urql/core';
4import { minifyIntrospectionQuery } from '@urql/introspection';
5import { vi, expect, it, beforeEach, describe, beforeAll } from 'vitest';
6
7import { __initAnd_write as write } from './write';
8import * as InMemoryData from '../store/data';
9import { Store } from '../store/store';
10
11const TODO_QUERY = gql`
12 query todos {
13 todos {
14 id
15 text
16 complete
17 author {
18 id
19 name
20 known
21 __typename
22 }
23 __typename
24 }
25 }
26`;
27
28describe('Query', () => {
29 let schema, store;
30
31 beforeAll(() => {
32 schema = minifyIntrospectionQuery(
33 require('../test-utils/simple_schema.json')
34 );
35 });
36
37 beforeEach(() => {
38 store = new Store({ schema });
39 write(
40 store,
41 { query: TODO_QUERY },
42 {
43 __typename: 'Query',
44 todos: [
45 { id: '0', text: 'Teach', __typename: 'Todo' },
46 { id: '1', text: 'Learn', __typename: 'Todo' },
47 ],
48 }
49 );
50
51 vi.clearAllMocks();
52 });
53
54 it('should not crash for valid writes', async () => {
55 const VALID_TODO_QUERY = gql`
56 mutation {
57 toggleTodo {
58 id
59 text
60 complete
61 }
62 }
63 `;
64 write(
65 store,
66 { query: VALID_TODO_QUERY },
67 {
68 __typename: 'Mutation',
69 toggleTodo: {
70 __typename: 'Todo',
71 id: '0',
72 text: 'Teach',
73 complete: true,
74 },
75 }
76 );
77 expect(console.warn).not.toHaveBeenCalled();
78 expect(console.error).not.toHaveBeenCalled();
79 });
80
81 it('should warn once for invalid fields on an entity', () => {
82 const INVALID_TODO_QUERY = gql`
83 mutation {
84 toggleTodo {
85 id
86 text
87 incomplete
88 }
89 }
90 `;
91 write(
92 store,
93 { query: INVALID_TODO_QUERY },
94 {
95 __typename: 'Mutation',
96 toggleTodo: {
97 __typename: 'Todo',
98 id: '0',
99 text: 'Teach',
100 incomplete: false,
101 },
102 }
103 );
104 expect(console.warn).toHaveBeenCalledTimes(1);
105 write(
106 store,
107 { query: INVALID_TODO_QUERY },
108 {
109 __typename: 'Mutation',
110 toggleTodo: {
111 __typename: 'Todo',
112 id: '0',
113 text: 'Teach',
114 incomplete: false,
115 },
116 }
117 );
118 expect(console.warn).toHaveBeenCalledTimes(1);
119 expect((console.warn as any).mock.calls[0][0]).toMatch(
120 /The field `incomplete` does not exist on `Todo`/
121 );
122 });
123
124 it('should warn once for invalid link fields on an entity', () => {
125 const INVALID_TODO_QUERY = gql`
126 mutation {
127 toggleTodo {
128 id
129 text
130 writer {
131 id
132 }
133 }
134 }
135 `;
136 write(
137 store,
138 { query: INVALID_TODO_QUERY },
139 {
140 __typename: 'Mutation',
141 toggleTodo: {
142 __typename: 'Todo',
143 id: '0',
144 text: 'Teach',
145 writer: {
146 id: '0',
147 },
148 },
149 }
150 );
151 // Because of us indicating Todo:Writer as a scalar
152 expect(console.warn).toHaveBeenCalledTimes(2);
153 expect((console.warn as any).mock.calls[0][0]).toMatch(
154 /The field `writer` does not exist on `Todo`/
155 );
156 });
157
158 it('should skip undefined values that are expected', () => {
159 const query = gql`
160 {
161 field
162 }
163 `;
164
165 // This should not overwrite the field
166 write(store, { query }, { field: undefined } as any);
167 // Because of us writing an undefined field
168 expect(console.warn).toHaveBeenCalledTimes(2);
169
170 expect((console.warn as any).mock.calls[1][0]).toMatch(
171 /Invalid undefined: The field at `field`/
172 );
173
174 write(store, { query }, { field: 'test' } as any);
175 write(store, { query }, { field: undefined } as any);
176 InMemoryData.initDataState('read', store.data, null);
177 // The field must still be `'test'`
178 expect(InMemoryData.readRecord('Query', 'field')).toBe('test');
179 });
180
181 it('should write errored records as undefined rather than null', () => {
182 const query = gql`
183 {
184 missingField
185 setField
186 }
187 `;
188
189 write(
190 store,
191 { query },
192 { missingField: null, setField: 'test' } as any,
193 new CombinedError({
194 graphQLErrors: [
195 {
196 message: 'Test',
197 path: ['missingField'],
198 },
199 ],
200 })
201 );
202
203 InMemoryData.initDataState('read', store.data, null);
204
205 // The setField must still be `'test'`
206 expect(InMemoryData.readRecord('Query', 'setField')).toBe('test');
207 // The missingField must still be `undefined`
208 expect(InMemoryData.readRecord('Query', 'missingField')).toBe(undefined);
209 });
210
211 it('should write errored links as undefined rather than null', () => {
212 const query = gql`
213 {
214 missingTodoItem: todos {
215 id
216 text
217 }
218 missingTodo: todo {
219 id
220 text
221 }
222 }
223 `;
224
225 write(
226 store,
227 { query },
228 {
229 missingTodoItem: [null, { __typename: 'Todo', id: 1, text: 'Learn' }],
230 missingTodo: null,
231 } as any,
232 new CombinedError({
233 graphQLErrors: [
234 {
235 message: 'Test',
236 path: ['missingTodoItem', 0],
237 },
238 {
239 message: 'Test',
240 path: ['missingTodo'],
241 },
242 ],
243 })
244 );
245
246 InMemoryData.initDataState('read', store.data, null);
247 expect(InMemoryData.readLink('Query', 'todos')).toEqual([
248 undefined,
249 'Todo:1',
250 ]);
251 expect(InMemoryData.readLink('Query', 'todo')).toEqual(undefined);
252 });
253});