diff --git a/src/components/pages/DonationForm.vue b/src/components/pages/DonationForm.vue index 64728c8c4..00c59f115 100644 --- a/src/components/pages/DonationForm.vue +++ b/src/components/pages/DonationForm.vue @@ -1,68 +1,22 @@ diff --git a/src/components/pages/UpdateAddress.vue b/src/components/pages/UpdateAddress.vue index d2dc5846d..8471a5eb7 100644 --- a/src/components/pages/UpdateAddress.vue +++ b/src/components/pages/UpdateAddress.vue @@ -116,13 +116,13 @@ import NameFields from '@src/components/shared/NameFields.vue'; import PostalAddressFields from '@src/components/shared/PostalAddressFields.vue'; import FormButton from '@src/components/shared/form_elements/FormButton.vue'; import CheckboxField from '@src/components/shared/form_fields/CheckboxField.vue'; -import { useAddressTypeFunctions } from '@src/components/pages/donation_form/AddressTypeFunctions'; -import { useReceiptModel } from '@src/components/pages/donation_form/useReceiptModel'; +import { useAddressTypeFunctions } from '@src/components/shared/composables/useAddressTypeFunctions'; import ErrorSummary from '@src/components/shared/validation_summary/ErrorSummary.vue'; import ServerMessage from '@src/components/shared/ServerMessage.vue'; import { addressTypeName } from '@src/view_models/AddressTypeModel'; import { UpdateAddressResponse } from '@src/api/UpdateAddressResponse'; import { AddressChangeResource } from '@src/api/AddressChangeResource'; +import { useReceiptModel } from '@src/components/shared/composables/useReceiptModel'; defineOptions( { name: 'UpdateAddress', diff --git a/src/components/pages/donation_confirmation/AddressUpdateForm.vue b/src/components/pages/donation_confirmation/AddressUpdateForm.vue index b89a52d34..c4b12be5e 100644 --- a/src/components/pages/donation_confirmation/AddressUpdateForm.vue +++ b/src/components/pages/donation_confirmation/AddressUpdateForm.vue @@ -116,7 +116,7 @@ import ValueEqualsPlaceholderWarning from '@src/components/shared/ValueEqualsPla import NameFields from '@src/components/shared/NameFields.vue'; import RadioField from '@src/components/shared/form_fields/RadioField.vue'; import PostalAddressFields from '@src/components/shared/PostalAddressFields.vue'; -import { useAddressTypeFunctions } from '@src/components/pages/donation_form/AddressTypeFunctions'; +import { useAddressTypeFunctions } from '@src/components/shared/composables/useAddressTypeFunctions'; import { MAILING_LIST_ADDRESS_PAGE } from '@src/config'; import AddressUpdateFormErrorSummaries from '@src/components/pages/donation_confirmation/AddressUpdateFormErrorSummaries.vue'; diff --git a/src/components/pages/donation_form/AddressForms.vue b/src/components/pages/donation_form/AddressForms.vue index e778c9c7f..7596ab5bb 100644 --- a/src/components/pages/donation_form/AddressForms.vue +++ b/src/components/pages/donation_form/AddressForms.vue @@ -15,30 +15,15 @@ field-id-namespace="person" v-on:field-changed="onFieldChange" /> - - - - +
- - - - +
import { computed, onBeforeMount, ref, toRefs } from 'vue'; -import PostalAddressFields from '@src/components/shared/PostalAddressFields.vue'; -import StreetAutocompletePostalAddressFields from '@src/components/pages/donation_form/StreetAutocomplete/PostalAddressFields.vue'; +import PostalAddressFields from '@src/components/pages/donation_form/PostalAddressFields.vue'; import AutofillHandler from '@src/components/shared/AutofillHandler.vue'; import CheckboxSingleFormInput from '@src/components/shared/form_elements/CheckboxSingleFormInput.vue'; import EmailField from '@src/components/shared/form_fields/EmailField.vue'; @@ -192,27 +161,25 @@ import { CampaignValues } from '@src/view_models/CampaignValues'; import { AddressTypeIds } from '@src/components/pages/donation_form/AddressTypeIds'; import { Validity } from '@src/view_models/Validity'; import ValueEqualsPlaceholderWarning from '@src/components/shared/ValueEqualsPlaceholderWarning.vue'; -import { useReceiptModel } from '@src/components/pages/donation_form/useReceiptModel'; import { useMailingListModel } from '@src/components/shared/form_fields/useMailingListModel'; import ScrollTarget from '@src/components/shared/ScrollTarget.vue'; import { useStore } from 'vuex'; +import { useReceiptModel } from '@src/components/shared/composables/useReceiptModel'; interface Props { countries: Country[], addressValidationPatterns: AddressValidation, addressType: AddressTypeModel, - isFullSelected?: boolean, salutations: Salutation[], trackingData: TrackingData, campaignValues: CampaignValues, } const props = withDefaults( defineProps(), { - isFullSelected: false, addressType: AddressTypeModel.PERSON, } ); -const { addressType, isFullSelected, addressValidationPatterns } = toRefs( props ); +const { addressType, addressValidationPatterns } = toRefs( props ); const store = useStore(); const { formData, @@ -221,12 +188,13 @@ const { onFieldChange, onAutofill, } = useAddressFunctions( { addressValidationPatterns: addressValidationPatterns.value }, store ); + const mailingList = useMailingListModel( store ); const { receiptNeeded } = useReceiptModel( store ); const addressTypeId = computed( () => { - if ( isFullSelected.value && addressType.value === AddressTypeModel.UNSET ) { + if ( addressType.value === AddressTypeModel.UNSET ) { return AddressTypeIds.get( AddressTypeModel.PERSON ); } return AddressTypeIds.has( addressType.value ) ? AddressTypeIds.get( addressType.value ) : ''; diff --git a/src/components/pages/donation_form/AddressTypeBasic.vue b/src/components/pages/donation_form/AddressTypeBasic.vue index 3bfaf2513..b9c39fb0a 100644 --- a/src/components/pages/donation_form/AddressTypeBasic.vue +++ b/src/components/pages/donation_form/AddressTypeBasic.vue @@ -50,12 +50,10 @@ interface Props { } const props = defineProps(); -const emit = defineEmits( [ 'address-type', 'set-full-selected' ] ); +const emit = defineEmits( [ 'address-type' ] ); const addressType = ref( props.initialAddressType ?? AddressTypeModel.UNSET ); -emit( 'set-full-selected', true ); - watch( addressType, newAddressType => { emit( 'address-type', newAddressType ); } ); diff --git a/src/components/pages/donation_form/DonationReceipt/AddressFields.vue b/src/components/pages/donation_form/DonationReceipt/AddressFields.vue index e31592f53..cf9c32ed7 100644 --- a/src/components/pages/donation_form/DonationReceipt/AddressFields.vue +++ b/src/components/pages/donation_form/DonationReceipt/AddressFields.vue @@ -29,25 +29,19 @@ @field-changed="$emit('field-changed', 'companyName')" /> - - - {{ $t('donation_form_street_number_warning') }} - - + + - - +
@@ -113,18 +105,19 @@ import { useAddressTypeModel } from '@src/components/pages/donation_form/Donatio import { AddressFormData, AddressValidity } from '@src/view_models/Address'; import TextField from '@src/components/shared/form_fields/TextField.vue'; import ValueEqualsPlaceholderWarning from '@src/components/shared/ValueEqualsPlaceholderWarning.vue'; -import { computed } from 'vue'; +import { computed, onBeforeMount, ref } from 'vue'; import CityAutocompleteField from '@src/components/shared/form_fields/CityAutocompleteField.vue'; import CountryAutocompleteField from '@src/components/shared/form_fields/CountryAutocompleteField.vue'; +import StreetAutocompleteField from '@src/components/shared/form_fields/StreetAutocompleteField.vue'; import { Country } from '@src/view_models/Country'; import ScrollTarget from '@src/components/shared/ScrollTarget.vue'; +import { Validity } from '@src/view_models/Validity'; interface Props { formData: AddressFormData; showError: AddressValidity; countries: Country[]; postCodeValidation: string; - countryWasRestored: boolean; } const props = defineProps(); @@ -133,8 +126,8 @@ const emit = defineEmits( [ 'field-changed' ] ); const store = useStore(); const addressType = useAddressTypeModel( store ); -const showStreetWarning = computed( () => /^\D+$/.test( props.formData.street.value ) ); const showAddressTypeError = computed( () => store.getters[ 'address/addressTypeIsInvalid' ] ); +const countryWasRestored = ref( false ); const onCountryFieldChanged = ( country: Country | undefined ) => { if ( country ) { @@ -150,4 +143,8 @@ const onCountryFieldChanged = ( country: Country | undefined ) => { } }; +onBeforeMount( () => { + countryWasRestored.value = store.state.address.validity.country === Validity.RESTORED; +} ); + diff --git a/src/components/pages/donation_form/DonationReceipt/AddressFieldsStreetAutocomplete.vue b/src/components/pages/donation_form/DonationReceipt/AddressFieldsStreetAutocomplete.vue deleted file mode 100644 index 8dad74c0f..000000000 --- a/src/components/pages/donation_form/DonationReceipt/AddressFieldsStreetAutocomplete.vue +++ /dev/null @@ -1,145 +0,0 @@ - - - diff --git a/src/components/pages/donation_form/DonationReceipt/ErrorSummary.vue b/src/components/pages/donation_form/DonationReceipt/ErrorSummary.vue index 33ca8584d..1eed03bcb 100644 --- a/src/components/pages/donation_form/DonationReceipt/ErrorSummary.vue +++ b/src/components/pages/donation_form/DonationReceipt/ErrorSummary.vue @@ -1,6 +1,6 @@ diff --git a/src/components/pages/donation_form/FormSections/PersonalDataSectionDonationReceipt.vue b/src/components/pages/donation_form/FormSections/PersonalDataSectionDonationReceipt.vue index 84b2372f3..660ca1d93 100644 --- a/src/components/pages/donation_form/FormSections/PersonalDataSectionDonationReceipt.vue +++ b/src/components/pages/donation_form/FormSections/PersonalDataSectionDonationReceipt.vue @@ -6,7 +6,7 @@ >

{{ $t( 'donation_form_address_subheading' ) }}

{{ $t( 'donation_form_section_address_tagline' ) }}

-
+ - - - - - + - - - - - - -
-
-
diff --git a/src/components/pages/donation_form/PaymentSummary.vue b/src/components/pages/donation_form/PaymentSummary.vue index 65fa132f3..fd932fd54 100644 --- a/src/components/pages/donation_form/PaymentSummary.vue +++ b/src/components/pages/donation_form/PaymentSummary.vue @@ -51,7 +51,7 @@ const summaryWithoutPaymentType = computed( () => { margin: 0 0 map.get( units.$spacing, 'large' ); @include breakpoints.tablet-up { - margin: 0 ( -( map.get( units.$spacing, 'xx-small' ) ) ) map.get( units.$spacing, 'large' ); + margin: 0 0 map.get( units.$spacing, 'large' ); } &-text { diff --git a/src/components/pages/donation_form/StreetAutocomplete/PostalAddressFields.vue b/src/components/pages/donation_form/PostalAddressFields.vue similarity index 100% rename from src/components/pages/donation_form/StreetAutocomplete/PostalAddressFields.vue rename to src/components/pages/donation_form/PostalAddressFields.vue diff --git a/src/components/pages/donation_form/StreetAutocomplete/ErrorSummary.vue b/src/components/pages/donation_form/StreetAutocomplete/ErrorSummary.vue deleted file mode 100644 index ddb5e3664..000000000 --- a/src/components/pages/donation_form/StreetAutocomplete/ErrorSummary.vue +++ /dev/null @@ -1,222 +0,0 @@ - - - diff --git a/src/components/pages/donation_form/SubPages/DonationForm.vue b/src/components/pages/donation_form/SubPages/DonationForm.vue new file mode 100644 index 000000000..944eedc27 --- /dev/null +++ b/src/components/pages/donation_form/SubPages/DonationForm.vue @@ -0,0 +1,141 @@ + + + diff --git a/src/components/pages/donation_form/SubPages/DonationFormReceipt.vue b/src/components/pages/donation_form/SubPages/DonationFormReceipt.vue new file mode 100644 index 000000000..1d4becdb3 --- /dev/null +++ b/src/components/pages/donation_form/SubPages/DonationFormReceipt.vue @@ -0,0 +1,147 @@ + + + diff --git a/src/components/shared/FormSummary.vue b/src/components/shared/FormSummary.vue index f490770c8..b8fedcaac 100644 --- a/src/components/shared/FormSummary.vue +++ b/src/components/shared/FormSummary.vue @@ -37,7 +37,6 @@ withDefaults( defineProps(), { @include breakpoints.tablet-up { padding: map.get( units.$spacing, 'small' ); - margin: 0 ( -( map.get( units.$spacing, 'small' ) ) ); } &-content { diff --git a/src/components/shared/SalutationLocaleAdjuster.ts b/src/components/shared/SalutationLocaleAdjuster.ts deleted file mode 100644 index ebddaeacf..000000000 --- a/src/components/shared/SalutationLocaleAdjuster.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Salutation } from '@src/view_models/Salutation'; - -/* -* This is used when a user changes language on the address form -* It takes the old salutation and looks to see if there's a locale -* for it. If you're thinking of improving it, please don't. There -* is already a proposal for improving it here: -* -* https://phabricator.wikimedia.org/T317388 -*/ - -const salutationValueTranslations: Record = { - 'Herr': 'Mr', - 'Frau': 'Ms', - 'Mr': 'Herr', - 'Ms': 'Frau', - 'No Salutation': 'Keine Anrede', - 'Keine Anrede': 'No Salutation', -}; - -export const adjustSalutationLocaleIfNeeded = ( salutations: Array, salutation: string ): string => { - if ( salutation === '' ) { - return ''; - } - - const currentSalutation = salutations.find( s => s.value === salutation ); - - if ( currentSalutation !== undefined ) { - return salutation; - } - - return salutationValueTranslations[ salutation ] ?? ''; -}; diff --git a/src/components/pages/donation_form/AddressTypeFunctions.ts b/src/components/shared/composables/useAddressTypeFunctions.ts similarity index 100% rename from src/components/pages/donation_form/AddressTypeFunctions.ts rename to src/components/shared/composables/useAddressTypeFunctions.ts diff --git a/src/components/pages/donation_form/useReceiptModel.ts b/src/components/shared/composables/useReceiptModel.ts similarity index 100% rename from src/components/pages/donation_form/useReceiptModel.ts rename to src/components/shared/composables/useReceiptModel.ts diff --git a/src/pages/donation_form.ts b/src/pages/donation_form.ts index 9be8c1d1f..481a9307d 100644 --- a/src/pages/donation_form.ts +++ b/src/pages/donation_form.ts @@ -67,6 +67,10 @@ dataPersister.initialize( persistenceItems ).then( () => { action( 'address', 'initializeAddress' ), createInitialDonationAddressValues( dataPersister, pageData.applicationVars.initialFormValues ) ), + store.dispatch( + action( 'address', 'adjustSalutationLocale' ), + { salutations: pageData.applicationVars.salutations, salutation: store.state.address.values.salutation } + ), ] ).then( () => { // ignoring result of initializeAddress const app = createVueApp( diff --git a/src/store/address/actions.ts b/src/store/address/actions.ts index 00e856e2e..d8801a500 100644 --- a/src/store/address/actions.ts +++ b/src/store/address/actions.ts @@ -9,6 +9,8 @@ import { import { ValidationResponse } from '@src/store/ValidationResponse'; import { AddressTypeModel, addressTypeName } from '@src/view_models/AddressTypeModel'; import { Validity } from '@src/view_models/Validity'; +import { Salutation } from '@src/view_models/Salutation'; +import { salutationValueTranslations } from '@src/view_models/salutationValueTranslations'; export const actions = { validateAddressField( context: ActionContext, field: InputField ) { @@ -112,5 +114,24 @@ export const actions = { } context.commit( 'INITIALIZE_ADDRESS', initialValues.fields ); }, + /* + * This is used when a user changes language on the address form + * It takes the old salutation and looks to see if there's a locale + * for it. If you're thinking of improving it, please don't. There + * is already a proposal for improving it here: + * + * https://phabricator.wikimedia.org/T317388 + */ + adjustSalutationLocale( context: ActionContext, payload: { salutations: Salutation[], salutation: string } ): void { + if ( payload.salutation === '' ) { + return; + } + const currentSalutation = payload.salutations.find( s => s.value === payload.salutation ); + if ( currentSalutation !== undefined ) { + context.commit( 'SET_SALUTATION', payload.salutation ); + return; + } + context.commit( 'SET_SALUTATION', salutationValueTranslations[ payload.salutation ] ); + }, }; diff --git a/src/store/address/mutations.ts b/src/store/address/mutations.ts index 416e92db8..f6edb58f3 100644 --- a/src/store/address/mutations.ts +++ b/src/store/address/mutations.ts @@ -96,4 +96,7 @@ export const mutations: MutationTree = { state.values[ field.name ] = field.value; } ); }, + SET_SALUTATION( state: AddressState, salutation: string ) { + state.values.salutation = salutation; + }, }; diff --git a/src/view_models/salutationValueTranslations.ts b/src/view_models/salutationValueTranslations.ts new file mode 100644 index 000000000..cd6776338 --- /dev/null +++ b/src/view_models/salutationValueTranslations.ts @@ -0,0 +1,8 @@ +export const salutationValueTranslations: Record = { + 'Herr': 'Mr', + 'Frau': 'Ms', + 'Mr': 'Herr', + 'Ms': 'Frau', + 'No Salutation': 'Keine Anrede', + 'Keine Anrede': 'No Salutation', +}; diff --git a/tests/unit/components/pages/donation_form/AddressForms.spec.ts b/tests/unit/components/pages/donation_form/AddressForms.spec.ts index 520efcc66..497fa5e8b 100644 --- a/tests/unit/components/pages/donation_form/AddressForms.spec.ts +++ b/tests/unit/components/pages/donation_form/AddressForms.spec.ts @@ -55,7 +55,6 @@ describe( 'AddressForms.vue', () => { countries: countries, addressValidationPatterns: addressValidationPatterns, addressType, - isFullSelected: true, trackingData: { bannerImpressionCount: 1, impressionCount: 5, @@ -88,15 +87,13 @@ describe( 'AddressForms.vue', () => { } ); each( [ - [ AddressTypeModel.ANON, false, 'address-type-anonymous' ], - [ AddressTypeModel.EMAIL, false, 'address-type-email' ], - [ AddressTypeModel.UNSET, false, 'address-type-unset' ], - [ AddressTypeModel.UNSET, true, 'address-type-person' ], - [ AddressTypeModel.PERSON, true, 'address-type-person' ], - [ AddressTypeModel.COMPANY, true, 'address-type-company' ], - ] ).test( 'adapts the class attribute', ( addressType, isFullSelected, expectedClass ) => { + [ AddressTypeModel.ANON, 'address-type-anonymous' ], + [ AddressTypeModel.EMAIL, 'address-type-email' ], + [ AddressTypeModel.UNSET, 'address-type-person' ], + [ AddressTypeModel.PERSON, 'address-type-person' ], + [ AddressTypeModel.COMPANY, 'address-type-company' ], + ] ).test( 'adapts the class attribute', ( addressType, expectedClass ) => { const options = createOptionsForAddressType( addressType ); - options.props.isFullSelected = isFullSelected; wrapper = mount( AddressForms, options ); expect( wrapper.classes() ).toContain( expectedClass ); } ); diff --git a/tests/unit/components/pages/donation_form/DonationForm.spec.ts b/tests/unit/components/pages/donation_form/DonationForm.spec.ts index 97ef16edf..cf26051e1 100644 --- a/tests/unit/components/pages/donation_form/DonationForm.spec.ts +++ b/tests/unit/components/pages/donation_form/DonationForm.spec.ts @@ -1,8 +1,7 @@ import { flushPromises, mount, VueWrapper } from '@vue/test-utils'; -import DonationForm from '@src/components/pages/DonationForm.vue'; +import DonationForm from '@src/components/pages/donation_form/SubPages/DonationForm.vue'; import countries from '@test/data/countries'; import { AddressValidation } from '@src/view_models/Validation'; -import { createFeatureToggle } from '@src/util/createFeatureToggle'; import { createStore } from '@src/store/donation_store'; import { nextTick } from 'vue'; import axios from 'axios'; @@ -84,14 +83,19 @@ describe( 'DonationForm.vue', () => { provide: { bankValidationResource: newSucceedingBankValidationResource(), }, - components: { - FeatureToggle: createFeatureToggle( [ 'campaigns.address_pages.legacy' ] ), - }, }, attachTo: document.body, } ); }; + it( 'sets the correct default field values', async () => { + const wrapper = getWrapper(); + + expect( wrapper.find( '#interval-0' ).element.checked ).toBeTruthy(); + expect( wrapper.find( '#receipt-option-person' ).element.checked ).toBeTruthy(); + expect( wrapper.find( '#person-newsletter' ).element.checked ).toBeTruthy(); + } ); + it( 'handles the error summary when no address type was selected before submitting', async () => { const wrapper = getWrapper(); @@ -410,4 +414,15 @@ describe( 'DonationForm.vue', () => { expect( submitForm.element.submit ).toHaveBeenCalled(); } ); + + it( 'scrolls to payment section when button for changing payment data is clicked', async () => { + const scrollElement = { scrollIntoView: jest.fn() }; + Object.defineProperty( document, 'getElementById', { writable: true, configurable: true, value: () => scrollElement } ); + + const wrapper = getWrapper(); + + await wrapper.find( '#previous-btn' ).trigger( 'click' ); + + expect( scrollElement.scrollIntoView ).toHaveBeenCalledWith( { behavior: 'smooth' } ); + } ); } ); diff --git a/tests/unit/components/pages/donation_form/DonationFormAlternateOrder.spec.ts b/tests/unit/components/pages/donation_form/DonationFormAlternateOrder.spec.ts deleted file mode 100644 index 3b94eee1e..000000000 --- a/tests/unit/components/pages/donation_form/DonationFormAlternateOrder.spec.ts +++ /dev/null @@ -1,413 +0,0 @@ -import { flushPromises, mount, VueWrapper } from '@vue/test-utils'; -import DonationForm from '@src/components/pages/DonationForm.vue'; -import countries from '@test/data/countries'; -import { AddressValidation } from '@src/view_models/Validation'; -import { createFeatureToggle } from '@src/util/createFeatureToggle'; -import { createStore } from '@src/store/donation_store'; -import { nextTick } from 'vue'; -import axios from 'axios'; -import { newSucceedingBankValidationResource } from '@test/unit/TestDoubles/SucceedingBankValidationResource'; -import { IBAN } from '@test/data/bankdata'; - -jest.mock( 'axios' ); -const mockedAxios = axios as jest.Mocked; - -declare global { - namespace NodeJS { - interface Global { - window: Window; - } - } -} - -const errorSummaryItemIsFunctional = ( wrapper: VueWrapper, formElement: string, scrollElement: string ): boolean => { - const errorItemExists = wrapper.find( `.error-summary a[href="#${formElement}"]` ).exists(); - const formElementExists = wrapper.find( `#${formElement}` ).exists(); - const scrollElementExists = wrapper.find( `#${scrollElement}` ).exists(); - - return errorItemExists && formElementExists && scrollElementExists; -}; - -describe( 'DonationForm.vue (alternate order for street autocomplete)', () => { - - beforeEach( () => { - global.window.scrollTo = jest.fn(); - jest.useFakeTimers(); - } ); - - afterEach( () => { - jest.clearAllMocks(); - document.getElementsByTagName( 'html' )[ 0 ].innerHTML = ''; - } ); - - const getWrapper = (): VueWrapper => { - const store = createStore(); - return mount( DonationForm, { - props: { - assetsPath: '', - paymentAmounts: [ 500, 1000, 2000 ], - paymentIntervals: [ 0, 1, 3, 6, 12 ], - paymentTypes: [ 'BEZ', 'PPL', 'UEB', 'BTC' ], - validateAddressUrl: 'https://example.com/address-check', - countries: countries, - trackingData: { bannerImpressionCount: 0, impressionCount: 0 }, - campaignValues: { campaign: 'nicholas', keyword: 'cage' }, - validateEmailUrl: '', - validateBankDataUrl: '', - validateLegacyBankDataUrl: '', - salutations: [ - { - label: 'Mr', - value: 'Mr', - display: 'Mr', - greetings: { - formal: 'Mr', - informal: 'Mr', - lastNameInformal: 'Mr', - }, - }, - { - label: 'Ms', - value: 'Ms', - display: 'Ms', - greetings: { - formal: 'Ms', - informal: 'Ms', - lastNameInformal: 'Ms', - }, - }, - ], - addressValidationPatterns: { postcode: '', country: null } as AddressValidation, - }, - global: { - plugins: [ store ], - provide: { - bankValidationResource: newSucceedingBankValidationResource(), - }, - components: { - FeatureToggle: createFeatureToggle( [ 'campaigns.address_pages.legacy', 'campaigns.address_field_order.new_order' ] ), - }, - }, - attachTo: document.body, - } ); - }; - - it( 'handles the error summary when no address type was selected before submitting', async () => { - const wrapper = getWrapper(); - - await wrapper.find( '#person-country' ).setValue( 'I am clearly not a country' ); - await wrapper.find( '#person-country' ).trigger( 'blur' ); - await jest.runAllTimersAsync(); - - await wrapper.find( '#submit-btn' ).trigger( 'click' ); - await nextTick(); - await nextTick(); - - expect( errorSummaryItemIsFunctional( wrapper, 'paymentType-0', 'payment-form-type-scroll-target' ) ).toBeTruthy(); - - // Make the IBAN field appear - await wrapper.find( 'input[name="paymentType"][value="BEZ"]' ).trigger( 'change' ); - await wrapper.find( '#submit-btn' ).trigger( 'click' ); - await nextTick(); - await nextTick(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'amount-500', 'payment-form-amount-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'iban', 'iban-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'addressType-0', 'address-type-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-salutation-0', 'person-salutation-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-first-name', 'person-first-name-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-last-name', 'person-last-name-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-street', 'person-street-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-post-code', 'person-post-code-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-city', 'person-city-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-country', 'person-country-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-email', 'person-email-scroll-target' ) ).toBeTruthy(); - - await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="addressType"][value="0"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="salutation"][value="Mr"]' ).trigger( 'change' ); - - await wrapper.find( '#iban' ).setValue( IBAN ); - await wrapper.find( '#iban' ).trigger( 'blur' ); - - await wrapper.find( '#person-first-name' ).setValue( 'first-name' ); - await wrapper.find( '#person-first-name' ).trigger( 'blur' ); - - await wrapper.find( '#person-last-name' ).setValue( 'last-name' ); - await wrapper.find( '#person-last-name' ).trigger( 'blur' ); - - await wrapper.find( '#person-street' ).setValue( 'street' ); - await wrapper.find( '#person-street' ).trigger( 'blur' ); - - await wrapper.find( '#person-post-code' ).setValue( '12345' ); - await wrapper.find( '#person-post-code' ).trigger( 'blur' ); - - await wrapper.find( '#person-city' ).setValue( 'city' ); - await wrapper.find( '#person-city' ).trigger( 'blur' ); - - await wrapper.find( '#person-country' ).setValue( countries[ 0 ].countryFullName ); - await wrapper.find( '#person-country' ).trigger( 'blur' ); - - await wrapper.find( '#person-email' ).setValue( 'joe@dolan.com' ); - await wrapper.find( '#person-email' ).trigger( 'blur' ); - - await jest.runAllTimersAsync(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeFalsy(); - } ); - - it( 'handles the error summary when person address type was selected before submitting', async () => { - const wrapper = getWrapper(); - - await wrapper.find( 'input[name="addressType"][value="0"]' ).trigger( 'change' ); - await wrapper.find( '#person-country' ).setValue( 'I am clearly not a country' ); - await wrapper.find( '#person-country' ).trigger( 'blur' ); - await jest.runAllTimersAsync(); - - await wrapper.find( '#submit-btn' ).trigger( 'click' ); - await nextTick(); - await nextTick(); - - expect( errorSummaryItemIsFunctional( wrapper, 'paymentType-0', 'payment-form-type-scroll-target' ) ).toBeTruthy(); - - // Make the IBAN field appear - await wrapper.find( 'input[name="paymentType"][value="BEZ"]' ).trigger( 'change' ); - await wrapper.find( '#submit-btn' ).trigger( 'click' ); - await nextTick(); - await nextTick(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'amount-500', 'payment-form-amount-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'iban', 'iban-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-salutation-0', 'person-salutation-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-first-name', 'person-first-name-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-last-name', 'person-last-name-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-street', 'person-street-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-post-code', 'person-post-code-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-city', 'person-city-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-country', 'person-country-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'person-email', 'person-email-scroll-target' ) ).toBeTruthy(); - - await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="salutation"][value="Mr"]' ).trigger( 'change' ); - - await wrapper.find( '#iban' ).setValue( IBAN ); - await wrapper.find( '#iban' ).trigger( 'blur' ); - - await wrapper.find( '#person-first-name' ).setValue( 'first-name' ); - await wrapper.find( '#person-first-name' ).trigger( 'blur' ); - - await wrapper.find( '#person-last-name' ).setValue( 'last-name' ); - await wrapper.find( '#person-last-name' ).trigger( 'blur' ); - - await wrapper.find( '#person-street' ).setValue( 'street' ); - await wrapper.find( '#person-street' ).trigger( 'blur' ); - - await wrapper.find( '#person-post-code' ).setValue( '12345' ); - await wrapper.find( '#person-post-code' ).trigger( 'blur' ); - - await wrapper.find( '#person-city' ).setValue( 'city' ); - await wrapper.find( '#person-city' ).trigger( 'blur' ); - - await wrapper.find( '#person-country' ).setValue( countries[ 0 ].countryFullName ); - await wrapper.find( '#person-country' ).trigger( 'blur' ); - - await wrapper.find( '#person-email' ).setValue( 'joe@dolan.com' ); - await wrapper.find( '#person-email' ).trigger( 'blur' ); - - await jest.runAllTimersAsync(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeFalsy(); - } ); - - it( 'handles the error summary when company address type was selected before submitting', async () => { - const wrapper = getWrapper(); - - await wrapper.find( 'input[name="addressType"][value="1"]' ).trigger( 'change' ); - await wrapper.find( '#company-country' ).setValue( 'I am clearly not a country' ); - await wrapper.find( '#company-country' ).trigger( 'blur' ); - await jest.runAllTimersAsync(); - - await wrapper.find( '#submit-btn' ).trigger( 'click' ); - await nextTick(); - await nextTick(); - - expect( errorSummaryItemIsFunctional( wrapper, 'paymentType-0', 'payment-form-type-scroll-target' ) ).toBeTruthy(); - - // Make the IBAN field appear - await wrapper.find( 'input[name="paymentType"][value="BEZ"]' ).trigger( 'change' ); - await wrapper.find( '#submit-btn' ).trigger( 'click' ); - await nextTick(); - await nextTick(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'amount-500', 'payment-form-amount-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'iban', 'iban-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'company-company-name', 'company-company-name-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'company-street', 'company-street-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'company-post-code', 'company-post-code-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'company-city', 'company-city-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'company-country', 'company-country-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'company-email', 'company-email-scroll-target' ) ).toBeTruthy(); - - await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="paymentType"][value="PPL"]' ).trigger( 'change' ); - - await wrapper.find( 'input[name="salutation"][value="Mr"]' ).trigger( 'change' ); - - await wrapper.find( '#company-company-name' ).setValue( 'company-name' ); - await wrapper.find( '#company-company-name' ).trigger( 'blur' ); - - await wrapper.find( '#company-street' ).setValue( 'street' ); - await wrapper.find( '#company-street' ).trigger( 'blur' ); - - await wrapper.find( '#company-post-code' ).setValue( '12345' ); - await wrapper.find( '#company-post-code' ).trigger( 'blur' ); - - await wrapper.find( '#company-city' ).setValue( 'city' ); - await wrapper.find( '#company-city' ).trigger( 'blur' ); - - await wrapper.find( '#company-country' ).setValue( countries[ 0 ].countryFullName ); - await wrapper.find( '#company-country' ).trigger( 'blur' ); - - await wrapper.find( '#company-email' ).setValue( 'joe@dolan.com' ); - await wrapper.find( '#company-email' ).trigger( 'blur' ); - - await jest.runAllTimersAsync(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeFalsy(); - } ); - - it( 'handles the error summary when without address type was selected before submitting', async () => { - const wrapper = getWrapper(); - - await wrapper.find( 'input[name="addressType"][value="4"]' ).trigger( 'change' ); - - await wrapper.find( '#submit-btn' ).trigger( 'click' ); - await nextTick(); - await nextTick(); - await flushPromises(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'amount-500', 'payment-form-amount-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'paymentType-0', 'payment-form-type-scroll-target' ) ).toBeTruthy(); - - await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="paymentType"][value="PPL"]' ).trigger( 'change' ); - - await jest.runAllTimersAsync(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeFalsy(); - } ); - - it( 'submits the form for a person', async () => { - mockedAxios.post.mockResolvedValue( { data: { status: 'OK' } } ); - const wrapper = getWrapper(); - - const submitForm = wrapper.find( '#submit-form' ); - submitForm.element.submit = jest.fn(); - - await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="paymentType"][value="BEZ"]' ).trigger( 'change' ); - - await wrapper.find( '#iban' ).setValue( IBAN ); - await wrapper.find( '#iban' ).trigger( 'blur' ); - - await wrapper.find( 'input[name="addressType"][value="0"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="salutation"][value="Mr"]' ).trigger( 'change' ); - - await wrapper.find( '#person-first-name' ).setValue( 'first-name' ); - await wrapper.find( '#person-first-name' ).trigger( 'blur' ); - - await wrapper.find( '#person-last-name' ).setValue( 'last-name' ); - await wrapper.find( '#person-last-name' ).trigger( 'blur' ); - - await wrapper.find( '#person-street' ).setValue( 'street' ); - await wrapper.find( '#person-street' ).trigger( 'blur' ); - - await wrapper.find( '#person-post-code' ).setValue( '12345' ); - await wrapper.find( '#person-post-code' ).trigger( 'blur' ); - - await wrapper.find( '#person-city' ).setValue( 'city' ); - await wrapper.find( '#person-city' ).trigger( 'blur' ); - - await jest.runAllTimersAsync(); - - await wrapper.find( '#person-country' ).setValue( countries[ 0 ].countryFullName ); - await wrapper.find( '#person-country' ).trigger( 'blur' ); - - await wrapper.find( '#person-email' ).setValue( 'joe@dolan.com' ); - await wrapper.find( '#person-email' ).trigger( 'blur' ); - - await wrapper.find( '#submit-btn' ).trigger( 'click' ); - - await jest.runAllTimersAsync(); - await flushPromises(); - - expect( submitForm.element.submit ).toHaveBeenCalled(); - } ); - - it( 'submits the form for a company', async () => { - mockedAxios.post.mockResolvedValue( { data: { status: 'OK' } } ); - const wrapper = getWrapper(); - - const submitForm = wrapper.find( '#submit-form' ); - submitForm.element.submit = jest.fn(); - - await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="paymentType"][value="BEZ"]' ).trigger( 'change' ); - - await wrapper.find( '#iban' ).setValue( IBAN ); - await wrapper.find( '#iban' ).trigger( 'blur' ); - - await wrapper.find( 'input[name="addressType"][value="1"]' ).trigger( 'change' ); - - await wrapper.find( '#company-company-name' ).setValue( 'company-name' ); - await wrapper.find( '#company-company-name' ).trigger( 'blur' ); - - await wrapper.find( '#company-street' ).setValue( 'street' ); - await wrapper.find( '#company-street' ).trigger( 'blur' ); - - await wrapper.find( '#company-post-code' ).setValue( '12345' ); - await wrapper.find( '#company-post-code' ).trigger( 'blur' ); - - await wrapper.find( '#company-city' ).setValue( 'city' ); - await wrapper.find( '#company-city' ).trigger( 'blur' ); - - await jest.runAllTimersAsync(); - - await wrapper.find( '#company-country' ).setValue( countries[ 0 ].countryFullName ); - await wrapper.find( '#company-country' ).trigger( 'blur' ); - - await wrapper.find( '#company-email' ).setValue( 'joe@dolan.com' ); - await wrapper.find( '#company-email' ).trigger( 'blur' ); - - await wrapper.find( '#submit-btn' ).trigger( 'click' ); - - await jest.runAllTimersAsync(); - await flushPromises(); - - expect( submitForm.element.submit ).toHaveBeenCalled(); - } ); - - it( 'submits the form for anonymous', async () => { - mockedAxios.post.mockResolvedValue( { data: { status: 'OK' } } ); - const wrapper = getWrapper(); - - const submitForm = wrapper.find( '#submit-form' ); - submitForm.element.submit = jest.fn(); - - await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="paymentType"][value="PPL"]' ).trigger( 'change' ); - - await wrapper.find( 'input[name="addressType"][value="4"]' ).trigger( 'change' ); - - await wrapper.find( '#submit-btn' ).trigger( 'click' ); - - await jest.runAllTimersAsync(); - await flushPromises(); - - expect( submitForm.element.submit ).toHaveBeenCalled(); - } ); -} ); diff --git a/tests/unit/components/pages/donation_form/DonationFormReceipt.spec.ts b/tests/unit/components/pages/donation_form/DonationFormReceipt.spec.ts index 00c1d53ce..917a3cfb2 100644 --- a/tests/unit/components/pages/donation_form/DonationFormReceipt.spec.ts +++ b/tests/unit/components/pages/donation_form/DonationFormReceipt.spec.ts @@ -1,8 +1,7 @@ import { flushPromises, mount, VueWrapper } from '@vue/test-utils'; -import DonationForm from '@src/components/pages/DonationForm.vue'; +import DonationForm from '@src/components/pages/donation_form/SubPages/DonationFormReceipt.vue'; import countries from '@test/data/countries'; import { AddressValidation } from '@src/view_models/Validation'; -import { createFeatureToggle } from '@src/util/createFeatureToggle'; import { createStore } from '@src/store/donation_store'; import { nextTick } from 'vue'; import axios from 'axios'; @@ -28,7 +27,7 @@ const errorSummaryItemIsFunctional = ( wrapper: VueWrapper, formElement: st return errorItemExists && formElementExists && scrollElementExists; }; -describe( 'DonationForm.vue (with receipt question field)', () => { +describe( 'DonationFormReciept.vue', () => { beforeEach( () => { global.window.scrollTo = jest.fn(); @@ -84,18 +83,24 @@ describe( 'DonationForm.vue (with receipt question field)', () => { provide: { bankValidationResource: newSucceedingBankValidationResource(), }, - components: { - FeatureToggle: createFeatureToggle( [ 'campaigns.address_pages.test_02' ] ), - }, }, attachTo: document.body, } ); }; + it( 'sets the correct default field values', async () => { + const wrapper = getWrapper(); + + expect( wrapper.find( '#interval-0' ).element.checked ).toBeTruthy(); + expect( wrapper.find( '#newsletter' ).element.checked ).toBeTruthy(); + expect( wrapper.find( '#donationReceipt-0' ).element.checked ).toBeFalsy(); + expect( wrapper.find( '#donationReceipt-1' ).element.checked ).toBeFalsy(); + } ); + it( 'handles the error summary when no receipt option was selected before submitting', async () => { const wrapper = getWrapper(); - await wrapper.find( '#donation-form' ).trigger( 'submit' ); + await wrapper.find( '#submit-btn' ).trigger( 'click' ); await nextTick(); await nextTick(); @@ -136,7 +141,7 @@ describe( 'DonationForm.vue (with receipt question field)', () => { await wrapper.find( '#country' ).trigger( 'blur' ); await jest.runAllTimersAsync(); - await wrapper.find( '#donation-form' ).trigger( 'submit' ); + await wrapper.find( '#submit-btn' ).trigger( 'click' ); await nextTick(); await nextTick(); @@ -144,7 +149,7 @@ describe( 'DonationForm.vue (with receipt question field)', () => { // Make the IBAN field appear await wrapper.find( 'input[name="paymentType"][value="BEZ"]' ).trigger( 'change' ); - await wrapper.find( '#donation-form' ).trigger( 'submit' ); + await wrapper.find( '#submit-btn' ).trigger( 'click' ); await nextTick(); await nextTick(); @@ -204,7 +209,7 @@ describe( 'DonationForm.vue (with receipt question field)', () => { await wrapper.find( '#country' ).trigger( 'blur' ); await jest.runAllTimersAsync(); - await wrapper.find( '#donation-form' ).trigger( 'submit' ); + await wrapper.find( '#submit-btn' ).trigger( 'click' ); await nextTick(); await nextTick(); @@ -212,7 +217,7 @@ describe( 'DonationForm.vue (with receipt question field)', () => { // Make the IBAN field appear await wrapper.find( 'input[name="paymentType"][value="BEZ"]' ).trigger( 'change' ); - await wrapper.find( '#donation-form' ).trigger( 'submit' ); + await wrapper.find( '#submit-btn' ).trigger( 'click' ); await nextTick(); await nextTick(); @@ -271,7 +276,7 @@ describe( 'DonationForm.vue (with receipt question field)', () => { await wrapper.find( '#country' ).trigger( 'blur' ); await jest.runAllTimersAsync(); - await wrapper.find( '#donation-form' ).trigger( 'submit' ); + await wrapper.find( '#submit-btn' ).trigger( 'click' ); await nextTick(); await nextTick(); @@ -279,7 +284,7 @@ describe( 'DonationForm.vue (with receipt question field)', () => { // Make the IBAN field appear await wrapper.find( 'input[name="paymentType"][value="BEZ"]' ).trigger( 'change' ); - await wrapper.find( '#donation-form' ).trigger( 'submit' ); + await wrapper.find( '#submit-btn' ).trigger( 'click' ); await nextTick(); await nextTick(); @@ -335,7 +340,7 @@ describe( 'DonationForm.vue (with receipt question field)', () => { mockedAxios.post.mockResolvedValue( { data: { status: 'OK' } } ); const wrapper = getWrapper(); - const submitForm = wrapper.find( '#donation-form-submit-values' ); + const submitForm = wrapper.find( '#submit-form' ); submitForm.element.submit = jest.fn(); await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); @@ -372,7 +377,7 @@ describe( 'DonationForm.vue (with receipt question field)', () => { await wrapper.find( '#country' ).setValue( countries[ 0 ].countryFullName ); await wrapper.find( '#country' ).trigger( 'blur' ); - await wrapper.find( '#donation-form' ).trigger( 'submit' ); + await wrapper.find( '#submit-btn' ).trigger( 'click' ); await jest.runAllTimersAsync(); await flushPromises(); @@ -384,7 +389,7 @@ describe( 'DonationForm.vue (with receipt question field)', () => { mockedAxios.post.mockResolvedValue( { data: { status: 'OK' } } ); const wrapper = getWrapper(); - const submitForm = wrapper.find( '#donation-form-submit-values' ); + const submitForm = wrapper.find( '#submit-form' ); submitForm.element.submit = jest.fn(); await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); @@ -424,7 +429,7 @@ describe( 'DonationForm.vue (with receipt question field)', () => { await wrapper.find( '#country' ).setValue( countries[ 0 ].countryFullName ); await wrapper.find( '#country' ).trigger( 'blur' ); - await wrapper.find( '#donation-form' ).trigger( 'submit' ); + await wrapper.find( '#submit-btn' ).trigger( 'click' ); await jest.runAllTimersAsync(); await flushPromises(); @@ -436,7 +441,7 @@ describe( 'DonationForm.vue (with receipt question field)', () => { mockedAxios.post.mockResolvedValue( { data: { status: 'OK' } } ); const wrapper = getWrapper(); - const submitForm = wrapper.find( '#donation-form-submit-values' ); + const submitForm = wrapper.find( '#submit-form' ); submitForm.element.submit = jest.fn(); await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); @@ -458,7 +463,7 @@ describe( 'DonationForm.vue (with receipt question field)', () => { await wrapper.find( 'input[name="donationReceipt"][value="false"]' ).trigger( 'change' ); - await wrapper.find( '#donation-form' ).trigger( 'submit' ); + await wrapper.find( '#submit-btn' ).trigger( 'click' ); await jest.runAllTimersAsync(); await flushPromises(); diff --git a/tests/unit/components/pages/donation_form/DonationFormReceiptAlternateOrder.spec.ts b/tests/unit/components/pages/donation_form/DonationFormReceiptAlternateOrder.spec.ts deleted file mode 100644 index e56bf7d82..000000000 --- a/tests/unit/components/pages/donation_form/DonationFormReceiptAlternateOrder.spec.ts +++ /dev/null @@ -1,468 +0,0 @@ -import { flushPromises, mount, VueWrapper } from '@vue/test-utils'; -import DonationForm from '@src/components/pages/DonationForm.vue'; -import countries from '@test/data/countries'; -import { AddressValidation } from '@src/view_models/Validation'; -import { createFeatureToggle } from '@src/util/createFeatureToggle'; -import { createStore } from '@src/store/donation_store'; -import { nextTick } from 'vue'; -import axios from 'axios'; -import { newSucceedingBankValidationResource } from '@test/unit/TestDoubles/SucceedingBankValidationResource'; -import { IBAN } from '@test/data/bankdata'; - -jest.mock( 'axios' ); -const mockedAxios = axios as jest.Mocked; - -declare global { - namespace NodeJS { - interface Global { - window: Window; - } - } -} - -const errorSummaryItemIsFunctional = ( wrapper: VueWrapper, formElement: string, scrollElement: string ): boolean => { - const errorItemExists = wrapper.find( `.error-summary a[href="#${formElement}"]` ).exists(); - const formElementExists = wrapper.find( `#${formElement}` ).exists(); - const scrollElementExists = wrapper.find( `#${scrollElement}` ).exists(); - - return errorItemExists && formElementExists && scrollElementExists; -}; - -describe( 'DonationForm.vue (with receipt question field and alternate order)', () => { - - beforeEach( () => { - global.window.scrollTo = jest.fn(); - jest.useFakeTimers(); - } ); - - afterEach( () => { - jest.clearAllMocks(); - document.getElementsByTagName( 'html' )[ 0 ].innerHTML = ''; - } ); - - const getWrapper = (): VueWrapper => { - const store = createStore(); - return mount( DonationForm, { - props: { - assetsPath: '', - paymentAmounts: [ 500, 1000, 2000 ], - paymentIntervals: [ 0, 1, 3, 6, 12 ], - paymentTypes: [ 'BEZ', 'PPL', 'UEB', 'BTC' ], - validateAddressUrl: 'https://example.com/address-check', - countries: countries, - trackingData: { bannerImpressionCount: 0, impressionCount: 0 }, - campaignValues: { campaign: 'nicholas', keyword: 'cage' }, - validateEmailUrl: '', - validateBankDataUrl: '', - validateLegacyBankDataUrl: '', - salutations: [ - { - label: 'Mr', - value: 'Mr', - display: 'Mr', - greetings: { - formal: 'Mr', - informal: 'Mr', - lastNameInformal: 'Mr', - }, - }, - { - label: 'Ms', - value: 'Ms', - display: 'Ms', - greetings: { - formal: 'Ms', - informal: 'Ms', - lastNameInformal: 'Ms', - }, - }, - ], - addressValidationPatterns: { postcode: '', country: null } as AddressValidation, - }, - global: { - plugins: [ store ], - provide: { - bankValidationResource: newSucceedingBankValidationResource(), - }, - components: { - FeatureToggle: createFeatureToggle( [ 'campaigns.address_pages.test_02', 'campaigns.address_field_order.new_order' ] ), - }, - }, - attachTo: document.body, - } ); - }; - - it( 'handles the error summary when no receipt option was selected before submitting', async () => { - const wrapper = getWrapper(); - - await wrapper.find( '#donation-form' ).trigger( 'submit' ); - await nextTick(); - await nextTick(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'amount-500', 'payment-form-amount-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'paymentType-0', 'payment-form-type-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'salutation-0', 'salutation-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'first-name', 'first-name-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'last-name', 'last-name-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'email', 'email-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'donationReceipt-0', 'receipt-scroll-target' ) ).toBeTruthy(); - - await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="paymentType"][value="PPL"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="salutation"][value="Mr"]' ).trigger( 'change' ); - - await wrapper.find( '#first-name' ).setValue( 'first-name' ); - await wrapper.find( '#first-name' ).trigger( 'blur' ); - - await wrapper.find( '#last-name' ).setValue( 'last-name' ); - await wrapper.find( '#last-name' ).trigger( 'blur' ); - - await wrapper.find( '#email' ).setValue( 'joe@dolan.com' ); - await wrapper.find( '#email' ).trigger( 'blur' ); - - await wrapper.find( 'input[name="donationReceipt"][value="false"]' ).trigger( 'change' ); - - await jest.runAllTimersAsync(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeFalsy(); - } ); - - it( 'handles the error summary when only receipt option yes was selected before submitting', async () => { - const wrapper = getWrapper(); - - await wrapper.find( 'input[name="donationReceipt"][value="true"]' ).trigger( 'change' ); - await wrapper.find( '#country' ).setValue( 'I am clearly not a country' ); - await wrapper.find( '#country' ).trigger( 'blur' ); - await jest.runAllTimersAsync(); - - await wrapper.find( '#donation-form' ).trigger( 'submit' ); - await nextTick(); - await nextTick(); - - expect( errorSummaryItemIsFunctional( wrapper, 'paymentType-0', 'payment-form-type-scroll-target' ) ).toBeTruthy(); - - // Make the IBAN field appear - await wrapper.find( 'input[name="paymentType"][value="BEZ"]' ).trigger( 'change' ); - await wrapper.find( '#donation-form' ).trigger( 'submit' ); - await nextTick(); - await nextTick(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'amount-500', 'payment-form-amount-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'iban', 'iban-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'salutation-0', 'salutation-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'first-name', 'first-name-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'last-name', 'last-name-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'email', 'email-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'addressType-0', 'address-type-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'street', 'street-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'post-code', 'post-code-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'city', 'city-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'country', 'country-scroll-target' ) ).toBeTruthy(); - - await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="salutation"][value="Mr"]' ).trigger( 'change' ); - - await wrapper.find( '#iban' ).setValue( IBAN ); - await wrapper.find( '#iban' ).trigger( 'blur' ); - - await wrapper.find( '#first-name' ).setValue( 'first-name' ); - await wrapper.find( '#first-name' ).trigger( 'blur' ); - - await wrapper.find( '#last-name' ).setValue( 'last-name' ); - await wrapper.find( '#last-name' ).trigger( 'blur' ); - - await wrapper.find( 'input[name="addressTypeSelector"][value="0"]' ).trigger( 'change' ); - - await wrapper.find( '#street' ).setValue( 'street' ); - await wrapper.find( '#street' ).trigger( 'blur' ); - - await wrapper.find( '#post-code' ).setValue( '12345' ); - await wrapper.find( '#post-code' ).trigger( 'blur' ); - - await wrapper.find( '#city' ).setValue( 'city' ); - await wrapper.find( '#city' ).trigger( 'blur' ); - - await wrapper.find( '#country' ).setValue( countries[ 0 ].countryFullName ); - await wrapper.find( '#country' ).trigger( 'blur' ); - - await wrapper.find( '#email' ).setValue( 'joe@dolan.com' ); - await wrapper.find( '#email' ).trigger( 'blur' ); - - await jest.runAllTimersAsync(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeFalsy(); - } ); - - it( 'handles the error summary when person address type was selected before submitting', async () => { - const wrapper = getWrapper(); - - await wrapper.find( 'input[name="donationReceipt"][value="true"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="addressTypeSelector"][value="0"]' ).trigger( 'change' ); - await wrapper.find( '#country' ).setValue( 'I am clearly not a country' ); - await wrapper.find( '#country' ).trigger( 'blur' ); - await jest.runAllTimersAsync(); - - await wrapper.find( '#donation-form' ).trigger( 'submit' ); - await nextTick(); - await nextTick(); - - expect( errorSummaryItemIsFunctional( wrapper, 'paymentType-0', 'payment-form-type-scroll-target' ) ).toBeTruthy(); - - // Make the IBAN field appear - await wrapper.find( 'input[name="paymentType"][value="BEZ"]' ).trigger( 'change' ); - await wrapper.find( '#donation-form' ).trigger( 'submit' ); - await nextTick(); - await nextTick(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'amount-500', 'payment-form-amount-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'iban', 'iban-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'salutation-0', 'salutation-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'first-name', 'first-name-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'last-name', 'last-name-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'email', 'email-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'street', 'street-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'post-code', 'post-code-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'city', 'city-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'country', 'country-scroll-target' ) ).toBeTruthy(); - - await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="salutation"][value="Mr"]' ).trigger( 'change' ); - - await wrapper.find( '#iban' ).setValue( IBAN ); - await wrapper.find( '#iban' ).trigger( 'blur' ); - - await wrapper.find( '#first-name' ).setValue( 'first-name' ); - await wrapper.find( '#first-name' ).trigger( 'blur' ); - - await wrapper.find( '#last-name' ).setValue( 'last-name' ); - await wrapper.find( '#last-name' ).trigger( 'blur' ); - - await wrapper.find( '#street' ).setValue( 'street' ); - await wrapper.find( '#street' ).trigger( 'blur' ); - - await wrapper.find( '#post-code' ).setValue( '12345' ); - await wrapper.find( '#post-code' ).trigger( 'blur' ); - - await wrapper.find( '#city' ).setValue( 'city' ); - await wrapper.find( '#city' ).trigger( 'blur' ); - - await wrapper.find( '#country' ).setValue( countries[ 0 ].countryFullName ); - await wrapper.find( '#country' ).trigger( 'blur' ); - - await wrapper.find( '#email' ).setValue( 'joe@dolan.com' ); - await wrapper.find( '#email' ).trigger( 'blur' ); - - await jest.runAllTimersAsync(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeFalsy(); - } ); - - it( 'handles the error summary when company address type was selected before submitting', async () => { - const wrapper = getWrapper(); - - await wrapper.find( 'input[name="donationReceipt"][value="true"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="addressTypeSelector"][value="2"]' ).trigger( 'change' ); - await nextTick(); - - await wrapper.find( '#country' ).setValue( 'I am clearly not a country' ); - await wrapper.find( '#country' ).trigger( 'blur' ); - await jest.runAllTimersAsync(); - - await wrapper.find( '#donation-form' ).trigger( 'submit' ); - await nextTick(); - await nextTick(); - - expect( errorSummaryItemIsFunctional( wrapper, 'paymentType-0', 'payment-form-type-scroll-target' ) ).toBeTruthy(); - - // Make the IBAN field appear - await wrapper.find( 'input[name="paymentType"][value="BEZ"]' ).trigger( 'change' ); - await wrapper.find( '#donation-form' ).trigger( 'submit' ); - await nextTick(); - await nextTick(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'amount-500', 'payment-form-amount-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'iban', 'iban-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'salutation-0', 'salutation-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'first-name', 'first-name-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'last-name', 'last-name-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'email', 'email-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'company-name', 'company-name-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'street', 'street-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'post-code', 'post-code-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'city', 'city-scroll-target' ) ).toBeTruthy(); - expect( errorSummaryItemIsFunctional( wrapper, 'country', 'country-scroll-target' ) ).toBeTruthy(); - - await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="salutation"][value="Mr"]' ).trigger( 'change' ); - - await wrapper.find( '#iban' ).setValue( IBAN ); - await wrapper.find( '#iban' ).trigger( 'blur' ); - - await wrapper.find( '#first-name' ).setValue( 'first-name' ); - await wrapper.find( '#first-name' ).trigger( 'blur' ); - - await wrapper.find( '#last-name' ).setValue( 'last-name' ); - await wrapper.find( '#last-name' ).trigger( 'blur' ); - - await wrapper.find( '#company-name' ).setValue( 'company-name' ); - await wrapper.find( '#company-name' ).trigger( 'blur' ); - - await wrapper.find( '#street' ).setValue( 'street' ); - await wrapper.find( '#street' ).trigger( 'blur' ); - - await wrapper.find( '#post-code' ).setValue( '12345' ); - await wrapper.find( '#post-code' ).trigger( 'blur' ); - - await wrapper.find( '#city' ).setValue( 'city' ); - await wrapper.find( '#city' ).trigger( 'blur' ); - - await wrapper.find( '#country' ).setValue( countries[ 0 ].countryFullName ); - await wrapper.find( '#country' ).trigger( 'blur' ); - - await wrapper.find( '#email' ).setValue( 'joe@dolan.com' ); - await wrapper.find( '#email' ).trigger( 'blur' ); - - await jest.runAllTimersAsync(); - - expect( wrapper.find( '.error-summary' ).exists() ).toBeFalsy(); - } ); - - it( 'submits the form for a person', async () => { - mockedAxios.post.mockResolvedValue( { data: { status: 'OK' } } ); - const wrapper = getWrapper(); - - const submitForm = wrapper.find( '#donation-form-submit-values' ); - submitForm.element.submit = jest.fn(); - - await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="paymentType"][value="BEZ"]' ).trigger( 'change' ); - - await wrapper.find( '#iban' ).setValue( IBAN ); - await wrapper.find( '#iban' ).trigger( 'blur' ); - - await wrapper.find( 'input[name="salutation"][value="Mr"]' ).trigger( 'change' ); - - await wrapper.find( '#first-name' ).setValue( 'first-name' ); - await wrapper.find( '#first-name' ).trigger( 'blur' ); - - await wrapper.find( '#last-name' ).setValue( 'last-name' ); - await wrapper.find( '#last-name' ).trigger( 'blur' ); - - await wrapper.find( '#email' ).setValue( 'joe@dolan.com' ); - await wrapper.find( '#email' ).trigger( 'blur' ); - - await wrapper.find( 'input[name="donationReceipt"][value="true"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="addressTypeSelector"][value="0"]' ).trigger( 'change' ); - - await wrapper.find( '#street' ).setValue( 'street' ); - await wrapper.find( '#street' ).trigger( 'blur' ); - - await wrapper.find( '#post-code' ).setValue( '12345' ); - await wrapper.find( '#post-code' ).trigger( 'blur' ); - - await wrapper.find( '#city' ).setValue( 'city' ); - await wrapper.find( '#city' ).trigger( 'blur' ); - - await jest.runAllTimersAsync(); - - await wrapper.find( '#country' ).setValue( countries[ 0 ].countryFullName ); - await wrapper.find( '#country' ).trigger( 'blur' ); - - await wrapper.find( '#donation-form' ).trigger( 'submit' ); - - await jest.runAllTimersAsync(); - await flushPromises(); - - expect( submitForm.element.submit ).toHaveBeenCalled(); - } ); - - it( 'submits the form for a company', async () => { - mockedAxios.post.mockResolvedValue( { data: { status: 'OK' } } ); - const wrapper = getWrapper(); - - const submitForm = wrapper.find( '#donation-form-submit-values' ); - submitForm.element.submit = jest.fn(); - - await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="paymentType"][value="BEZ"]' ).trigger( 'change' ); - - await wrapper.find( '#iban' ).setValue( IBAN ); - await wrapper.find( '#iban' ).trigger( 'blur' ); - - await wrapper.find( 'input[name="salutation"][value="Mr"]' ).trigger( 'change' ); - - await wrapper.find( '#first-name' ).setValue( 'first-name' ); - await wrapper.find( '#first-name' ).trigger( 'blur' ); - - await wrapper.find( '#last-name' ).setValue( 'last-name' ); - await wrapper.find( '#last-name' ).trigger( 'blur' ); - - await wrapper.find( '#email' ).setValue( 'joe@dolan.com' ); - await wrapper.find( '#email' ).trigger( 'blur' ); - - await wrapper.find( 'input[name="donationReceipt"][value="true"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="addressTypeSelector"][value="2"]' ).trigger( 'change' ); - - await wrapper.find( '#company-name' ).setValue( 'company-name' ); - await wrapper.find( '#company-name' ).trigger( 'blur' ); - - await wrapper.find( '#street' ).setValue( 'street' ); - await wrapper.find( '#street' ).trigger( 'blur' ); - - await wrapper.find( '#post-code' ).setValue( '12345' ); - await wrapper.find( '#post-code' ).trigger( 'blur' ); - - await wrapper.find( '#city' ).setValue( 'city' ); - await wrapper.find( '#city' ).trigger( 'blur' ); - - await jest.runAllTimersAsync(); - - await wrapper.find( '#country' ).setValue( countries[ 0 ].countryFullName ); - await wrapper.find( '#country' ).trigger( 'blur' ); - - await wrapper.find( '#donation-form' ).trigger( 'submit' ); - - await jest.runAllTimersAsync(); - await flushPromises(); - - expect( submitForm.element.submit ).toHaveBeenCalled(); - } ); - - it( 'submits the form for anonymous', async () => { - mockedAxios.post.mockResolvedValue( { data: { status: 'OK' } } ); - const wrapper = getWrapper(); - - const submitForm = wrapper.find( '#donation-form-submit-values' ); - submitForm.element.submit = jest.fn(); - - await wrapper.find( 'input[name="amount"][value="500"]' ).trigger( 'change' ); - await wrapper.find( 'input[name="paymentType"][value="BEZ"]' ).trigger( 'change' ); - - await wrapper.find( '#iban' ).setValue( IBAN ); - await wrapper.find( '#iban' ).trigger( 'blur' ); - - await wrapper.find( 'input[name="salutation"][value="Mr"]' ).trigger( 'change' ); - - await wrapper.find( '#first-name' ).setValue( 'first-name' ); - await wrapper.find( '#first-name' ).trigger( 'blur' ); - - await wrapper.find( '#last-name' ).setValue( 'last-name' ); - await wrapper.find( '#last-name' ).trigger( 'blur' ); - - await wrapper.find( '#email' ).setValue( 'joe@dolan.com' ); - await wrapper.find( '#email' ).trigger( 'blur' ); - - await wrapper.find( 'input[name="donationReceipt"][value="false"]' ).trigger( 'change' ); - - await wrapper.find( '#donation-form' ).trigger( 'submit' ); - - await jest.runAllTimersAsync(); - await flushPromises(); - - expect( submitForm.element.submit ).toHaveBeenCalled(); - } ); -} ); diff --git a/tests/unit/components/pages/donation_form/DonationReceipt/AddressFields.spec.ts b/tests/unit/components/pages/donation_form/DonationReceipt/AddressFields.spec.ts new file mode 100644 index 000000000..95b5eb38a --- /dev/null +++ b/tests/unit/components/pages/donation_form/DonationReceipt/AddressFields.spec.ts @@ -0,0 +1,120 @@ +import { mount, VueWrapper } from '@vue/test-utils'; +import AddressFields from '@src/components/pages/donation_form/DonationReceipt/AddressFields.vue'; +import { AddressTypeModel } from '@src/view_models/AddressTypeModel'; +import { InitialAddressValues } from '@src/view_models/Address'; +import { Validity } from '@src/view_models/Validity'; +import { Store } from 'vuex'; +import { createStore } from '@src/store/donation_store'; +import { action } from '@src/store/util'; +import countries from '@test/data/countries'; + +const initialValues: InitialAddressValues = { + addressType: AddressTypeModel.PERSON, + fields: [ + { name: 'salutation', value: 'Herr', validity: Validity.RESTORED }, + { name: 'title', value: 'Prof. Dr.', validity: Validity.RESTORED }, + { name: 'companyName', value: 'ACME', validity: Validity.RESTORED }, + { name: 'firstName', value: 'Wiley', validity: Validity.RESTORED }, + { name: 'lastName', value: 'Coyote', validity: Validity.RESTORED }, + { name: 'street', value: 'Desert Street', validity: Validity.RESTORED }, + { name: 'city', value: 'The Desert', validity: Validity.RESTORED }, + { name: 'postcode', value: '666', validity: Validity.RESTORED }, + { name: 'country', value: 'ie', validity: Validity.RESTORED }, + { name: 'email', value: 'wiley.coyote@wikimedia.de', validity: Validity.RESTORED }, + ], + newsletter: false, + receipt: true, +}; + +describe( 'AddressFields.vue', () => { + + const getWrapper = ( store: Store = createStore() ): VueWrapper => { + return mount( AddressFields, { + props: { + formData: { + salutation: { name: 'salutation', value: 'Herr', pattern: '', optionalField: false }, + title: { name: 'title', value: 'Prof. Dr.', pattern: '', optionalField: true }, + companyName: { name: 'companyName', value: 'ACME', pattern: '', optionalField: false }, + firstName: { name: 'firstName', value: 'Wiley', pattern: '', optionalField: false }, + lastName: { name: 'lastName', value: 'Coyote', pattern: '', optionalField: false }, + street: { name: 'street', value: 'Desert Street', pattern: '', optionalField: false }, + city: { name: 'city', value: 'The Desert', pattern: '', optionalField: false }, + postcode: { name: 'postcode', value: '666', pattern: '', optionalField: false }, + country: { name: 'country', value: 'IE', pattern: '', optionalField: false }, + email: { name: 'email', value: 'wiley.coyote@wikimedia.de', pattern: '', optionalField: false }, + }, + showError: { + salutation: false, + title: false, + companyName: false, + firstName: false, + lastName: false, + street: false, + city: false, + postcode: false, + country: false, + email: false, + }, + countries, + postCodeValidation: '', + }, + global: { + plugins: [ store ], + }, + } ); + }; + + it( 'restores the country field when mounted', async () => { + const store = createStore(); + await store.dispatch( action( 'address', 'initializeAddress' ), initialValues ); + + const wrapper = getWrapper( store ); + + expect( wrapper.find( '#country' ).element.value ).toStrictEqual( 'Ireland' ); + } ); + + it( 'sets the post code validation pattern when the country field is changed', async () => { + jest.useFakeTimers(); + + const wrapper = getWrapper(); + + await wrapper.find( '#country' ).setValue( 'Ireland' ); + await wrapper.find( '#country' ).trigger( 'blur' ); + await jest.runAllTimersAsync(); + + expect( wrapper.props( 'formData' ).postcode.pattern ).toStrictEqual( '^[0-9]{11}$' ); + + await wrapper.find( '#country' ).setValue( 'Not a country' ); + await wrapper.find( '#country' ).trigger( 'blur' ); + await jest.runAllTimersAsync(); + + expect( wrapper.props( 'formData' ).postcode.pattern ).toStrictEqual( '' ); + expect( wrapper.emitted( 'field-changed' ).length ).toStrictEqual( 4 ); + expect( wrapper.emitted( 'field-changed' )[ 0 ][ 0 ] ).toStrictEqual( 'country' ); + expect( wrapper.emitted( 'field-changed' )[ 1 ][ 0 ] ).toStrictEqual( 'postcode' ); + expect( wrapper.emitted( 'field-changed' )[ 2 ][ 0 ] ).toStrictEqual( 'country' ); + expect( wrapper.emitted( 'field-changed' )[ 3 ][ 0 ] ).toStrictEqual( 'postcode' ); + + jest.restoreAllMocks(); + } ); + + it( 'emits field changes', async () => { + jest.useFakeTimers(); + + const wrapper = getWrapper(); + + await wrapper.find( '#street' ).setValue( 'Sesame' ); + await wrapper.find( '#street' ).trigger( 'blur' ); + + await wrapper.find( '#city' ).setValue( 'Big City' ); + await wrapper.find( '#city' ).trigger( 'blur' ); + + await jest.runAllTimersAsync(); + + expect( wrapper.emitted( 'field-changed' ).length ).toStrictEqual( 2 ); + expect( wrapper.emitted( 'field-changed' )[ 0 ][ 0 ] ).toStrictEqual( 'street' ); + expect( wrapper.emitted( 'field-changed' )[ 1 ][ 0 ] ).toStrictEqual( 'city' ); + + jest.restoreAllMocks(); + } ); +} ); diff --git a/tests/unit/components/pages/donation_form/FormSections/PersonalDataSection.spec.ts b/tests/unit/components/pages/donation_form/FormSections/PersonalDataSection.spec.ts index b856dbfd6..f9de967dd 100644 --- a/tests/unit/components/pages/donation_form/FormSections/PersonalDataSection.spec.ts +++ b/tests/unit/components/pages/donation_form/FormSections/PersonalDataSection.spec.ts @@ -1,17 +1,11 @@ import { mount, VueWrapper } from '@vue/test-utils'; import { createStore } from '@src/store/donation_store'; -import { action } from '@src/store/util'; import { AddressTypeModel } from '@src/view_models/AddressTypeModel'; -import { createFeatureToggle } from '@src/util/createFeatureToggle'; -import { Store } from 'vuex'; import { TrackingData } from '@src/view_models/TrackingData'; import { CampaignValues } from '@src/view_models/CampaignValues'; import { AddressValidation } from '@src/view_models/Validation'; -import { nextTick } from 'vue'; -import AddressTypeBasic from '@src/components/pages/donation_form/AddressTypeBasic.vue'; import { Salutation } from '@src/view_models/Salutation'; import PersonalDataSection from '@src/components/pages/donation_form/FormSections/PersonalDataSection.vue'; -import { FakeBankValidationResource } from '@test/unit/TestDoubles/FakeBankValidationResource'; const testCountry = { countryCode: 'de', @@ -34,76 +28,50 @@ const salutations: Salutation[] = [ ]; describe( 'PersonalDataSection.vue', () => { - const getWrapper = ( store: Store = createStore() ): { wrapper: VueWrapper, store: Store } => { - const wrapper = mount( PersonalDataSection, { + const getWrapper = (): VueWrapper => { + return mount( PersonalDataSection, { props: { - assetsPath: '', - validateAddressUrl: 'https://localhost:8082', - validateEmailUrl: 'https://localhost:8082', - validateBankDataUrl: 'https://localhost:8082', - validateLegacyBankDataUrl: 'https://localhost:8082', countries: [ testCountry ], salutations, trackingData: {} as TrackingData, campaignValues: {} as CampaignValues, addressValidationPatterns: { postcode: '', country: null } as AddressValidation, + isDirectDebitPayment: false, + disabledAddressTypes: [], + addressType: AddressTypeModel.UNSET, + addressTypeIsInvalid: false, }, global: { - plugins: [ store ], - stubs: { - Address: true, - }, - provide: { - bankValidationResource: new FakeBankValidationResource(), - }, - components: { - FeatureToggle: createFeatureToggle( [ 'campaigns.address_type_steps.preselect' ] ), - }, + plugins: [ createStore() ], }, } ); - - return { wrapper, store }; }; - it( 'sets address type in store when it receives address-type event', () => { - const { wrapper, store } = getWrapper(); - - store.dispatch = jest.fn(); - const expectedAction = action( 'address', 'setAddressType' ); - const expectedPayload = AddressTypeModel.ANON; + it( 'emits when it receives address-type event', async () => { + const wrapper = getWrapper(); - wrapper.findComponent( AddressTypeBasic ).vm.$emit( 'address-type', AddressTypeModel.ANON ); + await wrapper.find( '#addressType-1' ).trigger( 'change' ); + await wrapper.find( '#addressType-2' ).trigger( 'change' ); - expect( store.dispatch ).toBeCalledWith( expectedAction, expectedPayload ); + expect( wrapper.emitted( 'set-address-type' ).length ).toStrictEqual( 2 ); + expect( wrapper.emitted( 'set-address-type' )[ 0 ][ 0 ] ).toStrictEqual( AddressTypeModel.COMPANY ); + expect( wrapper.emitted( 'set-address-type' )[ 1 ][ 0 ] ).toStrictEqual( AddressTypeModel.ANON ); } ); - it( 'scrolls to payment section when button for changing payment data is clicked', async () => { - const scrollElement = { scrollIntoView: jest.fn() }; - Object.defineProperty( document, 'getElementById', { writable: true, configurable: true, value: () => scrollElement } ); - - const { wrapper } = getWrapper(); + it( 'Shows the correct form when address type is changed', async () => { + const wrapper = getWrapper(); - await wrapper.find( '#previous-btn' ).trigger( 'click' ); - - expect( scrollElement.scrollIntoView ).toHaveBeenCalledWith( { behavior: 'smooth' } ); - } ); - - it( 'updates full selected', async () => { - const { wrapper } = getWrapper(); - - wrapper.findComponent( AddressTypeBasic ).vm.$emit( 'address-type', AddressTypeModel.PERSON ); - wrapper.findComponent( AddressTypeBasic ).vm.$emit( 'set-full-selected' ); - await nextTick(); + await wrapper.setProps( { addressType: AddressTypeModel.PERSON } ); expect( wrapper.find( '.address-type-person' ).exists() ).toBeTruthy(); - } ); - it( 'validates the payment section input on page submit', async () => { - const { wrapper, store } = getWrapper(); - store.dispatch = jest.fn().mockResolvedValue( true ); + await wrapper.setProps( { addressType: AddressTypeModel.COMPANY } ); + + expect( wrapper.find( '.address-type-company' ).exists() ).toBeTruthy(); - await wrapper.find( '#submit-btn' ).trigger( 'click' ); + await wrapper.setProps( { addressType: AddressTypeModel.ANON } ); - expect( store.dispatch ).toHaveBeenCalledWith( action( 'payment', 'markEmptyValuesAsInvalid' ) ); + expect( wrapper.find( '.address-type-person' ).exists() ).toBeFalsy(); + expect( wrapper.find( '.address-type-company' ).exists() ).toBeFalsy(); } ); } ); diff --git a/tests/unit/components/pages/donation_form/FormSections/PersonalDataSectionDonationReceipt.spec.ts b/tests/unit/components/pages/donation_form/FormSections/PersonalDataSectionDonationReceipt.spec.ts index 807865273..3e069aa27 100644 --- a/tests/unit/components/pages/donation_form/FormSections/PersonalDataSectionDonationReceipt.spec.ts +++ b/tests/unit/components/pages/donation_form/FormSections/PersonalDataSectionDonationReceipt.spec.ts @@ -9,13 +9,12 @@ import { AddressValidation } from '@src/view_models/Validation'; import { Salutation } from '@src/view_models/Salutation'; import PersonalDataSectionDonationReceipt from '@src/components/pages/donation_form/FormSections/PersonalDataSectionDonationReceipt.vue'; import { FakeBankValidationResource } from '@test/unit/TestDoubles/FakeBankValidationResource'; - -const testCountry = { - countryCode: 'de', - countryFullName: 'Germany', - group: '', - postCodeValidation: '', -}; +import { AddressTypeModel } from '@src/view_models/AddressTypeModel'; +import { useReceiptModel } from '@src/components/pages/donation_form/DonationReceipt/useReceiptModel'; +import { InitialAddressValues } from '@src/view_models/Address'; +import { Validity } from '@src/view_models/Validity'; +import countries from '@test/data/countries'; +import { nextTick } from 'vue'; const salutations: Salutation[] = [ { @@ -30,20 +29,38 @@ const salutations: Salutation[] = [ }, ]; +const initialValues: InitialAddressValues = { + addressType: AddressTypeModel.PERSON, + fields: [ + { name: 'salutation', value: 'Herr', validity: Validity.RESTORED }, + { name: 'title', value: 'Prof. Dr.', validity: Validity.RESTORED }, + { name: 'companyName', value: 'ACME', validity: Validity.RESTORED }, + { name: 'firstName', value: 'Wiley', validity: Validity.RESTORED }, + { name: 'lastName', value: 'Coyote', validity: Validity.RESTORED }, + { name: 'country', value: 'IE', validity: Validity.RESTORED }, + { name: 'city', value: 'The Desert', validity: Validity.RESTORED }, + { name: 'postcode', value: '666', validity: Validity.RESTORED }, + { name: 'street', value: 'Desert Street', validity: Validity.RESTORED }, + { name: 'email', value: 'wiley.coyote@wikimedia.de', validity: Validity.RESTORED }, + ], + newsletter: false, + receipt: true, +}; + describe( 'PersonalDataSectionDonationReceipt.vue', () => { - const getWrapper = ( store: Store = createStore() ): { wrapper: VueWrapper, store: Store } => { - const wrapper = mount( PersonalDataSectionDonationReceipt, { + const getWrapper = ( store: Store = createStore() ): VueWrapper => { + return mount( PersonalDataSectionDonationReceipt, { props: { - assetsPath: '', - validateAddressUrl: '', - validateEmailUrl: '', - validateBankDataUrl: 'https://localhost:8082', - validateLegacyBankDataUrl: 'https://localhost:8082', - countries: [ testCountry ], + countries: countries, salutations, trackingData: {} as TrackingData, campaignValues: {} as CampaignValues, - addressValidationPatterns: { postcode: '' } as AddressValidation, + addressValidationPatterns: { postcode: '', salutation: '' } as AddressValidation, + isDirectDebitPayment: false, + disabledAddressTypes: [], + addressType: AddressTypeModel.UNSET, + receiptModel: useReceiptModel( store ), + addressTypeIsInvalid: false, }, global: { plugins: [ store ], @@ -58,27 +75,36 @@ describe( 'PersonalDataSectionDonationReceipt.vue', () => { }, }, } ); - - return { wrapper, store }; }; - it( 'scrolls to payment section when button for changing payment data is clicked', async () => { - const scrollElement = { scrollIntoView: jest.fn() }; - Object.defineProperty( document, 'getElementById', { writable: true, configurable: true, value: () => scrollElement } ); + it( 'initialises the address form data when mounted', async () => { + const store = createStore(); + await store.dispatch( action( 'address', 'initializeAddress' ), initialValues ); + store.dispatch = jest.fn().mockResolvedValue( true ); - const { wrapper } = getWrapper(); + const wrapper = getWrapper( store ); - await wrapper.find( '#previous-btn' ).trigger( 'click' ); + await nextTick(); - expect( scrollElement.scrollIntoView ).toHaveBeenCalledWith( { behavior: 'smooth' } ); + expect( wrapper.find( '#salutation-0' ).element.checked ).toBeTruthy(); + expect( wrapper.find( '#title' ).element.value ).toStrictEqual( 'Prof. Dr.' ); + expect( wrapper.find( '#first-name' ).element.value ).toStrictEqual( 'Wiley' ); + expect( wrapper.find( '#last-name' ).element.value ).toStrictEqual( 'Coyote' ); + expect( wrapper.find( '#email' ).element.value ).toStrictEqual( 'wiley.coyote@wikimedia.de' ); + expect( wrapper.find( '#addressType-0' ).element.checked ).toBeTruthy(); + expect( wrapper.find( '#country' ).element.value ).toStrictEqual( 'Ireland' ); + expect( wrapper.find( '#city' ).element.value ).toStrictEqual( 'The Desert' ); + expect( wrapper.find( '#post-code' ).element.value ).toStrictEqual( '666' ); + expect( wrapper.find( '#street' ).element.value ).toStrictEqual( 'Desert Street' ); } ); - it( 'validates the payment section input on page submit', async () => { - const { wrapper, store } = getWrapper(); - store.dispatch = jest.fn().mockResolvedValue( true ); + it( 'shows the address fields when the donor wants a receipt', async () => { + const wrapper = getWrapper(); + + expect( wrapper.find( '.address-section' ).exists() ).toBeFalsy(); - await wrapper.find( '#donation-form' ).trigger( 'submit' ); + await wrapper.find( '#donationReceipt-0' ).trigger( 'change' ); - expect( store.dispatch ).toHaveBeenCalledWith( action( 'payment', 'markEmptyValuesAsInvalid' ) ); + expect( wrapper.find( '.address-section' ).exists() ).toBeTruthy(); } ); } ); diff --git a/tests/unit/components/pages/donation_form/FormSections/PersonalDataSectionDonationReceiptStreetAutoComplete.spec.ts b/tests/unit/components/pages/donation_form/FormSections/PersonalDataSectionDonationReceiptStreetAutoComplete.spec.ts deleted file mode 100644 index 262c1391f..000000000 --- a/tests/unit/components/pages/donation_form/FormSections/PersonalDataSectionDonationReceiptStreetAutoComplete.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { mount, VueWrapper } from '@vue/test-utils'; -import { createStore } from '@src/store/donation_store'; -import { action } from '@src/store/util'; -import { createFeatureToggle } from '@src/util/createFeatureToggle'; -import { Store } from 'vuex'; -import { TrackingData } from '@src/view_models/TrackingData'; -import { CampaignValues } from '@src/view_models/CampaignValues'; -import { AddressValidation } from '@src/view_models/Validation'; -import { Salutation } from '@src/view_models/Salutation'; -import PersonalDataSectionDonationReceipt from '@src/components/pages/donation_form/FormSections/PersonalDataSectionDonationReceipt.vue'; -import { FakeBankValidationResource } from '@test/unit/TestDoubles/FakeBankValidationResource'; - -const testCountry = { - countryCode: 'de', - countryFullName: 'Germany', - group: '', - postCodeValidation: '', -}; - -const salutations: Salutation[] = [ - { - label: 'Herr', - value: 'Herr', - display: 'Herr', - greetings: { - formal: 'Herr', - informal: 'Herr', - lastNameInformal: 'Herr', - }, - }, -]; - -describe( 'PersonalDataSectionDonationReceipt.vue (With Street Autocomplete)', () => { - const getWrapper = ( store: Store = createStore() ): { wrapper: VueWrapper, store: Store } => { - const wrapper = mount( PersonalDataSectionDonationReceipt, { - props: { - assetsPath: '', - validateAddressUrl: '', - validateEmailUrl: '', - validateBankDataUrl: 'https://localhost:8082', - validateLegacyBankDataUrl: 'https://localhost:8082', - countries: [ testCountry ], - salutations, - trackingData: {} as TrackingData, - campaignValues: {} as CampaignValues, - addressValidationPatterns: { postcode: '' } as AddressValidation, - }, - global: { - plugins: [ store ], - stubs: { - Address: true, - }, - provide: { - bankValidationResource: new FakeBankValidationResource(), - }, - components: { - FeatureToggle: createFeatureToggle( [ - 'campaigns.address_type_steps.preselect', - 'campaigns.address_field_order.new_order', - ] ), - }, - }, - } ); - - return { wrapper, store }; - }; - - it( 'scrolls to payment section when button for changing payment data is clicked', async () => { - const scrollElement = { scrollIntoView: jest.fn() }; - Object.defineProperty( document, 'getElementById', { writable: true, configurable: true, value: () => scrollElement } ); - - const { wrapper } = getWrapper(); - - await wrapper.find( '#previous-btn' ).trigger( 'click' ); - - expect( scrollElement.scrollIntoView ).toHaveBeenCalledWith( { behavior: 'smooth' } ); - } ); - - it( 'validates the payment section input on page submit', async () => { - const { wrapper, store } = getWrapper(); - store.dispatch = jest.fn().mockResolvedValue( true ); - - await wrapper.find( '#donation-form' ).trigger( 'submit' ); - - expect( store.dispatch ).toHaveBeenCalledWith( action( 'payment', 'markEmptyValuesAsInvalid' ) ); - } ); -} ); diff --git a/tests/unit/components/pages/donation_form/FormSections/PersonalDataSectionStreetAutoComplete.spec.ts b/tests/unit/components/pages/donation_form/FormSections/PersonalDataSectionStreetAutoComplete.spec.ts deleted file mode 100644 index 6d5763605..000000000 --- a/tests/unit/components/pages/donation_form/FormSections/PersonalDataSectionStreetAutoComplete.spec.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { mount, VueWrapper } from '@vue/test-utils'; - -import PersonalDataSection from '@src/components/pages/donation_form/FormSections/PersonalDataSection.vue'; -import { createStore } from '@src/store/donation_store'; -import { action } from '@src/store/util'; -import { AddressTypeModel } from '@src/view_models/AddressTypeModel'; -import { createFeatureToggle } from '@src/util/createFeatureToggle'; -import { Store } from 'vuex'; -import { TrackingData } from '@src/view_models/TrackingData'; -import { CampaignValues } from '@src/view_models/CampaignValues'; -import { AddressValidation } from '@src/view_models/Validation'; -import { nextTick } from 'vue'; -import AddressTypeBasic from '@src/components/pages/donation_form/AddressTypeBasic.vue'; -import { Salutation } from '@src/view_models/Salutation'; -import { FakeBankValidationResource } from '@test/unit/TestDoubles/FakeBankValidationResource'; - -const testCountry = { - countryCode: 'de', - countryFullName: 'Germany', - group: '', - postCodeValidation: '', -}; - -const salutations: Salutation[] = [ - { - label: 'Herr', - value: 'Herr', - display: 'Herr', - greetings: { - formal: 'Herr', - informal: 'Herr', - lastNameInformal: 'Herr', - }, - }, -]; - -describe( 'PersonalDataSection.vue (With Street Autocomplete)', () => { - const getWrapper = ( store: Store = createStore() ): { wrapper: VueWrapper, store: Store } => { - const wrapper = mount( PersonalDataSection, { - props: { - assetsPath: '', - validateAddressUrl: '', - validateEmailUrl: '', - validateBankDataUrl: '', - validateLegacyBankDataUrl: '', - countries: [ testCountry ], - salutations, - trackingData: {} as TrackingData, - campaignValues: {} as CampaignValues, - addressValidationPatterns: { postcode: '', country: null } as AddressValidation, - }, - global: { - plugins: [ store ], - stubs: { - Address: true, - }, - provide: { - bankValidationResource: new FakeBankValidationResource(), - }, - components: { - FeatureToggle: createFeatureToggle( [ - 'campaigns.address_type_steps.preselect', - 'campaigns.address_field_order.new_order', - ] ), - }, - }, - } ); - - return { wrapper, store }; - }; - - it( 'sets address type in store when it receives address-type event', () => { - const { wrapper, store } = getWrapper(); - - store.dispatch = jest.fn(); - const expectedAction = action( 'address', 'setAddressType' ); - const expectedPayload = AddressTypeModel.ANON; - - wrapper.findComponent( AddressTypeBasic ).vm.$emit( 'address-type', AddressTypeModel.ANON ); - - expect( store.dispatch ).toBeCalledWith( expectedAction, expectedPayload ); - } ); - - it( 'scrolls to top when the donor clicks the previous button', async () => { - const scrollElement = { scrollIntoView: jest.fn() }; - Object.defineProperty( document, 'getElementById', { writable: true, configurable: true, value: () => scrollElement } ); - - const { wrapper } = getWrapper(); - - await wrapper.find( '#previous-btn' ).trigger( 'click' ); - - expect( scrollElement.scrollIntoView ).toHaveBeenCalledWith( { behavior: 'smooth' } ); - } ); - - it( 'updates full selected', async () => { - const { wrapper } = getWrapper(); - - wrapper.findComponent( AddressTypeBasic ).vm.$emit( 'address-type', AddressTypeModel.PERSON ); - wrapper.findComponent( AddressTypeBasic ).vm.$emit( 'set-full-selected' ); - await nextTick(); - - expect( wrapper.find( '.address-type-person' ).exists() ).toBeTruthy(); - } ); - - it( 'validates the payment section input on page submit', async () => { - const { wrapper, store } = getWrapper(); - store.dispatch = jest.fn().mockResolvedValue( true ); - - await wrapper.find( '#submit-btn' ).trigger( 'click' ); - - expect( store.dispatch ).toHaveBeenCalledWith( action( 'payment', 'markEmptyValuesAsInvalid' ) ); - } ); -} ); diff --git a/tests/unit/store/address_store.spec.ts b/tests/unit/store/address_store.spec.ts index 20d76b282..74ae1475c 100644 --- a/tests/unit/store/address_store.spec.ts +++ b/tests/unit/store/address_store.spec.ts @@ -490,6 +490,44 @@ describe( 'Address', () => { } ); } ); + describe( 'Actions/adjustSalutationLocale', () => { + it( 'does not adjust when salutation is empty', () => { + const commit = jest.fn(); + const action = actions.adjustSalutationLocale as any; + const salutations = [ { value: 'Mr' }, { value: 'Ms' } ]; + + action( { commit }, { salutations, salutation: '' } ); + + expect( commit ).not.toHaveBeenCalled(); + } ); + + it( 'adjusts the salutation when it finds it in the server salutations array', () => { + const commit = jest.fn(); + const action = actions.adjustSalutationLocale as any; + const salutations = [ { value: 'Mr' }, { value: 'Ms' } ]; + + action( { commit }, { salutations, salutation: 'Mr' } ); + + expect( commit ).toHaveBeenCalledWith( + 'SET_SALUTATION', + 'Mr' + ); + } ); + + it( 'adjusts the salutation when it finds it in the local translations array', () => { + const commit = jest.fn(); + const action = actions.adjustSalutationLocale as any; + const salutations = [ { value: 'Mr' }, { value: 'Ms' } ]; + + action( { commit }, { salutations, salutation: 'No Salutation' } ); + + expect( commit ).toHaveBeenCalledWith( + 'SET_SALUTATION', + 'Keine Anrede' + ); + } ); + } ); + describe( 'Mutations/VALIDATE_INPUT', () => { it( 'sets validity to valid for optional unfilled fields', () => { const inputField = {