diff --git a/src/pages/app/__tests__/dashboard.createResume.test.js b/src/pages/app/__tests__/dashboard.createResume.test.js
new file mode 100644
index 00000000..72fc0ad6
--- /dev/null
+++ b/src/pages/app/__tests__/dashboard.createResume.test.js
@@ -0,0 +1,151 @@
+import {
+ fireEvent,
+ getByText,
+ screen,
+ waitFor,
+ waitForElementToBeRemoved,
+} from '@testing-library/react';
+
+import FirebaseStub, { DatabaseConstants } from 'gatsby-plugin-firebase';
+
+import { createResumeButtonDataTestId } from '../../../components/dashboard/CreateResume';
+import {
+ waitForResumeToBeRenderedInPreview,
+ expectResumeToBeRenderedInPreview,
+ waitForModalWindowToHaveBeenClosed,
+ dismissNotification,
+ unsplashPhotoResponseUrl,
+ setupWithFetchMockAndWaitForLoadingScreenToDisappear,
+} from './helpers/dashboard';
+
+const tooShortResumeName = 'CV 1';
+const validResumeName = 'Resume for SW development roles';
+
+async function setup() {
+ const user = DatabaseConstants.user1;
+ await setupWithFetchMockAndWaitForLoadingScreenToDisappear(user);
+
+ const dashboardCreateResumeButton = await screen.findByTestId(
+ createResumeButtonDataTestId,
+ );
+ fireEvent.click(dashboardCreateResumeButton);
+
+ const nameTextBox = screen.getByRole('textbox', { name: /name/i });
+ return { user, nameTextBox };
+}
+
+async function setupAndSubmit(resumeName) {
+ const { user, nameTextBox } = await setup();
+
+ fireEvent.change(nameTextBox, {
+ target: { value: resumeName },
+ });
+
+ const modalCreateResumeButton = screen.getByRole('button', {
+ name: /create resume/i,
+ });
+ fireEvent.click(modalCreateResumeButton);
+
+ return { user };
+}
+
+describe('with invalid name', () => {
+ it('displays validation error', async () => {
+ const { nameTextBox } = await setup();
+
+ fireEvent.change(nameTextBox, { target: { value: tooShortResumeName } });
+ fireEvent.blur(nameTextBox);
+
+ await waitFor(() =>
+ expect(
+ screen.getByText(/Please enter at least 5 characters/i),
+ ).toBeInTheDocument(),
+ );
+ });
+
+ it('displays notification', async () => {
+ await setupAndSubmit(tooShortResumeName);
+
+ const notification = await screen.findByRole('alert');
+ expect(
+ getByText(
+ notification,
+ /You might need to fill up all the required fields/i,
+ ),
+ ).toBeInTheDocument();
+ dismissNotification(notification);
+ });
+});
+
+describe('with valid name', () => {
+ it('renders loading message', async () => {
+ await setupAndSubmit(validResumeName);
+
+ await waitFor(() =>
+ expect(
+ screen.getByRole('button', {
+ name: /loading/i,
+ }),
+ ).toBeInTheDocument(),
+ );
+ await waitForElementToBeRemoved(() =>
+ screen.getByRole('button', {
+ name: /loading/i,
+ }),
+ );
+
+ await waitForModalWindowToHaveBeenClosed();
+ await waitForResumeToBeRenderedInPreview(validResumeName);
+ });
+
+ it('closes modal window', async () => {
+ await setupAndSubmit(validResumeName);
+
+ await waitFor(() =>
+ expect(waitForModalWindowToHaveBeenClosed()).resolves.toBeUndefined(),
+ );
+
+ await waitForResumeToBeRenderedInPreview(validResumeName);
+ });
+
+ it('renders created resume in preview', async () => {
+ await setupAndSubmit(validResumeName);
+
+ await waitForModalWindowToHaveBeenClosed();
+
+ await waitFor(() =>
+ expect(
+ expectResumeToBeRenderedInPreview(validResumeName),
+ ).resolves.toBeUndefined(),
+ );
+ });
+
+ it('adds resume in initial state to database', async () => {
+ const now = new Date().getTime();
+ const { user } = await setupAndSubmit(validResumeName);
+
+ await waitForModalWindowToHaveBeenClosed();
+ await waitForResumeToBeRenderedInPreview(validResumeName);
+
+ const actualUserResumes = (
+ await FirebaseStub.database()
+ .ref(DatabaseConstants.resumesPath)
+ .orderByChild('user')
+ .equalTo(user.uid)
+ .once('value')
+ ).val();
+ expect(Object.values(actualUserResumes)).toHaveLength(3);
+
+ const actualUserResumesFiltered = Object.values(actualUserResumes).filter(
+ (resume) => resume.name === validResumeName,
+ );
+ expect(actualUserResumesFiltered).toHaveLength(1);
+ const createdResume = actualUserResumesFiltered[0];
+ expect(createdResume.id).toBeTruthy();
+ expect(createdResume.preview).toBeTruthy();
+ expect(createdResume.preview).toEqual(unsplashPhotoResponseUrl);
+ expect(createdResume.createdAt).toBeTruthy();
+ expect(createdResume.createdAt).toBeGreaterThanOrEqual(now);
+ expect(createdResume.createdAt).toEqual(createdResume.updatedAt);
+ });
+});
diff --git a/src/pages/app/__tests__/dashboard.deleteResume.test.js b/src/pages/app/__tests__/dashboard.deleteResume.test.js
new file mode 100644
index 00000000..9d3b39b3
--- /dev/null
+++ b/src/pages/app/__tests__/dashboard.deleteResume.test.js
@@ -0,0 +1,121 @@
+import {
+ fireEvent,
+ getByText,
+ queryByText,
+ screen,
+ waitFor,
+} from '@testing-library/react';
+
+import FirebaseStub, { DatabaseConstants } from 'gatsby-plugin-firebase';
+
+import { menuToggleDataTestIdPrefix as resumePreviewMenuToggleDataTestIdPrefix } from '../../../components/dashboard/ResumePreview';
+import {
+ setupAndWaitForLoadingScreenToDisappear,
+ waitForResumeToDisappearFromPreview,
+ expectResumeToBeRenderedInPreview,
+ dismissNotification,
+ findAndDismissNotification,
+} from './helpers/dashboard';
+
+const waitForDatabaseRemoveToHaveCompleted = async (
+ mockDatabaseRemoveFunction,
+) => {
+ await waitFor(() => mockDatabaseRemoveFunction.mock.results[0].value);
+};
+
+const expectDatabaseRemoveToHaveCompleted = async (
+ mockDatabaseRemoveFunction,
+) => {
+ await waitFor(() =>
+ expect(mockDatabaseRemoveFunction).toHaveBeenCalledTimes(1),
+ );
+ await waitFor(() =>
+ expect(
+ mockDatabaseRemoveFunction.mock.results[0].value,
+ ).resolves.toBeUndefined(),
+ );
+};
+
+async function setup() {
+ const userResumes = await setupAndWaitForLoadingScreenToDisappear(
+ DatabaseConstants.user1,
+ );
+
+ const [resumeToDelete] = Object.values(userResumes).filter(
+ (resume) => resume.id === DatabaseConstants.demoStateResume1Id,
+ );
+ const resumeToDeleteId = resumeToDelete.id;
+ const [undeletedResume] = Object.values(userResumes).filter(
+ (resume) => resume.id === DatabaseConstants.initialStateResumeId,
+ );
+
+ const 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);
+
+ return { resumeToDelete, undeletedResume, mockDatabaseRemoveFunction };
+}
+
+it('removes resume from database and preview', async () => {
+ const {
+ resumeToDelete,
+ undeletedResume,
+ mockDatabaseRemoveFunction,
+ } = await setup();
+
+ await findAndDismissNotification();
+
+ await expectDatabaseRemoveToHaveCompleted(mockDatabaseRemoveFunction);
+
+ await waitFor(() =>
+ expect(
+ waitForResumeToDisappearFromPreview(resumeToDelete.name),
+ ).resolves.toBeUndefined(),
+ );
+ await expectResumeToBeRenderedInPreview(undeletedResume.name);
+});
+
+it('displays notification', async () => {
+ const { resumeToDelete, mockDatabaseRemoveFunction } = await setup();
+
+ const notification = await screen.findByRole('alert');
+ expect(
+ getByText(
+ notification,
+ new RegExp(`${resumeToDelete.name} was deleted successfully`, 'i'),
+ ),
+ ).toBeInTheDocument();
+ dismissNotification(notification);
+
+ await waitForDatabaseRemoveToHaveCompleted(mockDatabaseRemoveFunction);
+ await waitForResumeToDisappearFromPreview(resumeToDelete.name);
+});
+
+it('closes menu', async () => {
+ const { resumeToDelete, mockDatabaseRemoveFunction } = await setup();
+
+ const menuItems = screen.queryAllByRole('menuitem');
+ expect(menuItems).toHaveLength(0);
+
+ await findAndDismissNotification();
+ await waitForDatabaseRemoveToHaveCompleted(mockDatabaseRemoveFunction);
+ await waitForResumeToDisappearFromPreview(resumeToDelete.name);
+});
diff --git a/src/pages/app/__tests__/dashboard.duplicateResume.test.js b/src/pages/app/__tests__/dashboard.duplicateResume.test.js
new file mode 100644
index 00000000..43271418
--- /dev/null
+++ b/src/pages/app/__tests__/dashboard.duplicateResume.test.js
@@ -0,0 +1,103 @@
+import {
+ fireEvent,
+ queryByText,
+ screen,
+ waitFor,
+} from '@testing-library/react';
+
+import FirebaseStub, { DatabaseConstants } from 'gatsby-plugin-firebase';
+
+import { menuToggleDataTestIdPrefix as resumePreviewMenuToggleDataTestIdPrefix } from '../../../components/dashboard/ResumePreview';
+import {
+ setupWithFetchMockAndWaitForLoadingScreenToDisappear,
+ waitForResumeToBeRenderedInPreview,
+ expectResumeToBeRenderedInPreview,
+ unsplashPhotoResponseUrl,
+} from './helpers/dashboard';
+
+async function setup() {
+ const user = DatabaseConstants.user1;
+ const userResumes = await setupWithFetchMockAndWaitForLoadingScreenToDisappear(
+ user,
+ );
+
+ const [resumeToDuplicate] = Object.values(userResumes).filter(
+ (resume) => resume.id === DatabaseConstants.demoStateResume1Id,
+ );
+ const resumeToDuplicateId = resumeToDuplicate.id;
+ const duplicateResumeName = `${resumeToDuplicate.name} Copy`;
+
+ const resumeToDuplicateMenuToggle = await screen.findByTestId(
+ `${resumePreviewMenuToggleDataTestIdPrefix}${resumeToDuplicateId}`,
+ );
+ fireEvent.click(resumeToDuplicateMenuToggle);
+
+ const menuItems = screen.getAllByRole('menuitem');
+ let duplicateMenuItem = null;
+ for (let index = 0; index < menuItems.length; index++) {
+ if (queryByText(menuItems[index], /duplicate/i)) {
+ duplicateMenuItem = menuItems[index];
+ break;
+ }
+ }
+ fireEvent.click(duplicateMenuItem);
+
+ return { user, resumeToDuplicate, duplicateResumeName };
+}
+
+it('renders duplicate resume in preview', async () => {
+ const { duplicateResumeName } = await setup();
+
+ await waitFor(() =>
+ expect(
+ expectResumeToBeRenderedInPreview(duplicateResumeName),
+ ).resolves.toBeUndefined(),
+ );
+});
+
+it('adds duplicate resume to database', async () => {
+ const now = new Date().getTime();
+ const { user, resumeToDuplicate, duplicateResumeName } = await setup();
+
+ await waitForResumeToBeRenderedInPreview(duplicateResumeName);
+
+ const actualUserResumes = (
+ await FirebaseStub.database()
+ .ref(DatabaseConstants.resumesPath)
+ .orderByChild('user')
+ .equalTo(user.uid)
+ .once('value')
+ ).val();
+ expect(Object.values(actualUserResumes)).toHaveLength(3);
+
+ const actualUserResumesFiltered = Object.values(actualUserResumes).filter(
+ (resume) =>
+ resume.name === duplicateResumeName && resume.id !== resumeToDuplicate.id,
+ );
+ expect(actualUserResumesFiltered).toHaveLength(1);
+ const createdResume = actualUserResumesFiltered[0];
+ expect(createdResume.id).toBeTruthy();
+ expect(createdResume.preview).toBeTruthy();
+ expect(createdResume.preview).toEqual(unsplashPhotoResponseUrl);
+ expect(createdResume.createdAt).toBeTruthy();
+ expect(createdResume.createdAt).toBeGreaterThanOrEqual(now);
+ expect(createdResume.createdAt).toEqual(createdResume.updatedAt);
+ const expectedResume = {
+ ...resumeToDuplicate,
+ id: createdResume.id,
+ name: createdResume.name,
+ preview: createdResume.preview,
+ createdAt: createdResume.createdAt,
+ updatedAt: createdResume.updatedAt,
+ };
+ expect(createdResume).toEqual(expectedResume);
+});
+
+it('closes menu', async () => {
+ const { duplicateResumeName } = await setup();
+
+ const menuItems = screen.queryAllByRole('menuitem');
+ expect(menuItems).toHaveLength(0);
+
+ await waitForResumeToBeRenderedInPreview(duplicateResumeName);
+});
diff --git a/src/pages/app/__tests__/dashboard.rendering.test.js b/src/pages/app/__tests__/dashboard.rendering.test.js
new file mode 100644
index 00000000..76271428
--- /dev/null
+++ b/src/pages/app/__tests__/dashboard.rendering.test.js
@@ -0,0 +1,50 @@
+import { screen, waitFor } from '@testing-library/react';
+
+import { DatabaseConstants } from 'gatsby-plugin-firebase';
+
+import { createResumeButtonDataTestId } from '../../../components/dashboard/CreateResume';
+import setup, {
+ setupAndWaitForLoadingScreenToDisappear,
+ expectResumeToBeRenderedInPreview,
+ expectLoadingScreenToBeRendered,
+ waitForLoadingScreenToDisappear,
+} from './helpers/dashboard';
+
+const user = DatabaseConstants.user1;
+
+it('renders loading screen', async () => {
+ await setup(user);
+
+ expect(expectLoadingScreenToBeRendered()).toBeUndefined();
+ await waitForLoadingScreenToDisappear();
+});
+
+it('renders document title', async () => {
+ await setupAndWaitForLoadingScreenToDisappear(user);
+
+ await waitFor(() => {
+ expect(document.title).toEqual('Dashboard | Reactive Resume');
+ });
+});
+
+it('renders create resume', async () => {
+ await setupAndWaitForLoadingScreenToDisappear(user);
+
+ await waitFor(() => {
+ expect(screen.getByText(/create resume/i)).toBeInTheDocument();
+ });
+ await waitFor(() => {
+ expect(
+ screen.getByTestId(createResumeButtonDataTestId),
+ ).toBeInTheDocument();
+ });
+});
+
+it('renders preview of user resumes', async () => {
+ const userResumes = await setupAndWaitForLoadingScreenToDisappear(user);
+
+ expect(Object.keys(userResumes)).toHaveLength(2);
+
+ await expectResumeToBeRenderedInPreview(Object.values(userResumes)[0].name);
+ await expectResumeToBeRenderedInPreview(Object.values(userResumes)[1].name);
+});
diff --git a/src/pages/app/__tests__/dashboard.test.js b/src/pages/app/__tests__/dashboard.test.js
deleted file mode 100644
index 7b0da3b6..00000000
--- a/src/pages/app/__tests__/dashboard.test.js
+++ /dev/null
@@ -1,483 +0,0 @@
-import React from 'react';
-import {
- fireEvent,
- getByText,
- queryByText,
- render,
- screen,
- waitFor,
- waitForElementToBeRemoved,
-} from '@testing-library/react';
-
-import fetchMock from 'jest-fetch-mock';
-
-import FirebaseStub, { DatabaseConstants } from 'gatsby-plugin-firebase';
-
-import '../../../i18n/index';
-import '../../../utils/dayjs';
-import { unsplashPhotoRequestUrl, delay } from '../../../utils/index';
-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 unsplashPhotoResponseUrl = 'https://test-url-123456789.com';
-
- const waitForResumeToBeRenderedInPreview = async (resumeName) => {
- await screen.findByText(resumeName);
- };
-
- const expectResumeToBeRenderedInPreview = async (resumeName) => {
- await waitFor(() => {
- expect(screen.getByText(resumeName)).toBeInTheDocument();
- });
- };
-
- const waitForModalWindowToHaveBeenClosed = async () => {
- await waitForElementToBeRemoved(() =>
- screen.getByRole('textbox', { name: /name/i }),
- );
- };
-
- const dismissNotification = (notification) => {
- fireEvent.click(notification);
- };
-
- const findAndDismissNotification = async () => {
- const notification = await screen.findByRole('alert');
- dismissNotification(notification);
- };
-
- const waitForLoadingScreenToDisappearFn = async () => {
- await waitForElementToBeRemoved(() =>
- screen.getByTestId(loadingScreenTestId),
- );
- };
-
- const setupFetchMockFn = () => {
- fetchMock.resetMocks();
-
- fetchMock.mockImplementationOnce(async (input) => {
- await delay(100);
-
- if (input === unsplashPhotoRequestUrl) {
- return {
- url: unsplashPhotoResponseUrl,
- };
- }
-
- throw new Error('Unsupported input.');
- });
- };
-
- async function setup(
- waitForLoadingScreenToDisappear = true,
- setupFetchMock = false,
- ) {
- if (setupFetchMock) {
- setupFetchMockFn();
- }
-
- 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 waitForLoadingScreenToDisappearFn();
- }
- }
-
- 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();
- });
- await waitFor(() => {
- expect(
- screen.getByTestId(createResumeButtonDataTestId),
- ).toBeInTheDocument();
- });
- });
-
- it('preview of user resumes', async () => {
- expect(Object.keys(userResumes)).toHaveLength(2);
-
- await expectResumeToBeRenderedInPreview(
- Object.values(userResumes)[0].name,
- );
- await expectResumeToBeRenderedInPreview(
- Object.values(userResumes)[1].name,
- );
- });
- });
-
- describe('when resume is created', () => {
- let nameTextBox = null;
-
- beforeEach(async () => {
- await setup(true, true);
-
- 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.blur(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();
- dismissNotification(notification);
- });
- });
-
- describe('with valid name', () => {
- const resumeName = 'Resume for SW development roles';
- let now = 0;
-
- beforeEach(() => {
- now = new Date().getTime();
-
- fireEvent.change(nameTextBox, {
- target: { value: resumeName },
- });
-
- const modalCreateResumeButton = screen.getByRole('button', {
- name: /create resume/i,
- });
- fireEvent.click(modalCreateResumeButton);
- });
-
- it('renders loading message', async () => {
- await waitFor(() =>
- expect(
- screen.getByRole('button', {
- name: /loading/i,
- }),
- ).toBeInTheDocument(),
- );
- await waitForElementToBeRemoved(() =>
- screen.getByRole('button', {
- name: /loading/i,
- }),
- );
-
- await waitForModalWindowToHaveBeenClosed();
- await waitForResumeToBeRenderedInPreview(resumeName);
- });
-
- it('closes modal window', async () => {
- await waitFor(() =>
- expect(waitForModalWindowToHaveBeenClosed()).resolves.toBeUndefined(),
- );
-
- await waitForResumeToBeRenderedInPreview(resumeName);
- });
-
- it('renders created resume in preview', async () => {
- await waitForModalWindowToHaveBeenClosed();
-
- await waitFor(() =>
- expect(
- expectResumeToBeRenderedInPreview(resumeName),
- ).resolves.toBeUndefined(),
- );
- });
-
- it('adds resume in initial state to database', async () => {
- await waitForModalWindowToHaveBeenClosed();
- await waitForResumeToBeRenderedInPreview(resumeName);
-
- const actualUserResumes = (
- await FirebaseStub.database()
- .ref(DatabaseConstants.resumesPath)
- .orderByChild('user')
- .equalTo(user.uid)
- .once('value')
- ).val();
- expect(Object.values(actualUserResumes)).toHaveLength(3);
-
- const actualUserResumesFiltered = Object.values(
- actualUserResumes,
- ).filter((resume) => resume.name === resumeName);
- expect(actualUserResumesFiltered).toHaveLength(1);
- const createdResume = actualUserResumesFiltered[0];
- expect(createdResume.id).toBeTruthy();
- expect(createdResume.preview).toBeTruthy();
- expect(createdResume.preview).toEqual(unsplashPhotoResponseUrl);
- expect(createdResume.createdAt).toBeTruthy();
- expect(createdResume.createdAt).toBeGreaterThanOrEqual(now);
- expect(createdResume.createdAt).toEqual(createdResume.updatedAt);
- });
- });
- });
-
- 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(),
- );
- };
-
- const waitForResumeToDisappearFromPreview = async (resumeName) => {
- await waitFor(() =>
- screen.queryByText(resumeName) ? Promise.reject() : Promise.resolve(),
- );
- };
-
- beforeEach(async () => {
- await setup();
-
- [resumeToDelete] = Object.values(userResumes).filter(
- (resume) => resume.id === DatabaseConstants.demoStateResume1Id,
- );
- resumeToDeleteId = resumeToDelete.id;
- [undeletedResume] = Object.values(userResumes).filter(
- (resume) => resume.id === DatabaseConstants.initialStateResumeId,
- );
-
- 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 findAndDismissNotification();
-
- await expectDatabaseRemoveToHaveCompleted();
-
- await waitFor(() =>
- expect(
- waitForResumeToDisappearFromPreview(resumeToDelete.name),
- ).resolves.toBeUndefined(),
- );
- await expectResumeToBeRenderedInPreview(undeletedResume.name);
- });
-
- it('displays notification', async () => {
- const notification = await screen.findByRole('alert');
- expect(
- getByText(
- notification,
- new RegExp(`${resumeToDelete.name} was deleted successfully`, 'i'),
- ),
- ).toBeInTheDocument();
- dismissNotification(notification);
-
- await waitForDatabaseRemoveToHaveCompleted();
- await waitForResumeToDisappearFromPreview(resumeToDelete.name);
- });
-
- it('closes menu', async () => {
- const menuItems = screen.queryAllByRole('menuitem');
- expect(menuItems).toHaveLength(0);
-
- await findAndDismissNotification();
- await waitForDatabaseRemoveToHaveCompleted();
- await waitForResumeToDisappearFromPreview(resumeToDelete.name);
- });
- });
-
- describe('when resume is duplicated', () => {
- let resumeToDuplicate = null;
- let resumeToDuplicateId = null;
- let duplicateResumeName = null;
- let now = 0;
-
- beforeEach(async () => {
- await setup(true, true);
-
- now = new Date().getTime();
-
- [resumeToDuplicate] = Object.values(userResumes).filter(
- (resume) => resume.id === DatabaseConstants.demoStateResume1Id,
- );
- resumeToDuplicateId = resumeToDuplicate.id;
- duplicateResumeName = `${resumeToDuplicate.name} Copy`;
-
- const resumeToDuplicateMenuToggle = await screen.findByTestId(
- `${resumePreviewMenuToggleDataTestIdPrefix}${resumeToDuplicateId}`,
- );
- fireEvent.click(resumeToDuplicateMenuToggle);
-
- const menuItems = screen.getAllByRole('menuitem');
- let duplicateMenuItem = null;
- for (let index = 0; index < menuItems.length; index++) {
- if (queryByText(menuItems[index], /duplicate/i)) {
- duplicateMenuItem = menuItems[index];
- break;
- }
- }
- fireEvent.click(duplicateMenuItem);
- });
-
- it('renders duplicate resume in preview', async () => {
- await waitFor(() =>
- expect(
- expectResumeToBeRenderedInPreview(duplicateResumeName),
- ).resolves.toBeUndefined(),
- );
- });
-
- it('adds duplicate resume to database', async () => {
- await waitForResumeToBeRenderedInPreview(duplicateResumeName);
-
- const actualUserResumes = (
- await FirebaseStub.database()
- .ref(DatabaseConstants.resumesPath)
- .orderByChild('user')
- .equalTo(user.uid)
- .once('value')
- ).val();
- expect(Object.values(actualUserResumes)).toHaveLength(3);
-
- const actualUserResumesFiltered = Object.values(actualUserResumes).filter(
- (resume) =>
- resume.name === duplicateResumeName &&
- resume.id !== resumeToDuplicateId,
- );
- expect(actualUserResumesFiltered).toHaveLength(1);
- const createdResume = actualUserResumesFiltered[0];
- expect(createdResume.id).toBeTruthy();
- expect(createdResume.preview).toBeTruthy();
- expect(createdResume.preview).toEqual(unsplashPhotoResponseUrl);
- expect(createdResume.createdAt).toBeTruthy();
- expect(createdResume.createdAt).toBeGreaterThanOrEqual(now);
- expect(createdResume.createdAt).toEqual(createdResume.updatedAt);
- const expectedResume = {
- ...resumeToDuplicate,
- id: createdResume.id,
- name: createdResume.name,
- preview: createdResume.preview,
- createdAt: createdResume.createdAt,
- updatedAt: createdResume.updatedAt,
- };
- expect(createdResume).toEqual(expectedResume);
- });
-
- it('closes menu', async () => {
- const menuItems = screen.queryAllByRole('menuitem');
- expect(menuItems).toHaveLength(0);
-
- await waitForResumeToBeRenderedInPreview(duplicateResumeName);
- });
- });
-
- describe('while loading', () => {
- beforeEach(async () => {
- await setup(false);
- });
-
- it('renders loading screen', async () => {
- expect(screen.getByTestId(loadingScreenTestId)).toBeInTheDocument();
- await waitForLoadingScreenToDisappearFn();
-
- await waitForResumeToBeRenderedInPreview(
- Object.values(userResumes)[0].name,
- );
- await waitForResumeToBeRenderedInPreview(
- Object.values(userResumes)[1].name,
- );
- });
- });
-});
diff --git a/src/pages/app/__tests__/helpers/dashboard.js b/src/pages/app/__tests__/helpers/dashboard.js
new file mode 100644
index 00000000..f97226da
--- /dev/null
+++ b/src/pages/app/__tests__/helpers/dashboard.js
@@ -0,0 +1,158 @@
+import React from 'react';
+import {
+ fireEvent,
+ render,
+ screen,
+ waitFor,
+ waitForElementToBeRemoved,
+} from '@testing-library/react';
+
+import fetchMock from 'jest-fetch-mock';
+
+import FirebaseStub, { DatabaseConstants } from 'gatsby-plugin-firebase';
+
+import '../../../../i18n/index';
+import '../../../../utils/dayjs';
+import { dataTestId as loadingScreenTestId } from '../../../../components/router/LoadingScreen';
+import { unsplashPhotoRequestUrl, delay } from '../../../../utils/index';
+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';
+
+const waitForResumeToBeRenderedInPreview = async (resumeName) => {
+ await screen.findByText(resumeName);
+};
+
+const waitForResumeToDisappearFromPreview = async (resumeName) => {
+ await waitFor(() =>
+ screen.queryByText(resumeName) ? Promise.reject() : Promise.resolve(),
+ );
+};
+
+const expectResumeToBeRenderedInPreview = async (resumeName) => {
+ await waitFor(() => {
+ expect(screen.getByText(resumeName)).toBeInTheDocument();
+ });
+};
+
+const waitForModalWindowToHaveBeenClosed = async () => {
+ await waitForElementToBeRemoved(() =>
+ screen.getByRole('textbox', { name: /name/i }),
+ );
+};
+
+const dismissNotification = (notification) => {
+ fireEvent.click(notification);
+};
+
+const findAndDismissNotification = async () => {
+ const notification = await screen.findByRole('alert');
+ dismissNotification(notification);
+};
+
+const expectLoadingScreenToBeRendered = () => {
+ expect(screen.getByTestId(loadingScreenTestId)).toBeInTheDocument();
+};
+
+const waitForLoadingScreenToDisappearFn = async () => {
+ await waitForElementToBeRemoved(() =>
+ screen.getByTestId(loadingScreenTestId),
+ );
+};
+
+const unsplashPhotoResponseUrl = 'https://test-url-123456789.com';
+
+const setupFetchMockFn = () => {
+ fetchMock.resetMocks();
+
+ fetchMock.mockImplementationOnce(async (input) => {
+ await delay(100);
+
+ if (input === unsplashPhotoRequestUrl) {
+ return {
+ url: unsplashPhotoResponseUrl,
+ };
+ }
+
+ throw new Error('Unsupported input.');
+ });
+};
+
+// eslint-disable-next-line no-underscore-dangle
+async function _setup(user, waitForLoadingScreenToDisappear, setupFetchMock) {
+ if (setupFetchMock) {
+ setupFetchMockFn();
+ }
+
+ FirebaseStub.database().initializeData();
+
+ const userResumes = (
+ await FirebaseStub.database()
+ .ref(DatabaseConstants.resumesPath)
+ .orderByChild('user')
+ .equalTo(user.uid)
+ .once('value')
+ ).val();
+
+ FirebaseStub.auth().signInAnonymously();
+
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ if (waitForLoadingScreenToDisappear) {
+ await waitForLoadingScreenToDisappearFn();
+ }
+
+ return userResumes;
+}
+
+async function setup(user) {
+ const userResumes = await _setup(user, false, false);
+ return userResumes;
+}
+
+async function setupAndWaitForLoadingScreenToDisappear(user) {
+ const userResumes = await _setup(user, true, false);
+ return userResumes;
+}
+
+async function setupWithFetchMockAndWaitForLoadingScreenToDisappear(user) {
+ const userResumes = await _setup(user, true, true);
+ return userResumes;
+}
+
+export default setup;
+
+export {
+ setupAndWaitForLoadingScreenToDisappear,
+ setupWithFetchMockAndWaitForLoadingScreenToDisappear,
+ waitForResumeToBeRenderedInPreview,
+ waitForResumeToDisappearFromPreview,
+ expectResumeToBeRenderedInPreview,
+ waitForModalWindowToHaveBeenClosed,
+ dismissNotification,
+ findAndDismissNotification,
+ expectLoadingScreenToBeRendered,
+ waitForLoadingScreenToDisappearFn as waitForLoadingScreenToDisappear,
+ unsplashPhotoResponseUrl,
+};