You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. import * as redux from "react-redux";
  2. import { mockState } from "../../mockState";
  3. import * as api from "../../request/loginRequest";
  4. import * as authHelper from "../../util/helpers/authScopeHelpers";
  5. import * as reqHelper from "../../request";
  6. import { runSaga } from "redux-saga";
  7. import {
  8. fetchGoogleUserSuccess,
  9. fetchUserError,
  10. fetchUserSuccess,
  11. forgetPasswordSuccess,
  12. resetLoginState,
  13. } from "../../store/actions/login/loginActions";
  14. import jwt from "jsonwebtoken";
  15. import {
  16. authenticateUser,
  17. fetchGoogleUser,
  18. fetchUser,
  19. forgetPassword,
  20. resetPassword,
  21. logoutUser,
  22. refreshToken as ref,
  23. generateToken,
  24. } from "../../store/saga/loginSaga";
  25. import { setUser } from "../../store/actions/user/userActions";
  26. import { BASE_PAGE } from "../../constants/pages";
  27. import history from "../../store/utils/history";
  28. describe("Login Saga reducer tests", () => {
  29. let spyOnUseSelector;
  30. let spyOnUseDispatch;
  31. let mockDispatch;
  32. beforeEach(() => {
  33. // Mock useSelector hook
  34. spyOnUseSelector = jest.spyOn(redux, "useSelector");
  35. spyOnUseSelector.mockReturnValueOnce(mockState.user.user);
  36. // Mock useDispatch hook
  37. spyOnUseDispatch = jest.spyOn(redux, "useDispatch");
  38. // Mock dispatch function returned from useDispatch
  39. mockDispatch = jest.fn();
  40. spyOnUseDispatch.mockReturnValue(mockDispatch);
  41. });
  42. afterEach(() => {
  43. jest.restoreAllMocks();
  44. });
  45. it("should run fetchUser saga function with actions", async () => {
  46. const dispatchedActions = [];
  47. const res = { data: { token: "token", refreshToken: "token" } };
  48. api.attemptLogin = jest.fn(() => Promise.resolve(res));
  49. const fakeStore = {
  50. getState: () => ({
  51. email: "",
  52. errorMessage: "",
  53. }),
  54. dispatch: (action) => dispatchedActions.push(action),
  55. };
  56. const mockfn = jest.fn();
  57. await runSaga(fakeStore, fetchUser, {
  58. payload: {
  59. password: "pass",
  60. username: "username",
  61. handleApiResponseSuccess: mockfn,
  62. },
  63. }).done;
  64. expect(api.attemptLogin.mock.calls.length).toBe(1);
  65. expect(mockfn).toHaveBeenCalled();
  66. expect(dispatchedActions).toContainEqual(fetchUserSuccess(res.data));
  67. expect(dispatchedActions).toContainEqual(setUser(res.data));
  68. });
  69. it("should handle fetchUser saga error with actions", async () => {
  70. const dispatchedActions = [];
  71. const res = { response: { data: { message: "Error" } } };
  72. api.attemptLogin = jest.fn(() => Promise.reject(res));
  73. const fakeStore = {
  74. getState: () => ({
  75. email: "",
  76. errorMessage: "",
  77. }),
  78. dispatch: (action) => dispatchedActions.push(action),
  79. };
  80. const mockfn = jest.fn();
  81. await runSaga(fakeStore, fetchUser, {
  82. payload: {
  83. password: "pass",
  84. username: "username",
  85. handleApiResponseFailed: mockfn,
  86. },
  87. }).done;
  88. expect(api.attemptLogin.mock.calls.length).toBe(1);
  89. expect(mockfn).toHaveBeenCalled();
  90. expect(dispatchedActions).toContainEqual(fetchUserError(res.data));
  91. });
  92. it("should run forgetPassword saga function with actions", async () => {
  93. const dispatchedActions = [];
  94. const res = { data: {} };
  95. api.forgetPasswordEmailSend = jest.fn(() => Promise.resolve(res));
  96. const fakeStore = {
  97. getState: () => ({
  98. email: "",
  99. errorMessage: "",
  100. }),
  101. dispatch: (action) => dispatchedActions.push(action),
  102. };
  103. const mockfn = jest.fn();
  104. await runSaga(fakeStore, forgetPassword, {
  105. payload: {
  106. email: "email",
  107. handleApiResponseSuccess: mockfn,
  108. },
  109. }).done;
  110. expect(api.forgetPasswordEmailSend.mock.calls.length).toBe(1);
  111. expect(mockfn).toHaveBeenCalled();
  112. expect(dispatchedActions).toContainEqual(forgetPasswordSuccess(res.data));
  113. });
  114. it("should handle forgetPassword saga error with actions", async () => {
  115. const dispatchedActions = [];
  116. const res = { response: { data: { message: "Error" } } };
  117. api.forgetPasswordEmailSend = jest.fn(() => Promise.reject(res));
  118. const fakeStore = {
  119. getState: () => ({
  120. email: "",
  121. errorMessage: "",
  122. }),
  123. dispatch: (action) => dispatchedActions.push(action),
  124. };
  125. const mockfn = jest.fn();
  126. await runSaga(fakeStore, forgetPassword, {
  127. payload: {
  128. password: "pass",
  129. username: "username",
  130. handleApiResponseFailed: mockfn,
  131. },
  132. }).done;
  133. expect(api.forgetPasswordEmailSend.mock.calls.length).toBe(1);
  134. expect(mockfn).toHaveBeenCalled();
  135. expect(dispatchedActions).toContainEqual(fetchUserError(res.data));
  136. });
  137. it("should run reset password saga function with actions", async () => {
  138. const dispatchedActions = [];
  139. const res = { data: {} };
  140. api.sendResetPassword = jest.fn(() => Promise.resolve(res));
  141. const fakeStore = {
  142. getState: () => ({
  143. email: "",
  144. errorMessage: "",
  145. }),
  146. dispatch: (action) => dispatchedActions.push(action),
  147. };
  148. const mockfn = jest.fn();
  149. await runSaga(fakeStore, resetPassword, {
  150. payload: {
  151. code: "code",
  152. email: "mail",
  153. password: "password",
  154. handleApiResponseSuccess: mockfn,
  155. },
  156. }).done;
  157. expect(api.sendResetPassword.mock.calls.length).toBe(1);
  158. expect(mockfn).toHaveBeenCalled();
  159. expect(dispatchedActions).toContainEqual(forgetPasswordSuccess(res.data));
  160. });
  161. it("should handle forgetPassword saga error with actions", async () => {
  162. const dispatchedActions = [];
  163. const res = { response: { data: { message: "Error" } } };
  164. api.sendResetPassword = jest.fn(() => Promise.reject(res));
  165. const fakeStore = {
  166. getState: () => ({
  167. email: "",
  168. errorMessage: "",
  169. }),
  170. dispatch: (action) => dispatchedActions.push(action),
  171. };
  172. const mockfn = jest.fn();
  173. await runSaga(fakeStore, resetPassword, {
  174. payload: {
  175. code: "code",
  176. email: "mail",
  177. password: "password",
  178. handleApiResponseFailed: mockfn,
  179. },
  180. }).done;
  181. expect(api.sendResetPassword.mock.calls.length).toBe(1);
  182. expect(mockfn).toHaveBeenCalled();
  183. expect(dispatchedActions).toContainEqual(fetchUserError(res.data));
  184. });
  185. it("should run fetch google user saga function with actions", async () => {
  186. const dispatchedActions = [];
  187. const res = { data: { token: "token" } };
  188. api.attemptGoogleLogin = jest.fn(() => Promise.resolve(res));
  189. authHelper.authScopeSetHelper = jest.fn();
  190. const fakeStore = {
  191. getState: () => ({
  192. email: "",
  193. errorMessage: "",
  194. }),
  195. dispatch: (action) => dispatchedActions.push(action),
  196. };
  197. const mockfn = jest.fn();
  198. await runSaga(fakeStore, fetchGoogleUser, {
  199. payload: {
  200. user: "mail",
  201. token: "token",
  202. handleApiResponseSuccess: mockfn,
  203. },
  204. }).done;
  205. expect(api.attemptGoogleLogin.mock.calls.length).toBe(1);
  206. expect(mockfn).toHaveBeenCalled();
  207. expect(authHelper.authScopeSetHelper).toHaveBeenCalled();
  208. expect(dispatchedActions).toContainEqual(fetchGoogleUserSuccess(res.data));
  209. expect(dispatchedActions).toContainEqual({
  210. payload: { token: "token" },
  211. type: "[SUCCESS]LOGIN_GOOGLE_USER",
  212. });
  213. });
  214. it("should handle fetch google error with actions", async () => {
  215. const dispatchedActions = [];
  216. const res = { response: { data: { message: "Error" } } };
  217. api.attemptGoogleLogin = jest.fn(() => Promise.reject(res));
  218. const fakeStore = {
  219. getState: () => ({
  220. email: "",
  221. errorMessage: "",
  222. }),
  223. dispatch: (action) => dispatchedActions.push(action),
  224. };
  225. const mockfn = jest.fn();
  226. await runSaga(fakeStore, fetchGoogleUser, {
  227. payload: {
  228. user: "mail",
  229. token: "token",
  230. handleApiResponseFailed: mockfn,
  231. },
  232. }).done;
  233. expect(api.attemptGoogleLogin.mock.calls.length).toBe(1);
  234. expect(mockfn).toHaveBeenCalled();
  235. expect(dispatchedActions).toContainEqual(fetchUserError(res.data));
  236. });
  237. it("should run authenticate user saga function with actions if token does not exist", async () => {
  238. const dispatchedActions = [];
  239. const res = { data: { token: null } };
  240. api.attemptGoogleLogin = jest.fn(() => Promise.resolve(res));
  241. authHelper.authScopeStringGetHelper = jest.fn(() => "");
  242. const mockHistoryPush = jest.fn();
  243. history.push = mockHistoryPush;
  244. const fakeStore = {
  245. getState: () => ({
  246. email: "",
  247. errorMessage: "",
  248. }),
  249. dispatch: (action) => dispatchedActions.push(action),
  250. };
  251. await runSaga(fakeStore, authenticateUser).done;
  252. expect(mockHistoryPush).toHaveBeenCalledWith(BASE_PAGE);
  253. expect(dispatchedActions).not.toContainEqual(
  254. fetchUserSuccess(res.data.token)
  255. );
  256. });
  257. it("should run authenticate user saga function with actions if token exists", async () => {
  258. const dispatchedActions = [];
  259. const res = { data: { token: "token" } };
  260. api.attemptGoogleLogin = jest.fn(() => Promise.resolve(res));
  261. authHelper.authScopeStringGetHelper = jest.fn(() => "token");
  262. const mockHistoryPush = jest.fn();
  263. history.push = mockHistoryPush;
  264. const fakeStore = {
  265. getState: () => ({
  266. email: "",
  267. errorMessage: "",
  268. }),
  269. dispatch: (action) => dispatchedActions.push(action),
  270. };
  271. await runSaga(fakeStore, authenticateUser).done;
  272. expect(mockHistoryPush).not.toHaveBeenCalled();
  273. expect(dispatchedActions).toContainEqual({
  274. payload: { JwtToken: "token" },
  275. type: "[SUCCESS]LOGIN_USER",
  276. });
  277. });
  278. it("should run authenticate user saga function and handle errors", async () => {
  279. const dispatchedActions = [];
  280. const res = { response: { data: { message: "message" } } };
  281. authHelper.authScopeStringGetHelper = jest.fn(() => Promise.reject(res));
  282. const fakeStore = {
  283. getState: () => ({
  284. email: "",
  285. errorMessage: "",
  286. }),
  287. dispatch: (action) => dispatchedActions.push(action),
  288. };
  289. await runSaga(fakeStore, authenticateUser).done;
  290. expect(dispatchedActions).toContainEqual({
  291. // undefined because we have not mocked rejectCodeHelper
  292. payload: undefined,
  293. type: "[ERROR]LOGIN_USER",
  294. });
  295. });
  296. it("should run logout user saga function with actions", async () => {
  297. const dispatchedActions = [];
  298. const res = { data: { token: "token" } };
  299. // simply mock it to ensure we can test if method has been called
  300. api.logoutUserRequest = jest.fn(() => Promise.resolve(res));
  301. authHelper.authScopeStringGetHelper = jest.fn(() => "token");
  302. authHelper.authScopeClearHelper = jest.fn();
  303. reqHelper.removeHeaderToken = jest.fn();
  304. const mockUser = { id: 1 };
  305. jwt.decode = jest.fn(() => mockUser);
  306. const mockHistoryReplace = jest.fn();
  307. history.replace = mockHistoryReplace;
  308. const fakeStore = {
  309. getState: () => ({
  310. email: "",
  311. errorMessage: "",
  312. }),
  313. dispatch: (action) => dispatchedActions.push(action),
  314. };
  315. await runSaga(fakeStore, logoutUser).done;
  316. expect(mockHistoryReplace).toHaveBeenCalled();
  317. expect(authHelper.authScopeClearHelper).toHaveBeenCalled();
  318. expect(reqHelper.removeHeaderToken).toHaveBeenCalled();
  319. expect(api.logoutUserRequest).toHaveBeenCalledWith(mockUser.id);
  320. expect(dispatchedActions).toContainEqual(resetLoginState());
  321. });
  322. it("should run refreshToken saga function with actions", async () => {
  323. const dispatchedActions = [];
  324. const res = { data: { user: "user", token: "token" } };
  325. api.refreshTokenRequest = jest.fn(() => Promise.resolve(res));
  326. authHelper.authScopeStringGetHelper = jest.fn();
  327. authHelper.authScopeStringGetHelper
  328. .mockReturnValueOnce("Token")
  329. .mockReturnValueOnce("RefreshToken");
  330. authHelper.authScopeSetHelper = jest.fn();
  331. reqHelper.addHeaderToken = jest.fn();
  332. const fakeStore = {
  333. getState: () => ({
  334. email: "",
  335. errorMessage: "",
  336. }),
  337. dispatch: (action) => dispatchedActions.push(action),
  338. };
  339. await runSaga(fakeStore, ref).done;
  340. expect(authHelper.authScopeStringGetHelper).toHaveBeenCalled();
  341. expect(api.refreshTokenRequest).toHaveBeenCalledWith({
  342. refreshToken: "RefreshToken",
  343. token: "Token",
  344. });
  345. });
  346. it("should run generateToken saga function with actions", async () => {
  347. const dispatchedActions = [];
  348. const res = { data: { JwtToken: "jwt", JwtRefreshToken: "refresh" } };
  349. api.generateTokenRequest = jest.fn(() => Promise.resolve(res));
  350. authHelper.authScopeSetHelper = jest.fn();
  351. authHelper.authScopeStringGetHelper = jest.fn();
  352. authHelper.authScopeStringGetHelper
  353. .mockReturnValueOnce("Token")
  354. .mockReturnValueOnce("RefreshToken");
  355. reqHelper.addHeaderToken = jest.fn();
  356. const fakeStore = {
  357. getState: () => ({
  358. email: "",
  359. errorMessage: "",
  360. }),
  361. dispatch: (action) => dispatchedActions.push(action),
  362. };
  363. var mockSession = jest.fn();
  364. Storage.prototype.setItem = mockSession;
  365. const mockUser = { id: 1 };
  366. jwt.decode = jest.fn(() => mockUser);
  367. const mockSuccess = jest.fn()
  368. await runSaga(fakeStore, generateToken, {
  369. payload: {
  370. data: { data: "data" },
  371. impersonate: { data: "data" },
  372. registration: { data: "data" },
  373. onSuccess: mockSuccess
  374. },
  375. }).done;
  376. expect(authHelper.authScopeSetHelper).toHaveBeenCalled();
  377. expect(mockSession).toHaveBeenCalled();
  378. expect(mockSuccess).toHaveBeenCalled();
  379. expect(dispatchedActions).toContainEqual(setUser(mockUser));
  380. });
  381. });