From d6c9e0f94bdc87beef136f26c54128380938379a Mon Sep 17 00:00:00 2001 From: Sampo Tawast Date: Fri, 6 Oct 2023 14:07:44 +0300 Subject: [PATCH 1/7] fix: get rid of isReadOnly flag the data already exists in prop so no need to make router checks --- .../applicationList/useApplicationList.ts | 4 +-- .../step5/ApplicationFormStep5.tsx | 20 +---------- .../step6/ApplicationFormStep6.tsx | 20 +---------- .../applications/pageContent/PageContent.tsx | 36 +++++++++++-------- .../pageContent/usePageContent.ts | 6 ++-- .../applicant/src/utils/applications.ts | 6 ++++ 6 files changed, 35 insertions(+), 57 deletions(-) create mode 100644 frontend/benefit/applicant/src/utils/applications.ts diff --git a/frontend/benefit/applicant/src/components/applications/applicationList/useApplicationList.ts b/frontend/benefit/applicant/src/components/applications/applicationList/useApplicationList.ts index 163f6e0565..30195c218a 100644 --- a/frontend/benefit/applicant/src/components/applications/applicationList/useApplicationList.ts +++ b/frontend/benefit/applicant/src/components/applications/applicationList/useApplicationList.ts @@ -96,9 +96,7 @@ const useApplicationList = (status: string[]): ApplicationListProps => { return { label: t(`${translationListBase}.common.check`), handleAction: (): void => { - void router.push( - `${ROUTES.APPLICATION_FORM}?id=${id}&isReadOnly=true` - ); + void router.push(`${ROUTES.APPLICATION_FORM}?id=${id}`); }, }; } diff --git a/frontend/benefit/applicant/src/components/applications/forms/application/step5/ApplicationFormStep5.tsx b/frontend/benefit/applicant/src/components/applications/forms/application/step5/ApplicationFormStep5.tsx index 0e3ee38c3e..8672cf6142 100644 --- a/frontend/benefit/applicant/src/components/applications/forms/application/step5/ApplicationFormStep5.tsx +++ b/frontend/benefit/applicant/src/components/applications/forms/application/step5/ApplicationFormStep5.tsx @@ -1,11 +1,9 @@ -import NotificationView from 'benefit/applicant/components/notificationView/NotificationView'; import SummarySection from 'benefit/applicant/components/summarySection/SummarySection'; import { DynamicFormStepComponentProps } from 'benefit/applicant/types/common'; import { ATTACHMENT_TYPES, BENEFIT_TYPES } from 'benefit-shared/constants'; import { Button, IconPen } from 'hds-react'; import isEmpty from 'lodash/isEmpty'; import React from 'react'; -import { getFullName } from 'shared/utils/application.utils'; import { useTheme } from 'styled-components'; import ConsentViewer from '../consentViewer/ConsentViewer'; @@ -17,13 +15,12 @@ import { useApplicationFormStep5 } from './useApplicationFormStep5'; type ExtendedProps = { isReadOnly?: boolean; - isSubmittedApplication?: boolean; onSubmit?: () => void; }; const ApplicationFormStep5: React.FC< DynamicFormStepComponentProps & ExtendedProps -> = ({ data, isReadOnly, isSubmittedApplication, onSubmit }) => { +> = ({ data, isReadOnly, onSubmit }) => { const { t, handleBack, @@ -38,21 +35,6 @@ const ApplicationFormStep5: React.FC< const theme = useTheme(); - if (isSubmittedApplication) { - return ( - - ); - } - return ( <> void; }; const ApplicationFormStep6: React.FC< DynamicFormStepComponentProps & ExtendedProps -> = ({ data, isSubmittedApplication, onSubmit }) => { +> = ({ data, onSubmit }) => { const { t, handleSubmit, @@ -35,21 +32,6 @@ const ApplicationFormStep6: React.FC< applicantTermsInEffectUrl, } = useApplicationFormStep6(data, onSubmit); - if (isSubmittedApplication) { - return ( - - ); - } - // todo: implement resizing for pdf reader (f. ex. react-sizeme), styling as in design return (
diff --git a/frontend/benefit/applicant/src/components/applications/pageContent/PageContent.tsx b/frontend/benefit/applicant/src/components/applications/pageContent/PageContent.tsx index 22656703f6..ce05e0661a 100644 --- a/frontend/benefit/applicant/src/components/applications/pageContent/PageContent.tsx +++ b/frontend/benefit/applicant/src/components/applications/pageContent/PageContent.tsx @@ -17,11 +17,13 @@ import { IconInfoCircleFill, LoadingSpinner, Stepper } from 'hds-react'; import { useRouter } from 'next/router'; import React, { useEffect } from 'react'; import Container from 'shared/components/container/Container'; +import { getFullName } from 'shared/utils/application.utils'; import { convertToUIDateAndTimeFormat } from 'shared/utils/date.utils'; import { useTheme } from 'styled-components'; import ErrorPage from '../../errorPage/ErrorPage'; import { $Notification } from '../../Notification/Notification.sc'; +import NotificationView from '../../notificationView/NotificationView'; import { usePageContent } from './usePageContent'; const stepperCss = { @@ -51,12 +53,11 @@ const PageContent: React.FC = () => { const router = useRouter(); useEffect(() => { - if (router.query.isReadOnly) - document.title = t('common:pageTitles.viewApplication'); + if (isReadOnly) document.title = t('common:pageTitles.viewApplication'); else if (router.query.id) document.title = t('common:pageTitles.editApplication'); else document.title = t('common:pageTitles.createApplication'); - }, [router.query.id, router.query.isReadOnly, t]); + }, [router.query.id, isReadOnly, t]); useEffect(() => { window.scrollTo(0, 0); @@ -80,11 +81,26 @@ const PageContent: React.FC = () => { ); } + if (isSubmittedApplication) { + return ( + + ); + } + // if view mode, show customized summary if ( application.status && SUBMITTED_STATUSES.includes(application.status) && - Boolean(isReadOnly) + isReadOnly ) { return ( @@ -189,18 +205,10 @@ const PageContent: React.FC = () => { {currentStep === 3 && } {currentStep === 4 && } {currentStep === 5 && ( - + )} {currentStep === 6 && ( - + )} ); diff --git a/frontend/benefit/applicant/src/components/applications/pageContent/usePageContent.ts b/frontend/benefit/applicant/src/components/applications/pageContent/usePageContent.ts index eb7b1a9903..50e8a83bd2 100644 --- a/frontend/benefit/applicant/src/components/applications/pageContent/usePageContent.ts +++ b/frontend/benefit/applicant/src/components/applications/pageContent/usePageContent.ts @@ -4,6 +4,7 @@ import { } from 'benefit/applicant/constants'; import useApplicationQuery from 'benefit/applicant/hooks/useApplicationQuery'; import { useTranslation } from 'benefit/applicant/i18n'; +import { isApplicationEditable } from 'benefit/applicant/utils/applications'; import { getApplicationStepFromString } from 'benefit/applicant/utils/common'; import { Application } from 'benefit-shared/types/application'; import camelcaseKeys from 'camelcase-keys'; @@ -18,7 +19,7 @@ type ExtendedComponentProps = { steps: StepperProps['steps']; currentStep: number; application: Application; - isReadOnly: string | string[] | undefined; + isReadOnly: boolean; id: string | string[] | undefined; isError: boolean; isLoading: boolean; @@ -32,7 +33,6 @@ const isApplicationLoaded = (id: number | string, status: string): boolean => const usePageContent = (): ExtendedComponentProps => { const router = useRouter(); const id = router?.query?.id?.toString() ?? ''; - const isReadOnly = router?.query?.isReadOnly?.toString() ?? ''; const { t } = useTranslation(); const [isLoading, setIsLoading] = useState(true); @@ -46,6 +46,8 @@ const usePageContent = (): ExtendedComponentProps => { error: existingApplicationError, } = useApplicationQuery(id); + const isReadOnly = !isApplicationEditable(existingApplication?.status); + useEffect(() => { if (isApplicationLoaded(id, existingApplicationStatus)) { setIsLoading(false); diff --git a/frontend/benefit/applicant/src/utils/applications.ts b/frontend/benefit/applicant/src/utils/applications.ts new file mode 100644 index 0000000000..25c45f3125 --- /dev/null +++ b/frontend/benefit/applicant/src/utils/applications.ts @@ -0,0 +1,6 @@ +import { APPLICATION_STATUSES } from 'benefit-shared/constants'; + +export const isApplicationEditable = (status: APPLICATION_STATUSES): boolean => + [APPLICATION_STATUSES.DRAFT, APPLICATION_STATUSES.INFO_REQUIRED].includes( + status + ); From 866cda564fbb41d7a347e769c96df8884acb340f Mon Sep 17 00:00:00 2001 From: Sampo Tawast Date: Fri, 6 Oct 2023 15:11:15 +0300 Subject: [PATCH 2/7] chore: add more form filling tests --- .../browser-tests/page-model/deminimis.ts | 6 +- .../browser-tests/page-model/step1.ts | 59 ++++++- .../browser-tests/page-model/step2.ts | 20 ++- .../browser-tests/page-model/step3.ts | 20 ++- .../browser-tests/page-model/step4.ts | 26 ++++ .../browser-tests/page-model/step5.ts | 69 +++++++++ .../browser-tests/page-model/step6.ts | 44 ++++++ .../association.testcafe.ts | 4 +- .../browser-tests/pages/company.testcafe.ts | 146 ++++++++++++++++++ .../{testruns => pages}/deminimis.testcafe.ts | 2 +- .../applicant/browser-tests/pages/sample.pdf | Bin 0 -> 3028 bytes .../testruns/company.testcafe.ts | 81 ---------- .../benefit/applicant/browser-tests/types.ts | 29 ++++ .../browser-tests/utils/fieldMaps.ts | 115 ++++++++++++++ frontend/benefit/applicant/package.json | 5 +- .../step4/ApplicationFormStep4.tsx | 2 +- .../AttachmentsListView.tsx | 2 +- .../step5/companyInfoView/CompanyInfoView.tsx | 43 ++++-- .../step5/employeeView/EmployeeView.tsx | 75 +++++---- .../step6/ApplicationFormStep6.tsx | 1 + 20 files changed, 590 insertions(+), 159 deletions(-) create mode 100644 frontend/benefit/applicant/browser-tests/page-model/step4.ts create mode 100644 frontend/benefit/applicant/browser-tests/page-model/step5.ts create mode 100644 frontend/benefit/applicant/browser-tests/page-model/step6.ts rename frontend/benefit/applicant/browser-tests/{testruns => pages}/association.testcafe.ts (95%) create mode 100644 frontend/benefit/applicant/browser-tests/pages/company.testcafe.ts rename frontend/benefit/applicant/browser-tests/{testruns => pages}/deminimis.testcafe.ts (98%) create mode 100644 frontend/benefit/applicant/browser-tests/pages/sample.pdf delete mode 100644 frontend/benefit/applicant/browser-tests/testruns/company.testcafe.ts create mode 100644 frontend/benefit/applicant/browser-tests/types.ts create mode 100644 frontend/benefit/applicant/browser-tests/utils/fieldMaps.ts diff --git a/frontend/benefit/applicant/browser-tests/page-model/deminimis.ts b/frontend/benefit/applicant/browser-tests/page-model/deminimis.ts index b8e1c0d506..2035367400 100644 --- a/frontend/benefit/applicant/browser-tests/page-model/deminimis.ts +++ b/frontend/benefit/applicant/browser-tests/page-model/deminimis.ts @@ -46,7 +46,7 @@ class DeMinimisAid { '050001234', 'tester@example.com' ); - await this.step1.selectNocoOperationNegotiations(); + await this.step1.selectCoOperationNegotiations(false); }; public getRowCount = async (): Promise => @@ -144,7 +144,7 @@ class DeMinimisAid { t: TestController, action: SAVE_ACTIONS ): Promise => { - await this.step1.selectNoDeMinimis(); + await this.step1.selectDeMinimis(false); if (action === SAVE_ACTIONS.CONTINUE) { await this.actions.saveStep1AndReturn(); @@ -153,7 +153,7 @@ class DeMinimisAid { await this.saveExitAndEdit(t); } - await this.step1.selectYesDeMinimis(); + await this.step1.selectDeMinimis(true); await t.scrollIntoView(this.getSelectorContinueButton()); await t.expect(await this.getRowCount()).eql(0); }, diff --git a/frontend/benefit/applicant/browser-tests/page-model/step1.ts b/frontend/benefit/applicant/browser-tests/page-model/step1.ts index 01b4b10c95..ef7b2da2f1 100644 --- a/frontend/benefit/applicant/browser-tests/page-model/step1.ts +++ b/frontend/benefit/applicant/browser-tests/page-model/step1.ts @@ -75,6 +75,18 @@ class Step1 extends WizardStep { .associationHasBusinessActivities.no, }); + private businessActivitiesTrue = this.within( + this.component.getByRole('group', { + name: this.regexp( + this.translations.applications.sections.company.fields + .associationHasBusinessActivities.label + ), + }) + ).findByRole('radio', { + name: this.translations.applications.sections.company.fields + .associationHasBusinessActivities.yes, + }); + private deMinimisAidFalse = this.within( this.component.getByRole('group', { name: this.regexp( @@ -118,6 +130,28 @@ class Step1 extends WizardStep { .coOperationNegotiations.no, }); + private coOperationNegotiationsTrue = this.within( + this.component.getByRole('group', { + name: this.regexp( + this.translations.applications.sections.company.fields + .coOperationNegotiations.label + ), + }) + ).findByRole('radio', { + name: this.translations.applications.sections.company.fields + .coOperationNegotiations.yes, + }); + + private coOperationNegotiationsDescription = this.component.findByRole( + 'textbox', + { + name: this.regexp( + this.translations.applications.sections.company.fields + .coOperationNegotiationsDescription.label + ), + } + ); + public async fillEmployerInfo( iban: string, isAssociation: boolean @@ -152,6 +186,15 @@ class Step1 extends WizardStep { return this.clickDeminimisSave(); } + public async fillCoOperationNegotiationsDescription( + clarification: string + ): Promise { + await this.fillInput( + this.coOperationNegotiationsDescription, + clarification + ); + } + private deminimisSave = this.component.findByRole('button', { name: this.translations.applications.sections.company.deMinimisAidsAdd, }); @@ -167,19 +210,19 @@ class Step1 extends WizardStep { return t.click(this.deminimisRemove(index)); } - public selectNoBusinessActivities(): Promise { - return this.clickSelectRadioButton(this.businessActivitiesFalse); + public selectBusinessActivities(yes: boolean): Promise { + if (yes) return this.clickSelectRadioButton(this.businessActivitiesFalse); + return this.clickSelectRadioButton(this.businessActivitiesTrue); } - public selectNoDeMinimis(): Promise { + public selectDeMinimis(yes: boolean): Promise { + if (yes) return this.clickSelectRadioButton(this.deMinimisAidTrue); return this.clickSelectRadioButton(this.deMinimisAidFalse); } - public selectYesDeMinimis(): Promise { - return this.clickSelectRadioButton(this.deMinimisAidTrue); - } - - public selectNocoOperationNegotiations(): Promise { + public selectCoOperationNegotiations(yes: boolean): Promise { + if (yes) + return this.clickSelectRadioButton(this.coOperationNegotiationsTrue); return this.clickSelectRadioButton(this.coOperationNegotiationsFalse); } } diff --git a/frontend/benefit/applicant/browser-tests/page-model/step2.ts b/frontend/benefit/applicant/browser-tests/page-model/step2.ts index e54ed9179b..0fa59d145b 100644 --- a/frontend/benefit/applicant/browser-tests/page-model/step2.ts +++ b/frontend/benefit/applicant/browser-tests/page-model/step2.ts @@ -55,6 +55,18 @@ class Step2 extends WizardStep { .apprenticeshipProgram.no, }); + private apprenticeshipProgramTrue = this.within( + this.component.getByRole('group', { + name: this.regexp( + this.translations.applications.sections.employee.fields + .apprenticeshipProgram.label + ), + }) + ).findByRole('radio', { + name: this.translations.applications.sections.employee.fields + .apprenticeshipProgram.yes, + }); + private benefitTypeEmployment = this.component.findByRole('radio', { name: this.translations.applications.sections.employee.fields.benefitType .employment, @@ -122,8 +134,14 @@ class Step2 extends WizardStep { await this.clickSelectRadioButton(this.isLivingInHelsinkiCheckbox); } - public async fillPaidSubsidyGrant(): Promise { + public async fillPaidSubsidyGrant( + apprenticeshipProgram: boolean + ): Promise { await this.clickSelectRadioButton(this.paidSubsidyDefault); + if (apprenticeshipProgram) { + await this.clickSelectRadioButton(this.apprenticeshipProgramTrue); + return; + } await this.clickSelectRadioButton(this.apprenticeshipProgramFalse); } diff --git a/frontend/benefit/applicant/browser-tests/page-model/step3.ts b/frontend/benefit/applicant/browser-tests/page-model/step3.ts index dc3f6e8dc4..ea4d1313b1 100644 --- a/frontend/benefit/applicant/browser-tests/page-model/step3.ts +++ b/frontend/benefit/applicant/browser-tests/page-model/step3.ts @@ -10,29 +10,41 @@ class Step3 extends WizardStep { private employmentContract = this.component.findByTestId( 'employment_contract' ); + private paySubsidyDecision = this.component.findByTestId( 'pay_subsidy_decision' ); + private helsinkiBenefitVoucher = this.component.findByTestId( 'helsinki_benefit_voucher' ); + private educationContract = this.component.findByTestId('education_contract'); - async employmentContractNeeded() { + async employmentContractNeeded(): Promise { await t.expect(this.employmentContract.exists).ok(); } - async paySubsidyDecisionNeeded() { + async paySubsidyDecisionNeeded(): Promise { await t.expect(this.paySubsidyDecision.exists).ok(); } - async helsinkiBenefitVoucherNeeded() { + async helsinkiBenefitVoucherNeeded(): Promise { await t.expect(this.helsinkiBenefitVoucher.exists).ok(); } - async educationContractNeeded() { + async educationContractNeeded(): Promise { await t.expect(this.educationContract.exists).ok(); } + + async stageUploadFiles(uploadIds: string[]): Promise { + // eslint-disable-next-line no-restricted-syntax + for (const id of uploadIds) { + // eslint-disable-next-line no-await-in-loop + await t.setFilesToUpload(id, 'sample.pdf'); + } + await t.wait(500); + } } export default Step3; diff --git a/frontend/benefit/applicant/browser-tests/page-model/step4.ts b/frontend/benefit/applicant/browser-tests/page-model/step4.ts new file mode 100644 index 0000000000..89160d6d57 --- /dev/null +++ b/frontend/benefit/applicant/browser-tests/page-model/step4.ts @@ -0,0 +1,26 @@ +import { t } from 'testcafe'; + +import WizardStep from './WizardStep'; + +class Step4 extends WizardStep { + constructor() { + super(4); + } + + private employeeConsent = this.component.findByTestId('employee_consent'); + + async employeeConsentNeeded(): Promise { + await t.expect(this.employeeConsent.exists).ok(); + } + + // eslint-disable-next-line class-methods-use-this + async stageUploadFiles(): Promise { + await t.setFilesToUpload( + '#upload_attachment_employee_consent', + 'sample.pdf' + ); + await t.wait(500); + } +} + +export default Step4; diff --git a/frontend/benefit/applicant/browser-tests/page-model/step5.ts b/frontend/benefit/applicant/browser-tests/page-model/step5.ts new file mode 100644 index 0000000000..682339e7e0 --- /dev/null +++ b/frontend/benefit/applicant/browser-tests/page-model/step5.ts @@ -0,0 +1,69 @@ +import { Selector, t } from 'testcafe'; + +import { ApplicationFormData } from '../types'; +import { + ApplicationField, + mapFullForm as mapFieldsExtra, + mapRequiredForm as mapFieldsMandatory, +} from '../utils/fieldMaps'; +import WizardStep from './WizardStep'; + +class Step5 extends WizardStep { + private fieldsRequired: ApplicationField[]; + + private fieldsExtra: ApplicationField[]; + + private Selector = Selector; + + constructor(form: ApplicationFormData) { + super(5); + + this.fieldsRequired = mapFieldsMandatory(form); + this.fieldsExtra = mapFieldsExtra(form); + } + + private associationFieldNames = [ + 'application-field-associationImmediateManagerCheck', + ]; + + private fieldIsVisible = async (testId: string): Promise => + this.component.findByTestId(testId).visible; + + private fieldValueIsVisible = async ( + testId: string, + value: string + ): Promise => + this.Selector(`[data-testid="${testId}"]`).withText(value).visible; + + async fieldsExistFor( + organizationType: 'company' | 'association' + ): Promise { + const fullForm = [...this.fieldsRequired, ...this.fieldsExtra]; + // eslint-disable-next-line no-restricted-syntax + for (const field of fullForm) { + // eslint-disable-next-line no-await-in-loop + await t.scrollIntoView(`[data-testid="${field.testId}"]`); + // eslint-disable-next-line no-await-in-loop + await t.expect(await this.fieldIsVisible(field.testId)).ok(); + if (field.value) { + // eslint-disable-next-line no-await-in-loop + const fieldIsVisible = await this.fieldValueIsVisible( + field.testId, + field.value + ); + // eslint-disable-next-line no-await-in-loop + await t.expect(fieldIsVisible).ok(); + } + } + + if (organizationType === 'association') { + // eslint-disable-next-line no-restricted-syntax + for (const testId of this.associationFieldNames) { + // eslint-disable-next-line no-await-in-loop + await t.expect(await this.fieldIsVisible(testId)).ok(); + } + } + } +} + +export default Step5; diff --git a/frontend/benefit/applicant/browser-tests/page-model/step6.ts b/frontend/benefit/applicant/browser-tests/page-model/step6.ts new file mode 100644 index 0000000000..f6612760f3 --- /dev/null +++ b/frontend/benefit/applicant/browser-tests/page-model/step6.ts @@ -0,0 +1,44 @@ +import { Selector, t } from 'testcafe'; + +import WizardStep from './WizardStep'; + +class Step6 extends WizardStep { + constructor() { + super(6); + } + + protected nextButton = this.component.findByRole('button', { + name: this.translations.applications.actions.send, + }); + + public submitSuccessLabel = this.component.findByText( + this.translations.notifications.applicationSubmitted.label, + { selector: 'h1' } + ); + + public async isShowingSubmitSuccess(): Promise { + await t + .expect( + await Selector('h1').withText( + this.translations.notifications.applicationSubmitted.label + ).visible + ) + .ok(); + } + + public applicantTerms = this.component.findByTestId(''); + + /** + * Click through all applicant terms. + * Assume terms are loaded from fixture default_terms.json using LOAD_DEFAULT_TERMS=1 + */ + public async checkApplicantTerms(): Promise { + const consentSelector = '[data-testid="application-terms-consent"]'; + await t.click(Selector(consentSelector).nth(0)); + await t.click(Selector(consentSelector).nth(1)); + await t.click(Selector(consentSelector).nth(2)); + await t.click(Selector(consentSelector).nth(3)); + } +} + +export default Step6; diff --git a/frontend/benefit/applicant/browser-tests/testruns/association.testcafe.ts b/frontend/benefit/applicant/browser-tests/pages/association.testcafe.ts similarity index 95% rename from frontend/benefit/applicant/browser-tests/testruns/association.testcafe.ts rename to frontend/benefit/applicant/browser-tests/pages/association.testcafe.ts index 205b3efdc5..2e7ed75da2 100644 --- a/frontend/benefit/applicant/browser-tests/testruns/association.testcafe.ts +++ b/frontend/benefit/applicant/browser-tests/pages/association.testcafe.ts @@ -44,14 +44,14 @@ test.skip('New application', async () => { 'need.email@example.com' ); - await step1.selectNocoOperationNegotiations(); + await step1.selectCoOperationNegotiations(false); await step1.clickSubmit(); const step2 = new Step2(); await step2.isLoaded(); await step2.fillEmployeeInfo('Truu', 'Koos', '121148-8060'); - await step2.fillPaidSubsidyGrant(); + await step2.fillPaidSubsidyGrant(false); const currentYear: number = new Date().getFullYear(); await step2.fillBenefitPeriod( `1.3.${currentYear}`, diff --git a/frontend/benefit/applicant/browser-tests/pages/company.testcafe.ts b/frontend/benefit/applicant/browser-tests/pages/company.testcafe.ts new file mode 100644 index 0000000000..745b293154 --- /dev/null +++ b/frontend/benefit/applicant/browser-tests/pages/company.testcafe.ts @@ -0,0 +1,146 @@ +import { HttpRequestHook } from '@frontend/shared/browser-tests/http-utils/http-request-hook'; +import requestLogger from '@frontend/shared/browser-tests/utils/request-logger'; +import { clearDataToPrintOnFailure } from '@frontend/shared/browser-tests/utils/testcafe.utils'; +import { t } from 'testcafe'; + +import DeMinimisAid from '../page-model/deminimis'; +import Login from '../page-model/login'; +import MainIngress from '../page-model/MainIngress'; +import Step1 from '../page-model/step1'; +import Step2 from '../page-model/step2'; +import Step3 from '../page-model/step3'; +import Step4 from '../page-model/step4'; +import Step5 from '../page-model/step5'; +import Step6 from '../page-model/step6'; +import TermsOfService from '../page-model/TermsOfService'; +import { ApplicationFormData } from '../types'; +import { getBackendDomain, getFrontendUrl } from '../utils/url.utils'; + +const url = getFrontendUrl('/'); + +fixture('Company') + .page(url) + .requestHooks(requestLogger, new HttpRequestHook(url, getBackendDomain())) + .beforeEach(async (testController) => { + clearDataToPrintOnFailure(testController); + }); + +const currentYear = new Date().getFullYear(); +const form: ApplicationFormData = { + organization: { + iban: '6051437344779954', + firstName: 'Raven', + lastName: 'Stamm', + phone: '050001234', + email: 'Raven_Stamm@example.net', + coOperationNegotiationsDescription: 'Lorem ipsum dolor sit amet', + }, + employee: { + firstName: 'Larry', + lastName: 'Blick', + ssn: '010101-150J', + title: 'Kuljettaja', + workHours: '30', + collectiveBargainingAgreement: 'Logistiikka TES', + monthlyPay: '2300', + otherExpenses: '300', + vacationMoney: '500', + startDate: `1.1.${currentYear}`, + endDate: `1.2.${currentYear}`, + }, + deMinimisAid: { + granter: 'One', + amount: '123', + grantedAt: '1.1.2023', + }, +}; + +test('New application', async () => { + await t.click(Login.loginButton); + + const termsAndConditions = new TermsOfService(); + await termsAndConditions.isLoaded(); + await termsAndConditions.clickContinueButton(); + + const mainIngress = new MainIngress(); + await mainIngress.isLoaded(); + await mainIngress.clickCreateNewApplicationButton(); + + const step1 = new Step1(); + await step1.isLoaded(60_000); + const step2 = new Step2(); + + await step1.fillEmployerInfo(form.organization.iban, false); + await step1.fillContactPerson( + form.organization.firstName, + form.organization.lastName, + form.organization.phone, + form.organization.email + ); + + await step1.selectDeMinimis(true); + const deminimisAid = new DeMinimisAid(t, step1, step2); + await deminimisAid.actions.fillRows(t, [ + { + granter: form.deMinimisAid.granter, + amount: form.deMinimisAid.amount, + grantedAt: form.deMinimisAid.grantedAt, + }, + ]); + + await step1.selectCoOperationNegotiations(true); + await step1.fillCoOperationNegotiationsDescription( + form.organization.coOperationNegotiationsDescription + ); + + await step1.clickSubmit(); + + await step2.isLoaded(); + await step2.fillEmployeeInfo( + form.employee.firstName, + form.employee.lastName, + form.employee.ssn + ); + await step2.fillEmploymentInfo( + form.employee.title, + form.employee.workHours, + form.employee.collectiveBargainingAgreement, + form.employee.monthlyPay, + form.employee.otherExpenses, + form.employee.vacationMoney + ); + await step2.fillPaidSubsidyGrant(true); + + await step2.fillBenefitPeriod(form.employee.startDate, form.employee.endDate); + + await step2.clickSubmit(); + + const step3 = new Step3(); + await step3.isLoaded(); + await step3.employmentContractNeeded(); + await step3.paySubsidyDecisionNeeded(); + await step3.helsinkiBenefitVoucherNeeded(); + await step3.stageUploadFiles([ + '#upload_attachment_employment_contract', + '#upload_attachment_pay_subsidy_decision', + '#upload_attachment_education_contract', + '#upload_attachment_helsinki_benefit_voucher', + ]); + await step3.clickSubmit(); + + const step4 = new Step4(); + await step4.isLoaded(); + await step4.employeeConsentNeeded(); + await step4.stageUploadFiles(); + await step4.clickSubmit(); + + const step5 = new Step5(form); + await step5.isLoaded(); + await step5.fieldsExistFor('company'); + await step5.clickSubmit(); + + const step6 = new Step6(); + await step6.checkApplicantTerms(); + await step6.clickSubmit(); + await step6.isShowingSubmitSuccess(); +}); diff --git a/frontend/benefit/applicant/browser-tests/testruns/deminimis.testcafe.ts b/frontend/benefit/applicant/browser-tests/pages/deminimis.testcafe.ts similarity index 98% rename from frontend/benefit/applicant/browser-tests/testruns/deminimis.testcafe.ts rename to frontend/benefit/applicant/browser-tests/pages/deminimis.testcafe.ts index e1f3ccbde9..2f1a4ef65e 100644 --- a/frontend/benefit/applicant/browser-tests/testruns/deminimis.testcafe.ts +++ b/frontend/benefit/applicant/browser-tests/pages/deminimis.testcafe.ts @@ -21,7 +21,7 @@ fixture('De minimis') await t.useRole(applicantRole); await deMinimis.fillMandatoryFields(); - await deMinimis.step1.selectYesDeMinimis(); + await deMinimis.step1.selectDeMinimis(true); }); test('Aid form (errors)', async (t: TestController) => { diff --git a/frontend/benefit/applicant/browser-tests/pages/sample.pdf b/frontend/benefit/applicant/browser-tests/pages/sample.pdf new file mode 100644 index 0000000000000000000000000000000000000000..dbf091df9a607221e000593a8b5a97b5ea5fb073 GIT binary patch literal 3028 zcmd5;+iK%D7``|79sZj_3mw^3d>n>>rft|$r=<Gl zWIx$CH11?D%do5oLHZ^AN9p^&qnnG-8;=ca>*%k)|M=6kY|A5;iigj(_3oW*IpgQ0 zB;?N?G}O?F~e-o&fdSbEveGxxNVs&T}_+wIC);wN|S3_`=^Ym z?y1Je_6W!5=Pa%0o_u4M!sh=IbybO>E+wq5{dR6;Rn+AKku*_{3aqswkCH}v ztJ}FLi^-kT6dU1Mb|uqH42vhacOeZu&Rl#HCGFr-xBWM-|+e^~95C3kuyCa8Nz&=d3 zPw9GoO7mhx4-J@*eqI7o0NLmbm9D2#M#EZ@Dl~~|vk9Y>(382@*~jiaPA^4vloGW^${BO z&|L0&$FyZ34q1)S0TXjiAeyzye;HJqPhX+oj`M@hIuz@m%ZWTgO?gR!qsqvQPqV zVBwTl{djT$Lm)?KJ&`!^p- zB?yU29^x`|s{JSof_pN9Oqel#YyZFw~B0kRS*9GB0?&&kJAg z<34|7m-_(#cV8b5!0fg%+X9aQc`Db0`!4$;o1mTB0`JJMabTnKqnZ}rgzd~^$`C_Q S>NZV0@_bPEqs!}&ZT$n`rwj1_ literal 0 HcmV?d00001 diff --git a/frontend/benefit/applicant/browser-tests/testruns/company.testcafe.ts b/frontend/benefit/applicant/browser-tests/testruns/company.testcafe.ts deleted file mode 100644 index 0e565ba531..0000000000 --- a/frontend/benefit/applicant/browser-tests/testruns/company.testcafe.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { HttpRequestHook } from '@frontend/shared/browser-tests/http-utils/http-request-hook'; -import requestLogger, { - filterLoggedRequests, -} from '@frontend/shared/browser-tests/utils/request-logger'; -import { clearDataToPrintOnFailure } from '@frontend/shared/browser-tests/utils/testcafe.utils'; -import { t } from 'testcafe'; - -import Login from '../page-model/login'; -import MainIngress from '../page-model/MainIngress'; -import Step1 from '../page-model/step1'; -import Step2 from '../page-model/step2'; -import Step3 from '../page-model/step3'; -import TermsOfService from '../page-model/TermsOfService'; -import { getBackendDomain, getFrontendUrl } from '../utils/url.utils'; - -const url = getFrontendUrl('/'); - -fixture('Company') - .page(url) - .requestHooks(requestLogger, new HttpRequestHook(url, getBackendDomain())) - .beforeEach(async (testController) => { - clearDataToPrintOnFailure(testController); - }) - .afterEach(async () => - // eslint-disable-next-line no-console - console.log(filterLoggedRequests(requestLogger)) - ); - -test('New application', async () => { - await t.click(Login.loginButton); - - const termsAndConditions = new TermsOfService(); - await termsAndConditions.isLoaded(); - await termsAndConditions.clickContinueButton(); - - const mainIngress = new MainIngress(); - await mainIngress.isLoaded(); - await mainIngress.clickCreateNewApplicationButton(); - - const step1 = new Step1(); - await step1.isLoaded(60_000); - - await step1.fillEmployerInfo('6051437344779954', false); - await step1.fillContactPerson( - 'Raven', - 'Stamm', - '050001234', - 'Raven_Stamm@example.net' - ); - await step1.selectNoDeMinimis(); - await step1.selectNocoOperationNegotiations(); - await step1.clickSubmit(); - - const step2 = new Step2(); - await step2.isLoaded(); - - await step2.fillEmployeeInfo('Larry', 'Blick', '010101-150J'); - await step2.fillEmploymentInfo( - 'Kuljettaja', - '30', - 'Logistiikka TES', - '2300', - '300', - '500' - ); - await step2.fillPaidSubsidyGrant(); - - const currentYear: number = new Date().getFullYear(); - await step2.fillBenefitPeriod(`1.3.${currentYear}`, `1.4.${currentYear}`); - - await step2.clickSubmit(); - - const step3 = new Step3(); - await step3.isLoaded(); - await step3.employmentContractNeeded(); - await step3.paySubsidyDecisionNeeded(); - await step3.helsinkiBenefitVoucherNeeded(); - - await step3.clickDeleteApplication(); - await step3.confirmDeleteApplication(); -}); diff --git a/frontend/benefit/applicant/browser-tests/types.ts b/frontend/benefit/applicant/browser-tests/types.ts new file mode 100644 index 0000000000..d28b6ee221 --- /dev/null +++ b/frontend/benefit/applicant/browser-tests/types.ts @@ -0,0 +1,29 @@ +export type ApplicationFormData = { + organization: { + iban: string; + firstName: string; + lastName: string; + phone: string; + email: string; + coOperationNegotiationsDescription: string; + }; + employee: { + firstName: string; + lastName: string; + ssn: string; + title: string; + workHours: string; + collectiveBargainingAgreement: string; + monthlyPay: string; + otherExpenses: string; + vacationMoney: string; + startDate: string; + endDate: string; + }; + + deMinimisAid: { + granter: string; + amount: string; + grantedAt: string; + }; +}; diff --git a/frontend/benefit/applicant/browser-tests/utils/fieldMaps.ts b/frontend/benefit/applicant/browser-tests/utils/fieldMaps.ts new file mode 100644 index 0000000000..3e3fd6cf62 --- /dev/null +++ b/frontend/benefit/applicant/browser-tests/utils/fieldMaps.ts @@ -0,0 +1,115 @@ +import { friendlyFormatIBAN } from 'ibantools'; + +import { ApplicationFormData } from '../types'; + +export type ApplicationField = { + testId: string; + value?: string | null; +}; + +const formatFloatToCurrency = ( + value: string | number, + currency: 'EUR' | null, + locale = 'fi-FI' +): string => { + const parsedValue = typeof value === 'string' ? parseFloat(value) : value; + const currencyOptions = currency + ? { + style: 'currency', + currency, + } + : {}; + + return parsedValue.toLocaleString(locale, { + minimumFractionDigits: 2, + ...currencyOptions, + }); +}; + +export const mapRequiredForm = ( + form: ApplicationFormData +): ApplicationField[] => [ + { testId: 'application-field-companyName', value: 'Demo I. Haanpää Oy' }, + { testId: 'application-field-companyBusinessId', value: '0877830-0' }, + { testId: 'application-field-companyAddress', value: 'Vasaratie 4 A 3' }, + { + testId: 'application-field-companyBankAccountNumber', + value: friendlyFormatIBAN(`FI${form.organization.iban}`), + }, + { + testId: 'application-field-companyContactPersonFirstName', + value: form.organization.firstName, + }, + { + testId: 'application-field-companyContactPersonLastName', + value: form.organization.lastName, + }, + { + testId: 'application-field-companyContactPersonPhoneNumber', + value: form.organization.phone, + }, + { + testId: 'application-field-companyContactPersonEmail', + value: form.organization.email, + }, + { testId: 'application-field-applicantLanguage', value: 'Suomi' }, + { + testId: 'application-field-coOperationNegotiations', + value: null, + }, + { testId: 'application-field-firstName', value: form.employee.firstName }, + { testId: 'application-field-lastName', value: form.employee.lastName }, + { + testId: 'application-field-socialSecurityNumber', + value: form.employee.ssn, + }, + { testId: 'application-field-isLivingInHelsinki', value: 'Kyllä' }, + { testId: 'application-field-jobTitle', value: form.employee.title }, + { + testId: 'application-field-collectiveBargainingAgreement', + value: form.employee.collectiveBargainingAgreement, + }, + { + testId: 'application-field-workingHours', + value: form.employee.workHours, + }, + { + testId: 'application-field-monthlyPay', + value: formatFloatToCurrency(form.employee.monthlyPay, 'EUR'), + }, + { + testId: 'application-field-vacationMoney', + value: formatFloatToCurrency(form.employee.vacationMoney, 'EUR'), + }, + { + testId: 'application-field-otherExpenses', + value: formatFloatToCurrency(form.employee.otherExpenses, 'EUR'), + }, + { testId: 'application-field-paySubsidyGranted', value: 'Palkkatuki' }, + { testId: 'application-field-apprenticeshipProgram', value: null }, + { testId: 'application-field-startDate', value: form.employee.startDate }, + { testId: 'application-field-endDate', value: form.employee.endDate }, + { testId: 'attachment-employment_contract', value: null }, + { testId: 'attachment-employee_consent', value: null }, +]; + +export const mapNoDeMinimisAid = [ + { + name: 'application-field-deMinimisAidsNo', + value: null, + }, +]; + +// TODO: testId: 'application-field-alternativeCompanyStreetAddress' +export const mapFullForm = (form: ApplicationFormData): ApplicationField[] => [ + { + testId: 'application-field-deMinimisAidsAmount', + value: form.deMinimisAid.amount, + }, + { + testId: 'application-field-coOperationNegotiationsDescription', + value: form.organization.coOperationNegotiationsDescription, + }, + { testId: 'attachment-education_contract', value: null }, + { testId: 'attachment-helsinki_benefit_voucher', value: null }, +]; diff --git a/frontend/benefit/applicant/package.json b/frontend/benefit/applicant/package.json index 50623cd1d3..07f64335ef 100644 --- a/frontend/benefit/applicant/package.json +++ b/frontend/benefit/applicant/package.json @@ -11,8 +11,9 @@ "test": "jest --passWithNoTests", "test:staged": "yarn test --watchAll=false --findRelatedTests", "test:coverage": "yarn test --verbose --coverage", - "browser-test": "testcafe 'chrome --allow-insecure-localhost --ignore-certificate-errors --ignore-urlfetcher-cert-requests --window-size=\"1249,1249\"' browser-tests/testruns/deminimis.testcafe.ts", - "browser-test:ci": "testcafe 'chrome:headless --disable-gpu --window-size=\"1249,720\" --ignore-certificate-errors-spki-list=\"8sg/cl7YabrOFqSqH+Bu0e+P27Av33gWgi8Lq28DW1I=,gJt+wt/T3afCRkxtMMSjXcl/99sgzWc2kk1c1PC9tG0=,zrQI2/1q8i2SRPmMZ1sMntIkG+lMW0legPFokDo3nrY=\"' --screenshots path=report --video report --reporter spec,custom,html:report/index.html browser-tests/testruns/*.ts --experimental-proxyless" + "browser-test": "testcafe 'chrome --allow-insecure-localhost --ignore-certificate-errors --ignore-urlfetcher-cert-requests --window-size=\"1249,720\"' browser-tests/pages/*.testcafe.ts", + "browser-test:headless": "testcafe 'chrome:headless --allow-insecure-localhost --ignore-certificate-errors --ignore-urlfetcher-cert-requests --window-size=\"1249,720\"' browser-tests/pages/*.testcafe.ts", + "browser-test:ci": "testcafe 'chrome:headless --disable-gpu --window-size=\"1249,720\" --ignore-certificate-errors-spki-list=\"8sg/cl7YabrOFqSqH+Bu0e+P27Av33gWgi8Lq28DW1I=,gJt+wt/T3afCRkxtMMSjXcl/99sgzWc2kk1c1PC9tG0=,zrQI2/1q8i2SRPmMZ1sMntIkG+lMW0legPFokDo3nrY=\"' --screenshots path=report --video report --reporter spec,custom,html:report/index.html browser-tests/pages/*.testcafe.ts" }, "dependencies": { "@frontend/shared": "*", diff --git a/frontend/benefit/applicant/src/components/applications/forms/application/step4/ApplicationFormStep4.tsx b/frontend/benefit/applicant/src/components/applications/forms/application/step4/ApplicationFormStep4.tsx index 12580d7bfb..80f6c384a3 100644 --- a/frontend/benefit/applicant/src/components/applications/forms/application/step4/ApplicationFormStep4.tsx +++ b/frontend/benefit/applicant/src/components/applications/forms/application/step4/ApplicationFormStep4.tsx @@ -123,7 +123,7 @@ const ApplicationFormStep4: React.FC = ({ {t(`${translationsBase}.uploadPowerOfAttorney.action1`)} - <$GridCell $colSpan={8}> + <$GridCell $colSpan={8} data-testid="employee_consent"> = ({ ); return attachmentItems.length > 0 ? ( - <$GridCell $colStart={1} $colSpan={6}> + <$GridCell $colStart={1} $colSpan={6} data-testid={`attachment-${type}`}> {title && <$ViewFieldBold>{title}} {attachmentItems.map((attachment) => ( <$ViewField diff --git a/frontend/benefit/applicant/src/components/applications/forms/application/step5/companyInfoView/CompanyInfoView.tsx b/frontend/benefit/applicant/src/components/applications/forms/application/step5/companyInfoView/CompanyInfoView.tsx index d6a443ccb7..11cdf9fd14 100644 --- a/frontend/benefit/applicant/src/components/applications/forms/application/step5/companyInfoView/CompanyInfoView.tsx +++ b/frontend/benefit/applicant/src/components/applications/forms/application/step5/companyInfoView/CompanyInfoView.tsx @@ -56,7 +56,7 @@ const CompanyInfoView: React.FC = ({ > <$GridCell $colSpan={5}> <$ApplicationDetailWrapper $fontSize={theme.fontSize.body.m}> - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-companyName"> <$ApplicationDetailLabel> {t(`${translationsBase}.company.fields.companyName`)} @@ -65,7 +65,7 @@ const CompanyInfoView: React.FC = ({ - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-companyBusinessId"> <$ApplicationDetailLabel> {t(`${translationsBase}.company.fields.companyBusinessId`)} @@ -74,7 +74,7 @@ const CompanyInfoView: React.FC = ({ - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-companyAddress"> <$ApplicationDetailLabel> {t(`${translationsBase}.company.fields.companyAddress`)} @@ -86,7 +86,7 @@ const CompanyInfoView: React.FC = ({ {data.alternativeCompanyStreetAddress && ( - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-alternativeCompanyStreetAddress"> <$ApplicationDetailLabel> {t( `${translationsBase}.company.fields.alternativeCompanyStreetAddress.view` @@ -104,7 +104,7 @@ const CompanyInfoView: React.FC = ({ )} - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-companyBankAccountNumber"> <$ApplicationDetailLabel> {t( `${translationsBase}.company.fields.companyBankAccountNumber.label` @@ -116,7 +116,10 @@ const CompanyInfoView: React.FC = ({ {data?.organizationType === ORGANIZATION_TYPES.ASSOCIATION && ( - <$ApplicationDetailRow $forceColumn> + <$ApplicationDetailRow + $forceColumn + data-testid="application-field-associationHasBusinessActivities" + > <$ApplicationDetailLabel> {t( `${translationsBase}.company.fields.associationHasBusinessActivities.label` @@ -139,7 +142,7 @@ const CompanyInfoView: React.FC = ({ > <$GridCell $colSpan={3}> <$ApplicationDetailWrapper $fontSize={theme.fontSize.body.m}> - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-companyContactPersonFirstName"> <$ApplicationDetailLabel> {t( `${translationsBase}.company.fields.companyContactPersonFirstName.label` @@ -149,7 +152,7 @@ const CompanyInfoView: React.FC = ({ {data.companyContactPersonFirstName} - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-companyContactPersonLastName"> <$ApplicationDetailLabel> {t( `${translationsBase}.company.fields.companyContactPersonLastName.label` @@ -159,7 +162,7 @@ const CompanyInfoView: React.FC = ({ {data.companyContactPersonLastName} - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-companyContactPersonPhoneNumber"> <$ApplicationDetailLabel> {t( `${translationsBase}.company.fields.companyContactPersonPhoneNumber.label` @@ -169,7 +172,7 @@ const CompanyInfoView: React.FC = ({ {data.companyContactPersonPhoneNumber} - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-companyContactPersonEmail"> <$ApplicationDetailLabel> {t( `${translationsBase}.company.fields.companyContactPersonEmail.placeholder` @@ -179,7 +182,7 @@ const CompanyInfoView: React.FC = ({ {data.companyContactPersonEmail} - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-applicantLanguage"> <$ApplicationDetailLabel> {t( `${translationsBase}.company.fields.applicantLanguage.label` @@ -230,7 +233,7 @@ const CompanyInfoView: React.FC = ({ <$SummaryTableValue>{aid.granter} <$GridCell $colSpan={2}> - <$SummaryTableValue> + <$SummaryTableValue data-testid="application-field-deMinimisAidsAmount"> {formatFloatToCurrency(aid.amount, 'EUR')} @@ -243,7 +246,10 @@ const CompanyInfoView: React.FC = ({ ))} ) : ( - <$GridCell $colSpan={12}> + <$GridCell + $colSpan={12} + data-testid="application-field-deMinimisAidsNo" + > <$ViewField> {t(`${translationsBase}.company.deMinimisAidsNo`)} @@ -251,7 +257,11 @@ const CompanyInfoView: React.FC = ({ )} - <$GridCell $colStart={1} $colSpan={12}> + <$GridCell + $colStart={1} + $colSpan={12} + data-testid="application-field-coOperationNegotiations" + > {t( `${translationsBase}.company.fields.coOperationNegotiations.view.${ data.coOperationNegotiations ? 'yes' : 'no' @@ -261,7 +271,10 @@ const CompanyInfoView: React.FC = ({ {data.coOperationNegotiations && ( <$GridCell $colSpan={12}> <$ApplicationDetailWrapper> - <$ApplicationDetailRow $forceColumn> + <$ApplicationDetailRow + $forceColumn + data-testid="application-field-coOperationNegotiationsDescription" + > <$ApplicationDetailLabel> {t( `${translationsBase}.company.fields.coOperationNegotiationsDescription.labelShort` diff --git a/frontend/benefit/applicant/src/components/applications/forms/application/step5/employeeView/EmployeeView.tsx b/frontend/benefit/applicant/src/components/applications/forms/application/step5/employeeView/EmployeeView.tsx index 90732550c3..94f8d490ac 100644 --- a/frontend/benefit/applicant/src/components/applications/forms/application/step5/employeeView/EmployeeView.tsx +++ b/frontend/benefit/applicant/src/components/applications/forms/application/step5/employeeView/EmployeeView.tsx @@ -70,7 +70,7 @@ const EmployeeView: React.FC = ({ > <$GridCell $colSpan={12}> <$ApplicationDetailWrapper $fontSize={theme.fontSize.body.m}> - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-firstName"> <$ApplicationDetailLabel> {t(`${translationsBase}.employee.fields.firstName.label`)} @@ -78,7 +78,7 @@ const EmployeeView: React.FC = ({ {data.employee?.firstName || ''} - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-lastName"> <$ApplicationDetailLabel> {t(`${translationsBase}.employee.fields.lastName.label`)} @@ -86,7 +86,7 @@ const EmployeeView: React.FC = ({ {data.employee?.lastName || ''} - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-socialSecurityNumber"> <$ApplicationDetailLabel> {t( `${translationsBase}.employee.fields.socialSecurityNumber.label` @@ -96,7 +96,11 @@ const EmployeeView: React.FC = ({ {data.employee?.socialSecurityNumber} - <$ApplicationDetailRow $alignItems="flex-start" $forceColumn> + <$ApplicationDetailRow + $alignItems="flex-start" + $forceColumn + data-testid="application-field-isLivingInHelsinki" + > <$ApplicationDetailLabel> {t( `${translationsBase}.employee.fields.isLivingInHelsinki.label` @@ -110,16 +114,22 @@ const EmployeeView: React.FC = ({ )} - <$ApplicationDetailRow $alignItems="flex-start" $forceColumn> - <$ApplicationDetailLabel> - {t( - `${translationsBase}.employee.fields.associationImmediateManagerCheck.label` - )} - - <$ApplicationDetailValue> - {t('common:utility.yes')} - - + {data.associationImmediateManagerCheck && ( + <$ApplicationDetailRow + $alignItems="flex-start" + $forceColumn + data-testid="application-field-associationImmediateManagerCheck" + > + <$ApplicationDetailLabel> + {t( + `${translationsBase}.employee.fields.associationImmediateManagerCheck.label` + )} + + <$ApplicationDetailValue> + {t('common:utility.yes')} + + + )} @@ -132,7 +142,7 @@ const EmployeeView: React.FC = ({ > <$GridCell $colSpan={5}> <$ApplicationDetailWrapper $fontSize={theme.fontSize.body.m}> - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-jobTitle"> <$ApplicationDetailLabel> {t(`${translationsBase}.employee.fields.jobTitle.label`)} @@ -141,7 +151,7 @@ const EmployeeView: React.FC = ({ - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-collectiveBargainingAgreement"> <$ApplicationDetailLabel> {t( `${translationsBase}.employee.fields.collectiveBargainingAgreement.placeholder` @@ -151,7 +161,7 @@ const EmployeeView: React.FC = ({ {data.employee?.collectiveBargainingAgreement} - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-workingHours"> <$ApplicationDetailLabel> {t(`${translationsBase}.employee.fields.workingHours.label`)} @@ -163,7 +173,7 @@ const EmployeeView: React.FC = ({ - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-monthlyPay"> <$ApplicationDetailLabel> {t(`${translationsBase}.employee.fields.monthlyPay.label`)} @@ -172,7 +182,7 @@ const EmployeeView: React.FC = ({ - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-vacationMoney"> <$ApplicationDetailLabel> {t(`${translationsBase}.employee.fields.vacationMoney.label`)} @@ -180,7 +190,7 @@ const EmployeeView: React.FC = ({ {formatFloatToCurrency(data.employee?.vacationMoney, 'EUR')} - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-otherExpenses"> <$ApplicationDetailLabel> {t(`${translationsBase}.employee.fields.otherExpenses.label`)} @@ -189,9 +199,9 @@ const EmployeeView: React.FC = ({ {formatFloatToCurrency(data.employee?.otherExpenses, 'EUR')} - {data.paySubsidyGranted ? ( + {data.paySubsidyGranted && ( <$ApplicationDetailWrapper $fontSize={theme.fontSize.body.m}> - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-paySubsidyGranted"> <$ApplicationDetailLabel> {t( `${translationsBase}.employee.fields.paySubsidyGranted.labelShort` @@ -205,7 +215,7 @@ const EmployeeView: React.FC = ({ )} - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-apprenticeshipProgram"> <$ApplicationDetailLabel> {t( `${translationsBase}.employee.fields.apprenticeshipProgram.label` @@ -220,21 +230,6 @@ const EmployeeView: React.FC = ({ - ) : ( - <$ApplicationDetailWrapper $fontSize={theme.fontSize.body.m}> - <$ApplicationDetailRow> - <$ApplicationDetailLabel> - {t( - `${translationsBase}.employee.fields.paySubsidyGranted.label` - )}{' '} - - <$ApplicationDetailValue> - {t( - `${translationsBase}.employee.fields.paySubsidyGranted.no` - )} - - - )} @@ -242,7 +237,7 @@ const EmployeeView: React.FC = ({ )} <$ApplicationDetailWrapper> - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-startDate"> <$ApplicationDetailLabel> {t(`${translationsBase}.employee.fields.startDate.label`)} @@ -250,7 +245,7 @@ const EmployeeView: React.FC = ({ {convertToUIDateFormat(data.startDate) || '-'} - <$ApplicationDetailRow> + <$ApplicationDetailRow data-testid="application-field-endDate"> <$ApplicationDetailLabel> {t(`${translationsBase}.employee.fields.endDate.label`)} diff --git a/frontend/benefit/applicant/src/components/applications/forms/application/step6/ApplicationFormStep6.tsx b/frontend/benefit/applicant/src/components/applications/forms/application/step6/ApplicationFormStep6.tsx index c21487b3e9..8f5eb96c49 100644 --- a/frontend/benefit/applicant/src/components/applications/forms/application/step6/ApplicationFormStep6.tsx +++ b/frontend/benefit/applicant/src/components/applications/forms/application/step6/ApplicationFormStep6.tsx @@ -61,6 +61,7 @@ const ApplicationFormStep6: React.FC< {data?.applicantTermsInEffect?.applicantConsents.map((consent, i) => ( <$GridCell $colSpan={12} key={consent.id}> <$Checkbox + data-testid="application-terms-consent" id={`${cbPrefix}_${consent.id}`} name={`${cbPrefix}_${i}`} label={consent[`text${textLocale}` as TextProp] || ''} From cf8ec052596faddf62dcdf9fe635858f3bf558fe Mon Sep 17 00:00:00 2001 From: Sampo Tawast Date: Wed, 11 Oct 2023 14:55:35 +0300 Subject: [PATCH 3/7] feat: use env to skip console.log on tests --- frontend/.testcaferc.base.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/frontend/.testcaferc.base.js b/frontend/.testcaferc.base.js index 117b39c84e..e3d9da6c60 100644 --- a/frontend/.testcaferc.base.js +++ b/frontend/.testcaferc.base.js @@ -25,11 +25,13 @@ module.exports = (envPath) => { hooks: { test: { after: async (t) => { - const { error, warn, log, info } = await t.getBrowserConsoleMessages(); - console.log('Console logs:', JSON.stringify(log, null, 2)); - console.info('Console infos:', JSON.stringify(info, null, 2)); - console.warn('Console warnings:', JSON.stringify(warn, null, 2)); - console.error('Console errors:', JSON.stringify(error, null, 2)); + if (!process.env.TESTCAFE_SKIP_CONSOLE_LOG) { + const { error, warn, log, info } = await t.getBrowserConsoleMessages(); + console.log('Console logs:', JSON.stringify(log, null, 2)); + console.info('Console infos:', JSON.stringify(info, null, 2)); + console.warn('Console warnings:', JSON.stringify(warn, null, 2)); + console.error('Console errors:', JSON.stringify(error, null, 2)); + } }, }, }, From c918d98102519904b10086e370a587dbede3c49d Mon Sep 17 00:00:00 2001 From: Sampo Tawast Date: Wed, 11 Oct 2023 14:56:21 +0300 Subject: [PATCH 4/7] docs: add missing env for running tests --- .env.benefit-backend.example | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.benefit-backend.example b/.env.benefit-backend.example index b862202104..ad4a2cb7e6 100644 --- a/.env.benefit-backend.example +++ b/.env.benefit-backend.example @@ -5,6 +5,7 @@ ENABLE_DEBUG_ENV=1 APPLY_MIGRATIONS=1 CREATE_SUPERUSER=1 LOAD_FIXTURES=1 +LOAD_DEFAULT_TERMS=1 COMPILE_TRANSLATIONS=1 CORS_ALLOW_ALL_ORIGINS=1 From b596a658a9480038de78ec3d82271f94f10ce854 Mon Sep 17 00:00:00 2001 From: Sampo Tawast Date: Thu, 12 Oct 2023 15:46:27 +0300 Subject: [PATCH 5/7] refactor: rename types --- .../forms/application/ApplicationInfo.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/benefit/applicant/src/components/applications/forms/application/ApplicationInfo.ts b/frontend/benefit/applicant/src/components/applications/forms/application/ApplicationInfo.ts index 54d184071b..9fc0b318f9 100644 --- a/frontend/benefit/applicant/src/components/applications/forms/application/ApplicationInfo.ts +++ b/frontend/benefit/applicant/src/components/applications/forms/application/ApplicationInfo.ts @@ -1,18 +1,18 @@ import { breakpoints } from 'shared/styles/mediaQueries'; import styled from 'styled-components'; -type $CompanyInfoWrapperProps = { +type $ApplicationDetailWrapperProps = { $fontSize?: string; }; -type $CompanyInfoRowProps = { +type $ApplicationDetailRowProps = { $forceColumn?: boolean; $alignItems?: string; }; -type $CompanyInfoValueProps = { +type $ApplicationDetailValueProps = { $column?: boolean; }; -export const $ApplicationDetailWrapper = styled.dl<$CompanyInfoWrapperProps>` +export const $ApplicationDetailWrapper = styled.dl<$ApplicationDetailWrapperProps>` font-size: ${(props) => props.$fontSize ? String(props.$fontSize) : props.theme.fontSize.body.m}; `; @@ -23,7 +23,7 @@ export const $ApplicationDetailLabel = styled.dt` margin-right: ${(props) => props.theme.spacing.m}; `; -export const $ApplicationDetailValue = styled.dd<$CompanyInfoValueProps>` +export const $ApplicationDetailValue = styled.dd<$ApplicationDetailValueProps>` margin-right: ${(props) => props.theme.spacing.xs}; margin-inline-start: 0; display: ${(props) => (props.$column ? 'flex-start' : 'inline-flex')}; @@ -32,7 +32,7 @@ export const $ApplicationDetailValue = styled.dd<$CompanyInfoValueProps>` } `; -export const $ApplicationDetailRow = styled.div<$CompanyInfoRowProps>` +export const $ApplicationDetailRow = styled.div<$ApplicationDetailRowProps>` display: flex; font-size: 1.1em; line-height: ${(props) => props.theme.lineHeight.l}; From b747837122535499685a560386c7def55f9e57af Mon Sep 17 00:00:00 2001 From: Sampo Tawast Date: Fri, 13 Oct 2023 12:09:55 +0300 Subject: [PATCH 6/7] feat: add a new prop to have other than attachment type as translation key --- .../step3/attachmentsList/AttachmentsList.tsx | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/frontend/benefit/applicant/src/components/applications/forms/application/step3/attachmentsList/AttachmentsList.tsx b/frontend/benefit/applicant/src/components/applications/forms/application/step3/attachmentsList/AttachmentsList.tsx index 0dcd2941b2..286572f97c 100644 --- a/frontend/benefit/applicant/src/components/applications/forms/application/step3/attachmentsList/AttachmentsList.tsx +++ b/frontend/benefit/applicant/src/components/applications/forms/application/step3/attachmentsList/AttachmentsList.tsx @@ -1,5 +1,6 @@ import { ATTACHMENT_TYPES } from 'benefit-shared/constants'; import camelCase from 'lodash/camelCase'; +import { TFunction } from 'next-i18next'; import * as React from 'react'; import AttachmentsListBase from 'shared/components/attachments/AttachmentsList'; import { BenefitAttachment } from 'shared/types/attachment'; @@ -8,14 +9,28 @@ import { useAttachmentsList } from './useAttachmentsList'; export type AttachmentsListProps = { attachmentType: ATTACHMENT_TYPES; + attachmentTypeTranslationKey?: string; showMessage?: boolean; attachments?: BenefitAttachment[]; required?: boolean; as?: 'div' | 'li'; }; +const getTitleTranslation = ( + t: TFunction, + translationsBase: string, + attachmentType: ATTACHMENT_TYPES, + attachmentTypeTranslationKey: string +): string => { + const key = attachmentTypeTranslationKey + ? String(attachmentTypeTranslationKey) + : attachmentType; + return t(`${translationsBase}.types.${camelCase(key)}.title`); +}; + const AttachmentsList: React.FC = ({ attachmentType, + attachmentTypeTranslationKey, showMessage = true, attachments, required, @@ -38,7 +53,12 @@ const AttachmentsList: React.FC = ({ return ( Date: Fri, 13 Oct 2023 12:10:40 +0300 Subject: [PATCH 7/7] feat: use custom translation key on pay subsidy decision attachements --- .../applicant/public/locales/en/common.json | 3 +++ .../applicant/public/locales/fi/common.json | 3 +++ .../applicant/public/locales/sv/common.json | 3 +++ .../application/step3/ApplicationFormStep3.tsx | 15 +++++++++++++++ 4 files changed, 24 insertions(+) diff --git a/frontend/benefit/applicant/public/locales/en/common.json b/frontend/benefit/applicant/public/locales/en/common.json index 69ec4f1399..b37627defb 100644 --- a/frontend/benefit/applicant/public/locales/en/common.json +++ b/frontend/benefit/applicant/public/locales/en/common.json @@ -362,6 +362,9 @@ "title": "Pay subsidy decision", "message": "Attach both issued pay subsidy decisions" }, + "paySubsidyDecisionAged": { + "title": "Decision for employment aid for people aged 55 and above" + }, "commissionContract": { "title": "Toimeksiantosopimus", "message": "" diff --git a/frontend/benefit/applicant/public/locales/fi/common.json b/frontend/benefit/applicant/public/locales/fi/common.json index e2c4b81c21..53fbbeb7be 100644 --- a/frontend/benefit/applicant/public/locales/fi/common.json +++ b/frontend/benefit/applicant/public/locales/fi/common.json @@ -362,6 +362,9 @@ "title": "Palkkatukipäätös", "message": "Liitä molemmat myönnetyt palkkatukipäätökset" }, + "paySubsidyDecisionAged": { + "title": "55 vuotta täyttäneiden työllistämistukipäätös" + }, "commissionContract": { "title": "Toimeksiantosopimus", "message": "" diff --git a/frontend/benefit/applicant/public/locales/sv/common.json b/frontend/benefit/applicant/public/locales/sv/common.json index 4eda5b864f..51f74ef03b 100644 --- a/frontend/benefit/applicant/public/locales/sv/common.json +++ b/frontend/benefit/applicant/public/locales/sv/common.json @@ -362,6 +362,9 @@ "title": "Beslut om lönesubvention", "message": "Bifoga båda besluten om beviljad lönesubvention" }, + "paySubsidyDecisionAged": { + "title": "Beslut om finansiella stöd för personer som är 55 år och äldre" + }, "commissionContract": { "title": "Toimeksiantosopimus", "message": "" diff --git a/frontend/benefit/applicant/src/components/applications/forms/application/step3/ApplicationFormStep3.tsx b/frontend/benefit/applicant/src/components/applications/forms/application/step3/ApplicationFormStep3.tsx index 6afae7ca58..6840cb8e7d 100644 --- a/frontend/benefit/applicant/src/components/applications/forms/application/step3/ApplicationFormStep3.tsx +++ b/frontend/benefit/applicant/src/components/applications/forms/application/step3/ApplicationFormStep3.tsx @@ -13,6 +13,18 @@ import StepperActions from '../stepperActions/StepperActions'; import AttachmentsList from './attachmentsList/AttachmentsList'; import { useApplicationFormStep3 } from './useApplicationFormStep3'; +const translationKeyForPaySubsidyAttachement = ( + paySubsidyGranted: PAY_SUBSIDY_GRANTED +): 'paySubsidyDecision' | 'paySubsidyDecisionAged' => { + if (paySubsidyGranted === PAY_SUBSIDY_GRANTED.GRANTED) { + return 'paySubsidyDecision'; + } + if (paySubsidyGranted === PAY_SUBSIDY_GRANTED.GRANTED_AGED) { + return 'paySubsidyDecisionAged'; + } + return 'paySubsidyDecision'; +}; + const ApplicationFormStep3: React.FC = ({ data, }) => { @@ -60,6 +72,9 @@ const ApplicationFormStep3: React.FC = ({ as="li" attachments={attachments} attachmentType={ATTACHMENT_TYPES.PAY_SUBSIDY_CONTRACT} + attachmentTypeTranslationKey={translationKeyForPaySubsidyAttachement( + paySubsidyGranted + )} showMessage={showSubsidyMessage} required />