review(front): refacto url-manager (#8861)

Replace https://github.com/twentyhq/twenty/pull/8855
This commit is contained in:
Antoine Moreaux
2024-12-05 11:47:51 +01:00
committed by GitHub
parent 7ab00a4c82
commit 081ecbcfaf
33 changed files with 639 additions and 271 deletions

View File

@ -30,8 +30,8 @@ import { useAuth } from '@/auth/hooks/useAuth';
import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken';
import { signInUpModeState } from '@/auth/states/signInUpModeState';
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
import { useUrlManager } from '@/url-manager/hooks/useUrlManager';
import { SignInUpMode } from '@/auth/types/signInUpMode';
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
const StyledContentContainer = styled(motion.div)`
margin-bottom: ${({ theme }) => theme.spacing(8)};
@ -53,8 +53,7 @@ export const SignInUpGlobalScopeForm = () => {
const { signInWithMicrosoft } = useSignInWithMicrosoft();
const { checkUserExists } = useAuth();
const { readCaptchaToken } = useReadCaptchaToken();
const { redirectToWorkspace } = useUrlManager();
const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain();
const setSignInUpStep = useSetRecoilState(signInUpStepState);
const [signInUpMode, setSignInUpMode] = useRecoilState(signInUpModeState);
@ -97,7 +96,7 @@ export const SignInUpGlobalScopeForm = () => {
isDefined(data?.checkUserExists.availableWorkspaces) &&
data.checkUserExists.availableWorkspaces.length >= 1
) {
return redirectToWorkspace(
return redirectToWorkspaceDomain(
data?.checkUserExists.availableWorkspaces[0].subdomain,
pathname,
{

View File

@ -0,0 +1,72 @@
import { act, renderHook } from '@testing-library/react';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useEmailPasswordResetLinkMutation } from '~/generated/graphql';
import { useHandleResetPassword } from '@/auth/sign-in-up/hooks/useHandleResetPassword';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
// Mocks
jest.mock('@/ui/feedback/snack-bar-manager/hooks/useSnackBar');
jest.mock('~/generated/graphql');
describe('useHandleResetPassword', () => {
const enqueueSnackBarMock = jest.fn();
const emailPasswordResetLinkMock = jest.fn();
beforeEach(() => {
(useSnackBar as jest.Mock).mockReturnValue({
enqueueSnackBar: enqueueSnackBarMock,
});
(useEmailPasswordResetLinkMutation as jest.Mock).mockReturnValue([
emailPasswordResetLinkMock,
]);
jest.clearAllMocks();
});
it('should show error message if email is invalid', async () => {
const { result } = renderHook(() => useHandleResetPassword());
await act(() => result.current.handleResetPassword('')());
expect(enqueueSnackBarMock).toHaveBeenCalledWith('Invalid email', {
variant: SnackBarVariant.Error,
});
});
it('should show success message if password reset link is sent', async () => {
emailPasswordResetLinkMock.mockResolvedValue({
data: { emailPasswordResetLink: { success: true } },
});
const { result } = renderHook(() => useHandleResetPassword());
await act(() => result.current.handleResetPassword('test@example.com')());
expect(enqueueSnackBarMock).toHaveBeenCalledWith(
'Password reset link has been sent to the email',
{ variant: SnackBarVariant.Success },
);
});
it('should show error message if sending reset link fails', async () => {
emailPasswordResetLinkMock.mockResolvedValue({
data: { emailPasswordResetLink: { success: false } },
});
const { result } = renderHook(() => useHandleResetPassword());
await act(() => result.current.handleResetPassword('test@example.com')());
expect(enqueueSnackBarMock).toHaveBeenCalledWith('There was some issue', {
variant: SnackBarVariant.Error,
});
});
it('should show error message in case of request error', async () => {
const errorMessage = 'Network Error';
emailPasswordResetLinkMock.mockRejectedValue(new Error(errorMessage));
const { result } = renderHook(() => useHandleResetPassword());
await act(() => result.current.handleResetPassword('test@example.com')());
expect(enqueueSnackBarMock).toHaveBeenCalledWith(errorMessage, {
variant: SnackBarVariant.Error,
});
});
});

View File

@ -0,0 +1,73 @@
import { renderHook } from '@testing-library/react';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useGetAuthorizationUrlMutation } from '~/generated/graphql';
import { useSSO } from '@/auth/sign-in-up/hooks/useSSO';
// Mock dependencies
jest.mock('@/ui/feedback/snack-bar-manager/hooks/useSnackBar');
jest.mock('~/generated/graphql');
// Helpers
const mockEnqueueSnackBar = jest.fn();
const mockGetAuthorizationUrlMutation = jest.fn();
// Mock return values
(useSnackBar as jest.Mock).mockReturnValue({
enqueueSnackBar: mockEnqueueSnackBar,
});
(useGetAuthorizationUrlMutation as jest.Mock).mockReturnValue([
mockGetAuthorizationUrlMutation,
]);
describe('useSSO', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should call getAuthorizationUrlForSSO with correct parameters', async () => {
const { result } = renderHook(() => useSSO());
const identityProviderId = 'test-id';
mockGetAuthorizationUrlMutation.mockResolvedValueOnce({
data: {
getAuthorizationUrl: {
authorizationURL: 'http://example.com',
},
},
});
await result.current.getAuthorizationUrlForSSO({ identityProviderId });
expect(mockGetAuthorizationUrlMutation).toHaveBeenCalledWith({
variables: { input: { identityProviderId } },
});
});
it('should enqueue error snackbar when URL retrieval fails', async () => {
const { result } = renderHook(() => useSSO());
const identityProviderId = 'test-id';
mockGetAuthorizationUrlMutation.mockResolvedValueOnce({
errors: [{ message: 'Error message' }],
});
await result.current.redirectToSSOLoginPage(identityProviderId);
expect(mockEnqueueSnackBar).toHaveBeenCalledWith('Error message', {
variant: 'error',
});
});
it('should enqueue default error snackbar when error message is not provided', async () => {
const { result } = renderHook(() => useSSO());
const identityProviderId = 'test-id';
mockGetAuthorizationUrlMutation.mockResolvedValueOnce({ errors: [{}] });
await result.current.redirectToSSOLoginPage(identityProviderId);
expect(mockEnqueueSnackBar).toHaveBeenCalledWith('Unknown error', {
variant: 'error',
});
});
});

View File

@ -0,0 +1,55 @@
import { renderHook } from '@testing-library/react';
import { useParams, useSearchParams } from 'react-router-dom';
import { useAuth } from '@/auth/hooks/useAuth';
import { useSignInWithGoogle } from '@/auth/sign-in-up/hooks/useSignInWithGoogle';
jest.mock('react-router-dom', () => ({
useParams: jest.fn(),
useSearchParams: jest.fn(),
}));
jest.mock('@/auth/hooks/useAuth', () => ({
useAuth: jest.fn(),
}));
describe('useSignInWithGoogle', () => {
it('should call signInWithGoogle with correct params', () => {
const signInWithGoogleMock = jest.fn();
const mockUseParams = { workspaceInviteHash: 'testHash' };
const mockSearchParams = new URLSearchParams('inviteToken=testToken');
(useParams as jest.Mock).mockReturnValue(mockUseParams);
(useSearchParams as jest.Mock).mockReturnValue([mockSearchParams]);
(useAuth as jest.Mock).mockReturnValue({
signInWithGoogle: signInWithGoogleMock,
});
const { result } = renderHook(() => useSignInWithGoogle());
result.current.signInWithGoogle();
expect(signInWithGoogleMock).toHaveBeenCalledWith({
workspaceInviteHash: 'testHash',
workspacePersonalInviteToken: 'testToken',
});
});
it('should call signInWithGoogle with undefined invite token if not present', () => {
const signInWithGoogleMock = jest.fn();
const mockUseParams = { workspaceInviteHash: 'testHash' };
const mockSearchParams = new URLSearchParams();
(useParams as jest.Mock).mockReturnValue(mockUseParams);
(useSearchParams as jest.Mock).mockReturnValue([mockSearchParams]);
(useAuth as jest.Mock).mockReturnValue({
signInWithGoogle: signInWithGoogleMock,
});
const { result } = renderHook(() => useSignInWithGoogle());
result.current.signInWithGoogle();
expect(signInWithGoogleMock).toHaveBeenCalledWith({
workspaceInviteHash: 'testHash',
workspacePersonalInviteToken: undefined,
});
});
});

View File

@ -0,0 +1,60 @@
import { renderHook } from '@testing-library/react';
import { useParams, useSearchParams } from 'react-router-dom';
import { useAuth } from '@/auth/hooks/useAuth';
import { useSignInWithMicrosoft } from '@/auth/sign-in-up/hooks/useSignInWithMicrosoft';
jest.mock('react-router-dom', () => ({
useParams: jest.fn(),
useSearchParams: jest.fn(),
}));
jest.mock('@/auth/hooks/useAuth', () => ({
useAuth: jest.fn(),
}));
describe('useSignInWithMicrosoft', () => {
it('should call signInWithMicrosoft with the correct parameters', () => {
const workspaceInviteHashMock = 'testHash';
const inviteTokenMock = 'testToken';
const signInWithMicrosoftMock = jest.fn();
(useParams as jest.Mock).mockReturnValue({
workspaceInviteHash: workspaceInviteHashMock,
});
(useSearchParams as jest.Mock).mockReturnValue([
new URLSearchParams(`inviteToken=${inviteTokenMock}`),
]);
(useAuth as jest.Mock).mockReturnValue({
signInWithMicrosoft: signInWithMicrosoftMock,
});
const { result } = renderHook(() => useSignInWithMicrosoft());
result.current.signInWithMicrosoft();
expect(signInWithMicrosoftMock).toHaveBeenCalledWith({
workspaceInviteHash: workspaceInviteHashMock,
workspacePersonalInviteToken: inviteTokenMock,
});
});
it('should handle missing inviteToken gracefully', () => {
const workspaceInviteHashMock = 'testHash';
const signInWithMicrosoftMock = jest.fn();
(useParams as jest.Mock).mockReturnValue({
workspaceInviteHash: workspaceInviteHashMock,
});
(useSearchParams as jest.Mock).mockReturnValue([new URLSearchParams('')]);
(useAuth as jest.Mock).mockReturnValue({
signInWithMicrosoft: signInWithMicrosoftMock,
});
const { result } = renderHook(() => useSignInWithMicrosoft());
result.current.signInWithMicrosoft();
expect(signInWithMicrosoftMock).toHaveBeenCalledWith({
workspaceInviteHash: workspaceInviteHashMock,
workspacePersonalInviteToken: undefined,
});
});
});