diff --git a/src/api/authentication.ts b/src/api/authentication.ts index bae6b3ce..d02de44e 100644 --- a/src/api/authentication.ts +++ b/src/api/authentication.ts @@ -6,6 +6,7 @@ import configureAxios, { verifyAuthentication } from './axios'; import { MOBILE_SIGN_IN_ROUTE, MOBILE_SIGN_IN_WITH_PASSWORD_ROUTE, + MOBILE_SIGN_UP_ROUTE, SIGN_IN_ROUTE, SIGN_IN_WITH_PASSWORD_ROUTE, SIGN_OUT_ROUTE, @@ -58,6 +59,11 @@ export const mobileSignInWithPassword = async ( .then(({ data }) => data); export const signUp = async ( - payload: { name: string; email: string }, + payload: { name: string; email: string; captcha: string }, { API_HOST }: QueryClientConfig, ): Promise => axios.post(`${API_HOST}/${SIGN_UP_ROUTE}`, payload); + +export const mobileSignUp = async ( + payload: { name: string; email: string; challenge: string; captcha: string }, + { API_HOST }: QueryClientConfig, +): Promise => axios.post(`${API_HOST}/${MOBILE_SIGN_UP_ROUTE}`, payload); diff --git a/src/api/routes.ts b/src/api/routes.ts index 0b68a0e0..6407b5f9 100644 --- a/src/api/routes.ts +++ b/src/api/routes.ts @@ -156,6 +156,7 @@ export const buildDownloadItemThumbnailRoute = ({ )}`; export const GET_CURRENT_MEMBER_ROUTE = `${MEMBERS_ROUTE}/current`; +export const MOBILE_SIGN_UP_ROUTE = `m/register`; export const MOBILE_SIGN_IN_ROUTE = `m/login`; export const MOBILE_SIGN_IN_WITH_PASSWORD_ROUTE = `m/login-password`; export const SIGN_IN_ROUTE = `login`; diff --git a/src/mutations/authentication.test.ts b/src/mutations/authentication.test.ts index 18c3a574..58165fbc 100644 --- a/src/mutations/authentication.test.ts +++ b/src/mutations/authentication.test.ts @@ -11,6 +11,8 @@ import { SUCCESS_MESSAGES } from '@graasp/translations'; import { OK_RESPONSE, UNAUTHORIZED_RESPONSE } from '../../test/constants'; import { mockMutation, setUpTest, waitForMutation } from '../../test/utils'; import { + MOBILE_SIGN_IN_ROUTE, + MOBILE_SIGN_IN_WITH_PASSWORD_ROUTE, SIGN_IN_ROUTE, SIGN_IN_WITH_PASSWORD_ROUTE, SIGN_OUT_ROUTE, @@ -48,6 +50,7 @@ jest.spyOn(Cookies, 'get').mockReturnValue({ session: 'somesession' }); const captcha = 'captcha'; const email = 'myemail@email.com'; +const challenge = '1234'; describe('Authentication Mutations', () => { const mockedNotifier = jest.fn(); @@ -115,6 +118,61 @@ describe('Authentication Mutations', () => { }); }); + describe('useMobileSignIn', () => { + const route = `/${MOBILE_SIGN_IN_ROUTE}`; + const mutation = mutations.useMobileSignIn; + + it(`Sign in`, async () => { + const endpoints = [ + { route, response: OK_RESPONSE, method: HttpMethod.POST }, + ]; + + const mockedMutation = await mockMutation({ + endpoints, + mutation, + wrapper, + }); + + await act(async () => { + await mockedMutation.mutate({ email, captcha, challenge }); + await waitForMutation(); + }); + + expect(mockedNotifier).toHaveBeenCalledWith({ + type: signInRoutine.SUCCESS, + payload: { message: SUCCESS_MESSAGES.SIGN_IN }, + }); + }); + + it(`Unauthorized`, async () => { + const endpoints = [ + { + route, + response: UNAUTHORIZED_RESPONSE, + method: HttpMethod.POST, + statusCode: StatusCodes.UNAUTHORIZED, + }, + ]; + + const mockedMutation = await mockMutation({ + endpoints, + mutation, + wrapper, + }); + + await act(async () => { + await mockedMutation.mutate({ email, captcha }); + await waitForMutation(); + }); + + expect(mockedNotifier).toHaveBeenCalledWith( + expect.objectContaining({ + type: signInRoutine.FAILURE, + }), + ); + }); + }); + describe('useSignInWithPassword', () => { const route = `/${SIGN_IN_WITH_PASSWORD_ROUTE}`; const mutation = mutations.useSignInWithPassword; @@ -182,6 +240,73 @@ describe('Authentication Mutations', () => { }); }); + describe('useMobileSignInWithPassword', () => { + const route = `/${MOBILE_SIGN_IN_WITH_PASSWORD_ROUTE}`; + const mutation = mutations.useMobileSignInWithPassword; + const password = 'password'; + const link = 'mylink'; + + it(`Sign in with password`, async () => { + const endpoints = [ + { + route, + response: { resource: link }, + statusCode: StatusCodes.SEE_OTHER, + method: HttpMethod.POST, + }, + ]; + // set random data in cache + queryClient.setQueryData(CURRENT_MEMBER_KEY, 'somevalue'); + + const mockedMutation = await mockMutation({ + endpoints, + mutation, + wrapper, + }); + + await act(async () => { + await mockedMutation.mutate({ email, password, captcha, challenge }); + await waitForMutation(); + }); + + // verify cache keys + expect(queryClient.getQueryData(CURRENT_MEMBER_KEY)).toBeFalsy(); + + expect(mockedNotifier).toHaveBeenCalledWith({ + type: signInWithPasswordRoutine.SUCCESS, + payload: { message: SUCCESS_MESSAGES.SIGN_IN_WITH_PASSWORD }, + }); + }); + + it(`Unauthorized`, async () => { + const endpoints = [ + { + route, + response: UNAUTHORIZED_RESPONSE, + method: HttpMethod.POST, + statusCode: StatusCodes.UNAUTHORIZED, + }, + ]; + + const mockedMutation = await mockMutation({ + endpoints, + mutation, + wrapper, + }); + + await act(async () => { + await mockedMutation.mutate({ email, captcha }); + await waitForMutation(); + }); + + expect(mockedNotifier).toHaveBeenCalledWith( + expect.objectContaining({ + type: signInWithPasswordRoutine.FAILURE, + }), + ); + }); + }); + describe('useUpdatePassword', () => { const route = `/${buildUpdateMemberPasswordRoute()}`; const mutation = mutations.useUpdatePassword; @@ -301,6 +426,61 @@ describe('Authentication Mutations', () => { ); }); }); + describe('useMobileSignUp', () => { + const route = `/${SIGN_UP_ROUTE}`; + const mutation = mutations.useMobileSignUp; + const name = 'name'; + + it(`Sign up`, async () => { + const endpoints = [ + { route, response: OK_RESPONSE, method: HttpMethod.POST }, + ]; + + const mockedMutation = await mockMutation({ + endpoints, + mutation, + wrapper, + }); + + await act(async () => { + await mockedMutation.mutate({ email, name, captcha, challenge }); + await waitForMutation(); + }); + + expect(mockedNotifier).toHaveBeenCalledWith({ + type: signUpRoutine.SUCCESS, + payload: { message: SUCCESS_MESSAGES.SIGN_UP }, + }); + }); + + it(`Unauthorized`, async () => { + const endpoints = [ + { + route, + response: UNAUTHORIZED_RESPONSE, + method: HttpMethod.POST, + statusCode: StatusCodes.UNAUTHORIZED, + }, + ]; + + const mockedMutation = await mockMutation({ + endpoints, + mutation, + wrapper, + }); + + await act(async () => { + await mockedMutation.mutate({ email, name, captcha }); + await waitForMutation(); + }); + + expect(mockedNotifier).toHaveBeenCalledWith( + expect.objectContaining({ + type: signUpRoutine.FAILURE, + }), + ); + }); + }); describe('useSignOut', () => { const route = `/${SIGN_OUT_ROUTE}`; diff --git a/src/mutations/authentication.ts b/src/mutations/authentication.ts index 0be626d9..7b441bef 100644 --- a/src/mutations/authentication.ts +++ b/src/mutations/authentication.ts @@ -159,6 +159,30 @@ export default (queryConfig: QueryClientConfig) => { }, ); + const useMobileSignUp = () => + useMutation( + (payload: { + name: string; + email: string; + challenge: string; + captcha: string; + }) => Api.mobileSignUp(payload, queryConfig), + { + onSuccess: () => { + notifier?.({ + type: signUpRoutine.SUCCESS, + payload: { message: SUCCESS_MESSAGES.SIGN_UP }, + }); + }, + onError: (error: Error) => { + notifier?.({ + type: signUpRoutine.FAILURE, + payload: { error }, + }); + }, + }, + ); + const useSignOut = () => { const queryClient = useQueryClient(); return useMutation((_currentMemberId: UUID) => Api.signOut(queryConfig), { @@ -227,6 +251,7 @@ export default (queryConfig: QueryClientConfig) => { useSignInWithPassword, useSignOut, useSignUp, + useMobileSignUp, useUpdatePassword, useMobileSignIn, useMobileSignInWithPassword,