tests: improve coverage on newly created code

This commit is contained in:
Nicolas Meienberger 2023-05-08 19:01:14 +02:00 committed by Nicolas Meienberger
parent a2e03bf384
commit 1eab6ef2d9
7 changed files with 247 additions and 19 deletions

View file

@ -0,0 +1,109 @@
import nookies from 'nookies';
import { getTRPCMock } from '@/client/mocks/getTrpcMock';
import { server } from '@/client/mocks/server';
import { renderHook, waitFor } from '../../../../tests/test-utils';
import { useLocale } from '../useLocale';
beforeEach(() => {
nookies.destroy(null, 'locale');
});
describe('test: useLocale()', () => {
describe('test: locale', () => {
it('should return users locale if logged in', async () => {
// arrange
const locale = 'fr-FR';
// @ts-expect-error - we're mocking the trpc context
server.use(getTRPCMock({ path: ['auth', 'me'], response: { locale } }));
// act
const { result } = renderHook(() => useLocale());
// assert
await waitFor(() => {
expect(result.current.locale).toEqual(locale);
});
});
it('should return cookie locale if not logged in', async () => {
// arrange
const locale = 'fr-FR';
nookies.set(null, 'locale', locale);
server.use(getTRPCMock({ path: ['auth', 'me'], response: null }));
// act
const { result } = renderHook(() => useLocale());
// assert
await waitFor(() => {
expect(result.current.locale).toEqual(locale);
});
});
it('should return browser locale if not logged in and no cookie', async () => {
// arrange
const locale = 'fr-FR';
jest.spyOn(window.navigator, 'language', 'get').mockReturnValueOnce(locale);
server.use(getTRPCMock({ path: ['auth', 'me'], response: null }));
// act
const { result } = renderHook(() => useLocale());
// assert
await waitFor(() => {
expect(result.current.locale).toEqual(locale);
});
});
it('should default to english if no locale is found', async () => {
// arrange
server.use(getTRPCMock({ path: ['auth', 'me'], response: null }));
// @ts-expect-error - we're mocking window.navigator
jest.spyOn(window.navigator, 'language', 'get').mockReturnValueOnce(undefined);
// act
const { result } = renderHook(() => useLocale());
// assert
await waitFor(() => {
expect(result.current.locale).toEqual('en');
});
});
});
describe('test: changeLocale()', () => {
it('should set the locale in the cookie', async () => {
// arrange
const locale = 'fr-FR';
const { result } = renderHook(() => useLocale());
// act
result.current.changeLocale(locale);
// assert
await waitFor(() => {
expect(nookies.get(null)).toEqual({ locale: 'fr-FR' });
});
});
it('should update the locale in the user profile when logged in', async () => {
// arrange
const locale = 'en';
// @ts-expect-error - we're mocking the trpc context
server.use(getTRPCMock({ path: ['auth', 'me'], response: { locale: 'fr-FR' } }));
server.use(getTRPCMock({ path: ['auth', 'changeLocale'], type: 'mutation', response: true }));
const { result } = renderHook(() => useLocale());
await waitFor(() => {
expect(result.current.locale).toEqual('fr-FR');
});
// act
result.current.changeLocale(locale);
// assert
await waitFor(() => {
expect(nookies.get(null)).toEqual({ locale });
});
});
});
});

View file

@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { toast } from 'react-hot-toast';
import { useRouter } from 'next/router';
import { useTranslations } from 'next-intl';
import type { MessageKey } from '@/server/utils/errors';
import { trpc } from '../../../../utils/trpc';
import { AuthFormLayout } from '../../components/AuthFormLayout';
import { LoginForm } from '../../components/LoginForm';
@ -16,10 +17,7 @@ export const LoginContainer: React.FC = () => {
const utils = trpc.useContext();
const login = trpc.auth.login.useMutation({
onError: (e) => {
let toastMessage = e.message;
if (e.data?.translatedError) {
toastMessage = t(e.data.translatedError);
}
const toastMessage = t(e.data?.translatedError || (e.message as MessageKey));
toast.error(toastMessage);
},
onSuccess: (data) => {
@ -34,10 +32,7 @@ export const LoginContainer: React.FC = () => {
const verifyTotp = trpc.auth.verifyTotp.useMutation({
onError: (e) => {
let toastMessage = e.message;
if (e.data?.translatedError) {
toastMessage = t(e.data.translatedError);
}
const toastMessage = t(e.data?.translatedError || (e.message as MessageKey));
toast.error(toastMessage);
},
onSuccess: () => {

View file

@ -3,6 +3,7 @@ import React from 'react';
import { toast } from 'react-hot-toast';
import { useLocale } from '@/client/hooks/useLocale';
import { useTranslations } from 'next-intl';
import type { MessageKey } from '@/server/utils/errors';
import { trpc } from '../../../../utils/trpc';
import { AuthFormLayout } from '../../components/AuthFormLayout';
import { RegisterForm } from '../../components/RegisterForm';
@ -16,10 +17,7 @@ export const RegisterContainer: React.FC = () => {
const utils = trpc.useContext();
const register = trpc.auth.register.useMutation({
onError: (e) => {
let toastMessage = e.message;
if (e.data?.translatedError) {
toastMessage = t(e.data.translatedError);
}
const toastMessage = t(e.data?.translatedError || (e.message as MessageKey));
toast.error(toastMessage);
},
onSuccess: () => {

View file

@ -2,6 +2,7 @@ import { useRouter } from 'next/router';
import React from 'react';
import { toast } from 'react-hot-toast';
import { useTranslations } from 'next-intl';
import type { MessageKey } from '@/server/utils/errors';
import { Button } from '../../../../components/ui/Button';
import { trpc } from '../../../../utils/trpc';
import { AuthFormLayout } from '../../components/AuthFormLayout';
@ -22,10 +23,7 @@ export const ResetPasswordContainer: React.FC<Props> = ({ isRequested }) => {
utils.auth.checkPasswordChangeRequest.invalidate();
},
onError: (e) => {
let toastMessage = e.message;
if (e.data?.translatedError) {
toastMessage = t(e.data.translatedError);
}
const toastMessage = t(e.data?.translatedError || (e.message as MessageKey));
toast.error(toastMessage);
},
});

View file

@ -1,6 +1,10 @@
import { getAuthedPageProps } from '../page-helpers';
import nookies from 'nookies';
import merge from 'lodash.merge';
import { getAuthedPageProps, getMessagesPageProps } from '../page-helpers';
import englishMessages from '../../messages/en.json';
import frenchMessages from '../../messages/fr-FR.json';
describe('getAuthedPageProps', () => {
describe('test: getAuthedPageProps()', () => {
it('should redirect to /login if there is no user id in session', async () => {
// arrange
const ctx = { req: { session: {} } };
@ -26,3 +30,58 @@ describe('getAuthedPageProps', () => {
expect(props).toEqual({});
});
});
describe('test: getMessagesPageProps()', () => {
beforeEach(() => {
nookies.destroy(null, 'locale');
});
it('should return correct messages if the locale is in the session', async () => {
// arrange
const ctx = { req: { session: { locale: 'fr' }, headers: {} } };
// act
// @ts-expect-error - we're passing in a partial context
const { props } = await getMessagesPageProps(ctx);
// assert
expect(props.messages).toEqual(merge(frenchMessages, englishMessages));
});
it('should return correct messages if the locale in the cookie', async () => {
// arrange
const ctx = { req: { session: {}, headers: {} } };
nookies.set(null, 'locale', 'fr-FR');
// act
// @ts-expect-error - we're passing in a partial context
const { props } = await getMessagesPageProps(ctx);
// assert
expect(props.messages).toEqual(merge(frenchMessages, englishMessages));
});
it('should return correct messages if the locale is detected from the browser', async () => {
// arrange
const ctx = { req: { session: {}, headers: { 'accept-language': 'fr-FR' } } };
// act
// @ts-expect-error - we're passing in a partial context
const { props } = await getMessagesPageProps(ctx);
// assert
expect(props.messages).toEqual(merge(frenchMessages, englishMessages));
});
it('should default to english messages if the locale is not found', async () => {
// arrange
const ctx = { req: { session: {}, headers: {} } };
// act
// @ts-expect-error - we're passing in a partial context
const { props } = await getMessagesPageProps(ctx);
// assert
expect(props.messages).toEqual(englishMessages);
});
});

View file

@ -84,7 +84,7 @@ describe('Test: register', () => {
// act
try {
await caller.register({ username: 'test@test.com', password: '123' });
await caller.register({ username: 'test@test.com', password: '123', locale: 'en' });
} catch (e) {
error = e as { code: string };
}
@ -327,3 +327,38 @@ describe('Test: resetPassword', () => {
expect(error?.code).not.toBe('UNAUTHORIZED');
});
});
describe('Test: changeLocale', () => {
it('should not be accessible without an account', async () => {
// arrange
const caller = authRouter.createCaller(fromPartial({ req: { session: {} } }));
let error;
// act
try {
await caller.changeLocale({ locale: 'en' });
} catch (e) {
error = e as { code: string };
}
// assert
expect(error?.code).toBe('UNAUTHORIZED');
});
it('should be accessible with an account', async () => {
// arrange
await createUser({ id: 122, locale: 'en' }, db);
const caller = authRouter.createCaller(fromPartial({ req: { session: { userId: 122 } } }));
let error;
// act
try {
await caller.changeLocale({ locale: 'fr-FR' });
} catch (e) {
error = e as { code: string };
}
// assert
expect(error?.code).not.toBe('UNAUTHORIZED');
});
});

View file

@ -687,3 +687,37 @@ describe('Test: changePassword', () => {
expect(sessions).toHaveLength(0);
});
});
describe('test: changeLocale()', () => {
it('should change the locale of the user', async () => {
// arrange
const email = faker.internet.email();
const user = await createUser({ email }, database);
const locale = 'fr-FR';
// act
await AuthService.changeLocale({ userId: user.id, locale });
// assert
const updatedUser = await getUserById(user.id, database);
expect(updatedUser?.locale).toBe(locale);
});
it('should throw if the user does not exist', async () => {
// arrange
const locale = 'fr-FR';
// act & assert
await expect(AuthService.changeLocale({ userId: 1, locale })).rejects.toThrowError('server-messages.errors.user-not-found');
});
it('should throw if the locale is invalid', async () => {
// arrange
const email = faker.internet.email();
const user = await createUser({ email }, database);
const locale = 'invalid';
// act & assert
await expect(AuthService.changeLocale({ userId: user.id, locale })).rejects.toThrowError('server-messages.errors.invalid-locale');
});
});