1.1.0 - In this version, the account logout method and the latest changes published by Tuitio were implemented.
parent
67035fbc7e
commit
8e200a1d14
|
@ -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.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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" })
|
||||||
});
|
});
|
||||||
|
|
15
src/types.ts
15
src/types.ts
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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" });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue