testing conversation context in mobile app

This commit is contained in:
Roland Osborne 2023-02-14 21:25:39 -08:00
parent 50fa8bf9af
commit d9157e4c08
6 changed files with 565 additions and 120 deletions

View File

@ -66,7 +66,7 @@ export function useCardContext() {
const card = cards.current.get(cardId); const card = cards.current.get(cardId);
if (card) { if (card) {
card[field] = value; card[field] = value;
cards.set(cardId, { ...card }); cards.current.set(cardId, { ...card });
updateState({ cards: cards.current }); updateState({ cards: cards.current });
} }
}; };
@ -134,7 +134,27 @@ export function useCardContext() {
for (let card of delta) { for (let card of delta) {
if (card.data) { if (card.data) {
const item = setCardItem(card); const item = setCardItem(card);
const entry = cards.current.get(card.id) || { card: { cardId: card.id }, channels: new Map() }; const entry = cards.current.get(card.id) || { channels: new Map() };
if (!entry.card) {
const { cardId, detailRevision, profileRevision } = item;
const entryCard = { cardId, detailRevision, profileRevision };
if (item.detail) {
entryCard.detail = item.detail;
}
else {
entryCard.detail = await getCardDetail(server, token, card.id);
}
if (item.profile) {
entryCard.profile = item.profile;
}
else {
entryCard.profile = await getCardProfile(server, token, card.id);
}
await store.actions.setCardItem(guid, card.id, entryCard);
entry.card = entryCard;
cards.current.set(card.id, entry);
}
else {
const { profileRevision, detailRevision } = entry.card; const { profileRevision, detailRevision } = entry.card;
if (item.profileRevision !== profileRevision) { if (item.profileRevision !== profileRevision) {
if (item.profile) { if (item.profile) {
@ -156,6 +176,7 @@ export function useCardContext() {
entry.card.detailRevision = item.detailRevision; entry.card.detailRevision = item.detailRevision;
await store.actions.setCardItemDetail(guid, card.id, entry.card.detailRevision, entry.card.detail); await store.actions.setCardItemDetail(guid, card.id, entry.card.detailRevision, entry.card.detail);
} }
}
if (entry.card.detail?.status === 'connected' && !entry.card.offsync) { if (entry.card.detail?.status === 'connected' && !entry.card.offsync) {
try { try {
const { notifiedView, notifiedProfile, notifiedArticle, notifiedChannel } = item; const { notifiedView, notifiedProfile, notifiedArticle, notifiedChannel } = item;
@ -168,7 +189,7 @@ export function useCardContext() {
await store.action.setCardItemOffsync(guid, card.id); await store.action.setCardItemOffsync(guid, card.id);
} }
} }
cards.current.set(card.id, entry); cards.current.set(card.id, { ...entry });
} }
else { else {
const entry = cards.current.get(card.id) || { card: {}, channels: new Map() }; const entry = cards.current.get(card.id) || { card: {}, channels: new Map() };
@ -223,9 +244,19 @@ export function useCardContext() {
for (let channel of delta) { for (let channel of delta) {
if (channel.data) { if (channel.data) {
const channelItem = setCardChannelItem(channel); const channelItem = setCardChannelItem(channel);
const channelEntry = entry.channels.get(channel.id) || { channelId: channel.id }; const channelEntry = entry.channels.get(channel.id);
const { detailRevision, topicRevision } = channelEntry; if (!channelEntry) {
if (channelItem.detailRevision !== detailRevision) { if (!channelItem.detail) {
channelItem.detail = await getContactChannelDetail(cardServer, cardToken, channel.id);
}
if (!channelItem.summary) {
channelItem.summary = await getContactChannelSummary(cardServer, cardToken, channel.id);
}
await store.actions.setCardChannelItem(guid, cardId, channelItem);
entry.channels.set(channel.id, { ...channelItem });
}
else {
if (channelItem.detailRevision !== channelEntry.detailRevision) {
if (channelItem.detail) { if (channelItem.detail) {
channelEntry.detail = channelItem.detail; channelEntry.detail = channelItem.detail;
} }
@ -234,9 +265,9 @@ export function useCardContext() {
} }
channelEntry.unsealedDetail = null; channelEntry.unsealedDetail = null;
channelEntry.detailRevision = channelItem.detailRevision; channelEntry.detailRevision = channelItem.detailRevision;
await store.actions.setCardChannelItemDetail(guid, cardId, channel.id, channelItem.detailRevision, channelEntry.detail); await store.actions.setCardChannelItemDetail(guid, cardId, channel.id, channelEntry.detailRevision, channelEntry.detail);
} }
if (channelItem.topicRevision !== topicRevision) { if (channelItem.topicRevision !== channelEntry.topicRevision) {
if (channelItem.summary) { if (channelItem.summary) {
channelEntry.summary = channelItem.summary; channelEntry.summary = channelItem.summary;
} }
@ -245,9 +276,10 @@ export function useCardContext() {
} }
channelEntry.unsealedSummary = null; channelEntry.unsealedSummary = null;
channelEntry.topicRevision = channelItem.topicRevision; channelEntry.topicRevision = channelItem.topicRevision;
await store.actions.setCardChannelItemSummary(guid, cardId, channel.id, channelItem.topicRevision, channelEntry.summary); await store.actions.setCardChannelItemSummary(guid, cardId, channel.id, channelEntry.topicRevision, channelEntry.summary);
}
entry.channels.set(channel.id, { ...channelEntry });
} }
entry.channels.set(channel.id, channelEntry);
} }
else { else {
await store.actions.clearCardChannelTopicItems(guid, cardId, channel.id); await store.actions.clearCardChannelTopicItems(guid, cardId, channel.id);
@ -271,14 +303,12 @@ export function useCardContext() {
cards.current = new Map(); cards.current = new Map();
const cardItems = await store.actions.getCardItems(session.guid); const cardItems = await store.actions.getCardItems(session.guid);
for(card of cardItems) { for(card of cardItems) {
cards.current.set(card.cardId, { card, channels: new Map() }); const entry = { card, channels: new Map() };
} const cardChannelItems = await store.actions.getCardChannelItems(session.guid, card.cardId);
const cardChannelItems = await store.actions.getCardChannelItems(session.guid);
for (cardChannel of cardChannelItems) { for (cardChannel of cardChannelItems) {
const card = cards.current.get(cardChannel.cardId); entry.channels.set(cardChannel.channelId, cardChannel);
if (card) {
card.channels.set(card.channelId, cardChannel);
} }
cards.current.set(card.cardId, entry);
} }
const status = await store.actions.getCardRequestStatus(session.guid); const status = await store.actions.getCardRequestStatus(session.guid);
const revision = await store.actions.getCardRevision(session.guid); const revision = await store.actions.getCardRevision(session.guid);
@ -362,27 +392,27 @@ export function useCardContext() {
} }
}, },
removeTopic: async (cardId, channelId, topicId) => { removeTopic: async (cardId, channelId, topicId) => {
const { detail, profile } = cards.current.get(cardId) || {}; const { detail, profile } = (cards.current.get(cardId) || {}).card;
const cardToken = `${profile?.guid}.${detail?.token}`; const cardToken = `${profile?.guid}.${detail?.token}`;
return await removeContactChannelTopic(profile?.node, cardToken, channelId, topicId); return await removeContactChannelTopic(profile?.node, cardToken, channelId, topicId);
}, },
setTopicSubject: async (cardId, channelId, topicId, type, subject) => { setTopicSubject: async (cardId, channelId, topicId, type, subject) => {
const { detail, profile } = cards.current.get(cardId) || {}; const { detail, profile } = (cards.current.get(cardId) || {}).card;
const cardToken = `${profile?.guid}.${detail?.token}`; const cardToken = `${profile?.guid}.${detail?.token}`;
return await setContactChannelTopicSubject(profile?.node, cardToken, channelId, topicId, type, subject); return await setContactChannelTopicSubject(profile?.node, cardToken, channelId, topicId, type, subject);
}, },
getTopicAssetUrl: (cardId, channelId, topicId, assetId) => { getTopicAssetUrl: (cardId, channelId, topicId, assetId) => {
const { detail, profile } = cards.current.get(cardId) || {}; const { detail, profile } = (cards.current.get(cardId) || {}).card;
const cardToken = `${profile?.guid}.${detail?.token}`; const cardToken = `${profile?.guid}.${detail?.token}`;
return getContactChannelTopicAssetUrl(profile?.node, cardToken, channelId, topicId, assetId); return getContactChannelTopicAssetUrl(profile?.node, cardToken, channelId, topicId, assetId);
}, },
getTopics: async (cardId, channelId, revision, count, begin, end) => { getTopics: async (cardId, channelId, revision, count, begin, end) => {
const { detail, profile } = cards.current.get(cardId) || {}; const { detail, profile } = (cards.current.get(cardId) || {}).card;
const cardToken = `${profile?.guid}.${detail?.token}`; const cardToken = `${profile?.guid}.${detail?.token}`;
return await getContactChannelTopics(profile?.node, cardToken, channelId); return await getContactChannelTopics(profile?.node, cardToken, channelId);
}, },
getChannelTopic: async (cardId, channelId, topicId) => { getTopic: async (cardId, channelId, topicId) => {
const { detail, profile } = cards.current.get(cardId) || {}; const { detail, profile } = (cards.current.get(cardId) || {}).card;
const cardToken = `${profile?.guid}.${detail?.token}`; const cardToken = `${profile?.guid}.${detail?.token}`;
return await getContactChannelTopic(profile?.node, cardToken, channelId, topicId); return await getContactChannelTopic(profile?.node, cardToken, channelId, topicId);
}, },
@ -394,22 +424,22 @@ export function useCardContext() {
setChannelReadRevision: async (cardId, channelId, revision) => { setChannelReadRevision: async (cardId, channelId, revision) => {
const { guid } = access.current; const { guid } = access.current;
await store.actions.setCardChannelItemReadRevision(guid, cardId, channelId, revision); await store.actions.setCardChannelItemReadRevision(guid, cardId, channelId, revision);
setCardField(cardId, 'readRevision', revision); setCardChannelField(cardId, channelId, 'readRevision', revision);
}, },
setChannelSyncRevision: async (cardId, channelId, revision) => { setChannelSyncRevision: async (cardId, channelId, revision) => {
const { guid } = access.current; const { guid } = access.current;
await store.actions.setCardChannelItemSyncRevision(guid, cardId, channelId, revision); await store.actions.setCardChannelItemSyncRevision(guid, cardId, channelId, revision);
setCardField(cardId, 'syncRevision', revision); setCardChannelField(cardId, channelId, 'syncRevision', revision);
}, },
setChannelTopicMarker: async (cardId, channelId, marker) => { setChannelTopicMarker: async (cardId, channelId, marker) => {
const { guid } = access.current; const { guid } = access.current;
await store.actions.setCardChannelItemTopicMarker(guid, cardId, channelId, marker); await store.actions.setCardChannelItemTopicMarker(guid, cardId, channelId, marker);
setCardField(cardId, 'topicMarker', marker); setCardChannelField(cardId, channelId, 'topicMarker', marker);
}, },
setChannelMarkerAndSync: async (cardId, channelId, marker, revision) => { setChannelMarkerAndSync: async (cardId, channelId, marker, revision) => {
const { guid } = access.current; const { guid } = access.current;
await store.actions.setCardChannelItemMarkerAndSync(guid, cardId, channelId, marker, revision); await store.actions.setCardChannelItemMarkerAndSync(guid, cardId, channelId, marker, revision);
setCardField(cardId, 'topicMarker', marker, 'syncRevision', revision); setCardChannelField(cardId, channelId, 'topicMarker', marker, 'syncRevision', revision);
}, },
setCardFlag: async (cardId) => { setCardFlag: async (cardId) => {
const { guid } = acccess.current; const { guid } = acccess.current;
@ -440,20 +470,20 @@ export function useCardContext() {
await store.actions.setCardChannelTopicBlocked(guid, cardId, channelId, topicId, false); await store.actions.setCardChannelTopicBlocked(guid, cardId, channelId, topicId, false);
}, },
addChannelAlert: async (cardId, channelId) => { addChannelAlert: async (cardId, channelId) => {
const { profile } = cards.current.get(cardId) || {}; const { detail, profile } = (cards.current.get(cardId) || {}).card;
return await addFlag(profile?.node, profile?.guid, channelId); return await addFlag(profile?.node, profile?.guid, channelId);
}, },
addTopicAlert: async (cardId, channelId, topicId) => { addTopicAlert: async (cardId, channelId, topicId) => {
const { detail, profile } = cards.current.get(cardId) || {}; const { detail, profile } = (cards.current.get(cardId) || {}).card;
return await addFlag(profile?.node, profile?.guid, channelId, topicId); return await addFlag(profile?.node, profile?.guid, channelId, topicId);
}, },
getChannelNotifications: async (cardId, channelId) => { getChannelNotifications: async (cardId, channelId) => {
const { detail, profile } = cards.current.get(cardId) || {}; const { detail, profile } = (cards.current.get(cardId) || {}).card;
const token = `${profile?.guid}.${detail?.token}`; const token = `${profile?.guid}.${detail?.token}`;
return await getContactChannelNotifications(profile?.node, token, channelId); return await getContactChannelNotifications(profile?.node, token, channelId);
}, },
setChannelNotifications: async (cardId, channelId, notify) => { setChannelNotifications: async (cardId, channelId, notify) => {
const { detail, profile } = cards.current.get(cardId) || {}; const { detail, profile } = (cards.current.get(cardId) || {}).card;
const token = `${profile?.guid}.${detail?.token}`; const token = `${profile?.guid}.${detail?.token}`;
return await setContactChannelNotifications(profile?.node, token, channelId, notify); return await setContactChannelNotifications(profile?.node, token, channelId, notify);
}, },

View File

@ -48,12 +48,12 @@ export function useChannelContext() {
} }
const setChannelField = (channelId, field, value, field2, value2) => { const setChannelField = (channelId, field, value, field2, value2) => {
const channel = channels.get(channelId) || {}; const channel = channels.current.get(channelId) || {};
channel[field] = value; channel[field] = value;
if (field2) { if (field2) {
channel[field2] = value2; channel[field2] = value2;
} }
channels.set(channelId, { ...channel }); channels.current.set(channelId, { ...channel });
updateState({ channels: channels.current }); updateState({ channels: channels.current });
}; };
@ -67,30 +67,31 @@ export function useChannelContext() {
for (let channel of delta) { for (let channel of delta) {
if (channel.data) { if (channel.data) {
const item = setChannelItem(channel); const item = setChannelItem(channel);
if (item.detail && item.summary) { const entry = channels.current.get(channel.id);
await store.actions.setChannelItem(guid, item); if (!entry) {
channels.current.set(item.channelId, item); if (!item.detail) {
item.detail = await getChannelDetail(server, token, channel.id);
} }
else { if (!item.summary) {
const { channelId, detailRevision, topicRevision, detail, summary } = channels.current.get(channel.id) || {} item.summary = await getChannelSummary(server, token, channel.id);
if (item.detailRevision !== detailRevision) {
item.detailRevision = detailRevision;
item.detail = await getChannelDetail(server, token, channelId);
await store.actions.setChannelItemDetail(guid, channelId, detailRevision, item.detail);
}
else {
item.datail = detail;
}
if (item.topicRevision !== topicRevision) {
item.topicRevision = topicRevision;
item.summary = await getChannelSummary(server, token, item.channelId);
await store.actions.setChannelItemSummary(guid, channelId, topicRevision, item.summary);
}
else {
item.summary = summary;
} }
await store.actions.setChannelItem(guid, item); await store.actions.setChannelItem(guid, item);
channels.current.set(channelId, item); channels.current.set(channel.id, item);
}
else {
if (item.detailRevision !== entry.detailRevision) {
entry.detail = await getChannelDetail(server, token, channel.id);
entry.unsealedDetail = null;
entry.detailRevision = item.detailRevision;
await store.actions.setChannelItemDetail(guid, channel.id, entry.detailRevision, entry.detail);
}
if (item.topicRevision !== entry.topicRevision) {
entry.summary = await getChannelSummary(server, token, channel.id);
entry.unsealedSummary = null;
entry.topicRevision = item.topicRevision;
await store.actions.setChannelItemSummary(guid, channel.id, entry.topicRevision, entry.summary);
}
channels.current.set(channel.id, { ...entry });
} }
} }
else { else {
@ -192,7 +193,7 @@ export function useChannelContext() {
const { server, token } = access.current; const { server, token } = access.current;
getChannelTopicAssetUrl(server, token, channelId, topicId, assetId); getChannelTopicAssetUrl(server, token, channelId, topicId, assetId);
}, },
getTopics: async (channelId) => { getTopics: async (channelId, revision, count, begin, end) => {
const { server, token } = access.current; const { server, token } = access.current;
return await getChannelTopics(server, token, channelId, revision, count, begin, end); return await getChannelTopics(server, token, channelId, revision, count, begin, end);
}, },

View File

@ -33,11 +33,11 @@ export function useConversationContext() {
} }
const sync = async () => { const sync = async () => {
if (!syncing.current && (reset.current || update.current || force.current || more.current)) {
if (!syncing.current && (reset.current || update.current || force.current || more.current)) {
const loadMore = more.current; const loadMore = more.current;
const ignoreRevision = force.current; const ignoreRevision = force.current;
const conversation = converstaionId.current; const conversation = conversationId.current;
let curRevision, setRevision, marker; let curRevision, setRevision, marker;
syncing.current = true; syncing.current = true;
@ -55,9 +55,11 @@ export function useConversationContext() {
if (conversation) { if (conversation) {
const { cardId, channelId } = conversation; const { cardId, channelId } = conversation;
const cardValue = cardId ? card.state.cards.get(cardId) : null; const cardValue = cardId ? card.state.cards.get(cardId) : null;
const channelValue = cardId ? cardValue?.get(channelId) : channel.state.channels.get(channelId); const channelValue = cardId ? cardValue?.channels.get(channelId) : channel.state.channels.get(channelId);
if (channelValue) { if (channelValue) {
const { topicRevision, syncRevision, topicMarker } = channelValue; const { topicRevision, syncRevision, topicMarker } = channelValue;
curRevision = topicRevision; curRevision = topicRevision;
setRevision = syncRevision; setRevision = syncRevision;
marker = topicMarker; marker = topicMarker;
@ -78,21 +80,22 @@ export function useConversationContext() {
return; return;
} }
if (ignoreRevision || curRevision !== setRevision) {
try { try {
if (!marker) { if (!marker) {
const delta = await getTopicDelta(cardId, channelId, null, COUNT, null, null); const delta = await getTopicDelta(cardId, channelId, null, COUNT, null, null);
await setTopicDelta(cardId, channelId, delta.topics); await setTopicDelta(cardId, channelId, delta.topics);
setMarkerAndSync(cardId, channelId, topicMarker, topicRevision); await setMarkerAndSync(cardId, channelId, delta.marker, curRevision);
} }
if (loadMore && marker) { if (loadMore && marker) {
const delta = await getTopicDelta(cardId, channelId, null, COUNT, null, marker); const delta = await getTopicDelta(cardId, channelId, null, COUNT, null, marker);
await setTopicDelta(cardId, channelId, delta.topics); await setTopicDelta(cardId, channelId, delta.topics);
setTopicMarker(cardId, channelId, delta.topicMarker); await setTopicMarker(cardId, channelId, delta.marker);
} }
if (ignoreRevision || curTopicRevision.current !== setTopicRevision.current) { if (ignoreRevision || curRevision !== setRevision) {
const delta = await getTopicDelta(cardId, channelId, setRevision, null, marker, null); const delta = await getTopicDelta(cardId, channelId, setRevision, null, marker, null);
await setTopicDelta(cardId, channelId, delta.topics); await setTopicDelta(cardId, channelId, delta.topics);
setSyncRevision(cardid, channelId, delta.topicRevision); await setSyncRevision(cardId, channelId, curRevision);
} }
} }
catch(err) { catch(err) {
@ -101,12 +104,13 @@ export function useConversationContext() {
syncing.current = false; syncing.current = false;
return return
} }
}
}
syncing.current = false; syncing.current = false;
await sync(); await sync();
} }
} }
}
const setTopicDelta = async (cardId, channelId, entries) => { const setTopicDelta = async (cardId, channelId, entries) => {
for (let entry of entries) { for (let entry of entries) {
@ -354,7 +358,7 @@ export function useConversationContext() {
if (cardId) { if (cardId) {
return await card.actions.setChannelSyncRevision(cardId, channelId, revision); return await card.actions.setChannelSyncRevision(cardId, channelId, revision);
} }
return await channel.actions.setSyncRevision(channelid, revision); return await channel.actions.setSyncRevision(channelId, revision);
} }
const setMarkerAndSync = async (cardId, channelId, marker, revision) => { const setMarkerAndSync = async (cardId, channelId, marker, revision) => {
@ -378,7 +382,7 @@ export function useConversationContext() {
return await channel.actions.getTopic(channelId, topicId); return await channel.actions.getTopic(channelId, topicId);
} }
const mapTopicItem = (entry) => { const mapTopicEntry = (entry) => {
return { return {
topicId: entry.id, topicId: entry.id,
revision: entry.revision, revision: entry.revision,
@ -392,7 +396,7 @@ export function useConversationContext() {
if (topic) { if (topic) {
topic[field] = value; topic[field] = value;
} }
topics.current.set(topicId, topic); topics.current.set(topicId, { ...topic });
updateState({ topics: topics.current }); updateState({ topics: topics.current });
}; };

View File

@ -296,10 +296,9 @@ export function useStoreContext() {
setCardChannelItemUnsealedSummary: async (guid, cardId, channelId, revision, unsealed) => { setCardChannelItemUnsealedSummary: async (guid, cardId, channelId, revision, unsealed) => {
await db.current.executeSql(`UPDATE card_channel_${guid} set unsealed_summary=? where topic_revision=? AND card_id=? AND channel_id=?`, [encodeObject(unsealed), revision, cardId, channelId]); await db.current.executeSql(`UPDATE card_channel_${guid} set unsealed_summary=? where topic_revision=? AND card_id=? AND channel_id=?`, [encodeObject(unsealed), revision, cardId, channelId]);
}, },
getCardChannelItems: async (guid) => { getCardChannelItems: async (guid, cardId) => {
const values = await getAppValues(db.current, `SELECT card_id, channel_id, read_revision, sync_revision, revision, blocked, detail_revision, topic_revision, topic_marker, detail, unsealed_detail, summary, unsealed_summary FROM card_channel_${guid}`, []); const values = await getAppValues(db.current, `SELECT channel_id, read_revision, sync_revision, revision, blocked, detail_revision, topic_revision, topic_marker, detail, unsealed_detail, summary, unsealed_summary FROM card_channel_${guid} where card_id=?`, [cardId]);
return values.map(channel => ({ return values.map(channel => ({
cardId: channel.card_id,
channelId: channel.channel_id, channelId: channel.channel_id,
revision: channel.revision, revision: channel.revision,
readRevision: channel.read_revision, readRevision: channel.read_revision,

View File

@ -19,11 +19,16 @@ function ConversationView() {
useEffect(() => { useEffect(() => {
setRenderCount(renderCount + 1); setRenderCount(renderCount + 1);
const rendered = []; const rendered = [];
conversation.state.topics.forEach((value) => {
rendered.push(<Text key={value.topicId} testID={value.topicId}>{ value.detail.data }</Text>);
});
setTopics(rendered);
}, [conversation.state]); }, [conversation.state]);
return ( return (
<View key="conversation" testID="conversation" renderCount={renderCount} <View key="conversation" testID="conversation" renderCount={renderCount}
card={card} channel={channel} conversation={conversation}> card={card} channel={channel} conversation={conversation}>
{ topics }
</View> </View>
); );
} }
@ -40,10 +45,20 @@ function ConversationTestApp() {
) )
} }
let fetchCards;
let fetchChannels;
let fetchCardChannels;
let fetchTopics;
const realUseContext = React.useContext; const realUseContext = React.useContext;
const realFetchWithTimeout = fetchUtil.fetchWithTimeout; const realFetchWithTimeout = fetchUtil.fetchWithTimeout;
const realFetchWithCustomTimeout = fetchUtil.fetchWithCustomTimeout; const realFetchWithCustomTimeout = fetchUtil.fetchWithCustomTimeout;
beforeEach(() => { beforeEach(() => {
fetchCards = [];
fetchChannels = [];
fetchCardChannels = [];
const mockUseContext = jest.fn().mockImplementation((ctx) => { const mockUseContext = jest.fn().mockImplementation((ctx) => {
if (ctx === StoreContext) { if (ctx === StoreContext) {
return useTestStoreContext(); return useTestStoreContext();
@ -53,9 +68,39 @@ beforeEach(() => {
React.useContext = mockUseContext; React.useContext = mockUseContext;
const mockFetch = jest.fn().mockImplementation((url, options) => { const mockFetch = jest.fn().mockImplementation((url, options) => {
if (url.startsWith('https://test.org/content/channels?agent')) {
return Promise.resolve({
json: () => Promise.resolve(fetchChannels)
});
}
else if (url.startsWith('https://test.org/contact/cards?agent')) {
return Promise.resolve({
json: () => Promise.resolve(fetchCards)
});
}
else if (url.startsWith('https://test.org/content/channels?contact')) {
return Promise.resolve({
json: () => Promise.resolve(fetchCardChannels)
});
}
else if (url.startsWith('https://test.org/content/channels/aabb/topics?contact') ||
url.startsWith('https://test.org/content/channels/123/topics?agent')) {
const headers = new Map();
headers.set('topic-marker', 48);
headers.set('topic-revision', 55);
return Promise.resolve({
url: 'getTopics',
status: 200,
headers: headers,
json: () => Promise.resolve(fetchTopics),
});
}
else {
return Promise.resolve({ return Promise.resolve({
json: () => Promise.resolve([]) json: () => Promise.resolve([])
}); });
}
}); });
fetchUtil.fetchWithTimeout = mockFetch; fetchUtil.fetchWithTimeout = mockFetch;
fetchUtil.fetchWithCustomTimeout = mockFetch; fetchUtil.fetchWithCustomTimeout = mockFetch;
@ -67,7 +112,7 @@ afterEach(() => {
fetchUtil.fetchWithCustomTimeout = realFetchWithCustomTimeout; fetchUtil.fetchWithCustomTimeout = realFetchWithCustomTimeout;
}); });
test('test conversation', async () => { test('add, update, remove card channel topic', async () => {
render(<ConversationTestApp />) render(<ConversationTestApp />)
@ -81,5 +126,362 @@ test('test conversation', async () => {
await card.actions.setRevision(1); await card.actions.setRevision(1);
//const conversation = screen.getByTestId('conversation').props.conversation; //const conversation = screen.getByTestId('conversation').props.conversation;
}); });
await waitFor(async () => {
expect(screen.getByTestId('conversation').props.children).toHaveLength(0);
});
fetchChannels = [
{ id: '123', revision: 2, data: {
detailRevision: 3,
topicRevision: 5,
channelSummary: { guid: '11', dataType: 'superbasictopic', data: 'testdata' },
channelDetail: { dataType: 'superbasic', data: 'testdata' },
}
},
];
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' },
},
}];
fetchCardChannels = [
{ id: 'aabb', revision: 2, data: {
detailRevision: 3,
topicRevision: 5,
channelSummary: { guid: '11', dataType: 'superbasictopic', data: 'testcardtopic' },
channelDetail: { dataType: 'superbasic', data: 'testcardchannel' },
}
},
];
fetchTopics = [
{ id: '888', revision: 5, data: {
detailRevision: 3,
tagRevision: 0,
topicDetail: {
guid: '0123',
dataType: 'topictype',
data: 'contacttopicdata',
created: 1,
updated: 1,
status: 'confirmed',
transform: 'complete',
},
}
}
];
await act(async () => {
const conversation = screen.getByTestId('conversation').props.conversation;
conversation.actions.setConversation('000a', 'aabb');
const channel = screen.getByTestId('conversation').props.channel;
await channel.actions.setRevision(2);
const card = screen.getByTestId('conversation').props.card;
await card.actions.setRevision(2);
});
await waitFor(async () => {
expect(screen.getByTestId('conversation').props.children).toHaveLength(1);
expect(screen.getByTestId('888').props.children).toBe('contacttopicdata');
});
fetchCards = [{
id: '000a',
revision: 2,
data: {
detailRevision: 2,
profileRevision: 3,
notifiedProfile: 3,
notifiedArticle: 5,
notifiedChannel: 7,
notifiedView: 7,
},
}];
fetchCardChannels = [
{ id: 'aabb', revision: 3, data: {
detailRevision: 3,
topicRevision: 6,
}
},
];
fetchTopics = [
{ id: '888', revision: 6, data: {
detailRevision: 4,
tagRevision: 0,
topicDetail: {
guid: '0123',
dataType: 'topictype',
data: 'contacttopicdata2',
created: 1,
updated: 1,
status: 'confirmed',
transform: 'complete',
},
}
}
];
await act(async () => {
const card = screen.getByTestId('conversation').props.card;
await card.actions.setRevision(3);
});
await waitFor(async () => {
expect(screen.getByTestId('conversation').props.children).toHaveLength(1);
expect(screen.getByTestId('888').props.children).toBe('contacttopicdata2');
});
fetchCards = [{
id: '000a',
revision: 3,
data: {
detailRevision: 2,
profileRevision: 3,
notifiedProfile: 3,
notifiedArticle: 5,
notifiedChannel: 8,
notifiedView: 7,
},
}];
fetchCardChannels = [
{ id: 'aabb', revision: 4, data: {
detailRevision: 3,
topicRevision: 6,
}
},
];
fetchTopics = [
{ id: '888', revision: 6 }
];
await act(async () => {
const card = screen.getByTestId('conversation').props.card;
await card.actions.setRevision(4);
});
await waitFor(async () => {
expect(screen.getByTestId('conversation').props.children).toHaveLength(1);
});
fetchCards = [{
id: '000a',
revision: 4,
data: {
detailRevision: 2,
profileRevision: 3,
notifiedProfile: 3,
notifiedArticle: 5,
notifiedChannel: 9,
notifiedView: 7,
},
}];
fetchCardChannels = [
{ id: 'aabb', revision: 5, data: {
detailRevision: 3,
topicRevision: 7,
}
},
];
await act(async () => {
const card = screen.getByTestId('conversation').props.card;
await card.actions.setRevision(5);
});
await waitFor(async () => {
expect(screen.getByTestId('conversation').props.children).toHaveLength(0);
});
});
test('add, update, remove channel topic', async () => {
render(<ConversationTestApp />)
await act(async () => {
const channel = screen.getByTestId('conversation').props.channel;
await channel.actions.setSession({ guid: 'abc', server: 'test.org', token: '123' });
await channel.actions.setRevision(1);
const card = screen.getByTestId('conversation').props.card;
await card.actions.setSession({ guid: 'abc', server: 'test.org', token: '123' });
await card.actions.setRevision(1);
//const conversation = screen.getByTestId('conversation').props.conversation;
});
await waitFor(async () => {
expect(screen.getByTestId('conversation').props.children).toHaveLength(0);
});
fetchChannels = [
{ id: '123', revision: 2, data: {
detailRevision: 3,
topicRevision: 5,
channelSummary: { guid: '11', dataType: 'superbasictopic', data: 'testdata' },
channelDetail: { dataType: 'superbasic', data: 'testdata' },
}
},
];
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' },
},
}];
fetchCardChannels = [
{ id: 'aabb', revision: 2, data: {
detailRevision: 3,
topicRevision: 5,
channelSummary: { guid: '11', dataType: 'superbasictopic', data: 'testcardtopic' },
channelDetail: { dataType: 'superbasic', data: 'testcardchannel' },
}
},
];
fetchTopics = [
{ id: '888', revision: 5, data: {
detailRevision: 3,
tagRevision: 0,
topicDetail: {
guid: '0123',
dataType: 'topictype',
data: 'contacttopicdata',
created: 1,
updated: 1,
status: 'confirmed',
transform: 'complete',
},
}
}
];
await act(async () => {
const conversation = screen.getByTestId('conversation').props.conversation;
conversation.actions.setConversation(null, '123');
const channel = screen.getByTestId('conversation').props.channel;
await channel.actions.setRevision(2);
const card = screen.getByTestId('conversation').props.card;
await card.actions.setRevision(2);
});
await waitFor(async () => {
expect(screen.getByTestId('conversation').props.children).toHaveLength(1);
expect(screen.getByTestId('888').props.children).toBe('contacttopicdata');
});
fetchChannels = [
{ id: '123', revision: 3, data: {
detailRevision: 3,
topicRevision: 6,
}
},
];
fetchTopics = [
{ id: '888', revision: 7, data: {
detailRevision: 5,
tagRevision: 0,
topicDetail: {
guid: '0123',
dataType: 'topictype',
data: 'contacttopicdata2',
created: 1,
updated: 1,
status: 'confirmed',
transform: 'complete',
},
}
}
];
await act(async () => {
const channel = screen.getByTestId('conversation').props.channel;
await channel.actions.setRevision(3);
});
await waitFor(async () => {
expect(screen.getByTestId('conversation').props.children).toHaveLength(1);
expect(screen.getByTestId('888').props.children).toBe('contacttopicdata2');
});
fetchChannels = [
{ id: '123', revision: 4, data: {
detailRevision: 3,
topicRevision: 6,
}
},
];
fetchTopics = [
{ id: '888', revision: 8 }
];
await act(async () => {
const channel = screen.getByTestId('conversation').props.channel;
await channel.actions.setRevision(4);
});
await waitFor(async () => {
expect(screen.getByTestId('conversation').props.children).toHaveLength(1);
});
fetchChannels = [
{ id: '123', revision: 5, data: {
detailRevision: 3,
topicRevision: 7,
}
},
];
await act(async () => {
const channel = screen.getByTestId('conversation').props.channel;
await channel.actions.setRevision(5);
});
await waitFor(async () => {
expect(screen.getByTestId('conversation').props.children).toHaveLength(0);
});
}); });

View File

@ -123,6 +123,8 @@ export function useTestStoreContext() {
}, },
setChannelItemTopicMarker: async (guid, channelId, marker) => { setChannelItemTopicMarker: async (guid, channelId, marker) => {
}, },
setChannelItemMarkerAndSync: async (guid, channelId, marker, revision) => {
},
setChannelItemBlocked: async (guid, channelId) => { setChannelItemBlocked: async (guid, channelId) => {
}, },
clearChannelItemBlocked: async (guid, channelId) => { clearChannelItemBlocked: async (guid, channelId) => {
@ -143,6 +145,7 @@ export function useTestStoreContext() {
}, },
getChannelTopicItems: async (guid, channelId) => { getChannelTopicItems: async (guid, channelId) => {
return [];
}, },
setChannelTopicItem: async (guid, channelId, topic) => { setChannelTopicItem: async (guid, channelId, topic) => {
}, },
@ -158,7 +161,9 @@ export function useTestStoreContext() {
}, },
setCardChannelItem: async (guid, cardId, channel) => { setCardChannelItem: async (guid, cardId, channel) => {
cardChannelItems.current.set(`${cardId}:${channel.channelId}`, channel); const card = cardChannels.current.get(cardId) || new Map();
card.set(channel.channelId, channel);
cardChannels.current.set(cardId, card);
}, },
clearCardChannelItem: async (guid, cardId, channelId) => { clearCardChannelItem: async (guid, cardId, channelId) => {
}, },
@ -170,6 +175,8 @@ export function useTestStoreContext() {
}, },
setCardChannelItemTopicMarker: async (guid, cardId, channelId, marker) => { setCardChannelItemTopicMarker: async (guid, cardId, channelId, marker) => {
}, },
setCardChannelItemMarkerAndSync: async (guid, cardid, channelId, marker, revision) => {
},
setCardChannelItemDetail: async (guid, cardId, channelId, revision, detail) => { setCardChannelItemDetail: async (guid, cardId, channelId, revision, detail) => {
}, },
setCardChannelItemUnsealedDetail: async (guid, cardId, channelId, revision, unsealed) => { setCardChannelItemUnsealedDetail: async (guid, cardId, channelId, revision, unsealed) => {
@ -180,13 +187,15 @@ export function useTestStoreContext() {
}, },
getCardChannelItemView: async (guid, cardId, channelId) => { getCardChannelItemView: async (guid, cardId, channelId) => {
}, },
getCardChannelItems: async (guid) => { getCardChannelItems: async (guid, cardId) => {
return Array.from(cardChannels.current.values()); const card = cardChannels.current.get(cardId) || new Map();
return Array.from(card.values());
}, },
clearCardChannelItems: async (guid, cardId) => { clearCardChannelItems: async (guid, cardId) => {
}, },
getCardChannelTopicItems: async (guid, cardId, channelId) => { getCardChannelTopicItems: async (guid, cardId, channelId) => {
return [];
}, },
setCardChannelTopicItem: async (guid, cardId, channelId, topic) => { setCardChannelTopicItem: async (guid, cardId, channelId, topic) => {
}, },