From 130e9f276c190eaaaba104a550f38342d9be2b43 Mon Sep 17 00:00:00 2001 From: Roland Osborne Date: Tue, 7 Feb 2023 15:27:58 -0800 Subject: [PATCH] testing refactored card context from mobile app --- app/mobile/src/context/useCardContext.hook.js | 19 +- app/mobile/test/Card.test.js | 186 ++++++++++++++++++ 2 files changed, 199 insertions(+), 6 deletions(-) create mode 100644 app/mobile/test/Card.test.js diff --git a/app/mobile/src/context/useCardContext.hook.js b/app/mobile/src/context/useCardContext.hook.js index 88c224f2..0b58c239 100644 --- a/app/mobile/src/context/useCardContext.hook.js +++ b/app/mobile/src/context/useCardContext.hook.js @@ -143,16 +143,26 @@ export function useCardContext() { for (let card of delta) { if (card.data) { const item = setCardItem(card); - const entry = cards.current.get(card.id) || { card: {}, channels: new Map() }; + const entry = cards.current.get(card.id) || { card: { cardId: card.id }, channels: new Map() }; const { profileRevision, detailRevision } = entry.card; if (item.profileRevision !== profileRevision) { + if (item.profile) { + entry.card.profile = item.profile; + } + else { + entry.card.profile = await getCardProfile(server, token, card.id); + } entry.card.profileRevision = item.profileRevision; - entry.card.profile = await getCardProfile(server, token, card.id); await store.actions.setCardItemProfile(guid, card.id, entry.card.profileRevision, entry.card.profile); } if (item.detailRevision !== detailRevision) { + if (item.detail) { + entry.card.detail = item.detail; + } + else { + entry.card.detail = await getCardDetail(server, token, card.id); + } entry.card.detailRevision = item.detailRevision; - entry.card.detail = await getCardDetail(server, token, card.id); await store.actions.setCardItemDetail(guid, card.id, entry.card.detailRevision, entry.card.detail); } if (entry.card.detail?.state === 'connected' && !entry.card.offsync) { @@ -427,7 +437,6 @@ export function useCardContext() { const { detail, profile } = cards.current.get(cardId) || {}; return await addFlag(profile?.node, profile?.guid, channelId, topicId); }, - getChannelNotifications: async (cardId, channelId) => { const { detail, profile } = cards.current.get(cardId) || {}; const token = `${profile?.guid}.${detail?.token}`; @@ -438,7 +447,6 @@ export function useCardContext() { const token = `${profile?.guid}.${detail?.token}`; return await setContactChannelNotifications(profile?.node, token, channelId, notify); }, - getTopicItems: async (cardId, channelId) => { const { guid } = access.current; return await store.actions.getCardChannelTopicItems(guid, cardId, channelId); @@ -455,7 +463,6 @@ export function useCardContext() { const { guid } = access.current; return await store.actions.clearCardChannelTopicItems(guid, cardId, channelId); }, - setUnsealedChannelSubject: async (cardId, channelId, revision, unsealed) => { const { guid } = access.current; await store.actions.setCardChannelItemUnsealedDetail(guid, cardId, channelId, revision, unsealed); diff --git a/app/mobile/test/Card.test.js b/app/mobile/test/Card.test.js new file mode 100644 index 00000000..29f05dcc --- /dev/null +++ b/app/mobile/test/Card.test.js @@ -0,0 +1,186 @@ +import React, { useState, useEffect, useContext } from 'react'; +import { View, Text } from 'react-native'; +import { useTestStoreContext } from './useTestStoreContext.hook'; +import { prettyDOM } from '@testing-library/dom'; +import {render, act, screen, waitFor, fireEvent} from '@testing-library/react-native'; +import { CardContextProvider, CardContext } from 'context/CardContext'; +import * as fetchUtil from 'api/fetchUtil'; + +function CardView() { + const [renderCount, setRenderCount] = useState(0); + const card = useContext(CardContext); + const [cards, setCards] = useState([]); + + useEffect(() => { + setRenderCount(renderCount + 1); + const rendered = []; + card.state.cards.forEach((value) => { + rendered.push({ value.card.profile.handle }); + }); + setCards(rendered); + }, [card.state]); + + return ( + + { cards } + + ); +} + +function CardTestApp() { + return ( + + + + ) +} + +const realUseContext = React.useContext; +const realFetchWithTimeout = fetchUtil.fetchWithTimeout; +const realFetchWithCustomTimeout = fetchUtil.fetchWithCustomTimeout; + +let fetchCards; +let fetchDetail; +let fetchProfile; +beforeEach(() => { + fetchCards = []; + + const mockUseContext = jest.fn().mockImplementation((ctx) => { + return useTestStoreContext(); + }); + React.useContext = mockUseContext; + + const mockFetch = jest.fn().mockImplementation((url, options) => { + console.log(url); + + if (url.startsWith('https://test.org/contact/cards?agent')) { + return Promise.resolve({ + json: () => Promise.resolve(fetchCards) + }); + } + if (url.startsWith('https://test.org/contact/cards/000a/profile?agent')) { + return Promise.resolve({ + json: () => Promise.resolve(fetchProfile) + }); + } + if (url.startsWith('https://test.org/contact/cards/000a/detail?agent')) { + return Promise.resolve({ + json: () => Promise.resolve(fetchDetail) + }); + } + else { + return Promise.resolve({ + json: () => Promise.resolve([]) + }); + } + }); + fetchUtil.fetchWithTimeout = mockFetch; + fetchUtil.fetchWithCustomTimeout = mockFetch; + +}); + +afterEach(() => { + React.useContext = realUseContext; + fetchUtil.fetchWithTimeout = realFetchWithTimeout; + fetchUtil.fetchWithCustomTimeout = realFetchWithCustomTimeout; +}); + +test('add, update, and remove', async () => { + + render() + + await act(async () => { + const card = screen.getByTestId('card').props.card; + await card.actions.setSession({ guid: 'abc', server: 'test.org', token: '123' }); + await card.actions.setRevision(1); + }); + + await waitFor(async () => { + expect(screen.getByTestId('card').props.children).toHaveLength(0); + }); + + fetchCards = [{ + id: '000a', + revision: 1, + data: { + detailRevision: 2, + profileRevision: 3, + notifiedProfile: 3, + notifiedArticle: 5, + notifiedChannel: 6, + notifiedView: 7, + cardDetail: { status: 'connected', statusUpdate: 136, token: '01ab', }, + cardProfile: { guid: '01ab23', handle: 'test1', name: 'tester', imageSet: false, + seal: 'abc', version: '1.1.1', node: 'test.org' }, + }, + }]; + + await act(async () => { + const card = screen.getByTestId('card').props.card; + await card.actions.setRevision(2); + }); + + await waitFor(async () => { + expect(screen.getByTestId('card').props.children).toHaveLength(1); + expect(screen.getByTestId('000a').props.children).toBe('test1'); + }); + + fetchCards = [{ + id: '000a', + revision: 2, + data: { + detailRevision: 3, + profileRevision: 4, + notifiedProfile: 3, + notifiedArticle: 5, + notifiedChannel: 6, + notifiedView: 7, + }, + }]; + + fetchProfile = { + guid: '01ab23', + handle: 'test2', + }; + + fetchDetail = { + status: 'confirmed', + } + + await act(async () => { + const card = screen.getByTestId('card').props.card; + await card.actions.setRevision(3); + }); + + await waitFor(async () => { + expect(screen.getByTestId('card').props.children).toHaveLength(1); + expect(screen.getByTestId('000a').props.children).toBe('test2'); + }); + + fetchCards = [{ + id: '000a', + revision: 3, + }]; + + await act(async () => { + const card = screen.getByTestId('card').props.card; + await card.actions.setRevision(3); + }); + + await waitFor(async () => { + expect(screen.getByTestId('card').props.children).toHaveLength(1); + }); + + await act(async () => { + const card = screen.getByTestId('card').props.card; + await card.actions.setRevision(4); + }); + + await waitFor(async () => { + expect(screen.getByTestId('card').props.children).toHaveLength(0); + }); + +}); + + +