Skip to content

Commit

Permalink
chore: test redux components with jest
Browse files Browse the repository at this point in the history
  • Loading branch information
jvelezpo committed Jan 13, 2023
1 parent 6dfa40e commit 5486a69
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 119 deletions.
7 changes: 2 additions & 5 deletions src/components/MainList.test.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
import React from 'react';
import { render } from '@testing-library/react';
import { renderWithProviders } from '../utils/test-utils';
import MainList from './MainList';

type onClick = (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void;
describe('MainList', () => {
let disableLogging: onClick;
let enableLogging: onClick;
let loggedIn: boolean;
let loggingEnabled: boolean;
let logoutUser: onClick;
let totalTimeLoggedToday: string;
beforeEach(() => {
disableLogging = jest.fn();
enableLogging = jest.fn();
loggingEnabled = false;
loggedIn = false;
logoutUser = jest.fn();
totalTimeLoggedToday = '1/1/1999';
});
it('should render properly', () => {
const { container } = render(
const { container } = renderWithProviders(
<MainList
disableLogging={disableLogging}
enableLogging={enableLogging}
loggingEnabled={loggingEnabled}
loggedIn={loggedIn}
logoutUser={logoutUser}
totalTimeLoggedToday={totalTimeLoggedToday}
/>,
Expand Down
19 changes: 12 additions & 7 deletions src/components/MainList.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { ReduxSelector } from '../types/store';
import { User } from '../types/user';

export interface MainListProps {
disableLogging: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void;
enableLogging: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void;
loggedIn: boolean;
loggingEnabled: boolean;
logoutUser: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void;
totalTimeLoggedToday?: string;
Expand All @@ -15,14 +17,17 @@ const openOptionsPage = async (): Promise<void> => {
export default function MainList({
disableLogging,
enableLogging,
loggedIn,
loggingEnabled,
logoutUser,
totalTimeLoggedToday,
}: MainListProps): JSX.Element {
const user: User | undefined = useSelector(
(selector: ReduxSelector) => selector.currentUser.user,
);

return (
<div>
{loggedIn && (
{user && (
<div className="row">
<div className="col-xs-12">
<blockquote>
Expand All @@ -34,7 +39,7 @@ export default function MainList({
</div>
</div>
)}
{loggingEnabled && loggedIn && (
{loggingEnabled && user && (
<div className="row">
<div className="col-xs-12">
<p>
Expand All @@ -45,7 +50,7 @@ export default function MainList({
</div>
</div>
)}
{!loggingEnabled && loggedIn && (
{!loggingEnabled && user && (
<div className="row">
<div className="col-xs-12">
<p>
Expand All @@ -61,15 +66,15 @@ export default function MainList({
<i className="fa fa-fw fa-cogs"></i>
Options
</a>
{loggedIn && (
{user && (
<div>
<a href="#" className="list-group-item" onClick={logoutUser}>
<i className="fa fa-fw fa-sign-out"></i>
Logout
</a>
</div>
)}
{!loggedIn && (
{!user && (
<a
target="_blank"
rel="noreferrer"
Expand Down
19 changes: 7 additions & 12 deletions src/components/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { setValue } from '../reducers/apiKey';
import { configLogout, setApiKey } from '../reducers/configReducer';
import { userLogout } from '../reducers/currentUser';
import { ReduxSelector } from '../types/store';
import { User } from '../types/user';
import config from '../config/config';
import apiKeyInvalid from '../utils/apiKey';
import WakaTimeCore from '../core/WakaTimeCore';
import { setUser } from '../reducers/currentUser';
import changeExtensionState from '../utils/changeExtensionState';
import { fetchUserData } from '../utils/user';

export default function NavBar(): JSX.Element {
const [state, setState] = useState({
Expand Down Expand Up @@ -158,15 +157,11 @@ export default function NavBar(): JSX.Element {
if (state.apiKeyError === '' && state.apiKey !== '') {
setState({ ...state, loading: true });
await browser.storage.sync.set({ apiKey: state.apiKey });
dispatch(setValue(state.apiKey));
dispatch(configLogout());
dispatch(userLogout());
dispatch(setApiKey(state.apiKey));

try {
const data = await WakaTimeCore.checkAuth(state.apiKey);
dispatch(setUser(data));
} catch (err: unknown) {
dispatch(setUser(undefined));
await changeExtensionState('notSignedIn');
}
await fetchUserData(state.apiKey, dispatch);
setState({ ...state, loading: false });
}
}}
Expand Down
62 changes: 10 additions & 52 deletions src/components/WakaTime.tsx
Original file line number Diff line number Diff line change
@@ -1,69 +1,28 @@
import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { setValue } from '../reducers/apiKey';
import { ReduxSelector } from '../types/store';
import { setUser } from '../reducers/currentUser';
import WakaTimeCore from '../core/WakaTimeCore';
import { ApiKeyReducer, ReduxSelector } from '../types/store';
import config from '../config/config';
import { fetchUserData } from '../utils/user';
import changeExtensionState from '../utils/changeExtensionState';
import NavBar from './NavBar';
import MainList from './MainList';

const API_KEY = 'waka_3766d693-bff3-4c63-8bf5-b439f3e12301';

export default function WakaTime(): JSX.Element {
const dispatch = useDispatch();

const defaultState = {
loggedIn: false,
loggingEnabled: config.loggingEnabled,
totalTimeLoggedToday: '0 minutes',
};
const [state, setState] = useState(defaultState);
const apiKeyFromRedux: string = useSelector((selector: ReduxSelector) => selector.apiKey.value);

const fetchUserData = async (): Promise<void> => {
// await browser.storage.sync.set({ apiKey: API_KEY });
let apiKey = '';
if (!apiKeyFromRedux) {
const storage = await browser.storage.sync.get({
apiKey: config.apiKey,
});
apiKey = storage.apiKey as string;
dispatch(setValue(apiKey));
}

if (!apiKey) {
await changeExtensionState('notSignedIn');
}

try {
const data = await WakaTimeCore.checkAuth(apiKey);
dispatch(setUser(data));
const items = await browser.storage.sync.get({ loggingEnabled: config.loggingEnabled });

if (items.loggingEnabled === true) {
await changeExtensionState('allGood');
} else {
await changeExtensionState('notLogging');
}

const totalTimeLoggedToday = await WakaTimeCore.getTotalTimeLoggedToday(apiKey);
setState({
...state,
loggedIn: true,
loggingEnabled: items.loggingEnabled as boolean,
totalTimeLoggedToday: totalTimeLoggedToday.text,
});

await WakaTimeCore.recordHeartbeat();
} catch (err: unknown) {
await changeExtensionState('notSignedIn');
}
};
const {
apiKey: apiKeyFromRedux,
loggingEnabled,
totalTimeLoggedToday,
}: ApiKeyReducer = useSelector((selector: ReduxSelector) => selector.config);

useEffect(() => {
fetchUserData();
fetchUserData(apiKeyFromRedux, dispatch);
}, []);

const disableLogging = async () => {
Expand Down Expand Up @@ -109,10 +68,9 @@ export default function WakaTime(): JSX.Element {
<MainList
disableLogging={disableLogging}
enableLogging={enableLogging}
loggingEnabled={state.loggingEnabled}
totalTimeLoggedToday={state.totalTimeLoggedToday}
loggingEnabled={loggingEnabled}
totalTimeLoggedToday={totalTimeLoggedToday}
logoutUser={logoutUser}
loggedIn={state.loggedIn}
/>
</div>
</div>
Expand Down
25 changes: 0 additions & 25 deletions src/reducers/apiKey.ts

This file was deleted.

50 changes: 50 additions & 0 deletions src/reducers/configReducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { createSlice } from '@reduxjs/toolkit';
import config from '../config/config';
import { ApiKeyReducer } from '../types/store';

interface SetApiKeyAction {
payload: string;
type: string;
}

interface SetLoggingEnabledAction {
payload: boolean;
type: string;
}

interface SetTotalTimeLoggedTodayAction {
payload: string;
type: string;
}

export const initialConfigState: ApiKeyReducer = {
apiKey: '',
loggingEnabled: config.loggingEnabled,
totalTimeLoggedToday: '0 minutes',
};

const apiKeySlice = createSlice({
initialState: initialConfigState,
name: 'configReducer',
reducers: {
configLogout: (state) => {
state.apiKey = '';
state.loggingEnabled = config.loggingEnabled;
state.totalTimeLoggedToday = '0 minutes';
},
setApiKey: (state, action: SetApiKeyAction) => {
state.apiKey = action.payload;
},
setLoggingEnabled: (state, action: SetLoggingEnabledAction) => {
state.loggingEnabled = action.payload;
},
setTotalTimeLoggedToday: (state, action: SetTotalTimeLoggedTodayAction) => {
state.totalTimeLoggedToday = action.payload;
},
},
});

export const actions = apiKeySlice.actions;
export const { configLogout, setApiKey, setLoggingEnabled, setTotalTimeLoggedToday } =
apiKeySlice.actions;
export default apiKeySlice.reducer;
5 changes: 4 additions & 1 deletion src/reducers/currentUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,12 @@ const currentUser = createSlice({
setUser: (state, action: setUserAction) => {
state.user = action.payload;
},
userLogout: (state) => {
state.user = undefined;
},
},
});

export const actions = currentUser.actions;
export const { setUser } = currentUser.actions;
export const { setUser, userLogout } = currentUser.actions;
export default currentUser.reducer;
24 changes: 12 additions & 12 deletions src/stores/createStore.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import { configureStore, Store } from '@reduxjs/toolkit';
import { configureStore, Store, combineReducers } from '@reduxjs/toolkit';
import { logger } from 'redux-logger';
import { reduxBatch } from '@manaflair/redux-batch';
import devToolsEnhancer from 'remote-redux-devtools';
import currentUserReducer, { initialState as InitalCurrentUser } from '../reducers/currentUser';
import apiKeyReducer from '../reducers/apiKey';
import configReducer, { initialConfigState } from '../reducers/configReducer';
import isProd from '../utils/isProd';
import { CurrentUser } from '../types/user';

export interface RootState {
currentUser: CurrentUser;
}
// Create the root reducer separately so we can extract the RootState type
const rootReducer = combineReducers({
config: configReducer,
currentUser: currentUserReducer,
});

export type RootState = ReturnType<typeof rootReducer>;

const preloadedState: RootState = {
config: initialConfigState,
currentUser: InitalCurrentUser,
};

export type RootStore = Store<RootState>;
export default (appName: string): RootStore => {
export default (appName: string): Store<RootState> => {
const enhancers = [];
enhancers.push(reduxBatch);
if (!isProd()) {
Expand All @@ -29,10 +32,7 @@ export default (appName: string): RootStore => {
enhancers,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
preloadedState,
reducer: {
apiKey: apiKeyReducer,
currentUser: currentUserReducer,
},
reducer: rootReducer,
});

return store;
Expand Down
6 changes: 4 additions & 2 deletions src/types/store.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { CurrentUser } from './user';

export interface ApiKeyReducer {
value: string;
apiKey: string;
loggingEnabled: boolean;
totalTimeLoggedToday: string;
}

export interface ReduxSelector {
apiKey: ApiKeyReducer;
config: ApiKeyReducer;
currentUser: CurrentUser;
}
7 changes: 4 additions & 3 deletions src/utils/checkCurrentUser.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { RootStore } from '../stores/createStore';
import { Store } from '@reduxjs/toolkit';
import { RootState } from '../stores/createStore';
import { fetchCurrentUser } from '../reducers/currentUser';
import { ReduxSelector } from '../types/store';

type unsub = () => void;
export default (store: RootStore) =>
export default (store: Store<RootState>) =>
(time: number): unsub => {
const fetchUser = () => {
const apiKey: string = (store.getState() as ReduxSelector).apiKey.value;
const apiKey: string = (store.getState() as ReduxSelector).config.apiKey;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
store.dispatch(fetchCurrentUser(apiKey));
Expand Down
Loading

0 comments on commit 5486a69

Please sign in to comment.