refactoring channel context in mobile app

This commit is contained in:
balzack 2023-02-02 23:14:49 -08:00
parent e8902492d4
commit 69b659409a
6 changed files with 236 additions and 340 deletions

View File

@ -60,7 +60,7 @@ export function useAppContext() {
await profile.actions.setSession(access);
await card.actions.setSession(access);
await channel.actions.setSession(access);
updateState({ session: true, server: access.server, appToken: access.appToken,
updateState({ session: true, server: access.server, token: access.appToken,
loginTimestamp: access.created });
setWebsocket(access.server, access.appToken);
}

View File

@ -18,151 +18,83 @@ import { clearChannelCard } from 'api/clearChannelCard';
import { addFlag } from 'api/addFlag';
import { setChannelNotifications } from 'api/setChannelNotifications';
import { getChannelNotifications } from 'api/getChannelNotifications';
import CryptoJS from "crypto-js";
import { RSA } from 'react-native-rsa-native';
export function useChannelContext() {
const [state, setState] = useState({
offsync: false,
channels: new Map(),
});
const store = useContext(StoreContext);
const upload = useContext(UploadContext);
const session = useRef(null);
const curRevision = useRef(null);
const access = useRef(null);
const setRevision = useRef(null);
const syncing = useRef(false);
const curRevision = useRef(null);
const channels = useRef(new Map());
const syncing = useRef(false);
const force = useRef(false);
const store = useContext(StoreContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }))
}
const unsealKey = async (seals, sealKey) => {
let seal;
if (seals?.length) {
seals.forEach(s => {
if (s.publicKey === sealKey.public) {
seal = s;
}
});
const setChannelItem = (channel) => {
return {
cardId: channel.id,
revision: channel.revision,
detailRevision: channel.data?.detailRevision,
topicRevision: channel.data?.topicRevision,
detail: channel.data?.channelDetail,
summary: channel.data?.channelSummary,
}
if (seal) {
const key = '-----BEGIN RSA PRIVATE KEY-----\n' + sealKey.private + '\n-----END RSA PRIVATE KEY-----'
return await RSA.decrypt(seal.sealedKey, key);
}
return null;
}
const setChannelField = (channelId, field, value) => {
const channel = channels.get(channelId) || {};
channel[field] = value;
channels.set(channelId, { ...channel });
updateState({ channels: channels.current });
};
const setChannel = (channelId, channel) => {
let update = channels.current.get(channelId);
if (!update) {
update = { readRevision: 0 };
const resync = async () => {
try {
force.current = true;
await sync();
}
update.channelId = channel?.id;
update.revision = channel?.revision;
update.detail = channel?.data?.channelDetail;
update.unsealedDetail = null;
update.summary = channel?.data?.channelSummary;
update.unsealedSummary = null;
update.detailRevision = channel?.data?.detailRevision;
update.topicRevision = channel?.data?.topicRevision;
channels.current.set(channelId, update);
}
const setChannelDetail = (channelId, detail, revision) => {
let channel = channels.current.get(channelId);
if (channel) {
channel.detail = detail;
channel.unsealedDetail = null;
channel.detailRevision = revision;
channels.current.set(channelId, channel);
catch (err) {
console.log(err);
}
}
const setChannelSummary = (channelId, summary, revision) => {
let channel = channels.current.get(channelId);
if (channel) {
channel.summary = summary;
channel.unsealedSummary = null;
channel.topicRevision = revision;
channels.current.set(channelId, channel);
}
}
const setChannelRevision = (channelId, revision) => {
let channel = channels.current.get(channelId);
if (channel) {
channel.revision = revision;
channels.current.set(channelId, channel);
}
}
const setChannelReadRevision = (channelId, revision) => {
let channel = channels.current.get(channelId);
if (channel) {
channel.readRevision = revision;
channels.current.set(channelId, channel);
}
}
const setChannelSyncRevision = (channelId, revision) => {
let channel = channels.current.get(channelId);
if (channel) {
channel.syncRevision = revision;
channels.current.set(channelId, channel);
}
}
const setChannelTopicMarker = (channelId, marker) => {
let channel = channels.current.get(channelId);
if (channel) {
channel.topicMarker = marker;
channels.current.set(channelId, channel);
}
}
const setChannelBlocked = (channelId, blocked) => {
let channel = channels.current.get(channelId);
if (channel) {
channel.blocked = blocked;
channels.current.set(channelId, channel);
}
}
};
const sync = async () => {
if (!syncing.current && setRevision.current !== curRevision.current) {
if (!syncing.current && (setRevision.current !== curRevision.current || force.current)) {
syncing.current = true;
force.current = false;
try {
const revision = curRevision.current;
const { server, appToken, guid } = session.current;
const delta = await getChannels(server, appToken, setRevision.current);
const { server, token, guid } = session.current;
const delta = await getChannels(server, token, setRevision.current);
for (let channel of delta) {
if (channel.data) {
if (channel.data.channelDetail && channel.data.channelSummary) {
await store.actions.setChannelItem(guid, channel);
setChannel(channel.id, channel);
const item = setChannelItem(channel);
if (item.detail && item.summary) {
await store.actions.setChannelItem(guid, item);
channels.set(item.channelId, item);
}
else {
const { detailRevision, topicRevision, channelDetail, channelSummary } = channel.data;
const view = await store.actions.getChannelItemView(guid, channel.id);
if (view == null) {
let assembled = JSON.parse(JSON.stringify(channel));
assembled.data.channelDetail = await getChannelDetail(server, appToken, channel.id);
assembled.data.channelSummary = await getChannelSummary(server, appToken, channel.id);
await store.actions.setChannelItem(guid, assembled);
setChannel(assembled.id, assembled);
const { channelId, detailRevision, topicRevision, detail, summary } = channel;
const view = await store.actions.getChannelItemView(guid, channelId);
if (view?.detailRevision !== detailRevision) {
item.detail = await getChannelDetail(server, token, channelId);
await store.actions.setChannelItemDetail(guid, channelId, detailRevision, item.detail);
}
else {
if (view.detailRevision != detailRevision) {
const detail = await getChannelDetail(server, appToken, channel.id);
await store.actions.setChannelItemDetail(guid, channel.id, detailRevision, detail);
setChannelDetail(channel.id, detail, detailRevision);
}
if (view.topicRevision != topicRevision) {
const summary = await getChannelSummary(server, appToken, channel.id);
await store.actions.setChannelItemSummary(guid, channel.id, topicRevision, summary);
setChannelSummary(channel.id, summary, topicRevision);
}
await store.actions.setChannelItemRevision(guid, channel.id, channel.revision);
setChannelRevision(channel.id, channel.revision);
if (view?.topicRevision !== topicRevision) {
item.summary = await getChannelSummary(server, token, item.channelId);
await store.actions.setChannelItemSummary(guid, channel.id, topicRevision, item.summary);
}
await store.actions.setChannelItem(guid, item);
channels.set(channelId, item);
}
}
else {
@ -187,107 +119,59 @@ export function useChannelContext() {
};
const actions = {
setSession: async (access) => {
const { guid, server, appToken } = access;
setSession: async (session) => {
if (access.current || syncing.current) {
throw new Error('invalid channel state');
}
access.current = session;
channels.current = new Map();
const items = await store.actions.getChannelItems(guid);
const items = await store.actions.getChannelItems(session.guid);
for(item of items) {
channels.current.set(item.channelId, item);
}
const revision = await store.actions.getChannelRevision(guid);
updateState({ channels: channels.current });
setRevision.current = revision;
const revision = await store.actions.getChannelRevision(session.guid);
curRevision.current = revision;
session.current = access;
setRevision.current = revision;
setState({ offsync: false, channels: channels.current });
},
clearSession: () => {
session.current = {};
channels.current = new Map();
updateState({ account: null, channels: channels.current });
clearToken: () => {
access.current = null;
},
setRevision: (rev) => {
setRevision: async (rev) => {
curRevision.current = rev;
sync();
await sync();
},
setReadRevision: async (channelId, rev) => {
await store.actions.setChannelItemReadRevision(session.current.guid, channelId, rev);
setChannelReadRevision(channelId, rev);
updateState({ channels: channels.current });
addChannel: async (type, subject, cards) => {
const { server, token } = access.current;
return await addChannel(server, token, type, subject, cards);
},
setSyncRevision: async (channelId, revision) => {
const { guid } = session.current;
await store.actions.setChannelItemSyncRevision(guid, channelId, revision);
setChannelSyncRevision(channelId, revision);
updateState({ channels: channels.current });
removeChannel: async (channelId) => {
const { server, token } = access.current;
return await removeChannel(access.current, channelId);
},
setTopicMarker: async (channelId, marker) => {
const { guid } = session.current;
await store.actions.setChannelItemTopicMarker(guid, channelId, marker);
setChannelTopicMarker(channelId, marker);
updateState({ channels: channels.current });
setChannelSubject: async (channelId, type, subject) => {
const { server, token } = access.current;
return await setChannelSubject(server, token, channelId, type, subject);
},
setBlocked: async (channelId) => {
const { guid } = session.current;
await store.actions.setChannelItemBlocked(guid, channelId);
setChannelBlocked(channelId, 1);
updateState({ channels: channels.current });
setChannelCard: async (channelId, cardId) => {
const { server, token } = access.current;
return await setChannelCard(server, token, channelId, cardId);
},
clearBlocked: async (channelId) => {
const { guid } = session.current;
await store.actions.clearChannelItemBlocked(guid, channelId);
setChannelBlocked(channelId, 0);
updateState({ channels: channels.current });
clearChannelCard: async (channelId, cardId) => {
const { server, token } = access.current;
return await clearChannelCard(server, token, channelId, cardId);
},
setTopicBlocked: async (channelId, topicId) => {
const { guid } = session.current;
await store.actions.setChannelTopicBlocked(guid, channelId, topicId, true);
},
clearTopicBlocked: async (channelId, topicId) => {
const { guid } = session.current;
await store.actions.setChannelTopicBlocked(guid, channelId, topicId, false);
},
getTopicBlocked: async () => {
const { guid } = session.current;
return await store.actions.getChannelTopicBlocked(guid);
},
getTopicItems: async (channelId) => {
const { guid } = session.current;
return await store.actions.getChannelTopicItems(guid, channelId);
},
setTopicItem: async (channelId, topicId, topic) => {
const { guid } = session.current;
return await store.actions.setChannelTopicItem(guid, channelId, topicId, topic);
},
clearTopicItem: async (channelId, topicId) => {
const { guid } = session.current;
return await store.actions.clearChannelTopicItem(guid, channelId, topicId);
},
clearTopicItems: async (channelId) => {
const { guid } = session.current;
return await store.actions.clearChannelTopicItems(guid, channelId);
},
getTopic: async (channelId, topicId) => {
const { server, appToken } = session.current;
return await getChannelTopic(server, appToken, channelId, topicId);
},
getTopics: async (channelId, revision, count, begin, end) => {
const { server, appToken } = session.current;
return await getChannelTopics(server, appToken, channelId, revision, count, begin, end);
},
getTopicAssetUrl: (channelId, topicId, assetId) => {
const { server, appToken } = session.current;
return getChannelTopicAssetUrl(server, appToken, channelId, topicId, assetId);
},
addTopic: async (channelId, message, files) => {
const { server, appToken } = session.current;
addTopic: async (channelId, type, message, files) => {
const { server, token } = session.current;
if (files?.length > 0) {
const topicId = await addChannelTopic(server, appToken, channelId, null, null, null);
upload.actions.addTopic(server, appToken, channelId, topicId, files, async (assets) => {
const topicId = await addChannelTopic(server, token, channelId, null, null, null);
upload.actions.addTopic(server, token, channelId, topicId, files, async (assets) => {
message.assets = assets;
await setChannelTopicSubject(server, appToken, channelId, topicId, 'superbasictopic', message);
const subject = message(assets);
await setChannelTopicSubject(server, token, channelId, topicId, type, sbuject);
}, async () => {
try {
await removeChannelTopic(server, appToken, channelId, topicId);
await removeChannelTopic(server, token, channelId, topicId);
}
catch (err) {
console.log(err);
@ -295,158 +179,102 @@ export function useChannelContext() {
});
}
else {
await addChannelTopic(server, appToken, channelId, 'superbasictopic', message, []);
const subject = message([]);
await addChannelTopic(server, token, channelId, type, subject, []);
}
},
addSealedTopic: async (channelId, message, sealKey) => {
const { server, appToken } = session.current;
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const key = CryptoJS.enc.Hex.parse(sealKey);
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ message }), key, { iv: iv });
const messageEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
const messageIv = iv.toString();
await addChannelTopic(server, appToken, channelId, 'sealedtopic', { messageEncrypted, messageIv });
},
setTopicSubject: async (channelId, topicId, dataType, data) => {
const { server, appToken } = session.current;
return await setChannelTopicSubject(server, appToken, channelId, topicId, dataType, data);
},
setTopicUnsealedDetail: async (channelId, topicId, revision, unsealed) => {
const { guid } = session.current;
await store.actions.setChannelTopicItemUnsealedDetail(guid, channelId, topicId, revision, unsealed);
},
setTopicUnsealedSummary: async (channelId, topicId, revision, unsealed) => {
const { guid } = seassion.current;
await store.actions.setChannelTopicItemUnsealedSummary(guid, channelId, topicId, revision, unsealed);
},
setSubject: async (channelId, subject) => {
const { server, appToken } = session.current;
return await setChannelSubject(server, appToken, channelId, 'superbasic', { subject });
},
setSealedSubject: async (channelId, subject, sealKey) => {
const { server, appToken } = session.current;
const channel = channels.current.get(channelId);
let { seals, subjectEncrypted, subjectIv } = JSON.parse(channel.detail.data);
const unsealedKey = await unsealKey(seals, sealKey);
if (!unsealedKey) {
throw new Error("cannot reseal subject");
}
const key = CryptoJS.enc.Hex.parse(unsealedKey);
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ subject }), key, { iv: iv });
subjectEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
subjectIv = iv.toString();
const data = { subjectEncrypted, subjectIv, seals };
return await setChannelSubject(server, appToken, channelId, 'sealed', data);
},
remove: async (channelId) => {
const { server, appToken } = session.current;
return await removeChannel(server, appToken, channelId);
},
addBasic: async (subject, cards) => {
const { server, appToken } = session.current;
return await addChannel(server, appToken, 'superbasic', { subject }, cards);
},
addSealed: async (subject, cards, keys) => {
const { server, appToken } = session.current;
const key = CryptoJS.lib.WordArray.random(256 / 8);
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ subject }), key, { iv: iv });
const subjectEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
const subjectIv = iv.toString();
const keyHex = key.toString();
let seals = [];
for (let i = 0; i < keys.length; i++) {
const publicKey = keys[i];
const key = '-----BEGIN PUBLIC KEY-----\n' + publicKey + '\n-----END PUBLIC KEY-----'
const sealedKey = await RSA.encrypt(keyHex, key);
seals.push({ publicKey, sealedKey });
};
const data = { subjectEncrypted, subjectIv, seals };
return await addChannel(server, appToken, 'sealed', data, cards);
},
removeTopic: async (channelId, topicId) => {
const { server, appToken } = session.current;
return await removeChannelTopic(server, appToken, channelId, topicId);
const { server, token } = access.current;
await removeChannelTopic(server, token, channelId, topicId);
},
addReport: async (channelId) => {
setTopicSubject: async (channelId, topicId, type, subject) => {
const { server, token } = access.current;
await setChannelTopicSubject(server, token, channelId, topicId, type, subject);
},
getTopicAssetUrl: (channelId, topicId, assetId) => {
const { server, token } = access.current;
getChannelTopicAssetUrl(server, token, channelId, topicId, assetId);
},
getTopics: async (channelId) => {
const { guid } = session.current;
return await store.actions.getChannelTopicItems(guid, channelId);
},
resync: async () => {
await resync();
},
setReadRevision: async (channelId, revision) => {
const { guid } = access.current;
await store.actions.setChannelItemReadRevision(guid, channelId, revision);
setChannelField(channelId, 'readRevision', revision);
},
setSyncRevision: async (channelId, revision) => {
const { guid } = session.current;
await store.actions.setChannelItemSyncRevision(guid, channelId, revision);
setChannelField(channelId, 'syncRevision', revision);
},
setTopicMarker: async (channelId, marker) => {
const { guid } = session.current;
await store.actions.setChannelItemTopicMarker(guid, channelId, revision);
setChannelField(channelId, 'topicMarker', marker);
},
setChannelFlag: async (channelId) => {
const { guid } = session.current;
await store.actions.setChannelItemBlocked(guid, channelId);
setChannelField(channelId, 'blocked', true);
},
clearChannelFlag: async (channelId) => {
const { guid } = session.current;
await store.actions.clearChannelItemBlocked(guid, channelId);
setChannelField(channelId, 'blocked', false);
},
setTopicFlag: async (channelId, topicId) => {
const { guid } = session.current;
await store.actions.setChannelTopicBlocked(guid, channelId, topicId, true);
},
clearTopicFlag: async (channelId, topicId) => {
const { guid } = session.current;
await store.actions.setChannelTopicBlocked(guid, channelId, topicId, false);
},
addChannelAlert: async (channelId) => {
const { server, guid } = session.current;
return await addFlag(server, guid, channelId);
},
addTopicReport: async (channelId, topicId) => {
addTopicAlert: async (channelId, topicId) => {
const { server, guid } = session.current;
return await addFlag(server, guid, channelId, topicId);
},
setCard: async (channelId, cardId) => {
const { server, appToken } = session.current;
return await setChannelCard(server, appToken, channelId, cardId);
getTopicItems: async (channelId, revision, count, begin, end) => {
const { server, token } = session.current;
return await getChannelTopics(server, token, channelId, revision, count, begin, end);
},
clearCard: async (channelId, cardId) => {
const { server, appToken } = session.current;
return await clearChannelCard(server, appToken, channelId, cardId);
getTopicItem: async (channelId, topicId) => {
const { server, token } = session.current;
return await getChannelTopic(server, token, channelId, topicId);
},
getNotifications: async (channelId) => {
const { server, appToken } = session.current;
return await getChannelNotifications(server, appToken, channelId);
setTopicItem: async (channelId, topic) => {
const { guid } = session.current;
return await store.actions.setChannelTopicItem(guid, channelId, topic);
},
setNotifications: async (channelId, notify) => {
const { server, appToken } = session.current;
return await setChannelNotifications(server, appToken, channelId, notify);
clearTopicItem: async (channelId, topicId) => {
const { guid } = session.current;
return await store.actions.clearChannelTopicItem(guid, channelId, topicId);
},
unsealChannelSubject: async (channelId, revision, sealKey) => {
try {
const { guid } = session.current;
const channel = channels.current.get(channelId);
const { subjectEncrypted, subjectIv, seals } = JSON.parse(channel.detail.data);
const unsealedKey = await unsealKey(seals, sealKey);
if (unsealedKey) {
const iv = CryptoJS.enc.Hex.parse(subjectIv);
const key = CryptoJS.enc.Hex.parse(unsealedKey);
const enc = CryptoJS.enc.Base64.parse(subjectEncrypted);
let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
if (revision === channel.detailRevision) {
channel.unsealedDetail = JSON.parse(dec.toString(CryptoJS.enc.Utf8));
await store.actions.setChannelItemUnsealedDetail(guid, channelId, revision, channel.unsealedDetail);
channels.current.set(channelId, { ...channel });
updateState({ channels: channels.current });
}
}
}
catch(err) {
console.log(err);
}
setUnsealedChannelSubject: async (channelId, revision, unsealed) => {
const { guid } = session.current;
await store.actions.setChannelItemUnsealedDetail(guid, channelId, revision, unsealed);
},
unsealChannelSummary: async (channelId, revision, sealKey) => {
try {
const { guid } = session.current;
const channel = channels.current.get(channelId);
const { seals } = JSON.parse(channel.detail.data);
const { messageEncrypted, messageIv } = JSON.parse(channel.summary.lastTopic.data);
const unsealedKey = await unsealKey(seals, sealKey);
if (unsealedKey) {
const iv = CryptoJS.enc.Hex.parse(messageIv);
const key = CryptoJS.enc.Hex.parse(unsealedKey);
const enc = CryptoJS.enc.Base64.parse(messageEncrypted);
let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
if (revision === channel.topicRevision) {
channel.unsealedSummary = JSON.parse(dec.toString(CryptoJS.enc.Utf8));
await store.actions.setChannelItemUnsealedSummary(guid, channelId, revision, channel.unsealedSummary);
channels.current.set(channelId, { ...channel });
updateState({ channels: channels.current });
}
}
}
catch(err) {
console.log(err);
}
setUnsealedChannelSummary: async (channelId, revision, unsealed) => {
const { guid } = session.current;
await store.actions.setChannelItemUnsealedSummary(guid, channelId, revision, unsealed);
},
}
setUnsealedTopicSubject: async (channelId, topicId, revision, unsealed) => {
const { guid } = session.current;
await store.actions.setChannelTopicItemUnsealedDetail(guid, channelId, topicId, revision, unsealed);
},
};
return { state, actions }
}

View File

@ -198,7 +198,7 @@ export function useStoreContext() {
},
setChannelItem: async (guid, channel) => {
const { id, revision, data } = channel;
await db.current.executeSql(`INSERT OR REPLACE INTO channel_${guid} (channel_id, revision, detail_revision, topic_revision, detail, summary, unsealed_detail, unsealed_summary) values (?, ?, ?, ?, ?, ?, null, null);`, [id, revision, data.detailRevision, data.topicRevision, encodeObject(data.channelDetail), encodeObject(data.channelSummary)]);
await db.current.executeSql(`INSERT OR REPLACE INTO channel_${guid} (channel_id, revision, detail_revision, topic_revision, detail, summary, unsealed_detail, unsealed_summary) values (?, ?, ?, ?, ?, ?, null, null);`, [id, revision, detailRevision, topicRevision, encodeObject(channelDetail), encodeObject(channelSummary)]);
},
clearChannelItem: async (guid, channelId) => {
await db.current.executeSql(`DELETE FROM channel_${guid} WHERE channel_id=?`, [channelId]);
@ -275,8 +275,8 @@ export function useStoreContext() {
}));
},
setChannelTopicItem: async (guid, channelId, topic) => {
const { id, revision, data } = topic;
await db.current.executeSql(`INSERT OR REPLACE INTO channel_topic_${guid} (channel_id, topic_id, revision, detail_revision, detail, unsealed_detail) values (?, ?, ?, ?, ?, null);`, [channelId, id, revision, data.detailRevision, encodeObject(data.topicDetail)]);
const { topicId, revision, detailRevision, detail } = topic;
await db.current.executeSql(`INSERT OR REPLACE INTO channel_topic_${guid} (channel_id, topic_id, revision, detail_revision, blocked detail, unsealed_detail) values (?, ?, ?, ?, false, ?, null);`, [channelId, topicId, revision, detailRevision, encodeObject(detail)]);
},
setChannelTopicItemUnsealedDetail: async (guid, channelId, topicId, revision, unsealed) => {
await db.current.executeSql(`UPDATE channel_topic_${guid} set unsealed_detail=? where detail_revision=? AND channel_id=? AND topic_id=?`, [encodeObject(unsealed), revision, channelId, topicId]);

View File

@ -0,0 +1,69 @@
import React, { useState, useEffect, useContext } from 'react';
import { View, Text } from 'react-native';
import { useTestStoreContext } from './useTestStoreContext.hook';
import {render, act, screen, waitFor, fireEvent} from '@testing-library/react-native'
import { ChannelContextProvider, ChannelContext } from 'context/ChannelContext';
import * as fetchUtil from 'api/fetchUtil';
function ChannelView() {
const [renderCount, setRenderCount] = useState(0);
const channel = useContext(ChannelContext);
useEffect(() => {
setRenderCount(renderCount + 1);
}, [channel.state]);
return (
<View testID="channel" channel={channel} renderCount={renderCount}>
</View>
);
}
function ChannelTestApp() {
return (
<ChannelContextProvider>
<ChannelView />
</ChannelContextProvider>
)
}
const realUseContext = React.useContext;
const realFetchWithTimeout = fetchUtil.fetchWithTimeout;
const realFetchWithCustomTimeout = fetchUtil.fetchWithCustomTimeout;
beforeEach(() => {
const mockUseContext = jest.fn().mockImplementation((ctx) => {
return useTestStoreContext();
});
React.useContext = mockUseContext;
const mockFetch = jest.fn().mockImplementation((url, options) => {
console.log(url);
return Promise.resolve({
json: () => Promise.resolve([])
});
});
fetchUtil.fetchWithTimeout = mockFetch;
fetchUtil.fetchWithCustomTimeout = mockFetch;
});
afterEach(() => {
React.useContext = realUseContext;
fetchUtil.fetchWithTimeout = realFetchWithTimeout;
fetchUtil.fetchWithCustomTimeout = realFetchWithCustomTimeout;
});
test('testing', async () => {
render(<ChannelTestApp />)
await act(async () => {
const channel = screen.getByTestId('channel').props.channel;
await channel.actions.setSession({ guid: 'abc', server: 'test.org', token: '123' });
await channel.actions.setRevision(1);
});
});

View File

@ -161,7 +161,6 @@ test('testing', async () => {
});
const renderCount = screen.getByTestId('profile').props.renderCount;
console.log("RENDER COUNT:", renderCount);
await act(async () => {
identity = { name: 'renderer' };
@ -169,8 +168,6 @@ console.log("RENDER COUNT:", renderCount);
await profile.actions.setRevision(2048);
});
console.log(renderCount);
await act(async () => {
expect(screen.getByTestId('profile').props.renderCount).toBe(renderCount + 1);
});

View File

@ -93,6 +93,7 @@ export function useTestStoreContext() {
},
getChannelRevision: async (guid) => {
return 1;
},
setChannelRevision: async (guid, revision) => {
},
@ -123,6 +124,7 @@ export function useTestStoreContext() {
getChannelItemView: async (guid, channelId) => {
},
getChannelItems: async (guid) => {
return [];
},
getChannelTopicItems: async (guid, channelId) => {