Update/6487 target audience of business feature step (https://github.com/woocommerce/woocommerce-admin/pull/6508)

* Prune out old bundle flow and expand range of new one

* Add feature flag, default to true

* Update feature flag, and updated WcPay logic

* Fix lint errors

* Add changelog

* Onlly show WC payments task if part of the business extensions

* Remove stray console log

* Update copy of WCPay card

* Add a couple more e2e tests

* Add await for click

* Add test instructions
This commit is contained in:
louwie17 2021-03-08 10:23:39 -04:00 committed by GitHub
parent 7ffe4a0f85
commit 14e2becc9a
21 changed files with 436 additions and 400 deletions

View File

@ -92,6 +92,37 @@ For each task in that list apart from "Store details":
3. A title in the top left should reflect the original task name from the task list. e.g. "Add tax rates"
4. Clicking the chevron to the left of the title should take you back to the home screen
### Update target audience of business feature step #6508
Scenario #1
1. With a fresh install of wc-admin and woocommerce, go to the home screen, which starts the onboarding wizard
2. Fill out the store details with a canadian address (addr: 4428 Blanshard, country/region: Canada -- British Columbia, city: Victoria, postcode: V8W 2H9)
3. Click continue and select **Fashion, apparel, and accessories**, continue, and select **Physical products**, and continue.
4. The business details tab should show a **Business details** tab, and a **Free features** tab (disabled at first)
- There should only be dropdowns visible on the **Business details** step (no checkboxes)
5. Select **1-10** for the first dropdown, and **No** for the second, and click Continue.
6. Click on the expansion icon for the **Add recommended business features to my site**
7. It should list 7 features, including **WooCommerce Payments** (top one)
- Note down the selected features, for step 10
8. Click continue, and select your theme, after it should redirect to the home screen (showing the welcome modal, you can step through this).
9. The home screen task list should include a **Set up WooCommerce Payments** task, and there should also be a **Set up additional payment providers** inbox card displayed (below the task list).
10. Go to **Plugins > installed Plugins**, check if the selected plugin features selected in step 7 are installed and activated.
Scenario #2
1. With a fresh install of wc-admin and woocommerce, go to the home screen, which starts the onboarding wizard
2. Fill out the store details with a spanish address (addr: C/ Benito Guinea 52, country/region: Spain -- Barcelona, city: Canet de Mar, postcode: 08360)
3. Click continue and select **Fashion, apparel, and accessories**, continue, and select **Physical products**, and continue.
4. On the business details tab select **1-10** for the first dropdown, and **No** for the second.
- After filling the dropdowns it should show several checkboxes with plugins (Facebook, mailchimp, creative mail, google ads)
- Note which ones you kept selected (you can unselect one or two)
5. Click continue, and select your theme, it should show the **WooCommerce Shipping & Tax** step after, you can click **No thanks**.
6. You will be redirected to the home screen, showing the welcome modal, you can step through this.
7. The task list should show the **Choose payment methods** task, and the **Set up additional payment providers** inbox card should not be present.
8. Click on the **Choose payment methods** task, it should not be displaying the **Woocommerce Payments** option.
9. Go to **Plugins > installed Plugins**, check if the selected plugin features selected in step 4 are installed and activated.
## 2.1.0
### Correct the Klarna slug #6440

View File

@ -36,7 +36,7 @@ const StoreAlerts = lazy( () =>
const WCPayUsageModal = lazy( () =>
import(
/* webpackChunkName: "wcpay-usage-modal" */ '../task-list/tasks/payments/wcpay-usage-modal'
/* webpackChunkName: "wcpay-usage-modal" */ '../task-list/tasks/payments/wcpay/wcpay-usage-modal'
)
);

View File

@ -5,13 +5,26 @@ import { getCountryCode } from '../../../../dashboard/utils';
// Determine if a store should see the selective bundle install a/b experiment based on country and chosen industries
// from the profile wizard.
export const isSelectiveBundleInstallSegmentation = (
country,
industrySlugs
) => {
return (
getCountryCode( country ) === 'US' &&
( industrySlugs.includes( 'food-drink' ) ||
industrySlugs.includes( 'other' ) )
);
const SUPPORTED_COUNTRIES = [
'US',
'BR',
'FR',
'ID',
'GB',
'DE',
'VN',
'CA',
'PL',
'MY',
'AU',
'NG',
'GR',
'BE',
'PT',
'DK',
'SE',
'JP',
];
export const isSelectiveBundleInstallSegmentation = ( country ) => {
return SUPPORTED_COUNTRIES.includes( getCountryCode( country ) );
};

View File

@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { __, _n, _x, sprintf } from '@wordpress/i18n';
import { __, _n, sprintf } from '@wordpress/i18n';
import { Component, Fragment } from '@wordpress/element';
import { compose } from '@wordpress/compose';
import {
@ -9,21 +9,11 @@ import {
Card,
CardBody,
CardFooter,
CheckboxControl,
FormToggle,
Popover,
} from '@wordpress/components';
import interpolateComponents from 'interpolate-components';
import { withDispatch, withSelect } from '@wordpress/data';
import { keys, get, pickBy } from 'lodash';
import {
H,
Link,
SelectControl,
Form,
TextControl,
} from '@woocommerce/components';
import { formatValue } from '@woocommerce/number';
import { H, SelectControl, Form, TextControl } from '@woocommerce/components';
import { getSetting } from '@woocommerce/wc-admin-settings';
import {
ONBOARDING_STORE_NAME,
@ -34,15 +24,10 @@ import {
} from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
import { Text } from '@woocommerce/experimental';
import { Icon, info, check } from '@wordpress/icons';
/**
* Internal dependencies
*/
import {
getCountryCode,
getCurrencyRegion,
} from '../../../../../dashboard/utils';
import { CurrencyContext } from '../../../../../lib/currency-context';
import { createNoticesFromResponse } from '../../../../../lib/notices';
import { extensionBenefits } from '../../data/extension-benefits';
@ -56,11 +41,7 @@ const wcAdminAssetUrl = getSetting( 'wcAdminAssetUrl', '' );
class BusinessDetails extends Component {
constructor( props ) {
super();
const settings = get( props, 'settings', {} );
const profileItems = get( props, 'profileItems', {} );
const industrySlugs = get( profileItems, 'industry', [] ).map(
( industry ) => industry.slug
);
const businessExtensions = get(
profileItems,
'business_extensions',
@ -101,15 +82,8 @@ class BusinessDetails extends Component {
'creative-mail-by-constant-contact',
];
this.bundleInstall =
getCountryCode( settings.woocommerce_default_country ) === 'US' &&
( industrySlugs.includes( 'fashion-apparel-accessories' ) ||
industrySlugs.includes( 'health-beauty' ) ) &&
! industrySlugs.includes( 'cbd-other-hemp-derived-products' );
this.onContinue = this.onContinue.bind( this );
this.validate = this.validate.bind( this );
this.getNumberRangeString = this.getNumberRangeString.bind( this );
this.numberFormat = this.numberFormat.bind( this );
}
onCreativeMailInstallAndActivated() {
@ -173,7 +147,6 @@ class BusinessDetails extends Component {
'kliken-marketing-for-google'
),
install_extensions: installExtensions,
bundle_install: this.bundleInstall,
} );
const _updates = {
@ -286,17 +259,6 @@ class BusinessDetails extends Component {
}
getBusinessExtensions( values ) {
if ( this.bundleInstall ) {
return values.install_extensions
? [
'jetpack',
'woocommerce-services',
'woocommerce-payments',
...this.extensions,
]
: [];
}
if ( values.selling_venues === '' ) {
return [];
}
@ -306,67 +268,6 @@ class BusinessDetails extends Component {
);
}
convertCurrency( value ) {
const region = getCurrencyRegion(
this.props.settings.woocommerce_default_country
);
if ( region === 'US' ) {
return value;
}
// These are rough exchange rates from USD. Precision is not paramount.
// The keys here should match the keys in `getCurrencyData`.
const exchangeRates = {
US: 1,
EU: 0.9,
IN: 71.24,
GB: 0.76,
BR: 4.19,
VN: 23172.5,
ID: 14031.0,
BD: 84.87,
PK: 154.8,
RU: 63.74,
TR: 5.75,
MX: 19.37,
CA: 1.32,
};
const exchangeRate = exchangeRates[ region ] || exchangeRates.US;
const digits = exchangeRate.toString().split( '.' )[ 0 ].length;
const multiplier = Math.pow( 10, 2 + digits );
return Math.round( ( value * exchangeRate ) / multiplier ) * multiplier;
}
numberFormat( value ) {
const { getCurrencyConfig } = this.context;
return formatValue( getCurrencyConfig(), 'number', value );
}
getNumberRangeString( min, max = false, format = this.numberFormat ) {
if ( ! max ) {
return sprintf(
_x(
'%s+',
'store product count or revenue',
'woocommerce-admin'
),
format( min )
);
}
return sprintf(
_x(
'%1$s - %2$s',
'store product count or revenue range',
'woocommerce-admin'
),
format( min ),
format( max )
);
}
renderBusinessExtensionHelpText( values ) {
const { isInstallingActivating } = this.props;
const extensions = this.getBusinessExtensions( values );
@ -396,45 +297,19 @@ class BusinessDetails extends Component {
</Text>
);
}
const accountRequiredText = this.bundleInstall
? __(
'User accounts are required to use these features.',
'woocommerce-admin'
)
: '';
return (
<div className="woocommerce-profile-wizard__footnote">
<Text variant="caption" as="p">
{ sprintf(
_n(
'The following plugin will be installed for free: %s. %s',
'The following plugins will be installed for free: %s. %s',
'The following plugin will be installed for free: %s.',
'The following plugins will be installed for free: %s.',
extensions.length,
'woocommerce-admin'
),
extensionsList,
accountRequiredText
extensionsList
) }
</Text>
{ this.bundleInstall && (
<Text variant="caption" as="p">
{ interpolateComponents( {
mixedString: __(
'By installing Jetpack and WooCommerce Shipping plugins for free you agree to our {{link}}Terms of Service{{/link}}.',
'woocommerce-admin'
),
components: {
link: (
<Link
href="https://wordpress.com/tos/"
target="_blank"
type="external"
/>
),
},
} ) }
</Text>
) }
</div>
);
}
@ -476,129 +351,6 @@ class BusinessDetails extends Component {
);
}
renderBusinessExtensionsBundle( values, getInputProps ) {
const { isPopoverVisible } = this.state;
const checkMarkIcon = (
<Icon
className="woocommerce-business-extensions__benefit__check-icon"
icon={ check }
/>
);
return (
<div className="woocommerce-business-extensions">
<label htmlFor="woocommerce-business-extensions__checkbox">
<CheckboxControl
id="woocommerce-business-extensions__checkbox"
{ ...getInputProps( 'install_extensions' ) }
/>
<span className="woocommerce-business-extensions__label-text">
{ interpolateComponents( {
mixedString: __(
'Install recommended {{strong}}free{{/strong}} business features',
'woocommerce-admin'
),
components: {
strong: <strong />,
},
} ) }
<span className="woocommerce-business-extensions__label-subtext">
{ __( 'Requires an account', 'woocommerce-admin' ) }
</span>
</span>
</label>
<div className="woocommerce-business-extensions__popover-wrapper">
<Button
isTertiary
label={ __(
'Learn more about recommended free business features',
'woocommerce-admin'
) }
onClick={ () => {
recordEvent(
'storeprofiler_store_business_details_popover'
);
this.setState( { isPopoverVisible: true } );
} }
>
<Icon icon={ info } />
</Button>
{ isPopoverVisible && (
<Popover
className="woocommerce-business-extensions__popover"
focusOnMount="container"
position="top center"
onClose={ () =>
this.setState( { isPopoverVisible: false } )
}
>
<div className="woocommerce-business-extensions__benefits">
<div className="woocommerce-business-extensions__benefit">
{ checkMarkIcon }
{ __(
'Manage your store on the go with the WooCommerce mobile app',
'woocommerce-admin'
) }
</div>
<div className="woocommerce-business-extensions__benefit">
{ checkMarkIcon }
{ __(
'Accept credit cards with WooCommerce Payments',
'woocommerce-admin'
) }
</div>
<div className="woocommerce-business-extensions__benefit">
{ checkMarkIcon }
{ __(
'Speed & security enhancements',
'woocommerce-admin'
) }
</div>
<div className="woocommerce-business-extensions__benefit">
{ checkMarkIcon }
{ __(
'Automatic sales taxes',
'woocommerce-admin'
) }
</div>
<div className="woocommerce-business-extensions__benefit">
{ checkMarkIcon }
{ __(
'Market on Facebook',
'woocommerce-admin'
) }
</div>
<div className="woocommerce-business-extensions__benefit">
{ checkMarkIcon }
{ __(
'Contact customers with Mailchimp',
'woocommerce-admin'
) }
</div>
<div className="woocommerce-business-extensions__benefit">
{ checkMarkIcon }
{ __(
'Drive sales with Google Ads',
'woocommerce-admin'
) }
</div>
<div className="woocommerce-business-extensions__benefit">
{ checkMarkIcon }
{ __(
'Print shipping labels at home',
'woocommerce-admin'
) }
</div>
</div>
</Popover>
) }
</div>
</div>
);
}
render() {
const {
goToNextStep,
@ -624,15 +376,10 @@ class BusinessDetails extends Component {
validate={ this.validate }
>
{ ( { getInputProps, handleSubmit, values, isValidForm } ) => {
const businessExtensions = this.bundleInstall
? this.renderBusinessExtensionsBundle(
values,
getInputProps
)
: this.renderBusinessExtensions(
values,
getInputProps
);
const businessExtensions = this.renderBusinessExtensions(
values,
getInputProps
);
return (
<Fragment>
@ -773,7 +520,7 @@ class BusinessDetails extends Component {
BusinessDetails.contextType = CurrencyContext;
export const BundleBusinessDetailsStep = compose(
export const BusinessDetailsStepWithExtensionList = compose(
withSelect( ( select ) => {
const {
getSettings,

View File

@ -379,7 +379,10 @@ class BusinessDetails extends Component {
}
renderFreeFeaturesStep() {
const { isInstallingActivating } = this.props;
const { isInstallingActivating, settings, profileItems } = this.props;
const country = settings.woocommerce_default_country
? settings.woocommerce_default_country
: null;
return (
<>
@ -407,6 +410,8 @@ class BusinessDetails extends Component {
<SelectiveExtensionsBundle
isInstallingActivating={ isInstallingActivating }
onSubmit={ this.onContinue }
country={ country }
industry={ profileItems.industry }
/>
</>
);

View File

@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { useState } from '@wordpress/element';
import { useEffect, useState } from '@wordpress/element';
import {
Button,
Card,
@ -21,6 +21,8 @@ import { recordEvent } from '@woocommerce/tracks';
import { AppIllustration } from '../app-illustration';
import './style.scss';
import { setAllPropsToValue } from '../../../../../../lib/collections';
import { getCountryCode } from '../../../../../../dashboard/utils';
import { isWCPaySupported } from '../../../../../../task-list/tasks/payments/wcpay';
const generatePluginDescriptionWithLink = ( description, productName ) => {
return interpolateComponents( {
@ -51,6 +53,14 @@ const installableExtensions = [
),
'woocommerce-payments'
),
isVisible: ( countryCode, industry ) => {
const hasCbdIndustry = ( industry || [] ).some(
( { slug } ) => {
return slug === 'cbd-other-hemp-derived-products';
}
);
return isWCPaySupported( countryCode ) && ! hasCbdIndustry;
},
},
{
slug: 'woocommerce-services',
@ -121,20 +131,6 @@ const installableExtensions = [
},
];
const initialValues = installableExtensions.reduce(
( acc, curr ) => {
const plugins = curr.plugins.reduce( ( pluginAcc, { slug } ) => {
return { ...pluginAcc, [ slug ]: true };
}, {} );
return {
...acc,
...plugins,
};
},
{ install_extensions: true }
);
const FreeBadge = () => {
return (
<div className="woocommerce-admin__business-details__free-badge">
@ -239,12 +235,52 @@ const BundleExtensionCheckbox = ( { onChange, description, isChecked } ) => {
);
};
/**
* Returns plugins that either don't have the acceptedCountryCodes param or one defined
* that includes the passed in country.
*
* @param {Array} plugins list of plugins
* @param {string} country Woo store country
* @param {Array} industry List of selected industries
*/
const getVisiblePlugins = ( plugins, country, industry ) => {
const countryCode = getCountryCode( country );
return plugins.filter(
( plugin ) =>
! plugin.isVisible || plugin.isVisible( countryCode, industry )
);
};
export const SelectiveExtensionsBundle = ( {
isInstallingActivating,
onSubmit,
country,
industry,
} ) => {
const [ showExtensions, setShowExtensions ] = useState( false );
const [ values, setValues ] = useState( initialValues );
const [ values, setValues ] = useState( {} );
useEffect( () => {
const initialValues = installableExtensions.reduce(
( acc, curr ) => {
const plugins = getVisiblePlugins(
curr.plugins,
country,
industry
).reduce( ( pluginAcc, { slug } ) => {
return { ...pluginAcc, [ slug ]: true };
}, {} );
return {
...acc,
...plugins,
};
},
{ install_extensions: true }
);
setValues( initialValues );
}, [ country ] );
const getCheckboxChangeHandler = ( slug ) => {
return ( checked ) => {
@ -315,7 +351,11 @@ export const SelectiveExtensionsBundle = ( {
<div className="woocommerce-admin__business-details__selective-extensions-bundle__category">
{ title }
</div>
{ plugins.map( ( { description, slug } ) => (
{ getVisiblePlugins(
plugins,
country,
industry
).map( ( { description, slug } ) => (
<BundleExtensionCheckbox
key={ slug }
description={ description }

View File

@ -8,7 +8,7 @@ import { ONBOARDING_STORE_NAME, SETTINGS_STORE_NAME } from '@woocommerce/data';
/**
* Internal dependencies
*/
import { BundleBusinessDetailsStep } from './flows/bundle';
import { BusinessDetailsStepWithExtensionList } from './flows/bundle';
import { SelectiveFeaturesBusinessStep } from './flows/selective-bundle';
import './style.scss';
import { isSelectiveBundleInstallSegmentation } from './data/segmentation';
@ -33,13 +33,8 @@ export const BusinessDetailsStep = ( props ) => {
? settings.general.woocommerce_default_country
: null;
const industrySlugs = ( profileItems.industry || [] ).map(
( industry ) => industry.slug
);
const selectiveBundleInstallSegmentation = isSelectiveBundleInstallSegmentation(
country,
industrySlugs
country
);
if ( isLoading ) {
@ -67,5 +62,5 @@ export const BusinessDetailsStep = ( props ) => {
);
}
return <BundleBusinessDetailsStep { ...props } />;
return <BusinessDetailsStepWithExtensionList { ...props } />;
};

View File

@ -21,7 +21,10 @@ import { Products } from './tasks/products';
import Shipping from './tasks/shipping';
import Tax from './tasks/tax';
import Payments from './tasks/payments';
import { installActivateAndConnectWcpay } from './tasks/payments/methods';
import {
installActivateAndConnectWcpay,
isWCPaySupported,
} from './tasks/payments/wcpay';
import { groupListOfObjectsBy } from '../lib/collections';
export function recordTaskViewEvent(
@ -83,8 +86,13 @@ export function getAllTasks( {
const {
completed: profilerCompleted,
product_types: productTypes,
business_extensions: businessExtensions,
} = profileItems;
const woocommercePaymentsSelectedInProfiler = (
businessExtensions || []
).includes( 'woocommerce-payments' );
let purchaseAndInstallText = __(
'Add paid extensions to your store',
'woocommerce-admin'
@ -163,7 +171,6 @@ export function getAllTasks( {
);
onTaskSelect( 'woocommerce-payments' );
return installActivateAndConnectWcpay(
resolve,
reject,
createNotice,
installAndActivatePlugins
@ -172,8 +179,9 @@ export function getAllTasks( {
},
visible:
window.wcAdminFeatures.wcpay &&
woocommercePaymentsSelectedInProfiler &&
woocommercePaymentsInstalled &&
countryCode === 'US',
isWCPaySupported( countryCode ),
additionalInfo: __(
'By setting up, you are agreeing to the <a href="https://wordpress.com/tos/" target="_blank">Terms of Service</a>',
'woocommerce-admin'
@ -190,7 +198,10 @@ export function getAllTasks( {
onTaskSelect( 'payments' );
updateQueryString( { task: 'payments' } );
},
visible: ! woocommercePaymentsInstalled || countryCode !== 'US',
visible:
! woocommercePaymentsInstalled ||
! woocommercePaymentsSelectedInProfiler ||
! isWCPaySupported( countryCode ),
time: __( '2 minutes', 'woocommerce-admin' ),
type: 'setup',
},

View File

@ -2,15 +2,12 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
import interpolateComponents from 'interpolate-components';
import {
getAdminLink,
WC_ASSET_URL as wcAssetUrl,
} from '@woocommerce/wc-admin-settings';
import { Link } from '@woocommerce/components';
import { WC_ADMIN_NAMESPACE } from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
/**
* Internal dependencies
@ -24,7 +21,12 @@ import { MollieLogo } from './images/mollie';
import { PayUIndiaLogo } from './images/payu-india';
import Stripe from './stripe';
import Square from './square';
import WCPay from './wcpay';
import {
WCPay,
WCPayUsageModal,
installActivateAndConnectWcpay,
isWCPaySupported,
} from './wcpay';
import PayPal, { PAYPAL_PLUGIN } from './paypal';
import Klarna from './klarna';
import PayFast from './payfast';
@ -32,47 +34,6 @@ import EWay from './eway';
import Razorpay from './razorpay';
import { Mollie } from './mollie';
import { PayUIndia } from './payu-india';
import WCPayUsageModal from './wcpay-usage-modal';
import { createNoticesFromResponse } from '../../../lib/notices';
export function installActivateAndConnectWcpay(
resolve,
reject,
createNotice,
installAndActivatePlugins
) {
const errorMessage = __(
'There was an error connecting to WooCommerce Payments. Please try again or connect later in store settings.',
'woocommerce-admin'
);
const connect = () => {
apiFetch( {
path: WC_ADMIN_NAMESPACE + '/plugins/connect-wcpay',
method: 'POST',
} )
.then( ( response ) => {
window.location = response.connectUrl;
} )
.catch( () => {
createNotice( 'error', errorMessage );
reject();
} );
};
installAndActivatePlugins( [ 'woocommerce-payments' ] )
.then( () => {
recordEvent( 'woocommerce_payments_install', {
context: 'tasklist',
} );
connect();
} )
.catch( ( error ) => {
createNoticesFromResponse( error );
reject();
} );
}
export function getPaymentMethods( {
activePlugins,
@ -485,18 +446,32 @@ export function getPaymentMethods( {
{ __( 'Settings', 'woocommerce-admin' ) }
</Link>
);
const wcPayFeesLink = (
<Link
href={
'https://docs.woocommerce.com/document/payments/faq/fees/'
}
target="_blank"
type="external"
/>
);
const wooPaymentsCopy = interpolateComponents( {
mixedString: __(
'Accept credit card payments the easy way! {{feesLink}}No setup fees. No monthly fees.{{/feesLink}}',
'woocommerce-admin'
),
components: {
feesLink: wcPayFeesLink,
},
} );
methods.unshift( {
key: 'wcpay',
title: __( 'WooCommerce Payments', 'woocommerce-admin' ),
content: (
<>
{ __(
'Accept credit card payments the easy way! No setup fees. No ' +
'monthly fees. Just 2.9% + $0.30 per transaction ' +
'on U.S. issued cards. ',
'woocommerce-admin'
) }
{ wooPaymentsCopy }
{ wcPayIsConnected && wcPaySettingsLink }
{ ! wcPayIsConnected && <p>{ tosPrompt }</p> }
{ profileItems.setup_client && <p>{ wcPayDocPrompt }</p> }
@ -506,13 +481,12 @@ export function getPaymentMethods( {
before: <WCPayLogo />,
onClick: ( resolve, reject ) => {
return installActivateAndConnectWcpay(
resolve,
reject,
createNotice,
installAndActivatePlugins
);
},
visible: [ 'US', 'PR' ].includes( countryCode ) && ! hasCbdIndustry,
visible: isWCPaySupported( countryCode ) && ! hasCbdIndustry,
plugins: [ 'woocommerce-payments' ],
container: <WCPay />,
isConfigured: wcPayIsConnected,

View File

@ -0,0 +1,10 @@
/**
* Internal dependencies
*/
import WCPayUsageModal from './wcpay-usage-modal';
import WCPay from './wcpay';
export * from './is-supported';
export * from './install-activate-and-connect';
export { WCPay, WCPayUsageModal };

View File

@ -0,0 +1,50 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
import { WC_ADMIN_NAMESPACE } from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
/**
* Internal dependencies
*/
import { createNoticesFromResponse } from '../../../../lib/notices';
export function installActivateAndConnectWcpay(
reject,
createNotice,
installAndActivatePlugins
) {
const errorMessage = __(
'There was an error connecting to WooCommerce Payments. Please try again or connect later in store settings.',
'woocommerce-admin'
);
const connect = () => {
apiFetch( {
path: WC_ADMIN_NAMESPACE + '/plugins/connect-wcpay',
method: 'POST',
} )
.then( ( response ) => {
window.location = response.connectUrl;
} )
.catch( () => {
createNotice( 'error', errorMessage );
reject();
} );
};
installAndActivatePlugins( [ 'woocommerce-payments' ] )
.then( () => {
recordEvent( 'woocommerce_payments_install', {
context: 'tasklist',
} );
connect();
} )
.catch( ( error ) => {
createNoticesFromResponse( error );
reject();
} );
}

View File

@ -0,0 +1,10 @@
export function isWCPaySupported( countryCode ) {
const supportedCountries = [ 'US', 'PR' ];
if (
window.wcAdminFeatures &&
window.wcAdminFeatures[ 'wcpay/support-international-countries' ]
) {
supportedCountries.push( 'AU', 'CA', 'GB', 'IE', 'NZ' );
}
return supportedCountries.includes( countryCode );
}

View File

@ -10,7 +10,7 @@ import { Link } from '@woocommerce/components';
/**
* Internal dependencies
*/
import UsageModal from '../../../profile-wizard/steps/usage-modal';
import UsageModal from '../../../../profile-wizard/steps/usage-modal';
const WCPayUsageModal = () => {
const query = getQuery();

View File

@ -16,6 +16,7 @@
"settings": false,
"shipping-label-banner": true,
"store-alerts": true,
"wcpay": true
"wcpay": true,
"wcpay/support-international-countries": true
}
}

View File

@ -16,6 +16,7 @@
"settings": false,
"shipping-label-banner": true,
"store-alerts": true,
"wcpay": true
"wcpay": true,
"wcpay/support-international-countries": true
}
}

View File

@ -16,6 +16,7 @@
"settings": false,
"shipping-label-banner": true,
"store-alerts": true,
"wcpay": true
"wcpay": true,
"wcpay/support-international-countries": true
}
}

View File

@ -101,6 +101,7 @@ Release and roadmap notes are available on the [WooCommerce Developers Blog](htt
- Dev: Added warning when WC-Admin is active but not being used #6453
- Add: Remove Mollie promo note on install #6510
- Add: Remote Inbox Notifications rule to trigger when WooCommerce Admin is upgraded. #6040
- Feature: Increase target audience for business feature step. #6508
== 2.1.0 ==

View File

@ -5,7 +5,11 @@
/**
* Internal dependencies
*/
import { setCheckboxToUnchecked, clickContinue } from './utils';
import {
setCheckboxToUnchecked,
clickContinue,
getElementByText,
} from './utils';
import { waitForElementCount } from '../../utils/lib';
const config = require( 'config' );
@ -22,11 +26,19 @@ async function fillOutDropdowns() {
} );
// Fill currently selling elsewhere
await selectControls[ 1 ].click();
await page.waitForSelector( '.woocommerce-select-control__listbox' );
await expect( page ).toClick( '.woocommerce-select-control__option', {
text: config.get( 'onboardingwizard.sellingelsewhere' ),
const value = await page.evaluate( () => {
const inputs = document.querySelectorAll(
'.woocommerce-select-control .woocommerce-select-control__control-input'
);
return inputs[ 1 ].value;
} );
if ( value !== config.get( 'onboardingwizard.sellingelsewhere' ) ) {
await selectControls[ 1 ].click();
await page.waitForSelector( '.woocommerce-select-control__listbox' );
await expect( page ).toClick( '.woocommerce-select-control__option', {
text: config.get( 'onboardingwizard.sellingelsewhere' ),
} );
}
}
export async function completeBusinessSection() {
@ -34,10 +46,13 @@ export async function completeBusinessSection() {
// Site is in US so the "Install recommended free business features"
// checkbox is present, uncheck it.
const installFeaturesCheckbox = await page.$(
'#woocommerce-business-extensions__checkbox'
const installFeaturesCheckboxes = await page.$$(
'.woocommerce-profile-wizard__benefit .components-form-toggle__input'
);
await setCheckboxToUnchecked( installFeaturesCheckbox );
// Uncheck all checkboxes, to avoid installing plugins
for ( const checkbox of installFeaturesCheckboxes ) {
await setCheckboxToUnchecked( checkbox );
}
await clickContinue();
}
@ -47,3 +62,36 @@ export async function completeSelectiveBundleInstallBusinessDetailsTab() {
await page.click( 'button.is-primary' );
}
export async function unselectAllFeaturesAndContinue(
shouldWCPayBeListed = true
) {
const expandButtonSelector =
'.woocommerce-admin__business-details__selective-extensions-bundle__expand';
await page.waitForSelector( expandButtonSelector );
await page.click( expandButtonSelector );
// Confirm that expanding the list shows all the extensions available to install.
await waitForElementCount(
page,
'.components-checkbox-control__input',
shouldWCPayBeListed ? 8 : 7
);
const wcPayLabel = await getElementByText( 'a', 'WooCommerce Payments' );
if ( shouldWCPayBeListed ) {
expect( wcPayLabel ).toBeDefined();
} else {
expect( wcPayLabel ).toBeUndefined();
}
const allCheckboxes = await page.$$(
'.components-checkbox-control__input'
);
// Uncheck all checkboxes, to avoid installing plugins
for ( const checkbox of allCheckboxes ) {
await setCheckboxToUnchecked( checkbox );
}
await page.click( 'button.is-primary' );
}

View File

@ -15,11 +15,18 @@ import { completeProductTypesSection } from './complete-product-types-section';
import {
completeBusinessSection,
completeSelectiveBundleInstallBusinessDetailsTab,
unselectAllFeaturesAndContinue,
} from './complete-business-section';
import { completeThemeSelectionSection } from './complete-theme-selection-section';
import { completeBenefitsSection } from './complete-benefits-section';
import { waitForElementCount } from '../../utils/lib';
import { getElementProperty, setCheckboxToUnchecked } from './utils';
import {
clickOnTaskList,
getTaskList,
onHomescreen,
possibleDismissWelcomeModal,
TaskTitles,
} from './homescreen';
import { getElementByText } from './utils';
/**
* This tests a default, happy path for the onboarding wizard.
@ -30,25 +37,27 @@ describe( 'Store owner can complete onboarding wizard', () => {
it( 'can complete the store details section', completeStoreDetailsSection );
it( 'can complete the industry section', completeIndustrySection );
it( 'can complete the product types section', completeProductTypesSection );
it( 'can complete the business section', completeBusinessSection );
it( 'can complete the business section', async () =>
await completeSelectiveBundleInstallBusinessDetailsTab() );
it( 'can unselect all business features and contine', async () =>
await unselectAllFeaturesAndContinue() );
it(
'can complete the theme selection section',
completeThemeSelectionSection
);
it( 'can complete the benefits section', completeBenefitsSection );
} );
/**
* A non-US store doesn't get the "install recommended features" checkbox.
*/
describe( 'Non-US store does not get the install recommended features checkbox', () => {
describe( 'A spanish store does not get the install recommended features tab, but sees the benefits section', () => {
it( 'can log in', StoreOwnerFlow.login );
it( 'can start the profile wizard', StoreOwnerFlow.startProfileWizard );
it( 'can complete the store details section', async () => {
await completeStoreDetailsSection( {
countryRegionSubstring: 'australia',
countryRegionSelector: 'AU\\:QLD',
countryRegion: 'Australia - Queensland',
countryRegionSubstring: 'spain',
countryRegionSelector: 'ES\\:B',
countryRegion: 'Spain - Barcelona',
} );
} );
it( 'can complete the industry section', async () => {
@ -62,12 +71,47 @@ describe( 'Non-US store does not get the install recommended features checkbox',
expect( installFeaturesCheckbox ).toBe( null );
} );
it( 'can complete the business section', async () =>
await completeBusinessSection() );
it(
'can complete the theme selection section',
completeThemeSelectionSection
);
it( 'can complete the benefits section', completeBenefitsSection );
it( 'should display the choose payments task, and not the woocommerce payments task', async () => {
await onHomescreen();
await possibleDismissWelcomeModal();
const tasks = await getTaskList();
expect( tasks ).toContain( TaskTitles.addPayments );
expect( tasks ).not.toContain( TaskTitles.wooPayments );
} );
it( 'should not display woocommerce payments as a payments option', async () => {
const tasks = await getTaskList();
const index = tasks.indexOf( TaskTitles.addPayments );
await clickOnTaskList( index );
await page.waitForFunction(
'document.querySelector(".woocommerce-layout__header-heading").innerText == "Choose payment methods"'
);
const wcPayLabel = await getElementByText(
'h2',
'WooCommerce Payments'
);
expect( wcPayLabel ).toBeUndefined();
} );
} );
describe( 'A US store with industry "other" can complete the selective bundle install a/b test. ', () => {
describe( 'A japanese store can complete the selective bundle install but does not include WCPay. ', () => {
it( 'can log in', StoreOwnerFlow.login );
it( 'can start the profile wizard', StoreOwnerFlow.startProfileWizard );
it( 'can complete the store details section', completeStoreDetailsSection );
it( 'can complete the store details section', async () => {
await completeStoreDetailsSection( {
countryRegionSubstring: 'japan',
countryRegionSelector: 'JP\\:JP01',
countryRegion: 'Japan — Hokkaido',
} );
} );
// JP:JP01
it( 'can choose the "Other" industry', async () => {
await chooseIndustries( [ 'Other' ] );
} );
@ -77,32 +121,18 @@ describe( 'A US store with industry "other" can complete the selective bundle in
} );
it( 'can choose not to install any extensions', async () => {
const expandButtonSelector =
'.woocommerce-admin__business-details__selective-extensions-bundle__expand';
await page.waitForSelector( expandButtonSelector );
await page.click( expandButtonSelector );
// Confirm that expanding the list shows all the extensions available to install.
await waitForElementCount(
page,
'.components-checkbox-control__input',
8
);
const allCheckboxes = await page.$$(
'.components-checkbox-control__input'
);
// Uncheck all checkboxes, to avoid installing plugins
for ( const checkbox of allCheckboxes ) {
await setCheckboxToUnchecked( checkbox );
}
await page.click( 'button.is-primary' );
await unselectAllFeaturesAndContinue( false );
} );
it(
'can finish the rest of the wizard successfully',
completeThemeSelectionSection
);
it( 'should display the choose payments task, and not the woocommerce payments task', async () => {
await onHomescreen();
await possibleDismissWelcomeModal();
const tasks = await getTaskList();
expect( tasks ).toContain( TaskTitles.addPayments );
expect( tasks ).not.toContain( TaskTitles.wooPayments );
} );
} );

View File

@ -0,0 +1,68 @@
import { getElementByText } from './utils';
export async function onHomescreen() {
// Wait for Benefits section to appear
await page.waitForSelector( '.woocommerce-homescreen' );
await page.waitForFunction(
'document.querySelector(".woocommerce-layout__inbox-title").innerText == "Your store today"'
);
}
export async function possibleDismissWelcomeModal() {
// Wait for Benefits section to appear
const modal = await getElementByText(
'h2',
'Welcome to your WooCommerce stores online HQ!'
);
if ( modal ) {
let nextButton = await getElementByText( 'button', 'Next' );
await nextButton.click();
await page.waitForFunction(
'document.querySelector(".woocommerce__welcome-modal h2").innerText == "A personalized inbox full of relevant advice"'
);
nextButton = await getElementByText( 'button', 'Next' );
await nextButton.click();
await page.waitForFunction(
'document.querySelector(".woocommerce__welcome-modal h2").innerText == "Good data leads to smart business decisions"'
);
nextButton = await page.$( '.components-guide__finish-button' );
await nextButton.click();
}
}
export async function getTaskList() {
// Log out link in admin bar is not visible so can't be clicked directly.
const list = await page.$$eval(
'.woocommerce-task-card .woocommerce-list__item',
( am ) => am.map( ( e ) => e.textContent )
);
return list.map( ( item ) => {
const match = item.match( /(.+)[0-9] minute/ );
if ( match && match.length > 1 ) {
return match[ 1 ];
}
return item;
} );
}
export async function clickOnTaskList( index ) {
const taskItems = await page.$$(
'.woocommerce-task-card .woocommerce-list__item'
);
// Fill the number of products you plan to sell
await taskItems[ index ].click();
await page.waitForNavigation( { waitUntil: 'networkidle0' } );
}
export const TaskTitles = {
storeDetails: 'Store details',
addPayments: 'Choose payment methods',
wooPayments:
'Set up WooCommerce PaymentsBy setting up, you are agreeing to the Terms of Service2 minutes',
addProducts: 'Add products',
addTaxRates: 'Add tax rates',
setUpShippingCosts: 'Set up shipping costs',
personalizeStore: 'Personalize your store',
};