* Remove unused options

* Remove preloade tracking option

* Remove preloaded wc_connect_options

* Remove preloaded modal options

* Refactor requesting options into task list component

* Remove preloaded dismissed and completed tasks

* Add changelog entry

* Wait for modal before attempting to dismiss

* Attempt to make closing welcome modal more robust

* Update the payment E2E tests to support new app flow

* Update isResolving selectors to hasFinishedResolution

Co-authored-by: Lourens Schep <lourensschep@gmail.com>
This commit is contained in:
Joshua T Flowers 2021-08-10 14:58:01 -04:00 committed by GitHub
parent 5ade1e1c66
commit 0ab7c7a3ec
18 changed files with 178 additions and 131 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: Tweak
Remove the preloaded onboarding options

View File

@ -48,7 +48,6 @@ export const Layout = ( {
defaultHomescreenLayout,
isBatchUpdating,
query,
requestingTaskList,
taskListComplete,
bothTaskListsHidden,
shouldShowWelcomeModal,
@ -115,10 +114,6 @@ export const Layout = ( {
const renderTaskList = () => {
const isSingleTask = Boolean( query.task );
if ( requestingTaskList && ! isSingleTask ) {
return <TaskListPlaceholder />;
}
return (
<Suspense
fallback={ isSingleTask ? null : <TaskListPlaceholder /> }
@ -160,10 +155,6 @@ export const Layout = ( {
};
Layout.propTypes = {
/**
* If the task list option is being fetched.
*/
requestingTaskList: PropTypes.bool.isRequired,
/**
* If the task list has been completed.
*/
@ -193,7 +184,7 @@ Layout.propTypes = {
export default compose(
withSelect( ( select ) => {
const { isNotesRequesting } = select( NOTES_STORE_NAME );
const { getOption, isResolving, hasFinishedResolution } = select(
const { getOption, hasFinishedResolution } = select(
OPTIONS_STORE_NAME
);
@ -242,20 +233,8 @@ export default compose(
bothTaskListsHidden:
isTaskListHidden &&
getOption( 'woocommerce_extended_task_list_hidden' ) === 'yes',
requestingTaskList:
isResolving( 'getOption', [
'woocommerce_task_list_complete',
] ) ||
isResolving( 'getOption', [
'woocommerce_task_list_hidden',
] ) ||
isResolving( 'getOption', [
'woocommerce_extended_task_list_hidden',
] ),
taskListComplete:
! isResolving( 'getOption', [
'woocommerce_task_list_complete',
] ) && getOption( 'woocommerce_task_list_complete' ) === 'yes',
getOption( 'woocommerce_task_list_complete' ) === 'yes',
};
} ),
withDispatch( ( dispatch ) => ( {

View File

@ -42,22 +42,6 @@ jest.mock( '@wordpress/element', () => {
} );
describe( 'Homescreen Layout', () => {
it( 'should show TaskList placeholder when loading', () => {
const { container } = render(
<Layout
requestingTaskList
bothTaskListsHidden={ false }
query={ {} }
updateOptions={ () => {} }
/>
);
const placeholder = container.querySelector(
'.woocommerce-task-card.is-loading'
);
expect( placeholder ).toBeInTheDocument();
} );
it( 'should show TaskList inline', () => {
const { container } = render(
<Layout

View File

@ -16,7 +16,11 @@ import { compose } from '@wordpress/compose';
import { withDispatch, withSelect } from '@wordpress/data';
import { Form } from '@woocommerce/components';
import { getSetting } from '@woocommerce/wc-admin-settings';
import { ONBOARDING_STORE_NAME, SETTINGS_STORE_NAME } from '@woocommerce/data';
import {
ONBOARDING_STORE_NAME,
OPTIONS_STORE_NAME,
SETTINGS_STORE_NAME,
} from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
import { Text } from '@woocommerce/experimental';
import { Icon, info } from '@wordpress/icons';
@ -192,7 +196,7 @@ class StoreDetails extends Component {
isStoreDetailsPopoverVisible,
isSkipSetupPopoverVisible,
} = this.state;
const { skipProfiler, isUpdatingProfileItems } = this.props;
const { skipProfiler, isBusy } = this.props;
/* eslint-disable @wordpress/i18n-no-collapsible-whitespace */
const skipSetupText = __(
@ -308,10 +312,8 @@ class StoreDetails extends Component {
<Button
isPrimary
onClick={ handleSubmit }
isBusy={ isUpdatingProfileItems }
disabled={
! isValidForm || isUpdatingProfileItems
}
isBusy={ isBusy }
disabled={ ! isValidForm || isBusy }
>
{ __( 'Continue', 'woocommerce-admin' ) }
</Button>
@ -378,6 +380,7 @@ export default compose(
getProfileItems,
isOnboardingRequesting,
} = select( ONBOARDING_STORE_NAME );
const { isResolving } = select( OPTIONS_STORE_NAME );
const profileItems = getProfileItems();
const isProfileItemsError = Boolean(
@ -386,14 +389,16 @@ export default compose(
const { general: settings = {} } = getSettings( 'general' );
const isSettingsError = Boolean( getSettingsError( 'general' ) );
const isUpdatingProfileItems =
const isBusy =
isOnboardingRequesting( 'updateProfileItems' ) ||
isUpdateSettingsRequesting( 'general' );
isUpdateSettingsRequesting( 'general' ) ||
isResolving( 'getOption', [ 'woocommerce_allow_tracking' ] );
return {
isProfileItemsError,
isSettingsError,
profileItems,
isUpdatingProfileItems,
isBusy,
settings,
};
} ),

View File

@ -95,9 +95,14 @@ class UsageModal extends Component {
}
render() {
const { allowTracking, isResolving, onClose, onContinue } = this.props;
if ( isResolving ) {
return null;
}
// Bail if site has already opted in to tracking
if ( this.props.allowTracking ) {
const { onClose, onContinue } = this.props;
if ( allowTracking ) {
onClose();
onContinue();
return null;
@ -172,17 +177,19 @@ export default compose(
getOption,
getOptionsUpdatingError,
isOptionsUpdating,
hasFinishedResolution,
} = select( OPTIONS_STORE_NAME );
const allowTracking =
getOption( 'woocommerce_allow_tracking' ) === 'yes';
const isRequesting = Boolean( isOptionsUpdating() );
const hasErrors = Boolean( getOptionsUpdatingError() );
return {
allowTracking,
isRequesting,
hasErrors,
allowTracking: getOption( 'woocommerce_allow_tracking' ) === 'yes',
isRequesting: Boolean( isOptionsUpdating() ),
isResolving:
! hasFinishedResolution( 'getOption', [
'woocommerce_allow_tracking',
] ) ||
typeof getOption( 'woocommerce_allow_tracking' ) ===
'undefined',
hasErrors: Boolean( getOptionsUpdatingError() ),
};
} ),
withDispatch( ( dispatch ) => {

View File

@ -33,7 +33,7 @@ const taskDashboardSelect = ( select ) => {
ONBOARDING_STORE_NAME
);
const { getSettings } = select( SETTINGS_STORE_NAME );
const { getOption } = select( OPTIONS_STORE_NAME );
const { getOption, hasFinishedResolution } = select( OPTIONS_STORE_NAME );
const {
getActivePlugins,
getInstalledPlugins,
@ -83,6 +83,28 @@ const taskDashboardSelect = ( select ) => {
profileItems,
trackedCompletedTasks,
hasCompleteAddress,
isResolving:
! hasFinishedResolution( 'getOption', [
'woocommerce_task_list_complete',
] ) ||
! hasFinishedResolution( 'getOption', [
'woocommerce_task_list_hidden',
] ) ||
! hasFinishedResolution( 'getOption', [
'woocommerce_extended_task_list_complete',
] ) ||
! hasFinishedResolution( 'getOption', [
'woocommerce_extended_task_list_hidden',
] ) ||
! hasFinishedResolution( 'getOption', [
'woocommerce_task_list_remind_me_later_tasks',
] ) ||
! hasFinishedResolution( 'getOption', [
'woocommerce_task_list_tracked_completed_tasks',
] ) ||
! hasFinishedResolution( 'getOption', [
'woocommerce_task_list_dismissed_tasks',
] ),
};
};
@ -106,6 +128,7 @@ const TaskDashboard = ( { userPreferences, query } ) => {
isExtendedTaskListHidden,
isExtendedTaskListComplete,
hasCompleteAddress,
isResolving,
} = useSelect( taskDashboardSelect );
const [ isCartModalOpen, setIsCartModalOpen ] = useState( false );
@ -238,6 +261,10 @@ const TaskDashboard = ( { userPreferences, query } ) => {
);
}
if ( isResolving ) {
return <TaskListPlaceholder />;
}
const scrollToExtendedList =
window.location.hash.substr( 1 ) === 'extended_task_list';
@ -272,7 +299,6 @@ const TaskDashboard = ( { userPreferences, query } ) => {
}
onHide={ () =>
updateOptions( {
woocommerce_task_list_prompt_shown: true,
woocommerce_default_homepage_layout:
'two_columns',
} )

View File

@ -506,10 +506,9 @@ export default compose(
const { getSettings, isUpdateSettingsRequesting } = select(
SETTINGS_STORE_NAME
);
const { getOption, isResolving: isOptionResolving } = select(
const { getOption, hasFinishedResolution } = select(
OPTIONS_STORE_NAME
);
const {
getActivePlugins,
isJetpackConnected,
@ -547,10 +546,12 @@ export default compose(
isUpdateSettingsRequesting( 'general' );
const isResolving =
isPluginsRequesting( 'getJetpackConnectUrl' ) ||
isOptionResolving( 'getOption', [
! hasFinishedResolution( 'getOption', [
'woocommerce_setup_jetpack_opted_in',
] ) ||
jetpackOptIn === undefined;
! hasFinishedResolution( 'getOption', [ 'wc_connect_options' ] ) ||
jetpackOptIn === undefined ||
connectOptions === undefined;
return {
countryCode,

View File

@ -198,6 +198,27 @@ describe( 'TaskDashboard and TaskList', () => {
).toBeDefined();
} );
it( 'should show TaskList placeholder when loading', () => {
useSelect.mockImplementation( () => ( {
isResolving: true,
} ) );
getAllTasks.mockReturnValue( tasks );
const { container } = render(
<TaskDashboard
requestingTaskList
bothTaskListsHidden={ false }
query={ {} }
updateOptions={ () => {} }
/>
);
const placeholder = container.querySelector(
'.woocommerce-task-card.is-loading'
);
expect( placeholder ).toBeInTheDocument();
} );
it( 'renders a dismiss button for tasks that are optional and incomplete', async () => {
apiFetch.mockResolvedValue( {} );
getAllTasks.mockReturnValue( tasks );
@ -320,7 +341,6 @@ describe( 'TaskDashboard and TaskList', () => {
userEvent.click( getByRole( 'button', { name: 'Hide this' } ) );
expect( updateOptions ).toHaveBeenCalledWith( {
woocommerce_task_list_prompt_shown: true,
woocommerce_default_homepage_layout: 'two_columns',
} );
} );

View File

@ -47,7 +47,6 @@ A few new WordPress options have been introduced to store information and settin
* `woocommerce_task_list_hidden`. This option is used to conditionally show the entire task list. The task list can be hidden by the user after they have completed all tasks. Hiding the wizard stops it from showing in both full screen mode, and the collapsed inline version that shows above the dashboard analytics cards.
* `woocommerce_task_list_welcome_modal_dismissed`. This option is used to show a congratulations modal during the transition between the profile wizard and task list.
* `woocommerce_task_list_prompt_shown`. This option is used to conditionally show the "Is this card useful?" snackbar notice, shown once right after a user completes all the task list tasks.
We also use existing options from WooCommerce Core or extensions like WooCommerce Shipping & Tax or Stripe. The list below may not be complete, as new tasks are introduced, but you can generally find usage of these by searching for the [getOptions selector](https://github.com/woocommerce/woocommerce-admin/search?q=getOptions&unscoped_q=getOptions).

View File

@ -9185,9 +9185,9 @@
"integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
},
"@types/puppeteer": {
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.3.tgz",
"integrity": "sha512-3nE8YgR9DIsgttLW+eJf6mnXxq8Ge+27m5SU3knWmrlfl6+KOG0Bf9f7Ua7K+C4BnaTMAh3/UpySqdAYvrsvjg==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-4.0.1.tgz",
"integrity": "sha512-AgoyBI8JCmtbqlvU0qkssgCSBj+S3JCH6xk4+KLr1D46S/ER5IwqHpK3Qy30BMab736FpVD2ELVWkrtqPQg1/w==",
"dev": true,
"requires": {
"@types/node": "*"
@ -10322,6 +10322,7 @@
"version": "file:packages/components",
"dev": true,
"requires": {
"@storybook/addon-knobs": "^6.3.0",
"@woocommerce/csv-export": "file:packages/csv-export",
"@woocommerce/currency": "file:packages/currency",
"@woocommerce/data": "file:packages/data",
@ -10361,6 +10362,24 @@
"react-transition-group": "4.4.1"
},
"dependencies": {
"@storybook/addon-knobs": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@storybook/addon-knobs/-/addon-knobs-6.3.0.tgz",
"integrity": "sha512-wsZZ1t38KHdaxzrc9oPyiIJDihJnjHRRabrENQbylktJwETEjb2z3eX0iBRJGiz/YCHO+tGd0ItyZArOdijT6g==",
"dev": true,
"requires": {
"core-js": "^3.8.2",
"escape-html": "^1.0.3",
"fast-deep-equal": "^3.1.3",
"global": "^4.4.0",
"lodash": "^4.17.20",
"prop-types": "^15.7.2",
"qs": "^6.10.0",
"react-colorful": "^5.1.2",
"react-lifecycles-compat": "^3.0.4",
"react-select": "^3.2.0"
}
},
"@wordpress/api-fetch": {
"version": "3.23.1",
"resolved": "https://registry.npmjs.org/@wordpress/api-fetch/-/api-fetch-3.23.1.tgz",
@ -10613,6 +10632,15 @@
"lodash": "^4.17.19"
}
},
"qs": {
"version": "6.10.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz",
"integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==",
"dev": true,
"requires": {
"side-channel": "^1.0.4"
}
},
"uuid": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",

View File

@ -158,7 +158,7 @@
"@types/history": "4.7.8",
"@types/jest": "26.0.23",
"@types/lodash": "4.14.170",
"@types/puppeteer": "5.4.3",
"@types/puppeteer": "^4.0.1",
"@types/react-router-dom": "5.1.7",
"@types/react-transition-group": "4.4.1",
"@types/wordpress__components": "9.8.6",

View File

@ -72,7 +72,7 @@ export class OnboardingWizard extends BasePage {
await this.page.waitForNavigation( {
waitUntil: 'networkidle0',
timeout: 2000,
timeout: 4000,
} );
}
}

View File

@ -40,11 +40,11 @@ export class PaymentsSetup extends BasePage {
}
async methodHasBeenSetup( method: PaymentMethod ) {
await getElementByText(
'button',
'Manage',
`.woocommerce-task-payment-${ method }`
);
const selector = `.woocommerce-task-payment-${ method }`;
await this.page.waitForSelector( selector );
expect(
await getElementByText( '*', 'Manage', selector )
).toBeDefined();
}
async enableCashOnDelivery() {

View File

@ -1,7 +1,10 @@
/**
* Internal dependencies
*/
import { getElementByText, waitForElementByText } from '../utils/actions';
import {
waitForElementByText,
waitForElementByTextWithoutThrow,
} from '../utils/actions';
import { BasePage } from './BasePage';
export class WcHomescreen extends BasePage {
@ -13,16 +16,20 @@ export class WcHomescreen extends BasePage {
}
async possiblyDismissWelcomeModal() {
// Wait for Benefits section to appear
const modal = await getElementByText(
const modalText = 'Welcome to your WooCommerce stores online HQ!';
const modal = await waitForElementByTextWithoutThrow(
'h2',
'Welcome to your WooCommerce stores online HQ!'
modalText,
10
);
if ( modal ) {
await this.clickButtonWithText( 'Next' );
await this.page.waitFor( 1000 );
await this.clickButtonWithText( 'Next' );
await this.page.waitFor( 1000 );
await this.click( '.components-guide__finish-button' );
await this.page.waitFor( 500 );
}
}
@ -45,7 +52,7 @@ export class WcHomescreen extends BasePage {
}
async clickOnTaskList( taskTitle: string ) {
const item = await waitForElementByText( 'span', taskTitle );
const item = await waitForElementByText( '*', taskTitle );
if ( ! item ) {
throw new Error(
@ -56,4 +63,11 @@ export class WcHomescreen extends BasePage {
await waitForElementByText( 'h1', taskTitle );
}
}
async waitForNotesRequestToBeLoaded() {
return await this.page.waitForResponse( ( response ) => {
const url = encodeURIComponent( response.url() );
return url.includes( '/wc-analytics/admin/notes' ) && response.ok();
} );
}
}

View File

@ -1,3 +1,8 @@
/**
* External dependencies
*/
import { takeScreenshotFor } from '@woocommerce/e2e-environment';
/**
* Internal dependencies
*/
@ -30,6 +35,7 @@ const testAdminPaymentSetupTask = () => {
await profileWizard.skipStoreSetup();
await homeScreen.isDisplayed();
await takeScreenshotFor( 'Payment setup task home screen' );
await homeScreen.possiblyDismissWelcomeModal();
} );
@ -54,12 +60,17 @@ const testAdminPaymentSetupTask = () => {
swiftCode: 'ABBA',
} );
await homeScreen.isDisplayed();
await homeScreen.clickOnTaskList( 'Set up payments' );
await paymentsSetup.isDisplayed();
await paymentsSetup.methodHasBeenSetup( 'bacs' );
} );
it( 'Enabling cash on delivery enables the payment method', async () => {
await paymentsSetup.enableCashOnDelivery();
await homeScreen.isDisplayed();
await homeScreen.clickOnTaskList( 'Set up payments' );
await paymentsSetup.isDisplayed();
await paymentsSetup.methodHasBeenSetup( 'cod' );
} );
} );

View File

@ -104,7 +104,7 @@ const getElementByText = async (
parent = await page.$( parentSelector );
}
const els = await ( parent || page ).$x(
`//${ element }[contains(text(), '${ text }')]`
`//${ element }[contains(text(), "${ text }")]`
);
return els[ 0 ];
};
@ -115,12 +115,28 @@ const waitForElementByText = async (
options?: { timeout?: number }
): Promise< ElementHandle | null > => {
const els = await page.waitForXPath(
`//${ element }[contains(text(), '${ text }')]`,
`//${ element }[contains(text(), "${ text }")]`,
options
);
return els;
};
export const waitForElementByTextWithoutThrow = async (
element: string,
text: string,
timeoutInSeconds = 5
) => {
let selected = await getElementByText( element, text );
for ( let s = 0; s < timeoutInSeconds; s++ ) {
if ( selected ) {
break;
}
await page.waitFor( 1000 );
selected = await getElementByText( element, text );
}
return Boolean( selected );
};
export {
uiUnblocked,
verifyPublishAndTrash,

View File

@ -0,0 +1 @@
declare module '@woocommerce/e2e-environment';

View File

@ -214,7 +214,6 @@ class Onboarding {
add_filter( 'woocommerce_components_settings', array( $this, 'component_settings' ), 20 );
// New settings injection.
add_filter( 'woocommerce_shared_settings', array( $this, 'component_settings' ), 20 );
add_filter( 'woocommerce_admin_preload_options', array( $this, 'preload_options' ) );
add_filter( 'woocommerce_admin_preload_settings', array( $this, 'preload_settings' ) );
add_filter( 'woocommerce_admin_is_loading', array( $this, 'is_loading' ) );
add_filter( 'woocommerce_show_admin_notice', array( $this, 'remove_install_notice' ), 10, 2 );
@ -689,53 +688,6 @@ class Onboarding {
return $settings;
}
/**
* Preload options to prime state of the application.
*
* @param array $options Array of options to preload.
* @return array
*/
public function preload_options( $options ) {
$options[] = 'woocommerce_task_list_complete';
$options[] = 'woocommerce_task_list_do_this_later';
$options[] = 'woocommerce_task_list_hidden';
$options[] = 'woocommerce_extended_task_list_complete';
$options[] = 'woocommerce_extended_task_list_hidden';
$options[] = 'woocommerce_task_list_remind_me_later_tasks';
if ( ! self::should_show_tasks() && ! self::should_show_profiler() ) {
return $options;
}
$options[] = 'wc_connect_options';
$options[] = 'woocommerce_task_list_welcome_modal_dismissed';
$options[] = 'woocommerce_welcome_from_calypso_modal_dismissed';
$options[] = 'woocommerce_task_list_prompt_shown';
$options[] = 'woocommerce_task_list_tracked_completed_tasks';
$options[] = 'woocommerce_task_list_dismissed_tasks';
$options[] = 'woocommerce_allow_tracking';
$options[] = 'woocommerce_woo-mercado-pago-basic_settings';
$options[] = 'woocommerce_stripe_settings';
$options[] = 'woocommerce-ppcp-settings';
$options[] = 'woocommerce_ppcp-gateway_settings';
$options[] = 'wc_square_refresh_tokens';
$options[] = 'woocommerce_square_credit_card_settings';
$options[] = 'woocommerce_payfast_settings';
$options[] = 'woocommerce_paystack_settings';
$options[] = 'woocommerce_kco_settings';
$options[] = 'woocommerce_klarna_payments_settings';
$options[] = 'woocommerce_cod_settings';
$options[] = 'woocommerce_bacs_settings';
$options[] = 'woocommerce_bacs_accounts';
$options[] = 'woocommerce_woocommerce_payments_settings';
$options[] = 'woocommerce_eway_settings';
$options[] = 'woocommerce_razorpay_settings';
$options[] = 'woocommerce_payubiz_settings';
$options[] = 'woocommerce_mollie_payments_settings';
return $options;
}
/**
* Preload WC setting options to prime state of the application.
*