diff --git a/plugins/woocommerce-blocks/.prettierignore b/plugins/woocommerce-blocks/.prettierignore
index 061c1d13a93..7e155376458 100644
--- a/plugins/woocommerce-blocks/.prettierignore
+++ b/plugins/woocommerce-blocks/.prettierignore
@@ -1,2 +1,3 @@
+*.json
*.scss
*.yml
diff --git a/plugins/woocommerce-blocks/package.json b/plugins/woocommerce-blocks/package.json
index c3174880ba6..aaa81faafc8 100644
--- a/plugins/woocommerce-blocks/package.json
+++ b/plugins/woocommerce-blocks/package.json
@@ -53,8 +53,8 @@
"lint:js:report": "npm run lint:js -- --output-file eslint_report.json --ext=js,ts,tsx --format json",
"lint:js-fix": "eslint assets/js --ext=js,jsx,ts,tsx --fix",
"lint:md:docs": "wp-scripts lint-md-docs",
- "lint:php": "composer run-script phpcs ./src",
- "lint:php-fix": "composer run-script phpcbf ./src",
+ "lint:php": "composer run-script phpcs ./src && composer run-script phpcs ./tests/mocks/woo-test-helper",
+ "lint:php-fix": "composer run-script phpcbf ./src && composer run-script phpcbf ./tests/mocks/woo-test-helper",
"package-plugin": "rimraf woocommerce-gutenberg-products-block.zip && ./bin/build-plugin-zip.sh",
"package-plugin:dev": "rimraf woocommerce-gutenberg-products-block.zip && ./bin/build-plugin-zip.sh -d",
"package-plugin:zip-only": "rimraf woocommerce-gutenberg-products-block.zip && ./bin/build-plugin-zip.sh -z",
diff --git a/plugins/woocommerce-blocks/tests/e2e/specs/backend/__fixtures__/cart.fixture.json b/plugins/woocommerce-blocks/tests/e2e/specs/backend/__fixtures__/cart.fixture.json
index 9196f9830ae..caaf0d003ec 100644
--- a/plugins/woocommerce-blocks/tests/e2e/specs/backend/__fixtures__/cart.fixture.json
+++ b/plugins/woocommerce-blocks/tests/e2e/specs/backend/__fixtures__/cart.fixture.json
@@ -1 +1 @@
-{"title":"Cart Block","pageContent":"\n
\n
\n\n\n\n
Your cart is currently empty!
\n\n\n\n
Browse store.
\n\n\n\n
\n\n\n\n
New in store
\n\n\n
\n"}
\ No newline at end of file
+{"title": "Cart Block","pageContent": "\n\n
\n
\n
\n\n\n\n
\n
You may be interested in…
\n\n\n\n
\n
\n
\n\n\n\n
\n
\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n
\n
\n\n\n\n
\n
\n\n\n\n
Your cart is currently empty!
\n\n\n\n
Browse store.
\n\n\n\n
\n\n\n\n
New in store
\n\n\n
\n
\n"}
diff --git a/plugins/woocommerce-blocks/tests/e2e/specs/backend/cart.test.js b/plugins/woocommerce-blocks/tests/e2e/specs/backend/cart.test.js
index f9f8f875dd4..647ed745e5a 100644
--- a/plugins/woocommerce-blocks/tests/e2e/specs/backend/cart.test.js
+++ b/plugins/woocommerce-blocks/tests/e2e/specs/backend/cart.test.js
@@ -43,6 +43,12 @@ const emptyCartBlock = {
class: '.wp-block-woocommerce-empty-cart-block',
};
+const crossSellsBlock = {
+ name: 'Cart Cross-Sells block',
+ slug: 'woocommerce/cart-cross-sells-products-block',
+ class: '.wp-block-woocommerce-cart-cross-sells-products-block',
+};
+
if ( process.env.WOOCOMMERCE_BLOCKS_PHASE < 2 ) {
// eslint-disable-next-line jest/no-focused-tests, jest/expect-expect
test.only( `skipping ${ block.name } tests`, () => {} );
@@ -63,6 +69,7 @@ describe( `${ block.name } Block`, () => {
it( 'renders without crashing', async () => {
await expect( page ).toRenderBlock( block );
await expect( page ).toRenderBlock( filledCartBlock );
+ await expect( page ).toRenderBlock( crossSellsBlock );
await expect( page ).toRenderBlock( emptyCartBlock );
} );
diff --git a/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/cart.test.js b/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/cart.test.js
index 5b1b4e9c19f..ba1d4a4b906 100644
--- a/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/cart.test.js
+++ b/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/cart.test.js
@@ -1,7 +1,12 @@
/**
* Internal dependencies
*/
-import { shopper, SIMPLE_VIRTUAL_PRODUCT_NAME } from '../../../../utils';
+import {
+ shopper,
+ SIMPLE_VIRTUAL_PRODUCT_NAME,
+ SIMPLE_PHYSICAL_PRODUCT_NAME,
+} from '../../../../utils';
+import { BASE_URL } from '../../../../utils/constants';
if ( process.env.WOOCOMMERCE_BLOCKS_PHASE < 2 ) {
// Skips all the tests if it's a WooCommerce Core process environment.
@@ -11,6 +16,9 @@ if ( process.env.WOOCOMMERCE_BLOCKS_PHASE < 2 ) {
describe( 'Shopper → Cart', () => {
beforeAll( async () => {
+ await page.goto( `${ BASE_URL }/?setup_cross_sells` );
+ // eslint-disable-next-line jest/no-standalone-expect
+ await expect( page ).toMatch( 'Cross-Sells products set up.' );
await shopper.block.emptyCart();
} );
@@ -97,6 +105,22 @@ describe( 'Shopper → Cart', () => {
await shopper.block.productIsInCart( SIMPLE_VIRTUAL_PRODUCT_NAME, 4 );
} );
+ it( 'User can see Cross-Sells products block', async () => {
+ await shopper.block.emptyCart();
+ await shopper.goToShop();
+ await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME );
+ await shopper.block.goToCart();
+ await expect( page ).toMatchElement(
+ '.wp-block-woocommerce-cart-cross-sells-block'
+ );
+ await shopper.block.addCrossSellsProductToCart();
+ // To avoid flakiness: Wait until the cart contains two entries.
+ await page.waitForSelector(
+ '.wp-block-woocommerce-cart-line-items-block tr:nth-child(2)'
+ );
+ await shopper.block.productIsInCart( '32GB USB Stick', 1 );
+ } );
+
it( 'User can proceed to checkout', async () => {
await shopper.goToShop();
await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME );
diff --git a/plugins/woocommerce-blocks/tests/mocks/woo-test-helper/woo-test-helper.php b/plugins/woocommerce-blocks/tests/mocks/woo-test-helper/woo-test-helper.php
index e19bcb48eba..4e689448df2 100644
--- a/plugins/woocommerce-blocks/tests/mocks/woo-test-helper/woo-test-helper.php
+++ b/plugins/woocommerce-blocks/tests/mocks/woo-test-helper/woo-test-helper.php
@@ -28,7 +28,7 @@ function woocommerce_setup_terms_and_privacy_page() {
exit( 'Terms & Privacy pages set up.' );
}
- // phpcs:disable WordPress.Security.NonceVerification.Recommended
+ // phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['teardown_terms_and_privacy'] ) ) {
unpublish_privacy_page();
delete_terms_page();
@@ -94,3 +94,32 @@ function delete_terms_page() {
$data = array( 'post_title' => 'Terms & Conditions' );
$wpdb->delete( $table, $data );
}
+
+/**
+ * Define URL endpoint for setting up cross-sells products.
+ */
+function woocommerce_setup_cross_sells_products() {
+ // phpcs:disable WordPress.Security.NonceVerification.Recommended
+ if ( isset( $_GET['setup_cross_sells'] ) ) {
+ setup_cross_sells();
+ exit( 'Cross-Sells products set up.' );
+ }
+}
+add_action( 'init', 'woocommerce_setup_cross_sells_products' );
+
+/**
+ * Set up Cross-Sells products.
+ */
+function setup_cross_sells() {
+ global $wpdb;
+
+ // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
+ $select = "SELECT * FROM {$wpdb->prefix}posts WHERE post_title = '128GB USB Stick' AND post_status = 'publish' AND post_type = 'product'";
+ $id_product = $wpdb->get_row( $select );
+
+ // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
+ $select = "SELECT * FROM {$wpdb->prefix}posts WHERE post_title = '32GB USB Stick' AND post_status = 'publish' AND post_type = 'product'";
+ $id_cross_sell = $wpdb->get_row( $select );
+
+ add_post_meta( $id_product->ID, '_crosssell_ids', $id_cross_sell->ID );
+}
diff --git a/plugins/woocommerce-blocks/tests/utils/shopper.js b/plugins/woocommerce-blocks/tests/utils/shopper.js
index 3e65238b5be..e952750492a 100644
--- a/plugins/woocommerce-blocks/tests/utils/shopper.js
+++ b/plugins/woocommerce-blocks/tests/utils/shopper.js
@@ -311,6 +311,15 @@ export const shopper = {
] );
},
+ addCrossSellsProductToCart: async () => {
+ await page.waitForSelector(
+ '.wc-block-components-product-add-to-cart-button'
+ );
+ expect( page ).toClick(
+ '.wc-block-components-product-add-to-cart-button'
+ );
+ },
+
selectAndVerifyShippingOption: async (
shippingName,
shippingPrice