Commit 9f43f5c696e for woocommerce
commit 9f43f5c696e1e855a819e6d2f366c1aa8dab3747
Author: Ahmar Zaidi <71930390+AhmarZaidi@users.noreply.github.com>
Date: Fri Sep 11 18:40:26 2026 +0530
fix: "Create shipping" label button doesn't refresh page (#67895)
* fix: Create shipping lable button doesn't refresh page
* Add focused tests for success & failure cases and clearing the busy state
* add: changelog entry
* fix: address PR feedback
* fix: failing tests
---------
Co-authored-by: Sam Najian <dev@najian.info>
Co-authored-by: Fernando Espinosa <Ferdev@users.noreply.github.com>
diff --git a/plugins/woocommerce/changelog/fix-40683 b/plugins/woocommerce/changelog/fix-40683
new file mode 100644
index 00000000000..1b1760c9a7e
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-40683
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix the Create shipping label button by showing a completion notice and a Reload page button after WooCommerce Shipping is installed and activated.
diff --git a/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/setup-notice/index.js b/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/setup-notice/index.js
index c9c8f819a55..b6212ddcbe5 100644
--- a/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/setup-notice/index.js
+++ b/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/setup-notice/index.js
@@ -2,7 +2,7 @@
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
-import { Icon, cautionFilled } from '@wordpress/icons';
+import { Icon, cautionFilled, info } from '@wordpress/icons';
export const setupErrorTypes = {
DOWNLOAD: 'download',
@@ -20,7 +20,24 @@ const setupErrorDescriptions = {
[ setupErrorTypes.START ]: __( 'start', 'woocommerce' ),
};
-export default function SetupNotice( { isSetupError, errorReason } ) {
+export default function SetupNotice( {
+ isSetupError,
+ errorReason,
+ infoMessage,
+} ) {
+ if ( infoMessage ) {
+ return (
+ <div className="wc-admin-shipping-banner-install-info">
+ <Icon icon={ info } className="info-icon" />
+ { infoMessage }
+ </div>
+ );
+ }
+
+ if ( ! isSetupError ) {
+ return null;
+ }
+
const getErrorMessage = ( errorType ) => {
// Default to 'set up' description if the error type somehow doesn't exist.
const description =
@@ -38,10 +55,6 @@ export default function SetupNotice( { isSetupError, errorReason } ) {
);
};
- if ( ! isSetupError ) {
- return null;
- }
-
return (
<div className="wc-admin-shipping-banner-install-error">
<Icon icon={ cautionFilled } className="warning-icon" />
diff --git a/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/shipping-banner/index.js b/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/shipping-banner/index.js
index c8cd1c6e6e9..e44573d23db 100644
--- a/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/shipping-banner/index.js
+++ b/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/shipping-banner/index.js
@@ -42,6 +42,8 @@ export class ShippingBanner extends Component {
wcsSetupError: false,
isShippingLabelButtonBusy: false,
isWcsModalOpen: false,
+ isPluginInstalledAndActivated: false,
+ infoMessage: null,
};
}
@@ -72,8 +74,24 @@ export class ShippingBanner extends Component {
};
createShippingLabelClicked = () => {
- const { activePlugins } = this.props;
- this.setState( { isShippingLabelButtonBusy: true } );
+ if ( this.state.isPluginInstalledAndActivated ) {
+ window.location.reload( true );
+ return;
+ }
+
+ const { activePlugins, isRequesting } = this.props;
+ if ( isRequesting ) {
+ return;
+ }
+
+ this.setState( {
+ isShippingLabelButtonBusy: true,
+ infoMessage: __(
+ 'Installing and activating WooCommerce Shipping in the background…',
+ 'woocommerce'
+ ),
+ wcsSetupError: false,
+ } );
this.trackElementClicked( 'shipping_banner_create_label' );
if ( ! activePlugins.includes( wcShippingPluginSlug ) ) {
this.installAndActivatePlugins( wcShippingPluginSlug );
@@ -84,54 +102,62 @@ export class ShippingBanner extends Component {
async installAndActivatePlugins( pluginSlug ) {
// Avoid double activating.
- const {
- installPlugins,
- activatePlugins,
- isRequesting,
- activePlugins,
- isWcstCompatible,
- isIncompatibleWCShippingInstalled,
- } = this.props;
+ const { installPlugins, activatePlugins, isRequesting } = this.props;
if ( isRequesting ) {
+ this.setState( { isShippingLabelButtonBusy: false } );
return false;
}
- const install = await installPlugins( [ pluginSlug ] );
- if ( install.success !== true ) {
+
+ try {
+ const install = await installPlugins( [ pluginSlug ] );
+ if ( ! install || install.success !== true ) {
+ this.setState( {
+ setupErrorReason: setupErrorTypes.INSTALL,
+ wcsSetupError: true,
+ isShippingLabelButtonBusy: false,
+ infoMessage: null,
+ } );
+ return;
+ }
+ } catch ( error ) {
this.setState( {
setupErrorReason: setupErrorTypes.INSTALL,
wcsSetupError: true,
+ isShippingLabelButtonBusy: false,
+ infoMessage: null,
} );
return;
}
- const activation = await activatePlugins( [ pluginSlug ] );
- if ( activation.success !== true ) {
+ try {
+ const activation = await activatePlugins( [ pluginSlug ] );
+ if ( ! activation || activation.success !== true ) {
+ this.setState( {
+ setupErrorReason: setupErrorTypes.ACTIVATE,
+ wcsSetupError: true,
+ isShippingLabelButtonBusy: false,
+ infoMessage: null,
+ } );
+ return;
+ }
+ } catch ( error ) {
this.setState( {
setupErrorReason: setupErrorTypes.ACTIVATE,
wcsSetupError: true,
+ isShippingLabelButtonBusy: false,
+ infoMessage: null,
} );
return;
}
- /**
- * If a incompatible version of the WooCommerce Shipping plugin is installed, the necessary endpoints
- * are not available, so we need to reload the page to ensure to make the plugin usable.
- */
- if ( isIncompatibleWCShippingInstalled ) {
- window.location.reload( true );
- return;
- }
-
- if (
- ! activePlugins.includes( wcShippingPluginSlug ) &&
- isWcstCompatible
- ) {
- this.acceptTosAndGetWCSAssets();
- } else {
- this.setState( {
- showShippingBanner: false,
- } );
- }
+ this.setState( {
+ isPluginInstalledAndActivated: true,
+ isShippingLabelButtonBusy: false,
+ infoMessage: __(
+ 'WooCommerce Shipping is installed and activated. Please reload the page to get started.',
+ 'woocommerce'
+ ),
+ } );
}
woocommerceServiceLinkClicked = () => {
@@ -169,7 +195,12 @@ export class ShippingBanner extends Component {
.then( () => getWcsAssets() )
.then( ( wcsAssets ) => this.loadWcsAssets( wcsAssets ) )
.catch( () => {
- this.setState( { wcsSetupError: true } );
+ this.setState( {
+ wcsSetupError: true,
+ wcsAssetsLoading: false,
+ isShippingLabelButtonBusy: false,
+ infoMessage: null,
+ } );
} );
};
@@ -255,7 +286,7 @@ export class ShippingBanner extends Component {
.querySelectorAll( 'link[href*="/woocommerce-services/"]' )
.forEach( ( node ) => node.remove?.() );
- Promise.all( [
+ return Promise.all( [
new Promise( ( resolve, reject ) => {
const script = document.createElement( 'script' );
script.src = jsPath;
@@ -406,6 +437,11 @@ export class ShippingBanner extends Component {
}
const { actionButtonLabel, headline } = this.props;
+ const { isPluginInstalledAndActivated, infoMessage } = this.state;
+ const buttonLabel = isPluginInstalledAndActivated
+ ? __( 'Reload page', 'woocommerce' )
+ : actionButtonLabel;
+
return (
<div>
<div className="wc-admin-shipping-banner-container">
@@ -416,41 +452,44 @@ export class ShippingBanner extends Component {
/>
<div className="wc-admin-shipping-banner-blob">
<h3>{ headline }</h3>
- <p>
- { interpolateComponents( {
- mixedString: sprintf(
- // translators: %s is the action button label.
- __(
- 'By clicking "%s", {{wcsLink}}WooCommerce Shipping{{/wcsLink}} will be installed and you agree to its {{tosLink}}Terms of Service{{/tosLink}}.',
- 'woocommerce'
- ),
- actionButtonLabel
- ),
- components: {
- tosLink: (
- <ExternalLink
- href="https://wordpress.com/tos"
- target="_blank"
- type="external"
- />
- ),
- wcsLink: (
- <ExternalLink
- href="https://woocommerce.com/products/shipping/?utm_medium=product"
- target="_blank"
- type="external"
- onClick={
- this
- .woocommerceServiceLinkClicked
- }
- />
+ { ! isPluginInstalledAndActivated && (
+ <p>
+ { interpolateComponents( {
+ mixedString: sprintf(
+ // translators: %s is the action button label.
+ __(
+ 'By clicking "%s", {{wcsLink}}WooCommerce Shipping{{/wcsLink}} will be installed and you agree to its {{tosLink}}Terms of Service{{/tosLink}}.',
+ 'woocommerce'
+ ),
+ actionButtonLabel
),
- },
- } ) }
- </p>
+ components: {
+ tosLink: (
+ <ExternalLink
+ href="https://wordpress.com/tos"
+ target="_blank"
+ type="external"
+ />
+ ),
+ wcsLink: (
+ <ExternalLink
+ href="https://woocommerce.com/products/shipping/?utm_medium=product"
+ target="_blank"
+ type="external"
+ onClick={
+ this
+ .woocommerceServiceLinkClicked
+ }
+ />
+ ),
+ },
+ } ) }
+ </p>
+ ) }
<SetupNotice
isSetupError={ this.isSetupError() }
errorReason={ this.state.setupErrorReason }
+ infoMessage={ infoMessage }
/>
</div>
<Button
@@ -459,7 +498,7 @@ export class ShippingBanner extends Component {
isBusy={ isShippingLabelButtonBusy }
onClick={ this.createShippingLabelClicked }
>
- { actionButtonLabel }
+ { buttonLabel }
</Button>
<button
diff --git a/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/shipping-banner/test/index.js b/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/shipping-banner/test/index.js
index 2fdac725720..5e9b2014d32 100644
--- a/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/shipping-banner/test/index.js
+++ b/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/shipping-banner/test/index.js
@@ -71,6 +71,9 @@ describe( 'Tracking clicks in shippingBanner', () => {
it( 'should record an event when user clicks "Create shipping label"', async () => {
const actionButtonLabel = 'Create shipping label';
+ const activatePluginsMock = jest
+ .fn()
+ .mockResolvedValue( { success: true } );
const { getByRole } = render(
<ShippingBanner
@@ -79,9 +82,7 @@ describe( 'Tracking clicks in shippingBanner', () => {
installPlugins={ jest
.fn()
.mockResolvedValue( { success: true } ) }
- activatePlugins={ jest
- .fn()
- .mockResolvedValue( { success: true } ) }
+ activatePlugins={ activatePluginsMock }
isRequesting={ false }
itemsCount={ 1 }
orderId={ 1 }
@@ -99,9 +100,10 @@ describe( 'Tracking clicks in shippingBanner', () => {
)
);
- // Wait for any async calls to complete so we don't get `Warning: An update to ShippingBanner inside a test was not wrapped in act(...).
await waitFor( () => {
- expect( acceptWcsTos ).toHaveBeenCalled();
+ expect( activatePluginsMock ).toHaveBeenCalledWith( [
+ wcsPluginSlug,
+ ] );
} );
} );
@@ -172,10 +174,12 @@ describe( 'Create shipping label button', () => {
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',
+ reload: jest.fn(),
};
beforeEach( () => {
acceptWcsTos.mockClear();
+ window.location.reload.mockClear();
} );
it( 'should install WooCommerce Shipping when button is clicked', async () => {
@@ -201,11 +205,6 @@ describe( 'Create shipping label button', () => {
} )
);
- // Wait for any async calls to complete so we don't get `Warning: An update to ShippingBanner inside a test was not wrapped in act(...).
- await waitFor( () => {
- expect( acceptWcsTos ).toHaveBeenCalled();
- } );
-
await waitFor( () =>
expect( installPlugins ).toHaveBeenCalledWith( [
'woocommerce-shipping',
@@ -213,9 +212,9 @@ describe( 'Create shipping label button', () => {
);
} );
- it( 'should activate WooCommerce Shipping when installation finishes', async () => {
+ it( 'should show info notice and switch button to "Reload page" when installation and activation finishes', async () => {
const actionButtonLabel = 'Create shipping label';
- const { getByRole } = render(
+ const { getByRole, getByText, queryByText } = render(
<ShippingBanner
isJetpackConnected={ true }
activatePlugins={ activatePlugins }
@@ -235,16 +234,31 @@ describe( 'Create shipping label button', () => {
} )
);
- // Wait for any async calls to complete so we don't get `Warning: An update to ShippingBanner inside a test was not wrapped in act(...).
- await waitFor( () => {
- expect( acceptWcsTos ).toHaveBeenCalled();
- } );
-
await waitFor( () =>
expect( activatePlugins ).toHaveBeenCalledWith( [
'woocommerce-shipping',
] )
);
+
+ await waitFor( () =>
+ expect(
+ getByText(
+ 'WooCommerce Shipping is installed and activated. Please reload the page to get started.'
+ )
+ ).toBeInTheDocument()
+ );
+
+ expect(
+ queryByText( /By clicking "Create shipping label"/i )
+ ).not.toBeInTheDocument();
+
+ const reloadButton = getByRole( 'button', { name: 'Reload page' } );
+ expect( reloadButton ).toBeInTheDocument();
+ expect( reloadButton ).not.toHaveClass( 'is-busy' );
+
+ userEvent.click( reloadButton );
+
+ expect( window.location.reload ).toHaveBeenCalledWith( true );
} );
it( 'should perform a request to accept the TOS and get WCS assets to load', async () => {
@@ -256,7 +270,7 @@ describe( 'Create shipping label button', () => {
<ShippingBanner
isJetpackConnected={ true }
activatePlugins={ activatePlugins }
- activePlugins={ [ wcstPluginSlug ] }
+ activePlugins={ [ wcsPluginSlug ] }
installPlugins={ installPlugins }
isRequesting={ false }
itemsCount={ 1 }
@@ -301,7 +315,7 @@ describe( 'Create shipping label button', () => {
<ShippingBanner
isJetpackConnected={ true }
activatePlugins={ activatePlugins }
- activePlugins={ [ wcstPluginSlug, 'jetpack' ] }
+ activePlugins={ [ wcsPluginSlug, 'jetpack' ] }
installPlugins={ installPlugins }
isRequesting={ false }
itemsCount={ 1 }
@@ -362,9 +376,9 @@ describe( 'Create shipping label button', () => {
assets: {
// Easy to identify string in our hijacked setter function.
wcshipping_create_label_script:
- 'wcshipping_create_label_script',
+ '/wcshipping_create_label_script',
wcshipping_shipment_tracking_script:
- 'wcshipping_create_label_script',
+ '/wcshipping_shipment_tracking_script',
// Empty string to avoid creating a script tag we also have to hijack.
wcshipping_create_label_style: '',
wcshipping_shipment_tracking_style: '',
@@ -372,16 +386,17 @@ describe( 'Create shipping label button', () => {
} )
);
- // Force the script tag to trigger its onload().
- // Adapted from https://stackoverflow.com/a/49204336.
- // const scriptSrcProperty = window.HTMLScriptElement.prototype.src;
+ const originalSrc = Object.getOwnPropertyDescriptor(
+ window.HTMLScriptElement.prototype,
+ 'src'
+ );
+
Object.defineProperty( window.HTMLScriptElement.prototype, 'src', {
+ configurable: true,
set( src ) {
if (
- [
- 'wcshipping_create_label_script',
- 'wcshipping_shipment_tracking_script',
- ].includes( src )
+ src.includes( 'wcshipping_create_label_script' ) ||
+ src.includes( 'wcshipping_shipment_tracking_script' )
) {
setTimeout( () => {
this.onload();
@@ -428,6 +443,16 @@ describe( 'Create shipping label button', () => {
} );
expect( openWcsModalSpy ).toHaveBeenCalledTimes( 1 );
+
+ if ( originalSrc ) {
+ Object.defineProperty(
+ window.HTMLScriptElement.prototype,
+ 'src',
+ originalSrc
+ );
+ } else {
+ delete window.HTMLScriptElement.prototype.src;
+ }
} );
} );
@@ -439,8 +464,10 @@ describe( 'In the process of installing, activating, loading assets for WooComme
isJetpackConnected={ true }
activatePlugins={ jest.fn() }
activePlugins={ [ 'jetpack' ] }
- installPlugins={ jest.fn() }
- isRequesting={ true }
+ installPlugins={ jest
+ .fn()
+ .mockImplementation( () => new Promise( () => {} ) ) }
+ isRequesting={ false }
itemsCount={ 1 }
orderId={ 1 }
isWcstCompatible={ true }
@@ -520,6 +547,9 @@ describe( 'Setup error message', () => {
)
).toBeInTheDocument()
);
+ expect(
+ getByRole( 'button', { name: actionButtonLabel } )
+ ).not.toHaveClass( 'is-busy' );
} );
it( 'should show if there is activation error', async () => {
@@ -552,6 +582,255 @@ describe( 'Setup error message', () => {
)
).toBeInTheDocument()
);
+ expect(
+ getByRole( 'button', { name: actionButtonLabel } )
+ ).not.toHaveClass( 'is-busy' );
+ } );
+
+ it( 'should clear busy state and show error if setup API call fails', async () => {
+ acceptWcsTos.mockRejectedValueOnce( new Error( 'API Error' ) );
+ const actionButtonLabel = 'Create shipping label';
+
+ const { getByRole, getByText } = render(
+ <ShippingBanner
+ isJetpackConnected={ true }
+ activatePlugins={ jest.fn().mockReturnValue( {
+ success: true,
+ } ) }
+ activePlugins={ [ wcsPluginSlug, 'jetpack' ] }
+ installPlugins={ jest.fn().mockReturnValue( {
+ success: true,
+ } ) }
+ itemsCount={ 1 }
+ isRequesting={ false }
+ orderId={ 1 }
+ isWcstCompatible={ true }
+ actionButtonLabel={ actionButtonLabel }
+ />
+ );
+
+ userEvent.click( getByRole( 'button', { name: actionButtonLabel } ) );
+
+ await waitFor( () =>
+ expect(
+ getByText(
+ 'Unable to set up the plugin. Refresh the page and try again.'
+ )
+ ).toBeInTheDocument()
+ );
+ expect(
+ getByRole( 'button', { name: actionButtonLabel } )
+ ).not.toHaveClass( 'is-busy' );
+ } );
+
+ it( 'should clear busy state and show error if installPlugins throws an error', async () => {
+ const actionButtonLabel = 'Create shipping label';
+
+ const { getByRole, getByText } = render(
+ <ShippingBanner
+ isJetpackConnected={ true }
+ activatePlugins={ jest.fn() }
+ activePlugins={ [ 'jetpack' ] }
+ installPlugins={ jest
+ .fn()
+ .mockRejectedValue( new Error( 'Network error' ) ) }
+ itemsCount={ 1 }
+ isRequesting={ false }
+ orderId={ 1 }
+ isWcstCompatible={ true }
+ actionButtonLabel={ actionButtonLabel }
+ />
+ );
+
+ userEvent.click( getByRole( 'button', { name: actionButtonLabel } ) );
+
+ await waitFor( () =>
+ expect(
+ getByText(
+ 'Unable to install the plugin. Refresh the page and try again.'
+ )
+ ).toBeInTheDocument()
+ );
+ expect(
+ getByRole( 'button', { name: actionButtonLabel } )
+ ).not.toHaveClass( 'is-busy' );
+ } );
+
+ it( 'should clear busy state and show error if activatePlugins throws an error', async () => {
+ const actionButtonLabel = 'Create shipping label';
+
+ const { getByRole, getByText } = render(
+ <ShippingBanner
+ isJetpackConnected={ true }
+ activatePlugins={ jest
+ .fn()
+ .mockRejectedValue( new Error( 'Activation failed' ) ) }
+ activePlugins={ [ 'jetpack' ] }
+ installPlugins={ jest.fn().mockReturnValue( {
+ success: true,
+ } ) }
+ itemsCount={ 1 }
+ isRequesting={ false }
+ orderId={ 1 }
+ isWcstCompatible={ true }
+ actionButtonLabel={ actionButtonLabel }
+ />
+ );
+
+ userEvent.click( getByRole( 'button', { name: actionButtonLabel } ) );
+
+ await waitFor( () =>
+ expect(
+ getByText(
+ 'Unable to activate the plugin. Refresh the page and try again.'
+ )
+ ).toBeInTheDocument()
+ );
+ expect(
+ getByRole( 'button', { name: actionButtonLabel } )
+ ).not.toHaveClass( 'is-busy' );
+ } );
+
+ it( 'should clear busy state and show error if installPlugins returns null or undefined', async () => {
+ const actionButtonLabel = 'Create shipping label';
+
+ const { getByRole, getByText } = render(
+ <ShippingBanner
+ isJetpackConnected={ true }
+ activatePlugins={ jest.fn() }
+ activePlugins={ [ 'jetpack' ] }
+ installPlugins={ jest.fn().mockResolvedValue( undefined ) }
+ itemsCount={ 1 }
+ isRequesting={ false }
+ orderId={ 1 }
+ isWcstCompatible={ true }
+ actionButtonLabel={ actionButtonLabel }
+ />
+ );
+
+ userEvent.click( getByRole( 'button', { name: actionButtonLabel } ) );
+
+ await waitFor( () =>
+ expect(
+ getByText(
+ 'Unable to install the plugin. Refresh the page and try again.'
+ )
+ ).toBeInTheDocument()
+ );
+ expect(
+ getByRole( 'button', { name: actionButtonLabel } )
+ ).not.toHaveClass( 'is-busy' );
+ } );
+
+ it( 'should clear busy state and show error if activatePlugins returns null or undefined', async () => {
+ const actionButtonLabel = 'Create shipping label';
+
+ const { getByRole, getByText } = render(
+ <ShippingBanner
+ isJetpackConnected={ true }
+ activatePlugins={ jest.fn().mockResolvedValue( undefined ) }
+ activePlugins={ [ 'jetpack' ] }
+ installPlugins={ jest.fn().mockReturnValue( {
+ success: true,
+ } ) }
+ itemsCount={ 1 }
+ isRequesting={ false }
+ orderId={ 1 }
+ isWcstCompatible={ true }
+ actionButtonLabel={ actionButtonLabel }
+ />
+ );
+
+ userEvent.click( getByRole( 'button', { name: actionButtonLabel } ) );
+
+ await waitFor( () =>
+ expect(
+ getByText(
+ 'Unable to activate the plugin. Refresh the page and try again.'
+ )
+ ).toBeInTheDocument()
+ );
+ expect(
+ getByRole( 'button', { name: actionButtonLabel } )
+ ).not.toHaveClass( 'is-busy' );
+ } );
+
+ it( 'should not set busy state when isRequesting is true', () => {
+ const actionButtonLabel = 'Create shipping label';
+ const installPluginsMock = jest.fn();
+
+ const { getByRole } = render(
+ <ShippingBanner
+ isJetpackConnected={ true }
+ activatePlugins={ jest.fn() }
+ activePlugins={ [] }
+ installPlugins={ installPluginsMock }
+ itemsCount={ 1 }
+ isRequesting={ true }
+ orderId={ 1 }
+ isWcstCompatible={ true }
+ actionButtonLabel={ actionButtonLabel }
+ />
+ );
+
+ userEvent.click( getByRole( 'button', { name: actionButtonLabel } ) );
+
+ expect(
+ getByRole( 'button', { name: actionButtonLabel } )
+ ).not.toHaveClass( 'is-busy' );
+ expect( installPluginsMock ).not.toHaveBeenCalled();
+ } );
+
+ it( 'should reset wcsAssetsLoading to false when asset loading fails so retry attempts to load assets again', async () => {
+ const actionButtonLabel = 'Create shipping label';
+ getWcsLabelPurchaseConfigs.mockResolvedValue( {} );
+ getWcsAssets.mockResolvedValue( {
+ assets: {
+ wcshipping_create_label_script: '/path/to/fail.js',
+ wcshipping_create_label_style: '',
+ wcshipping_shipment_tracking_script:
+ '/path/to/fail-tracking.js',
+ wcshipping_shipment_tracking_style: '',
+ },
+ } );
+
+ // Simulate asset loading error
+ const loadWcsAssetsSpy = jest
+ .spyOn( ShippingBanner.prototype, 'loadWcsAssets' )
+ .mockRejectedValueOnce( new Error( 'Failed to load script' ) );
+
+ const { getByRole, getByText } = render(
+ <Fragment>
+ <div id="woocommerce-order-data" />
+ <div id="woocommerce-order-actions" />
+ <ShippingBanner
+ isJetpackConnected={ true }
+ activatePlugins={ jest.fn() }
+ activePlugins={ [ wcsPluginSlug, 'jetpack' ] }
+ installPlugins={ jest.fn() }
+ itemsCount={ 1 }
+ isRequesting={ false }
+ orderId={ 1 }
+ isWcstCompatible={ true }
+ actionButtonLabel={ actionButtonLabel }
+ />
+ </Fragment>
+ );
+
+ userEvent.click( getByRole( 'button', { name: actionButtonLabel } ) );
+
+ await waitFor( () =>
+ expect(
+ getByText(
+ 'Unable to set up the plugin. Refresh the page and try again.'
+ )
+ ).toBeInTheDocument()
+ );
+ expect(
+ getByRole( 'button', { name: actionButtonLabel } )
+ ).not.toHaveClass( 'is-busy' );
+
+ loadWcsAssetsSpy.mockRestore();
} );
} );
@@ -626,7 +905,7 @@ describe( 'If incompatible WCS&T is active', () => {
it( 'should install and activate but show an error notice when an incompatible version of WCS&T is installed', async () => {
const actionButtonLabel = 'Install WooCommerce Shipping';
- const { getByRole, findByText } = render(
+ const { getByRole } = render(
<Fragment>
<div id="woocommerce-order-data" />
<div id="woocommerce-order-actions" />
@@ -658,22 +937,11 @@ describe( 'If incompatible WCS&T is active', () => {
expect( acceptWcsTos ).not.toHaveBeenCalled();
} );
- const notice = await findByText( ( _, element ) => {
- const hasText = ( node ) =>
- node.textContent ===
- 'Please update the WooCommerce Shipping & Tax plugin to the latest version to ensure compatibility with WooCommerce Shipping.';
- const nodeHasText = hasText( element );
- const childrenDontHaveText = Array.from( element.children ).every(
- ( child ) => ! hasText( child )
- );
- return nodeHasText && childrenDontHaveText;
- } );
-
- await waitFor( () => expect( notice ).toBeInTheDocument() );
+ const reloadButton = await waitFor( () =>
+ getByRole( 'button', { name: 'Reload page' } )
+ );
+ userEvent.click( reloadButton );
- // Assert that the "update" link is present
- const updateLink = await findByText( /update/i );
- expect( updateLink ).toBeInTheDocument();
- expect( updateLink.tagName ).toBe( 'A' ); // Ensures it's a link
+ expect( window.location.reload ).toHaveBeenCalledWith( true );
} );
} );
diff --git a/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/style.scss b/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/style.scss
index 60f359098c9..6a1fb34cab1 100644
--- a/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/style.scss
+++ b/plugins/woocommerce/client/admin/client/wp-admin-scripts/print-shipping-label-banner/style.scss
@@ -63,6 +63,17 @@
}
}
}
+ .wc-admin-shipping-banner-install-info {
+ color: #007cba;
+ > .info-icon {
+ margin-right: 3px;
+ vertical-align: middle;
+ margin-top: -2px;
+ path {
+ fill: #007cba;
+ }
+ }
+ }
.components-external-link__icon {
display: none;
}