From b57f91bfab770c8645bccd14a7c079af31ad5775 Mon Sep 17 00:00:00 2001 From: Oleksandr Aratovskyi <79862886+oaratovskyi@users.noreply.github.com> Date: Thu, 5 Oct 2023 16:09:53 +0300 Subject: [PATCH] Add e2e tests for progressive onboarding (#7309) Co-authored-by: Vlad Olaru --- ...-6214-e2e-tests-for-progressive-onboarding | 4 + .../custom-select-control/index.tsx | 3 + .../grouped-select-control/index.tsx | 3 + client/onboarding/form.tsx | 24 ++-- .../merchant-progressive-onboarding.spec.js | 128 ++++++++++++++++++ tests/e2e/utils/flows.js | 82 +++++++++++ 6 files changed, 232 insertions(+), 12 deletions(-) create mode 100644 changelog/dev-6214-e2e-tests-for-progressive-onboarding create mode 100644 tests/e2e/specs/wcpay/merchant/merchant-progressive-onboarding.spec.js diff --git a/changelog/dev-6214-e2e-tests-for-progressive-onboarding b/changelog/dev-6214-e2e-tests-for-progressive-onboarding new file mode 100644 index 00000000000..73526b51ed3 --- /dev/null +++ b/changelog/dev-6214-e2e-tests-for-progressive-onboarding @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +e2e tests for progressive onboarding diff --git a/client/components/custom-select-control/index.tsx b/client/components/custom-select-control/index.tsx index 88fca32ae53..3fe7de467cb 100644 --- a/client/components/custom-select-control/index.tsx +++ b/client/components/custom-select-control/index.tsx @@ -30,6 +30,7 @@ export interface Item { } export interface ControlProps< ItemType > { + name?: string; className?: string; label: string; describedBy?: string; @@ -81,6 +82,7 @@ const stateReducer = ( }; function CustomSelectControl< ItemType extends Item >( { + name, className, label, describedBy, @@ -168,6 +170,7 @@ function CustomSelectControl< ItemType extends Item >( { 'components-custom-select-control__button', { placeholder: ! itemString } ), + name, } ) } > diff --git a/client/components/grouped-select-control/index.tsx b/client/components/grouped-select-control/index.tsx index 013605ce609..a8b75dfea0c 100644 --- a/client/components/grouped-select-control/index.tsx +++ b/client/components/grouped-select-control/index.tsx @@ -27,11 +27,13 @@ export interface GroupedSelectControlProps< ItemType > { value?: ItemType | null; placeholder?: string; searchable?: boolean; + name?: string; className?: string; onChange?: ( changes: Partial< UseSelectState< ItemType > > ) => void; } const GroupedSelectControl = < ItemType extends ListItem >( { + name, className, label, options: listItems, @@ -176,6 +178,7 @@ const GroupedSelectControl = < ItemType extends ListItem >( { 'components-text-control__input wcpay-component-grouped-select-control__button', { placeholder } ), + name, } ) } > diff --git a/client/onboarding/form.tsx b/client/onboarding/form.tsx index 1b3f2852ec9..d5cb1eeb4ed 100644 --- a/client/onboarding/form.tsx +++ b/client/onboarding/form.tsx @@ -58,10 +58,10 @@ interface OnboardingTextFieldProps extends Partial< TextFieldProps > { name: keyof OnboardingFields; } -export const OnboardingTextField: React.FC< OnboardingTextFieldProps > = ( { - name, - ...rest -} ) => { +export const OnboardingTextField: React.FC< OnboardingTextFieldProps > = ( + props +) => { + const { name } = props; const { data, setData, touched } = useOnboardingContext(); const { validate, error } = useValidation( name ); const inputRef = React.useRef< HTMLInputElement >( null ); @@ -85,7 +85,7 @@ export const OnboardingTextField: React.FC< OnboardingTextFieldProps > = ( { if ( event.key === 'Enter' ) validate(); } } error={ error() } - { ...rest } + { ...props } /> ); }; @@ -95,10 +95,10 @@ interface OnboardingPhoneNumberFieldProps name: keyof OnboardingFields; } -export const OnboardingPhoneNumberField: React.FC< OnboardingPhoneNumberFieldProps > = ( { - name, - ...rest -} ) => { +export const OnboardingPhoneNumberField: React.FC< OnboardingPhoneNumberFieldProps > = ( + props +) => { + const { name } = props; const { data, setData, temp, setTemp, touched } = useOnboardingContext(); const { validate, error } = useValidation( name ); @@ -117,7 +117,7 @@ export const OnboardingPhoneNumberField: React.FC< OnboardingPhoneNumberFieldPro onKeyDown={ ( event: React.KeyboardEvent< HTMLInputElement > ) => { if ( event.key === 'Enter' ) validate(); } } - { ...rest } + { ...props } /> ); }; @@ -129,10 +129,10 @@ interface OnboardingSelectFieldProps< ItemType > } export const OnboardingSelectField = < ItemType extends SelectItem >( { - name, onChange, ...rest }: OnboardingSelectFieldProps< ItemType > ): JSX.Element => { + const { name } = rest; const { data, setData } = useOnboardingContext(); const { validate, error } = useValidation( name ); @@ -169,10 +169,10 @@ interface OnboardingGroupedSelectFieldProps< ItemType > export const OnboardingGroupedSelectField = < ListItemType extends GroupedSelectItem >( { - name, onChange, ...rest }: OnboardingGroupedSelectFieldProps< ListItemType > ): JSX.Element => { + const { name } = rest; const { data, setData } = useOnboardingContext(); const { validate, error } = useValidation( name ); diff --git a/tests/e2e/specs/wcpay/merchant/merchant-progressive-onboarding.spec.js b/tests/e2e/specs/wcpay/merchant/merchant-progressive-onboarding.spec.js new file mode 100644 index 00000000000..e7cf88553e1 --- /dev/null +++ b/tests/e2e/specs/wcpay/merchant/merchant-progressive-onboarding.spec.js @@ -0,0 +1,128 @@ +/** + * External dependencies + */ +const { merchant, evalAndClick } = require( '@woocommerce/e2e-utils' ); + +/** + * Internal dependencies + */ +import { merchantWCP, uiLoaded } from '../../../utils'; + +describe( 'Admin merchant progressive onboarding', () => { + beforeAll( async () => { + await merchant.login(); + await merchantWCP.enableProgressiveOnboarding(); + await merchantWCP.enableActAsDisconnectedFromWCPay(); + } ); + + afterAll( async () => { + await merchantWCP.disableProgressiveOnboarding(); + await merchantWCP.disableActAsDisconnectedFromWCPay(); + await merchant.logout(); + } ); + + it( 'should pass merchant flow without any errors', async () => { + // Open connect account page and click Finish Setup + await merchantWCP.openConnectPage(); + await Promise.all( [ + evalAndClick( + 'div.connect-account-page button.components-button.is-primary' + ), + page.waitForNavigation( { waitUntil: 'networkidle0' } ), + uiLoaded(), + ] ); + + // Merchant vs builder flow step + await expect( page ).toMatchElement( 'h1.stepper__heading', { + text: 'Let’s get your store ready to accept payments', + } ); + await expect( page ).toClick( + 'div.stepper__content button.components-button.is-primary', + { + text: 'Continue', + } + ); + + // User details step + await expect( page ).toMatchElement( 'h1.stepper__heading', { + text: 'First, you’ll need to create an account', + } ); + await expect( page ).toFill( '[name="individual.first_name"]', 'Test' ); + await expect( page ).toFill( '[name="individual.last_name"]', 'Test' ); + await expect( page ).toFill( '[name="email"]', 'test@gmail.com' ); + await page.waitForSelector( + 'div.wcpay-component-phone-number-control input[type="text"]' + ); + await expect( page ).toFill( + 'div.wcpay-component-phone-number-control input[type="text"]', + '0000000000' + ); + await expect( page ).toClick( + 'div.stepper__content button.components-button.is-primary', + { + text: 'Continue', + } + ); + + // Tell us about your business step + await expect( page ).toMatchElement( 'h1.stepper__heading', { + text: 'Tell us about your business', + } ); + // pick Individual business entity + await expect( page ).toClick( '[name="business_type"]' ); + await page.waitForSelector( + '[name="business_type"] ~ ul li.components-custom-select-control__item' + ); + await expect( page ).toClick( + '[name="business_type"] ~ ul li.components-custom-select-control__item' + ); + // pick Software type of goods + await expect( page ).toClick( '[name="mcc"]' ); + await page.waitForSelector( + '[name="mcc"] ~ ul li.wcpay-component-grouped-select-control__item:not(.is-group)' + ); + await expect( page ).toClick( + '[name="mcc"] ~ ul li.wcpay-component-grouped-select-control__item:not(.is-group)' + ); + await expect( page ).toClick( + 'div.stepper__content button.components-button.is-primary', + { + text: 'Continue', + } + ); + + // Store details step: pick annual revenue and go live timeframe + await expect( page ).toMatchElement( 'h1.stepper__heading', { + text: 'Please share a few more details', + } ); + await expect( page ).toClick( '[name="annual_revenue"]' ); + await page.waitForSelector( + '[name="annual_revenue"] ~ ul li.components-custom-select-control__item' + ); + await expect( page ).toClick( + '[name="annual_revenue"] ~ ul li.components-custom-select-control__item' + ); + await expect( page ).toClick( '[name="go_live_timeframe"]' ); + await page.waitForSelector( + '[name="go_live_timeframe"] ~ ul li.components-custom-select-control__item' + ); + await expect( page ).toClick( + '[name="go_live_timeframe"] ~ ul li.components-custom-select-control__item' + ); + await expect( page ).toClick( + 'div.stepper__content button.components-button.is-primary', + { + text: 'Continue', + } + ); + + // Loading screen + await expect( page ).toMatchElement( 'h1.stepper__heading', { + text: 'Let’s get you set up for payments', + } ); + + // Merchant is redirected away to payments/connect again (because of force fisconnected option) + // todo at some point test real Stripe KYC + await page.waitForNavigation( { waitUntil: 'networkidle0' } ); + } ); +} ); diff --git a/tests/e2e/utils/flows.js b/tests/e2e/utils/flows.js index 0a342e136ab..6459acaf6ca 100644 --- a/tests/e2e/utils/flows.js +++ b/tests/e2e/utils/flows.js @@ -30,6 +30,8 @@ const WC_ADMIN_BASE_URL = baseUrl + 'wp-admin/'; const MY_ACCOUNT_SUBSCRIPTIONS = baseUrl + 'my-account/subscriptions'; const MY_ACCOUNT_EDIT = baseUrl + 'my-account/edit-account'; const MY_ACCOUNT_ORDERS = SHOP_MY_ACCOUNT_PAGE + 'orders/'; +const WCPAY_CONNECT = + baseUrl + 'wp-admin/admin.php?page=wc-admin&path=/payments/connect'; const WCPAY_DISPUTES = baseUrl + 'wp-admin/admin.php?page=wc-admin&path=/payments/disputes'; const WCPAY_DEPOSITS = @@ -462,6 +464,79 @@ export const merchantWCP = { } ); }, + enableProgressiveOnboarding: async () => { + await page.goto( WCPAY_DEV_TOOLS, { + waitUntil: 'networkidle0', + } ); + + if ( + ! ( await page.$( + '#_wcpay_feature_progressive_onboarding:checked' + ) ) + ) { + await expect( page ).toClick( + 'label[for="_wcpay_feature_progressive_onboarding"]' + ); + } + + await expect( page ).toClick( 'input#submit' ); + await page.waitForNavigation( { + waitUntil: 'networkidle0', + } ); + }, + + disableProgressiveOnboarding: async () => { + await page.goto( WCPAY_DEV_TOOLS, { + waitUntil: 'networkidle0', + } ); + + if ( + await page.$( '#_wcpay_feature_progressive_onboarding:checked' ) + ) { + await expect( page ).toClick( + 'label[for="_wcpay_feature_progressive_onboarding"]' + ); + } + + await expect( page ).toClick( 'input#submit' ); + await page.waitForNavigation( { + waitUntil: 'networkidle0', + } ); + }, + + enableActAsDisconnectedFromWCPay: async () => { + await page.goto( WCPAY_DEV_TOOLS, { + waitUntil: 'networkidle0', + } ); + + if ( ! ( await page.$( '#wcpaydev_force_disconnected:checked' ) ) ) { + await expect( page ).toClick( + 'label[for="wcpaydev_force_disconnected"]' + ); + } + + await expect( page ).toClick( 'input#submit' ); + await page.waitForNavigation( { + waitUntil: 'networkidle0', + } ); + }, + + disableActAsDisconnectedFromWCPay: async () => { + await page.goto( WCPAY_DEV_TOOLS, { + waitUntil: 'networkidle0', + } ); + + if ( await page.$( '#wcpaydev_force_disconnected:checked' ) ) { + await expect( page ).toClick( + 'label[for="wcpaydev_force_disconnected"]' + ); + } + await expect( page ).toClick( 'input#submit' ); + await page.waitForNavigation( { + waitUntil: 'networkidle0', + } ); + }, + enablePaymentMethod: async ( paymentMethods ) => { await page.goto( WCPAY_PAYMENT_SETTINGS, { waitUntil: 'networkidle0', @@ -607,6 +682,13 @@ export const merchantWCP = { await uiLoaded(); }, + openConnectPage: async () => { + await page.goto( WCPAY_CONNECT, { + waitUntil: 'networkidle0', + } ); + await uiLoaded(); + }, + openOrderAnalytics: async () => { await merchant.openAnalyticsPage( 'orders' ); await uiLoaded();