diff --git a/src/pages/app/__tests__/dashboard.createResume.test.js b/src/pages/app/__tests__/dashboard.createResume.test.js index 72fc0ad6..17a5d616 100644 --- a/src/pages/app/__tests__/dashboard.createResume.test.js +++ b/src/pages/app/__tests__/dashboard.createResume.test.js @@ -15,7 +15,7 @@ import { waitForModalWindowToHaveBeenClosed, dismissNotification, unsplashPhotoResponseUrl, - setupWithFetchMockAndWaitForLoadingScreenToDisappear, + setupWithFetchMockAndWait, } from './helpers/dashboard'; const tooShortResumeName = 'CV 1'; @@ -23,7 +23,7 @@ const validResumeName = 'Resume for SW development roles'; async function setup() { const user = DatabaseConstants.user1; - await setupWithFetchMockAndWaitForLoadingScreenToDisappear(user); + await setupWithFetchMockAndWait(user, true); const dashboardCreateResumeButton = await screen.findByTestId( createResumeButtonDataTestId, diff --git a/src/pages/app/__tests__/dashboard.deleteResume.test.js b/src/pages/app/__tests__/dashboard.deleteResume.test.js index 9d3b39b3..1b84ac1d 100644 --- a/src/pages/app/__tests__/dashboard.deleteResume.test.js +++ b/src/pages/app/__tests__/dashboard.deleteResume.test.js @@ -10,7 +10,7 @@ import FirebaseStub, { DatabaseConstants } from 'gatsby-plugin-firebase'; import { menuToggleDataTestIdPrefix as resumePreviewMenuToggleDataTestIdPrefix } from '../../../components/dashboard/ResumePreview'; import { - setupAndWaitForLoadingScreenToDisappear, + setupAndWait, waitForResumeToDisappearFromPreview, expectResumeToBeRenderedInPreview, dismissNotification, @@ -37,9 +37,7 @@ const expectDatabaseRemoveToHaveCompleted = async ( }; async function setup() { - const userResumes = await setupAndWaitForLoadingScreenToDisappear( - DatabaseConstants.user1, - ); + const userResumes = await setupAndWait(DatabaseConstants.user1, true); const [resumeToDelete] = Object.values(userResumes).filter( (resume) => resume.id === DatabaseConstants.demoStateResume1Id, diff --git a/src/pages/app/__tests__/dashboard.duplicateResume.test.js b/src/pages/app/__tests__/dashboard.duplicateResume.test.js index 43271418..9b9c254c 100644 --- a/src/pages/app/__tests__/dashboard.duplicateResume.test.js +++ b/src/pages/app/__tests__/dashboard.duplicateResume.test.js @@ -9,7 +9,7 @@ import FirebaseStub, { DatabaseConstants } from 'gatsby-plugin-firebase'; import { menuToggleDataTestIdPrefix as resumePreviewMenuToggleDataTestIdPrefix } from '../../../components/dashboard/ResumePreview'; import { - setupWithFetchMockAndWaitForLoadingScreenToDisappear, + setupWithFetchMockAndWait, waitForResumeToBeRenderedInPreview, expectResumeToBeRenderedInPreview, unsplashPhotoResponseUrl, @@ -17,9 +17,7 @@ import { async function setup() { const user = DatabaseConstants.user1; - const userResumes = await setupWithFetchMockAndWaitForLoadingScreenToDisappear( - user, - ); + const userResumes = await setupWithFetchMockAndWait(user, true); const [resumeToDuplicate] = Object.values(userResumes).filter( (resume) => resume.id === DatabaseConstants.demoStateResume1Id, diff --git a/src/pages/app/__tests__/dashboard.renameResume.test.js b/src/pages/app/__tests__/dashboard.renameResume.test.js new file mode 100644 index 00000000..4ad7556f --- /dev/null +++ b/src/pages/app/__tests__/dashboard.renameResume.test.js @@ -0,0 +1,214 @@ +import { + fireEvent, + getByText, + queryByText, + screen, + waitFor, + waitForElementToBeRemoved, +} from '@testing-library/react'; + +import FirebaseStub, { DatabaseConstants } from 'gatsby-plugin-firebase'; + +import { menuToggleDataTestIdPrefix as resumePreviewMenuToggleDataTestIdPrefix } from '../../../components/dashboard/ResumePreview'; +import { + setupAndWait, + waitForResumeToBeRenderedInPreview, + expectResumeToBeRenderedInPreview, + dismissNotification, + waitForModalWindowToHaveBeenClosed, +} from './helpers/dashboard'; + +const tooShortResumeName = 'CV 2'; +const validResumeName = 'Resume for SW development roles - renamed'; + +const waitForDatabaseUpdateToHaveCompleted = async ( + mockDatabaseUpdateFunction, +) => { + await waitFor(() => mockDatabaseUpdateFunction.mock.calls[0][0]); + await waitFor(() => mockDatabaseUpdateFunction.mock.results[0].value); +}; + +const expectDatabaseUpdateToHaveCompleted = async ( + mockDatabaseUpdateFunction, +) => { + await waitFor(() => + expect(mockDatabaseUpdateFunction).toHaveBeenCalledTimes(1), + ); + await waitFor(() => + expect( + mockDatabaseUpdateFunction.mock.results[0].value, + ).resolves.toBeUndefined(), + ); +}; + +async function setup() { + const userResumes = await setupAndWait(DatabaseConstants.user1, true); + + const [resumeToRename] = Object.values(userResumes).filter( + (resume) => resume.id === DatabaseConstants.demoStateResume1Id, + ); + const resumeToRenameId = resumeToRename.id; + + const mockDatabaseUpdateFunction = jest.spyOn( + FirebaseStub.database().ref( + `${DatabaseConstants.resumesPath}/${resumeToRenameId}`, + ), + 'update', + ); + mockDatabaseUpdateFunction.mockClear(); + + const resumeToRenameMenuToggle = await screen.findByTestId( + `${resumePreviewMenuToggleDataTestIdPrefix}${resumeToRenameId}`, + ); + fireEvent.click(resumeToRenameMenuToggle); + + const menuItems = screen.getAllByRole('menuitem'); + let renameMenuItem = null; + for (let index = 0; index < menuItems.length; index++) { + if (queryByText(menuItems[index], /rename/i)) { + renameMenuItem = menuItems[index]; + break; + } + } + fireEvent.click(renameMenuItem); + + const nameTextBox = screen.getByRole('textbox', { name: /name/i }); + + return { resumeToRename, mockDatabaseUpdateFunction, nameTextBox }; +} + +async function setupAndSubmit(resumeNewName) { + const { + resumeToRename, + mockDatabaseUpdateFunction, + nameTextBox, + } = await setup(); + + fireEvent.change(nameTextBox, { + target: { value: resumeNewName }, + }); + + const modalEditResumeButton = screen.getByRole('button', { + name: /edit resume/i, + }); + fireEvent.click(modalEditResumeButton); + + return { + renamedResume: resumeToRename, + mockDatabaseUpdateFunction, + nameTextBox, + }; +} + +test('renders current name in modal window', async () => { + const { resumeToRename, nameTextBox } = await setup(); + + expect(nameTextBox).toHaveValue(resumeToRename.name); +}); + +describe('with invalid name', () => { + test('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(), + ); + }); + + test('displays notification', async () => { + await setupAndSubmit(tooShortResumeName); + + const notification = await screen.findByRole('alert'); + /* + const notification = await screen.findByText( + /You might need to fill up all the required fields/i, + ); + */ + expect( + getByText( + notification, + /You might need to fill up all the required fields/i, + ), + ).toBeInTheDocument(); + dismissNotification(notification); + }); +}); + +describe('with valid name', () => { + test('renders loading message', async () => { + const { mockDatabaseUpdateFunction } = await setupAndSubmit( + validResumeName, + ); + + await waitFor(() => + expect( + screen.getByRole('button', { + name: /loading/i, + }), + ).toBeInTheDocument(), + ); + await waitForElementToBeRemoved(() => + screen.getByRole('button', { + name: /loading/i, + }), + ); + + await waitForModalWindowToHaveBeenClosed(); + await waitForDatabaseUpdateToHaveCompleted(mockDatabaseUpdateFunction); + await waitForResumeToBeRenderedInPreview(validResumeName); + }); + + test('closes modal window', async () => { + const { mockDatabaseUpdateFunction } = await setupAndSubmit( + validResumeName, + ); + + await waitFor(() => + expect(waitForModalWindowToHaveBeenClosed()).resolves.toBeUndefined(), + ); + + await waitForDatabaseUpdateToHaveCompleted(mockDatabaseUpdateFunction); + await waitForResumeToBeRenderedInPreview(validResumeName); + }); + + test('renders renamed resume in preview', async () => { + const { mockDatabaseUpdateFunction } = await setupAndSubmit( + validResumeName, + ); + + await waitForModalWindowToHaveBeenClosed(); + await waitForDatabaseUpdateToHaveCompleted(mockDatabaseUpdateFunction); + + await waitFor(() => + expect( + expectResumeToBeRenderedInPreview(validResumeName), + ).resolves.toBeUndefined(), + ); + }); + + test('updates database', async () => { + const now = new Date().getTime(); + const { renamedResume, mockDatabaseUpdateFunction } = await setupAndSubmit( + validResumeName, + ); + const renamedResumeId = renamedResume.id; + + await waitForModalWindowToHaveBeenClosed(); + + await expectDatabaseUpdateToHaveCompleted(mockDatabaseUpdateFunction); + const mockDatabaseUpdateFunctionCallArgument = + mockDatabaseUpdateFunction.mock.calls[0][0]; + expect(mockDatabaseUpdateFunctionCallArgument.id).toBe(renamedResumeId); + expect(mockDatabaseUpdateFunctionCallArgument.name).toBe(validResumeName); + expect( + mockDatabaseUpdateFunctionCallArgument.updatedAt, + ).toBeGreaterThanOrEqual(now); + + await waitForResumeToBeRenderedInPreview(validResumeName); + }); +}); diff --git a/src/pages/app/__tests__/dashboard.rendering.test.js b/src/pages/app/__tests__/dashboard.rendering.test.js index 76271428..83873677 100644 --- a/src/pages/app/__tests__/dashboard.rendering.test.js +++ b/src/pages/app/__tests__/dashboard.rendering.test.js @@ -4,7 +4,7 @@ import { DatabaseConstants } from 'gatsby-plugin-firebase'; import { createResumeButtonDataTestId } from '../../../components/dashboard/CreateResume'; import setup, { - setupAndWaitForLoadingScreenToDisappear, + setupAndWait, expectResumeToBeRenderedInPreview, expectLoadingScreenToBeRendered, waitForLoadingScreenToDisappear, @@ -20,7 +20,7 @@ it('renders loading screen', async () => { }); it('renders document title', async () => { - await setupAndWaitForLoadingScreenToDisappear(user); + await setupAndWait(user, true); await waitFor(() => { expect(document.title).toEqual('Dashboard | Reactive Resume'); @@ -28,7 +28,7 @@ it('renders document title', async () => { }); it('renders create resume', async () => { - await setupAndWaitForLoadingScreenToDisappear(user); + await setupAndWait(user, true); await waitFor(() => { expect(screen.getByText(/create resume/i)).toBeInTheDocument(); @@ -41,7 +41,7 @@ it('renders create resume', async () => { }); it('renders preview of user resumes', async () => { - const userResumes = await setupAndWaitForLoadingScreenToDisappear(user); + const userResumes = await setupAndWait(user, true); expect(Object.keys(userResumes)).toHaveLength(2); diff --git a/src/pages/app/__tests__/helpers/dashboard.js b/src/pages/app/__tests__/helpers/dashboard.js index 4eba4f3e..884ec0ed 100644 --- a/src/pages/app/__tests__/helpers/dashboard.js +++ b/src/pages/app/__tests__/helpers/dashboard.js @@ -133,21 +133,28 @@ async function setup(user) { return userResumes; } -async function setupAndWaitForLoadingScreenToDisappear(user) { - const userResumes = await _setup(user, true, false); +async function setupAndWait(user, waitForLoadingScreenToDisappear) { + const userResumes = await _setup( + user, + waitForLoadingScreenToDisappear, + false, + ); return userResumes; } -async function setupWithFetchMockAndWaitForLoadingScreenToDisappear(user) { - const userResumes = await _setup(user, true, true); +async function setupWithFetchMockAndWait( + user, + waitForLoadingScreenToDisappear, +) { + const userResumes = await _setup(user, waitForLoadingScreenToDisappear, true); return userResumes; } export default setup; export { - setupAndWaitForLoadingScreenToDisappear, - setupWithFetchMockAndWaitForLoadingScreenToDisappear, + setupAndWait, + setupWithFetchMockAndWait, waitForResumeToBeRenderedInPreview, waitForResumeToDisappearFromPreview, expectResumeToBeRenderedInPreview,