|
1 | 1 | --- |
2 | 2 | name: data-client-vue-testing |
3 | | -description: Test @data-client/vue composables and components - renderDataCompose, mountDataClient, fixtures, jest, nock, Vue 3 reactive props, useSuspense testing |
| 3 | +description: Test @data-client/vue composables and components - renderDataCompose, mountDataClient, fixtures, jest, nock HTTP mocking, polling/subscription tests with fake timers, useSuspense, useLive, useSubscription, Vue 3 reactive props |
4 | 4 | license: Apache 2.0 |
5 | 5 | --- |
6 | 6 |
|
@@ -251,73 +251,63 @@ await nextTick(); |
251 | 251 |
|
252 | 252 | ## Testing with nock (HTTP Mocking) |
253 | 253 |
|
| 254 | +Use nock when a test must exercise the real fetch path — verifying URL construction, headers, request bodies, retries, or anything in your `RestEndpoint`/`Resource` networking layer. For pure store/state behavior, prefer `initialFixtures`/`resolverFixtures` (lighter and faster). |
| 255 | + |
| 256 | +Minimal shape: |
| 257 | + |
254 | 258 | ```typescript |
255 | 259 | import nock from 'nock'; |
256 | 260 |
|
257 | 261 | beforeAll(() => { |
258 | 262 | nock(/.*/) |
259 | 263 | .persist() |
260 | | - .defaultReplyHeaders({ |
261 | | - 'Access-Control-Allow-Origin': '*', |
262 | | - 'Content-Type': 'application/json', |
263 | | - }) |
264 | | - .options(/.*/) |
265 | | - .reply(200) |
266 | | - .get('/article/5') |
267 | | - .reply(200, { id: 5, title: 'hi ho' }); |
| 264 | + .defaultReplyHeaders({ 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json' }) |
| 265 | + .options(/.*/).reply(200) // CORS preflight (required in JSDOM) |
| 266 | + .get('/article/5').reply(200, { id: 5, title: 'hi ho' }); |
268 | 267 | }); |
269 | 268 |
|
270 | | -afterAll(() => { |
271 | | - nock.cleanAll(); |
272 | | -}); |
| 269 | +afterAll(() => nock.cleanAll()); |
273 | 270 | ``` |
274 | 271 |
|
275 | | -**Dynamic responses with nock:** |
276 | | -```typescript |
277 | | -const fetchMock = jest.fn(() => payload); |
278 | | -nock(/.*/) |
279 | | - .get(`/article/${payload.id}`) |
280 | | - .reply(200, fetchMock); |
| 272 | +For dynamic server state, mutating-closure replies, request spying with `jest.fn()`, error responses, and mixing nock with fixtures, see [references/nock-http-mocking.md](references/nock-http-mocking.md). |
281 | 273 |
|
282 | | -// Later verify: |
283 | | -expect(fetchMock).toHaveBeenCalledTimes(1); |
284 | | -``` |
| 274 | +## Testing Polling and Subscriptions |
285 | 275 |
|
286 | | -## Testing Polling/Subscriptions |
| 276 | +For composables with `pollFrequency`, `useLive`, or `useSubscription`, use fake timers so polls fire deterministically. Core flow: |
| 277 | + |
| 278 | +1. `jest.useFakeTimers()` **before** mount/render (so the interval is created under fake timers). |
| 279 | +2. Render, then `jest.advanceTimersByTime(frequency)` to drive the initial fetch. |
| 280 | +3. Mutate the response (e.g. `responseMock.mockReturnValue(...)`), advance time again, `await allSettled()` and `await nextTick()`. |
| 281 | +4. Restore real timers in `afterEach`: `jest.useRealTimers()`. |
| 282 | + |
| 283 | +Quick example: |
287 | 284 |
|
288 | 285 | ```typescript |
289 | | -it('should poll and update', async () => { |
290 | | - jest.useFakeTimers(); |
291 | | - let serverData = { id: 5, title: 'Original' }; |
| 286 | +jest.useFakeTimers(); |
| 287 | +const responseMock = jest.fn(() => payload); |
292 | 288 |
|
293 | | - nock(/.*/) |
294 | | - .persist() |
295 | | - .get('/article/5') |
296 | | - .reply(200, () => serverData); |
297 | | - |
298 | | - const { wrapper } = mountDataClient(PollingComponent); |
299 | | - |
300 | | - // Wait for initial render |
301 | | - for (let i = 0; i < 100 && !wrapper.find('h3').exists(); i++) { |
302 | | - await jest.advanceTimersByTimeAsync(frequency / 10); |
303 | | - await nextTick(); |
304 | | - } |
305 | | - expect(wrapper.find('h3').text()).toBe('Original'); |
| 289 | +const { result, allSettled, waitForNextUpdate, cleanup } = await renderDataCompose( |
| 290 | + () => useSuspense(PollingArticleResource.get, { id: payload.id }), |
| 291 | + { resolverFixtures: [{ endpoint: PollingArticleResource.get, response: responseMock }] }, |
| 292 | +); |
306 | 293 |
|
307 | | - // Simulate server update |
308 | | - serverData = { id: 5, title: 'Updated' }; |
| 294 | +jest.advanceTimersByTime(frequency); |
| 295 | +await allSettled(); |
| 296 | +await waitForNextUpdate(); |
| 297 | +const articleRef = await result; |
309 | 298 |
|
310 | | - // Advance timers to trigger poll |
311 | | - for (let i = 0; i < 20 && wrapper.find('h3').text() !== 'Updated'; i++) { |
312 | | - await jest.advanceTimersByTimeAsync(frequency / 10); |
313 | | - await nextTick(); |
314 | | - } |
315 | | - expect(wrapper.find('h3').text()).toBe('Updated'); |
| 299 | +responseMock.mockReturnValue({ ...payload, title: 'updated' }); |
| 300 | +jest.advanceTimersByTime(frequency); |
| 301 | +await allSettled(); |
| 302 | +await nextTick(); |
316 | 303 |
|
317 | | - jest.useRealTimers(); |
318 | | -}); |
| 304 | +expect(articleRef!.value.title).toBe('updated'); |
| 305 | +jest.useRealTimers(); |
| 306 | +cleanup(); |
319 | 307 | ``` |
320 | 308 |
|
| 309 | +For unsubscribe patterns, component-level polling tests, fake-timer-safe `flushUntil`, polling via nock, and common pitfalls, see [references/polling-subscriptions.md](references/polling-subscriptions.md). |
| 310 | + |
321 | 311 | ## Vue Suspense Behavior |
322 | 312 |
|
323 | 313 | **useSuspense() returns Promise → ComputedRef:** |
@@ -380,6 +370,8 @@ For detailed API documentation, see the [references](references/) directory: |
380 | 370 |
|
381 | 371 | - [Fixtures](references/Fixtures.md) - Fixture format reference |
382 | 372 | - [unit-testing-hooks](references/unit-testing-hooks.md) - Hook/composable testing guide |
| 373 | +- [nock-http-mocking](references/nock-http-mocking.md) - Full nock setup, dynamic server state, request spying, errors, pitfalls |
| 374 | +- [polling-subscriptions](references/polling-subscriptions.md) - Fake-timer patterns for `useLive`/`useSubscription`/`pollFrequency`, unsubscribe verification, polling via nock |
383 | 375 |
|
384 | 376 | ## Common Patterns |
385 | 377 |
|
|
0 commit comments