Add tests for modules/auth and modules/command-menu (#3548)
Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com> Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: Matheus <matheus_benini@hotmail.com>
This commit is contained in:
@ -0,0 +1,111 @@
|
||||
import {
|
||||
ChallengeDocument,
|
||||
SignUpDocument,
|
||||
VerifyDocument,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
export const queries = {
|
||||
challenge: ChallengeDocument,
|
||||
verify: VerifyDocument,
|
||||
signup: SignUpDocument,
|
||||
};
|
||||
|
||||
export const email = 'test@test.com';
|
||||
export const password = 'testing';
|
||||
export const token =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
|
||||
|
||||
export const variables = {
|
||||
challenge: {
|
||||
email,
|
||||
password,
|
||||
},
|
||||
verify: { loginToken: token },
|
||||
signup: {},
|
||||
};
|
||||
|
||||
export const results = {
|
||||
challenge: {
|
||||
loginToken: {
|
||||
token,
|
||||
expiresAt: '2022-01-01',
|
||||
},
|
||||
},
|
||||
verify: {
|
||||
user: {
|
||||
id: 'id',
|
||||
firstName: 'firstName',
|
||||
lastName: 'lastName',
|
||||
email: 'email',
|
||||
canImpersonate: 'canImpersonate',
|
||||
supportUserHash: 'supportUserHash',
|
||||
workspaceMember: {
|
||||
id: 'id',
|
||||
name: {
|
||||
firstName: 'firstName',
|
||||
lastName: 'lastName',
|
||||
},
|
||||
colorScheme: 'colorScheme',
|
||||
avatarUrl: 'avatarUrl',
|
||||
locale: 'locale',
|
||||
},
|
||||
defaultWorkspace: {
|
||||
id: 'id',
|
||||
displayName: 'displayName',
|
||||
logo: 'logo',
|
||||
domainName: 'domainName',
|
||||
inviteHash: 'inviteHash',
|
||||
allowImpersonation: true,
|
||||
subscriptionStatus: 'subscriptionStatus',
|
||||
featureFlags: {
|
||||
id: 'id',
|
||||
key: 'key',
|
||||
value: 'value',
|
||||
workspaceId: 'workspaceId',
|
||||
},
|
||||
},
|
||||
},
|
||||
tokens: {
|
||||
accessToken: { token, expiresAt: 'expiresAt' },
|
||||
refreshToken: { token, expiresAt: 'expiresAt' },
|
||||
},
|
||||
signup: {},
|
||||
},
|
||||
signUp: { loginToken: { token, expiresAt: 'expiresAt' } },
|
||||
};
|
||||
|
||||
export const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: queries.challenge,
|
||||
variables: variables.challenge,
|
||||
},
|
||||
result: jest.fn(() => ({
|
||||
data: {
|
||||
challenge: results.challenge,
|
||||
},
|
||||
})),
|
||||
},
|
||||
{
|
||||
request: {
|
||||
query: queries.verify,
|
||||
variables: variables.verify,
|
||||
},
|
||||
result: jest.fn(() => ({
|
||||
data: {
|
||||
verify: results.verify,
|
||||
},
|
||||
})),
|
||||
},
|
||||
{
|
||||
request: {
|
||||
query: queries.signup,
|
||||
variables: variables.challenge,
|
||||
},
|
||||
result: jest.fn(() => ({
|
||||
data: {
|
||||
signUp: results.signUp,
|
||||
},
|
||||
})),
|
||||
},
|
||||
];
|
||||
@ -0,0 +1,147 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { expect } from '@storybook/test';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
||||
import { billingState } from '@/client-config/states/billingState';
|
||||
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
|
||||
import { isSignInPrefilledState } from '@/client-config/states/isSignInPrefilledState';
|
||||
import { supportChatState } from '@/client-config/states/supportChatState';
|
||||
import { telemetryState } from '@/client-config/states/telemetryState';
|
||||
import { iconsState } from '@/ui/display/icon/states/iconsState';
|
||||
|
||||
import { email, mocks, password, results, token } from '../__mocks__/useAuth';
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
const renderHooks = () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
return useAuth();
|
||||
},
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
return { result };
|
||||
};
|
||||
|
||||
describe('useAuth', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return challenge object', async () => {
|
||||
const { result } = renderHooks();
|
||||
|
||||
await act(async () => {
|
||||
expect(await result.current.challenge(email, password)).toStrictEqual(
|
||||
results.challenge,
|
||||
);
|
||||
});
|
||||
|
||||
expect(mocks[0].result).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should verify user', async () => {
|
||||
const { result } = renderHooks();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.verify(token);
|
||||
});
|
||||
|
||||
expect(mocks[1].result).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle credential sign-in', async () => {
|
||||
const { result } = renderHooks();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.signInWithCredentials(email, password);
|
||||
});
|
||||
|
||||
expect(mocks[0].result).toHaveBeenCalled();
|
||||
expect(mocks[1].result).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle sign-out', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const client = useApolloClient();
|
||||
const icons = useRecoilValue(iconsState);
|
||||
const authProviders = useRecoilValue(authProvidersState);
|
||||
const billing = useRecoilValue(billingState);
|
||||
const isSignInPrefilled = useRecoilValue(isSignInPrefilledState);
|
||||
const supportChat = useRecoilValue(supportChatState);
|
||||
const telemetry = useRecoilValue(telemetryState);
|
||||
const isDebugMode = useRecoilValue(isDebugModeState);
|
||||
return {
|
||||
...useAuth(),
|
||||
client,
|
||||
state: {
|
||||
icons,
|
||||
authProviders,
|
||||
billing,
|
||||
isSignInPrefilled,
|
||||
supportChat,
|
||||
telemetry,
|
||||
isDebugMode,
|
||||
},
|
||||
};
|
||||
},
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
const { signOut, client } = result.current;
|
||||
|
||||
await act(async () => {
|
||||
await signOut();
|
||||
});
|
||||
|
||||
expect(sessionStorage.length).toBe(0);
|
||||
expect(client.cache.extract()).toEqual({});
|
||||
|
||||
const { state } = result.current;
|
||||
|
||||
expect(state.icons).toEqual({});
|
||||
expect(state.authProviders).toEqual({
|
||||
google: false,
|
||||
magicLink: false,
|
||||
password: true,
|
||||
});
|
||||
expect(state.billing).toBeNull();
|
||||
expect(state.isSignInPrefilled).toBe(false);
|
||||
expect(state.supportChat).toEqual({
|
||||
supportDriver: 'none',
|
||||
supportFrontChatId: null,
|
||||
});
|
||||
expect(state.telemetry).toEqual({
|
||||
enabled: true,
|
||||
anonymizationEnabled: true,
|
||||
});
|
||||
expect(state.isDebugMode).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle credential sign-up', async () => {
|
||||
const { result } = renderHooks();
|
||||
|
||||
await act(async () => {
|
||||
const res = await result.current.signUpWithCredentials(email, password);
|
||||
expect(res).toHaveProperty('user');
|
||||
expect(res).toHaveProperty('workspaceMember');
|
||||
expect(res).toHaveProperty('workspace');
|
||||
});
|
||||
|
||||
expect(mocks[2].result).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,47 @@
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
||||
import { tokenPairState } from '@/auth/states/tokenPairState';
|
||||
|
||||
const renderHooks = () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const isLogged = useIsLogged();
|
||||
const setTokenPair = useSetRecoilState(tokenPairState);
|
||||
|
||||
return {
|
||||
isLogged,
|
||||
setTokenPair,
|
||||
};
|
||||
},
|
||||
{
|
||||
wrapper: RecoilRoot,
|
||||
},
|
||||
);
|
||||
return { result };
|
||||
};
|
||||
|
||||
describe('useIsLogged', () => {
|
||||
it('should return correct value', async () => {
|
||||
const { result } = renderHooks();
|
||||
|
||||
expect(result.current.isLogged).toBe(false);
|
||||
|
||||
await act(async () => {
|
||||
result.current.setTokenPair({
|
||||
accessToken: {
|
||||
expiresAt: '',
|
||||
token: 'testToken',
|
||||
},
|
||||
refreshToken: {
|
||||
expiresAt: '',
|
||||
token: 'testToken',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
expect(result.current.isLogged).toBe(true);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,195 @@
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState';
|
||||
import { tokenPairState } from '@/auth/states/tokenPairState';
|
||||
import { billingState } from '@/client-config/states/billingState';
|
||||
|
||||
const tokenPair = {
|
||||
accessToken: { token: 'accessToken', expiresAt: 'expiresAt' },
|
||||
refreshToken: { token: 'refreshToken', expiresAt: 'expiresAt' },
|
||||
};
|
||||
const billing = {
|
||||
billingUrl: 'testing.com',
|
||||
isBillingEnabled: true,
|
||||
};
|
||||
const currentWorkspace = {
|
||||
displayName: 'testing',
|
||||
id: '1',
|
||||
allowImpersonation: true,
|
||||
};
|
||||
const currentWorkspaceMember = {
|
||||
id: '1',
|
||||
locale: '',
|
||||
name: {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
},
|
||||
};
|
||||
|
||||
const renderHooks = () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const onboardingStatus = useOnboardingStatus();
|
||||
const setBilling = useSetRecoilState(billingState);
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
const setTokenPair = useSetRecoilState(tokenPairState);
|
||||
const setVerifyPending = useSetRecoilState(isVerifyPendingState);
|
||||
|
||||
return {
|
||||
onboardingStatus,
|
||||
setBilling,
|
||||
setCurrentWorkspace,
|
||||
setCurrentWorkspaceMember,
|
||||
setTokenPair,
|
||||
setVerifyPending,
|
||||
};
|
||||
},
|
||||
{
|
||||
wrapper: RecoilRoot,
|
||||
},
|
||||
);
|
||||
return { result };
|
||||
};
|
||||
|
||||
describe('useOnboardingStatus', () => {
|
||||
it('should return "ongoing_user_creation" when user is not logged in', async () => {
|
||||
const { result } = renderHooks();
|
||||
|
||||
expect(result.current.onboardingStatus).toBe('ongoing_user_creation');
|
||||
});
|
||||
|
||||
it('should return undefined when currentWorkspaceMember in undefined', async () => {
|
||||
const { result } = renderHooks();
|
||||
|
||||
act(() => {
|
||||
result.current.setTokenPair(tokenPair);
|
||||
});
|
||||
|
||||
expect(result.current.onboardingStatus).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should return "incomplete"', async () => {
|
||||
const { result } = renderHooks();
|
||||
const {
|
||||
setTokenPair,
|
||||
setBilling,
|
||||
setCurrentWorkspace,
|
||||
setCurrentWorkspaceMember,
|
||||
} = result.current;
|
||||
|
||||
act(() => {
|
||||
setTokenPair(tokenPair);
|
||||
setBilling(billing);
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
subscriptionStatus: 'incomplete',
|
||||
});
|
||||
setCurrentWorkspaceMember(currentWorkspaceMember);
|
||||
});
|
||||
|
||||
expect(result.current.onboardingStatus).toBe('incomplete');
|
||||
});
|
||||
|
||||
it('should return "canceled"', async () => {
|
||||
const { result } = renderHooks();
|
||||
const {
|
||||
setTokenPair,
|
||||
setBilling,
|
||||
setCurrentWorkspace,
|
||||
setCurrentWorkspaceMember,
|
||||
} = result.current;
|
||||
|
||||
act(() => {
|
||||
setTokenPair(tokenPair);
|
||||
setBilling(billing);
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
subscriptionStatus: 'canceled',
|
||||
});
|
||||
setCurrentWorkspaceMember(currentWorkspaceMember);
|
||||
});
|
||||
|
||||
expect(result.current.onboardingStatus).toBe('canceled');
|
||||
});
|
||||
|
||||
it('should return "ongoing_workspace_creation"', async () => {
|
||||
const { result } = renderHooks();
|
||||
const {
|
||||
setTokenPair,
|
||||
setBilling,
|
||||
setCurrentWorkspace,
|
||||
setCurrentWorkspaceMember,
|
||||
} = result.current;
|
||||
|
||||
act(() => {
|
||||
setTokenPair(tokenPair);
|
||||
setBilling(billing);
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
displayName: '',
|
||||
subscriptionStatus: 'completed',
|
||||
});
|
||||
setCurrentWorkspaceMember(currentWorkspaceMember);
|
||||
});
|
||||
|
||||
expect(result.current.onboardingStatus).toBe('ongoing_workspace_creation');
|
||||
});
|
||||
|
||||
it('should return "ongoing_profile_creation"', async () => {
|
||||
const { result } = renderHooks();
|
||||
const {
|
||||
setTokenPair,
|
||||
setBilling,
|
||||
setCurrentWorkspace,
|
||||
setCurrentWorkspaceMember,
|
||||
} = result.current;
|
||||
|
||||
act(() => {
|
||||
setTokenPair(tokenPair);
|
||||
setBilling(billing);
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
subscriptionStatus: 'completed',
|
||||
});
|
||||
setCurrentWorkspaceMember(currentWorkspaceMember);
|
||||
});
|
||||
|
||||
expect(result.current.onboardingStatus).toBe('ongoing_profile_creation');
|
||||
});
|
||||
|
||||
it('should return "completed"', async () => {
|
||||
const { result } = renderHooks();
|
||||
const {
|
||||
setTokenPair,
|
||||
setBilling,
|
||||
setCurrentWorkspace,
|
||||
setCurrentWorkspaceMember,
|
||||
} = result.current;
|
||||
|
||||
act(() => {
|
||||
setTokenPair(tokenPair);
|
||||
setBilling(billing);
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
subscriptionStatus: 'completed',
|
||||
});
|
||||
setCurrentWorkspaceMember({
|
||||
...currentWorkspaceMember,
|
||||
name: {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
expect(result.current.onboardingStatus).toBe('completed');
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,33 @@
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { enableFetchMocks } from 'jest-fetch-mock';
|
||||
|
||||
import { renewToken } from '@/auth/services/AuthService';
|
||||
|
||||
enableFetchMocks();
|
||||
|
||||
const tokens = {
|
||||
accessToken: {
|
||||
token: 'accessToken',
|
||||
expiresAt: 'expiresAt',
|
||||
},
|
||||
refreshToken: {
|
||||
token: 'refreshToken',
|
||||
expiresAt: 'expiresAt',
|
||||
},
|
||||
};
|
||||
|
||||
describe('AuthService', () => {
|
||||
it('should renewToken', async () => {
|
||||
fetchMock.mockResponse(() =>
|
||||
Promise.resolve({
|
||||
body: JSON.stringify({
|
||||
data: { renewToken: { tokens } },
|
||||
}),
|
||||
}),
|
||||
);
|
||||
await act(async () => {
|
||||
const res = await renewToken('http://localhost:3000', tokens);
|
||||
expect(res).toEqual(tokens);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,11 @@
|
||||
import { PASSWORD_REGEX } from '@/auth/utils/passwordRegex';
|
||||
|
||||
describe('PASSWORD_REGEX', () => {
|
||||
it('should match passwords with at least 8 characters', () => {
|
||||
const validPassword = 'password123';
|
||||
const invalidPassword = '1234567';
|
||||
|
||||
expect(PASSWORD_REGEX.test(validPassword)).toBe(true);
|
||||
expect(PASSWORD_REGEX.test(invalidPassword)).toBe(false);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,119 @@
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { commandMenuCommandsState } from '@/command-menu/states/commandMenuCommandsState';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { CommandType } from '@/command-menu/types/Command';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
<MemoryRouter
|
||||
initialEntries={['/one', '/two', { pathname: '/three' }]}
|
||||
initialIndex={1}
|
||||
>
|
||||
{children}
|
||||
</MemoryRouter>
|
||||
</RecoilRoot>
|
||||
);
|
||||
|
||||
const renderHooks = () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const commandMenu = useCommandMenu();
|
||||
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
|
||||
const [commandMenuCommands, setCommandMenuCommands] = useRecoilState(
|
||||
commandMenuCommandsState,
|
||||
);
|
||||
|
||||
return {
|
||||
commandMenu,
|
||||
isCommandMenuOpened,
|
||||
commandMenuCommands,
|
||||
setCommandMenuCommands,
|
||||
};
|
||||
},
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
return { result };
|
||||
};
|
||||
|
||||
describe('useCommandMenu', () => {
|
||||
it('should open and close the command menu', () => {
|
||||
const { result } = renderHooks();
|
||||
|
||||
act(() => {
|
||||
result.current.commandMenu.openCommandMenu();
|
||||
});
|
||||
|
||||
expect(result.current.isCommandMenuOpened).toBe(true);
|
||||
|
||||
act(() => {
|
||||
result.current.commandMenu.closeCommandMenu();
|
||||
});
|
||||
|
||||
expect(result.current.isCommandMenuOpened).toBe(false);
|
||||
});
|
||||
|
||||
it('should toggle the command menu', () => {
|
||||
const { result } = renderHooks();
|
||||
|
||||
expect(result.current.isCommandMenuOpened).toBe(false);
|
||||
|
||||
act(() => {
|
||||
result.current.commandMenu.toggleCommandMenu();
|
||||
});
|
||||
|
||||
expect(result.current.isCommandMenuOpened).toBe(true);
|
||||
|
||||
act(() => {
|
||||
result.current.commandMenu.toggleCommandMenu();
|
||||
});
|
||||
|
||||
expect(result.current.isCommandMenuOpened).toBe(false);
|
||||
});
|
||||
|
||||
it('should add commands to the menu', () => {
|
||||
const { result } = renderHooks();
|
||||
|
||||
expect(
|
||||
result.current.commandMenuCommands.find((cmd) => cmd.label === 'Test'),
|
||||
).toBeUndefined();
|
||||
|
||||
act(() => {
|
||||
result.current.commandMenu.addToCommandMenu([
|
||||
{ label: 'Test', id: 'test', to: '/test', type: CommandType.Navigate },
|
||||
]);
|
||||
});
|
||||
|
||||
expect(
|
||||
result.current.commandMenuCommands.find((cmd) => cmd.label === 'Test'),
|
||||
).toBeDefined();
|
||||
});
|
||||
|
||||
it('onItemClick', () => {
|
||||
const { result } = renderHooks();
|
||||
const onClickMock = jest.fn();
|
||||
|
||||
act(() => {
|
||||
result.current.commandMenu.onItemClick(onClickMock, '/test');
|
||||
});
|
||||
|
||||
expect(result.current.isCommandMenuOpened).toBe(true);
|
||||
expect(onClickMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should setToIntitialCommandMenu command menu', () => {
|
||||
const { result } = renderHooks();
|
||||
|
||||
act(() => {
|
||||
result.current.commandMenu.setToIntitialCommandMenu();
|
||||
});
|
||||
|
||||
expect(result.current.commandMenuCommands.length).toBe(5);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user