/**
* External dependencies
*/
import { Fragment } from '@wordpress/element';
import { recordEvent } from '@woocommerce/tracks';
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
/**
* Internal dependencies
*/
import {
acceptWcsTos,
getWcsAssets,
getWcsLabelPurchaseConfigs,
} from '../../wcs-api.js';
import { ShippingBanner } from '../index.js';
jest.mock( '../../wcs-api.js' );
acceptWcsTos.mockReturnValue( Promise.resolve() );
jest.mock( '@woocommerce/tracks' );
const wcsPluginSlug = 'woocommerce-shipping';
const wcstPluginSlug = 'woocommerce-services';
describe( 'Tracking impression in shippingBanner', () => {
const expectedTrackingData = {
banner_name: 'wcadmin_install_wcs_prompt',
jetpack_connected: true,
jetpack_installed: true,
wcs_installed: false,
};
it( 'should record an event when user sees banner loaded', () => {
render(
);
expect( recordEvent ).toHaveBeenCalledTimes( 1 );
expect( recordEvent ).toHaveBeenCalledWith(
'banner_impression',
expectedTrackingData
);
} );
} );
describe( 'Tracking clicks in shippingBanner', () => {
const getExpectedTrackingData = ( element, wcsInstalled = true ) => {
return {
banner_name: 'wcadmin_install_wcs_prompt',
jetpack_connected: true,
jetpack_installed: true,
wcs_installed: wcsInstalled,
element,
};
};
it( 'should record an event when user clicks "Create shipping label"', async () => {
const actionButtonLabel = 'Create shipping label';
const { getByRole } = render(
);
userEvent.click( getByRole( 'button', { name: actionButtonLabel } ) );
await waitFor( () =>
expect( recordEvent ).toHaveBeenCalledWith(
'banner_element_clicked',
getExpectedTrackingData( 'shipping_banner_create_label', false )
)
);
} );
it( 'should record an event when user clicks "WooCommerce Shipping"', async () => {
// Render the banner without WCS being active.
const { getByRole } = render(
);
userEvent.click(
getByRole( 'link', { name: /WooCommerce Shipping/ } )
);
await waitFor( () =>
expect( recordEvent ).toHaveBeenCalledWith(
'banner_element_clicked',
getExpectedTrackingData(
'shipping_banner_woocommerce_service_link',
false
)
)
);
} );
it( 'should record an event when user clicks "x" to dismiss the banner', async () => {
const { getByRole } = render(
);
userEvent.click(
getByRole( 'button', { name: 'Close Print Label Banner.' } )
);
await waitFor( () =>
expect( recordEvent ).toHaveBeenCalledWith(
'banner_element_clicked',
getExpectedTrackingData( 'shipping_banner_dimiss', false )
)
);
} );
} );
describe( 'Create shipping label button', () => {
const installPlugins = jest.fn().mockReturnValue( {
success: true,
} );
const activatePlugins = jest.fn().mockReturnValue( {
success: true,
} );
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',
};
it( 'should install WooCommerce Shipping when button is clicked', async () => {
const actionButtonLabel = 'Create shipping label';
const { getByRole } = render(
);
userEvent.click(
getByRole( 'button', {
name: actionButtonLabel,
} )
);
await waitFor( () =>
expect( installPlugins ).toHaveBeenCalledWith( [
'woocommerce-shipping',
] )
);
} );
it( 'should activate WooCommerce Shipping when installation finishes', async () => {
const actionButtonLabel = 'Create shipping label';
const { getByRole } = render(
);
userEvent.click(
getByRole( 'button', {
name: actionButtonLabel,
} )
);
await waitFor( () =>
expect( activatePlugins ).toHaveBeenCalledWith( [
'woocommerce-shipping',
] )
);
} );
it( 'should perform a request to accept the TOS and get WCS assets to load', async () => {
getWcsLabelPurchaseConfigs.mockReturnValueOnce( Promise.resolve( {} ) );
getWcsAssets.mockReturnValueOnce( Promise.resolve( {} ) );
const actionButtonLabel = 'Create shipping label';
const { getByRole } = render(
);
userEvent.click(
getByRole( 'button', {
name: actionButtonLabel,
} )
);
await waitFor( () => expect( acceptWcsTos ).toHaveBeenCalled() );
expect( getWcsAssets ).toHaveBeenCalled();
} );
it( 'should load WCS assets when a path is provided', async () => {
const actionButtonLabel = 'Create shipping label';
getWcsLabelPurchaseConfigs.mockReturnValueOnce( Promise.resolve( {} ) );
const mockAssets = {
wcshipping_create_label_script: '/path/to/wcs.js',
wcshipping_create_label_style: '/path/to/wcs.css',
wcshipping_shipment_tracking_script:
'/wcshipping_shipment_tracking_script',
wcshipping_shipment_tracking_style:
'/wcshipping_shipment_tracking_style',
};
getWcsAssets.mockReturnValueOnce(
Promise.resolve( {
assets: mockAssets,
} )
);
const { getByRole } = render(
);
userEvent.click(
getByRole( 'button', {
name: actionButtonLabel,
} )
);
// Check that the metaboxes have been created.
await waitFor( () =>
expect(
getByRole( 'heading', { level: 2, name: 'Shipping Label' } )
).toBeInTheDocument()
);
expect(
getByRole( 'heading', { level: 2, name: 'Shipment Tracking' } )
).toBeInTheDocument();
// Check that the script and style elements have been created.
const allScriptSrcs = Array.from(
document.querySelectorAll( 'script' )
).map( ( script ) => script.src );
const allLinkHrefs = Array.from(
document.querySelectorAll( 'link' )
).map( ( link ) => link.href );
expect( allScriptSrcs ).toContain(
'http://localhost' + mockAssets.wcshipping_create_label_script
);
expect( allScriptSrcs ).toContain(
'http://localhost' + mockAssets.wcshipping_shipment_tracking_script
);
expect( allLinkHrefs ).toContain(
'http://localhost' + mockAssets.wcshipping_create_label_style
);
expect( allLinkHrefs ).toContain(
'http://localhost' + mockAssets.wcshipping_shipment_tracking_style
);
} );
it( 'should open WCS modal', async () => {
const actionButtonLabel = 'Create shipping label';
getWcsLabelPurchaseConfigs.mockReturnValueOnce( Promise.resolve( {} ) );
getWcsAssets.mockReturnValueOnce(
Promise.resolve( {
assets: {
// Easy to identify string in our hijacked setter function.
wcshipping_create_label_script:
'wcshipping_create_label_script',
wcshipping_shipment_tracking_script:
'wcshipping_create_label_script',
// Empty string to avoid creating a script tag we also have to hijack.
wcshipping_create_label_style: '',
wcshipping_shipment_tracking_style: '',
},
} )
);
// Force the script tag to trigger its onload().
// Adapted from https://stackoverflow.com/a/49204336.
// const scriptSrcProperty = window.HTMLScriptElement.prototype.src;
Object.defineProperty( window.HTMLScriptElement.prototype, 'src', {
set( src ) {
if (
[
'wcshipping_create_label_script',
'wcshipping_shipment_tracking_script',
].includes( src )
) {
setTimeout( () => {
this.onload();
}, 1 );
}
},
} );
const { getByRole } = render(
);
const openWcsModalSpy = jest.spyOn(
ShippingBanner.prototype,
'openWcsModal'
);
// Initiate the loading of WCS assets on first click.
userEvent.click(
getByRole( 'button', {
name: actionButtonLabel,
} )
);
await waitFor( () => {
expect(
document.getElementById( 'woocommerce-admin-print-label' )
).not.toBeVisible();
} );
expect( openWcsModalSpy ).toHaveBeenCalledTimes( 1 );
} );
} );
describe( 'In the process of installing, activating, loading assets for WooCommerce Service', () => {
it( 'should show a busy loading state on "Create shipping label" and should disable "Close Print Label Banner"', async () => {
const actionButtonLabel = 'Create shipping label';
const { getByRole } = render(
);
expect(
getByRole( 'button', { name: actionButtonLabel } )
).not.toHaveClass( 'is-busy' );
expect(
getByRole( 'button', { name: 'Close Print Label Banner.' } )
).toBeEnabled();
userEvent.click( getByRole( 'button', { name: actionButtonLabel } ) );
await waitFor( () =>
expect(
getByRole( 'button', { name: actionButtonLabel } )
).toHaveClass( 'is-busy' )
);
expect(
getByRole( 'button', { name: 'Close Print Label Banner.' } )
).toBeDisabled();
} );
} );
describe( 'Setup error message', () => {
it( 'should not show if there is no error (no interaction)', () => {
const { container } = render(
);
expect(
container.getElementsByClassName(
'wc-admin-shipping-banner-install-error'
)
).toHaveLength( 0 );
} );
it( 'should show if there is installation error', async () => {
const actionButtonLabel = 'Create shipping label';
const { getByRole, getByText } = render(
);
userEvent.click( getByRole( 'button', { name: actionButtonLabel } ) );
await waitFor( () =>
expect(
getByText(
'Unable to install the plugin. Refresh the page and try again.'
)
).toBeInTheDocument()
);
} );
it( 'should show if there is activation error', async () => {
const actionButtonLabel = 'Create shipping label';
const { getByRole, getByText } = render(
);
userEvent.click( getByRole( 'button', { name: actionButtonLabel } ) );
await waitFor( () =>
expect(
getByText(
'Unable to activate the plugin. Refresh the page and try again.'
)
).toBeInTheDocument()
);
} );
} );
describe( 'The message in the banner', () => {
const createShippingBannerWrapper = ( { activePlugins } ) =>
render(
);
const notActivatedMessage =
'By clicking "Create shipping label", WooCommerce Shipping(opens in a new tab) will be installed and you agree to its Terms of Service(opens in a new tab).';
it( 'should show install text "By clicking "Create shipping label"..." when first loaded.', () => {
const { container } = createShippingBannerWrapper( {
activePlugins: [],
} );
expect(
container.querySelector( '.wc-admin-shipping-banner-blob p' )
.textContent
).toBe( notActivatedMessage );
} );
it( 'should continue to show the initial message "By clicking "Create shipping label"..." after WooCommerce Service is installed successfully.', () => {
const { container, rerender } = createShippingBannerWrapper( {
activePlugins: [],
} );
rerender(
);
expect(
container.querySelector( '.wc-admin-shipping-banner-blob p' )
.textContent
).toBe( notActivatedMessage );
} );
} );
describe( 'If incompatible WCS&T is active', () => {
const installPlugins = jest.fn().mockReturnValue( {
success: true,
} );
const activatePlugins = jest.fn().mockReturnValue( {
success: true,
} );
beforeEach( () => {
acceptWcsTos.mockClear();
} );
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, getByText } = render(
);
userEvent.click( getByRole( 'button', { name: actionButtonLabel } ) );
await waitFor( () => {
expect( installPlugins ).toHaveBeenCalledWith( [ wcsPluginSlug ] );
} );
await waitFor( () => {
expect( activatePlugins ).toHaveBeenCalledWith( [ wcsPluginSlug ] );
} );
await waitFor( () => {
expect( acceptWcsTos ).not.toHaveBeenCalled();
} );
const notice = getByText( ( _, 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() );
// Assert that the "update" link is present
const updateLink = getByText( /update/i );
expect( updateLink ).toBeInTheDocument();
expect( updateLink.tagName ).toBe( 'A' ); // Ensures it's a link
} );
} );