From 972ad64e7acb48454d6b629d6ca9d056909709ce Mon Sep 17 00:00:00 2001 From: Corinna Hillebrand Date: Tue, 14 Nov 2023 14:06:11 +0100 Subject: [PATCH 1/4] Archive C23_WMDE_Desktop_DE_12 --- .../C23_WMDE_Desktop_DE_12/banner_ctrl.ts | 67 ++++++++ .../C23_WMDE_Desktop_DE_12/banner_var.ts | 67 ++++++++ .../components/BannerCtrl.vue | 152 ++++++++++++++++++ .../components/BannerVar.vue | 152 ++++++++++++++++++ .../components/MainBanner.vue | 44 +++++ .../content/AlreadyDonatedContent.vue | 14 ++ .../content/BannerSlides.vue | 51 ++++++ .../content/BannerSlidesVar.vue | 51 ++++++ .../content/BannerText.vue | 34 ++++ .../content/BannerTextVar.vue | 34 ++++ .../C23_WMDE_Desktop_DE_12/event_map.ts | 32 ++++ .../C23_WMDE_Desktop_DE_12/form_items.ts | 23 +++ .../C23_WMDE_Desktop_DE_12/messages.ts | 23 +++ .../C23_WMDE_Desktop_DE_12/styles/Banner.scss | 17 ++ .../styles/MainBanner.scss | 45 ++++++ .../C23_WMDE_Desktop_DE_12/styles/styles.scss | 36 +++++ 16 files changed, 842 insertions(+) create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/banner_ctrl.ts create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/banner_var.ts create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/components/BannerCtrl.vue create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/components/BannerVar.vue create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/components/MainBanner.vue create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/content/AlreadyDonatedContent.vue create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerSlides.vue create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerSlidesVar.vue create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerText.vue create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerTextVar.vue create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/event_map.ts create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/form_items.ts create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/messages.ts create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/styles/Banner.scss create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/styles/MainBanner.scss create mode 100644 archive/desktop/C23_WMDE_Desktop_DE_12/styles/styles.scss diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/banner_ctrl.ts b/archive/desktop/C23_WMDE_Desktop_DE_12/banner_ctrl.ts new file mode 100644 index 000000000..770b65a85 --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/banner_ctrl.ts @@ -0,0 +1,67 @@ +import { createVueApp } from '@src/createVueApp'; + +import './styles/styles.scss'; + +import BannerConductor from '@src/components/BannerConductor/BannerConductor.vue'; +import Banner from './components/BannerCtrl.vue'; +import getBannerDelay from '@src/utils/getBannerDelay'; +import { WindowResizeHandler } from '@src/utils/ResizeHandler'; +import PageWPORG from '@src/page/PageWPORG'; +import { WindowMediaWiki } from '@src/page/MediaWiki/WindowMediaWiki'; +import { SkinFactory } from '@src/page/skin/SkinFactory'; +import { WindowSizeIssueChecker } from '@src/utils/SizeIssueChecker/WindowSizeIssueChecker'; +import TranslationPlugin from '@src/TranslationPlugin'; +import { Translator } from '@src/Translator'; +import DynamicTextPlugin from '@src/DynamicTextPlugin'; +import { LocalImpressionCount } from '@src/utils/LocalImpressionCount'; +import { LegacyTrackerWPORG } from '@src/tracking/LegacyTrackerWPORG'; +import eventMappings from './event_map'; + +// Locale-specific imports +import messages from './messages'; +import { LocaleFactoryDe } from '@src/utils/LocaleFactory/LocaleFactoryDe'; + +// Channel specific form setup +import { createFormItems } from './form_items'; +import { createFormActions } from '@src/createFormActions'; + +const localeFactory = new LocaleFactoryDe(); +const translator = new Translator( messages ); +const mediaWiki = new WindowMediaWiki(); +const page = new PageWPORG( mediaWiki, ( new SkinFactory( mediaWiki ) ).getSkin(), new WindowSizeIssueChecker( 800 ) ); +const impressionCount = new LocalImpressionCount( page.getTracking().keyword ); +const tracker = new LegacyTrackerWPORG( mediaWiki, page.getTracking().keyword, eventMappings ); +const remainingImpressions = Math.max( page.getMaxBannerImpressions( 'desktop' ) - impressionCount.overallCountIncremented, 0 ); + +const app = createVueApp( BannerConductor, { + page, + bannerConfig: { + delay: getBannerDelay( 7500 ), + transitionDuration: 1000 + }, + bannerProps: { + useOfFundsContent: localeFactory.getUseOfFundsLoader().getContent(), + remainingImpressions + }, + resizeHandler: new WindowResizeHandler(), + banner: Banner, + impressionCount +} ); + +app.use( TranslationPlugin, translator ); +app.use( DynamicTextPlugin, { + campaignParameters: page.getCampaignParameters(), + date: new Date(), + formatters: localeFactory.getFormatters(), + impressionCount, + translator +} ); + +const currencyFormatter = localeFactory.getCurrencyFormatter(); + +app.provide( 'currencyFormatter', currencyFormatter ); +app.provide( 'formItems', createFormItems( translator, currencyFormatter.euroAmount.bind( currencyFormatter ) ) ); +app.provide( 'formActions', createFormActions( page.getTracking(), impressionCount, { des: '1' } ) ); +app.provide( 'tracker', tracker ); + +app.mount( page.getBannerContainer() ); diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/banner_var.ts b/archive/desktop/C23_WMDE_Desktop_DE_12/banner_var.ts new file mode 100644 index 000000000..d4798085c --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/banner_var.ts @@ -0,0 +1,67 @@ +import { createVueApp } from '@src/createVueApp'; + +import './styles/styles.scss'; + +import BannerConductor from '@src/components/BannerConductor/BannerConductor.vue'; +import Banner from './components/BannerVar.vue'; +import getBannerDelay from '@src/utils/getBannerDelay'; +import { WindowResizeHandler } from '@src/utils/ResizeHandler'; +import PageWPORG from '@src/page/PageWPORG'; +import { WindowMediaWiki } from '@src/page/MediaWiki/WindowMediaWiki'; +import { SkinFactory } from '@src/page/skin/SkinFactory'; +import { WindowSizeIssueChecker } from '@src/utils/SizeIssueChecker/WindowSizeIssueChecker'; +import TranslationPlugin from '@src/TranslationPlugin'; +import { Translator } from '@src/Translator'; +import DynamicTextPlugin from '@src/DynamicTextPlugin'; +import { LocalImpressionCount } from '@src/utils/LocalImpressionCount'; +import { LegacyTrackerWPORG } from '@src/tracking/LegacyTrackerWPORG'; +import eventMappings from './event_map'; + +// Locale-specific imports +import messages from './messages'; +import { LocaleFactoryDe } from '@src/utils/LocaleFactory/LocaleFactoryDe'; + +// Channel specific form setup +import { createFormItems } from './form_items'; +import { createFormActions } from '@src/createFormActions'; + +const localeFactory = new LocaleFactoryDe(); +const translator = new Translator( messages ); +const mediaWiki = new WindowMediaWiki(); +const page = new PageWPORG( mediaWiki, ( new SkinFactory( mediaWiki ) ).getSkin(), new WindowSizeIssueChecker( 800 ) ); +const impressionCount = new LocalImpressionCount( page.getTracking().keyword ); +const tracker = new LegacyTrackerWPORG( mediaWiki, page.getTracking().keyword, eventMappings ); +const remainingImpressions = Math.max( page.getMaxBannerImpressions( 'desktop' ) - impressionCount.overallCountIncremented, 0 ); + +const app = createVueApp( BannerConductor, { + page, + bannerConfig: { + delay: getBannerDelay( 7500 ), + transitionDuration: 1000 + }, + bannerProps: { + useOfFundsContent: localeFactory.getUseOfFundsLoader().getContent(), + remainingImpressions + }, + resizeHandler: new WindowResizeHandler(), + banner: Banner, + impressionCount +} ); + +app.use( TranslationPlugin, translator ); +app.use( DynamicTextPlugin, { + campaignParameters: page.getCampaignParameters(), + date: new Date(), + formatters: localeFactory.getFormatters(), + impressionCount, + translator +} ); + +const currencyFormatter = localeFactory.getCurrencyFormatter(); + +app.provide( 'currencyFormatter', currencyFormatter ); +app.provide( 'formItems', createFormItems( translator, currencyFormatter.euroAmount.bind( currencyFormatter ) ) ); +app.provide( 'formActions', createFormActions( page.getTracking(), impressionCount, { des: '1' } ) ); +app.provide( 'tracker', tracker ); + +app.mount( page.getBannerContainer() ); diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/components/BannerCtrl.vue b/archive/desktop/C23_WMDE_Desktop_DE_12/components/BannerCtrl.vue new file mode 100644 index 000000000..6166fa51c --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/components/BannerCtrl.vue @@ -0,0 +1,152 @@ + + + diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/components/BannerVar.vue b/archive/desktop/C23_WMDE_Desktop_DE_12/components/BannerVar.vue new file mode 100644 index 000000000..f2028754c --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/components/BannerVar.vue @@ -0,0 +1,152 @@ + + + diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/components/MainBanner.vue b/archive/desktop/C23_WMDE_Desktop_DE_12/components/MainBanner.vue new file mode 100644 index 000000000..ab097bf67 --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/components/MainBanner.vue @@ -0,0 +1,44 @@ +tart + + diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/content/AlreadyDonatedContent.vue b/archive/desktop/C23_WMDE_Desktop_DE_12/content/AlreadyDonatedContent.vue new file mode 100644 index 000000000..030127d52 --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/content/AlreadyDonatedContent.vue @@ -0,0 +1,14 @@ + diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerSlides.vue b/archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerSlides.vue new file mode 100644 index 000000000..1bda195d8 --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerSlides.vue @@ -0,0 +1,51 @@ + + + diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerSlidesVar.vue b/archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerSlidesVar.vue new file mode 100644 index 000000000..74950ba14 --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerSlidesVar.vue @@ -0,0 +1,51 @@ + + + diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerText.vue b/archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerText.vue new file mode 100644 index 000000000..388cdb47a --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerText.vue @@ -0,0 +1,34 @@ + + + diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerTextVar.vue b/archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerTextVar.vue new file mode 100644 index 000000000..a11df4d94 --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/content/BannerTextVar.vue @@ -0,0 +1,34 @@ + + + diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/event_map.ts b/archive/desktop/C23_WMDE_Desktop_DE_12/event_map.ts new file mode 100644 index 000000000..b4f7e49dd --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/event_map.ts @@ -0,0 +1,32 @@ +import { TrackingEventConverterFactory } from '@src/tracking/LegacyTrackerWPORG'; +import { WMDELegacyBannerEvent } from '@src/tracking/WPORG/WMDELegacyBannerEvent'; +import { WMDESizeIssueEvent } from '@src/tracking/WPORG/WMDEBannerSizeIssue'; +import { BannerSubmitEvent } from '@src/tracking/events/BannerSubmitEvent'; +import { FormStepShownEvent } from '@src/tracking/events/FormStepShownEvent'; +import { mapFormStepShownEvent } from '@src/tracking/LegacyEventTracking/mapFormStepShownEvent'; +import { CustomAmountChangedEvent } from '@src/tracking/events/CustomAmountChangedEvent'; +import { CloseEvent } from '@src/tracking/events/CloseEvent'; +import { mapCloseEvent } from '@src/tracking/LegacyEventTracking/mapCloseEvent'; +import { NotShownEvent } from '@src/tracking/events/NotShownEvent'; +import { mapNotShownEvent } from '@src/tracking/LegacyEventTracking/mapNotShownEvent'; +import { createViewportInfo } from '@src/tracking/LegacyEventTracking/createViewportInfo'; +import { AlreadyDonatedShownEvent } from '@src/tracking/events/AlreadyDonatedShownEvent'; + +export default new Map( [ + [ CloseEvent.EVENT_NAME, mapCloseEvent ], + [ FormStepShownEvent.EVENT_NAME, mapFormStepShownEvent ], + [ CustomAmountChangedEvent.EVENT_NAME, + ( e: CustomAmountChangedEvent ): WMDELegacyBannerEvent => + new WMDELegacyBannerEvent( e.userChoice + '-amount', 1 ) + ], + [ AlreadyDonatedShownEvent.EVENT_NAME, ( e: AlreadyDonatedShownEvent ): WMDELegacyBannerEvent => new WMDELegacyBannerEvent( e.eventName, 1 ) ], + [ NotShownEvent.EVENT_NAME, mapNotShownEvent ], + [ BannerSubmitEvent.EVENT_NAME, ( e: BannerSubmitEvent ): WMDESizeIssueEvent => { + switch ( e.feature ) { + case 'UpgradeToYearlyForm': + return new WMDESizeIssueEvent( `submit-${e.userChoice}`, createViewportInfo(), 1 ); + default: + return new WMDESizeIssueEvent( `submit`, createViewportInfo(), 1 ); + } + } ] +] ); diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/form_items.ts b/archive/desktop/C23_WMDE_Desktop_DE_12/form_items.ts new file mode 100644 index 000000000..b70650d19 --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/form_items.ts @@ -0,0 +1,23 @@ +import FormItemsBuilder from '@src/utils/FormItemsBuilder/FormItemsBuilder'; +import { Translator } from '@src/Translator'; +import { DonationFormItems } from '@src/utils/FormItemsBuilder/DonationFormItems'; +import { Intervals } from '@src/utils/FormItemsBuilder/fields/Intervals'; +import { PaymentMethods } from '@src/utils/FormItemsBuilder/fields/PaymentMethods'; +import { NumberFormatter } from '@src/utils/DynamicContent/formatters/NumberFormatter'; + +export function createFormItems( translations: Translator, amountFormatter: NumberFormatter ): DonationFormItems { + return new FormItemsBuilder( translations, amountFormatter ) + .setIntervals( + Intervals.ONCE, + Intervals.MONTHLY, + Intervals.QUARTERLY, + Intervals.YEARLY + ) + .setAmounts( 5, 10, 20, 25, 50, 100 ) + .setPaymentMethods( + PaymentMethods.PAYPAL, + PaymentMethods.BANK_TRANSFER, + PaymentMethods.CREDIT_CARD, + PaymentMethods.DIRECT_DEBIT + ).getItems(); +} diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/messages.ts b/archive/desktop/C23_WMDE_Desktop_DE_12/messages.ts new file mode 100644 index 000000000..cef9a830d --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/messages.ts @@ -0,0 +1,23 @@ +import CustomAmountFormDe from '@src/components/DonationForm/Forms/messages/CustomAmountForm.de'; +import DynamicCampaignTextDe from '@src/utils/DynamicContent/messages/DynamicCampaignText.de'; +import { TranslationMessages } from '@src/Translator'; +import UpgradeToYearlyDe from '@src/components/DonationForm/Forms/messages/UpgradeToYearly.de'; +import SoftCloseDe from '@src/components/SoftClose/messages/SoftClose.de'; +import AddressFormDe from '@src/components/DonationForm/Forms/messages/AddressForm.de'; +import FooterDe from '@src/components/Footer/messages/Footer.de'; +import MainDonationFormDe from '@src/components/DonationForm/Forms/messages/MainDonationForm.de'; +import AlreadyDonatedModal from '@src/components/AlreadyDonatedModal/translations/AlreadyDonatedModal.de'; + +const messages: TranslationMessages = { + ...CustomAmountFormDe, + ...DynamicCampaignTextDe, + ...UpgradeToYearlyDe, + ...SoftCloseDe, + ...AddressFormDe, + ...FooterDe, + ...MainDonationFormDe, + ...AlreadyDonatedModal, + 'already-donated-go-away-button': 'Fürs Erste reicht es' +}; + +export default messages; diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/styles/Banner.scss b/archive/desktop/C23_WMDE_Desktop_DE_12/styles/Banner.scss new file mode 100644 index 000000000..06e30756a --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/styles/Banner.scss @@ -0,0 +1,17 @@ +@use 'src/themes/Treedip/variables/fonts'; +@use 'src/themes/Treedip/variables/colors'; + +.wmde-banner { + &-wrapper { + font-size: 14px; + font-family: fonts.$ui; + box-shadow: 0 3px 0.6em rgba( 60 60 60 / 40% ); + background-color: colors.$white; + } + + &--closed { + .wmde-banner-wrapper { + display: none; + } + } +} diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/styles/MainBanner.scss b/archive/desktop/C23_WMDE_Desktop_DE_12/styles/MainBanner.scss new file mode 100644 index 000000000..c328cbca9 --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/styles/MainBanner.scss @@ -0,0 +1,45 @@ +@use 'src/themes/Treedip/variables/colors'; + +$banner-height: 357px !default; +$form-width: 300px !default; + +.wmde-banner { + &-main { + min-height: $banner-height; + display: flex; + flex-direction: column; + padding: 12px 24px 0; + } + + &-content { + display: flex; + flex-direction: row; + flex-grow: 1; + } + + &-message { + padding: 0 15px; + } + + &-column-left { + display: flex; + flex-direction: column; + justify-content: center; + flex: 1 1 auto; + margin-bottom: 0; + overflow-y: hidden; + margin-right: 30px; + padding: 0 0 10px; + border: 5px solid colors.$primary; + border-radius: 9px; + } + + &-column-right { + order: 2; + flex: 0 0 $form-width; + display: flex; + flex-direction: column; + width: $form-width; + padding: 10px 0; + } +} diff --git a/archive/desktop/C23_WMDE_Desktop_DE_12/styles/styles.scss b/archive/desktop/C23_WMDE_Desktop_DE_12/styles/styles.scss new file mode 100644 index 000000000..6cd8308ba --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_12/styles/styles.scss @@ -0,0 +1,36 @@ +// This is the file where we import the theme-specific component styles +$font: 'Iowan Old Style', 'Sitka Text', 'Cambria', serif; + +@use 'src/components/BannerConductor/banner-transition'; +@use 'Banner'; +@use 'MainBanner' with ( + $banner-height: 357px, + $form-width: 300px +); +@use 'src/themes/UseOfFunds/UseOfFunds'; +@use 'src/themes/Treedip/defaults'; +@use 'src/themes/Treedip/ButtonClose/ButtonClose'; +@use 'src/themes/Treedip/ProgressBar/ProgressBar' with ( + $progress-bar-margin: 0 15px +); +@use 'src/themes/Treedip/DonationForm/DonationForm'; +@use 'src/themes/Treedip/DonationForm/MultiStepDonation'; +@use 'src/themes/Treedip/DonationForm/SubComponents/SelectGroup'; +@use 'src/themes/Treedip/DonationForm/SubComponents/SelectGroupRadios'; +@use 'src/themes/Treedip/DonationForm/SubComponents/SelectCustomAmountRadio'; +@use 'src/themes/Treedip/DonationForm/SubComponents/SmsBox'; +@use 'src/themes/Treedip/DonationForm/Forms/MainDonationForm'; +@use 'src/themes/Treedip/DonationForm/Forms/UpgradeToYearlyForm'; +@use 'src/themes/Treedip/DonationForm/Forms/CustomAmountForm'; +@use 'src/themes/Treedip/Footer/FooterAlreadyDonated' with ( + $right-column-width: 300px +); +@use 'src/themes/Treedip/Footer/SelectionInput'; +@use 'src/themes/Treedip/Message/Message' with ( + $font: $font +); +@use 'src/themes/Treedip/Slider/KeenSlider' with ( + $font: $font +); +@use 'src/themes/Treedip/SoftClose/SoftClose'; +@use 'src/themes/Treedip/AlreadyDonatedModal/AlreadyDonatedModal'; From d202be00385fd78ef9a19e880b18701923e22f46 Mon Sep 17 00:00:00 2001 From: Corinna Hillebrand Date: Tue, 14 Nov 2023 17:21:41 +0100 Subject: [PATCH 2/4] Prepare campaign for C23_WMDE_Desktop_DE_13 - based on ctrl 12 - adds close button for softclose for both banners - the new x close button prevents banners for 7 days - clicking on the maybelater button on alreadydonated hides banners for 7 days https://phabricator.wikimedia.org/T351199 --- banners/desktop/components/BannerCtrl.vue | 5 +- banners/desktop/components/BannerVar.vue | 47 ++++++++-- .../components/SoftCloseWithXButton.vue | 86 +++++++++++++++++++ banners/desktop/content/BannerSlidesVar.vue | 51 ----------- banners/desktop/content/BannerTextVar.vue | 34 -------- banners/desktop/messages.ts | 2 +- .../SoftCloseWithXButtonOverwrites.scss | 16 ++++ banners/desktop/styles/styles.scss | 1 + campaign_info.toml | 14 +-- .../Forms/messages/UpgradeToYearly.de.ts | 2 +- src/components/SoftClose/SoftClose.vue | 2 +- src/domain/CloseChoices.ts | 2 +- .../dev/CampaignParameterOverride.ts | 4 +- src/tracking/LegacyCloseSources.ts | 3 + .../LegacyEventTracking/mapCloseEvent.ts | 9 ++ src/tracking/TrackingEvent.ts | 1 + src/tracking/events/UpgradeToMonthlyEvent.ts | 14 +++ 17 files changed, 184 insertions(+), 109 deletions(-) create mode 100644 banners/desktop/components/SoftCloseWithXButton.vue delete mode 100644 banners/desktop/content/BannerSlidesVar.vue delete mode 100644 banners/desktop/content/BannerTextVar.vue create mode 100644 banners/desktop/styles/SoftCloseWithXButtonOverwrites.scss create mode 100644 src/tracking/events/UpgradeToMonthlyEvent.ts diff --git a/banners/desktop/components/BannerCtrl.vue b/banners/desktop/components/BannerCtrl.vue index 6166fa51c..2fdbc79cd 100644 --- a/banners/desktop/components/BannerCtrl.vue +++ b/banners/desktop/components/BannerCtrl.vue @@ -55,6 +55,7 @@ @close="() => onClose( 'SoftClose', CloseChoices.Close )" @maybeLater="() => onClose( 'SoftClose', CloseChoices.MaybeLater )" @timeOutClose="() => onClose( 'SoftClose', CloseChoices.TimeOut )" + @maybeLater7Days="() => onClose('SoftClose', CloseChoices.Close)" /> + + @@ -55,6 +59,7 @@ @close="() => onClose( 'SoftClose', CloseChoices.Close )" @maybeLater="() => onClose( 'SoftClose', CloseChoices.MaybeLater )" @timeOutClose="() => onClose( 'SoftClose', CloseChoices.TimeOut )" + @maybeLater7Days="() => onClose('SoftClose', CloseChoices.Close)" /> @@ -59,7 +69,6 @@ @close="() => onClose( 'SoftClose', CloseChoices.Close )" @maybeLater="() => onClose( 'SoftClose', CloseChoices.MaybeLater )" @timeOutClose="() => onClose( 'SoftClose', CloseChoices.TimeOut )" - @maybeLater7Days="() => onClose('SoftClose', CloseChoices.Close)" /> import { BannerStates } from '@src/components/BannerConductor/StateMachine/BannerStates'; -import { computed, ref, watch } from 'vue'; +import { ref, watch } from 'vue'; import { UseOfFundsContent as useOfFundsContentInterface } from '@src/domain/UseOfFunds/UseOfFundsContent'; import SoftClose from './SoftCloseWithXButton.vue'; import MainBanner from './MainBanner.vue'; @@ -98,8 +107,8 @@ import UpgradeToMonthlyForm from '@src/components/DonationForm/Forms/UpgradeToMo import KeenSlider from '@src/components/Slider/KeenSlider.vue'; import { useFormModel } from '@src/components/composables/useFormModel'; import { - createSubmittableMainDonationForm -} from '@src/components/DonationForm/StepControllers/SubmittableMainDonationForm'; + createSubmittableMainDonationFormUpgradeOptions +} from '@src/components/DonationForm/StepControllers/SubmittableMainDonationFormUpgradeOptions'; import { createSubmittableUpgradeToYearly } from '@src/components/DonationForm/StepControllers/SubmittableUpgradeToYearly'; @@ -139,29 +148,18 @@ const isAlreadyDonatedModalVisible = ref( false ); const contentState = ref( ContentStates.Main ); const formModel = useFormModel(); -const shouldSuggestMonthly = computed( (): boolean => formModel.numericAmount.value >= 11 && formModel.numericAmount.value <= 100 ); - let stepControllers = [ - createSubmittableMainDonationForm( formModel, FormStepNames.UpgradeToMonthlyFormStep ), + createSubmittableMainDonationFormUpgradeOptions( + formModel, + FormStepNames.UpgradeToYearlyFormStep, + FormStepNames.UpgradeToMonthlyFormStep, + 11, + 100 + ), + createSubmittableUpgradeToYearly( formModel, FormStepNames.MainDonationFormStep, FormStepNames.MainDonationFormStep ), createSubmittableUpgradeToMonthly( formModel, FormStepNames.MainDonationFormStep, FormStepNames.MainDonationFormStep ) ]; -watch( formModel.numericAmount, () => { - if ( shouldSuggestMonthly.value ) { - console.log("amount is bigger than 11") - stepControllers = [ - createSubmittableMainDonationForm( formModel, FormStepNames.UpgradeToMonthlyFormStep ), - createSubmittableUpgradeToMonthly( formModel, FormStepNames.MainDonationFormStep, FormStepNames.MainDonationFormStep ) - ]; - } else { - console.log("amount is yearly") - stepControllers = [ - createSubmittableMainDonationForm( formModel, FormStepNames.UpgradeToYearlyFormStep ), - createSubmittableUpgradeToYearly( formModel, FormStepNames.MainDonationFormStep, FormStepNames.MainDonationFormStep ) - ]; - } -} ); - watch( contentState, async () => { emit( 'bannerContentChanged' ); } ); diff --git a/banners/desktop/components/SoftCloseWithXButton.vue b/banners/desktop/components/SoftCloseWithXButton.vue index 68518cd32..313754dd1 100644 --- a/banners/desktop/components/SoftCloseWithXButton.vue +++ b/banners/desktop/components/SoftCloseWithXButton.vue @@ -31,7 +31,7 @@ - + @@ -52,7 +52,7 @@ const props = withDefaults( defineProps(), { const timer = ref( 0 ); const secondsRemaining = ref( props.secondsTotal ); -const emit = defineEmits( [ 'close', 'maybeLater', 'timeOutClose', 'maybeLater7Days' ] ); +const emit = defineEmits( [ 'close', 'maybeLater', 'timeOutClose' ] ); const onMaybeLaterClick = (): void => { window.clearInterval( timer.value ); @@ -64,11 +64,6 @@ const onCloseClick = (): void => { emit( 'close' ); }; -const onXButtonClick = (): void => { - window.clearInterval( timer.value ); - emit( 'maybeLater7Days' ); -}; - onMounted( () => { timer.value = window.setInterval( () => { secondsRemaining.value = secondsRemaining.value - 1; diff --git a/banners/desktop/content/AlreadyDonatedContent.vue b/banners/desktop/content/AlreadyDonatedContent.vue index 030127d52..eb8eb8f8f 100644 --- a/banners/desktop/content/AlreadyDonatedContent.vue +++ b/banners/desktop/content/AlreadyDonatedContent.vue @@ -7,7 +7,7 @@

Falls Sie sich eine (weitere) Spende vorstellen können, dann klicken Sie "Vielleicht später". Falls - nicht, klicken Sie: "Fürs Erste reicht es". In diesem Fall wird ein Cookie in Ihrem Browser gesetzt, + nicht, klicken Sie: "Im Moment nicht". In diesem Fall wird ein Cookie in Ihrem Browser gesetzt, der die Anzeige der Banner vorerst verhindert.

diff --git a/banners/desktop/event_map.ts b/banners/desktop/event_map.ts index b4f7e49dd..76469826d 100644 --- a/banners/desktop/event_map.ts +++ b/banners/desktop/event_map.ts @@ -25,6 +25,8 @@ export default new Map( [ switch ( e.feature ) { case 'UpgradeToYearlyForm': return new WMDESizeIssueEvent( `submit-${e.userChoice}`, createViewportInfo(), 1 ); + case 'UpgradeToMonthlyForm': + return new WMDESizeIssueEvent( `submit-${e.userChoice}`, createViewportInfo(), 1 ); default: return new WMDESizeIssueEvent( `submit`, createViewportInfo(), 1 ); } diff --git a/banners/desktop/messages.ts b/banners/desktop/messages.ts index 2e2c04350..4cde279fe 100644 --- a/banners/desktop/messages.ts +++ b/banners/desktop/messages.ts @@ -2,6 +2,7 @@ import CustomAmountFormDe from '@src/components/DonationForm/Forms/messages/Cust import DynamicCampaignTextDe from '@src/utils/DynamicContent/messages/DynamicCampaignText.de'; import { TranslationMessages } from '@src/Translator'; import UpgradeToYearlyDe from '@src/components/DonationForm/Forms/messages/UpgradeToYearly.de'; +import UpgradeToMonthlyDe from '@src/components/DonationForm/Forms/messages/UpgradeToMonthly.de'; import SoftCloseDe from '@src/components/SoftClose/messages/SoftClose.de'; import AddressFormDe from '@src/components/DonationForm/Forms/messages/AddressForm.de'; import FooterDe from '@src/components/Footer/messages/Footer.de'; @@ -12,6 +13,7 @@ const messages: TranslationMessages = { ...CustomAmountFormDe, ...DynamicCampaignTextDe, ...UpgradeToYearlyDe, + ...UpgradeToMonthlyDe, ...SoftCloseDe, ...AddressFormDe, ...FooterDe, diff --git a/campaign_info.toml b/campaign_info.toml index c604c35fb..8a577fcd8 100644 --- a/campaign_info.toml +++ b/campaign_info.toml @@ -8,7 +8,7 @@ name = "Desktop" icon = "desktop" campaign = "C23_WMDE_Desktop_DE_13" description = "based on ctrl of test 12, variant asks for monthly donations" -campaign_tracking = "13-ba-231116" +campaign_tracking = "13-ba-231117" preview_link = "/wiki/Wikipedia:Hauptseite?devbanner={{banner}}&banner=B22_WMDE_local_prototype" preview_url = 'https://de.wikipedia.org/wiki/Wikipedia:Hauptseite?banner={{banner}}&devMode' wrapper_template = "wikipedia_org" @@ -18,12 +18,12 @@ use_of_funds_source = "MediaWiki:WMDE_Fundraising/UseOfFunds_2023_DE" [desktop.banners.ctrl] filename = "./banners/desktop/banner_ctrl.ts" pagename = "B23_WMDE_Desktop_DE_13_ctrl" -tracking = "org-13-231116-ctrl" +tracking = "org-13-231117-ctrl" [desktop.banners.var] filename = "./banners/desktop/banner_var.ts" pagename = "B23_WMDE_Desktop_DE_13_var" -tracking = "org-13-231116-var" +tracking = "org-13-231117-var" [desktop.test_matrix] platform = ["edge", "firefox_win10", "chrome_win10", "safari", "firefox_macos", "chrome_macos", "firefox_linux", "chrome_linux"] diff --git a/src/components/DonationForm/Forms/UpgradeToMonthlyForm.vue b/src/components/DonationForm/Forms/UpgradeToMonthlyForm.vue new file mode 100644 index 000000000..62dad4128 --- /dev/null +++ b/src/components/DonationForm/Forms/UpgradeToMonthlyForm.vue @@ -0,0 +1,177 @@ + + + + diff --git a/src/components/DonationForm/Forms/messages/UpgradeToMonthly.de.ts b/src/components/DonationForm/Forms/messages/UpgradeToMonthly.de.ts new file mode 100644 index 000000000..98a9cddc6 --- /dev/null +++ b/src/components/DonationForm/Forms/messages/UpgradeToMonthly.de.ts @@ -0,0 +1,15 @@ +import { TranslationMessages } from '@src/Translator'; + +const Translations: TranslationMessages = { + 'upgrade-to-monthly-header': 'Könnten Sie auch {{amount}} monatlich spenden?', + 'upgrade-to-monthly-copy': 'Jedes Jahr sind wir auf die Unterstützung von Menschen wie Ihnen angewiesen.' + + ' Monatliche Spenden helfen uns nachhaltig und ermöglichen langfristige Weiterentwicklungen.', + 'upgrade-to-monthly-no': 'Nein, ich möchte einmalig {{amount}} spenden.', + 'upgrade-to-monthly-yes': 'Ja, ich möchte {{amount}} monatlich spenden.', + 'upgrade-to-monthly-link': 'Ja, ich möchte monatlich spenden, aber einen anderen Betrag.', + 'upgrade-to-monthly-button': 'Weiter, um Spende abzuschließen', + 'upgrade-to-monthly-error': 'Bitte wählen Sie eine der Optionen aus.', + 'back-button': 'Zurück' +}; + +export default Translations; diff --git a/src/components/DonationForm/Forms/messages/UpgradeToMonthly.en.ts b/src/components/DonationForm/Forms/messages/UpgradeToMonthly.en.ts new file mode 100644 index 000000000..31871dcf6 --- /dev/null +++ b/src/components/DonationForm/Forms/messages/UpgradeToMonthly.en.ts @@ -0,0 +1,15 @@ +import { TranslationMessages } from '@src/Translator'; + +const Translations: TranslationMessages = { + 'upgrade-to-monthly-header': 'Can you make it {{amount}} monthly?', + 'upgrade-to-monthly-copy': 'Every year we are dependent on the support of people like you. Monthly donations' + + ' help sustainably and enable long term development.', + 'upgrade-to-monthly-no': 'No, thanks! I\'ll make a one-time donation of {{amount}}.', + 'upgrade-to-monthly-yes': 'Yes, I\'ll donate {{amount}} each year.', + 'upgrade-to-monthly-link': 'Yes, I\'ll donate monthly, but for a different amount.', + 'upgrade-to-monthly-button': 'Proceed with the donation', + 'upgrade-to-monthly-error': 'Please choose one of the above options.', + 'back-button': 'Back' +}; + +export default Translations; diff --git a/src/components/DonationForm/StepControllers/SubmittableMainDonationFormUpgradeOptions.ts b/src/components/DonationForm/StepControllers/SubmittableMainDonationFormUpgradeOptions.ts new file mode 100644 index 000000000..e5492bf49 --- /dev/null +++ b/src/components/DonationForm/StepControllers/SubmittableMainDonationFormUpgradeOptions.ts @@ -0,0 +1,37 @@ +import { FormModel } from '@src/utils/FormModel/FormModel'; +import { Intervals } from '@src/utils/FormItemsBuilder/fields/Intervals'; +import { PaymentMethods } from '@src/utils/FormItemsBuilder/fields/PaymentMethods'; +import { StepController } from '@src/components/DonationForm/StepController'; +import { StepAction } from '@src/components/DonationForm/StepNavigation'; +import { BannerSubmitEvent } from '@src/tracking/events/BannerSubmitEvent'; + +const shouldLeadToMonthlyUpgradePage = ( formModel: FormModel, lowerAmountLimit: number, upperAmountLimit: number ): boolean => { + if ( formModel.interval.value !== Intervals.ONCE.value ) { + return false; + } + return formModel.numericAmount.value >= lowerAmountLimit && formModel.numericAmount.value <= upperAmountLimit; +}; +export function createSubmittableMainDonationFormUpgradeOptions( + formModel: FormModel, + stepNameOfUpgradeToYearly: string, + stepNameOfUpgradeToMonthly: string, + lowerAmountLimit: number, + upperAmountLimit: number +): StepController { + return { + async submit( navigation: StepAction ): Promise { + if ( formModel.interval.value !== Intervals.ONCE.value || formModel.paymentMethod.value === PaymentMethods.SOFORT.value ) { + await navigation.submit( new BannerSubmitEvent( 'MainDonationForm' ) ); + return; + } + if ( shouldLeadToMonthlyUpgradePage( formModel, lowerAmountLimit, upperAmountLimit ) ) { + await navigation.goToStep( stepNameOfUpgradeToMonthly ); + return; + } + await navigation.goToStep( stepNameOfUpgradeToYearly ); + }, + async previous(): Promise { + return Promise.reject( 'we can\'t go to previous! This should never happen' ); + } + }; +} diff --git a/src/components/DonationForm/StepControllers/SubmittableUpgradeToMonthly.ts b/src/components/DonationForm/StepControllers/SubmittableUpgradeToMonthly.ts new file mode 100644 index 000000000..fe49660d7 --- /dev/null +++ b/src/components/DonationForm/StepControllers/SubmittableUpgradeToMonthly.ts @@ -0,0 +1,27 @@ +import { StepController } from '@src/components/DonationForm/StepController'; +import { Intervals } from '@src/utils/FormItemsBuilder/fields/Intervals'; +import { FormModel } from '@src/utils/FormModel/FormModel'; +import { StepAction } from '@src/components/DonationForm/StepNavigation'; +import { BannerSubmitEvent } from '@src/tracking/events/BannerSubmitEvent'; + +export function createSubmittableUpgradeToMonthly( formModel: FormModel, stepNameOfLinkClick: string, stepNameOfPreviousPage: string ): StepController { + return { + async submit( navigation: StepAction, submitData: Record ): Promise { + formModel.interval.value = submitData.upgradeToMonthlyInterval; + if ( submitData.changeOfAmount ) { + await navigation.goToStep( stepNameOfLinkClick ); + return; + } + formModel.customAmount.value = String( submitData.newNumericAmount ); + await navigation.submit( new BannerSubmitEvent( + 'UpgradeToMonthlyForm', + formModel.interval.value === Intervals.MONTHLY.value ? 'recurring' : 'non-recurring' + ) ); + }, + async previous( navigation: StepAction ): Promise { + formModel.interval.value = Intervals.ONCE.value; + await navigation.goToStep( stepNameOfPreviousPage ); + + } + }; +} diff --git a/src/components/SoftClose/SoftClose.vue b/src/components/SoftClose/SoftClose.vue index f2e6720d3..50a647182 100644 --- a/src/components/SoftClose/SoftClose.vue +++ b/src/components/SoftClose/SoftClose.vue @@ -45,7 +45,7 @@ interface Props { } const props = withDefaults( defineProps(), { - secondsTotal: 35 + secondsTotal: 15 } ); const timer = ref( 0 ); diff --git a/src/tracking/LegacyEventTracking/mapCloseEvent.ts b/src/tracking/LegacyEventTracking/mapCloseEvent.ts index 679409730..2bb3a9b7f 100644 --- a/src/tracking/LegacyEventTracking/mapCloseEvent.ts +++ b/src/tracking/LegacyEventTracking/mapCloseEvent.ts @@ -20,6 +20,10 @@ export function mapCloseEvent( event: CloseEvent ): WMDELegacyBannerEvent { return new WMDELegacyBannerEvent( closeSourceToLegacyEventName.get( LegacyCloseSources.AlreadyDonatedGoAway ), 0.1 ); } + if ( event.userChoice === CloseChoices.Close ) { + return new WMDELegacyBannerEvent( closeSourceToLegacyEventName.get( LegacyCloseSources.AlreadyDonatedMaybeLater ), 0.1 ); + } + if ( event.userChoice === CloseChoices.MaybeLater ) { return new WMDELegacyBannerEvent( closeSourceToLegacyEventName.get( LegacyCloseSources.AlreadyDonatedMaybeLater ), 0.1 ); } diff --git a/src/tracking/LegacyEventTracking/mapFormStepShownEvent.ts b/src/tracking/LegacyEventTracking/mapFormStepShownEvent.ts index ae13ee209..563c97842 100644 --- a/src/tracking/LegacyEventTracking/mapFormStepShownEvent.ts +++ b/src/tracking/LegacyEventTracking/mapFormStepShownEvent.ts @@ -7,6 +7,7 @@ import { WMDELegacyBannerEvent } from '@src/tracking/WPORG/WMDELegacyBannerEvent export function mapFormStepShownEvent( e: FormStepShownEvent ): WMDELegacyBannerEvent { const stepNameToEventLookup: Record = { UpgradeToYearlyForm: 'upgrade-to-yearly-form-page-shown', + UpgradeToMonthlyForm: 'upgrade-to-monthly-form-page-shown', CustomAmountForm: 'custom-amount-form-page-shown', AddressTypeForm: 'address-type-form-page-shown' }; diff --git a/src/tracking/events/UpgradeToMonthlyEvent.ts b/src/tracking/events/UpgradeToMonthlyEvent.ts index 9d54baf58..e3082bcd6 100644 --- a/src/tracking/events/UpgradeToMonthlyEvent.ts +++ b/src/tracking/events/UpgradeToMonthlyEvent.ts @@ -1,11 +1,11 @@ import { TrackingEvent, TrackingFeatureName } from '@src/tracking/TrackingEvent'; -export class UpgradeToMonthlyEvent implements TrackingEvent { +export class UpgradeToMonthlyEvent implements TrackingEvent { public static readonly EVENT_NAME = 'upgrade-to-monthly'; public readonly eventName = UpgradeToMonthlyEvent.EVENT_NAME; - public readonly customData: Record = {}; - public readonly feature: TrackingFeatureName = 'UpgradeToYearlyForm'; + public readonly customData: void; + public readonly feature: TrackingFeatureName = 'UpgradeToMonthlyForm'; public readonly userChoice: string; public constructor( userChoice: 'upgraded-to-monthly'|'not-upgraded-to-monthly' ) { diff --git a/test/banners/desktop/components/BannerVar.spec.ts b/test/banners/desktop/components/BannerVar.spec.ts index 4804011f3..7332aa970 100644 --- a/test/banners/desktop/components/BannerVar.spec.ts +++ b/test/banners/desktop/components/BannerVar.spec.ts @@ -14,7 +14,7 @@ import { bannerContentDisplaySwitchFeatures, bannerContentFeatures } from '@test/features/BannerContent'; -import { donationFormFeatures } from '@test/features/forms/MainDonation_UpgradeToYearlyLink'; +import { donationFormFeatures } from '@test/features/forms/MainDonation_UpgradeToMonthlyOrYearly'; import { useFormModel } from '@src/components/composables/useFormModel'; import { resetFormModel } from '@test/resetFormModel'; import { DynamicContent } from '@src/utils/DynamicContent/DynamicContent'; @@ -95,12 +95,17 @@ describe( 'BannerVar.vue', () => { test.each( [ [ 'expectMainDonationFormSubmitsWhenSofortIsSelected' ], [ 'expectMainDonationFormSubmitsWhenYearlyIsSelected' ], - [ 'expectMainDonationFormGoesToUpgrade' ], + [ 'expectMainDonationFormGoesToUpgradeYearlyOnVeryLowAmounts' ], + [ 'expectMainDonationFormGoesToUpgradeYearlyOnHighAmounts' ], + [ 'expectMainDonationFormGoesToUpgradeMonthly' ], [ 'expectUpgradeToYearlyFormSubmitsUpgrade' ], [ 'expectUpgradeToYearlyFormSubmitsDontUpgrade' ], [ 'expectUpgradeToYearlyFormGoesToMainDonation' ], [ 'expectUpgradeToYearlyFormSubmitsUpgrade' ], - [ 'expectUpgradeToYearlyFormSubmitsDontUpgrade' ] + [ 'expectUpgradeToYearlyFormSubmitsDontUpgrade' ], + [ 'expectUpgradeToMonthlyFormSubmitsUpgrade' ], + [ 'expectUpgradeToMonthlyFormSubmitsDontUpgrade' ], + [ 'expectUpgradeToMonthlyFormGoesToMainDonation' ] ] )( '%s', async ( testName: string ) => { await donationFormFeatures[ testName ]( getWrapper() ); } ); diff --git a/test/banners/formItems.ts b/test/banners/formItems.ts index ed93d45b9..7f665db28 100644 --- a/test/banners/formItems.ts +++ b/test/banners/formItems.ts @@ -10,7 +10,8 @@ export const formItems: DonationFormItems = { { value: '5', label: '€5', className: 'amount-5' }, { value: '10', label: '€10', className: 'amount-10' }, { value: '20', label: '€20', className: 'amount-20' }, - { value: '25', label: '€25', className: 'amount-25' } + { value: '25', label: '€25', className: 'amount-25' }, + { value: '200', label: '€200', className: 'amount-200' } ], intervals: [ Intervals.ONCE, Intervals.MONTHLY, Intervals.QUARTERLY, Intervals.YEARLY ], paymentMethods: [ PaymentMethods.PAYPAL, PaymentMethods.CREDIT_CARD, PaymentMethods.SOFORT, PaymentMethods.DIRECT_DEBIT, PaymentMethods.BANK_TRANSFER ] diff --git a/test/components/DonationForm/Forms/UpgradeToMonthlyForm.spec.ts b/test/components/DonationForm/Forms/UpgradeToMonthlyForm.spec.ts new file mode 100644 index 000000000..5b74ebbcf --- /dev/null +++ b/test/components/DonationForm/Forms/UpgradeToMonthlyForm.spec.ts @@ -0,0 +1,228 @@ +import { beforeEach, describe, expect, it } from 'vitest'; +import { shallowMount, VueWrapper } from '@vue/test-utils'; +import UpgradeToMonthlyForm from '@src/components/DonationForm/Forms/UpgradeToMonthlyForm.vue'; +import { useFormModel } from '@src/components/composables/useFormModel'; +import { resetFormModel } from '@test/resetFormModel'; +import { CurrencyEn } from '@src/utils/DynamicContent/formatters/CurrencyEn'; +import { TrackerSpy } from '@test/fixtures/TrackerSpy'; +import { UpgradeToMonthlyEvent } from '@src/tracking/events/UpgradeToMonthlyEvent'; +import { FormStepShownEvent } from '@src/tracking/events/FormStepShownEvent'; +import { nextTick } from 'vue'; + +const formModel = useFormModel(); + +describe( 'UpgradeToMonthlyForm.vue', () => { + let tracker: TrackerSpy; + + // The model values are in the global scope, and they need to be reset before each test + beforeEach( () => { + resetFormModel( formModel ); + tracker = new TrackerSpy(); + } ); + + const getWrapper = (): VueWrapper => { + return shallowMount( UpgradeToMonthlyForm, { + props: { + pageIndex: 4, + isCurrent: false, + suggestions: [ + { + lowerRangeLimit: 11, + upperRangeLimit: 100, + suggestedAmount: 666 + } + ] + }, + global: { + mocks: { + $translate: ( key: string, templateTags: Record = {} ) => { + return `${key} ${JSON.stringify( templateTags )}`; + } + }, + provide: { + currencyFormatter: new CurrencyEn(), + tracker + } + } + } ); + }; + + it( 'should emit "previous" event when back button is clicked', async () => { + const wrapper = getWrapper(); + + await wrapper.find( '.wmde-banner-form-upgrade-back' ).trigger( 'click' ); + + expect( wrapper.emitted( 'previous' ).length ).toBe( 1 ); + } ); + + it( 'should emit "submit" event with payload when user wants to donate monthly with different amount', async () => { + const wrapper = getWrapper(); + const monthlyIntervalValue = '1'; + + await wrapper.find( '.wmde-banner-form-upgrade-custom' ).trigger( 'click' ); + + expect( wrapper.emitted( 'submit' ).length ).toBe( 1 ); + expect( wrapper.emitted( 'submit' )[ 0 ][ 0 ] ).toEqual( { changeOfAmount: true, upgradeToMonthlyInterval: monthlyIntervalValue } ); + } ); + + it( 'should show an error when user does not select any interval ', async function () { + const wrapper = getWrapper(); + + await wrapper.find( '.wmde-banner-sub-form' ).trigger( 'submit' ); + + expect( wrapper.find( '.wmde-banner-select-group-error-message' ).exists() ).toBe( true ); + expect( wrapper.emitted( 'submit' ) ).toBe( undefined ); + } ); + + it( 'should hide the error when user selects an option', async function () { + const wrapper = getWrapper(); + + await wrapper.find( '.wmde-banner-sub-form' ).trigger( 'submit' ); + + expect( wrapper.find( '.wmde-banner-select-group-error-message' ).exists() ).toBe( true ); + + await wrapper.find( '.wmde-banner-select-group-input' ).trigger( 'change' ); + + expect( wrapper.find( '.wmde-banner-select-group-error-message' ).exists() ).toBe( false ); + } ); + + it( 'should hide the error when user clicks on the "link" option', async function () { + const wrapper = getWrapper(); + + await wrapper.find( '.wmde-banner-sub-form' ).trigger( 'submit' ); + + expect( wrapper.find( '.wmde-banner-select-group-error-message' ).exists() ).toBe( true ); + + await wrapper.find( '.wmde-banner-form-upgrade-custom' ).trigger( 'click' ); + + expect( wrapper.find( '.wmde-banner-select-group-error-message' ).exists() ).toBe( false ); + } ); + + it( 'should emit "submit" event when user selects an interval and submits ', async function () { + const wrapper = getWrapper(); + + await wrapper.find( '.wmde-banner-select-group-input' ).trigger( 'change' ); + await wrapper.find( '.wmde-banner-sub-form' ).trigger( 'submit' ); + + expect( wrapper.find( '.wmde-banner-select-group-error-message' ).exists() ).toBe( false ); + expect( wrapper.emitted( 'submit' ).length ).toBe( 1 ); + } ); + + it( 'should submit the chosen interval and new numeric amount when form was submitted ', async function () { + const wrapper = getWrapper(); + formModel.selectedAmount.value = '22'; + const monthlyIntervalValue = '1'; + + await wrapper.find( '.wmde-banner-select-group-option-no .wmde-banner-select-group-input' ).trigger( 'change' ); + await wrapper.find( '.wmde-banner-sub-form' ).trigger( 'submit' ); + + await wrapper.find( '.wmde-banner-select-group-option-yes .wmde-banner-select-group-input' ).trigger( 'change' ); + await wrapper.find( '.wmde-banner-sub-form' ).trigger( 'submit' ); + + expect( wrapper.emitted( 'submit' ).length ).toBe( 2 ); + expect( wrapper.emitted( 'submit' )[ 0 ][ 0 ] ).toEqual( { + changeOfAmount: false, + newNumericAmount: Number( '22' ), + upgradeToMonthlyInterval: '0' + } ); + expect( wrapper.emitted( 'submit' )[ 1 ][ 0 ] ).toEqual( { + changeOfAmount: false, + newNumericAmount: Number( '666' ), + upgradeToMonthlyInterval: monthlyIntervalValue + } ); + } ); + + it( 'should insert the euroAmount into the translations', async () => { + const wrapper = getWrapper(); + + // this simulates the user selecting an amount on the first page, triggering the watcher for the visible amounts + formModel.selectedAmount.value = '20'; + await nextTick(); + + expect( wrapper.find( '.wmde-banner-form-upgrade-notice strong' ).text() ).toContain( '{"amount":"€666"}' ); + expect( wrapper.find( '.wmde-banner-select-group-option-no .wmde-banner-select-group-label' ).text() ).toContain( '{"amount":"€20"}' ); + expect( wrapper.find( '.wmde-banner-select-group-option-yes .wmde-banner-select-group-label' ).text() ).toContain( '{"amount":"€666"}' ); + } ); + + it( 'should not change visible amounts when monthly form is the current form', async () => { + const wrapper = getWrapper(); + + // this simulates the user selecting an amount on the first page, triggering the watcher for the visible amounts + formModel.selectedAmount.value = '20'; + await nextTick(); + + wrapper.setProps( { isCurrent: true } ); + + // this simulates the submit event handler setting the new amount (in the store) + formModel.selectedAmount.value = '30'; + await nextTick(); + + expect( wrapper.find( '.wmde-banner-form-upgrade-notice strong' ).text() ).toContain( '{"amount":"€666"}' ); + expect( wrapper.find( '.wmde-banner-select-group-option-no .wmde-banner-select-group-label' ).text() ).toContain( '{"amount":"€20"}' ); + expect( wrapper.find( '.wmde-banner-select-group-option-yes .wmde-banner-select-group-label' ).text() ).toContain( '{"amount":"€666"}' ); + } ); + + it( 'should reset the error message when going to the previous page', async () => { + const wrapper = getWrapper(); + + await wrapper.find( '.wmde-banner-sub-form' ).trigger( 'submit' ); + + expect( wrapper.find( '.wmde-banner-select-group-error-message' ).exists() ).toBe( true ); + + await wrapper.find( '.wmde-banner-form-upgrade-back' ).trigger( 'click' ); + + expect( wrapper.find( '.wmde-banner-select-group-error-message' ).exists() ).toBe( false ); + } ); + + it( 'should reset the value when going to the previous page', async () => { + const wrapper = getWrapper(); + + await wrapper.find( '.wmde-banner-select-group-option-no .wmde-banner-select-group-input' ).trigger( 'change' ); + await wrapper.find( '.wmde-banner-form-upgrade-back' ).trigger( 'click' ); + + const options = wrapper.findAll( '.wmde-banner-select-group-input' ); + expect( options[ 0 ].element.checked ).toBe( false ); + expect( options[ 1 ].element.checked ).toBe( false ); + } ); + + it( 'should emit back event', async () => { + const wrapper = getWrapper(); + + await wrapper.find( '.wmde-banner-form-upgrade-back' ).trigger( 'click' ); + + expect( wrapper.emitted( 'previous' ).length ).toBe( 1 ); + } ); + + describe( 'tracking events ', function () { + + it( 'should track "Upgrade to monthly" event when user chooses Upgrade to monthly', async function () { + const wrapper = getWrapper(); + + await wrapper.find( '.wmde-banner-select-group-option-yes .wmde-banner-select-group-input' ).trigger( 'change' ); + await wrapper.find( '.wmde-banner-sub-form' ).trigger( 'submit' ); + + expect( tracker.hasTrackedEvent( UpgradeToMonthlyEvent.EVENT_NAME ) ).toBe( true ); + expect( tracker.getTrackedEvent( UpgradeToMonthlyEvent.EVENT_NAME ) ).toEqual( new UpgradeToMonthlyEvent( 'upgraded-to-monthly' ) ); + + } ); + + it( 'should track "Not upgraded to monthly" event when user does not choose Upgrade to monthly', async function () { + const wrapper = getWrapper(); + + await wrapper.find( '.wmde-banner-select-group-option-no .wmde-banner-select-group-input' ).trigger( 'change' ); + await wrapper.find( '.wmde-banner-sub-form' ).trigger( 'submit' ); + + expect( tracker.hasTrackedEvent( UpgradeToMonthlyEvent.EVENT_NAME ) ).toBe( true ); + expect( tracker.getTrackedEvent( UpgradeToMonthlyEvent.EVENT_NAME ) ).toEqual( new UpgradeToMonthlyEvent( 'not-upgraded-to-monthly' ) ); + } ); + + it( 'sends the FormStepShownEvent to tracker when the form becomes the current form', async () => { + const wrapper = getWrapper(); + + await wrapper.setProps( { isCurrent: true } ); + + expect( tracker.hasTrackedEvent( FormStepShownEvent.EVENT_NAME ) ).toBe( true ); + expect( tracker.getTrackedEvent( FormStepShownEvent.EVENT_NAME ) ).toEqual( new FormStepShownEvent( 'UpgradeToMonthlyForm' ) ); + } ); + } ); +} ); diff --git a/test/features/forms/MainDonation_UpgradeToMonthlyOrYearly.ts b/test/features/forms/MainDonation_UpgradeToMonthlyOrYearly.ts new file mode 100644 index 000000000..755d91d29 --- /dev/null +++ b/test/features/forms/MainDonation_UpgradeToMonthlyOrYearly.ts @@ -0,0 +1,92 @@ +import { VueWrapper } from '@vue/test-utils'; +import { + expectMainDonationFormGoesToPageOnSubmit, + expectMainDonationFormSubmits, + submitMainDonationForm +} from '@test/features/forms/subForms/MainDonationForm'; +import { + expectUpgradeToYearlyFormGoesToPageOnLinkClick, + expectUpgradeToYearlyFormSubmits +} from '@test/features/forms/subForms/UpgradeToYearlyForm'; +import { + expectUpgradeToMonthlyFormGoesToPageOnLinkClick, + expectUpgradeToMonthlyFormSubmits +} from '@test/features/forms/subForms/UpgradeToMonthlyForm'; +import { Intervals } from '@src/utils/FormItemsBuilder/fields/Intervals'; +import { PaymentMethods } from '@src/utils/FormItemsBuilder/fields/PaymentMethods'; + +enum Pages { + MainDonation = 1, + UpgradeToYearly = 2, + UpgradeToMonthly = 3 +} + +export const donationFormFeatures: Record ) => Promise> = { + expectMainDonationFormSubmitsWhenSofortIsSelected: ( wrapper: VueWrapper ) => expectMainDonationFormSubmits( + wrapper, + Intervals.ONCE, + PaymentMethods.SOFORT, + '5' + ), + expectMainDonationFormSubmitsWhenYearlyIsSelected: ( wrapper: VueWrapper ) => expectMainDonationFormSubmits( + wrapper, + Intervals.YEARLY, + PaymentMethods.PAYPAL, + '5' + ), + expectMainDonationFormSubmitsWhenMonthlyIsSelected: ( wrapper: VueWrapper ) => expectMainDonationFormSubmits( + wrapper, + Intervals.MONTHLY, + PaymentMethods.PAYPAL, + '5' + ), + expectMainDonationFormSubmitsWhenAmountIsGreaterThanMaxAmount: async ( wrapper: VueWrapper ) => expectMainDonationFormSubmits( + wrapper, + Intervals.YEARLY, + PaymentMethods.PAYPAL, + '25' + ), + expectMainDonationFormGoesToUpgradeYearlyOnHighAmounts: ( wrapper: VueWrapper ) => expectMainDonationFormGoesToPageOnSubmit( wrapper, + Pages.UpgradeToYearly, + Intervals.ONCE, + PaymentMethods.PAYPAL, + '200' + ), + expectMainDonationFormGoesToUpgradeYearlyOnVeryLowAmounts: ( wrapper: VueWrapper ) => expectMainDonationFormGoesToPageOnSubmit( wrapper, + Pages.UpgradeToYearly, + Intervals.ONCE, + PaymentMethods.PAYPAL, + '1' + ), + expectMainDonationFormGoesToUpgradeMonthly: ( wrapper: VueWrapper ) => expectMainDonationFormGoesToPageOnSubmit( wrapper, + Pages.UpgradeToMonthly, + Intervals.ONCE, + PaymentMethods.PAYPAL, + '20' + ), + expectUpgradeToYearlyFormSubmitsUpgrade: async ( wrapper: VueWrapper ) => { + await submitMainDonationForm( wrapper, Intervals.ONCE, '5', PaymentMethods.PAYPAL ); + await expectUpgradeToYearlyFormSubmits( wrapper, 'yes' ); + }, + expectUpgradeToYearlyFormSubmitsDontUpgrade: async ( wrapper: VueWrapper ) => { + await submitMainDonationForm( wrapper, Intervals.ONCE, '5', PaymentMethods.PAYPAL ); + await expectUpgradeToYearlyFormSubmits( wrapper, 'no' ); + }, + expectUpgradeToYearlyFormGoesToMainDonation: async ( wrapper: VueWrapper ) => { + await submitMainDonationForm( wrapper, Intervals.ONCE, '5', PaymentMethods.PAYPAL ); + await expectUpgradeToYearlyFormGoesToPageOnLinkClick( wrapper, Pages.MainDonation ); + }, + + expectUpgradeToMonthlyFormSubmitsUpgrade: async ( wrapper: VueWrapper ) => { + await submitMainDonationForm( wrapper, Intervals.ONCE, '5', PaymentMethods.PAYPAL ); + await expectUpgradeToMonthlyFormSubmits( wrapper, 'yes' ); + }, + expectUpgradeToMonthlyFormSubmitsDontUpgrade: async ( wrapper: VueWrapper ) => { + await submitMainDonationForm( wrapper, Intervals.ONCE, '5', PaymentMethods.PAYPAL ); + await expectUpgradeToMonthlyFormSubmits( wrapper, 'no' ); + }, + expectUpgradeToMonthlyFormGoesToMainDonation: async ( wrapper: VueWrapper ) => { + await submitMainDonationForm( wrapper, Intervals.ONCE, '5', PaymentMethods.PAYPAL ); + await expectUpgradeToMonthlyFormGoesToPageOnLinkClick( wrapper, Pages.MainDonation ); + } +}; diff --git a/test/features/forms/subForms/UpgradeToMonthlyForm.ts b/test/features/forms/subForms/UpgradeToMonthlyForm.ts new file mode 100644 index 000000000..9b133a9d4 --- /dev/null +++ b/test/features/forms/subForms/UpgradeToMonthlyForm.ts @@ -0,0 +1,30 @@ +import { VueWrapper } from '@vue/test-utils'; +import { expect, vi } from 'vitest'; + +export const submitUpgradeToMonthlyForm = async ( wrapper: VueWrapper, upgrade: 'yes' | 'no' ): Promise => { + await wrapper.find( `.wmde-banner-select-group-option-${upgrade} .wmde-banner-select-group-input` ).setValue(); + await wrapper.find( '.wmde-banner-form-upgrade' ).trigger( 'submit' ); +}; + +export const expectUpgradeToMonthlyFormSubmits = async ( wrapper: VueWrapper, upgrade: 'yes' | 'no' ): Promise => { + const submitForm = wrapper.find( '.wmde-banner-submit-form' ); + submitForm.element.submit = vi.fn(); + + await submitUpgradeToMonthlyForm( wrapper, upgrade ); + + expect( submitForm.element.submit ).toHaveBeenCalledOnce(); +}; + +export const expectUpgradeToMonthlyFormGoesToPageOnSubmit = async ( wrapper: VueWrapper, page: number, upgrade: 'yes' | 'no' ): Promise => { + await submitUpgradeToMonthlyForm( wrapper, upgrade ); + + expect( wrapper.find( `.wmde-banner-form-page:nth-child(${page})` ).attributes( 'class' ) ) + .toContain( 'wmde-banner-form-page--current' ); +}; + +export const expectUpgradeToMonthlyFormGoesToPageOnLinkClick = async ( wrapper: VueWrapper, page: number ): Promise => { + await wrapper.find( '.wmde-banner-form-upgrade-custom' ).trigger( 'click' ); + + expect( wrapper.find( `.wmde-banner-form-page:nth-child(${page})` ).attributes( 'class' ) ) + .toContain( 'wmde-banner-form-page--current' ); +}; From cce2bc031272d086ead2c7eaac77e0f4c974db97 Mon Sep 17 00:00:00 2001 From: Abban Dunne Date: Tue, 21 Nov 2023 16:51:48 +0100 Subject: [PATCH 4/4] Add step controller tests (#287) Ticket: https://phabricator.wikimedia.org/T351199 --- ...ableMainDonationFormUpgradeOptions.spec.ts | 117 ++++++++++++++++++ ...ableMainDonationFormWhenOverAmount.spec.ts | 4 +- .../SubmittableUpgradeToMonthly.spec.ts | 78 ++++++++++++ 3 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 test/components/DonationForm/StepControllers/SubmittableMainDonationFormUpgradeOptions.spec.ts create mode 100644 test/components/DonationForm/StepControllers/SubmittableUpgradeToMonthly.spec.ts diff --git a/test/components/DonationForm/StepControllers/SubmittableMainDonationFormUpgradeOptions.spec.ts b/test/components/DonationForm/StepControllers/SubmittableMainDonationFormUpgradeOptions.spec.ts new file mode 100644 index 000000000..fc5829bd6 --- /dev/null +++ b/test/components/DonationForm/StepControllers/SubmittableMainDonationFormUpgradeOptions.spec.ts @@ -0,0 +1,117 @@ +import { beforeEach, describe, expect, it, test, vi } from 'vitest'; +import { StepAction } from '@src/components/DonationForm/StepNavigation'; +import { resetFormModel } from '@test/resetFormModel'; +import { PaymentMethods } from '@src/utils/FormItemsBuilder/fields/PaymentMethods'; +import { Intervals } from '@src/utils/FormItemsBuilder/fields/Intervals'; +import { + createSubmittableMainDonationFormUpgradeOptions +} from '@src/components/DonationForm/StepControllers/SubmittableMainDonationFormUpgradeOptions'; +import { useFormModel } from '@src/components/composables/useFormModel'; + +const formModel = useFormModel(); + +describe( 'SubmittableMainDonationFormUpgradeOptions', () => { + let stepNavigation: StepAction; + + // The model values are in the global scope, and they need to be reset before each test + beforeEach( () => { + resetFormModel( formModel ); + stepNavigation = { + goToStep: vi.fn(), + submit: vi.fn() + }; + } ); + + test.each( [ + [ PaymentMethods.PAYPAL.value, '10', 'yearly' ], + [ PaymentMethods.BANK_TRANSFER.value, '10', 'yearly' ], + [ PaymentMethods.CREDIT_CARD.value, '10', 'yearly' ], + [ PaymentMethods.DIRECT_DEBIT.value, '10', 'yearly' ], + [ PaymentMethods.PAYPAL.value, '101', 'yearly' ], + [ PaymentMethods.BANK_TRANSFER.value, '101', 'yearly' ], + [ PaymentMethods.CREDIT_CARD.value, '101', 'yearly' ], + [ PaymentMethods.DIRECT_DEBIT.value, '101', 'yearly' ], + [ PaymentMethods.PAYPAL.value, '12', 'monthly' ], + [ PaymentMethods.BANK_TRANSFER.value, '12', 'monthly' ], + [ PaymentMethods.CREDIT_CARD.value, '12', 'monthly' ], + [ PaymentMethods.DIRECT_DEBIT.value, '12', 'monthly' ] + ] )( 'goes to correct step when payment method is not Sofort and amount is in upgrade range', async ( paymentMethod: string, selectedAmount: string, expectedPage: string ) => { + formModel.paymentMethod.value = paymentMethod; + formModel.interval.value = Intervals.ONCE.value; + formModel.selectedAmount.value = selectedAmount; + const donationForm = createSubmittableMainDonationFormUpgradeOptions( + formModel, + 'yearly', + 'monthly', + 11, + 100 + ); + + await donationForm.submit( stepNavigation, {} ); + + expect( stepNavigation.goToStep ).toHaveBeenCalledOnce(); + expect( stepNavigation.goToStep ).toHaveBeenCalledWith( expectedPage ); + } ); + + test.each( [ + [ PaymentMethods.SOFORT.value, '10' ], + [ PaymentMethods.SOFORT.value, '12' ], + [ PaymentMethods.SOFORT.value, '101' ] + ] )( 'submits when payment method is sofort', async ( paymentMethod: string, selectedAmount: string ) => { + formModel.interval.value = Intervals.ONCE.value; + formModel.paymentMethod.value = paymentMethod; + formModel.selectedAmount.value = selectedAmount; + const donationForm = createSubmittableMainDonationFormUpgradeOptions( + formModel, + 'yearly', + 'monthly', + 11, + 100 + ); + + await donationForm.submit( stepNavigation, {} ); + + expect( stepNavigation.submit ).toHaveBeenCalledOnce(); + } ); + + test.each( [ + [ Intervals.MONTHLY.value, '10' ], + [ Intervals.QUARTERLY.value, '10' ], + [ Intervals.BIANNUAL.value, '10' ], + [ Intervals.YEARLY.value, '10' ], + [ Intervals.MONTHLY.value, '11' ], + [ Intervals.QUARTERLY.value, '11' ], + [ Intervals.BIANNUAL.value, '11' ], + [ Intervals.YEARLY.value, '11' ], + [ Intervals.MONTHLY.value, '101' ], + [ Intervals.QUARTERLY.value, '101' ], + [ Intervals.BIANNUAL.value, '101' ], + [ Intervals.YEARLY.value, '101' ] + ] )( 'submits when interval is not once', async ( interval: string ) => { + formModel.interval.value = interval; + formModel.paymentMethod.value = PaymentMethods.PAYPAL.value; + const donationForm = createSubmittableMainDonationFormUpgradeOptions( + formModel, + 'yearly', + 'monthly', + 11, + 100 + ); + + await donationForm.submit( stepNavigation, {} ); + + expect( stepNavigation.submit ).toHaveBeenCalledOnce(); + } ); + + it( 'rejects calls to previous', async () => { + const donationForm = createSubmittableMainDonationFormUpgradeOptions( + formModel, + 'yearly', + 'monthly', + 11, + 100 + ); + + expect( donationForm.previous( stepNavigation ) ).rejects.toEqual( 'we can\'t go to previous! This should never happen' ); + } ); +} ); diff --git a/test/components/DonationForm/StepControllers/SubmittableMainDonationFormWhenOverAmount.spec.ts b/test/components/DonationForm/StepControllers/SubmittableMainDonationFormWhenOverAmount.spec.ts index 87e949926..24c70696d 100644 --- a/test/components/DonationForm/StepControllers/SubmittableMainDonationFormWhenOverAmount.spec.ts +++ b/test/components/DonationForm/StepControllers/SubmittableMainDonationFormWhenOverAmount.spec.ts @@ -27,7 +27,7 @@ describe( 'SubmittableMainDonationFormWhenOverAmount', () => { [ PaymentMethods.BANK_TRANSFER.value ], [ PaymentMethods.CREDIT_CARD.value ], [ PaymentMethods.DIRECT_DEBIT.value ] - ] )( 'submits when payment method is Sofort', async ( paymentMethod: string ) => { + ] )( 'goes to upgrade when payment method is not Sofort', async ( paymentMethod: string ) => { formModel.paymentMethod.value = paymentMethod; formModel.interval.value = Intervals.ONCE.value; const donationForm = createSubmittableMainDonationFormWhenOverAmount( formModel, 'yearly', 100 ); @@ -38,7 +38,7 @@ describe( 'SubmittableMainDonationFormWhenOverAmount', () => { expect( stepNavigation.goToStep ).toHaveBeenCalledWith( 'yearly' ); } ); - it( 'goes to upgrade when payment method is not Sofort', async () => { + it( 'submits when payment method is Sofort', async () => { formModel.paymentMethod.value = PaymentMethods.SOFORT.value; formModel.interval.value = Intervals.ONCE.value; const donationForm = createSubmittableMainDonationFormWhenOverAmount( formModel, 'yearly', 100 ); diff --git a/test/components/DonationForm/StepControllers/SubmittableUpgradeToMonthly.spec.ts b/test/components/DonationForm/StepControllers/SubmittableUpgradeToMonthly.spec.ts new file mode 100644 index 000000000..04362ef94 --- /dev/null +++ b/test/components/DonationForm/StepControllers/SubmittableUpgradeToMonthly.spec.ts @@ -0,0 +1,78 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { useFormModel } from '@src/components/composables/useFormModel'; +import { StepAction } from '@src/components/DonationForm/StepNavigation'; +import { resetFormModel } from '@test/resetFormModel'; +import { Intervals } from '@src/utils/FormItemsBuilder/fields/Intervals'; +import { BannerSubmitEvent } from '@src/tracking/events/BannerSubmitEvent'; +import { + createSubmittableUpgradeToMonthly +} from '@src/components/DonationForm/StepControllers/SubmittableUpgradeToMonthly'; + +const formModel = useFormModel(); + +describe( 'SubmittableUpgradeToMonthly', () => { + let stepNavigation: StepAction; + + // The model values are in the global scope, and they need to be reset before each test + beforeEach( () => { + resetFormModel( formModel ); + stepNavigation = { + goToStep: vi.fn(), + submit: vi.fn() + }; + } ); + + it( 'submits when user selects option', async () => { + const upgrade = createSubmittableUpgradeToMonthly( formModel, 'link', 'previous' ); + + await upgrade.submit( stepNavigation, {} ); + + expect( stepNavigation.submit ).toHaveBeenCalledOnce(); + } ); + + it( 'goes to link step when user clicks link', async () => { + const upgrade = createSubmittableUpgradeToMonthly( formModel, 'link', 'previous' ); + + await upgrade.submit( stepNavigation, { changeOfAmount: 'true' } ); + + expect( stepNavigation.goToStep ).toHaveBeenCalledOnce(); + expect( stepNavigation.goToStep ).toHaveBeenCalledWith( 'link' ); + } ); + + it( 'sets form interval to once on previous', async () => { + formModel.interval.value = Intervals.YEARLY.value; + const upgrade = createSubmittableUpgradeToMonthly( formModel, 'link', 'previous' ); + + await upgrade.previous( stepNavigation ); + + expect( formModel.interval.value ).toBe( Intervals.ONCE.value ); + } ); + + it( 'goes to previous step on previous', async () => { + formModel.interval.value = Intervals.YEARLY.value; + const upgrade = createSubmittableUpgradeToMonthly( formModel, 'link', 'previous' ); + + await upgrade.previous( stepNavigation ); + + expect( stepNavigation.goToStep ).toHaveBeenCalledOnce(); + expect( stepNavigation.goToStep ).toHaveBeenCalledWith( 'previous' ); + } ); + + describe( 'tracking events', function () { + it( 'converts recurring interval to "submit" event with the correct option selected', async () => { + const upgrade = createSubmittableUpgradeToMonthly( formModel, 'link', 'previous' ); + + await upgrade.submit( stepNavigation, { upgradeToMonthlyInterval: Intervals.MONTHLY.value } ); + + expect( stepNavigation.submit ).toHaveBeenCalledWith( new BannerSubmitEvent( 'UpgradeToMonthlyForm', 'recurring' ) ); + } ); + + it( 'converts non-recurring interval to "submit" event with the correct option selected', async () => { + const upgrade = createSubmittableUpgradeToMonthly( formModel, 'link', 'previous' ); + + await upgrade.submit( stepNavigation, { upgradeToMonthlyInterval: Intervals.ONCE.value } ); + + expect( stepNavigation.submit ).toHaveBeenCalledWith( new BannerSubmitEvent( 'UpgradeToMonthlyForm', 'non-recurring' ) ); + } ); + } ); +} );