Commit 3e14ba7e61e for woocommerce

commit 3e14ba7e61ea99c3afd7708c94ced4a6864ca03b
Author: Adrian Moldovan <3854374+adimoldovan@users.noreply.github.com>
Date:   Mon Jun 22 16:40:44 2026 +0300

    test: downgrade restricted-coupon e2e to PHPUnit validation + wiring tests (#65897)

    Move restricted-coupon validation-rule coverage from Playwright E2E down to
    PHPUnit: add four cart-based is_coupon_valid rejection cases (below-minimum
    spend, non-included product, excluded product, disallowed email) and a
    tearDown that resets the in-memory cart/current-user globals.

    Collapse cart-checkout-restricted-coupons.spec.ts from 12 tests to 3 genuine
    E2E tests (rejection wiring, acceptance wiring, full-stack email-restricted
    order placement + per-user usage limit). This also closes the gap left by the
    old "excluded product/category" test, which never applied the excluded coupon.

    No production code changes.

    Fixes TESTOPS-185

diff --git a/plugins/woocommerce/changelog/testops-185-restricted-coupon-e2e-downgrade b/plugins/woocommerce/changelog/testops-185-restricted-coupon-e2e-downgrade
new file mode 100644
index 00000000000..66160019e07
--- /dev/null
+++ b/plugins/woocommerce/changelog/testops-185-restricted-coupon-e2e-downgrade
@@ -0,0 +1,3 @@
+Significance: patch
+Type: dev
+Comment: Move restricted-coupon validation-rule coverage from e2e to PHPUnit; reduce restricted-coupon cart/checkout e2e to wiring + order-placement tests; fix the excluded-coupon test that never applied its coupon.
diff --git a/plugins/woocommerce/tests/e2e-pw/tests/coupons/cart-checkout-restricted-coupons.spec.ts b/plugins/woocommerce/tests/e2e-pw/tests/coupons/cart-checkout-restricted-coupons.spec.ts
index 1f37190da3c..69f9a05de19 100644
--- a/plugins/woocommerce/tests/e2e-pw/tests/coupons/cart-checkout-restricted-coupons.spec.ts
+++ b/plugins/woocommerce/tests/e2e-pw/tests/coupons/cart-checkout-restricted-coupons.spec.ts
@@ -7,6 +7,7 @@ import {
 	getOrderIdFromUrl,
 	WC_API_PATH,
 } from '@woocommerce/e2e-utils-playwright';
+
 /**
  * Internal dependencies
  */
@@ -19,11 +20,8 @@ import {
 } from '../../utils/pages';

 const includedProductName = 'Included test product';
-const excludedProductName = 'Excluded test product';
 const includedCategoryName = 'Included Category';
-const excludedCategoryName = 'Excluded Category';

-// This applies a coupon and waits for the result to prevent flakiness.
 const applyCoupon = async ( page: Page, couponCode: string ) => {
 	const responsePromise = page.waitForResponse(
 		( response ) =>
@@ -37,72 +35,52 @@ const applyCoupon = async ( page: Page, couponCode: string ) => {

 const expandCouponForm = async ( page: Page ) => {
 	await page
-		.getByRole( 'button', {
-			name: 'Enter your coupon code',
-		} )
+		.getByRole( 'button', { name: 'Enter your coupon code' } )
 		.click();
-	// This is to wait for the expand animation to finish, it avoids flakiness.
-	await expect(
-		page.locator( 'form.woocommerce-form-coupon' )
-	).toHaveAttribute( 'style', '' );
+	await expect( page.getByPlaceholder( 'Coupon code' ) ).toBeVisible();
+};
+
+const fillBillingDetails = async ( page: Page, email: string ) => {
+	await page.getByLabel( 'First name' ).first().fill( 'Homer' );
+	await page.getByLabel( 'Last name' ).first().fill( 'Simpson' );
+	await page
+		.getByLabel( 'Street address' )
+		.first()
+		.fill( '123 Evergreen Terrace' );
+	await page.getByLabel( 'Town / City' ).first().fill( 'Springfield' );
+	await page.getByLabel( 'ZIP Code' ).first().fill( '55555' );
+	await page.getByLabel( 'Phone' ).first().fill( '555-555-5555' );
+	await page.getByLabel( 'Email address' ).first().fill( email );
 };

 test.describe(
 	'Cart & Checkout Restricted Coupons',
-	{
-		tag: [
-			tags.PAYMENTS,
-			tags.SERVICES,
-			tags.HPOS,
-			tags.COULD_BE_LOWER_LEVEL_TEST,
-		],
-	},
+	{ tag: [ tags.PAYMENTS, tags.SERVICES, tags.HPOS ] },
 	() => {
-		let firstProductId: number,
-			secondProductId: number,
-			firstCategoryId: number,
-			secondCategoryId: number,
-			shippingZoneId: number;
+		let firstProductId: number;
+		let firstCategoryId: number;
+		let shippingZoneId: number;
 		const couponBatchId: number[] = [];

 		test.beforeAll( async ( { restApi } ) => {
-			// Make sure the classic cart and checkout pages exist
 			await createClassicCartPage();
 			await createClassicCheckoutPage();

-			// make sure the store address is US
 			await restApi.post( `${ WC_API_PATH }/settings/general/batch`, {
 				update: [
-					{
-						id: 'woocommerce_store_address',
-						value: 'addr 1',
-					},
-					{
-						id: 'woocommerce_store_city',
-						value: 'San Francisco',
-					},
-					{
-						id: 'woocommerce_default_country',
-						value: 'US:CA',
-					},
-					{
-						id: 'woocommerce_store_postcode',
-						value: '94107',
-					},
+					{ id: 'woocommerce_store_address', value: 'addr 1' },
+					{ id: 'woocommerce_store_city', value: 'San Francisco' },
+					{ id: 'woocommerce_default_country', value: 'US:CA' },
+					{ id: 'woocommerce_store_postcode', value: '94107' },
 				],
 			} );
-			// make sure the currency is USD
 			await restApi.put(
 				`${ WC_API_PATH }/settings/general/woocommerce_currency`,
-				{
-					value: 'USD',
-				}
+				{ value: 'USD' }
 			);
-			// enable COD
 			await restApi.put( `${ WC_API_PATH }/payment_gateways/cod`, {
 				enabled: true,
 			} );
-			// add a shipping zone and method
 			await restApi
 				.post( `${ WC_API_PATH }/shipping/zones`, {
 					name: 'Free Shipping',
@@ -112,11 +90,8 @@ test.describe(
 				} );
 			await restApi.post(
 				`${ WC_API_PATH }/shipping/zones/${ shippingZoneId }/methods`,
-				{
-					method_id: 'free_shipping',
-				}
+				{ method_id: 'free_shipping' }
 			);
-			// add categories
 			await restApi
 				.post( `${ WC_API_PATH }/products/categories`, {
 					name: includedCategoryName,
@@ -124,14 +99,6 @@ test.describe(
 				.then( ( response: { data: { id: number } } ) => {
 					firstCategoryId = response.data.id;
 				} );
-			await restApi
-				.post( `${ WC_API_PATH }/products/categories`, {
-					name: excludedCategoryName,
-				} )
-				.then( ( response: { data: { id: number } } ) => {
-					secondCategoryId = response.data.id;
-				} );
-			// add product
 			await restApi
 				.post( `${ WC_API_PATH }/products`, {
 					name: includedProductName,
@@ -142,134 +109,71 @@ test.describe(
 				.then( ( response: { data: { id: number } } ) => {
 					firstProductId = response.data.id;
 				} );
-			await restApi
-				.post( `${ WC_API_PATH }/products`, {
-					name: excludedProductName,
-					type: 'simple',
-					regular_price: '20.00',
-					sale_price: '15.00',
-					categories: [ { id: secondCategoryId } ],
-				} )
-				.then( ( response: { data: { id: number } } ) => {
-					secondProductId = response.data.id;
-				} );

-			const restrictedCoupons = [
+			const residualCoupons = [
 				{
 					code: 'expired-coupon',
 					discount_type: 'fixed_cart',
 					amount: '10.00',
-					description:
-						'This coupon has expired and should not be usable by anyone.',
 					date_expires: '2020-01-01T00:00:00',
 				},
-				{
-					code: 'min-max-spend-individual',
-					discount_type: 'fixed_cart',
-					amount: '20.00',
-					description:
-						'This coupon requires an order amount between 50 and 200 dollars. It can only be used by itself.',
-					minimum_amount: '50.00',
-					maximum_amount: '200.00',
-					individual_use: true,
-				},
-				{
-					code: 'no-sale-use-limit',
-					discount_type: 'fixed_cart',
-					amount: '15.00',
-					description:
-						'This coupon can only be used twice, and only on items that are not on sale.',
-					exclude_sale_items: true,
-					usage_limit: 2,
-				},
 				{
 					code: 'product-and-category-included',
 					discount_type: 'fixed_cart',
 					amount: '10.00',
-					description:
-						'This coupon can only be used for the specific products and categories.',
 					product_ids: [ firstProductId ],
 					product_categories: [ firstCategoryId ],
 				},
-				{
-					code: 'product-and-category-excluded',
-					discount_type: 'fixed_cart',
-					amount: '20.00',
-					description:
-						'This coupon can not be used for specific products and categories.',
-					excluded_product_ids: [ secondProductId ],
-					excluded_product_categories: [ secondCategoryId ],
-				},
 				{
 					code: 'email-restricted',
 					discount_type: 'fixed_cart',
 					amount: '25.00',
-					description:
-						'This coupon can only be used once by a specified user (email).',
 					email_restrictions: [ 'homer@example.com' ],
 					usage_limit_per_user: 1,
 				},
 			];
-
-			// add coupons
 			await restApi
 				.post( `${ WC_API_PATH }/coupons/batch`, {
-					create: restrictedCoupons,
+					create: residualCoupons,
 				} )
 				.then( ( response: { data: { create: { id: number }[] } } ) => {
-					for ( let i = 0; i < response.data.create.length; i++ ) {
-						couponBatchId.push( response.data.create[ i ].id );
+					for ( const created of response.data.create ) {
+						couponBatchId.push( created.id );
 					}
 				} );
 		} );

 		test.beforeEach( async ( { context } ) => {
-			// Shopping cart is very sensitive to cookies, so be explicit
+			// Shopping cart is very sensitive to cookies, so be explicit.
 			await context.clearCookies();
 		} );

 		test.afterAll( async ( { restApi } ) => {
 			await restApi.delete(
 				`${ WC_API_PATH }/products/${ firstProductId }`,
-				{
-					force: true,
-				}
-			);
-			await restApi.delete(
-				`${ WC_API_PATH }/products/${ secondProductId }`,
-				{
-					force: true,
-				}
+				{ force: true }
 			);
 			await restApi.delete(
 				`${ WC_API_PATH }/products/categories/${ firstCategoryId }`,
-				{
-					force: true,
-				}
-			);
-			await restApi.delete(
-				`${ WC_API_PATH }/products/categories/${ secondCategoryId }`,
-				{
-					force: true,
-				}
+				{ force: true }
 			);
 			await restApi.post( `${ WC_API_PATH }/coupons/batch`, {
 				delete: [ ...couponBatchId ],
 			} );
-
 			await restApi.put( `${ WC_API_PATH }/payment_gateways/cod`, {
 				enabled: false,
 			} );
 			await restApi.delete(
 				`${ WC_API_PATH }/shipping/zones/${ shippingZoneId }`,
-				{
-					force: true,
-				}
+				{ force: true }
 			);
 		} );

-		test( 'expired coupon cannot be used', async ( { page, context } ) => {
-			await test.step( 'Load cart page and try expired coupon usage', async () => {
+		test( 'rejected coupon surfaces its error in cart and checkout', async ( {
+			page,
+			context,
+		} ) => {
+			await test.step( 'cart', async () => {
 				await addAProductToCart( page, firstProductId );
 				await page.goto( CLASSIC_CART_PAGE.slug );
 				await applyCoupon( page, 'expired-coupon' );
@@ -280,7 +184,7 @@ test.describe(

 			await context.clearCookies();

-			await test.step( 'Load checkout page and try expired coupon usage', async () => {
+			await test.step( 'checkout', async () => {
 				await addAProductToCart( page, firstProductId );
 				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
 				await expandCouponForm( page );
@@ -291,288 +195,14 @@ test.describe(
 			} );
 		} );

-		test( 'coupon requiring min and max amounts and can only be used alone can only be used within limits', async ( {
+		test( 'accepted coupon surfaces success in cart and checkout', async ( {
 			page,
 			context,
 		} ) => {
-			await test.step( 'Load cart page and try limited coupon usage', async () => {
+			await test.step( 'cart', async () => {
 				await addAProductToCart( page, firstProductId );
 				await page.goto( CLASSIC_CART_PAGE.slug );
-				await applyCoupon( page, 'min-max-spend-individual' );
-				// failed because we need to have at least $50 in cart (single product is only $20)
-				await expect(
-					page
-						.getByRole( 'alert' )
-						.getByText(
-							'The minimum spend for coupon "min-max-spend-individual" is $50.00.'
-						)
-				).toBeVisible();
-
-				// add a couple more in order to hit minimum spend
-				await addAProductToCart( page, firstProductId, 2 );
-
-				// passed because we're between 50 and 200 dollars
-				await page.goto( CLASSIC_CART_PAGE.slug );
-				await applyCoupon( page, 'min-max-spend-individual' );
-				await expect(
-					page.getByText( 'Coupon code applied successfully.' )
-				).toBeVisible();
-
-				// fail because the min-max coupon can only be used by itself
-				await page.goto( CLASSIC_CART_PAGE.slug );
-				await applyCoupon( page, 'no-sale-use-limit' );
-				await expect(
-					page.getByText(
-						'Sorry, coupon "min-max-spend-individual" has already been applied and cannot be used in conjunction with other coupons.'
-					)
-				).toBeVisible();
-			} );
-
-			await context.clearCookies();
-
-			await test.step( 'Load checkout page and try limited coupon usage', async () => {
-				await addAProductToCart( page, firstProductId );
-
-				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-				await expandCouponForm( page );
-				await applyCoupon( page, 'min-max-spend-individual' );
-				// failed because we need to have at least $50 in cart (single product is only $20)
-				await expect(
-					page.getByText(
-						'The minimum spend for coupon "min-max-spend-individual" is $50.00.'
-					)
-				).toBeVisible();
-
-				// add a couple more in order to hit minimum spend
-				await addAProductToCart( page, firstProductId, 2 );
-
-				// passed because we're between 50 and 200 dollars
-				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-				await expandCouponForm( page );
-				await applyCoupon( page, 'min-max-spend-individual' );
-				await expect(
-					page.getByText( 'Coupon code applied successfully.' )
-				).toBeVisible();
-
-				// fail because the min-max coupon can only be used by itself
-				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-				await expandCouponForm( page );
-				await applyCoupon( page, 'no-sale-use-limit' );
-				await expect(
-					page.getByText(
-						'Sorry, coupon "min-max-spend-individual" has already been applied and cannot be used in conjunction with other coupons.'
-					)
-				).toBeVisible();
-			} );
-		} );
-
-		test( 'coupon cannot be used on sale item', async ( {
-			page,
-			context,
-		} ) => {
-			await test.step( 'Load cart page and try coupon usage on sale item', async () => {
-				await addAProductToCart( page, secondProductId );
-				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-				await expandCouponForm( page );
-				await applyCoupon( page, 'no-sale-use-limit' );
-				// failed because this product is on sale.
-				await expect(
-					page.getByText(
-						'Sorry, coupon "no-sale-use-limit" is not valid for sale items.'
-					)
-				).toBeVisible();
-			} );
-
-			await context.clearCookies();
-
-			await test.step( 'Load checkout page and try coupon usage on sale item', async () => {
-				await addAProductToCart( page, secondProductId );
-				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-				await expandCouponForm( page );
-				await applyCoupon( page, 'no-sale-use-limit' );
-				// failed because this product is on sale
-				await expect(
-					page.getByText(
-						'Sorry, coupon "no-sale-use-limit" is not valid for sale items.'
-					)
-				).toBeVisible();
-			} );
-		} );
-
-		test( 'coupon can only be used twice', async ( {
-			page,
-			context,
-			restApi,
-		} ) => {
-			const orderIds: number[] = [];
-
-			// create 2 orders using the limited coupon
-			for ( let i = 0; i < 2; i++ ) {
-				await restApi
-					.post( `${ WC_API_PATH }/orders`, {
-						status: 'completed',
-						billing: {
-							first_name: 'Marge',
-							last_name: 'Simpson',
-							email: 'marge.simpson@example.org',
-						},
-						line_items: [
-							{
-								product_id: firstProductId,
-								quantity: 5,
-							},
-						],
-						coupon_lines: [
-							{
-								code: 'no-sale-use-limit',
-							},
-						],
-					} )
-					.then( ( response: { data: { id: number } } ) => {
-						orderIds.push( response.data.id );
-					} );
-			}
-
-			await test.step( 'Load cart page and try over limit coupon usage', async () => {
-				await addAProductToCart( page, firstProductId );
-				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-				await expandCouponForm( page );
-				await applyCoupon( page, 'no-sale-use-limit' );
-				// failed because this coupon code has been used too much
-				await expect(
-					page.getByText(
-						'Usage limit for coupon "no-sale-use-limit" has been reached. Please try again after some time, or contact us for help.'
-					)
-				).toBeVisible();
-			} );
-
-			await context.clearCookies();
-
-			await test.step( 'Load checkout page and try over limit coupon usage', async () => {
-				await addAProductToCart( page, firstProductId );
-				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-				await expandCouponForm( page );
-				await applyCoupon( page, 'no-sale-use-limit' );
-				// failed because this coupon code has been used too much
-				await expect(
-					page.getByText(
-						'Usage limit for coupon "no-sale-use-limit" has been reached. Please try again after some time, or contact us for help.'
-					)
-				).toBeVisible();
-			} );
-
-			// clean up the orders
-			await restApi.post( `${ WC_API_PATH }/orders/batch`, {
-				delete: [ ...orderIds ],
-			} );
-		} );
-
-		test( 'coupon cannot be used on certain products/categories (included product/category)', async ( {
-			page,
-			context,
-		} ) => {
-			await test.step( 'Load cart page and try included certain items coupon usage', async () => {
-				await addAProductToCart( page, secondProductId );
-				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-				await expandCouponForm( page );
-				await applyCoupon( page, 'product-and-category-included' );
-				// failed because this product is not included for coupon
-				await expect(
-					page.getByText(
-						'Sorry, coupon "product-and-category-included" is not applicable to selected products.'
-					)
-				).toBeVisible();
-			} );
-
-			await context.clearCookies();
-
-			await test.step( 'Load checkout page and try included certain items coupon usage', async () => {
-				await addAProductToCart( page, secondProductId );
-				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-				await expandCouponForm( page );
-				await applyCoupon( page, 'product-and-category-included' );
-				// failed because this product is not included for coupon
-				await expect(
-					page.getByText(
-						'Sorry, coupon "product-and-category-included" is not applicable to selected products.'
-					)
-				).toBeVisible();
-			} );
-		} );
-
-		test( 'coupon can be used on certain products/categories', async ( {
-			page,
-			context,
-		} ) => {
-			await test.step( 'Load cart page and try on certain products coupon usage', async () => {
-				await addAProductToCart( page, firstProductId );
-				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-				await expandCouponForm( page );
-				await applyCoupon( page, 'product-and-category-included' );
-				// succeeded
-				await expect(
-					page.getByText( 'Coupon code applied successfully.' )
-				).toBeVisible();
-			} );
-
-			await context.clearCookies();
-
-			await test.step( 'Load checkout page and try on certain products coupon usage', async () => {
-				await addAProductToCart( page, firstProductId );
-
-				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-				await expandCouponForm( page );
-				await applyCoupon( page, 'product-and-category-included' );
-				// succeeded
-				await expect(
-					page.getByText( 'Coupon code applied successfully.' )
-				).toBeVisible();
-			} );
-		} );
-
-		test( 'coupon cannot be used on specific products/categories (excluded product/category)', async ( {
-			page,
-			context,
-		} ) => {
-			await test.step( 'Load cart page and try excluded items coupon usage', async () => {
-				await addAProductToCart( page, secondProductId );
-				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-				await expandCouponForm( page );
 				await applyCoupon( page, 'product-and-category-included' );
-				// failed because this product is excluded from coupon
-				await expect(
-					page.getByText(
-						'Sorry, coupon "product-and-category-included" is not applicable to selected products.'
-					)
-				).toBeVisible();
-			} );
-
-			await context.clearCookies();
-
-			await test.step( 'Load checkout page and try excluded items coupon usage', async () => {
-				await addAProductToCart( page, secondProductId );
-				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-				await expandCouponForm( page );
-				await applyCoupon( page, 'product-and-category-included' );
-				// failed because this product is excluded from coupon
-				await expect(
-					page.getByText(
-						'Sorry, coupon "product-and-category-included" is not applicable to selected products.'
-					)
-				).toBeVisible();
-			} );
-		} );
-
-		test( 'coupon can be used on other products/categories', async ( {
-			page,
-			context,
-		} ) => {
-			await test.step( 'Load cart page and try coupon usage on other items', async () => {
-				await addAProductToCart( page, firstProductId );
-				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-				await expandCouponForm( page );
-				await applyCoupon( page, 'product-and-category-included' );
-				// succeeded
 				await expect(
 					page.getByText( 'Coupon code applied successfully.' )
 				).toBeVisible();
@@ -580,91 +210,24 @@ test.describe(

 			await context.clearCookies();

-			await test.step( 'Load checkout page and try coupon usage on other items', async () => {
+			await test.step( 'checkout', async () => {
 				await addAProductToCart( page, firstProductId );
-
 				await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
 				await expandCouponForm( page );
 				await applyCoupon( page, 'product-and-category-included' );
-				// succeeded
 				await expect(
 					page.getByText( 'Coupon code applied successfully.' )
 				).toBeVisible();
 			} );
 		} );

-		test( 'coupon cannot be used by any customer on cart (email restricted)', async ( {
-			page,
-		} ) => {
-			await addAProductToCart( page, firstProductId );
-			await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-			await expandCouponForm( page );
-			await applyCoupon( page, 'email-restricted' );
-			await expect(
-				page.getByText(
-					'Please enter a valid email to use coupon code "email-restricted".'
-				)
-			).toBeVisible();
-		} );
-
-		test( 'coupon cannot be used by any customer on checkout (email restricted)', async ( {
-			page,
-		} ) => {
-			await addAProductToCart( page, firstProductId );
-
-			await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-
-			await page.getByLabel( 'First name' ).first().fill( 'Marge' );
-			await page.getByLabel( 'Last name' ).first().fill( 'Simpson' );
-			await page
-				.getByLabel( 'Street address' )
-				.first()
-				.fill( '123 Evergreen Terrace' );
-			await page
-				.getByLabel( 'Town / City' )
-				.first()
-				.fill( 'Springfield' );
-			await page.getByLabel( 'ZIP Code' ).first().fill( '55555' );
-			await page.getByLabel( 'Phone' ).first().fill( '555-555-5555' );
-			await page
-				.getByLabel( 'Email address' )
-				.first()
-				.fill( 'marge.simpson@example.org' );
-
-			await expandCouponForm( page );
-			await applyCoupon( page, 'email-restricted' );
-			await expect(
-				page.getByText(
-					'Please enter a valid email to use coupon code "email-restricted".'
-				)
-			).toBeVisible();
-		} );
-
-		test( 'coupon can be used by the right customer (email restricted) but only once', async ( {
+		test( 'email-restricted coupon can be used by the right customer but only once', async ( {
 			page,
 			restApi,
 		} ) => {
 			await addAProductToCart( page, firstProductId );
-
 			await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-
-			await page.getByLabel( 'First name' ).first().fill( 'Homer' );
-			await page.getByLabel( 'Last name' ).first().fill( 'Simpson' );
-			await page
-				.getByLabel( 'Street address' )
-				.first()
-				.fill( '123 Evergreen Terrace' );
-			await page
-				.getByLabel( 'Town / City' )
-				.first()
-				.fill( 'Springfield' );
-			await page.getByLabel( 'ZIP Code' ).first().fill( '55555' );
-			await page.getByLabel( 'Phone' ).first().fill( '555-555-5555' );
-			await page
-				.getByLabel( 'Email address' )
-				.first()
-				.fill( 'homer@example.com' );
-
+			await fillBillingDetails( page, 'homer@example.com' );
 			await expandCouponForm( page );
 			await applyCoupon( page, 'email-restricted' );
 			await expect(
@@ -672,34 +235,14 @@ test.describe(
 			).toBeVisible();

 			await page.getByRole( 'button', { name: 'Place order' } ).click();
-
 			await expect(
 				page.getByText( 'Your order has been received' )
 			).toBeVisible();
 			const newOrderId = getOrderIdFromUrl( page );

-			// try to order a second time, but should get an error
 			await addAProductToCart( page, firstProductId );
-
 			await page.goto( CLASSIC_CHECKOUT_PAGE.slug );
-
-			await page.getByLabel( 'First name' ).first().fill( 'Homer' );
-			await page.getByLabel( 'Last name' ).first().fill( 'Simpson' );
-			await page
-				.getByLabel( 'Street address' )
-				.first()
-				.fill( '123 Evergreen Terrace' );
-			await page
-				.getByLabel( 'Town / City' )
-				.first()
-				.fill( 'Springfield' );
-			await page.getByLabel( 'ZIP Code' ).first().fill( '55555' );
-			await page.getByLabel( 'Phone' ).first().fill( '555-555-5555' );
-			await page
-				.getByLabel( 'Email address' )
-				.first()
-				.fill( 'homer@example.com' );
-
+			await fillBillingDetails( page, 'homer@example.com' );
 			await expandCouponForm( page );
 			await applyCoupon( page, 'email-restricted' );
 			await expect(
@@ -707,14 +250,12 @@ test.describe(
 			).toBeVisible();

 			await page.getByRole( 'button', { name: 'Place order' } ).click();
-
 			await expect(
 				page.getByText(
 					'Usage limit for coupon "email-restricted" has been reached.'
 				)
 			).toBeVisible();

-			// clean up the order we just made
 			await restApi.delete( `${ WC_API_PATH }/orders/${ newOrderId }`, {
 				force: true,
 			} );
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-discounts-tests.php b/plugins/woocommerce/tests/php/includes/class-wc-discounts-tests.php
index cfc84129413..ccbd8141b10 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-discounts-tests.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-discounts-tests.php
@@ -12,6 +12,18 @@ use Automattic\WooCommerce\Enums\OrderStatus;
   */
 class WC_Discounts_Tests extends WC_Unit_Test_Case {

+	/**
+	 * Tear down test fixtures.
+	 *
+	 * The cart and current user are in-memory globals that the per-test DB transaction
+	 * does not roll back, so reset them explicitly to avoid leaking state into other tests.
+	 */
+	public function tearDown(): void {
+		WC()->cart->empty_cart();
+		wp_set_current_user( 0 );
+		parent::tearDown();
+	}
+
 	/**
 	 * Helper method to create limited coupon.
 	 */
@@ -114,4 +126,117 @@ class WC_Discounts_Tests extends WC_Unit_Test_Case {
 		$this->assertInstanceOf( WP_Error::class, $result, 'Once trashed, the coupon is no longer valid.' );
 		$this->assertEquals( 'invalid_coupon', $result->get_error_code(), 'We receive an appropriate WP_Error.' );
 	}
+
+	/**
+	 * @testdox is_coupon_valid rejects a coupon when the cart subtotal is below its minimum spend.
+	 */
+	public function test_is_coupon_valid_rejects_below_minimum_spend() {
+		update_option( 'woocommerce_calc_taxes', 'no' );
+		WC()->cart->empty_cart();
+
+		$product = WC_Helper_Product::create_simple_product( true, array( 'regular_price' => 20 ) );
+		$coupon  = new WC_Coupon();
+		$coupon->set_props(
+			array(
+				'discount_type'  => 'fixed_cart',
+				'amount'         => 10,
+				'minimum_amount' => 50,
+			)
+		);
+		$coupon->save();
+
+		// $20 < $50 minimum.
+		WC()->cart->add_to_cart( $product->get_id(), 1 );
+		$discounts = new WC_Discounts( WC()->cart );
+
+		$result = $discounts->is_coupon_valid( $coupon );
+		$this->assertWPError( $result, 'coupon below minimum spend should be invalid' );
+		$this->assertEquals( $coupon->get_coupon_error( WC_Coupon::E_WC_COUPON_MIN_SPEND_LIMIT_NOT_MET ), $result->get_error_message() );
+	}
+
+	/**
+	 * @testdox is_coupon_valid rejects a product/category-restricted coupon when the cart has none of its products.
+	 */
+	public function test_is_coupon_valid_rejects_non_included_product() {
+		update_option( 'woocommerce_calc_taxes', 'no' );
+		WC()->cart->empty_cart();
+
+		$included = WC_Helper_Product::create_simple_product( true, array( 'regular_price' => 20 ) );
+		$other    = WC_Helper_Product::create_simple_product( true, array( 'regular_price' => 20 ) );
+		$coupon   = new WC_Coupon();
+		$coupon->set_props(
+			array(
+				'code'          => 'included-only',
+				'discount_type' => 'fixed_cart',
+				'amount'        => 10,
+				'product_ids'   => array( $included->get_id() ),
+			)
+		);
+		$coupon->save();
+
+		// Not the included product.
+		WC()->cart->add_to_cart( $other->get_id(), 1 );
+		$discounts = new WC_Discounts( WC()->cart );
+
+		$result = $discounts->is_coupon_valid( $coupon );
+		$this->assertWPError( $result, 'coupon should not apply to non-included products' );
+		// The product_ids rule throws its own inline message (class-wc-discounts.php) rather than
+		// routing through WC_Coupon::get_coupon_error(), so assert that stable phrase directly.
+		$this->assertStringContainsString( 'is not applicable to selected products.', $result->get_error_message() );
+	}
+
+	/**
+	 * @testdox is_coupon_valid rejects a coupon when the cart contains one of its excluded products.
+	 *
+	 * Closes the gap left by the e2e "excluded product/category" test, which never applied the excluded coupon.
+	 */
+	public function test_is_coupon_valid_rejects_excluded_product() {
+		update_option( 'woocommerce_calc_taxes', 'no' );
+		WC()->cart->empty_cart();
+
+		$excluded = WC_Helper_Product::create_simple_product( true, array( 'regular_price' => 20 ) );
+		$coupon   = new WC_Coupon();
+		$coupon->set_props(
+			array(
+				'discount_type'        => 'fixed_cart',
+				'amount'               => 20,
+				'excluded_product_ids' => array( $excluded->get_id() ),
+			)
+		);
+		$coupon->save();
+
+		WC()->cart->add_to_cart( $excluded->get_id(), 1 );
+		$discounts = new WC_Discounts( WC()->cart );
+
+		$result = $discounts->is_coupon_valid( $coupon );
+		$this->assertWPError( $result, 'coupon should be rejected when an excluded product is in the cart' );
+		$this->assertEquals( $coupon->get_coupon_error( WC_Coupon::E_WC_COUPON_EXCLUDED_PRODUCTS ), $result->get_error_message() );
+	}
+
+	/**
+	 * @testdox is_coupon_valid rejects an email-restricted coupon for a non-matching customer.
+	 */
+	public function test_is_coupon_valid_rejects_disallowed_email() {
+		update_option( 'woocommerce_calc_taxes', 'no' );
+		WC()->cart->empty_cart();
+
+		$product = WC_Helper_Product::create_simple_product( true, array( 'regular_price' => 20 ) );
+		$coupon  = new WC_Coupon();
+		$coupon->set_props(
+			array(
+				'discount_type'      => 'fixed_cart',
+				'amount'             => 25,
+				'email_restrictions' => array( 'allowed@example.com' ),
+			)
+		);
+		$coupon->save();
+
+		wp_set_current_user( 0 );
+		WC()->cart->add_to_cart( $product->get_id(), 1 );
+		$discounts = new WC_Discounts( WC()->cart );
+
+		$result = $discounts->is_coupon_valid( $coupon );
+		$this->assertWPError( $result, 'email-restricted coupon should be invalid for a non-matching customer' );
+		$this->assertEquals( $coupon->get_coupon_error( WC_Coupon::E_WC_COUPON_NOT_YOURS_REMOVED ), $result->get_error_message() );
+	}
 }