1import { describe, it, expect } from 'vitest';
2import {
3 TypedDocumentNode,
4 FormattedNode,
5 formatDocument,
6 gql,
7} from '@urql/core';
8import { FieldNode } from '@0no-co/graphql.web';
9
10import { SelectionIterator, deferRef } from './shared';
11import { SelectionSet } from '../ast';
12
13const selectionOfDocument = (
14 doc: TypedDocumentNode
15): FormattedNode<SelectionSet> => {
16 for (const definition of formatDocument(doc).definitions)
17 if (definition.kind === 'OperationDefinition')
18 return definition.selectionSet.selections as FormattedNode<SelectionSet>;
19 return [];
20};
21
22const ctx = {} as any;
23
24describe('SelectionIterator', () => {
25 it('emits all fields', () => {
26 const selection = selectionOfDocument(gql`
27 {
28 a
29 b
30 c
31 }
32 `);
33 const iterate = new SelectionIterator(
34 'Query',
35 'Query',
36 false,
37 undefined,
38 selection,
39 ctx
40 );
41 const result: FieldNode[] = [];
42
43 let node: FieldNode | void;
44 while ((node = iterate.next())) result.push(node);
45
46 expect(result).toMatchInlineSnapshot(`
47 [
48 {
49 "alias": undefined,
50 "arguments": undefined,
51 "directives": undefined,
52 "kind": "Field",
53 "name": {
54 "kind": "Name",
55 "value": "a",
56 },
57 "selectionSet": undefined,
58 },
59 {
60 "alias": undefined,
61 "arguments": undefined,
62 "directives": undefined,
63 "kind": "Field",
64 "name": {
65 "kind": "Name",
66 "value": "b",
67 },
68 "selectionSet": undefined,
69 },
70 {
71 "alias": undefined,
72 "arguments": undefined,
73 "directives": undefined,
74 "kind": "Field",
75 "name": {
76 "kind": "Name",
77 "value": "c",
78 },
79 "selectionSet": undefined,
80 },
81 ]
82 `);
83 });
84
85 it('skips fields that are skipped or not included', () => {
86 const selection = selectionOfDocument(gql`
87 {
88 a @skip(if: true)
89 b @include(if: false)
90 }
91 `);
92
93 const iterate = new SelectionIterator(
94 'Query',
95 'Query',
96 false,
97 undefined,
98 selection,
99 ctx
100 );
101 const result: FieldNode[] = [];
102
103 let node: FieldNode | void;
104 while ((node = iterate.next())) result.push(node);
105
106 expect(result).toMatchInlineSnapshot('[]');
107 });
108
109 it('processes fragments', () => {
110 const selection = selectionOfDocument(gql`
111 {
112 a
113 ... {
114 b
115 }
116 ... {
117 ... {
118 c
119 }
120 }
121 }
122 `);
123
124 const iterate = new SelectionIterator(
125 'Query',
126 'Query',
127 false,
128 undefined,
129 selection,
130 ctx
131 );
132 const result: FieldNode[] = [];
133
134 let node: FieldNode | void;
135 while ((node = iterate.next())) result.push(node);
136
137 expect(result).toMatchInlineSnapshot(`
138 [
139 {
140 "alias": undefined,
141 "arguments": undefined,
142 "directives": undefined,
143 "kind": "Field",
144 "name": {
145 "kind": "Name",
146 "value": "a",
147 },
148 "selectionSet": undefined,
149 },
150 {
151 "alias": undefined,
152 "arguments": undefined,
153 "directives": undefined,
154 "kind": "Field",
155 "name": {
156 "kind": "Name",
157 "value": "b",
158 },
159 "selectionSet": undefined,
160 },
161 {
162 "alias": undefined,
163 "arguments": undefined,
164 "directives": undefined,
165 "kind": "Field",
166 "name": {
167 "kind": "Name",
168 "value": "c",
169 },
170 "selectionSet": undefined,
171 },
172 ]
173 `);
174 });
175
176 it('updates deferred state as needed', () => {
177 const selection = selectionOfDocument(gql`
178 {
179 a
180 ... @defer {
181 b
182 }
183 ... {
184 ... @defer {
185 c
186 }
187 }
188 ... {
189 ... {
190 d
191 }
192 }
193 ... @defer {
194 ... {
195 e
196 }
197 }
198 ... {
199 ... {
200 f
201 }
202 }
203 ... {
204 g
205 }
206 h
207 }
208 `);
209
210 const iterate = new SelectionIterator(
211 'Query',
212 'Query',
213 false,
214 undefined,
215 selection,
216 ctx
217 );
218
219 const deferred: boolean[] = [];
220 while (iterate.next()) deferred.push(deferRef);
221 expect(deferred).toEqual([
222 false, // a
223 true, // b
224 true, // c
225 false, // d
226 true, // e
227 false, // f
228 false, // g
229 false, // h
230 ]);
231 });
232
233 it('applies the parent’s defer state if needed', () => {
234 const selection = selectionOfDocument(gql`
235 {
236 a
237 ... @defer {
238 b
239 }
240 ... {
241 c
242 }
243 }
244 `);
245
246 const iterate = new SelectionIterator(
247 'Query',
248 'Query',
249 true,
250 undefined,
251 selection,
252 ctx
253 );
254
255 const deferred: boolean[] = [];
256 while (iterate.next()) deferred.push(deferRef);
257 expect(deferred).toEqual([true, true, true]);
258 });
259});