Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
at main 10 kB view raw
1import { 2 OperationResult, 3 OperationResultSource, 4 RequestPolicy, 5} from '@urql/core'; 6import { computed, nextTick, reactive, readonly, ref } from 'vue'; 7import { vi, expect, it, describe } from 'vitest'; 8 9vi.mock('./useClient.ts', async () => ({ 10 __esModule: true, 11 ...(await vi.importActual<typeof import('./useClient')>('./useClient.ts')), 12 useClient: () => ref(client), 13})); 14 15import { pipe, makeSubject, fromValue, delay } from 'wonka'; 16import { createClient } from '@urql/core'; 17import { useQuery, UseQueryArgs } from './useQuery'; 18 19const client = createClient({ url: '/graphql', exchanges: [] }); 20 21const createQuery = (args: UseQueryArgs) => { 22 const executeQuery = vi 23 .spyOn(client, 'executeQuery') 24 .mockImplementation(request => { 25 return pipe( 26 fromValue({ operation: request, data: { test: true } }), 27 delay(1) 28 ) as any; 29 }); 30 31 const query$ = useQuery(args); 32 33 return { 34 query$, 35 executeQuery, 36 }; 37}; 38 39describe('useQuery', () => { 40 it('runs a query and updates data', async () => { 41 const subject = makeSubject<any>(); 42 const executeQuery = vi 43 .spyOn(client, 'executeQuery') 44 .mockImplementation( 45 () => subject.source as OperationResultSource<OperationResult> 46 ); 47 48 const query = useQuery({ 49 query: `{ test }`, 50 }); 51 52 expect(readonly(query)).toMatchObject({ 53 data: undefined, 54 stale: false, 55 fetching: true, 56 error: undefined, 57 extensions: undefined, 58 operation: undefined, 59 isPaused: false, 60 pause: expect.any(Function), 61 resume: expect.any(Function), 62 executeQuery: expect.any(Function), 63 then: expect.any(Function), 64 }); 65 66 expect(executeQuery).toHaveBeenCalledWith( 67 { 68 key: expect.any(Number), 69 query: expect.any(Object), 70 variables: {}, 71 context: undefined, 72 }, 73 { 74 requestPolicy: undefined, 75 } 76 ); 77 78 expect(query.fetching.value).toBe(true); 79 80 subject.next({ data: { test: true } }); 81 82 expect(query.fetching.value).toBe(false); 83 expect(query.data.value).toHaveProperty('test', true); 84 }); 85 86 it('runs queries as a promise-like that resolves when used', async () => { 87 const executeQuery = vi 88 .spyOn(client, 'executeQuery') 89 .mockImplementation(() => { 90 return pipe(fromValue({ data: { test: true } }), delay(1)) as any; 91 }); 92 93 const query = await useQuery({ 94 query: `{ test }`, 95 }); 96 97 expect(executeQuery).toHaveBeenCalledTimes(1); 98 expect(query.fetching.value).toBe(false); 99 expect(query.data.value).toEqual({ test: true }); 100 }); 101 102 it('runs queries as a promise-like that resolves even when the query changes', async () => { 103 const doc = ref('{ test }'); 104 105 const { executeQuery, query$ } = createQuery({ 106 query: doc, 107 }); 108 109 doc.value = '{ test2 }'; 110 111 await query$; 112 113 expect(executeQuery).toHaveBeenCalledTimes(2); 114 expect(query$.fetching.value).toBe(false); 115 expect(query$.data.value).toEqual({ test: true }); 116 117 expect(query$.operation.value).toHaveProperty( 118 'query.definitions.0.selectionSet.selections.0.name.value', 119 'test2' 120 ); 121 }); 122 123 it('reacts to ref variables changing', async () => { 124 const variables = ref({ prop: 1 }); 125 126 const { executeQuery, query$ } = createQuery({ 127 query: ref('{ test }'), 128 variables, 129 }); 130 131 await query$; 132 expect(executeQuery).toHaveBeenCalledTimes(1); 133 expect(query$.operation.value).toHaveProperty('variables.prop', 1); 134 135 variables.value.prop++; 136 await query$; 137 expect(executeQuery).toHaveBeenCalledTimes(2); 138 expect(query$.operation.value).toHaveProperty('variables.prop', 2); 139 140 variables.value = { prop: 3 }; 141 await query$; 142 expect(executeQuery).toHaveBeenCalledTimes(3); 143 expect(query$.operation.value).toHaveProperty('variables.prop', 3); 144 }); 145 146 it('reacts to ref variables changing', async () => { 147 const foo = ref(1); 148 const bar = ref(1); 149 150 const { executeQuery, query$ } = createQuery({ 151 query: ref('{ test }'), 152 variables: ref({ 153 prop: foo, 154 nested: { 155 prop: bar, 156 }, 157 }), 158 }); 159 160 await query$; 161 expect(executeQuery).toHaveBeenCalledTimes(1); 162 expect(query$.operation.value).toHaveProperty('variables.prop', 1); 163 164 foo.value++; 165 await query$; 166 expect(executeQuery).toHaveBeenCalledTimes(2); 167 expect(query$.operation.value).toHaveProperty('variables.prop', 2); 168 169 bar.value++; 170 await query$; 171 expect(executeQuery).toHaveBeenCalledTimes(3); 172 expect(query$.operation.value).toHaveProperty('variables.nested.prop', 2); 173 }); 174 175 it('reacts to getter variables changing', async () => { 176 const foo = ref(1); 177 const bar = ref(1); 178 179 const { executeQuery, query$ } = createQuery({ 180 query: ref('{ test }'), 181 variables: () => ({ 182 prop: foo.value, 183 nested: { 184 prop: bar.value, 185 }, 186 }), 187 }); 188 189 await query$; 190 expect(executeQuery).toHaveBeenCalledTimes(1); 191 expect(query$.operation.value).toHaveProperty('variables.prop', 1); 192 193 foo.value++; 194 await query$; 195 expect(executeQuery).toHaveBeenCalledTimes(2); 196 expect(query$.operation.value).toHaveProperty('variables.prop', 2); 197 198 bar.value++; 199 await query$; 200 expect(executeQuery).toHaveBeenCalledTimes(3); 201 expect(query$.operation.value).toHaveProperty('variables.nested.prop', 2); 202 }); 203 204 it('reacts to reactive variables changing', async () => { 205 const prop = ref(1); 206 const variables = reactive({ prop: 1, deep: { nested: { prop } } }); 207 208 const { executeQuery, query$ } = createQuery({ 209 query: ref('{ test }'), 210 variables, 211 }); 212 213 await query$; 214 expect(executeQuery).toHaveBeenCalledTimes(1); 215 expect(query$.operation.value).toHaveProperty('variables.prop', 1); 216 217 variables.prop++; 218 await query$; 219 expect(executeQuery).toHaveBeenCalledTimes(2); 220 expect(query$.operation.value).toHaveProperty('variables.prop', 2); 221 222 prop.value++; 223 await query$; 224 expect(executeQuery).toHaveBeenCalledTimes(3); 225 expect(query$.operation.value).toHaveProperty( 226 'variables.deep.nested.prop', 227 2 228 ); 229 }); 230 231 it('reacts to computed variables changing', async () => { 232 const foo = ref(1); 233 const bar = ref(1); 234 const variables = computed(() => ({ 235 prop: foo.value, 236 deep: { nested: { prop: bar.value } }, 237 })); 238 239 const { executeQuery, query$ } = createQuery({ 240 query: ref('{ test }'), 241 variables, 242 }); 243 244 await query$; 245 expect(executeQuery).toHaveBeenCalledTimes(1); 246 expect(query$.operation.value).toHaveProperty('variables.prop', 1); 247 248 foo.value++; 249 await query$; 250 expect(executeQuery).toHaveBeenCalledTimes(2); 251 expect(query$.operation.value).toHaveProperty('variables.prop', 2); 252 253 bar.value++; 254 await query$; 255 expect(executeQuery).toHaveBeenCalledTimes(3); 256 expect(query$.operation.value).toHaveProperty( 257 'variables.deep.nested.prop', 258 2 259 ); 260 }); 261 262 it('reacts to reactive context argument', async () => { 263 const context = ref<{ requestPolicy: RequestPolicy }>({ 264 requestPolicy: 'cache-only', 265 }); 266 267 const { executeQuery, query$ } = createQuery({ 268 query: ref('{ test }'), 269 context, 270 }); 271 272 await query$; 273 expect(executeQuery).toHaveBeenCalledTimes(1); 274 275 context.value.requestPolicy = 'network-only'; 276 await query$; 277 expect(executeQuery).toHaveBeenCalledTimes(2); 278 }); 279 280 it('reacts to callback context argument', async () => { 281 const requestPolicy = ref<RequestPolicy>('cache-only'); 282 283 const { executeQuery, query$ } = createQuery({ 284 query: ref('{ test }'), 285 context: () => ({ 286 requestPolicy: requestPolicy.value, 287 }), 288 }); 289 290 await query$; 291 expect(executeQuery).toHaveBeenCalledTimes(1); 292 293 requestPolicy.value = 'network-only'; 294 await query$; 295 expect(executeQuery).toHaveBeenCalledTimes(2); 296 }); 297 298 it('pauses query when asked to do so', async () => { 299 const subject = makeSubject<any>(); 300 const executeQuery = vi 301 .spyOn(client, 'executeQuery') 302 .mockImplementation( 303 () => subject.source as OperationResultSource<OperationResult> 304 ); 305 306 const query = useQuery({ 307 query: `{ test }`, 308 pause: true, 309 }); 310 311 expect(executeQuery).not.toHaveBeenCalled(); 312 313 query.resume(); 314 await nextTick(); 315 expect(query.fetching.value).toBe(true); 316 317 subject.next({ data: { test: true } }); 318 319 expect(query.fetching.value).toBe(false); 320 expect(query.data.value).toHaveProperty('test', true); 321 }); 322 323 it('pauses query with ref variable', async () => { 324 const pause = ref(true); 325 326 const { executeQuery, query$ } = createQuery({ 327 query: ref('{ test }'), 328 pause, 329 }); 330 331 await query$; 332 expect(executeQuery).not.toHaveBeenCalled(); 333 334 pause.value = false; 335 await query$; 336 expect(executeQuery).toHaveBeenCalledTimes(1); 337 338 query$.pause(); 339 query$.resume(); 340 await query$; 341 expect(executeQuery).toHaveBeenCalledTimes(2); 342 }); 343 344 it('pauses query with computed variable', async () => { 345 const pause = ref(true); 346 347 const { executeQuery, query$ } = createQuery({ 348 query: ref('{ test }'), 349 pause: computed(() => pause.value), 350 }); 351 352 await query$; 353 expect(executeQuery).not.toHaveBeenCalled(); 354 355 pause.value = false; 356 await query$; 357 expect(executeQuery).toHaveBeenCalledTimes(1); 358 359 query$.pause(); 360 query$.resume(); 361 await query$; 362 // this shouldn't be called, as pause/resume functionality should works in sync with passed `pause` variable, e.g.: 363 // if we pass readonly computed variable, then we want to make sure that its value fully controls the state of the request. 364 expect(executeQuery).toHaveBeenCalledTimes(1); 365 }); 366 367 it('pauses query with callback', async () => { 368 const pause = ref(true); 369 370 const { executeQuery, query$ } = createQuery({ 371 query: ref('{ test }'), 372 pause: () => pause.value, 373 }); 374 375 await query$; 376 expect(executeQuery).not.toHaveBeenCalled(); 377 378 pause.value = false; 379 await query$; 380 expect(executeQuery).toHaveBeenCalledTimes(1); 381 382 query$.pause(); 383 query$.resume(); 384 await query$; 385 // the same as computed variable example - user has full control over the request state if using callback 386 expect(executeQuery).toHaveBeenCalledTimes(1); 387 }); 388});