-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
EPMGCIP-181: Fix test warnings (#40)
* EPMGCIP-181: Add "data-testid" attributes for "ContactForm" elements * EPMGCIP-181: Resolve "ContactForm" UTs bad setup for Captcha, update usages * EPMGCIP-181: Add new "Logger" util to perform log with given pre-conditions, update usages across app, extend existing "max-lines" ESLint rule * EPMGCIP0181: Improve React Testing debug configuration with output symbols amount 15000
- Loading branch information
1 parent
24f2015
commit 68efd62
Showing
11 changed files
with
157 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
process.env.DEBUG_PRINT_LIMIT = '15000'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,22 @@ | ||
window.matchMedia = jest.fn().mockImplementation((query: string) => ({ | ||
addEventListener: jest.fn(), | ||
addListener: jest.fn(), | ||
dispatchEvent: jest.fn(), | ||
matches: false, | ||
media: query, | ||
onchange: null, | ||
addListener: jest.fn(), | ||
removeListener: jest.fn(), | ||
addEventListener: jest.fn(), | ||
removeEventListener: jest.fn(), | ||
dispatchEvent: jest.fn(), | ||
removeListener: jest.fn(), | ||
})); | ||
|
||
/* eslint-disable @typescript-eslint/no-empty-function */ | ||
|
||
window.ResizeObserver = class ResizeObserverMock { | ||
observe() {} | ||
unobserve() {} | ||
disconnect() {} | ||
}; | ||
|
||
/* eslint-enable @typescript-eslint/no-empty-function */ | ||
|
||
window.HTMLElement.prototype.scrollIntoView = () => {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,134 +1,163 @@ | ||
import React, { forwardRef, useEffect, useImperativeHandle } from 'react'; | ||
|
||
import { MantineProvider } from '@mantine/core'; | ||
import { act, render, waitFor } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
import { screen, render, waitFor, fireEvent } from '@testing-library/react'; | ||
import { NextIntlClientProvider } from 'next-intl'; | ||
import ReCAPTCHA from 'react-google-recaptcha'; | ||
|
||
import messages from 'messages/en.json'; | ||
|
||
import { sendContactForm } from '@/actions'; | ||
import { NotificationType } from '@/enums'; | ||
import { useShowNotification } from '@/hooks'; | ||
|
||
import ContactForm from './ContactForm'; | ||
|
||
/* eslint-disable max-nested-callbacks */ | ||
|
||
jest.mock('react-google-recaptcha', () => { | ||
return jest.fn().mockImplementation(() => 'ReCAPTCHA'); | ||
return forwardRef(function Component( | ||
props: { asyncScriptOnLoad: () => void; onChange: (token: string) => void }, | ||
ref, | ||
) { | ||
useImperativeHandle(ref, () => ({ | ||
asyncScriptOnLoad: jest.fn(() => {}), | ||
execute: jest.fn(), | ||
executeAsync: jest.fn(() => 'test-token'), | ||
onChange: jest.fn(), | ||
reset: jest.fn(), | ||
})); | ||
|
||
useEffect(() => { | ||
props?.asyncScriptOnLoad(); | ||
props?.onChange('test-token'); | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, []); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, react/prop-types | ||
const { asyncScriptOnLoad, ...domProps } = props; | ||
|
||
// @ts-expect-error: Skip TS ref type incompatibilities. | ||
return <input {...domProps} ref={ref} type="checkbox" data-testid="mock-v2-captcha-element" />; | ||
}); | ||
}); | ||
|
||
/* eslint-enable max-nested-callbacks */ | ||
|
||
jest.mock('@/actions', () => ({ | ||
sendContactForm: jest.fn(() => Promise.resolve({ success: true })), | ||
})); | ||
jest.mock('@/hooks', () => ({ | ||
useShowNotification: jest.fn(() => jest.fn()), | ||
})); | ||
|
||
const useShowNotificationMock = useShowNotification as jest.Mock; | ||
const sendContactFormMock = sendContactForm as jest.Mock; | ||
|
||
const defaultProps = { | ||
reCaptchaSiteKey: 'test-site-key', | ||
}; | ||
|
||
const wrapper = ({ children }: { children: React.ReactNode }) => ( | ||
<NextIntlClientProvider locale="en"> | ||
<MantineProvider>{children}</MantineProvider> | ||
</NextIntlClientProvider> | ||
); | ||
|
||
const renderComponent = (props = {}) => | ||
render(<ContactForm {...defaultProps} {...props} />, { wrapper }); | ||
describe('ContactForm', () => { | ||
const wrapper = ({ children }: { children: React.ReactNode }) => ( | ||
<NextIntlClientProvider locale="en" messages={messages}> | ||
<MantineProvider>{children}</MantineProvider> | ||
</NextIntlClientProvider> | ||
); | ||
|
||
const solveCaptcha = () => | ||
act(async () => { | ||
(ReCAPTCHA as jest.Mock).mock.calls[0][0].onChange('test-token'); | ||
}); | ||
const renderComponent = (props = {}) => | ||
render(<ContactForm {...defaultProps} {...props} />, { wrapper }); | ||
|
||
describe('ContactForm', () => { | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should render form', () => { | ||
const { getByText } = renderComponent(); | ||
it('should render form', async () => { | ||
renderComponent(); | ||
|
||
expect(screen.getByText('Feedback form')).toBeInTheDocument(); | ||
expect(screen.getByText('Name')).toBeInTheDocument(); | ||
expect(screen.getByText('E-mail')).toBeInTheDocument(); | ||
expect(screen.getByText('Subject')).toBeInTheDocument(); | ||
expect(screen.getByText('Message')).toBeInTheDocument(); | ||
|
||
expect(getByText('contactForm.title')).toBeInTheDocument(); | ||
expect(getByText('contactForm.fields.name.label')).toBeInTheDocument(); | ||
expect(getByText('contactForm.fields.email.label')).toBeInTheDocument(); | ||
expect(getByText('contactForm.fields.subject.label')).toBeInTheDocument(); | ||
expect(getByText('contactForm.fields.message.label')).toBeInTheDocument(); | ||
expect(getByText('ReCAPTCHA')).toBeInTheDocument(); | ||
await waitFor(() => { | ||
expect(screen.getByTestId('mock-v2-captcha-element')).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it('should show validation error if form is submitted without filling all required fields', async () => { | ||
const { getByText, getByRole } = renderComponent(); | ||
renderComponent(); | ||
|
||
await solveCaptcha(); | ||
|
||
await userEvent.type( | ||
getByRole('textbox', { name: 'contactForm.fields.name.label' }), | ||
'test-name', | ||
); | ||
await userEvent.click(getByRole('button', { name: 'submit' })); | ||
fireEvent.change(screen.getByTestId('contact-name'), { target: { value: 'test-name' } }); | ||
fireEvent.click(screen.getByTestId('contact-submit-button')); | ||
|
||
await waitFor(() => { | ||
expect(getByText('contactForm.fields.email.validation')).toBeInTheDocument(); | ||
expect(getByText('contactForm.fields.subject.validation')).toBeInTheDocument(); | ||
expect(getByText('contactForm.fields.message.validation')).toBeInTheDocument(); | ||
expect(screen.getByText('E-mail is required')).toBeInTheDocument(); | ||
expect(screen.getByText('Select subject')).toBeInTheDocument(); | ||
expect(screen.getByText('Message is required')).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it('should submit form if all required fields are filled and ReCAPTCHA token is provided', async () => { | ||
const { getByRole } = renderComponent(); | ||
|
||
await solveCaptcha(); | ||
|
||
await userEvent.type( | ||
getByRole('textbox', { name: 'contactForm.fields.name.label' }), | ||
'test-name', | ||
); | ||
await userEvent.type( | ||
getByRole('textbox', { name: 'contactForm.fields.email.label' }), | ||
'[email protected]', | ||
); | ||
await userEvent.click(getByRole('textbox', { name: 'contactForm.fields.subject.label' })); | ||
await userEvent.click(getByRole('option', { name: 'contactForm.subjects.other' })); | ||
await userEvent.type( | ||
getByRole('textbox', { name: 'contactForm.fields.message.label' }), | ||
'test-message', | ||
); | ||
|
||
await userEvent.click(getByRole('button', { name: 'submit' })); | ||
|
||
expect(sendContactForm).toHaveBeenCalledWith({ | ||
email: '[email protected]', | ||
message: 'test-message', | ||
name: 'test-name', | ||
subject: 'contactForm.subjects.other', | ||
it('should handle send contact form action success if all required fields are filled and ReCAPTCHA token is provided', async () => { | ||
const showNotification = jest.fn(); | ||
|
||
useShowNotificationMock.mockReturnValue(showNotification); | ||
|
||
renderComponent(); | ||
|
||
fireEvent.change(screen.getByTestId('contact-name'), { | ||
target: { value: 'test-name' }, | ||
}); | ||
fireEvent.change(screen.getByTestId('contact-email'), { target: { value: '[email protected]' } }); | ||
fireEvent.change(screen.getByTestId('contact-message'), { target: { value: 'test-message' } }); | ||
|
||
fireEvent.click(screen.getByPlaceholderText('Select subject')); | ||
fireEvent.click(screen.getByText('Cooperation')); | ||
|
||
fireEvent.click(screen.getByTestId('contact-submit-button')); | ||
|
||
await waitFor(() => { | ||
expect(sendContactForm).toHaveBeenCalledWith({ | ||
email: '[email protected]', | ||
message: 'test-message', | ||
name: 'test-name', | ||
subject: 'Cooperation', | ||
}); | ||
}); | ||
|
||
expect(showNotification).toHaveBeenCalledWith({ | ||
message: 'Email sent successfully', | ||
type: NotificationType.Success, | ||
}); | ||
}); | ||
|
||
it('should handle send contact form action failure', async () => { | ||
it('should handle send contact form action failure if all required fields are filled and ReCAPTCHA token is provided', async () => { | ||
const showNotification = jest.fn(); | ||
|
||
(useShowNotification as jest.Mock).mockReturnValue(showNotification); | ||
(sendContactForm as jest.Mock).mockReturnValue({ messages: ['test-error'], success: false }); | ||
|
||
const { getByRole } = renderComponent(); | ||
|
||
await solveCaptcha(); | ||
|
||
await userEvent.type( | ||
getByRole('textbox', { name: 'contactForm.fields.name.label' }), | ||
'test-name', | ||
); | ||
await userEvent.type( | ||
getByRole('textbox', { name: 'contactForm.fields.email.label' }), | ||
'[email protected]', | ||
); | ||
await userEvent.click(getByRole('textbox', { name: 'contactForm.fields.subject.label' })); | ||
await userEvent.click(getByRole('option', { name: 'contactForm.subjects.other' })); | ||
await userEvent.type( | ||
getByRole('textbox', { name: 'contactForm.fields.message.label' }), | ||
'test-message', | ||
); | ||
|
||
await userEvent.click(getByRole('button', { name: 'submit' })); | ||
useShowNotificationMock.mockReturnValue(showNotification); | ||
sendContactFormMock.mockReturnValue({ messages: ['test-error'], success: false }); | ||
|
||
renderComponent(); | ||
|
||
fireEvent.change(screen.getByTestId('contact-name'), { | ||
target: { value: 'test-name' }, | ||
}); | ||
fireEvent.change(screen.getByTestId('contact-email'), { target: { value: '[email protected]' } }); | ||
fireEvent.change(screen.getByTestId('contact-message'), { target: { value: 'test-message' } }); | ||
|
||
fireEvent.click(screen.getByPlaceholderText('Select subject')); | ||
fireEvent.click(screen.getByText('Cooperation')); | ||
|
||
fireEvent.click(screen.getByTestId('contact-submit-button')); | ||
|
||
await waitFor(() => { | ||
expect(sendContactForm).toHaveBeenCalledWith({ | ||
email: '[email protected]', | ||
message: 'test-message', | ||
name: 'test-name', | ||
subject: 'Cooperation', | ||
}); | ||
}); | ||
|
||
expect(showNotification).toHaveBeenCalledWith({ | ||
message: 'test-error', | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.