|
1 | 1 | import React from 'react' |
2 | 2 | import { renderHook, waitFor } from '@testing-library/react' |
3 | 3 | import { vi } from 'vitest' |
4 | | -import { usePollMember } from 'src/hooks/usePollMember' |
5 | | -import { ApiProvider } from 'src/context/ApiContext' |
| 4 | +import { usePollMember, PollingState } from 'src/hooks/usePollMember' |
| 5 | +import { ApiProvider, ApiContextTypes } from 'src/context/ApiContext' |
6 | 6 | import { Provider } from 'react-redux' |
7 | | -import { createReduxStore } from 'src/redux/Store' |
| 7 | +import { createReduxStore, RootState } from 'src/redux/Store' |
8 | 8 | import { member, JOB_DATA } from 'src/services/mockedData' |
9 | 9 | import { ReadableStatuses } from 'src/const/Statuses' |
10 | 10 | import { CONNECTING_MESSAGES } from 'src/utilities/pollers' |
11 | 11 | import { take } from 'rxjs/operators' |
12 | 12 |
|
13 | | -interface PollingState { |
14 | | - isError: boolean |
15 | | - pollingCount: number |
16 | | - currentResponse?: unknown |
17 | | - pollingIsDone: boolean |
18 | | - userMessage?: string |
19 | | - initialDataReady?: boolean |
20 | | -} |
21 | | - |
22 | | -interface ApiValue { |
23 | | - loadMemberByGuid?: (guid: string, locale: string) => Promise<unknown> |
24 | | - loadJob?: (jobGuid: string) => Promise<unknown> |
25 | | -} |
26 | | - |
27 | | -interface PreloadedState { |
28 | | - experimentalFeatures?: { |
29 | | - optOutOfEarlyUserRelease?: boolean |
30 | | - memberPollingMilliseconds?: number |
31 | | - } |
32 | | -} |
33 | | - |
34 | | -const createWrapper = (apiValue: ApiValue, preloadedState?: PreloadedState) => { |
| 13 | +const createWrapper = (apiValue: Partial<ApiContextTypes>, preloadedState?: Partial<RootState>) => { |
35 | 14 | const store = createReduxStore(preloadedState) |
36 | 15 | const Wrapper = ({ children }: { children: React.ReactNode }) => ( |
37 | 16 | <Provider store={store}> |
@@ -641,4 +620,105 @@ describe('usePollMember', () => { |
641 | 620 |
|
642 | 621 | subscription.unsubscribe() |
643 | 622 | }, 10000) |
| 623 | + |
| 624 | + it('should correctly update previousResponse and currentResponse over multiple polls', async () => { |
| 625 | + const member1 = { ...member.member, guid: 'MBR-1' } |
| 626 | + const member2 = { ...member.member, guid: 'MBR-2' } |
| 627 | + |
| 628 | + const apiValue = { |
| 629 | + loadMemberByGuid: vi.fn().mockResolvedValueOnce(member1).mockResolvedValue(member2), |
| 630 | + loadJob: vi.fn().mockResolvedValue(JOB_DATA), |
| 631 | + } |
| 632 | + |
| 633 | + const preloadedState = { |
| 634 | + experimentalFeatures: { |
| 635 | + memberPollingMilliseconds: 1000, |
| 636 | + }, |
| 637 | + } |
| 638 | + |
| 639 | + const { result } = renderHook(() => usePollMember(), { |
| 640 | + wrapper: createWrapper(apiValue, preloadedState), |
| 641 | + }) |
| 642 | + |
| 643 | + const pollMember = result.current |
| 644 | + const states: PollingState[] = [] |
| 645 | + |
| 646 | + const subscription = pollMember('MBR-123') |
| 647 | + .pipe(take(2)) |
| 648 | + .subscribe((state: PollingState) => { |
| 649 | + states.push(state) |
| 650 | + }) |
| 651 | + |
| 652 | + await waitFor( |
| 653 | + () => { |
| 654 | + expect(states.length).toBeGreaterThanOrEqual(2) |
| 655 | + }, |
| 656 | + { timeout: 3500 }, |
| 657 | + ) |
| 658 | + |
| 659 | + // First poll |
| 660 | + expect(states[0].previousResponse).toEqual({}) |
| 661 | + expect(states[0].currentResponse).toEqual({ member: member1, job: JOB_DATA }) |
| 662 | + |
| 663 | + // Second poll |
| 664 | + expect(states[1].previousResponse).toEqual({ member: member1, job: JOB_DATA }) |
| 665 | + expect(states[1].currentResponse).toEqual({ member: member2, job: JOB_DATA }) |
| 666 | + |
| 667 | + subscription.unsubscribe() |
| 668 | + }, 10000) |
| 669 | + |
| 670 | + it('should preserve previousResponse and currentResponse when an intermediate poll fails', async () => { |
| 671 | + const member1 = { ...member.member, guid: 'MBR-1' } |
| 672 | + |
| 673 | + const apiValue = { |
| 674 | + loadMemberByGuid: vi |
| 675 | + .fn() |
| 676 | + .mockResolvedValueOnce(member1) |
| 677 | + .mockRejectedValueOnce(new Error('Intermediate Error')) |
| 678 | + .mockResolvedValue(member1), |
| 679 | + loadJob: vi.fn().mockResolvedValue(JOB_DATA), |
| 680 | + } |
| 681 | + |
| 682 | + const preloadedState = { |
| 683 | + experimentalFeatures: { |
| 684 | + memberPollingMilliseconds: 1000, |
| 685 | + }, |
| 686 | + } |
| 687 | + |
| 688 | + const { result } = renderHook(() => usePollMember(), { |
| 689 | + wrapper: createWrapper(apiValue, preloadedState), |
| 690 | + }) |
| 691 | + |
| 692 | + const pollMember = result.current |
| 693 | + const states: PollingState[] = [] |
| 694 | + |
| 695 | + const subscription = pollMember('MBR-123') |
| 696 | + .pipe(take(3)) |
| 697 | + .subscribe((state: PollingState) => { |
| 698 | + states.push(state) |
| 699 | + }) |
| 700 | + |
| 701 | + await waitFor( |
| 702 | + () => { |
| 703 | + expect(states.length).toBeGreaterThanOrEqual(3) |
| 704 | + }, |
| 705 | + { timeout: 5000 }, |
| 706 | + ) |
| 707 | + |
| 708 | + // First poll: Success |
| 709 | + expect(states[0].isError).toBe(false) |
| 710 | + expect(states[0].currentResponse).toEqual({ member: member1, job: JOB_DATA }) |
| 711 | + |
| 712 | + // Second poll: Error |
| 713 | + expect(states[1].isError).toBe(true) |
| 714 | + expect(states[1].previousResponse).toEqual({}) // Should be preserved from acc |
| 715 | + expect(states[1].currentResponse).toEqual({ member: member1, job: JOB_DATA }) // Should be preserved from acc |
| 716 | + |
| 717 | + // Third poll: Success again |
| 718 | + expect(states[2].isError).toBe(false) |
| 719 | + expect(states[2].previousResponse).toEqual({ member: member1, job: JOB_DATA }) // acc.currentResponse was preserved |
| 720 | + expect(states[2].currentResponse).toEqual({ member: member1, job: JOB_DATA }) |
| 721 | + |
| 722 | + subscription.unsubscribe() |
| 723 | + }, 10000) |
644 | 724 | }) |
0 commit comments