1.1.0 - In this version, the account logout method and the latest changes published by Tuitio were implemented.

master
Tudor Stanciu 2023-03-18 02:24:30 +02:00
parent 67035fbc7e
commit 8e200a1d14
15 changed files with 2656 additions and 2159 deletions

View File

@ -35,4 +35,5 @@ All tests in the package can be executed by running: `npm test`.
## Changelog ## Changelog
1.0.0 - Package initialization 1.0.0 - Package initialization
1.0.1 - Added useTuitioClient default options 1.0.1 - Added useTuitioClient default options
1.1.0 - In this version, the account logout method and the latest changes published by Tuitio were implemented.

4409
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@flare/tuitio-client-react", "name": "@flare/tuitio-client-react",
"version": "1.0.1", "version": "1.1.0",
"description": "Tuitio client react is an npm package written in typescript that facilitates the integration of a react application with Tuitio.", "description": "Tuitio client react is an npm package written in typescript that facilitates the integration of a react application with Tuitio.",
"main": "./dist/cjs/index.js", "main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js", "module": "./dist/esm/index.js",
@ -48,7 +48,7 @@
"README.md" "README.md"
], ],
"dependencies": { "dependencies": {
"@flare/tuitio-client": "^1.0.4" "@flare/tuitio-client": "^1.1.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.14.0" "react": "^16.14.0"

View File

@ -1,35 +1,42 @@
import { useContext } from "react"; import { useContext } from "react";
import { TuitioContext, TuitioDispatchContext } from "../contexts"; import { TuitioContext, TuitioDispatchContext } from "../contexts";
import { TuitioClient, invalidate } from "@flare/tuitio-client"; import { TuitioClient } from "@flare/tuitio-client";
import type { TuitioAuthenticationResult } from "@flare/tuitio-client"; import { TuitioLoginResponse, TuitioLogoutResponse } from "@flare/tuitio-client";
export type TuitioClientHookOptions = { export type TuitioClientHookOptions = {
onLoginSuccess?: (result: TuitioAuthenticationResult, userName: string) => void; onLoginSuccess?: (userName: string, token: string, expiresIn: number, validUntil: Date) => void;
onLoginFailed?: (result: TuitioAuthenticationResult, userName: string) => void; onLoginFailed?: (error: string) => void;
onLoginError?: (error: any) => void; onLoginError?: (error: any) => void;
onLogoutSuccess?: (userName: string, logoutDate: Date) => void;
onLogoutFailed?: (error: string) => void;
onLogoutError?: (error: any) => void;
}; };
const defaultOptions: TuitioClientHookOptions = { const defaultOptions: TuitioClientHookOptions = {
onLoginSuccess: undefined, onLoginSuccess: undefined,
onLoginFailed: undefined, onLoginFailed: undefined,
onLoginError: undefined onLoginError: undefined,
onLogoutSuccess: undefined,
onLogoutFailed: undefined,
onLogoutError: undefined
}; };
const useTuitioClient = (options: TuitioClientHookOptions = defaultOptions) => { const useTuitioClient = (options: TuitioClientHookOptions = defaultOptions) => {
const state = useContext(TuitioContext); const state = useContext(TuitioContext);
const dispatchActions = useContext(TuitioDispatchContext); const dispatchActions = useContext(TuitioDispatchContext);
const { onLoginSuccess, onLoginFailed, onLoginError, onLogoutSuccess, onLogoutFailed, onLogoutError } = options;
const { onLoginSuccess, onLoginFailed, onLoginError } = options; const login = async (userName: string, password: string): Promise<TuitioLoginResponse> => {
const login = async (userName: string, password: string) => {
try { try {
const client = new TuitioClient(state.configuration.tuitioUrl); const client = new TuitioClient(state.configuration.tuitioUrl);
const response = await client.authenticate(userName, password); const response = await client.login(userName, password);
if (response.token) { if (!response.error) {
dispatchActions.onLoginSuccess(response.token, userName); if (!response.result) throw new Error("Unexpected error: Login result and error are both null.");
onLoginSuccess && onLoginSuccess(response, userName); dispatchActions.onLoginSuccess(response.result.token, response.result.validUntil, userName);
onLoginSuccess &&
onLoginSuccess(userName, response.result.token, response.result.expiresIn, response.result.validUntil);
} else { } else {
onLoginFailed && onLoginFailed(response, userName); onLoginFailed && onLoginFailed(response.error);
} }
return response; return response;
} catch (error) { } catch (error) {
@ -38,9 +45,22 @@ const useTuitioClient = (options: TuitioClientHookOptions = defaultOptions) => {
} }
}; };
const logout = (): void => { const logout = async (): Promise<TuitioLogoutResponse> => {
invalidate(); try {
dispatchActions.onLogout(); const client = new TuitioClient(state.configuration.tuitioUrl);
const response = await client.logout();
if (!response.error) {
if (!response.result) throw new Error("Unexpected error: Logout result and error are both null.");
dispatchActions.onLogoutSuccess();
onLogoutSuccess && onLogoutSuccess(response.result.userName, response.result.logoutDate);
} else {
onLogoutFailed && onLogoutFailed(response.error);
}
return response;
} catch (error: any) {
onLogoutError && onLogoutError(error);
throw error;
}
}; };
return { login, logout }; return { login, logout };

View File

@ -6,12 +6,11 @@ const useTuitioToken = () => {
const token = state.token; const token = state.token;
const validate = (): boolean => { const validate = (): boolean => {
const token = state.token; const validUntil = state.validUntil;
if (!token) { if (!validUntil) {
return false; return false;
} }
const valid = new Date(validUntil) >= new Date();
const valid = new Date(token.validUntil) >= new Date();
return valid; return valid;
}; };

View File

@ -1,11 +1,12 @@
import { useContext } from "react"; import { useContext } from "react";
import { TuitioContext } from "../contexts"; import { TuitioContext } from "../contexts";
// vezi poate il combini cu token: useTuitioAuth
const useTuitioUser = () => { const useTuitioUser = () => {
const state = useContext(TuitioContext); const state = useContext(TuitioContext);
const userName = state.userName; const userName = state.userName;
const lastLoginDate = state.token?.validFrom; return { userName };
return { userName, lastLoginDate };
}; };
export { useTuitioUser }; export { useTuitioUser };

View File

@ -1,15 +1,17 @@
import { fetch } from "@flare/tuitio-client"; import { fetch } from "@flare/tuitio-client";
import type { TuitioReactState, TuitioDispatchActions } from "./types"; import type { TuitioReactState, TuitioDispatchActions } from "./types";
const { userName, token } = fetch(); const { token, validUntil, userName } = fetch();
export const initialState: TuitioReactState = { export const initialState: TuitioReactState = {
// pune intr-un obiect auth
userName, userName,
token, token,
validUntil,
configuration: { tuitioUrl: null } configuration: { tuitioUrl: null }
}; };
export const initialDispatchActions: TuitioDispatchActions = { export const initialDispatchActions: TuitioDispatchActions = {
onLoginSuccess: () => undefined, onLoginSuccess: () => undefined,
onLogout: () => undefined onLogoutSuccess: () => undefined
}; };

View File

@ -1,18 +1,19 @@
import { initialState } from "./initialState"; import { initialState } from "./initialState";
import type { TuitioReactState, TuitioDispatchActions } from "./types"; import type { TuitioReactState, TuitioDispatchActions } from "./types";
import type { TuitioToken } from "@flare/tuitio-client";
import { Dispatch } from "react"; import { Dispatch } from "react";
export const reducer = (state: TuitioReactState = initialState, action: any): TuitioReactState => { export const reducer = (state: TuitioReactState = initialState, action: any): TuitioReactState => {
switch (action.type) { switch (action.type) {
case "onLoginSuccess": { case "onLoginSuccess": {
const { token, validUntil, userName } = action;
return { return {
...state, ...state,
token: action.token, token,
userName: action.userName validUntil,
userName
}; };
} }
case "onLogout": { case "onLogoutSuccess": {
const { configuration } = state; const { configuration } = state;
return { return {
...initialState, ...initialState,
@ -26,6 +27,7 @@ export const reducer = (state: TuitioReactState = initialState, action: any): Tu
}; };
export const dispatchActions = (dispatch: Dispatch<any>): TuitioDispatchActions => ({ export const dispatchActions = (dispatch: Dispatch<any>): TuitioDispatchActions => ({
onLoginSuccess: (token: TuitioToken, userName: string): void => dispatch({ type: "onLoginSuccess", token, userName }), onLoginSuccess: (token: string, validUntil: Date, userName: string): void =>
onLogout: (): void => dispatch({ type: "onLogout" }) dispatch({ type: "onLoginSuccess", token, validUntil, userName }),
onLogoutSuccess: (): void => dispatch({ type: "onLogoutSuccess" })
}); });

View File

@ -1,8 +1,11 @@
import type { TuitioToken } from "@flare/tuitio-client";
export type TuitioConfiguration = { tuitioUrl: string | null }; export type TuitioConfiguration = { tuitioUrl: string | null };
export type TuitioReactState = { userName: string; token: TuitioToken | null; configuration: TuitioConfiguration }; export type TuitioReactState = {
export type TuitioDispatchActions = { userName: string | null;
onLoginSuccess: (token: TuitioToken, userName: string) => void; token: string | null;
onLogout: () => void; validUntil: Date | null;
configuration: TuitioConfiguration;
};
export type TuitioDispatchActions = {
onLoginSuccess: (token: string, validUntil: Date, userName: string) => void;
onLogoutSuccess: () => void;
}; };

View File

@ -1,63 +1,69 @@
import { renderHook, act } from "@testing-library/react-hooks"; import { renderHook, act } from "@testing-library/react-hooks";
import { TuitioClient, invalidate } from "@flare/tuitio-client"; import { TuitioClient } from "@flare/tuitio-client";
import { useTuitioClient } from "../../src/hooks"; import { useTuitioClient } from "../../src/hooks";
const userName = "tuitio.user";
const password = "password";
const token = "mocked-token";
const expiresIn = 600000;
const validUntil = new Date();
jest.mock("@flare/tuitio-client", () => ({ jest.mock("@flare/tuitio-client", () => ({
TuitioClient: jest.fn().mockImplementation(() => ({ TuitioClient: jest.fn().mockImplementation(() => ({
authenticate: jest.fn().mockResolvedValue({ login: jest.fn().mockResolvedValue({
token: { result: {
raw: "mocked-token", token,
validFrom: new Date(), expiresIn,
validUntil: new Date() validUntil
}, },
status: "success" error: null
}),
logout: jest.fn().mockResolvedValue({
result: {
userId: 0,
userName,
logoutDate: new Date()
},
error: null
}) })
})), })),
invalidate: jest.fn(),
fetch: jest.fn().mockResolvedValue({ fetch: jest.fn().mockResolvedValue({
token: { token,
raw: "mocked-token", validUntil,
validFrom: new Date(), userName
validUntil: new Date()
},
userName: "user"
}) })
})); }));
describe("useTuitioClient", () => { describe("useTuitioClient", () => {
it("should call onLoginSuccess when login is successful", async () => { it("should call onLoginSuccess when login is successful and should call onLogoutSuccess when logout is called", async () => {
const onLoginSuccess = jest.fn(); const onLoginSuccess = jest.fn();
const onLogoutSuccess = jest.fn();
const { result } = renderHook(() => const { result } = renderHook(() =>
useTuitioClient({ useTuitioClient({
onLoginSuccess, onLoginSuccess,
onLoginFailed: jest.fn(), onLoginFailed: jest.fn(),
onLoginError: jest.fn() onLoginError: jest.fn(),
onLogoutSuccess
}) })
); );
await act(async () => { await act(async () => {
await result.current.login("user", "password"); await result.current.login(userName, password);
await result.current.logout();
}); });
expect(onLoginSuccess).toHaveBeenCalledWith( expect(onLoginSuccess).toHaveBeenCalledWith(userName, token, expiresIn, validUntil);
{ expect(onLogoutSuccess).toHaveBeenCalled();
token: {
raw: "mocked-token",
validFrom: expect.any(Date),
validUntil: expect.any(Date)
},
status: "success"
},
"user"
);
}); });
it("should call onLoginFailed when login is unsuccessful", async () => { it("should call onLoginFailed when login is unsuccessful", async () => {
const error = "error";
const onLoginFailed = jest.fn(); const onLoginFailed = jest.fn();
(TuitioClient as jest.Mocked<any>).mockImplementation(() => ({ (TuitioClient as jest.Mocked<any>).mockImplementation(() => ({
authenticate: jest.fn().mockResolvedValue({ login: jest.fn().mockResolvedValue({
token: null, result: null,
status: "failed" error
}) })
})); }));
@ -70,21 +76,15 @@ describe("useTuitioClient", () => {
); );
await act(async () => { await act(async () => {
await result.current.login("user", "password"); await result.current.login(userName, password);
}); });
expect(onLoginFailed).toHaveBeenCalledWith( expect(onLoginFailed).toHaveBeenCalledWith(error);
{
token: null,
status: "failed"
},
"user"
);
}); });
it("should call onLoginError with error if authentication fails", async () => { it("should call onLoginError with error if authentication fails", async () => {
(TuitioClient as jest.Mocked<any>).mockImplementation(() => ({ (TuitioClient as jest.Mocked<any>).mockImplementation(() => ({
authenticate: jest.fn().mockRejectedValue(new Error("Login failed")) login: jest.fn().mockRejectedValue(new Error("Login failed"))
})); }));
const onLoginError = jest.fn(); const onLoginError = jest.fn();
@ -94,20 +94,78 @@ describe("useTuitioClient", () => {
await act(async () => { await act(async () => {
try { try {
await result.current.login("user", "password"); await result.current.login(userName, password);
} catch (error) { } catch (error) {
expect(onLoginError).toHaveBeenCalledWith(error); expect(onLoginError).toHaveBeenCalledWith(error);
} }
}); });
}); });
it("should call invalidate when logout is called", () => { it("should return error if logout returns error", async () => {
(TuitioClient as jest.Mocked<any>).mockImplementation(() => ({
logout: jest.fn().mockResolvedValue({
result: null,
error: "error"
})
}));
const onLogoutFailed = jest.fn();
const { result } = renderHook(() =>
useTuitioClient({
onLogoutFailed
})
);
const response = await result.current.logout();
expect(response.result).toBeNull();
expect(response.error).toBe("error");
expect(onLogoutFailed).toHaveBeenCalledWith(response.error);
});
it("should call onLogoutError with error if logout fails", async () => {
(TuitioClient as jest.Mocked<any>).mockImplementation(() => ({
logout: jest.fn().mockRejectedValue(new Error("Login failed"))
}));
const onLogoutError = jest.fn();
const { result } = renderHook(() => useTuitioClient({ onLogoutError }));
await act(async () => {
try {
await result.current.logout();
} catch (error) {
expect(onLogoutError).toHaveBeenCalledWith(error);
}
});
});
it("should throw exception if bots result and error are null", async () => {
(TuitioClient as jest.Mocked<any>).mockImplementation(() => ({
login: jest.fn().mockResolvedValue({
result: null,
error: null
}),
logout: jest.fn().mockResolvedValue({
result: null,
error: null
})
}));
const { result } = renderHook(() => useTuitioClient()); const { result } = renderHook(() => useTuitioClient());
act(() => { await act(async () => {
result.current.logout(); try {
}); await result.current.login(userName, password);
} catch (error) {
expect(error).toBeDefined();
}
expect(invalidate).toHaveBeenCalled(); try {
await result.current.logout();
} catch (error) {
expect(error).toBeDefined();
}
});
}); });
}); });

View File

@ -2,51 +2,47 @@ import React from "react";
import { renderHook } from "@testing-library/react-hooks"; import { renderHook } from "@testing-library/react-hooks";
import { TuitioContext, TuitioDispatchContext } from "../../src/contexts"; import { TuitioContext, TuitioDispatchContext } from "../../src/contexts";
import { useTuitioClient } from "../../src/hooks"; import { useTuitioClient } from "../../src/hooks";
import { TuitioToken } from "@flare/tuitio-client";
const userName = "tuitio.user";
const token = "mocked-token";
const expiresIn = 600000;
jest.mock("@flare/tuitio-client", () => ({ jest.mock("@flare/tuitio-client", () => ({
TuitioClient: jest.fn().mockImplementation(() => ({ TuitioClient: jest.fn().mockImplementation(() => ({
authenticate: jest.fn().mockResolvedValue({ login: jest.fn().mockResolvedValue({
token: { result: { token, expiresIn, validUntil: new Date() },
raw: "mocked-token", error: null
validFrom: new Date(), }),
validUntil: new Date() logout: jest.fn().mockResolvedValue({
}, result: { userId: 0, userName, logoutDate: new Date() },
status: "success" error: null
}) })
})), })),
invalidate: jest.fn(),
fetch: jest.fn().mockResolvedValue({ fetch: jest.fn().mockResolvedValue({
token: { token,
raw: "mocked-token", validUntil: new Date(),
validFrom: new Date(), userName
validUntil: new Date()
},
userName: "user"
}) })
})); }));
describe("useTuitioClient: dispatchActions", () => { describe("useTuitioClient: dispatchActions", () => {
let spyOnLoginSuccess: (token: TuitioToken, userName: string) => void; let spyOnLoginSuccess: (token: string, validUntil: Date, userName: string) => void;
let spyOnLogout: () => void; let spyOnLogoutSuccess: () => void;
beforeEach(() => { beforeEach(() => {
spyOnLoginSuccess = jest.fn(); spyOnLoginSuccess = jest.fn();
spyOnLogout = jest.fn(); spyOnLogoutSuccess = jest.fn();
}); });
afterEach(() => { afterEach(() => {
(spyOnLoginSuccess as jest.Mock).mockReset(); (spyOnLoginSuccess as jest.Mock).mockReset();
(spyOnLogout as jest.Mock).mockReset(); (spyOnLogoutSuccess as jest.Mock).mockReset();
}); });
const tuitioContextState = { const tuitioContextState = {
userName: "", userName: "",
token: { token: "mocked-token",
raw: "mocked-token", validUntil: new Date(),
validFrom: new Date(),
validUntil: new Date()
},
configuration: { tuitioUrl: null } configuration: { tuitioUrl: null }
}; };
@ -56,7 +52,9 @@ describe("useTuitioClient: dispatchActions", () => {
const wrapper = ({ children }: Props) => ( const wrapper = ({ children }: Props) => (
<TuitioContext.Provider value={tuitioContextState}> <TuitioContext.Provider value={tuitioContextState}>
<TuitioDispatchContext.Provider value={{ onLoginSuccess: spyOnLoginSuccess, onLogout: spyOnLogout }}> <TuitioDispatchContext.Provider
value={{ onLoginSuccess: spyOnLoginSuccess, onLogoutSuccess: spyOnLogoutSuccess }}
>
{children} {children}
</TuitioDispatchContext.Provider> </TuitioDispatchContext.Provider>
</TuitioContext.Provider> </TuitioContext.Provider>
@ -75,12 +73,12 @@ describe("useTuitioClient: dispatchActions", () => {
const password = "password"; const password = "password";
const response = await result.current.login(userName, password); const response = await result.current.login(userName, password);
expect(spyOnLoginSuccess).toHaveBeenCalledWith(response.token, userName); expect(spyOnLoginSuccess).toHaveBeenCalledWith(response.result?.token, response.result?.validUntil, userName);
}); });
it("onLogout should be called when user logs out", () => { it("onLogoutSuccess should be called when user logs out", async () => {
const { result } = renderHook(() => useTuitioClient(optionMock), { wrapper }); const { result } = renderHook(() => useTuitioClient(optionMock), { wrapper });
result.current.logout(); await result.current.logout();
expect(spyOnLogout).toHaveBeenCalled(); expect(spyOnLogoutSuccess).toHaveBeenCalled();
}); });
}); });

View File

@ -6,30 +6,22 @@ import { useTuitioToken } from "../../src/hooks";
describe("useTuitioToken: positive flow", () => { describe("useTuitioToken: positive flow", () => {
const testState = { const testState = {
userName: "test-user", userName: "test-user",
token: { token: "mocked-token",
raw: "mocked-token", validUntil: new Date(),
validFrom: new Date(),
validUntil: new Date()
},
configuration: { configuration: {
tuitioUrl: null tuitioUrl: null
} }
}; };
testState.token.validFrom.setHours(testState.token.validFrom.getHours() - 1); testState.validUntil.setHours(testState.validUntil.getHours() + 1);
testState.token.validUntil.setHours(testState.token.validUntil.getHours() + 1);
const wrapper = ({ children }: { children?: React.ReactNode }) => ( const wrapper = ({ children }: { children?: React.ReactNode }) => (
<TuitioContext.Provider value={testState}>{children}</TuitioContext.Provider> <TuitioContext.Provider value={testState}>{children}</TuitioContext.Provider>
); );
it("should return the correct token object", () => { it("should return the correct token", () => {
const { result } = renderHook(() => useTuitioToken(), { wrapper }); const { result } = renderHook(() => useTuitioToken(), { wrapper });
expect(result.current.token).toEqual({ expect(result.current.token).toEqual("mocked-token");
raw: "mocked-token",
validFrom: expect.any(Date),
validUntil: expect.any(Date)
});
}); });
it("should return false value for valid", () => { it("should return false value for valid", () => {
@ -48,6 +40,7 @@ describe("useTuitioToken: negative flow", () => {
const testState = { const testState = {
userName: "test-user", userName: "test-user",
token: null, token: null,
validUntil: null,
configuration: { configuration: {
tuitioUrl: null tuitioUrl: null
} }

View File

@ -4,14 +4,11 @@ import { TuitioContext } from "../../src/contexts";
import { useTuitioUser } from "../../src/hooks"; import { useTuitioUser } from "../../src/hooks";
describe("useTuitioUser: positive flow", () => { describe("useTuitioUser: positive flow", () => {
it("should return the userName and lastLoginDate from the TuitioContext", () => { it("should return the userName from the TuitioContext", () => {
const testState = { const testState = {
userName: "test-user", userName: "test-user",
token: { token: "mocked-token",
raw: "mocked-token", validUntil: new Date(2023, 12, 31),
validFrom: new Date(2023, 1, 1),
validUntil: new Date(2023, 12, 31)
},
configuration: { configuration: {
tuitioUrl: null tuitioUrl: null
} }
@ -23,26 +20,5 @@ describe("useTuitioUser: positive flow", () => {
const { result } = renderHook(() => useTuitioUser(), { wrapper }); const { result } = renderHook(() => useTuitioUser(), { wrapper });
expect(result.current.userName).toEqual("test-user"); expect(result.current.userName).toEqual("test-user");
expect(result.current.lastLoginDate).toEqual(new Date(2023, 1, 1));
});
});
describe("useTuitioUser: negative flow", () => {
it("should return the userName and lastLoginDate from the TuitioContext", () => {
const testState = {
userName: "test-user",
token: null,
configuration: {
tuitioUrl: null
}
};
const wrapper = ({ children }: { children?: React.ReactNode }) => (
<TuitioContext.Provider value={testState}>{children}</TuitioContext.Provider>
);
const { result } = renderHook(() => useTuitioUser(), { wrapper });
expect(result.current.userName).toEqual("test-user");
expect(result.current.lastLoginDate).toBe(undefined);
}); });
}); });

View File

@ -1,28 +1,24 @@
import { initialDispatchActions } from "../src/initialState"; import { initialDispatchActions } from "../src/initialState";
import { TuitioToken } from "@flare/tuitio-client";
describe("Initial dispatch actions", () => { describe("Initial dispatch actions", () => {
it("should have a property `onLoginSuccess` that is a function", () => { it("should have a property `onLoginSuccess` that is a function", () => {
expect(initialDispatchActions.onLoginSuccess).toBeInstanceOf(Function); expect(initialDispatchActions.onLoginSuccess).toBeInstanceOf(Function);
}); });
it("should have a property `onLogout` that is a function", () => { it("should have a property `onLogoutSuccess` that is a function", () => {
expect(initialDispatchActions.onLogout).toBeInstanceOf(Function); expect(initialDispatchActions.onLogoutSuccess).toBeInstanceOf(Function);
}); });
it("onLoginSuccess function must return undefined", () => { it("onLoginSuccess function must return undefined", () => {
const mockToken: TuitioToken = { const mockToken = "abc123";
raw: "abc123", const validUntil = new Date("2023-12-31T23:59:59.999Z");
validFrom: new Date("2023-01-01T00:00:00.000Z"),
validUntil: new Date("2023-12-31T23:59:59.999Z")
};
const result = initialDispatchActions.onLoginSuccess(mockToken, "user"); const result = initialDispatchActions.onLoginSuccess(mockToken, validUntil, "user");
expect(result).toBe(undefined); expect(result).toBe(undefined);
}); });
it("onLogout function must return undefined", () => { it("onLogoutSuccess function must return undefined", () => {
const result = initialDispatchActions.onLogout(); const result = initialDispatchActions.onLogoutSuccess();
expect(result).toBe(undefined); expect(result).toBe(undefined);
}); });
}); });

View File

@ -1,9 +1,12 @@
import { reducer, dispatchActions } from "../src/reducer"; import { reducer, dispatchActions } from "../src/reducer";
import { TuitioToken } from "@flare/tuitio-client";
import { TuitioReactState, TuitioDispatchActions } from "../src/types"; import { TuitioReactState, TuitioDispatchActions } from "../src/types";
import { initialState } from "../src/initialState"; import { initialState } from "../src/initialState";
import { Dispatch } from "react"; import { Dispatch } from "react";
const userName = "tuitio.user";
const token = "abc123";
const validUntil = new Date();
describe("reducer", () => { describe("reducer", () => {
it("should return the initial state by default", () => { it("should return the initial state by default", () => {
const result = reducer(undefined, { type: "UNKNOWN_ACTION" }); const result = reducer(undefined, { type: "UNKNOWN_ACTION" });
@ -11,14 +14,14 @@ describe("reducer", () => {
}); });
it('should handle the "onLoginSuccess" action', () => { it('should handle the "onLoginSuccess" action', () => {
const token: TuitioToken = { raw: "abc123", validFrom: new Date(), validUntil: new Date() }; const result = reducer(initialState, { type: "onLoginSuccess", token, validUntil, userName });
const result = reducer(initialState, { type: "onLoginSuccess", token, userName: "user" }); const expected = { ...initialState, token, validUntil, userName };
expect(result).toEqual({ ...initialState, token, userName: "user" }); expect(result).toEqual(expected);
}); });
it('should handle the "onLogout" action', () => { it('should handle the "onLogoutSuccess" action', () => {
const state: TuitioReactState = { ...initialState, configuration: { tuitioUrl: "https://test.com/api" } }; const state: TuitioReactState = { ...initialState, configuration: { tuitioUrl: "https://test.com/api" } };
const result = reducer(state, { type: "onLogout" }); const result = reducer(state, { type: "onLogoutSuccess" });
expect(result).toEqual({ ...initialState, configuration: state.configuration }); expect(result).toEqual({ ...initialState, configuration: state.configuration });
}); });
}); });
@ -33,14 +36,12 @@ describe("dispatchActions", () => {
}); });
it("should dispatch an action with type 'onLoginSuccess' and payload when 'onLoginSuccess' is called", () => { it("should dispatch an action with type 'onLoginSuccess' and payload when 'onLoginSuccess' is called", () => {
const token: TuitioToken = { raw: "test", validFrom: new Date(), validUntil: new Date() }; actions.onLoginSuccess(token, validUntil, userName);
const userName = "test"; expect(dispatch).toHaveBeenCalledWith({ type: "onLoginSuccess", token, validUntil, userName });
actions.onLoginSuccess(token, userName);
expect(dispatch).toHaveBeenCalledWith({ type: "onLoginSuccess", token, userName });
}); });
it("should dispatch an action with type 'onLogout' when 'onLogout' is called", () => { it("should dispatch an action with type 'onLogoutSuccess' when 'onLogoutSuccess' is called", () => {
actions.onLogout(); actions.onLogoutSuccess();
expect(dispatch).toHaveBeenCalledWith({ type: "onLogout" }); expect(dispatch).toHaveBeenCalledWith({ type: "onLogoutSuccess" });
}); });
}); });