WooCommerce Shipping order page prompt (https://github.com/woocommerce/woocommerce-admin/pull/3955)
Co-authored-by: David Stone <david@nnucomputerwhiz.com> Co-authored-by: Chris Shultz <chris.shultz@automattic.com> Co-authored-by: Harris Wong <harris.wong@a8c.com>
This commit is contained in:
parent
b50ad35d63
commit
b71bc861fb
|
@ -21,9 +21,9 @@ output 2 "Creating archive... 🎁"
|
|||
|
||||
ZIP_FILE=$1
|
||||
|
||||
build_files=$(ls dist/*/*.{js,css})
|
||||
build_files=$(find dist/ \( -name '*.js' -o -name '*.css' \))
|
||||
|
||||
zip -r ${ZIP_FILE} \
|
||||
zip -r "${ZIP_FILE}" \
|
||||
woocommerce-admin.php \
|
||||
uninstall.php \
|
||||
includes/ \
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Component } from '@wordpress/element';
|
||||
import { Button, Modal } from '@wordpress/components';
|
||||
import { withDispatch } from '@wordpress/data';
|
||||
import { compose } from '@wordpress/compose';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import '../style.scss';
|
||||
|
||||
export class DismissModal extends Component {
|
||||
setDismissed = ( timestamp ) => {
|
||||
this.props.updateOptions( {
|
||||
woocommerce_shipping_dismissed_timestamp: timestamp,
|
||||
} );
|
||||
};
|
||||
|
||||
hideBanner = () => {
|
||||
document.getElementById(
|
||||
'woocommerce-admin-print-label'
|
||||
).style.display = 'none';
|
||||
};
|
||||
|
||||
remindMeLaterClicked = () => {
|
||||
const { onCloseAll, trackElementClicked } = this.props;
|
||||
this.setDismissed( Date.now() );
|
||||
onCloseAll();
|
||||
this.hideBanner();
|
||||
trackElementClicked( 'shipping_banner_dismiss_modal_remind_me_later' );
|
||||
};
|
||||
|
||||
closeForeverClicked = () => {
|
||||
const { onCloseAll, trackElementClicked } = this.props;
|
||||
this.setDismissed( -1 );
|
||||
onCloseAll();
|
||||
this.hideBanner();
|
||||
trackElementClicked( 'shipping_banner_dismiss_modal_close_forever' );
|
||||
};
|
||||
|
||||
render() {
|
||||
const { onClose, visible } = this.props;
|
||||
|
||||
if ( ! visible ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={ __( 'Are you sure?', 'woocommerce-admin' ) }
|
||||
onRequestClose={ onClose }
|
||||
className="wc-admin-shipping-banner__dismiss-modal"
|
||||
>
|
||||
<p className="wc-admin-shipping-banner__dismiss-modal-help-text">
|
||||
{ __(
|
||||
'With WooCommerce Shipping you can Print shipping labels from your WooCommerce dashboard at the lowest USPS rates.',
|
||||
'woocommerce-admin'
|
||||
) }
|
||||
</p>
|
||||
<div className="wc-admin-shipping-banner__dismiss-modal-actions">
|
||||
<Button isDefault onClick={ this.remindMeLaterClicked }>
|
||||
{ __( 'Remind me later', 'woocommerce-admin' ) }
|
||||
</Button>
|
||||
<Button isPrimary onClick={ this.closeForeverClicked }>
|
||||
{ __( "I don't need this", 'woocommerce-admin' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDispatch( ( dispatch ) => {
|
||||
const { updateOptions } = dispatch( 'wc-api' );
|
||||
return { updateOptions };
|
||||
} )
|
||||
)( DismissModal );
|
|
@ -0,0 +1,141 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { shallow } from 'enzyme';
|
||||
import { Button } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { DismissModal } from '../index.js';
|
||||
|
||||
describe( 'Option Save events in DismissModal', () => {
|
||||
const spyUpdateOptions = jest.fn();
|
||||
|
||||
let dismissModalWrapper;
|
||||
|
||||
beforeEach( () => {
|
||||
document.body.innerHTML =
|
||||
document.body.innerHTML +
|
||||
'<div id="woocommerce-admin-print-label">';
|
||||
dismissModalWrapper = shallow(
|
||||
<DismissModal
|
||||
visible={ true }
|
||||
onClose={ jest.fn() }
|
||||
onCloseAll={ jest.fn() }
|
||||
trackElementClicked={ jest.fn() }
|
||||
updateOptions={ spyUpdateOptions }
|
||||
/>
|
||||
);
|
||||
} );
|
||||
|
||||
test( 'Should save permanent dismissal', () => {
|
||||
const permanentDismissTimestamp = -1;
|
||||
const actionButtons = dismissModalWrapper.find( Button );
|
||||
expect( actionButtons.length ).toBe( 2 );
|
||||
const permanenttDismissButton = actionButtons.last();
|
||||
permanenttDismissButton.simulate( 'click' );
|
||||
expect( spyUpdateOptions ).toHaveBeenCalledWith( {
|
||||
woocommerce_shipping_dismissed_timestamp: permanentDismissTimestamp,
|
||||
} );
|
||||
} );
|
||||
|
||||
test( 'Should save temporary dismissal', () => {
|
||||
// Mock Date.now() so a known timestamp will be saved.
|
||||
const mockDate = 123456;
|
||||
const realDateNow = Date.now.bind( global.Date );
|
||||
global.Date.now = jest.fn( () => mockDate );
|
||||
|
||||
const actionButtons = dismissModalWrapper.find( Button );
|
||||
expect( actionButtons.length ).toBe( 2 );
|
||||
const remindMeLaterButton = actionButtons.first();
|
||||
remindMeLaterButton.simulate( 'click' );
|
||||
expect( spyUpdateOptions ).toHaveBeenCalledWith( {
|
||||
woocommerce_shipping_dismissed_timestamp: mockDate,
|
||||
} );
|
||||
|
||||
// Restore Date.now().
|
||||
global.Date.now = realDateNow;
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'Tracking events in DismissModal', () => {
|
||||
const trackElementClicked = jest.fn();
|
||||
|
||||
let dismissModalWrapper;
|
||||
|
||||
beforeEach( () => {
|
||||
dismissModalWrapper = shallow(
|
||||
<DismissModal
|
||||
visible={ true }
|
||||
onClose={ jest.fn() }
|
||||
onCloseAll={ jest.fn() }
|
||||
trackElementClicked={ trackElementClicked }
|
||||
updateOptions={ jest.fn() }
|
||||
/>
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should record an event when user clicks "I don\'t need this"', () => {
|
||||
const actionButtons = dismissModalWrapper.find( Button );
|
||||
expect( actionButtons.length ).toBe( 2 );
|
||||
const iDoNotNeedThisButton = actionButtons.last();
|
||||
iDoNotNeedThisButton.simulate( 'click' );
|
||||
expect( trackElementClicked ).toHaveBeenCalledWith(
|
||||
'shipping_banner_dismiss_modal_close_forever'
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should record an event when user clicks "Remind me later"', () => {
|
||||
const actionButtons = dismissModalWrapper.find( Button );
|
||||
expect( actionButtons.length ).toBe( 2 );
|
||||
const remindMeLaterButton = actionButtons.first();
|
||||
remindMeLaterButton.simulate( 'click' );
|
||||
expect( trackElementClicked ).toHaveBeenCalledWith(
|
||||
'shipping_banner_dismiss_modal_remind_me_later'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'Dismissing modal', () => {
|
||||
let dismissModalWrapper;
|
||||
|
||||
beforeEach( () => {
|
||||
document.body.innerHTML =
|
||||
document.body.innerHTML +
|
||||
'<div id="woocommerce-admin-print-label">';
|
||||
dismissModalWrapper = shallow(
|
||||
<DismissModal
|
||||
visible={ true }
|
||||
onClose={ jest.fn() }
|
||||
onCloseAll={ jest.fn() }
|
||||
trackElementClicked={ jest.fn() }
|
||||
updateOptions={ jest.fn() }
|
||||
/>
|
||||
);
|
||||
} );
|
||||
|
||||
test( 'Should hide the banner by clicking permanent dismissal', () => {
|
||||
const actionButtons = dismissModalWrapper.find( Button );
|
||||
expect( actionButtons.length ).toBe( 2 );
|
||||
const permanenttDismissButton = actionButtons.last();
|
||||
permanenttDismissButton.simulate( 'click' );
|
||||
|
||||
const bannerStyle = document.getElementById(
|
||||
'woocommerce-admin-print-label'
|
||||
).style;
|
||||
expect( bannerStyle.display ).toBe( 'none' );
|
||||
} );
|
||||
|
||||
test( 'Should hide the banner by clicking temporary dismissal', () => {
|
||||
const actionButtons = dismissModalWrapper.find( Button );
|
||||
expect( actionButtons.length ).toBe( 2 );
|
||||
const remindMeLaterButton = actionButtons.first();
|
||||
remindMeLaterButton.simulate( 'click' );
|
||||
|
||||
const bannerStyle = document.getElementById(
|
||||
'woocommerce-admin-print-label'
|
||||
).style;
|
||||
expect( bannerStyle.display ).toBe( 'none' );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { render } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ShippingBanner from './shipping-banner';
|
||||
|
||||
const metaBox = document.getElementById( 'wc-admin-shipping-banner-root' );
|
||||
const args =
|
||||
( metaBox.dataset.args && JSON.parse( metaBox.dataset.args ) ) || {};
|
||||
|
||||
// Render the header.
|
||||
render( <ShippingBanner itemsCount={ args.shippable_items_count } />, metaBox );
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { Dashicon } from '@wordpress/components';
|
||||
|
||||
export const setupErrorTypes = {
|
||||
DOWNLOAD: 'download',
|
||||
INSTALL: 'install',
|
||||
ACTIVATE: 'activate',
|
||||
SETUP: 'setup',
|
||||
START: 'start',
|
||||
};
|
||||
|
||||
const setupErrorDescriptions = {
|
||||
[ setupErrorTypes.DOWNLOAD ]: __( 'download', 'woocommerce-admin' ),
|
||||
[ setupErrorTypes.INSTALL ]: __( 'install', 'woocommerce-admin' ),
|
||||
[ setupErrorTypes.ACTIVATE ]: __( 'activate', 'woocommerce-admin' ),
|
||||
[ setupErrorTypes.SETUP ]: __( 'set up', 'woocommerce-admin' ),
|
||||
[ setupErrorTypes.START ]: __( 'start', 'woocommerce-admin' ),
|
||||
};
|
||||
|
||||
export default function SetupNotice( { isSetupError, errorReason } ) {
|
||||
const getErrorMessage = ( errorType ) => {
|
||||
// Default to 'set up' description if the error type somehow doesn't exist.
|
||||
const description =
|
||||
errorType in setupErrorDescriptions
|
||||
? setupErrorDescriptions[ errorType ]
|
||||
: setupErrorDescriptions[ setupErrorTypes.SETUP ];
|
||||
|
||||
return sprintf(
|
||||
__(
|
||||
'Unable to %s the plugin. Refresh the page and try again.',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
description
|
||||
);
|
||||
};
|
||||
|
||||
if ( ! isSetupError ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="wc-admin-shipping-banner-install-error">
|
||||
<Dashicon icon="warning" />
|
||||
{ getErrorMessage( errorReason ) }
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import SetupNotice, { setupErrorTypes } from '../';
|
||||
|
||||
describe( 'SetupNotice', () => {
|
||||
it( 'should be hidden for no error', () => {
|
||||
const notice = mount( <SetupNotice isSetupError={ false } /> );
|
||||
expect( notice.isEmptyRender() ).toBe( true );
|
||||
} );
|
||||
|
||||
it( 'should show div if there is an error', () => {
|
||||
const notice = mount( <SetupNotice isSetupError={ true } /> );
|
||||
const contents = notice.find(
|
||||
'.wc-admin-shipping-banner-install-error'
|
||||
);
|
||||
expect( contents.length ).toBe( 1 );
|
||||
} );
|
||||
|
||||
it( 'should show download message for download error', () => {
|
||||
const notice = mount(
|
||||
<SetupNotice
|
||||
isSetupError={ true }
|
||||
errorReason={ setupErrorTypes.DOWNLOAD }
|
||||
/>
|
||||
);
|
||||
expect(
|
||||
notice.text().includes( 'Unable to download the plugin' )
|
||||
).toBe( true );
|
||||
} );
|
||||
|
||||
it( 'should show default message for unset error', () => {
|
||||
const notice = mount( <SetupNotice isSetupError={ true } /> );
|
||||
expect( notice.text().includes( 'Unable to set up the plugin' ) ).toBe(
|
||||
true
|
||||
);
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,556 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Component } from '@wordpress/element';
|
||||
import { Button, ExternalLink } from '@wordpress/components';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { recordEvent } from 'lib/tracks';
|
||||
import interpolateComponents from 'interpolate-components';
|
||||
import PropTypes from 'prop-types';
|
||||
import { get, isArray } from 'lodash';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import '../style.scss';
|
||||
import DismissModal from '../dismiss-modal';
|
||||
import { getSetting } from '@woocommerce/wc-admin-settings';
|
||||
import withSelect from 'wc-api/with-select';
|
||||
import SetupNotice, { setupErrorTypes } from '../setup-notice';
|
||||
import { withDispatch } from '@wordpress/data';
|
||||
import { getWcsAssets, acceptWcsTos } from '../wcs-api';
|
||||
|
||||
const wcAdminAssetUrl = getSetting( 'wcAdminAssetUrl', '' );
|
||||
|
||||
export class ShippingBanner extends Component {
|
||||
constructor( props ) {
|
||||
super( props );
|
||||
|
||||
const orderId = new URL( window.location.href ).searchParams.get(
|
||||
'post'
|
||||
);
|
||||
|
||||
this.state = {
|
||||
showShippingBanner: true,
|
||||
isDismissModalOpen: false,
|
||||
setupErrorReason: setupErrorTypes.SETUP,
|
||||
orderId: parseInt( orderId, 10 ),
|
||||
wcsAssetsLoaded: false,
|
||||
wcsAssetsLoading: false,
|
||||
wcsSetupError: false,
|
||||
isShippingLabelButtonBusy: false,
|
||||
installText: this.getInstallText(),
|
||||
isWcsModalOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { showShippingBanner } = this.state;
|
||||
|
||||
if ( showShippingBanner ) {
|
||||
this.trackImpression();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate( prevProps ) {
|
||||
const { activatePlugins, wcsPluginSlug } = this.props;
|
||||
|
||||
if ( this.justInstalledWcs( prevProps ) ) {
|
||||
activatePlugins( [ wcsPluginSlug ] );
|
||||
}
|
||||
if ( this.justActivatedWcs( prevProps ) ) {
|
||||
this.acceptTosAndGetWCSAssets();
|
||||
}
|
||||
}
|
||||
|
||||
justInstalledWcs( prevProps ) {
|
||||
const { installedPlugins, wcsPluginSlug } = this.props;
|
||||
const wcsNowInstalled = installedPlugins.includes( wcsPluginSlug );
|
||||
const wcsPrevInstalled = prevProps.installedPlugins.includes(
|
||||
wcsPluginSlug
|
||||
);
|
||||
return wcsNowInstalled && ! wcsPrevInstalled;
|
||||
}
|
||||
|
||||
justActivatedWcs( prevProps ) {
|
||||
const { activatedPlugins, wcsPluginSlug } = this.props;
|
||||
const wcsNowActivated = activatedPlugins.includes( wcsPluginSlug );
|
||||
const wcsPrevActivated = prevProps.activatedPlugins.includes(
|
||||
wcsPluginSlug
|
||||
);
|
||||
return wcsNowActivated && ! wcsPrevActivated;
|
||||
}
|
||||
|
||||
hasActivationError = () => {
|
||||
return Boolean( this.props.activationErrors.length );
|
||||
};
|
||||
|
||||
hasInstallationError = () => {
|
||||
return Boolean( this.props.installationErrors.length );
|
||||
};
|
||||
|
||||
isSetupError = () => {
|
||||
return (
|
||||
this.hasActivationError() ||
|
||||
this.hasInstallationError() ||
|
||||
this.state.wcsSetupError
|
||||
);
|
||||
};
|
||||
|
||||
setupErrorReason = () => {
|
||||
if ( this.hasInstallationError() ) {
|
||||
return setupErrorTypes.INSTALL;
|
||||
}
|
||||
if ( this.hasActivationError() ) {
|
||||
return setupErrorTypes.ACTIVATE;
|
||||
}
|
||||
return setupErrorTypes.SETUP;
|
||||
};
|
||||
|
||||
closeDismissModal = () => {
|
||||
this.setState( { isDismissModalOpen: false } );
|
||||
this.trackElementClicked(
|
||||
'shipping_banner_dismiss_modal_close_button'
|
||||
);
|
||||
};
|
||||
|
||||
openDismissModal = () => {
|
||||
this.setState( { isDismissModalOpen: true } );
|
||||
this.trackElementClicked( 'shipping_banner_dimiss' );
|
||||
};
|
||||
|
||||
hideBanner = () => {
|
||||
this.setState( { showShippingBanner: false } );
|
||||
};
|
||||
|
||||
createShippingLabelClicked = () => {
|
||||
const { wcsPluginSlug, activePlugins } = this.props;
|
||||
this.setState( { isShippingLabelButtonBusy: true } );
|
||||
this.trackElementClicked( 'shipping_banner_create_label' );
|
||||
if ( ! activePlugins.includes( wcsPluginSlug ) ) {
|
||||
this.installAndActivatePlugins( wcsPluginSlug );
|
||||
} else {
|
||||
this.acceptTosAndGetWCSAssets();
|
||||
}
|
||||
};
|
||||
|
||||
installAndActivatePlugins( pluginSlug ) {
|
||||
// Avoid double activating.
|
||||
const { installPlugins, isRequesting } = this.props;
|
||||
if ( isRequesting ) {
|
||||
return false;
|
||||
}
|
||||
installPlugins( [ pluginSlug ] );
|
||||
}
|
||||
|
||||
woocommerceServiceLinkClicked = () => {
|
||||
this.trackElementClicked( 'shipping_banner_woocommerce_service_link' );
|
||||
};
|
||||
|
||||
trackBannerEvent = ( eventName, customProps = {} ) => {
|
||||
const { activePlugins, isJetpackConnected, wcsPluginSlug } = this.props;
|
||||
recordEvent( eventName, {
|
||||
banner_name: 'wcadmin_install_wcs_prompt',
|
||||
jetpack_installed: activePlugins.includes( 'jetpack' ),
|
||||
jetpack_connected: isJetpackConnected,
|
||||
wcs_installed: activePlugins.includes( wcsPluginSlug ),
|
||||
...customProps,
|
||||
} );
|
||||
};
|
||||
|
||||
trackImpression = () => {
|
||||
this.trackBannerEvent( 'banner_impression' );
|
||||
};
|
||||
|
||||
trackElementClicked = ( element ) => {
|
||||
this.trackBannerEvent( 'banner_element_clicked', {
|
||||
element,
|
||||
} );
|
||||
};
|
||||
|
||||
acceptTosAndGetWCSAssets() {
|
||||
return acceptWcsTos()
|
||||
.then( () => getWcsAssets() )
|
||||
.then( ( wcsAssets ) => this.loadWcsAssets( wcsAssets ) )
|
||||
.catch( () => this.setState( { wcsSetupError: true } ) );
|
||||
}
|
||||
|
||||
generateMetaBoxHtml( nodeId, title, args ) {
|
||||
const argsJsonString = JSON.stringify( args ).replace( /"/g, '"' ); // JS has no native html_entities so we just replace.
|
||||
|
||||
const togglePanelText = __( 'Toggle panel:', 'woocommerce-admin' );
|
||||
|
||||
return `
|
||||
<div id="${ nodeId }" class="postbox">
|
||||
<button type="button" class="handlediv" aria-expanded="true">
|
||||
<span class="screen-reader-text">${ togglePanelText } ${ title }</span>
|
||||
<span class="toggle-indicator" aria-hidden="true"></span>
|
||||
</button>
|
||||
<h2 class="hndle"><span>${ title }</span></h2>
|
||||
<div class="inside">
|
||||
<div class="wcc-root woocommerce wc-connect-create-shipping-label" data-args="${ argsJsonString }">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
loadWcsAssets( { assets } ) {
|
||||
if ( this.state.wcsAssetsLoaded || this.state.wcsAssetsLoading ) {
|
||||
this.openWcsModal();
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState( { wcsAssetsLoading: true } );
|
||||
|
||||
const js = assets.wc_connect_admin_script;
|
||||
const styles = assets.wc_connect_admin_style;
|
||||
|
||||
const { orderId } = this.state;
|
||||
const { itemsCount } = this.props;
|
||||
|
||||
const shippingLabelContainerHtml = this.generateMetaBoxHtml(
|
||||
'woocommerce-order-label',
|
||||
__( 'Shipping Label', 'woocommerce-admin' ),
|
||||
{
|
||||
orderId,
|
||||
context: 'shipping_label',
|
||||
items: itemsCount,
|
||||
}
|
||||
);
|
||||
// Insert shipping label metabox just above main order details box.
|
||||
document
|
||||
.getElementById( 'woocommerce-order-data' )
|
||||
.insertAdjacentHTML( 'beforebegin', shippingLabelContainerHtml );
|
||||
|
||||
const shipmentTrackingHtml = this.generateMetaBoxHtml(
|
||||
'woocommerce-order-shipment-tracking',
|
||||
__( 'Shipment Tracking', 'woocommerce-admin' ),
|
||||
{
|
||||
orderId,
|
||||
context: 'shipment_tracking',
|
||||
items: itemsCount,
|
||||
}
|
||||
);
|
||||
// Insert tracking metabox in the side after the order actions.
|
||||
document
|
||||
.getElementById( 'woocommerce-order-actions' )
|
||||
.insertAdjacentHTML( 'afterend', shipmentTrackingHtml );
|
||||
|
||||
if ( window.jQuery ) {
|
||||
// Need to refresh so the new metaboxes are sortable.
|
||||
window.jQuery( '#normal-sortables' ).sortable( 'refresh' );
|
||||
window.jQuery( '#side-sortables' ).sortable( 'refresh' );
|
||||
|
||||
window.jQuery( '#woocommerce-order-label' ).hide();
|
||||
}
|
||||
|
||||
Promise.all( [
|
||||
new Promise( ( resolve, reject ) => {
|
||||
const script = document.createElement( 'script' );
|
||||
script.src = js;
|
||||
script.async = true;
|
||||
script.onload = resolve;
|
||||
script.onerror = reject;
|
||||
document.body.appendChild( script );
|
||||
} ),
|
||||
new Promise( ( resolve, reject ) => {
|
||||
const head = document.getElementsByTagName( 'head' )[ 0 ];
|
||||
const link = document.createElement( 'link' );
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
link.href = styles;
|
||||
link.media = 'all';
|
||||
link.onload = resolve;
|
||||
link.onerror = reject;
|
||||
head.appendChild( link );
|
||||
} ),
|
||||
] ).then( () => {
|
||||
this.setState( {
|
||||
wcsAssetsLoaded: true,
|
||||
wcsAssetsLoading: false,
|
||||
isShippingLabelButtonBusy: false,
|
||||
} );
|
||||
this.openWcsModal();
|
||||
} );
|
||||
}
|
||||
|
||||
getInstallText = () => {
|
||||
const { activePlugins, wcsPluginSlug } = this.props;
|
||||
if ( activePlugins.includes( wcsPluginSlug ) ) {
|
||||
// If WCS is active, then the only remaining step is to agree to the ToS.
|
||||
return __(
|
||||
'You\'ve already installed WooCommerce Shipping. By clicking "Create shipping label", you agree to its {{tosLink}}Terms of Service{{/tosLink}}.',
|
||||
'woocommerce-admin'
|
||||
);
|
||||
}
|
||||
return __(
|
||||
'By clicking "Create shipping label", {{wcsLink}}WooCommerce Shipping{{/wcsLink}} will be installed and you agree to its {{tosLink}}Terms of Service{{/tosLink}}.',
|
||||
'woocommerce-admin'
|
||||
);
|
||||
};
|
||||
|
||||
openWcsModal() {
|
||||
if ( window.wcsGetAppStore ) {
|
||||
const wcsStore = window.wcsGetAppStore(
|
||||
'wc-connect-create-shipping-label'
|
||||
);
|
||||
const state = wcsStore.getState();
|
||||
const { orderId } = this.state;
|
||||
const siteId = state.ui.selectedSiteId;
|
||||
|
||||
const wcsStoreUnsubscribe = wcsStore.subscribe( () => {
|
||||
const latestState = wcsStore.getState();
|
||||
|
||||
const shippingLabelState = get(
|
||||
latestState,
|
||||
[
|
||||
'extensions',
|
||||
'woocommerce',
|
||||
'woocommerceServices',
|
||||
siteId,
|
||||
'shippingLabel',
|
||||
orderId,
|
||||
],
|
||||
null
|
||||
);
|
||||
|
||||
const labelSettingsState = get(
|
||||
latestState,
|
||||
[
|
||||
'extensions',
|
||||
'woocommerce',
|
||||
'woocommerceServices',
|
||||
siteId,
|
||||
'labelSettings',
|
||||
],
|
||||
null
|
||||
);
|
||||
|
||||
const packageState = get(
|
||||
latestState,
|
||||
[
|
||||
'extensions',
|
||||
'woocommerce',
|
||||
'woocommerceServices',
|
||||
siteId,
|
||||
'packages',
|
||||
],
|
||||
null
|
||||
);
|
||||
|
||||
const locationsState = get( latestState, [
|
||||
'extensions',
|
||||
'woocommerce',
|
||||
'sites',
|
||||
siteId,
|
||||
'data',
|
||||
'locations',
|
||||
] );
|
||||
|
||||
if (
|
||||
shippingLabelState &&
|
||||
labelSettingsState &&
|
||||
labelSettingsState.meta &&
|
||||
packageState &&
|
||||
locationsState
|
||||
) {
|
||||
if (
|
||||
shippingLabelState.loaded &&
|
||||
labelSettingsState.meta.isLoaded &&
|
||||
packageState.isLoaded &&
|
||||
isArray( locationsState ) &&
|
||||
! this.state.isWcsModalOpen
|
||||
) {
|
||||
if ( window.jQuery ) {
|
||||
this.setState( { isWcsModalOpen: true } );
|
||||
window
|
||||
.jQuery( '.shipping-label__new-label-button' )
|
||||
.click();
|
||||
}
|
||||
wcsStore.dispatch( {
|
||||
type: 'NOTICE_CREATE',
|
||||
notice: {
|
||||
duration: 10000,
|
||||
status: 'is-success',
|
||||
text: __(
|
||||
'Plugin installed and activated',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
},
|
||||
} );
|
||||
} else if ( shippingLabelState.showPurchaseDialog ) {
|
||||
wcsStoreUnsubscribe();
|
||||
if ( window.jQuery ) {
|
||||
window.jQuery( '#woocommerce-order-label' ).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
document.getElementById(
|
||||
'woocommerce-admin-print-label'
|
||||
).style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
isDismissModalOpen,
|
||||
showShippingBanner,
|
||||
isShippingLabelButtonBusy,
|
||||
} = this.state;
|
||||
if ( ! showShippingBanner ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="wc-admin-shipping-banner-container">
|
||||
<img
|
||||
className="wc-admin-shipping-banner-illustration"
|
||||
src={ wcAdminAssetUrl + 'shippingillustration.svg' }
|
||||
alt={ __( 'Shipping ', 'woocommerce-admin' ) }
|
||||
/>
|
||||
<div className="wc-admin-shipping-banner-blob">
|
||||
<h3>
|
||||
{ __(
|
||||
'Print discounted shipping labels with a click.',
|
||||
'woocommerce-admin'
|
||||
) }
|
||||
</h3>
|
||||
<p>
|
||||
{ interpolateComponents( {
|
||||
mixedString: this.state.installText,
|
||||
components: {
|
||||
tosLink: (
|
||||
<ExternalLink
|
||||
href="https://wordpress.com/tos"
|
||||
target="_blank"
|
||||
type="external"
|
||||
/>
|
||||
),
|
||||
wcsLink: (
|
||||
<ExternalLink
|
||||
href="https://woocommerce.com/products/shipping/"
|
||||
target="_blank"
|
||||
type="external"
|
||||
onClick={
|
||||
this
|
||||
.woocommerceServiceLinkClicked
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
} ) }
|
||||
</p>
|
||||
<SetupNotice
|
||||
isSetupError={ this.isSetupError() }
|
||||
errorReason={ this.setupErrorReason() }
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
disabled={ isShippingLabelButtonBusy }
|
||||
isPrimary
|
||||
isBusy={ isShippingLabelButtonBusy }
|
||||
onClick={ this.createShippingLabelClicked }
|
||||
>
|
||||
{ __( 'Create shipping label', 'woocommerce-admin' ) }
|
||||
</Button>
|
||||
|
||||
<button
|
||||
onClick={ this.openDismissModal }
|
||||
type="button"
|
||||
className="notice-dismiss"
|
||||
disabled={ this.state.isShippingLabelButtonBusy }
|
||||
>
|
||||
<span className="screen-reader-text">
|
||||
{ __(
|
||||
'Close Print Label Banner.',
|
||||
'woocommerce-admin'
|
||||
) }
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<DismissModal
|
||||
visible={ isDismissModalOpen }
|
||||
onClose={ this.closeDismissModal }
|
||||
onCloseAll={ this.hideBanner }
|
||||
trackElementClicked={ this.trackElementClicked }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ShippingBanner.propTypes = {
|
||||
itemsCount: PropTypes.number.isRequired,
|
||||
isJetpackConnected: PropTypes.bool.isRequired,
|
||||
activatedPlugins: PropTypes.array.isRequired,
|
||||
activePlugins: PropTypes.array.isRequired,
|
||||
installedPlugins: PropTypes.array.isRequired,
|
||||
wcsPluginSlug: PropTypes.string.isRequired,
|
||||
activationErrors: PropTypes.array.isRequired,
|
||||
installationErrors: PropTypes.array.isRequired,
|
||||
activatePlugins: PropTypes.func.isRequired,
|
||||
installPlugins: PropTypes.func.isRequired,
|
||||
isRequesting: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default compose(
|
||||
withSelect( ( select ) => {
|
||||
const wcsPluginSlug = 'woocommerce-services';
|
||||
const {
|
||||
getActivePlugins,
|
||||
getPluginInstallations,
|
||||
getPluginActivations,
|
||||
getPluginActivationErrors,
|
||||
getPluginInstallationErrors,
|
||||
isJetpackConnected,
|
||||
isPluginActivateRequesting,
|
||||
isPluginInstallRequesting,
|
||||
} = select( 'wc-api' );
|
||||
|
||||
const isRequesting =
|
||||
isPluginActivateRequesting() || isPluginInstallRequesting();
|
||||
const allInstallationErrors = getPluginInstallationErrors( [
|
||||
wcsPluginSlug,
|
||||
] );
|
||||
const installedPlugins = Object.keys(
|
||||
getPluginInstallations( [ wcsPluginSlug ] )
|
||||
);
|
||||
const allActivationErrors = getPluginActivationErrors( [
|
||||
wcsPluginSlug,
|
||||
] );
|
||||
const activatedPlugins = Object.keys(
|
||||
getPluginActivations( [ wcsPluginSlug ] )
|
||||
);
|
||||
const activationErrors = [];
|
||||
const installationErrors = [];
|
||||
Object.keys( allActivationErrors ).map( ( plugin ) =>
|
||||
activationErrors.push( allActivationErrors[ plugin ].message )
|
||||
);
|
||||
Object.keys( allInstallationErrors ).map( ( plugin ) =>
|
||||
installationErrors.push( allInstallationErrors[ plugin ].message )
|
||||
);
|
||||
return {
|
||||
activePlugins: getActivePlugins(),
|
||||
isJetpackConnected: isJetpackConnected(),
|
||||
isRequesting,
|
||||
installedPlugins,
|
||||
activatedPlugins,
|
||||
wcsPluginSlug,
|
||||
activationErrors,
|
||||
installationErrors,
|
||||
};
|
||||
} ),
|
||||
withDispatch( ( dispatch ) => {
|
||||
const { activatePlugins, installPlugins } = dispatch( 'wc-api' );
|
||||
|
||||
return {
|
||||
activatePlugins,
|
||||
installPlugins,
|
||||
};
|
||||
} )
|
||||
)( ShippingBanner );
|
|
@ -0,0 +1,459 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { shallow } from 'enzyme';
|
||||
import { recordEvent } from 'lib/tracks';
|
||||
import { ExternalLink, Button } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
|
||||
jest.mock( '../../wcs-api.js' );
|
||||
import { acceptWcsTos, getWcsAssets } from '../../wcs-api.js';
|
||||
|
||||
acceptWcsTos.mockReturnValue( Promise.resolve() );
|
||||
const wcsAssetsMock = {};
|
||||
getWcsAssets.mockReturnValue( Promise.resolve( wcsAssetsMock ) );
|
||||
|
||||
import { ShippingBanner } from '../index.js';
|
||||
|
||||
jest.mock( 'lib/tracks' );
|
||||
jest.mock( '@woocommerce/wc-admin-settings' );
|
||||
|
||||
describe( 'Tracking impression in shippingBanner', () => {
|
||||
const expectedTrackingData = {
|
||||
banner_name: 'wcadmin_install_wcs_prompt',
|
||||
jetpack_connected: true,
|
||||
jetpack_installed: true,
|
||||
wcs_installed: true,
|
||||
};
|
||||
const wcsPluginSlug = 'it-does-n-t-matter-since-its-a-prop';
|
||||
|
||||
beforeEach( () => {
|
||||
shallow(
|
||||
<ShippingBanner
|
||||
isJetpackConnected={ true }
|
||||
activatedPlugins={ [] }
|
||||
activePlugins={ [ wcsPluginSlug, 'jetpack' ] }
|
||||
installedPlugins={ [ wcsPluginSlug, 'jetpack' ] }
|
||||
activationErrors={ [] }
|
||||
installationErrors={ [] }
|
||||
itemsCount={ 1 }
|
||||
wcsPluginSlug={ wcsPluginSlug }
|
||||
activatePlugins={ jest.fn() }
|
||||
installPlugins={ jest.fn() }
|
||||
isRequesting={ false }
|
||||
/>
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should record an event when user sees banner loaded', () => {
|
||||
expect( recordEvent ).toHaveBeenCalledTimes( 1 );
|
||||
expect( recordEvent ).toHaveBeenCalledWith(
|
||||
'banner_impression',
|
||||
expectedTrackingData
|
||||
);
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'Tracking clicks in shippingBanner', () => {
|
||||
const isJetpackConnected = true;
|
||||
let shippingBannerWrapper;
|
||||
|
||||
const getExpectedTrackingData = ( element ) => {
|
||||
return {
|
||||
banner_name: 'wcadmin_install_wcs_prompt',
|
||||
jetpack_connected: true,
|
||||
jetpack_installed: true,
|
||||
wcs_installed: true,
|
||||
element,
|
||||
};
|
||||
};
|
||||
const wcsPluginSlug = 'it-does-n-t-matter-since-its-a-prop';
|
||||
|
||||
beforeEach( () => {
|
||||
shippingBannerWrapper = shallow(
|
||||
<ShippingBanner
|
||||
isJetpackConnected={ isJetpackConnected }
|
||||
activatedPlugins={ [] }
|
||||
activePlugins={ [ wcsPluginSlug, 'jetpack' ] }
|
||||
installedPlugins={ [ wcsPluginSlug, 'jetpack' ] }
|
||||
installPlugins={ jest.fn() }
|
||||
activatePlugins={ jest.fn() }
|
||||
wcsPluginSlug={ wcsPluginSlug }
|
||||
activationErrors={ [] }
|
||||
installationErrors={ [] }
|
||||
isRequesting={ false }
|
||||
itemsCount={ 1 }
|
||||
/>
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should record an event when user clicks "Create shipping label"', () => {
|
||||
const createShippingLabelButton = shippingBannerWrapper.find( Button );
|
||||
expect( createShippingLabelButton.length ).toBe( 1 );
|
||||
createShippingLabelButton.simulate( 'click' );
|
||||
expect( recordEvent ).toHaveBeenCalledWith(
|
||||
'banner_element_clicked',
|
||||
getExpectedTrackingData( 'shipping_banner_create_label' )
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should record an event when user clicks "WooCommerce Service"', () => {
|
||||
const links = shippingBannerWrapper.find( ExternalLink );
|
||||
expect( links.length ).toBe( 1 );
|
||||
const wcsLink = links.first();
|
||||
wcsLink.simulate( 'click' );
|
||||
expect( recordEvent ).toHaveBeenCalledWith(
|
||||
'banner_impression',
|
||||
getExpectedTrackingData()
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should record an event when user clicks "x" to dismiss the banner', () => {
|
||||
const noticeDimissButton = shippingBannerWrapper.find(
|
||||
'.notice-dismiss'
|
||||
);
|
||||
expect( noticeDimissButton.length ).toBe( 1 );
|
||||
noticeDimissButton.simulate( 'click' );
|
||||
expect( recordEvent ).toHaveBeenCalledWith(
|
||||
'banner_element_clicked',
|
||||
getExpectedTrackingData( 'shipping_banner_dimiss' )
|
||||
);
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'Create shipping label button', () => {
|
||||
let shippingBannerWrapper;
|
||||
const installPlugins = jest.fn();
|
||||
const activatePlugins = jest.fn();
|
||||
delete window.location; // jsdom won't allow to rewrite window.location unless deleted first
|
||||
window.location = {
|
||||
href: 'http://wcship.test/wp-admin/post.php?post=1000&action=edit',
|
||||
};
|
||||
|
||||
beforeEach( () => {
|
||||
shippingBannerWrapper = shallow(
|
||||
<ShippingBanner
|
||||
isJetpackConnected={ true }
|
||||
activatePlugins={ activatePlugins }
|
||||
activePlugins={ [] }
|
||||
activatedPlugins={ [] }
|
||||
installPlugins={ installPlugins }
|
||||
installedPlugins={ [] }
|
||||
wcsPluginSlug={ 'woocommerce-services' }
|
||||
activationErrors={ [] }
|
||||
installationErrors={ [] }
|
||||
isRequesting={ false }
|
||||
itemsCount={ 1 }
|
||||
/>
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should install WooCommerce Shipping when button is clicked', () => {
|
||||
const createShippingLabelButton = shippingBannerWrapper.find( Button );
|
||||
expect( createShippingLabelButton.length ).toBe( 1 );
|
||||
createShippingLabelButton.simulate( 'click' );
|
||||
expect( installPlugins ).toHaveBeenCalledWith( [
|
||||
'woocommerce-services',
|
||||
] );
|
||||
} );
|
||||
|
||||
it( 'should activate WooCommerce Shipping when installation finishes', () => {
|
||||
// Cause a 'componentDidUpdate' by changing the props.
|
||||
shippingBannerWrapper.setProps( {
|
||||
installedPlugins: [ 'woocommerce-services' ],
|
||||
} );
|
||||
expect( activatePlugins ).toHaveBeenCalledWith( [
|
||||
'woocommerce-services',
|
||||
] );
|
||||
} );
|
||||
|
||||
it( 'should perform a request to accept the TOS and get WCS assets to load', async () => {
|
||||
const loadWcsAssetsMock = jest.fn();
|
||||
shippingBannerWrapper.instance().loadWcsAssets = loadWcsAssetsMock;
|
||||
|
||||
await shippingBannerWrapper.instance().acceptTosAndGetWCSAssets();
|
||||
|
||||
expect( acceptWcsTos ).toHaveBeenCalled();
|
||||
|
||||
expect( getWcsAssets ).toHaveBeenCalled();
|
||||
|
||||
expect( loadWcsAssetsMock ).toHaveBeenCalledWith( wcsAssetsMock );
|
||||
} );
|
||||
|
||||
it( 'should load WCS assets when a path is provided', () => {
|
||||
const scriptMock = {};
|
||||
const linkMock = {};
|
||||
const divMock = { dataset: { args: null } };
|
||||
const createElementMockReturn = {
|
||||
div: divMock,
|
||||
script: scriptMock,
|
||||
link: linkMock,
|
||||
};
|
||||
|
||||
window.jQuery = jest.fn();
|
||||
window.jQuery.mockReturnValue( {
|
||||
sortable: jest.fn(),
|
||||
hide: jest.fn(),
|
||||
} );
|
||||
|
||||
const createElementMock = jest.fn( ( tagName ) => {
|
||||
return createElementMockReturn[ tagName ];
|
||||
} );
|
||||
const createElement = document.createElement;
|
||||
document.createElement = createElementMock;
|
||||
|
||||
const getElementsByTagNameMock = jest.fn();
|
||||
const headMock = {
|
||||
appendChild: jest.fn(),
|
||||
};
|
||||
getElementsByTagNameMock.mockReturnValueOnce( [ headMock ] );
|
||||
const getElementsByTagName = document.getElementsByTagName;
|
||||
document.getElementsByTagName = getElementsByTagNameMock;
|
||||
const getElementByIdMock = jest.fn();
|
||||
getElementByIdMock.mockReturnValue( {
|
||||
insertAdjacentHTML: jest.fn(),
|
||||
} );
|
||||
const getElementById = document.getElementById;
|
||||
document.getElementById = getElementByIdMock;
|
||||
|
||||
const appendChildMock = jest.fn();
|
||||
const appendChild = document.body.appendChild;
|
||||
document.body.appendChild = appendChildMock;
|
||||
|
||||
const openWcsModalMock = jest.fn();
|
||||
shippingBannerWrapper.instance().openWcsModal = openWcsModalMock;
|
||||
|
||||
shippingBannerWrapper.instance().loadWcsAssets( {
|
||||
assets: {
|
||||
wc_connect_admin_script: '/path/to/wcs.js',
|
||||
wc_connect_admin_style: '/path/to/wcs.css',
|
||||
},
|
||||
} );
|
||||
|
||||
expect( createElementMock ).toHaveBeenCalledWith( 'script' );
|
||||
expect( createElementMock ).toHaveNthReturnedWith( 1, scriptMock );
|
||||
expect( scriptMock.async ).toEqual( true );
|
||||
expect( scriptMock.src ).toEqual( '/path/to/wcs.js' );
|
||||
expect( appendChildMock ).toHaveBeenCalledWith( scriptMock );
|
||||
|
||||
expect( getElementsByTagNameMock ).toHaveBeenCalledWith( 'head' );
|
||||
expect( getElementsByTagNameMock ).toHaveReturnedWith( [ headMock ] );
|
||||
expect( createElementMock ).toHaveBeenCalledWith( 'link' );
|
||||
expect( createElementMock ).toHaveNthReturnedWith( 2, linkMock );
|
||||
expect( linkMock.rel ).toEqual( 'stylesheet' );
|
||||
expect( linkMock.type ).toEqual( 'text/css' );
|
||||
expect( linkMock.href ).toEqual( '/path/to/wcs.css' );
|
||||
expect( linkMock.media ).toEqual( 'all' );
|
||||
|
||||
document.createElement = createElement;
|
||||
document.getElementById = getElementById;
|
||||
document.body.appendChild = appendChild;
|
||||
document.getElementsByTagName = getElementsByTagName;
|
||||
} );
|
||||
|
||||
it( 'should open WCS modal', () => {
|
||||
window.wcsGetAppStore = jest.fn();
|
||||
const getState = jest.fn();
|
||||
const dispatch = jest.fn();
|
||||
const subscribe = jest.fn();
|
||||
window.wcsGetAppStore.mockReturnValueOnce( {
|
||||
getState,
|
||||
dispatch,
|
||||
subscribe,
|
||||
} );
|
||||
getState.mockReturnValueOnce( {
|
||||
ui: {
|
||||
selectedSiteId: 'SITE_ID',
|
||||
},
|
||||
} );
|
||||
|
||||
const getElementByIdMock = jest.fn();
|
||||
getElementByIdMock.mockReturnValue( {
|
||||
style: { display: null },
|
||||
} );
|
||||
const getElementById = document.getElementById;
|
||||
document.getElementById = getElementByIdMock;
|
||||
|
||||
shippingBannerWrapper.instance().openWcsModal();
|
||||
expect( window.wcsGetAppStore ).toHaveBeenCalledWith(
|
||||
'wc-connect-create-shipping-label'
|
||||
);
|
||||
expect( getState ).toHaveBeenCalledTimes( 1 );
|
||||
expect( subscribe ).toHaveBeenCalledTimes( 1 );
|
||||
expect( getElementByIdMock ).toHaveBeenCalledWith(
|
||||
'woocommerce-admin-print-label'
|
||||
);
|
||||
|
||||
document.getElementById = getElementById;
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'In the process of installing, activating, loading assets for WooCommerce Service', () => {
|
||||
let shippingBannerWrapper;
|
||||
|
||||
beforeEach( () => {
|
||||
shippingBannerWrapper = shallow(
|
||||
<ShippingBanner
|
||||
isJetpackConnected={ true }
|
||||
activatePlugins={ jest.fn() }
|
||||
activePlugins={ [ 'jetpack', 'woocommerce-services' ] }
|
||||
activatedPlugins={ [] }
|
||||
installPlugins={ jest.fn() }
|
||||
installedPlugins={ [] }
|
||||
wcsPluginSlug={ 'woocommerce-services' }
|
||||
activationErrors={ [] }
|
||||
installationErrors={ [] }
|
||||
isRequesting={ true }
|
||||
itemsCount={ 1 }
|
||||
/>
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should show a busy loading state on "Create shipping label"', () => {
|
||||
shippingBannerWrapper.setState( { isShippingLabelButtonBusy: true } );
|
||||
const createShippingLabelButton = shippingBannerWrapper.find( Button );
|
||||
expect( createShippingLabelButton.length ).toBe( 1 );
|
||||
expect( createShippingLabelButton.prop( 'disabled' ) ).toBe( true );
|
||||
expect( createShippingLabelButton.prop( 'isBusy' ) ).toBe( true );
|
||||
} );
|
||||
|
||||
it( 'should disable the dismiss button ', () => {
|
||||
shippingBannerWrapper.setState( { isShippingLabelButtonBusy: true } );
|
||||
const dismissButton = shippingBannerWrapper.find( '.notice-dismiss' );
|
||||
expect( dismissButton.length ).toBe( 1 );
|
||||
expect( dismissButton.prop( 'disabled' ) ).toBe( true );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'Setup error message', () => {
|
||||
let shippingBannerWrapper;
|
||||
|
||||
beforeEach( () => {
|
||||
shippingBannerWrapper = shallow(
|
||||
<ShippingBanner
|
||||
isJetpackConnected={ true }
|
||||
activatePlugins={ jest.fn() }
|
||||
activePlugins={ [ 'jetpack', 'woocommerce-services' ] }
|
||||
activatedPlugins={ [] }
|
||||
installPlugins={ jest.fn() }
|
||||
installedPlugins={ [] }
|
||||
wcsPluginSlug={ 'woocommerce-services' }
|
||||
activationErrors={ [] }
|
||||
installationErrors={ [] }
|
||||
itemsCount={ 1 }
|
||||
isRequesting={ false }
|
||||
/>
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should not show if there is no error', () => {
|
||||
expect( shippingBannerWrapper.instance().isSetupError() ).toBe( false );
|
||||
expect( shippingBannerWrapper.instance().hasActivationError() ).toBe(
|
||||
false
|
||||
);
|
||||
expect( shippingBannerWrapper.instance().hasInstallationError() ).toBe(
|
||||
false
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should show if there is activation error', () => {
|
||||
shippingBannerWrapper.setProps( {
|
||||
activationErrors: [ 'Can not activate' ],
|
||||
} );
|
||||
expect( shippingBannerWrapper.instance().isSetupError() ).toBe( true );
|
||||
expect( shippingBannerWrapper.instance().hasActivationError() ).toBe(
|
||||
true
|
||||
);
|
||||
expect( shippingBannerWrapper.instance().hasInstallationError() ).toBe(
|
||||
false
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should show if there is installation error', () => {
|
||||
shippingBannerWrapper.setProps( {
|
||||
installationErrors: [ 'Can not activate' ],
|
||||
} );
|
||||
expect( shippingBannerWrapper.instance().isSetupError() ).toBe( true );
|
||||
expect( shippingBannerWrapper.instance().hasActivationError() ).toBe(
|
||||
false
|
||||
);
|
||||
expect( shippingBannerWrapper.instance().hasInstallationError() ).toBe(
|
||||
true
|
||||
);
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'The message in the banner', () => {
|
||||
const createShippingBannerWrapper = ( {
|
||||
activePlugins,
|
||||
installedPlugins,
|
||||
} ) =>
|
||||
shallow(
|
||||
<ShippingBanner
|
||||
isJetpackConnected={ true }
|
||||
activatePlugins={ jest.fn() }
|
||||
activePlugins={ activePlugins }
|
||||
activatedPlugins={ [] }
|
||||
installPlugins={ jest.fn() }
|
||||
installedPlugins={ installedPlugins }
|
||||
wcsPluginSlug={ 'woocommerce-services' }
|
||||
activationErrors={ [] }
|
||||
installationErrors={ [] }
|
||||
isRequesting={ true }
|
||||
itemsCount={ 1 }
|
||||
/>
|
||||
);
|
||||
|
||||
const notActivatedMessage =
|
||||
'By clicking "Create shipping label", WooCommerce Shipping will be installed and you agree to its Terms of Service.';
|
||||
const activatedMessage =
|
||||
'You\'ve already installed WooCommerce Shipping. By clicking "Create shipping label", you agree to its Terms of Service.';
|
||||
|
||||
it( 'should show install text "By clicking "Create shipping label"..." when first loaded.', () => {
|
||||
const shippingBannerWrapper = createShippingBannerWrapper( {
|
||||
activePlugins: [],
|
||||
installedPlugins: [],
|
||||
} );
|
||||
const createShippingLabelText = shippingBannerWrapper.find( 'p' );
|
||||
expect( createShippingLabelText.text() ).toBe( notActivatedMessage );
|
||||
} );
|
||||
|
||||
it( 'should continue to show the initial message "By clicking "Create shipping label"..." after WooCommerce Service is installed successfully.', () => {
|
||||
const shippingBannerWrapper = createShippingBannerWrapper( {
|
||||
activePlugins: [],
|
||||
installedPlugins: [ 'woocommerce-services' ],
|
||||
} );
|
||||
// Mock installation and activation successful
|
||||
shippingBannerWrapper.setProps( {
|
||||
activePlugins: [ 'woocommerce-services' ],
|
||||
} );
|
||||
const createShippingLabelText = shippingBannerWrapper.find( 'p' );
|
||||
expect( createShippingLabelText.text() ).toBe( notActivatedMessage );
|
||||
} );
|
||||
|
||||
it( 'should continue to show the initial message "By clicking "Create shipping label"..." after WooCommerce Service is installed and activated successfully.', () => {
|
||||
const shippingBannerWrapper = createShippingBannerWrapper( {
|
||||
activePlugins: [],
|
||||
installedPlugins: [],
|
||||
} );
|
||||
// Mock installation and activation successful
|
||||
shippingBannerWrapper.setProps( {
|
||||
activePlugins: [ 'woocommerce-services' ],
|
||||
installedPlugins: [ 'woocommerce-services' ],
|
||||
} );
|
||||
const createShippingLabelText = shippingBannerWrapper.find( 'p' );
|
||||
expect( createShippingLabelText.text() ).toBe( notActivatedMessage );
|
||||
} );
|
||||
|
||||
it( 'should show install text "By clicking "You\'ve already installed WooCommerce Shipping."..." when WooCommerce Service is already installed.', () => {
|
||||
const shippingBannerWrapper = createShippingBannerWrapper( {
|
||||
activePlugins: [ 'woocommerce-services' ],
|
||||
installedPlugins: [ 'woocommerce-services' ],
|
||||
} );
|
||||
const createShippingLabelText = shippingBannerWrapper.find( 'p' );
|
||||
expect( createShippingLabelText.text() ).toBe( activatedMessage );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,153 @@
|
|||
// This is needed so ExternalLinks appear correctly. Should be part of 'wp-components' style but for some reason it's not.
|
||||
@import '@wordpress/components/src/visually-hidden/style';
|
||||
|
||||
#woocommerce-admin-print-label {
|
||||
min-height: 100px;
|
||||
|
||||
.hndle,
|
||||
.handlediv {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wc-admin-shipping-banner-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 18px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.wc-admin-shipping-banner-illustration {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 1.15em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 2px 0 11px;
|
||||
font-size: 15px;
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
.components-button.is-button {
|
||||
margin: 10px 61px 10px auto;
|
||||
padding: 0 30px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 25px;
|
||||
text-decoration: none;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.notice-dismiss {
|
||||
top: 14px;
|
||||
right: 18px;
|
||||
}
|
||||
|
||||
.wc-admin-shipping-banner-install-error {
|
||||
color: #d63638;
|
||||
> .dashicon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 3px;
|
||||
vertical-align: middle;
|
||||
margin-top: -2px;
|
||||
path {
|
||||
fill: #d63638;
|
||||
}
|
||||
}
|
||||
}
|
||||
.components-external-link__icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.components-modal__frame.wc-admin-shipping-banner__dismiss-modal {
|
||||
|
||||
width: 500px;
|
||||
max-width: 100%;
|
||||
|
||||
.components-modal__header {
|
||||
border-bottom: 0;
|
||||
margin: 4px 0;
|
||||
padding: 0;
|
||||
height: 50px;
|
||||
.components-button.components-icon-button {
|
||||
|
||||
left: 15px;
|
||||
svg.dashicon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.components-modal__header-heading {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
|
||||
.wc-admin-shipping-banner__dismiss-modal-help-text {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin: 0 60px 1em 0;
|
||||
}
|
||||
|
||||
.wc-admin-shipping-banner__dismiss-modal-actions {
|
||||
text-align: right;
|
||||
|
||||
.components-button.is-button {
|
||||
height: 30px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
line-height: 15px;
|
||||
font-weight: 500;
|
||||
align-items: center;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
button.is-primary {
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1080px) {
|
||||
#woocommerce-admin-print-label {
|
||||
text-align: center;
|
||||
|
||||
.wc-admin-shipping-banner-container {
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
.components-button.is-button {
|
||||
margin: 10px 0 0 0;
|
||||
}
|
||||
p {
|
||||
margin: 5px 15px;
|
||||
max-width: initial;
|
||||
}
|
||||
h3 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notice.wcs-nux__notice {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wc-admin-shipping-banner__dismiss-modal {
|
||||
.components-button {
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
|
||||
export function acceptWcsTos() {
|
||||
const path = '/wc/v1/connect/tos';
|
||||
return apiFetch( {
|
||||
path,
|
||||
method: 'POST',
|
||||
data: { accepted: true },
|
||||
} );
|
||||
}
|
||||
|
||||
export function getWcsAssets() {
|
||||
const path = '/wc/v1/connect/assets';
|
||||
return apiFetch( {
|
||||
path,
|
||||
method: 'GET',
|
||||
} );
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
"analytics-dashboard/customizable": true,
|
||||
"devdocs": false,
|
||||
"onboarding": true,
|
||||
"store-alerts": true
|
||||
"store-alerts": true,
|
||||
"shipping-label-banner": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"analytics-dashboard/customizable": true,
|
||||
"devdocs": true,
|
||||
"onboarding": true,
|
||||
"store-alerts": true
|
||||
"store-alerts": true,
|
||||
"shipping-label-banner": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"analytics-dashboard/customizable": true,
|
||||
"devdocs": false,
|
||||
"onboarding": true,
|
||||
"store-alerts": true
|
||||
"store-alerts": true,
|
||||
"shipping-label-banner": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg width="96" height="68" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill="#fff" d="M0 0h96v68H0z"/><g clip-path="url(#clip0)"><path d="M0 0h96v68H0V0z" fill="#fff"/><g clip-path="url(#clip1)"><path d="M96 63.366H3v.224h93v-.224z" fill="#7F54B3"/><path d="M90.549 29.889h-31.26v33.523h31.26V29.89z" fill="#F7EDF7"/><path d="M60.736 46.595V31.122h1.78l8.732.28-.39 2.299v12.894H60.737zM77.645 53.434h-5.451V63.3h5.45v-9.866z" fill="#DCDCDE"/><path d="M35.595 45.361h-22.36v18.163h22.36V45.361z" fill="#F7EDF7"/><path d="M25.137 58.255h-3.782v5.157h3.782v-5.157z" fill="#DCDCDE"/><path d="M67.4 35.041c-.843 1.486-6.78-.99-6.78-.99l-11.344-4.605-2.742-2.046-1.747-1.303 3.88-1.095.109.053 2.704 1.3 10.37 6.243s7.75-1.432 5.55 2.443z" fill="#A0616A"/><path d="M38.807 17.784s-1.46.591-1.629 2.25c-.169 1.658-.183 11.012-.183 11.012s3.814-2.213 5.865-2.258c2.05-.046 6.815-3.532 6.815-3.532s-8.976-8.17-10.868-7.472z" fill="#D0CDE1"/><path opacity=".1" d="M38.807 17.784s-1.46.591-1.629 2.25c-.169 1.658-.183 11.012-.183 11.012s3.814-2.213 5.865-2.258c2.05-.046 6.815-3.532 6.815-3.532s-8.976-8.17-10.868-7.472z" fill="#000"/><path d="M53.115 48.388l-3.114.225s-.096 5.89-.501 7.4l5.34 4.26-1.113-3.588-.612-8.297z" fill="#A0616A"/><path d="M55.142 60.395c-.221-.079-.191-.122-.191-.122v-.224l-5.442-4.402c-.053.197-.112.326-.176.365-.2.121-.485.516-.769.973a4.393 4.393 0 00-.455 3.624l-.222 3.812h.89l.222-3.476h1.113s1.891 2.467 2.447 3.364c.556.897 3.004 1.121 5.451-.56 2.068-1.422-1.662-2.923-2.868-3.354z" fill="#2F2E41"/><path d="M25.75 35.102s-5.452 6.615-.223 10.651c2.3 1.776 4.321 5.137 5.863 8.344a19.152 19.152 0 005.114 6.534 18.967 18.967 0 007.378 3.734S62.46 42.502 56.12 36.335c-6.341-6.166-23.472-.785-23.472-.785l-6.898-.448z" fill="#2F2E41"/><path d="M41.182 29.524c.025 1.477-.256 3.098-1.527 3.448-2.447.673-2.67.336-2.67.336s-.445.785-.334 1.682c.112.897-1.056.168-1.056.168s-11.848 1.514-11.403-.168c.445-1.682 4.672-7.512 4.672-7.512s6.897-9.754 8.344-9.418c.049.011.099.025.15.04 1.474.432 3.973 2.342 4.188 3.1.223.784-.667 5.718-.667 5.718.18.857.282 1.73.303 2.606z" fill="#D0CDE1"/><path d="M58.678 43.287c-1.447.897-2.336-5.494-2.336-5.494l-10.457-6.39-2.379-2.466-1.515-1.571 4.005-.448.099.069 2.46 1.724 9.233 7.849s2.336 5.83.89 6.727z" fill="#A0616A"/><path opacity=".1" d="M40.879 26.917c.18.858.282 1.73.303 2.607-.777.143-1.48.194-1.972.084-2.002-.448-6.23.785-6.23.785s2.225-9.082 2.781-10.651a2.747 2.747 0 011.598-1.643c1.473.433 3.972 2.342 4.187 3.1.223.785-.667 5.718-.667 5.718z" fill="#000"/><path d="M40.828 16.68c-2.403-1.15-3.428-4.052-2.29-6.481 1.137-2.43 4.007-3.466 6.41-2.316 2.403 1.15 3.428 4.052 2.29 6.482-1.137 2.43-4.007 3.466-6.41 2.315z" fill="#2F2E41"/><path opacity=".1" d="M46.663 27.814s-1.466.604-3.157 1.123l-1.515-1.571 4.005-.448.099.069c.358.515.568.827.568.827z" fill="#000"/><path d="M44.065 15.589l-1.77 7.578-4.269-3.989s2.186-3.058 2.186-3.855c0-.798 3.853.266 3.853.266z" fill="#A0616A"/><path d="M38.209 17.387s-1.558.225-2.114 1.794c-.556 1.57-2.781 10.652-2.781 10.652s4.227-1.234 6.23-.785c2.002.448 7.453-1.794 7.453-1.794s-6.786-10.09-8.788-9.867z" fill="#D0CDE1"/><path d="M42.025 17.743c-1.857-.889-2.65-3.131-1.77-5.008.88-1.878 3.097-2.679 4.954-1.79 1.856.889 2.649 3.131 1.77 5.009-.88 1.877-3.097 2.678-4.954 1.79z" fill="#9F616A"/><path d="M39.829 15.713c-1.748-.837-2.494-2.947-1.666-4.714a3.484 3.484 0 014.662-1.684c1.747.836 2.493 2.947 1.666 4.713a3.484 3.484 0 01-4.662 1.685z" fill="#2F2E41"/><path d="M37.569 9.31c-1.614-.773-2.303-2.723-1.539-4.355A3.219 3.219 0 0140.337 3.4c1.614.773 2.303 2.723 1.539 4.355a3.219 3.219 0 01-4.307 1.556z" fill="#2F2E41"/><path d="M35.392 7.67a3.294 3.294 0 002.476 1.675 3.22 3.22 0 001.517-.182 3.19 3.19 0 001.257-.86 3.19 3.19 0 01-2.153 1.44c-.44.076-.892.06-1.328-.047a3.274 3.274 0 01-2.08-1.587 3.28 3.28 0 01-.401-1.274 3.24 3.24 0 01.14-1.323c.139-.425.363-.816.66-1.149A3.222 3.222 0 0034.985 6a3.275 3.275 0 00.408 1.67zM43.534 15.039c-1.53-.732-2.308-2.31-1.739-3.525.569-1.215 2.27-1.606 3.799-.874 1.529.732 2.307 2.31 1.738 3.525-.569 1.215-2.27 1.606-3.798.874z" fill="#2F2E41"/><path d="M43.752 16.123c-.327-.157-.383-.732-.125-1.284.259-.552.734-.873 1.062-.716.328.157.383.732.125 1.284-.259.552-.734.872-1.062.716z" fill="#A0616A"/><path opacity=".5" d="M70.904 32H61v.576h9.904V32zM70.904 34.591H61v.576h9.904v-.576zM70.904 37.182h-5.598v.576h5.598v-.576zM63.727 36.318H61v2.303h2.727v-2.303zM70.904 45.674H65.02v2.303h5.885v-2.303zM70.904 39.773H61v.575h9.904v-.575zM70.904 42.364H61v.575h9.904v-.575z" fill="#9A69C7"/></g></g><defs><clipPath id="clip0"><path d="M0 0h96v68H0V0z" fill="#fff"/></clipPath><clipPath id="clip1"><path fill="#fff" d="M3-4h88v86H3z"/></clipPath></defs></svg>
|
After Width: | Height: | Size: 4.6 KiB |
|
@ -84,6 +84,14 @@ class Init {
|
|||
'Automattic\WooCommerce\Admin\API\OnboardingThemes',
|
||||
)
|
||||
);
|
||||
} elseif ( Loader::is_feature_enabled( 'shipping-label-banner' ) ) {
|
||||
// Shipping Banner needs to use /active /install and /activate endpoints.
|
||||
$controllers = array_merge(
|
||||
$controllers,
|
||||
array(
|
||||
\Automattic\WooCommerce\Admin\API\OnboardingPlugins::class,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// The performance indicators controller must be registered last, after other /stats endpoints have been registered.
|
||||
|
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
/**
|
||||
* WooCommerce Shipping Label banner.
|
||||
* NOTE: DO NOT edit this file in WooCommerce core, this is generated from woocommerce-admin.
|
||||
*
|
||||
* @package Woocommerce Admin
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features;
|
||||
|
||||
use \Automattic\WooCommerce\Admin\Loader;
|
||||
|
||||
/**
|
||||
* Shows print shipping label banner on edit order page.
|
||||
*/
|
||||
class ShippingLabelBanner {
|
||||
|
||||
/**
|
||||
* Singleton for the display rules class
|
||||
*
|
||||
* @var ShippingLabelBannerDisplayRules
|
||||
*/
|
||||
private $shipping_label_banner_display_rules;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ), 6, 2 );
|
||||
add_filter( 'woocommerce_components_settings', array( $this, 'component_settings' ), 20 );
|
||||
add_filter( 'woocommerce_shared_settings', array( $this, 'component_settings' ), 20 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if WooCommerce Shipping makes sense for this merchant.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function should_show_meta_box() {
|
||||
if ( ! $this->shipping_label_banner_display_rules ) {
|
||||
$jetpack_version = null;
|
||||
$jetpack_connected = null;
|
||||
$wcs_version = null;
|
||||
$wcs_tos_accepted = null;
|
||||
|
||||
if ( class_exists( '\Jetpack_Data' ) ) {
|
||||
$user_token = \Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
|
||||
|
||||
$jetpack_connected = isset( $user_token->external_user_id );
|
||||
$jetpack_version = JETPACK__VERSION;
|
||||
}
|
||||
|
||||
if ( class_exists( '\WC_Connect_Loader' ) ) {
|
||||
$wcs_version = \WC_Connect_Loader::get_wcs_version();
|
||||
}
|
||||
if ( class_exists( '\WC_Connect_Options' ) ) {
|
||||
$wcs_tos_accepted = \WC_Connect_Options::get_option( 'tos_accepted' );
|
||||
}
|
||||
|
||||
$incompatible_plugins = class_exists( '\WC_Shipping_Fedex_Init' ) ||
|
||||
class_exists( '\WC_Shipping_UPS_Init' ) ||
|
||||
class_exists( '\WC_Integration_ShippingEasy' ) ||
|
||||
class_exists( '\WC_ShipStation_Integration' );
|
||||
|
||||
$this->shipping_label_banner_display_rules =
|
||||
new ShippingLabelBannerDisplayRules(
|
||||
$jetpack_version,
|
||||
$jetpack_connected,
|
||||
$wcs_version,
|
||||
$wcs_tos_accepted,
|
||||
$incompatible_plugins
|
||||
);
|
||||
}
|
||||
|
||||
return $this->shipping_label_banner_display_rules->should_display_banner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add metabox to order page.
|
||||
*
|
||||
* @param string $post_type current post type.
|
||||
* @param \WP_Post $post Current post object.
|
||||
*/
|
||||
public function add_meta_boxes( $post_type, $post ) {
|
||||
$order = wc_get_order( $post );
|
||||
if ( $this->should_show_meta_box() ) {
|
||||
add_meta_box(
|
||||
'woocommerce-admin-print-label',
|
||||
__( 'Shipping Label', 'woocommerce-admin' ),
|
||||
array( $this, 'meta_box' ),
|
||||
null,
|
||||
'normal',
|
||||
'high',
|
||||
array(
|
||||
'context' => 'shipping_label',
|
||||
'order_id' => $post->ID,
|
||||
'shippable_items_count' => $this->count_shippable_items( $order ),
|
||||
)
|
||||
);
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'add_print_shipping_label_script' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Count shippable items
|
||||
*
|
||||
* @param \WC_Order $order Current order.
|
||||
* @return int
|
||||
*/
|
||||
private function count_shippable_items( \WC_Order $order ) {
|
||||
$count = 0;
|
||||
foreach ( $order->get_items() as $item ) {
|
||||
if ( $item instanceof \WC_Order_Item_Product ) {
|
||||
$product = $item->get_product();
|
||||
if ( $product && $product->needs_shipping() ) {
|
||||
$count += $item->get_quantity();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
/**
|
||||
* Adds JS to order page to render shipping banner.
|
||||
*
|
||||
* @param string $hook current page hook.
|
||||
*/
|
||||
public function add_print_shipping_label_script( $hook ) {
|
||||
$rtl = is_rtl() ? '-rtl' : '';
|
||||
wp_enqueue_style(
|
||||
'print-shipping-label-banner-style',
|
||||
Loader::get_url( "print-shipping-label-banner/style{$rtl}.css" ),
|
||||
array( 'wp-components' ),
|
||||
Loader::get_file_version( 'print-shipping-label-banner/style.css' )
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'print-shipping-label-banner',
|
||||
Loader::get_url( 'wp-admin-scripts/print-shipping-label-banner.js' ),
|
||||
array( 'wp-i18n', 'wp-data', 'wp-element', 'moment', 'wp-api-fetch', WC_ADMIN_APP ),
|
||||
Loader::get_file_version( 'wp-admin-scripts/print-shipping-label-banner.js' ),
|
||||
true
|
||||
);
|
||||
|
||||
$payload = array(
|
||||
'nonce' => wp_create_nonce( 'wp_rest' ),
|
||||
'baseURL' => get_rest_url(),
|
||||
'wcs_server_connection' => true,
|
||||
);
|
||||
|
||||
wp_localize_script( 'print-shipping-label-banner', 'wcConnectData', $payload );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render placeholder metabox.
|
||||
*
|
||||
* @param \WP_Post $post current post.
|
||||
* @param array $args empty args.
|
||||
*/
|
||||
public function meta_box( $post, $args ) {
|
||||
|
||||
?>
|
||||
<div id="wc-admin-shipping-banner-root" class="woocommerce <?php echo esc_attr( 'wc-admin-shipping-banner' ); ?>" data-args="<?php echo esc_attr( wp_json_encode( $args['args'] ) ); ?>">
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the settings for the component for wc-api to use. If onboarding
|
||||
* is active, return its settings. Otherwise, loads "activePlugins" since
|
||||
* that's the ones we need to get installation status for WCS and Jetpack.
|
||||
*
|
||||
* @param array $settings Component settings.
|
||||
* @return array
|
||||
*/
|
||||
public function component_settings( $settings ) {
|
||||
if ( ! isset( $settings['onboarding'] ) ) {
|
||||
$settings['onboarding'] = array();
|
||||
}
|
||||
if ( ! isset( $settings['onboarding']['activePlugins'] ) ) {
|
||||
$settings['onboarding']['activePlugins'] = Onboarding::get_active_plugins();
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
<?php
|
||||
/**
|
||||
* WooCommerce Shipping Label Banner Display Rules.
|
||||
* NOTE: DO NOT edit this file in WooCommerce core, this is generated from woocommerce-admin.
|
||||
*
|
||||
* @package Woocommerce Admin
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features;
|
||||
|
||||
/**
|
||||
* Determines whether or not the Shipping Label Banner should be displayed
|
||||
*/
|
||||
class ShippingLabelBannerDisplayRules {
|
||||
|
||||
/**
|
||||
* Holds the installed Jetpack version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $jetpack_version;
|
||||
|
||||
|
||||
/**
|
||||
* Whether or not the installed Jetpack is connected.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $jetpack_connected;
|
||||
|
||||
/**
|
||||
* Holds the installed WooCommerce Services version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $wcs_version;
|
||||
|
||||
/**
|
||||
* Whether or not there're plugins installed incompatible with the banner.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $no_incompatible_plugins_installed;
|
||||
|
||||
/**
|
||||
* Whether or not the WooCommerce Services ToS has been accepted.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $wcs_tos_accepted;
|
||||
|
||||
/**
|
||||
* Minimum supported Jetpack version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $min_jetpack_version = '4.4';
|
||||
|
||||
/**
|
||||
* Minimum supported WooCommerce Services version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $min_wcs_version = '1.22.5';
|
||||
|
||||
/**
|
||||
* Supported countries by USPS, see: https://webpmt.usps.gov/pmt010.cfm
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $supported_countries = array( 'US', 'AS', 'PR', 'VI', 'GU', 'MP', 'UM', 'FM', 'MH' );
|
||||
|
||||
/**
|
||||
* Array of supported currency codes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $supported_currencies = array( 'USD' );
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $jetpack_version Installed Jetpack version to check.
|
||||
* @param bool $jetpack_connected Is Jetpack connected?.
|
||||
* @param string $wcs_version Installed WooCommerce Services version to check.
|
||||
* @param bool $wcs_tos_accepted WooCommerce Services Terms of Service accepted?.
|
||||
* @param bool $incompatible_plugins_installed Are there any incompatible plugins installed?.
|
||||
*/
|
||||
public function __construct( $jetpack_version, $jetpack_connected, $wcs_version, $wcs_tos_accepted, $incompatible_plugins_installed ) {
|
||||
$this->jetpack_version = $jetpack_version;
|
||||
$this->jetpack_connected = $jetpack_connected;
|
||||
$this->wcs_version = $wcs_version;
|
||||
$this->wcs_tos_accepted = $wcs_tos_accepted;
|
||||
$this->no_incompatible_plugins_installed = ! $incompatible_plugins_installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not the banner should be displayed.
|
||||
*/
|
||||
public function should_display_banner() {
|
||||
if ( ! $this->should_allow_banner() ) {
|
||||
return false;
|
||||
}
|
||||
$ab_test = get_option( 'woocommerce_shipping_prompt_ab' );
|
||||
|
||||
// If it doesn't exist yet, generate it for later use and save it, so we always show the same to this user.
|
||||
if ( ! $ab_test ) {
|
||||
$ab_test = 1 !== wp_rand( 1, 4 ) ? 'a' : 'b'; // 25% of users. b gets the prompt.
|
||||
update_option( 'woocommerce_shipping_prompt_ab', $ab_test, false );
|
||||
}
|
||||
|
||||
return 'b' === $ab_test;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether banner is eligible for display (does not include a/b logic).
|
||||
*/
|
||||
public function should_allow_banner() {
|
||||
return $this->banner_not_dismissed() &&
|
||||
$this->jetpack_installed_and_active() &&
|
||||
$this->jetpack_up_to_date() &&
|
||||
$this->jetpack_connected &&
|
||||
$this->no_incompatible_plugins_installed &&
|
||||
$this->order_has_shippable_products() &&
|
||||
$this->store_in_us_and_usd() &&
|
||||
( $this->wcs_not_installed() || (
|
||||
$this->wcs_up_to_date() && ! $this->wcs_tos_accepted
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the banner was not dismissed by the user.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function banner_not_dismissed() {
|
||||
$dismissed_timestamp_ms = get_option( 'woocommerce_shipping_dismissed_timestamp' );
|
||||
|
||||
if ( ! is_numeric( $dismissed_timestamp_ms ) ) {
|
||||
return true;
|
||||
}
|
||||
$dismissed_timestamp_ms = intval( $dismissed_timestamp_ms );
|
||||
$dismissed_timestamp = intval( round( $dismissed_timestamp_ms / 1000 ) );
|
||||
$expired_timestamp = $dismissed_timestamp + 24 * 60 * 60; // 24 hours from click time
|
||||
|
||||
$dismissed_for_good = -1 === $dismissed_timestamp_ms;
|
||||
$dismissed_24h = time() < $expired_timestamp;
|
||||
|
||||
return ! $dismissed_for_good && ! $dismissed_24h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if jetpack is installed and active.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function jetpack_installed_and_active() {
|
||||
return ! ! $this->jetpack_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if Jetpack version is supported.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function jetpack_up_to_date() {
|
||||
return version_compare( $this->jetpack_version, $this->min_jetpack_version, '>=' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there's a shippable product in the current order.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function order_has_shippable_products() {
|
||||
$post = get_post();
|
||||
if ( ! $post ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$order = wc_get_order( get_post()->ID );
|
||||
|
||||
if ( ! $order ) {
|
||||
return false;
|
||||
}
|
||||
// At this point (no packaging data), only show if there's at least one existing and shippable product.
|
||||
foreach ( $order->get_items() as $item ) {
|
||||
if ( $item instanceof \WC_Order_Item_Product ) {
|
||||
$product = $item->get_product();
|
||||
|
||||
if ( $product && $product->needs_shipping() ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the store is in the US and has its default currency set to USD.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function store_in_us_and_usd() {
|
||||
$base_currency = get_woocommerce_currency();
|
||||
$base_location = wc_get_base_location();
|
||||
|
||||
return in_array( $base_currency, $this->supported_currencies, true ) && in_array( $base_location['country'], $this->supported_countries, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if WooCommerce Services is not installed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function wcs_not_installed() {
|
||||
return ! $this->wcs_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if WooCommerce Services is up to date.
|
||||
*/
|
||||
private function wcs_up_to_date() {
|
||||
return $this->wcs_version && version_compare( $this->wcs_version, $this->min_wcs_version, '>=' );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
<?php
|
||||
/**
|
||||
* Shipping Label Banner Display Rules tests.
|
||||
*
|
||||
* @package WooCommerce\Tests\Shipping-label-banner-display-rules
|
||||
*/
|
||||
|
||||
use \Automattic\WooCommerce\Admin\Features\ShippingLabelBannerDisplayRules;
|
||||
|
||||
/**
|
||||
* Class WC_Tests_Shipping_Label_Banner_Display_Rules
|
||||
*/
|
||||
class WC_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Case {
|
||||
|
||||
/**
|
||||
* Jetpack version to test the display manager.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $valid_jetpack_version = '4.4';
|
||||
|
||||
/**
|
||||
* Stores the default WordPress options stored in teh database.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $modified_options = array(
|
||||
'woocommerce_default_country' => null,
|
||||
'woocommerce_currency' => null,
|
||||
'woocommerce_shipping_prompt_ab' => null,
|
||||
'woocommerce_shipping_dismissed_timestamp' => null,
|
||||
);
|
||||
|
||||
/**
|
||||
* Setup for every single test.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setup();
|
||||
|
||||
update_option( 'woocommerce_default_country', 'US' );
|
||||
update_option( 'woocommerce_currency', 'USD' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup for the whole test class.
|
||||
*/
|
||||
public static function setUpBeforeClass() {
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
foreach ( self::$modified_options as $option_name => $option_value ) {
|
||||
self::$modified_options[ $option_name ] = $option_value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up test data once all test have run.
|
||||
*/
|
||||
public static function tearDownAfterClass() {
|
||||
parent::tearDownAfterClass();
|
||||
|
||||
foreach ( self::$modified_options as $option_name => $option_value ) {
|
||||
update_option( $option_name, $option_value );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the banner is displayed when all conditions are satisfied:
|
||||
* - Banner NOT dismissed
|
||||
* - Jetpack >= 4.4 installed and active
|
||||
* - Jetpack Connected
|
||||
* - No incompatible extensions installed:
|
||||
* - Shipstation not installed
|
||||
* - UPS not Installed
|
||||
* - Fedex not installed
|
||||
* - ShippingEasy not installed
|
||||
* - Order contains physical products which need to be shipped (we should check that the order status is not set to complete)
|
||||
* - Store is located in US
|
||||
* - Store currency is set to USD
|
||||
* - WCS plugin not installed OR WCS is installed *AND* ToS have NOT been accepted *AND* WCS version is 1.22.5 or greater
|
||||
* (The 1.22.5 or greater requirement is so we can launch the shipping modal from the banner)
|
||||
*/
|
||||
public function test_display_banner_if_all_conditions_are_met() {
|
||||
$this->with_order(
|
||||
function( $that ) {
|
||||
$shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false );
|
||||
|
||||
$that->assertEquals( $shipping_label_banner_display_rules->should_allow_banner(), true );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the banner is hidden when Jetpack is not active.
|
||||
*/
|
||||
public function test_if_banner_hidden_when_jetpack_disconnected() {
|
||||
$shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( null, null, null, null, null );
|
||||
|
||||
$this->assertEquals( $shipping_label_banner_display_rules->should_allow_banner(), false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the banner is hidden when a dismiss banner option is checked.
|
||||
*/
|
||||
public function test_if_banner_hidden_when_dismiss_option_enabled() {
|
||||
update_option( 'woocommerce_shipping_dismissed_timestamp', -1 );
|
||||
$shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false );
|
||||
|
||||
$this->assertEquals( $shipping_label_banner_display_rules->should_allow_banner(), false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Banner should not show if it was dismissed 2 hours ago.
|
||||
*/
|
||||
public function test_if_banner_hidden_when_dismiss_was_clicked_2_hrs_ago() {
|
||||
$two_hours_from_ago = ( time() - 2 * 60 * 60 ) * 1000;
|
||||
update_option( 'woocommerce_shipping_dismissed_timestamp', $two_hours_from_ago );
|
||||
|
||||
$shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false );
|
||||
|
||||
$this->assertEquals( $shipping_label_banner_display_rules->should_allow_banner(), false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Banner should show if it was dismissed 24 hours and 1 second ago.
|
||||
*/
|
||||
public function test_if_banner_hidden_when_dismiss_was_clicked_24_hrs_1s_ago() {
|
||||
$twenty_four_hours_one_sec_ago = ( time() - 24 * 60 * 60 - 1 ) * 1000;
|
||||
update_option( 'woocommerce_shipping_dismissed_timestamp', $twenty_four_hours_one_sec_ago );
|
||||
|
||||
$this->with_order(
|
||||
function( $that ) {
|
||||
$shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false );
|
||||
|
||||
$that->assertEquals( $shipping_label_banner_display_rules->should_allow_banner(), true );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the banner is hidden when no shippable product available.
|
||||
*/
|
||||
public function test_if_banner_hidden_when_no_shippable_product() {
|
||||
$shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false );
|
||||
|
||||
$this->assertEquals( $shipping_label_banner_display_rules->should_allow_banner(), false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the banner is displayed when the store is in the US.
|
||||
*/
|
||||
public function test_if_banner_hidden_when_store_is_not_in_us() {
|
||||
update_option( 'woocommerce_default_country', 'ES' );
|
||||
$this->with_order(
|
||||
function( $that ) {
|
||||
$shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false );
|
||||
|
||||
$that->assertEquals( $shipping_label_banner_display_rules->should_allow_banner(), false );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the banner is displayed when the store's currency is USD.
|
||||
*/
|
||||
public function test_if_banner_hidden_when_currency_is_not_usd() {
|
||||
update_option( 'woocommerce_currency', 'EUR' );
|
||||
$this->with_order(
|
||||
function( $that ) {
|
||||
$shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false );
|
||||
|
||||
$that->assertEquals( $shipping_label_banner_display_rules->should_allow_banner(), false );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the banner is hidden when an incompatible plugin is installed
|
||||
*/
|
||||
public function test_if_banner_hidden_when_incompatible_plugin_installed() {
|
||||
$this->with_order(
|
||||
function( $that ) {
|
||||
$shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, true );
|
||||
|
||||
$that->assertEquals( $shipping_label_banner_display_rules->should_allow_banner(), false );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the banner is hidden when Jetpack version is not at least 4.4.
|
||||
*/
|
||||
public function test_if_banner_hidden_when_jetpack_version_is_old() {
|
||||
$this->with_order(
|
||||
function( $that ) {
|
||||
$shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.3', true, '1.22.5', false, false );
|
||||
|
||||
$that->assertEquals( $shipping_label_banner_display_rules->should_allow_banner(), false );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the banner is hidden when the WooCommerce Services Terms of Service has been already accepted.
|
||||
*/
|
||||
public function test_if_banner_hidden_when_wcs_tos_accepted() {
|
||||
$this->with_order(
|
||||
function( $that ) {
|
||||
$shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', true, false );
|
||||
|
||||
$that->assertEquals( $shipping_label_banner_display_rules->should_allow_banner(), false );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the banner is hidden when WooCommerce Services is installed but not up to date.
|
||||
*/
|
||||
public function test_if_banner_hidden_when_wcs_not_installed() {
|
||||
$this->with_order(
|
||||
function( $that ) {
|
||||
$shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.4', false, false );
|
||||
|
||||
$that->assertEquals( $shipping_label_banner_display_rules->should_allow_banner(), false );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the banner is displayed when site is in 'b' group.
|
||||
*/
|
||||
public function test_display_banner_if_b_flag() {
|
||||
$this->with_order(
|
||||
function( $that ) {
|
||||
update_option( 'woocommerce_shipping_prompt_ab', 'b' );
|
||||
|
||||
$shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false );
|
||||
$that->assertEquals( $shipping_label_banner_display_rules->should_display_banner(), true );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the banner is displayed when site is in 'a' group.
|
||||
*/
|
||||
public function test_no_display_banner_if_a_flag() {
|
||||
$this->with_order(
|
||||
function( $that ) {
|
||||
update_option( 'woocommerce_shipping_prompt_ab', 'a' );
|
||||
|
||||
$shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false );
|
||||
$that->assertEquals( $shipping_label_banner_display_rules->should_display_banner(), false );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a test order.
|
||||
*/
|
||||
private function create_order() {
|
||||
$product = WC_Helper_Product::create_simple_product();
|
||||
$product->set_props( array( 'virtual' => true ) );
|
||||
|
||||
$order = new WC_Order();
|
||||
$order_item = new WC_Order_Item_Product();
|
||||
$order_item->set_props( array( 'product' => $product ) );
|
||||
$order->add_item( $order_item );
|
||||
$order->save();
|
||||
|
||||
global $post;
|
||||
|
||||
// phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
$post = new \stdClass();
|
||||
$post->ID = $order->get_id();
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the test order.
|
||||
*
|
||||
* @param object $order to destroy.
|
||||
*/
|
||||
private function destroy_order( $order ) {
|
||||
foreach ( $order->get_items() as $item ) {
|
||||
$product = $item->get_product();
|
||||
$product->delete( true );
|
||||
$item->delete( true );
|
||||
}
|
||||
|
||||
$order->delete( true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a function call within an order creation/deletion lifecycle.
|
||||
*
|
||||
* @param function $callback to wrap.
|
||||
*/
|
||||
private function with_order( $callback ) {
|
||||
$order = $this->create_order();
|
||||
|
||||
$callback( $this );
|
||||
|
||||
$this->destroy_order( $order );
|
||||
}
|
||||
|
||||
}
|
|
@ -77,6 +77,7 @@ const wpAdminScripts = [
|
|||
'onboarding-product-notice',
|
||||
'onboarding-product-import-notice',
|
||||
'onboarding-tax-notice',
|
||||
'print-shipping-label-banner',
|
||||
'onboarding-menu-experience',
|
||||
];
|
||||
wpAdminScripts.forEach( ( name ) => {
|
||||
|
|
Loading…
Reference in New Issue