testing web app context

This commit is contained in:
balzack 2023-01-11 21:54:26 -08:00
parent 28940671d9
commit a371bbbc1d
2 changed files with 60 additions and 62 deletions

View File

@ -15,7 +15,7 @@ import { createWebsocket } from 'api/fetchUtil';
export function useAppContext(websocket) { export function useAppContext(websocket) {
const [state, setState] = useState({ const [state, setState] = useState({
disconnected: true, status: 'disconnected',
}); });
const [appRevision, setAppRevision] = useState(); const [appRevision, setAppRevision] = useState();
@ -23,8 +23,8 @@ export function useAppContext(websocket) {
const appVersion = "1.0.0"; const appVersion = "1.0.0";
const userAgent = window.navigator.userAgent; const userAgent = window.navigator.userAgent;
const access = useRef(null);
const ws = useRef(null); const ws = useRef(null);
const revision = useRef(null);
const updateState = (value) => { const updateState = (value) => {
setState((s) => ({ ...s, ...value })) setState((s) => ({ ...s, ...value }))
@ -37,21 +37,28 @@ export function useAppContext(websocket) {
const channelContext = useContext(ChannelContext); const channelContext = useContext(ChannelContext);
const cardContext = useContext(CardContext); const cardContext = useContext(CardContext);
const resetData = () => { const setSession = (token) => {
revision.current = null; accountContext.actions.setToken(token);
profileContext.actions.setToken(token);
cardContext.actions.setToken(token);
channelContext.actions.setToken(token);
setWebsocket(token);
}
const clearSession = () => {
uploadContext.actions.clear();
storeContext.actions.clear();
accountContext.actions.clearToken(); accountContext.actions.clearToken();
profileContext.actions.clearToken(); profileContext.actions.clearToken();
cardContext.actions.clearToken(); cardContext.actions.clearToken();
channelContext.actions.clearToken(); channelContext.actions.clearToken();
setState({}); clearWebsocket();
} }
const actions = { const actions = {
logout: () => { logout: async () => {
appLogout(); await appLogout();
storeContext.actions.clear();
uploadContext.actions.clear();
resetData();
}, },
access: async (token) => { access: async (token) => {
await appAccess(token) await appAccess(token)
@ -62,21 +69,14 @@ export function useAppContext(websocket) {
create: async (username, password, token) => { create: async (username, password, token) => {
await appCreate(username, password, token) await appCreate(username, password, token)
}, },
username: getUsername,
available: getAvailable,
} }
const appCreate = async (username, password, token) => { const appCreate = async (username, password, token) => {
await addAccount(username, password, token); await addAccount(username, password, token);
let access = await setLogin(username, password, appName, appVersion, userAgent) const access = await setLogin(username, password, appName, appVersion, userAgent);
updateState({ access: access.appToken });
storeContext.actions.setValue('login:timestamp', access.created); storeContext.actions.setValue('login:timestamp', access.created);
accountContext.actions.setToken(access.appToken); setSession(access.appToken);
profileContext.actions.setToken(access.appToken);
cardContext.actions.setToken(access.appToken);
channelContext.actions.setToken(access.appToken);
setWebsocket(access.appToken)
localStorage.setItem("session", JSON.stringify({ localStorage.setItem("session", JSON.stringify({
access: access.appToken, access: access.appToken,
timestamp: access.created, timestamp: access.created,
@ -85,15 +85,10 @@ export function useAppContext(websocket) {
} }
const appLogin = async (username, password) => { const appLogin = async (username, password) => {
let access = await setLogin(username, password, appName, appVersion, userAgent) const access = await setLogin(username, password, appName, appVersion, userAgent);
updateState({ access: access.appToken });
storeContext.actions.setValue('login:timestamp', access.created); storeContext.actions.setValue('login:timestamp', access.created);
accountContext.actions.setToken(access.appToken); setSession(access.appToken);
profileContext.actions.setToken(access.appToken);
cardContext.actions.setToken(access.appToken);
channelContext.actions.setToken(access.appToken);
setWebsocket(access.appToken)
localStorage.setItem("session", JSON.stringify({ localStorage.setItem("session", JSON.stringify({
access: access.appToken, access: access.appToken,
timestamp: access.created, timestamp: access.created,
@ -102,15 +97,10 @@ export function useAppContext(websocket) {
} }
const appAccess = async (token) => { const appAccess = async (token) => {
let access = await setAccountAccess(token, appName, appVersion, userAgent) const access = await setAccountAccess(token, appName, appVersion, userAgent);
updateState({ access: access.appToken });
storeContext.actions.setValue('login:timestamp', access.created); storeContext.actions.setValue('login:timestamp', access.created);
accountContext.actions.setToken(access.appToken); setSession(access.appToken);
profileContext.actions.setToken(access.appToken);
cardContext.actions.setToken(access.appToken);
channelContext.actions.setToken(access.appToken);
setWebsocket(access.appToken)
localStorage.setItem("session", JSON.stringify({ localStorage.setItem("session", JSON.stringify({
access: access.appToken, access: access.appToken,
timestamp: access.created, timestamp: access.created,
@ -119,21 +109,15 @@ export function useAppContext(websocket) {
} }
const appLogout = async () => { const appLogout = async () => {
clearSession();
try { try {
await clearLogin(state.access); await clearLogin(access.current);
} }
catch (err) { catch (err) {
console.log(err); console.log(err);
} }
updateState({ access: null });
accountContext.actions.clearToken();
profileContext.actions.clearToken();
cardContext.actions.clearToken();
channelContext.actions.clearToken();
clearWebsocket()
localStorage.removeItem("session"); localStorage.removeItem("session");
} };
useEffect(() => { useEffect(() => {
if (appRevision) { if (appRevision) {
@ -154,20 +138,22 @@ export function useAppContext(websocket) {
protocol = 'wss://'; protocol = 'wss://';
} }
updateState({ status: 'connecting' });
ws.current = createWebsocket(protocol + window.location.host + "/status"); ws.current = createWebsocket(protocol + window.location.host + "/status");
ws.current.onmessage = (ev) => { ws.current.onmessage = (ev) => {
try { try {
let rev = JSON.parse(ev.data); let rev = JSON.parse(ev.data);
updateState({ status: 'connected' });
setAppRevision(rev); setAppRevision(rev);
updateState({ disconnected: false });
} }
catch (err) { catch (err) {
console.log(err); console.log(err);
ws.current.close();
} }
} }
ws.current.onclose = (e) => { ws.current.onclose = (e) => {
updateState({ disconnected: true });
console.log(e) console.log(e)
updateState({ status: 'disconnected' });
setTimeout(() => { setTimeout(() => {
if (ws.current != null) { if (ws.current != null) {
ws.current.onmessage = () => {} ws.current.onmessage = () => {}
@ -176,14 +162,15 @@ export function useAppContext(websocket) {
ws.current.onerror = () => {} ws.current.onerror = () => {}
setWebsocket(token); setWebsocket(token);
} }
}, 1000) }, 1000);
} }
ws.current.onopen = () => { ws.current.onopen = () => {
ws.current.send(JSON.stringify({ AppToken: token })) ws.current.send(JSON.stringify({ AppToken: token }))
} }
ws.current.error = (e) => { ws.current.error = (e) => {
updateState({ disconnected: true });
console.log(e) console.log(e)
ws.current.close();
updateState({ status: 'disconnected' });
} }
} }
@ -191,6 +178,7 @@ export function useAppContext(websocket) {
ws.current.onclose = () => {} ws.current.onclose = () => {}
ws.current.close() ws.current.close()
ws.current = null ws.current = null
updateState({ status: 'disconnected' });
} }
useEffect(() => { useEffect(() => {
@ -199,25 +187,16 @@ export function useAppContext(websocket) {
try { try {
const session = JSON.parse(storage) const session = JSON.parse(storage)
if (session?.access) { if (session?.access) {
setState({ access: session.access }) setSession(session.access);
setWebsocket(session.access);
} else {
setState({})
} }
} }
catch(err) { catch(err) {
console.log(err) console.log(err)
setState({})
} }
} else {
setState({})
} }
// eslint-disable-next-line // eslint-disable-next-line
}, []); }, []);
if (state == null) {
return {};
}
return { state, actions } return { state, actions }
} }

View File

@ -12,10 +12,9 @@ import { UploadContextProvider } from 'context/UploadContext';
import { ViewportContextProvider } from 'context/ViewportContext'; import { ViewportContextProvider } from 'context/ViewportContext';
import { ConversationContextProvider } from 'context/ConversationContext'; import { ConversationContextProvider } from 'context/ConversationContext';
let mockWebsocket;
function MockWebsocket(url) { function MockWebsocket(url) {
setTimeout(() => { this.url = url;
this.onmessage({ data: JSON.stringify({ account: 1, profile: 1, card: 1, channel: 1 }) });
});
}; };
let appContext = null; let appContext = null;
@ -31,7 +30,7 @@ function AppView() {
return ( return (
<div> <div>
<span data-testid="count">{ renderCount }</span> <span data-testid="count">{ renderCount }</span>
<span data-testid="disconnected">{ app.state.disconnected ? 'true' : 'false' }</span> <span data-testid="status">{ app.state.status }</span>
</div> </div>
); );
} }
@ -64,7 +63,8 @@ const realFetchWithCustomTimeout = fetchUtil.fetchWithCustomTimeout;
beforeEach(() => { beforeEach(() => {
const mockCreateWebsocket = jest.fn().mockImplementation((url) => { const mockCreateWebsocket = jest.fn().mockImplementation((url) => {
return new MockWebsocket(url); mockWebsocket = new MockWebsocket(url);
return mockWebsocket;
}); });
const mockFetch = jest.fn().mockImplementation((url, options) => { const mockFetch = jest.fn().mockImplementation((url, options) => {
@ -102,10 +102,29 @@ test('testing app sync', async () => {
await act(async () => { await act(async () => {
appContext.actions.login('testlogin', 'testpassword'); appContext.actions.login('testlogin', 'testpassword');
expect(mockWebsocket?.onmessage).not.toBe(null);
expect(mockWebsocket?.onclose).not.toBe(null);
}); });
await waitFor(async () => { await waitFor(async () => {
expect(screen.getByTestId('disconnected').textContent).toBe('false'); expect(screen.getByTestId('status').textContent).toBe('connecting');
});
await act(async () => {
mockWebsocket.onmessage({ data: JSON.stringify({ account: 1, profile: 1, card: 1, channel: 1 }) });
});
await waitFor(async () => {
expect(screen.getByTestId('status').textContent).toBe('connected');
});
await act(async () => {
mockWebsocket.onclose();
await new Promise(r => setTimeout(r, 1000));
});
await waitFor(async () => {
expect(screen.getByTestId('status').textContent).toBe('connecting');
}); });
}); });