import React from 'react'; import { fireEvent, getByText, queryByText, render, screen, waitFor, waitForElementToBeRemoved, } from '@testing-library/react'; import FirebaseStub, { DatabaseConstants } from 'gatsby-plugin-firebase'; import '../../../i18n/index'; import '../../../utils/dayjs'; import { dataTestId as loadingScreenTestId } from '../../../components/router/LoadingScreen'; import { createResumeButtonDataTestId } from '../../../components/dashboard/CreateResume'; import { menuToggleDataTestIdPrefix as resumePreviewMenuToggleDataTestIdPrefix } from '../../../components/dashboard/ResumePreview'; import { SettingsProvider } from '../../../contexts/SettingsContext'; import { ModalProvider } from '../../../contexts/ModalContext'; import { UserProvider } from '../../../contexts/UserContext'; import { DatabaseProvider } from '../../../contexts/DatabaseContext'; import { ResumeProvider } from '../../../contexts/ResumeContext'; import { StorageProvider } from '../../../contexts/StorageContext'; import Wrapper from '../../../components/shared/Wrapper'; import Dashboard from '../dashboard'; describe('Dashboard', () => { let userResumes = null; const user = DatabaseConstants.user1; const waitForResumeToBeRenderedInPreview = async (resume) => { await screen.findByText(resume.name); }; const expectResumeToBeRenderedInPreview = async (resume) => { await waitFor(() => { expect(screen.getByText(resume.name)).toBeInTheDocument(); }); }; async function setup(waitForLoadingScreenToDisappear = true) { FirebaseStub.database().initializeData(); userResumes = ( await FirebaseStub.database() .ref(DatabaseConstants.resumesPath) .orderByChild('user') .equalTo(user.uid) .once('value') ).val(); FirebaseStub.auth().signInAnonymously(); render( , ); if (waitForLoadingScreenToDisappear) { await waitForElementToBeRemoved(() => screen.getByTestId(loadingScreenTestId), ); } } describe('renders', () => { beforeEach(async () => { await setup(); }); it('document title', async () => { await waitFor(() => { expect(document.title).toEqual('Dashboard | Reactive Resume'); }); }); it('create resume', async () => { await waitFor(() => { expect(screen.getByText(/create resume/i)).toBeInTheDocument(); }); }); it('preview of user resumes', async () => { expect(Object.keys(userResumes)).toHaveLength(2); await expectResumeToBeRenderedInPreview(Object.values(userResumes)[0]); await expectResumeToBeRenderedInPreview(Object.values(userResumes)[1]); }); }); describe('when resume is created', () => { let nameTextBox = null; const waitForModalToHaveBeenClosed = async () => { await waitFor(() => screen.queryByRole('textbox', { name: /name/i }) ? Promise.reject() : Promise.resolve(), ); }; beforeEach(async () => { await setup(); fetch.resetMocks(); fetch.mockReturnValueOnce({ url: 'https://test-url-123456789.com' }); const dashboardCreateResumeButton = await screen.findByTestId( createResumeButtonDataTestId, ); fireEvent.click(dashboardCreateResumeButton); nameTextBox = screen.getByRole('textbox', { name: /name/i }); }); describe('with name shorter than 5 characters', () => { it('displays validation error and notification', async () => { fireEvent.change(nameTextBox, { target: { value: 'CV 1' } }); fireEvent.focusOut(nameTextBox); await waitFor(() => expect( screen.getByText(/Please enter at least 5 characters/i), ).toBeInTheDocument(), ); const modalCreateResumeButton = screen.getByRole('button', { name: /create resume/i, }); fireEvent.click(modalCreateResumeButton); const notification = await screen.findByRole('alert'); expect( getByText( notification, /You might need to fill up all the required fields/i, ), ).toBeInTheDocument(); fireEvent.click(notification); }); }); describe('with valid name', () => { it('renders loading message', async () => { fireEvent.change(nameTextBox, { target: { value: 'Resume for SW development roles' }, }); const modalCreateResumeButton = screen.getByRole('button', { name: /create resume/i, }); fireEvent.click(modalCreateResumeButton); expect( screen.getByRole('button', { name: /loading/i, }), ).toBeInTheDocument(); await waitFor(() => expect( screen.queryByRole('button', { name: /loading/i, }), ).toBeNull(), ); await waitForModalToHaveBeenClosed(); }); }); }); describe('when resume is deleted', () => { let mockDatabaseRemoveFunction = null; let resumeToDelete = null; let undeletedResume = null; let resumeToDeleteId = null; const waitForDatabaseRemoveToHaveCompleted = async () => { await waitFor(() => mockDatabaseRemoveFunction.mock.results[0].value); }; const expectDatabaseRemoveToHaveCompleted = async () => { await waitFor(() => expect(mockDatabaseRemoveFunction).toHaveBeenCalledTimes(1), ); await waitFor(() => expect( mockDatabaseRemoveFunction.mock.results[0].value, ).resolves.toBeUndefined(), ); }; beforeEach(async () => { await setup(); [resumeToDelete, undeletedResume] = Object.values(userResumes); resumeToDeleteId = resumeToDelete.id; mockDatabaseRemoveFunction = jest.spyOn( FirebaseStub.database().ref( `${DatabaseConstants.resumesPath}/${resumeToDeleteId}`, ), 'remove', ); const resumeToDeleteMenuToggle = await screen.findByTestId( `${resumePreviewMenuToggleDataTestIdPrefix}${resumeToDeleteId}`, ); fireEvent.click(resumeToDeleteMenuToggle); const menuItems = screen.getAllByRole('menuitem'); let deleteMenuItem = null; for (let index = 0; index < menuItems.length; index++) { if (queryByText(menuItems[index], /delete/i)) { deleteMenuItem = menuItems[index]; break; } } fireEvent.click(deleteMenuItem); }); it('removes it from database and preview', async () => { await expectDatabaseRemoveToHaveCompleted(); await waitFor(() => expect(screen.queryByText(resumeToDelete.name)).toBeNull(), ); await expectResumeToBeRenderedInPreview(undeletedResume); }); it('displays notification', async () => { const notification = await screen.findByRole('alert'); expect( getByText( notification, new RegExp(`${resumeToDelete.name} was deleted successfully`, 'i'), ), ).toBeInTheDocument(); fireEvent.click(notification); await waitForDatabaseRemoveToHaveCompleted(); }); it('closes menu', async () => { const menuItems = screen.queryAllByRole('menuitem'); expect(menuItems).toHaveLength(0); await waitForDatabaseRemoveToHaveCompleted(); }); }); describe('while loading', () => { beforeEach(async () => { await setup(false); }); it('renders loading screen', async () => { expect(screen.getByTestId(loadingScreenTestId)).toBeInTheDocument(); await waitForElementToBeRemoved(() => screen.getByTestId(loadingScreenTestId), ); await waitForResumeToBeRenderedInPreview(Object.values(userResumes)[0]); await waitForResumeToBeRenderedInPreview(Object.values(userResumes)[1]); }); }); });