From f72d2639e5d44ea25ec4694a54784b2572a2b131 Mon Sep 17 00:00:00 2001
From: gianantoniopini <63844628+gianantoniopini@users.noreply.github.com>
Date: Tue, 2 Feb 2021 10:27:30 +0100
Subject: [PATCH] Builder page: unit tests separated in different files
---
.../__tests__/builder.dataPersistence.test.js | 39 +++
.../__tests__/builder.errorHandling.test.js | 24 ++
.../app/__tests__/builder.rendering.test.js | 63 +++++
.../app/__tests__/builder.settings.test.js | 54 ++++
src/pages/app/__tests__/builder.test.js | 260 ------------------
src/pages/app/__tests__/helpers/builder.js | 127 +++++++++
src/pages/app/__tests__/helpers/dashboard.js | 6 +-
7 files changed, 311 insertions(+), 262 deletions(-)
create mode 100644 src/pages/app/__tests__/builder.dataPersistence.test.js
create mode 100644 src/pages/app/__tests__/builder.errorHandling.test.js
create mode 100644 src/pages/app/__tests__/builder.rendering.test.js
create mode 100644 src/pages/app/__tests__/builder.settings.test.js
delete mode 100644 src/pages/app/__tests__/builder.test.js
create mode 100644 src/pages/app/__tests__/helpers/builder.js
diff --git a/src/pages/app/__tests__/builder.dataPersistence.test.js b/src/pages/app/__tests__/builder.dataPersistence.test.js
new file mode 100644
index 00000000..9f1fef64
--- /dev/null
+++ b/src/pages/app/__tests__/builder.dataPersistence.test.js
@@ -0,0 +1,39 @@
+import { fireEvent, screen } from '@testing-library/react';
+
+import { DatabaseConstants } from 'gatsby-plugin-firebase';
+
+import {
+ setupAndWait,
+ expectDatabaseUpdateToHaveCompleted,
+} from './helpers/builder';
+
+const testTimeoutInMilliseconds = 20000;
+jest.setTimeout(testTimeoutInMilliseconds);
+
+test('when input value is changed, updates database', async () => {
+ const resumeId = DatabaseConstants.demoStateResume1Id;
+ const { mockDatabaseUpdateFunction } = await setupAndWait(
+ resumeId,
+ true,
+ true,
+ );
+
+ const input = screen.getByRole('textbox', { name: /address line 1/i });
+ const newInputValue = 'test street 123';
+ const now = new Date().getTime();
+
+ fireEvent.change(input, { target: { value: newInputValue } });
+
+ expect(input.value).toBe(newInputValue);
+
+ await expectDatabaseUpdateToHaveCompleted(mockDatabaseUpdateFunction);
+ const mockDatabaseUpdateFunctionCallArgument =
+ mockDatabaseUpdateFunction.mock.calls[0][0];
+ expect(mockDatabaseUpdateFunctionCallArgument.id).toBe(resumeId);
+ expect(mockDatabaseUpdateFunctionCallArgument.profile.address.line1).toBe(
+ newInputValue,
+ );
+ expect(
+ mockDatabaseUpdateFunctionCallArgument.updatedAt,
+ ).toBeGreaterThanOrEqual(now);
+});
diff --git a/src/pages/app/__tests__/builder.errorHandling.test.js b/src/pages/app/__tests__/builder.errorHandling.test.js
new file mode 100644
index 00000000..f573f9b5
--- /dev/null
+++ b/src/pages/app/__tests__/builder.errorHandling.test.js
@@ -0,0 +1,24 @@
+import { navigate as mockNavigateFunction } from 'gatsby';
+import { fireEvent, getByText, screen, waitFor } from '@testing-library/react';
+
+import setup from './helpers/builder';
+
+test('if resume does not exist, navigates to Dashboard and displays notification', async () => {
+ await setup('xxxxxx');
+
+ await waitFor(() => expect(mockNavigateFunction).toHaveBeenCalledTimes(1));
+ expect(mockNavigateFunction).toHaveBeenCalledWith('/app/dashboard');
+
+ const notification = await screen.findByRole('alert');
+ expect(
+ getByText(
+ notification,
+ /The resume you were looking for does not exist anymore/i,
+ ),
+ ).toBeInTheDocument();
+ fireEvent.click(notification);
+
+ await waitFor(() =>
+ expect(mockNavigateFunction.mock.results[0].value).resolves.toBeUndefined(),
+ );
+});
diff --git a/src/pages/app/__tests__/builder.rendering.test.js b/src/pages/app/__tests__/builder.rendering.test.js
new file mode 100644
index 00000000..95029652
--- /dev/null
+++ b/src/pages/app/__tests__/builder.rendering.test.js
@@ -0,0 +1,63 @@
+import {
+ fireEvent,
+ getByText,
+ screen,
+ waitForElementToBeRemoved,
+} from '@testing-library/react';
+
+import { DatabaseConstants } from 'gatsby-plugin-firebase';
+
+import { dataTestId as loadingScreenTestId } from '../../../components/router/LoadingScreen';
+
+import setup, {
+ setupAndWait,
+ waitForDatabaseUpdateToHaveCompleted,
+} from './helpers/builder';
+
+const testTimeoutInMilliseconds = 10000;
+jest.setTimeout(testTimeoutInMilliseconds);
+
+test('renders first and last name', async () => {
+ const { resume } = await setupAndWait(
+ DatabaseConstants.demoStateResume1Id,
+ true,
+ true,
+ );
+
+ expect(
+ screen.getByRole('textbox', { name: /first name/i }),
+ ).toHaveDisplayValue(resume.profile.firstName);
+ expect(
+ screen.getByRole('textbox', { name: /last name/i }),
+ ).toHaveDisplayValue(resume.profile.lastName);
+ expect(
+ screen.getAllByText(new RegExp(resume.profile.firstName, 'i')).length,
+ ).toBeTruthy();
+ expect(
+ screen.getAllByText(new RegExp(resume.profile.lastName, 'i')).length,
+ ).toBeTruthy();
+});
+
+test('renders loading screen', async () => {
+ const { mockDatabaseUpdateFunction } = await setup(
+ DatabaseConstants.demoStateResume1Id,
+ );
+
+ expect(screen.getByTestId(loadingScreenTestId)).toBeInTheDocument();
+
+ await waitForElementToBeRemoved(() =>
+ screen.getByTestId(loadingScreenTestId),
+ );
+
+ await waitForDatabaseUpdateToHaveCompleted(mockDatabaseUpdateFunction);
+});
+
+test('if resume is in initial state, renders load demo data notification', async () => {
+ await setup(DatabaseConstants.initialStateResumeId);
+
+ const notification = await screen.findByRole('alert');
+ expect(
+ getByText(notification, /Not sure where to begin\? Try loading demo data/i),
+ ).toBeInTheDocument();
+ fireEvent.click(notification);
+});
diff --git a/src/pages/app/__tests__/builder.settings.test.js b/src/pages/app/__tests__/builder.settings.test.js
new file mode 100644
index 00000000..7db8a1c1
--- /dev/null
+++ b/src/pages/app/__tests__/builder.settings.test.js
@@ -0,0 +1,54 @@
+import { fireEvent, screen } from '@testing-library/react';
+
+import { DatabaseConstants } from 'gatsby-plugin-firebase';
+
+import { languageStorageItemKey } from '../../../contexts/SettingsContext';
+
+import {
+ setupAndWait,
+ expectDatabaseUpdateToHaveCompleted,
+} from './helpers/builder';
+
+const testTimeoutInMilliseconds = 20000;
+jest.setTimeout(testTimeoutInMilliseconds);
+
+test('allows to change the language', async () => {
+ const resumeId = DatabaseConstants.demoStateResume1Id;
+ const { mockDatabaseUpdateFunction } = await setupAndWait(
+ resumeId,
+ true,
+ true,
+ );
+
+ const languageElement = screen.getByLabelText(/language/i);
+ const italianLanguageCode = 'it';
+ const now = new Date().getTime();
+
+ fireEvent.change(languageElement, {
+ target: { value: italianLanguageCode },
+ });
+
+ expect(languageElement).toHaveValue(italianLanguageCode);
+
+ expect(screen.queryByLabelText(/date of birth/i)).not.toBeInTheDocument();
+ expect(screen.getByLabelText(/data di nascita/i)).toBeInTheDocument();
+
+ const languageStorageItem = localStorage.getItem(languageStorageItemKey);
+ expect(languageStorageItem).toBe(italianLanguageCode);
+
+ await expectDatabaseUpdateToHaveCompleted(mockDatabaseUpdateFunction);
+ const mockDatabaseUpdateFunctionCallArgument =
+ mockDatabaseUpdateFunction.mock.calls[0][0];
+ expect(mockDatabaseUpdateFunctionCallArgument.id).toBe(resumeId);
+ expect(mockDatabaseUpdateFunctionCallArgument.metadata.language).toBe(
+ italianLanguageCode,
+ );
+ expect(
+ mockDatabaseUpdateFunctionCallArgument.updatedAt,
+ ).toBeGreaterThanOrEqual(now);
+});
+
+afterEach(() => {
+ const englishLanguageCode = 'en';
+ localStorage.setItem(languageStorageItemKey, englishLanguageCode);
+});
diff --git a/src/pages/app/__tests__/builder.test.js b/src/pages/app/__tests__/builder.test.js
deleted file mode 100644
index e22d6e9d..00000000
--- a/src/pages/app/__tests__/builder.test.js
+++ /dev/null
@@ -1,260 +0,0 @@
-import { navigate as mockNavigateFunction } from 'gatsby';
-import React from 'react';
-import {
- fireEvent,
- getByText,
- render,
- screen,
- waitFor,
- waitForElementToBeRemoved,
-} from '@testing-library/react';
-
-import FirebaseStub, { DatabaseConstants } from 'gatsby-plugin-firebase';
-
-import { dataTestId as loadingScreenTestId } from '../../../components/router/LoadingScreen';
-import {
- SettingsProvider,
- languageStorageItemKey,
-} from '../../../contexts/SettingsContext';
-import { ModalProvider } from '../../../contexts/ModalContext';
-import { UserProvider } from '../../../contexts/UserContext';
-import {
- DatabaseProvider,
- DebounceWaitTime,
-} from '../../../contexts/DatabaseContext';
-import { ResumeProvider } from '../../../contexts/ResumeContext';
-import { StorageProvider } from '../../../contexts/StorageContext';
-import Wrapper from '../../../components/shared/Wrapper';
-import Builder from '../builder';
-
-describe('Builder', () => {
- let resumeId = null;
- let resume = null;
- let mockDatabaseUpdateFunction = null;
-
- const fnWaitForDatabaseUpdateToHaveCompleted = async () => {
- await waitFor(() => mockDatabaseUpdateFunction.mock.calls[0][0], {
- timeout: DebounceWaitTime,
- });
- await waitFor(() => mockDatabaseUpdateFunction.mock.results[0].value);
- };
-
- const expectDatabaseUpdateToHaveCompleted = async () => {
- await waitFor(
- () => expect(mockDatabaseUpdateFunction).toHaveBeenCalledTimes(1),
- {
- timeout: DebounceWaitTime,
- },
- );
- await waitFor(() =>
- expect(
- mockDatabaseUpdateFunction.mock.results[0].value,
- ).resolves.toBeUndefined(),
- );
- };
-
- async function setup(
- resumeIdParameter,
- waitForLoadingScreenToDisappear = true,
- waitForDatabaseUpdateToHaveCompleted = true,
- ) {
- FirebaseStub.database().initializeData();
-
- resumeId = resumeIdParameter;
- resume = (
- await FirebaseStub.database()
- .ref(`${DatabaseConstants.resumesPath}/${resumeId}`)
- .once('value')
- ).val();
-
- mockDatabaseUpdateFunction = jest.spyOn(
- FirebaseStub.database().ref(
- `${DatabaseConstants.resumesPath}/${resumeId}`,
- ),
- 'update',
- );
-
- FirebaseStub.auth().signInAnonymously();
-
- render(
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ,
- );
-
- if (waitForLoadingScreenToDisappear) {
- await waitForElementToBeRemoved(() =>
- screen.getByTestId(loadingScreenTestId),
- );
- }
-
- if (waitForDatabaseUpdateToHaveCompleted) {
- await fnWaitForDatabaseUpdateToHaveCompleted();
- }
-
- mockDatabaseUpdateFunction.mockClear();
- }
-
- describe('handles errors', () => {
- describe('if resume does not exist', () => {
- beforeEach(async () => {
- await setup('xxxxxx', false, false);
- });
-
- it('navigates to Dashboard and displays notification', async () => {
- await waitFor(() =>
- expect(mockNavigateFunction).toHaveBeenCalledTimes(1),
- );
- expect(mockNavigateFunction).toHaveBeenCalledWith('/app/dashboard');
-
- const notification = await screen.findByRole('alert');
- expect(
- getByText(
- notification,
- /The resume you were looking for does not exist anymore/i,
- ),
- ).toBeInTheDocument();
- fireEvent.click(notification);
-
- await waitFor(() =>
- expect(
- mockNavigateFunction.mock.results[0].value,
- ).resolves.toBeUndefined(),
- );
- });
- });
- });
-
- describe('renders', () => {
- beforeEach(async () => {
- await setup(DatabaseConstants.demoStateResume1Id);
- });
-
- it('first and last name', () => {
- expect(
- screen.getByRole('textbox', { name: /first name/i }),
- ).toHaveDisplayValue(resume.profile.firstName);
- expect(
- screen.getByRole('textbox', { name: /last name/i }),
- ).toHaveDisplayValue(resume.profile.lastName);
- expect(
- screen.getAllByText(new RegExp(resume.profile.firstName, 'i')).length,
- ).toBeTruthy();
- expect(
- screen.getAllByText(new RegExp(resume.profile.lastName, 'i')).length,
- ).toBeTruthy();
- });
- });
-
- describe('settings', () => {
- beforeEach(async () => {
- await setup(DatabaseConstants.demoStateResume1Id);
- });
-
- it('allow to change the language', async () => {
- const languageElement = screen.getByLabelText(/language/i);
- const italianLanguageCode = 'it';
- const now = new Date().getTime();
-
- fireEvent.change(languageElement, {
- target: { value: italianLanguageCode },
- });
-
- expect(languageElement).toHaveValue(italianLanguageCode);
-
- expect(screen.queryByLabelText(/date of birth/i)).not.toBeInTheDocument();
- expect(screen.getByLabelText(/data di nascita/i)).toBeInTheDocument();
-
- const languageStorageItem = localStorage.getItem(languageStorageItemKey);
- expect(languageStorageItem).toBe(italianLanguageCode);
-
- await expectDatabaseUpdateToHaveCompleted();
- const mockDatabaseUpdateFunctionCallArgument =
- mockDatabaseUpdateFunction.mock.calls[0][0];
- expect(mockDatabaseUpdateFunctionCallArgument.id).toBe(resumeId);
- expect(mockDatabaseUpdateFunctionCallArgument.metadata.language).toBe(
- italianLanguageCode,
- );
- expect(
- mockDatabaseUpdateFunctionCallArgument.updatedAt,
- ).toBeGreaterThanOrEqual(now);
- });
-
- afterEach(() => {
- const englishLanguageCode = 'en';
- localStorage.setItem(languageStorageItemKey, englishLanguageCode);
- });
- });
-
- describe('updates data', () => {
- beforeEach(async () => {
- await setup(DatabaseConstants.demoStateResume1Id);
- });
-
- it('when input value is changed', async () => {
- const input = screen.getByRole('textbox', { name: /address line 1/i });
- const newInputValue = 'test street 123';
- const now = new Date().getTime();
-
- fireEvent.change(input, { target: { value: newInputValue } });
-
- expect(input.value).toBe(newInputValue);
-
- await expectDatabaseUpdateToHaveCompleted();
- const mockDatabaseUpdateFunctionCallArgument =
- mockDatabaseUpdateFunction.mock.calls[0][0];
- expect(mockDatabaseUpdateFunctionCallArgument.id).toBe(resumeId);
- expect(mockDatabaseUpdateFunctionCallArgument.profile.address.line1).toBe(
- newInputValue,
- );
- expect(
- mockDatabaseUpdateFunctionCallArgument.updatedAt,
- ).toBeGreaterThanOrEqual(now);
- });
- });
-
- describe('while loading', () => {
- beforeEach(async () => {
- await setup(DatabaseConstants.demoStateResume1Id, false, false);
- });
-
- it('renders loading screen', async () => {
- expect(screen.getByTestId(loadingScreenTestId)).toBeInTheDocument();
-
- await waitForElementToBeRemoved(() =>
- screen.getByTestId(loadingScreenTestId),
- );
-
- await fnWaitForDatabaseUpdateToHaveCompleted();
- });
- });
-
- describe('with resume in initial state', () => {
- beforeEach(async () => {
- await setup(DatabaseConstants.initialStateResumeId, false, false);
- });
-
- it('displays load demo data notification', async () => {
- const notification = await screen.findByRole('alert');
- expect(
- getByText(
- notification,
- /Not sure where to begin\? Try loading demo data/i,
- ),
- ).toBeInTheDocument();
- fireEvent.click(notification);
- });
- });
-});
diff --git a/src/pages/app/__tests__/helpers/builder.js b/src/pages/app/__tests__/helpers/builder.js
new file mode 100644
index 00000000..b05eca1b
--- /dev/null
+++ b/src/pages/app/__tests__/helpers/builder.js
@@ -0,0 +1,127 @@
+import React from 'react';
+import {
+ render,
+ screen,
+ waitFor,
+ waitForElementToBeRemoved,
+} from '@testing-library/react';
+
+import FirebaseStub, { DatabaseConstants } from 'gatsby-plugin-firebase';
+
+import { dataTestId as loadingScreenTestId } from '../../../../components/router/LoadingScreen';
+import { SettingsProvider } from '../../../../contexts/SettingsContext';
+import { ModalProvider } from '../../../../contexts/ModalContext';
+import { UserProvider } from '../../../../contexts/UserContext';
+import {
+ DatabaseProvider,
+ DebounceWaitTime,
+} from '../../../../contexts/DatabaseContext';
+import { ResumeProvider } from '../../../../contexts/ResumeContext';
+import { StorageProvider } from '../../../../contexts/StorageContext';
+import Wrapper from '../../../../components/shared/Wrapper';
+import Builder from '../../builder';
+
+const waitForDatabaseUpdateToHaveCompletedFn = async (
+ mockDatabaseUpdateFunction,
+) => {
+ await waitFor(() => mockDatabaseUpdateFunction.mock.calls[0][0], {
+ timeout: DebounceWaitTime,
+ });
+ await waitFor(() => mockDatabaseUpdateFunction.mock.results[0].value);
+};
+
+const expectDatabaseUpdateToHaveCompleted = async (
+ mockDatabaseUpdateFunction,
+) => {
+ await waitFor(
+ () => expect(mockDatabaseUpdateFunction).toHaveBeenCalledTimes(1),
+ {
+ timeout: DebounceWaitTime,
+ },
+ );
+ await waitFor(() =>
+ expect(
+ mockDatabaseUpdateFunction.mock.results[0].value,
+ ).resolves.toBeUndefined(),
+ );
+};
+
+// eslint-disable-next-line no-underscore-dangle
+async function _setup(
+ resumeId,
+ waitForLoadingScreenToDisappear,
+ waitForDatabaseUpdateToHaveCompleted,
+) {
+ FirebaseStub.database().initializeData();
+
+ const resume = (
+ await FirebaseStub.database()
+ .ref(`${DatabaseConstants.resumesPath}/${resumeId}`)
+ .once('value')
+ ).val();
+
+ const mockDatabaseUpdateFunction = jest.spyOn(
+ FirebaseStub.database().ref(`${DatabaseConstants.resumesPath}/${resumeId}`),
+ 'update',
+ );
+
+ FirebaseStub.auth().signInAnonymously();
+
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ if (waitForLoadingScreenToDisappear) {
+ await waitForElementToBeRemoved(() =>
+ screen.getByTestId(loadingScreenTestId),
+ );
+ }
+
+ if (waitForDatabaseUpdateToHaveCompleted) {
+ await waitForDatabaseUpdateToHaveCompletedFn(mockDatabaseUpdateFunction);
+ }
+
+ mockDatabaseUpdateFunction.mockClear();
+
+ return { resume, mockDatabaseUpdateFunction };
+}
+
+async function setup(resumeId) {
+ const returnValue = await _setup(resumeId, false, false);
+ return returnValue;
+}
+
+async function setupAndWait(
+ resumeId,
+ waitForLoadingScreenToDisappear,
+ waitForDatabaseUpdateToHaveCompleted,
+) {
+ const returnValue = await _setup(
+ resumeId,
+ waitForLoadingScreenToDisappear,
+ waitForDatabaseUpdateToHaveCompleted,
+ );
+ return returnValue;
+}
+
+export default setup;
+
+export {
+ setupAndWait,
+ waitForDatabaseUpdateToHaveCompletedFn as waitForDatabaseUpdateToHaveCompleted,
+ expectDatabaseUpdateToHaveCompleted,
+};
diff --git a/src/pages/app/__tests__/helpers/dashboard.js b/src/pages/app/__tests__/helpers/dashboard.js
index f97226da..4eba4f3e 100644
--- a/src/pages/app/__tests__/helpers/dashboard.js
+++ b/src/pages/app/__tests__/helpers/dashboard.js
@@ -41,8 +41,10 @@ const expectResumeToBeRenderedInPreview = async (resumeName) => {
};
const waitForModalWindowToHaveBeenClosed = async () => {
- await waitForElementToBeRemoved(() =>
- screen.getByRole('textbox', { name: /name/i }),
+ await waitFor(() =>
+ screen.queryByRole('textbox', { name: /name/i })
+ ? Promise.reject()
+ : Promise.resolve(),
);
};