Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
at main 8.1 kB view raw
1import { DocumentNode } from 'graphql'; 2import { gql } from '@urql/core'; 3import { it, expect } from 'vitest'; 4import { __initAnd_query as query } from '../operations/query'; 5import { __initAnd_write as write } from '../operations/write'; 6import { Store } from '../store/store'; 7 8interface TestCase { 9 query: DocumentNode; 10 variables?: any; 11 data: any; 12} 13 14const expectCacheIntegrity = (testcase: TestCase) => { 15 const store = new Store(); 16 const request = { query: testcase.query, variables: testcase.variables }; 17 const writeRes = write(store, request, testcase.data); 18 const queryRes = query(store, request); 19 expect(queryRes.data).not.toBe(null); 20 expect(queryRes.data).toEqual(testcase.data); 21 expect(queryRes.partial).toBe(false); 22 expect(queryRes.dependencies).toEqual(writeRes.dependencies); 23}; 24 25it('int on query', () => { 26 expectCacheIntegrity({ 27 query: gql` 28 { 29 __typename 30 int 31 } 32 `, 33 data: { __typename: 'Query', int: 42 }, 34 }); 35}); 36 37it('aliased field on query', () => { 38 expectCacheIntegrity({ 39 query: gql` 40 { 41 __typename 42 anotherName: int 43 } 44 `, 45 data: { __typename: 'Query', anotherName: 42 }, 46 }); 47}); 48 49it('@skip directive on field on query', () => { 50 expectCacheIntegrity({ 51 query: gql` 52 { 53 __typename 54 intA @skip(if: true) 55 intB @skip(if: false) 56 } 57 `, 58 data: { __typename: 'Query', intB: 2 }, 59 }); 60}); 61 62it('@include directive on field on query', () => { 63 expectCacheIntegrity({ 64 query: gql` 65 { 66 __typename 67 intA @include(if: true) 68 intB @include(if: false) 69 } 70 `, 71 data: { __typename: 'Query', intA: 2 }, 72 }); 73}); 74 75it('random directive on field on query', () => { 76 expectCacheIntegrity({ 77 query: gql` 78 { 79 __typename 80 int @shouldntMatter 81 } 82 `, 83 data: { __typename: 'Query', int: 1 }, 84 }); 85}); 86 87it('json on query', () => { 88 expectCacheIntegrity({ 89 query: gql` 90 { 91 __typename 92 json 93 } 94 `, 95 // The `__typename` field should not mislead the cache 96 data: { 97 __typename: 'Query', 98 json: { __typename: 'Misleading', test: true }, 99 }, 100 }); 101}); 102 103it('nullable field on query', () => { 104 expectCacheIntegrity({ 105 query: gql` 106 { 107 __typename 108 missing 109 } 110 `, 111 data: { __typename: 'Query', missing: null }, 112 }); 113}); 114 115it('int field with arguments on query', () => { 116 expectCacheIntegrity({ 117 query: gql` 118 { 119 __typename 120 int(test: true) 121 } 122 `, 123 data: { __typename: 'Query', int: 42 }, 124 }); 125}); 126 127it('non-keyable entity on query', () => { 128 expectCacheIntegrity({ 129 query: gql` 130 { 131 __typename 132 item { 133 __typename 134 name 135 } 136 } 137 `, 138 // This entity has no `id` or `_id` field 139 data: { __typename: 'Query', item: { __typename: 'Item', name: 'Test' } }, 140 }); 141}); 142 143it('non-IDable entity on query', () => { 144 expectCacheIntegrity({ 145 query: gql` 146 { 147 __typename 148 item { 149 __typename 150 name 151 } 152 } 153 `, 154 // This entity has a `__typename` but no ID fields 155 data: { __typename: 'Query', item: { __typename: 'Item', name: 'Test' } }, 156 }); 157}); 158 159it('entity on query', () => { 160 expectCacheIntegrity({ 161 query: gql` 162 { 163 __typename 164 item { 165 __typename 166 id 167 name 168 } 169 } 170 `, 171 data: { 172 __typename: 'Query', 173 item: { __typename: 'Item', id: '1', name: 'Test' }, 174 }, 175 }); 176}); 177 178it('entity on aliased field on query', () => { 179 expectCacheIntegrity({ 180 query: gql` 181 { 182 __typename 183 anotherName: item { 184 __typename 185 id 186 name 187 } 188 } 189 `, 190 data: { 191 __typename: 'Query', 192 anotherName: { __typename: 'Item', id: '1', name: 'Test' }, 193 }, 194 }); 195}); 196 197it('entity with arguments on query', () => { 198 expectCacheIntegrity({ 199 query: gql` 200 { 201 __typename 202 item(test: true) { 203 __typename 204 id 205 name 206 } 207 } 208 `, 209 data: { 210 __typename: 'Query', 211 item: { __typename: 'Item', id: '1', name: 'Test' }, 212 }, 213 }); 214}); 215 216it('entity with Int-like ID on query', () => { 217 expectCacheIntegrity({ 218 query: gql` 219 { 220 __typename 221 item { 222 __typename 223 id 224 name 225 } 226 } 227 `, 228 // This is the same as above, but with a number on `id` 229 data: { 230 __typename: 'Query', 231 item: { __typename: 'Item', id: 1, name: 'Test' }, 232 }, 233 }); 234}); 235 236it('entity list on query', () => { 237 expectCacheIntegrity({ 238 query: gql` 239 { 240 __typename 241 items { 242 __typename 243 id 244 } 245 } 246 `, 247 data: { 248 __typename: 'Query', 249 items: [ 250 { __typename: 'Item', id: 1 }, 251 { __typename: 'Item', id: 2 }, 252 ], 253 }, 254 }); 255}); 256 257it('nested entity list on query', () => { 258 expectCacheIntegrity({ 259 query: gql` 260 { 261 __typename 262 items { 263 __typename 264 id 265 } 266 } 267 `, 268 data: { 269 // This is the same as above, but with a nested array and added null values 270 __typename: 'Query', 271 items: [ 272 { __typename: 'Item', id: 1 }, 273 [{ __typename: 'Item', id: 2 }, null], 274 null, 275 ], 276 }, 277 }); 278}); 279 280it('entity list on query and inline fragment', () => { 281 expectCacheIntegrity({ 282 query: gql` 283 { 284 __typename 285 items { 286 __typename 287 id 288 } 289 ... on Query { 290 items { 291 test 292 } 293 } 294 } 295 `, 296 data: { 297 __typename: 'Query', 298 items: [{ __typename: 'Item', id: 1, test: true }, null], 299 }, 300 }); 301}); 302 303it('conditionless inline fragment', () => { 304 expectCacheIntegrity({ 305 query: gql` 306 { 307 __typename 308 ... { 309 test 310 } 311 } 312 `, 313 data: { 314 __typename: 'Query', 315 test: true, 316 }, 317 }); 318}); 319 320it('skipped conditionless inline fragment', () => { 321 expectCacheIntegrity({ 322 query: gql` 323 { 324 __typename 325 ... @skip(if: true) { 326 test 327 } 328 } 329 `, 330 data: { 331 __typename: 'Query', 332 }, 333 }); 334}); 335 336it('entity list on query and spread fragment', () => { 337 expectCacheIntegrity({ 338 query: gql` 339 query Test { 340 __typename 341 items { 342 __typename 343 id 344 } 345 ...TestFragment 346 } 347 348 fragment TestFragment on Query { 349 items { 350 test 351 } 352 } 353 `, 354 data: { 355 __typename: 'Query', 356 items: [{ __typename: 'Item', id: 1, test: true }, null], 357 }, 358 }); 359}); 360 361it('skipped spread fragment', () => { 362 expectCacheIntegrity({ 363 query: gql` 364 query Test { 365 __typename 366 ...TestFragment @skip(if: true) 367 } 368 369 fragment TestFragment on Query { 370 test 371 } 372 `, 373 data: { 374 __typename: 'Query', 375 }, 376 }); 377}); 378 379it('embedded objects on entities', () => { 380 expectCacheIntegrity({ 381 query: gql` 382 { 383 __typename 384 user { 385 __typename 386 id 387 posts { 388 __typename 389 edges { 390 __typename 391 node { 392 __typename 393 id 394 } 395 } 396 } 397 } 398 } 399 `, 400 data: { 401 __typename: 'Query', 402 user: { 403 __typename: 'User', 404 id: 1, 405 posts: { 406 __typename: 'PostsConnection', 407 edges: [ 408 { 409 __typename: 'PostsEdge', 410 node: { 411 __typename: 'Post', 412 id: 1, 413 }, 414 }, 415 ], 416 }, 417 }, 418 }, 419 }); 420}); 421 422it('nested viewer selections', () => { 423 expectCacheIntegrity({ 424 query: gql` 425 { 426 __typename 427 int 428 viewer { 429 __typename 430 int 431 } 432 } 433 `, 434 data: { 435 __typename: 'Query', 436 int: 42, 437 viewer: { 438 __typename: 'Query', 439 int: 42, 440 }, 441 }, 442 }); 443});