From d2fc9de4afe969b11c1a618ee84487497206bc6c Mon Sep 17 00:00:00 2001 From: balzack Date: Thu, 12 Jan 2023 21:22:34 -0800 Subject: [PATCH] testing web access hook --- net/web/src/access/login/useLogin.hook.js | 27 +------ net/web/src/access/useAccess.hook.js | 22 +++++- net/web/src/context/useAppContext.hook.js | 24 +++++- net/web/test/Access.test.js | 90 +++++++++++++++++++++++ net/web/test/AccessView.test.js | 65 ---------------- 5 files changed, 132 insertions(+), 96 deletions(-) create mode 100644 net/web/test/Access.test.js delete mode 100644 net/web/test/AccessView.test.js diff --git a/net/web/src/access/login/useLogin.hook.js b/net/web/src/access/login/useLogin.hook.js index a141f949..0672a791 100644 --- a/net/web/src/access/login/useLogin.hook.js +++ b/net/web/src/access/login/useLogin.hook.js @@ -1,7 +1,7 @@ import { useContext, useState, useEffect } from 'react'; import { AppContext } from 'context/AppContext'; import { getAvailable } from 'api/getAvailable'; -import { useNavigate, useLocation } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; export function useLogin() { @@ -14,7 +14,6 @@ export function useLogin() { }); const navigate = useNavigate(); - const { search } = useLocation(); const app = useContext(AppContext); const updateState = (value) => { @@ -56,30 +55,6 @@ export function useLogin() { }, }; - useEffect(() => { - if (app.state.status) { - navigate('/session') - } - else { - let params = new URLSearchParams(search); - let token = params.get("access"); - if (token) { - const access = async () => { - updateState({ busy: true }) - try { - await app.actions.access(token) - } - catch (err) { - console.log(err); - } - updateState({ busy: false }) - } - access(); - } - } - // eslint-disable-next-line - }, [app.state, navigate, search]); - useEffect(() => { const count = async () => { try { diff --git a/net/web/src/access/useAccess.hook.js b/net/web/src/access/useAccess.hook.js index 6489c25a..cb8bf85f 100644 --- a/net/web/src/access/useAccess.hook.js +++ b/net/web/src/access/useAccess.hook.js @@ -1,5 +1,5 @@ import { useContext, useState, useEffect } from 'react'; -import { useNavigate } from "react-router-dom"; +import { useNavigate, useLocation } from "react-router-dom"; import { AppContext } from 'context/AppContext'; import { ViewportContext } from 'context/ViewportContext'; @@ -10,9 +10,11 @@ export function useAccess() { }); const navigate = useNavigate(); + const location = useLocation(); const app = useContext(AppContext); const viewport = useContext(ViewportContext); + const updateState = (value) => { setState((s) => ({ ...s, ...value })); } @@ -23,6 +25,24 @@ export function useAccess() { } }, [app.state, navigate]); + useEffect(() => { + let params = new URLSearchParams(location); + let token = params.get("access"); + if (token) { + const access = async () => { + try { + await app.actions.access(token) + } + catch (err) { + console.log(err); + } + } + access(); + } + // eslint-disable-next-line + }, [navigate, location]); + + useEffect(() => { const { display } = viewport.state; updateState({ display }); diff --git a/net/web/src/context/useAppContext.hook.js b/net/web/src/context/useAppContext.hook.js index 5eebbc7f..1f0e8d3f 100644 --- a/net/web/src/context/useAppContext.hook.js +++ b/net/web/src/context/useAppContext.hook.js @@ -21,7 +21,8 @@ export function useAppContext(websocket) { const appVersion = "1.0.0"; const userAgent = window.navigator.userAgent; - const access = useRef(null); + const checked = useRef(false); + const appToken = useRef(null); const ws = useRef(null); const updateState = (value) => { @@ -36,7 +37,6 @@ export function useAppContext(websocket) { const cardContext = useContext(CardContext); const setSession = (token) => { -console.log("SET SESSION", token); try { accountContext.actions.setToken(token); profileContext.actions.setToken(token); @@ -80,10 +80,14 @@ console.log("SET SESSION", token); } const appCreate = async (username, password, token) => { + if (appToken.current || !checked.current) { + throw new Error('invalid session state'); + } await addAccount(username, password, token); const access = await setLogin(username, password, appName, appVersion, userAgent); storeContext.actions.setValue('login:timestamp', access.created); setSession(access.appToken); + appToken.current = access.appToken; localStorage.setItem("session", JSON.stringify({ access: access.appToken, @@ -93,9 +97,13 @@ console.log("SET SESSION", token); } const appLogin = async (username, password) => { + if (appToken.current || !checked.current) { + throw new Error('invalid session state'); + } const access = await setLogin(username, password, appName, appVersion, userAgent); storeContext.actions.setValue('login:timestamp', access.created); setSession(access.appToken); + appToken.current = access.appToken; localStorage.setItem("session", JSON.stringify({ access: access.appToken, @@ -105,9 +113,13 @@ console.log("SET SESSION", token); } const appAccess = async (token) => { + if (appToken.current || !checked.current) { + throw new Error('invalid session state'); + } const access = await setAccountAccess(token, appName, appVersion, userAgent); storeContext.actions.setValue('login:timestamp', access.created); setSession(access.appToken); + appToken.current = access.appToken; localStorage.setItem("session", JSON.stringify({ access: access.appToken, @@ -119,11 +131,12 @@ console.log("SET SESSION", token); const appLogout = async () => { clearSession(); try { - await clearLogin(access.current); + await clearLogin(appToken.current); } catch (err) { console.log(err); } + appToken.current = null; localStorage.removeItem("session"); }; @@ -195,13 +208,16 @@ console.log("SET SESSION", token); try { const session = JSON.parse(storage) if (session?.access) { - setSession(session.access); + const access = session.access; + setSession(access); + appToken.current = access; } } catch(err) { console.log(err) } } + checked.current = true; // eslint-disable-next-line }, []); diff --git a/net/web/test/Access.test.js b/net/web/test/Access.test.js new file mode 100644 index 00000000..2c0c4cc2 --- /dev/null +++ b/net/web/test/Access.test.js @@ -0,0 +1,90 @@ +import React, { useState, useEffect, useContext } from 'react'; +import {render, act, screen, waitFor, fireEvent} from '@testing-library/react' +import { AppContext, AppContextProvider } from 'context/AppContext'; +import { AccountContextProvider } from 'context/AccountContext'; +import { ProfileContextProvider } from 'context/ProfileContext'; +import { CardContextProvider } from 'context/CardContext'; +import { ChannelContextProvider } from 'context/ChannelContext'; +import { StoreContextProvider } from 'context/StoreContext'; +import { UploadContextProvider } from 'context/UploadContext'; +import { ViewportContextProvider } from 'context/ViewportContext'; +import { useAccess } from 'access/useAccess.hook'; +import * as fetchUtil from 'api/fetchUtil'; + +let navPath; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: () => { return (path) => { navPath = path } }, + useLocation: () => { return 'path' }, +})); + +let appContext; +function AccessView() { + const { state, actions } = useAccess(); + const app = useContext(AppContext); + appContext = app; + return (
); +} + +function AccessTestApp() { + return ( + + + + + + + + + + + + + + + + + + ); +} + +const realFetchWithTimeout = fetchUtil.fetchWithTimeout; +const realFetchWithCustomTimeout = fetchUtil.fetchWithCustomTimeout; +beforeEach(() => { + const mockFetch = jest.fn().mockImplementation((url, options) => { + return Promise.resolve({ + json: () => Promise.resolve({ + guid: '123', + appToken: 'aacc', + created: 2, + pushSupported: false, + }) + }); + }); + fetchUtil.fetchWithTimeout = mockFetch; + fetchUtil.fetchWithCustomTimeout = mockFetch; +}); + +afterEach(() => { + fetchUtil.fetchWithTimeout = realFetchWithTimeout; + fetchUtil.fetchWithCustomTimeout = realFetchWithCustomTimeout; +}); + +test('nav to session after login', async () => { + render(); + + await waitFor(async () => { + expect(appContext).not.toBe(null); + }); + + await act(async () => { + await appContext.actions.login('testusername', 'testpassword'); + }); + + await waitFor(async () => { + expect(navPath).toBe('/session'); + }); +}); + + + diff --git a/net/web/test/AccessView.test.js b/net/web/test/AccessView.test.js deleted file mode 100644 index 2d1d87d0..00000000 --- a/net/web/test/AccessView.test.js +++ /dev/null @@ -1,65 +0,0 @@ -import React, { useState, useEffect, useContext } from 'react'; -import {render, act, screen, waitFor, fireEvent} from '@testing-library/react' -import { HashRouter as Router, Routes, Route } from "react-router-dom"; -import { ViewportContextProvider } from 'context/ViewportContext'; -import { AppContextProvider } from 'context/AppContext'; -import { Access } from 'access/Access'; -import * as fetchUtil from 'api/fetchUtil'; - -function AccessTestApp() { - return ( - - - - - } /> - } /> - } /> - - - - - ); -} - -beforeAll(() => { - Object.defineProperty(window, "matchMedia", { - writable: true, - value: jest.fn().mockImplementation(query => ({ - matches: false, - media: query, - onchange: null, - addListener: jest.fn(), // Deprecated - removeListener: jest.fn(), // Deprecated - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - dispatchEvent: jest.fn(), - })) - }); -}); - -const realFetchWithTimeout = fetchUtil.fetchWithTimeout; -const realFetchWithCustomTimeout = fetchUtil.fetchWithCustomTimeout; -beforeEach(() => { - const mockFetch = jest.fn().mockImplementation((url, options) => { - return Promise.resolve({ - json: () => Promise.resolve([]) - }); - }); - fetchUtil.fetchWithTimeout = mockFetch; - fetchUtil.fetchWithCustomTimeout = mockFetch; -}); - -afterEach(() => { - fetchUtil.fetchWithTimeout = realFetchWithTimeout; - fetchUtil.fetchWithCustomTimeout = realFetchWithCustomTimeout; -}); - -test('login and create', async () => { - await act(async () => { - render(); - }); -}); - - -