From bae10755e163e3b39cf8cdef85ec9be7cb6106db Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Thu, 9 Jan 2025 19:29:01 +0200 Subject: [PATCH] VKT(Frontend): Tests for contact request flow --- .../public_enrollment_contact.spec.ts | 138 ++++++++++++++++++ .../vkt/src/tests/cypress/support/commands.ts | 11 ++ .../publicEnrollmentContactPage.ts | 84 +++++++++++ .../publicGoodAndSatisfactoryLevelPage.ts | 6 + .../tests/cypress/support/types/index.d.ts | 5 + .../src/tests/cypress/support/utils/dialog.ts | 4 + .../packages/vkt/src/tests/msw/handlers.ts | 9 ++ 7 files changed, 257 insertions(+) create mode 100644 frontend/packages/vkt/src/tests/cypress/integration/public_enrollment_contact.spec.ts create mode 100644 frontend/packages/vkt/src/tests/cypress/support/page-objects/publicEnrollmentContactPage.ts create mode 100644 frontend/packages/vkt/src/tests/cypress/support/utils/dialog.ts diff --git a/frontend/packages/vkt/src/tests/cypress/integration/public_enrollment_contact.spec.ts b/frontend/packages/vkt/src/tests/cypress/integration/public_enrollment_contact.spec.ts new file mode 100644 index 000000000..5d2fabaf7 --- /dev/null +++ b/frontend/packages/vkt/src/tests/cypress/integration/public_enrollment_contact.spec.ts @@ -0,0 +1,138 @@ +import { PublicEnrollmentContact } from 'interfaces/publicEnrollment'; +import { onPublicEnrollmentContactPage } from 'tests/cypress/support/page-objects/publicEnrollmentContactPage'; +import { + expectNoDialog, + findDialogByText, +} from 'tests/cypress/support/utils/dialog'; +import { publicExaminers } from 'tests/msw/fixtures/publicExaminer'; +import { onPublicGoodAndSatisfactoryLevelPage } from '../support/page-objects/publicGoodAndSatisfactoryLevelPage'; + +interface ContactDetailsField { + label: string; + value: string; +} + +const examinerToContact = publicExaminers.filter(({ id }) => id === 2)[0]; + +const expectedEnrollmentContact: PublicEnrollmentContact = { + firstName: 'Tero', + lastName: 'Testaaja', + message: 'Viestiä pukkaa!', + privacyStatementConfirmation: false, + email: 'tero@test.invalid', + emailConfirmation: 'tero@test.invalid', + phoneNumber: '0501234567', +}; + +const contactDetailsFields: Array = [ + { label: 'Etunimi *', value: expectedEnrollmentContact.firstName }, + { label: 'Sukunimi *', value: expectedEnrollmentContact.lastName }, + { label: 'Puhelinnumero *', value: expectedEnrollmentContact.phoneNumber }, + { label: 'Sähköposti *', value: expectedEnrollmentContact.email }, + { label: 'Vahvista sähköposti *', value: 'typo@test.invalid' }, +]; + +const expectAndCloseErrorDialog = ( + expectedText = 'Tiedoissa on korjattavaa!', +) => { + findDialogByText(expectedText) + .findByRole('button', { + name: 'Takaisin', + }) + .click(); + expectNoDialog(); +}; + +describe('PublicEnrollmentContactPage', () => { + it('should require user to fill contact information and specify their desired exam', () => { + cy.openPublicEnrollmentContactPage(examinerToContact.id); + // Step 1: Fill contact details + onPublicEnrollmentContactPage.expectStepHeading('Yhteystietosi'); + onPublicEnrollmentContactPage.clickNext(); + expectAndCloseErrorDialog(); + + // Assert error dialog is raised if there are missing or incorrect fields + contactDetailsFields.forEach(({ label, value }) => { + cy.findByRole('textbox', { name: label }).type(value); + onPublicEnrollmentContactPage.clickNext(); + expectAndCloseErrorDialog(); + }); + + // Fix confirmation of email and proceed to next step + cy.findByRole('textbox', { name: 'Vahvista sähköposti *' }) + .clear() + .type('tero@test.invalid'); + onPublicEnrollmentContactPage.clickNext(); + + // Step 2: Fill info regarding desired exam + + onPublicEnrollmentContactPage.expectStepHeading( + 'Valitse tutkinto ja lähetä viesti', + ); + onPublicEnrollmentContactPage.clickSubmit(); + expectAndCloseErrorDialog(); + + onPublicEnrollmentContactPage.selectFullExam(false); + onPublicEnrollmentContactPage.selectPreviousEnrollment(false); + onPublicEnrollmentContactPage.writeMessage('Viestiä pukkaa!'); + + onPublicEnrollmentContactPage.clickSubmit(); + expectAndCloseErrorDialog( + 'Kerro, minkä osakokeen / mitkä osakokeet haluat suorittaa', + ); + + onPublicEnrollmentContactPage.writePartialExamDescription('kirjoittaminen'); + onPublicEnrollmentContactPage.clickSubmit(); + + onPublicEnrollmentContactPage.expectStepHeading('Viesti lähetetty'); + }); + + describe('after successfully submitting a contact request', () => { + const stateToPersist = { + publicEnrollmentContact: JSON.stringify({ + enrollment: expectedEnrollmentContact, + contactedExaminers: [{ id: examinerToContact.id }], + }), + }; + beforeEach(() => { + cy.openPublicEnrollmentContactPage( + 2, + 'valmis', + JSON.stringify(stateToPersist), + ); + }); + + it('should allow user to contact another examiner with same details after submitting one request', () => { + onPublicEnrollmentContactPage.submitAnotherMessage(); + onPublicGoodAndSatisfactoryLevelPage.assertExaminerAlreadyContacted( + 'Anneli Alanen', + ); + onPublicGoodAndSatisfactoryLevelPage.contactExaminer('Eero Eskola'); + onPublicEnrollmentContactPage.expectStepHeading('Vahvista yhteystietosi'); + + onPublicEnrollmentContactPage.continueWithExistingDetails(); + contactDetailsFields.forEach(({ label, value }) => { + if (label === 'Vahvista sähköposti *') { + cy.findByRole('textbox', { name: label }).should( + 'have.value', + expectedEnrollmentContact.email, + ); + } else { + cy.findByRole('textbox', { name: label }).should('have.value', value); + } + }); + }); + + it('should allow user to clear their details', () => { + onPublicEnrollmentContactPage.clearExistingDetails(); + + // Contacting same examiner again should now be possible + onPublicGoodAndSatisfactoryLevelPage.contactExaminer('Anneli Alanen'); + onPublicEnrollmentContactPage.expectStepHeading('Yhteystietosi'); + // Expect all fields to be empty + contactDetailsFields.forEach(({ label }) => { + cy.findByRole('textbox', { name: label }).should('be.empty'); + }); + }); + }); +}); diff --git a/frontend/packages/vkt/src/tests/cypress/support/commands.ts b/frontend/packages/vkt/src/tests/cypress/support/commands.ts index d78263578..9a6cbb897 100644 --- a/frontend/packages/vkt/src/tests/cypress/support/commands.ts +++ b/frontend/packages/vkt/src/tests/cypress/support/commands.ts @@ -39,6 +39,17 @@ Cypress.Commands.add('openPublicGoodAndSatisfactoryLevelPage', () => { cy.visit(AppRoutes.PublicGoodAndSatisfactoryLevelLanding); }); +Cypress.Commands.add( + 'openPublicEnrollmentContactPage', + (examinerId: number, step = 'tiedot', persistedState = '{}') => { + cy.window().then((win) => { + win.sessionStorage.setItem('persist:root', persistedState); + cy.setCookie('cookie-consent-vkt', 'true'); + }); + cy.visit(`${AppRoutes.PublicEnrollmentContact}/${examinerId}/${step}`); + }, +); + Cypress.Commands.add('openClerkExcellentLevelPage', () => { cy.window().then((win) => win.sessionStorage.setItem('persist:root', '{}')); cy.visit(AppRoutes.ClerkExcellentLevelPage); diff --git a/frontend/packages/vkt/src/tests/cypress/support/page-objects/publicEnrollmentContactPage.ts b/frontend/packages/vkt/src/tests/cypress/support/page-objects/publicEnrollmentContactPage.ts new file mode 100644 index 000000000..0cf8391e4 --- /dev/null +++ b/frontend/packages/vkt/src/tests/cypress/support/page-objects/publicEnrollmentContactPage.ts @@ -0,0 +1,84 @@ +class PublicEnrollmentContactPage { + elements = { + nextButton: () => cy.findByRole('button', { name: 'Seuraava' }), + backButton: () => cy.findByRole('button', { name: 'Takaisin' }), + cancelButton: () => cy.findByRole('button', { name: 'Peruuta' }), + submitButton: () => cy.findByRole('button', { name: 'Lähetä' }), + submitAnotherMessageButton: () => + cy.findByRole('button', { name: 'Lähetä toinen viesti' }), + backToHomePageButton: () => + cy.findByRole('button', { name: 'Takaisin etusivulle' }), + continueWithExistingDetailsButton: () => cy.findByRole('button', { name: 'Kyllä, jatka'}), + heading: (heading: string) => cy.findByRole('heading', { name: heading }), + fullOrPartialExamRadioGroup: () => + cy.findByRole('group', { + name: 'Haluatko suorittaa sekä suullisen että kirjallisen taidon tutkinnon? *', + }), + previousEnrollmentRadioGroup: () => + cy.findByRole('group', { + name: 'Oletko osallistunut aiemmin hyvän ja tyydyttävän taidon kielitutkintoon? *', + }), + partialExamDescription: () => + cy.findByRole('textbox', { + name: 'Kerro, minkä osakokeen / mitkä osakokeet haluat suorittaa *', + }), + message: () => cy.findByRole('textbox', { name: 'Viesti *' }), + }; + + clickNext() { + this.elements.nextButton().click(); + } + + clickCancel() { + this.elements.cancelButton().click(); + } + + clickSubmit() { + this.elements.submitButton().click(); + } + + expectStepHeading(heading: string) { + this.elements.heading(heading).should('be.visible'); + } + + private selectRadioButton(radioGroup: Cypress.Chainable, value: boolean) { + const radioButtonSelector = value ? /Kyllä.*/ : /En.*/; + radioGroup.findByRole('radio', { name: radioButtonSelector }).click(); + } + + selectFullExam(isFullExam: boolean) { + this.selectRadioButton( + this.elements.fullOrPartialExamRadioGroup(), + isFullExam, + ); + } + + selectPreviousEnrollment(hasPreviousEnrollment: boolean) { + this.selectRadioButton( + this.elements.previousEnrollmentRadioGroup(), + hasPreviousEnrollment, + ); + } + + writePartialExamDescription(description: string) { + this.elements.partialExamDescription().type(description); + } + + writeMessage(message: string) { + this.elements.message().type(message); + } + + submitAnotherMessage() { + this.elements.submitAnotherMessageButton().click(); + } + + clearExistingDetails() { + this.elements.backToHomePageButton().click(); + } + + continueWithExistingDetails() { + this.elements.continueWithExistingDetailsButton().click(); + } +} + +export const onPublicEnrollmentContactPage = new PublicEnrollmentContactPage(); diff --git a/frontend/packages/vkt/src/tests/cypress/support/page-objects/publicGoodAndSatisfactoryLevelPage.ts b/frontend/packages/vkt/src/tests/cypress/support/page-objects/publicGoodAndSatisfactoryLevelPage.ts index 8b511cb06..bc0385e31 100644 --- a/frontend/packages/vkt/src/tests/cypress/support/page-objects/publicGoodAndSatisfactoryLevelPage.ts +++ b/frontend/packages/vkt/src/tests/cypress/support/page-objects/publicGoodAndSatisfactoryLevelPage.ts @@ -36,6 +36,12 @@ class PublicGoodAndSatisfactoryLevelPage { .should('have.length', count + 1); } + assertExaminerAlreadyContacted(examinerName: string) { + this.elements + .examinerRow(examinerName) + .should('contain.text', 'Yhteydenotto lähetetty'); + } + contactExaminer(examinerName: string) { this.elements .examinerRow(examinerName) diff --git a/frontend/packages/vkt/src/tests/cypress/support/types/index.d.ts b/frontend/packages/vkt/src/tests/cypress/support/types/index.d.ts index ca3093193..afa055406 100644 --- a/frontend/packages/vkt/src/tests/cypress/support/types/index.d.ts +++ b/frontend/packages/vkt/src/tests/cypress/support/types/index.d.ts @@ -12,6 +12,11 @@ declare global { persistedState?: string, ): void; openPublicGoodAndSatisfactoryLevelPage(): void; + openPublicEnrollmentContactPage( + examinerId: number, + step?: string, + persistedState?: string, + ); openClerkExcellentLevelPage(): void; openClerkExamEventPage(examEventId: number): void; openClerkCreateExamEventPage(): void; diff --git a/frontend/packages/vkt/src/tests/cypress/support/utils/dialog.ts b/frontend/packages/vkt/src/tests/cypress/support/utils/dialog.ts new file mode 100644 index 000000000..ed1b78162 --- /dev/null +++ b/frontend/packages/vkt/src/tests/cypress/support/utils/dialog.ts @@ -0,0 +1,4 @@ +export const findDialogByText = (text: string) => + cy.findByRole('dialog').should('contain', text); + +export const expectNoDialog = () => cy.findByRole('dialog').should('not.exist'); \ No newline at end of file diff --git a/frontend/packages/vkt/src/tests/msw/handlers.ts b/frontend/packages/vkt/src/tests/msw/handlers.ts index 59aca630f..e6f2d7104 100644 --- a/frontend/packages/vkt/src/tests/msw/handlers.ts +++ b/frontend/packages/vkt/src/tests/msw/handlers.ts @@ -170,4 +170,13 @@ export const handlers = [ return new Response(null, { status: 404 }); } }), + http.post(`${APIEndpoints.PublicEnrollmentContact}/:id`, ({ params }) => { + const { id } = params; + const examiner = publicExaminers.findLast((v) => v.id === Number(id)); + if (examiner) { + return new Response(null, { status: 201 }); + } else { + return new Response(null, { status: 404 }); + } + }) ];