Commit e557338793d for woocommerce

commit e557338793d40decd00ee9b58760a30ff4ca18c4
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date:   Thu Sep 24 18:00:59 2026 +0300

    Revert "[tests] Demote onboarding profiler and add-product coverage below E2E" (#69053)

    revert: Revert "[tests] Demote onboarding profiler and add-product coverage below E2E" (#68634)

    This reverts commit db0994e7f1b031fe4a63f19cd2d52d2f8efaa769.

    Rubik asked for the E2E migration's test changes in its areas to be
    reverted until the team can review them. #68634 is one of the 11 PRs on
    the revert list Rubik agreed on 2026-09-24.

    The revert brings back the browser tests the PR removed or cut down and
    removes the lower-layer tests it added in their place. Only test code
    and changelog entries change; nothing ships.

    Refs TESTOPS-288
    Refs #68634

    Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

diff --git a/plugins/woocommerce/changelog/testops-288-onboarding b/plugins/woocommerce/changelog/testops-288-onboarding
deleted file mode 100644
index 52980851752..00000000000
--- a/plugins/woocommerce/changelog/testops-288-onboarding
+++ /dev/null
@@ -1,4 +0,0 @@
-Significance: patch
-Type: dev
-Comment: Move onboarding profiler and add-product coverage below E2E; seven browser titles become three.
-
diff --git a/plugins/woocommerce/client/admin/client/core-profiler/pages/Plugins/test/Plugins.test.tsx b/plugins/woocommerce/client/admin/client/core-profiler/pages/Plugins/test/Plugins.test.tsx
index 0d23265d043..f4f9f41da21 100644
--- a/plugins/woocommerce/client/admin/client/core-profiler/pages/Plugins/test/Plugins.test.tsx
+++ b/plugins/woocommerce/client/admin/client/core-profiler/pages/Plugins/test/Plugins.test.tsx
@@ -1,8 +1,7 @@
 /**
  * External dependencies
  */
-import { render, screen, within } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
+import { render, screen, fireEvent } from '@testing-library/react';
 import { Extension } from '@woocommerce/data';

 /**
@@ -10,63 +9,53 @@ import { Extension } from '@woocommerce/data';
  */
 import { computePluginsSelection, joinWithAnd, Plugins } from '../Plugins';

-const getPluginCheckbox = ( name: string ) => {
-	const card = screen
-		.getByRole( 'heading', { level: 3, name } )
-		.closest( '.woocommerce-profiler-plugins-plugin-card' );
-
-	expect( card ).not.toBeNull();
-
-	return within( card! ).getByRole( 'checkbox' );
-};
-
 describe( 'Plugins Component', () => {
 	const mockSendEvent = jest.fn();
 	const mockContext = {
 		pluginsAvailable: [
 			{
-				slug: 'woocommerce-payments',
-				name: 'WooPayments',
-				label: 'WooPayments',
+				slug: 'plugin1',
+				name: 'Plugin 1',
+				label: 'Plugin 1',
 				is_activated: false,
 				description: '',
-				key: 'woocommerce-payments',
+				key: 'plugin1',
 				image_url: '',
 				manage_url: '',
 				is_built_by_wc: false,
 				is_visible: true,
 			},
 			{
-				slug: 'google-listings-and-ads',
-				name: 'Google for WooCommerce',
-				label: 'Google for WooCommerce',
-				is_activated: false,
+				slug: 'plugin2',
+				name: 'Plugin 2',
+				label: 'Plugin 2',
+				is_activated: true,
 				description: '',
-				key: 'google-listings-and-ads',
+				key: 'plugin2',
 				image_url: '',
 				manage_url: '',
 				is_built_by_wc: false,
 				is_visible: true,
 			},
 			{
-				slug: 'jetpack',
-				name: 'Jetpack',
-				label: 'Jetpack',
-				is_activated: true,
+				slug: 'plugin3',
+				name: 'Plugin 3',
+				label: 'Plugin 3',
+				is_activated: false,
 				description: '',
-				key: 'jetpack',
+				key: 'plugin3',
 				image_url: '',
 				manage_url: '',
 				is_built_by_wc: false,
 				is_visible: true,
 			},
 			{
-				slug: 'mailpoet',
-				name: 'MailPoet',
-				label: 'MailPoet',
+				slug: 'plugin4',
+				name: 'Plugin 4',
+				label: 'Plugin 4',
 				is_activated: false,
 				description: '',
-				key: 'mailpoet:alt',
+				key: 'plugin4',
 				image_url: '',
 				manage_url: '',
 				is_built_by_wc: false,
@@ -77,9 +66,6 @@ describe( 'Plugins Component', () => {
 		pluginsInstallationErrors: [],
 	};
 	const navigationProgress = 80;
-	beforeEach( () => {
-		mockSendEvent.mockClear();
-	} );

 	it( 'renders correctly', () => {
 		render(
@@ -94,17 +80,13 @@ describe( 'Plugins Component', () => {
 				/No commitment required – you can remove them at any time/
 			)
 		).toBeInTheDocument();
-		expect(
-			screen.getByRole( 'heading', { level: 3, name: 'WooPayments' } )
-		).toBeInTheDocument();
-		expect(
-			screen.getByText( 'Google for WooCommerce' )
-		).toBeInTheDocument();
-		expect( screen.getByText( 'Jetpack' ) ).toBeInTheDocument();
-		expect( screen.getByText( 'MailPoet' ) ).toBeInTheDocument();
+		expect( screen.getByText( 'Plugin 1' ) ).toBeInTheDocument();
+		expect( screen.getByText( 'Plugin 2' ) ).toBeInTheDocument();
+		expect( screen.getByText( 'Plugin 3' ) ).toBeInTheDocument();
+		expect( screen.getByText( 'Plugin 4' ) ).toBeInTheDocument();
 	} );

-	it( 'selects each default inactive recommendation', () => {
+	it( 'handles plugin selection', () => {
 		render(
 			<Plugins
 				context={ mockContext }
@@ -112,52 +94,83 @@ describe( 'Plugins Component', () => {
 				navigationProgress={ navigationProgress }
 			/>
 		);
+		const checkboxLabel = screen.getByText( 'Plugin 1' );
+		fireEvent.click( checkboxLabel ); // because the checkbox is enabled by default, let's uncheck it
+		fireEvent.click( checkboxLabel ); // then check it
+		const checkboxLabel3 = screen.getByText( 'Plugin 3' );
+		fireEvent.click( checkboxLabel3 );
+		const checkboxLabel4 = screen.getByText( 'Plugin 4' ); // attempt to uncheck 4, but it shouldn't do anything since it is already unchecked
+		fireEvent.click( checkboxLabel4 );
+		const installButton = screen.getByText( 'Continue' );
+		fireEvent.click( installButton );

-		expect( getPluginCheckbox( 'WooPayments' ) ).toBeChecked();
-		expect( getPluginCheckbox( 'Google for WooCommerce' ) ).toBeChecked();
-		expect( getPluginCheckbox( 'MailPoet' ) ).toBeChecked();
-		expect(
-			screen
-				.getByText( 'Jetpack' )
-				.closest( '.woocommerce-profiler-plugins-plugin-card' )
-		).toHaveClass( 'is-installed' );
+		expect( mockSendEvent ).toHaveBeenCalledWith( {
+			type: 'PLUGINS_INSTALLATION_REQUESTED',
+			payload: {
+				pluginsSelected: [ 'plugin1' ],
+				pluginsShown: [ 'plugin1', 'plugin2', 'plugin3', 'plugin4' ],
+				pluginsUnselected: [ 'plugin3', 'plugin4' ],
+			},
+		} );
 	} );

-	it( 'completes without selecting when every plugin is installed', async () => {
+	it( 'handles case where all plugins are already installed', () => {
 		render(
 			<Plugins
 				context={ {
 					...mockContext,
 					pluginsAvailable: mockContext.pluginsAvailable.map(
-						( plugin ) => ( { ...plugin, is_activated: true } )
+						( plugin ) => ( {
+							...plugin,
+							is_activated: true,
+						} )
 					),
 				} }
 				sendEvent={ mockSendEvent }
 				navigationProgress={ navigationProgress }
 			/>
 		);
-
+		const plugin1Card = screen
+			.getByText( 'Plugin 1' )
+			.closest( '.woocommerce-profiler-plugins-plugin-card' );
+		expect( plugin1Card ).toHaveClass( 'is-installed' );
+		expect( plugin1Card ).toHaveTextContent( 'Installed' );
 		expect(
 			screen
-				.getByRole( 'heading', { level: 3, name: 'WooPayments' } )
+				.getByText( 'Plugin 2' )
 				.closest( '.woocommerce-profiler-plugins-plugin-card' )
 		).toHaveTextContent( 'Installed' );
-
-		await userEvent.click( screen.getByText( 'Continue' ) );
-
+		const continueButton = screen.getByText( 'Continue' );
+		fireEvent.click( continueButton );
 		expect( mockSendEvent ).toHaveBeenCalledWith( {
 			type: 'PLUGINS_PAGE_COMPLETED_WITHOUT_SELECTING_PLUGINS',
 		} );
 	} );

-	it( 'retries the previous selection after an installation error', async () => {
+	it( 'initialises with all plugins selected when there were no errors previously', () => {
+		render(
+			<Plugins
+				context={ mockContext }
+				sendEvent={ mockSendEvent }
+				navigationProgress={ navigationProgress }
+			/>
+		);
+		const checkboxLabels = screen.getAllByRole( 'checkbox' );
+		expect( checkboxLabels ).toHaveLength( 3 );
+		checkboxLabels.forEach( ( checkbox ) => {
+			expect( checkbox ).toBeChecked();
+		} );
+	} );
+
+	it( 'initialises with the previous selection correctly when there were errors previously', () => {
 		render(
 			<Plugins
 				context={ {
 					...mockContext,
+					pluginsAvailable: [ ...mockContext.pluginsAvailable ],
 					pluginsInstallationErrors: [
 						{
-							plugin: 'woocommerce-payments',
+							plugin: 'plugin4',
 							error: 'Installation failed',
 							errorDetails: {
 								data: {
@@ -169,72 +182,36 @@ describe( 'Plugins Component', () => {
 							},
 						},
 					],
-					pluginsSelected: [ 'woocommerce-payments', 'mailpoet:alt' ],
+					pluginsSelected: [ 'plugin4' ],
 				} }
 				sendEvent={ mockSendEvent }
 				navigationProgress={ navigationProgress }
 			/>
 		);
-
 		expect(
 			screen.getByText(
 				/Oops! We encountered a problem while installing/
 			)
 		).toBeInTheDocument();
-		expect( getPluginCheckbox( 'WooPayments' ) ).toBeChecked();
-		expect(
-			getPluginCheckbox( 'Google for WooCommerce' )
-		).not.toBeChecked();
-		expect( getPluginCheckbox( 'MailPoet' ) ).toBeChecked();
-
-		await userEvent.click( screen.getByText( 'Please try again' ) );
-
-		expect( mockSendEvent ).toHaveBeenCalledWith( {
-			type: 'PLUGINS_INSTALLATION_REQUESTED',
-			payload: {
-				pluginsShown: [
-					'woocommerce-payments',
-					'google-listings-and-ads',
-					'jetpack',
-					'mailpoet',
-				],
-				pluginsSelected: [ 'woocommerce-payments', 'mailpoet' ],
-				pluginsUnselected: [ 'google-listings-and-ads' ],
-			},
-		} );
-	} );
-
-	it( 'submits normalized shown, selected, and unselected plugin keys', async () => {
-		render(
-			<Plugins
-				context={ mockContext }
-				sendEvent={ mockSendEvent }
-				navigationProgress={ navigationProgress }
-			/>
-		);
-
-		await userEvent.click( getPluginCheckbox( 'MailPoet' ) );
-		await userEvent.click( screen.getByText( 'Continue' ) );
-
-		expect( mockSendEvent ).toHaveBeenCalledWith( {
-			type: 'PLUGINS_INSTALLATION_REQUESTED',
-			payload: {
-				pluginsShown: [
-					'woocommerce-payments',
-					'google-listings-and-ads',
-					'jetpack',
-					'mailpoet',
-				],
-				pluginsSelected: [
-					'woocommerce-payments',
-					'google-listings-and-ads',
-				],
-				pluginsUnselected: [ 'mailpoet' ],
-			},
-		} );
+		const checkbox1 = screen
+			.getByText( 'Plugin 1' )
+			.closest( '.woocommerce-profiler-plugins-plugin-card' )
+			?.querySelector( 'input[type="checkbox"]' );
+		expect( checkbox1 ).not.toBeChecked();
+		const checkbox3 = screen
+			.getByText( 'Plugin 3' )
+			.closest( '.woocommerce-profiler-plugins-plugin-card' )
+			?.querySelector( 'input[type="checkbox"]' );
+		expect( checkbox3 ).not.toBeChecked();
+		const checkbox4 = screen
+			// use role because error message also contains the plugin name
+			.getByRole( 'heading', { level: 3, name: 'Plugin 4' } )
+			.closest( '.woocommerce-profiler-plugins-plugin-card' )
+			?.querySelector( 'input[type="checkbox"]' );
+		expect( checkbox4 ).toBeChecked();
 	} );

-	it( 'handles skip action', async () => {
+	it( 'handles skip action', () => {
 		render(
 			<Plugins
 				context={ mockContext }
@@ -243,7 +220,7 @@ describe( 'Plugins Component', () => {
 			/>
 		);
 		const skipButton = screen.getByText( 'Skip this step' );
-		await userEvent.click( skipButton );
+		fireEvent.click( skipButton );
 		expect( mockSendEvent ).toHaveBeenCalledWith( {
 			type: 'PLUGINS_PAGE_SKIPPED',
 		} );
diff --git a/plugins/woocommerce/tests/e2e/fixtures/site.setup.ts b/plugins/woocommerce/tests/e2e/fixtures/site.setup.ts
index 595eb096dd8..9aef9137c0b 100644
--- a/plugins/woocommerce/tests/e2e/fixtures/site.setup.ts
+++ b/plugins/woocommerce/tests/e2e/fixtures/site.setup.ts
@@ -87,21 +87,6 @@ setup( 'setup site', async ( { baseURL, restApi } ) => {
 		await skipOnboardingWizard();
 	} );

-	await setup.step( 'dismiss email improvements prompts', async () => {
-		await setOption(
-			request,
-			baseURL,
-			'woocommerce_admin_dismissed_email_improvements_modal',
-			'yes'
-		);
-		await setOption(
-			request,
-			baseURL,
-			'woocommerce_admin_dismissed_try_email_improvements_modal',
-			'yes'
-		);
-	} );
-
 	await setup.step( 'determine if multisite', async () => {
 		const response = await restApi.get( `${ WC_API_PATH }/system_status` );
 		const { environment } = response.data;
diff --git a/plugins/woocommerce/tests/e2e/tests/onboarding/add-product-task.spec.ts b/plugins/woocommerce/tests/e2e/tests/onboarding/add-product-task.spec.ts
index d3a5b8398a3..5bf49547d42 100644
--- a/plugins/woocommerce/tests/e2e/tests/onboarding/add-product-task.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/onboarding/add-product-task.spec.ts
@@ -41,9 +41,6 @@ const show_task_list = async ( restApi: any, task_list_name: string ) => {
 test.describe( 'Add Product Task', () => {
 	test.use( { storageState: ADMIN_STATE_PATH } );

-	// Ids this spec creates, so teardown can delete its own products and nobody else's.
-	const createdProductIds: number[] = [];
-
 	test.beforeAll( async ( { restApi } ) => {
 		const productIds = [];

@@ -83,72 +80,19 @@ test.describe( 'Add Product Task', () => {
 		} );
 	} );

-	test.afterEach( async ( { restApi } ) => {
-		// The test leaves a product behind if it fails before its own delete, and leaves the
-		// setup task list hidden if it fails between hiding and showing it. Both are torn down
-		// here rather than in the body so that a failure reports the assertion that failed
-		// instead of a cleanup error raised on the way out.
-		//
-		// Only this spec's own products are deleted. The site is shared with the rest of the
-		// run, so sweeping every id the products endpoint returns would take other specs'
-		// fixtures with it.
-		// Copy rather than drain: an id stays tracked until the batch confirms it deleted, so a
-		// later afterEach retries whatever this one could not remove.
-		const idsToDelete = [ ...createdProductIds ];
-		// Every teardown call runs before anything is asserted: a failed delete must not stop
-		// the task list from being unhidden, or the rest of this serial project inherits it.
-		const deleteResponse = idsToDelete.length
-			? await restApi.post( `${ WC_API_PATH }/products/batch`, {
-					delete: idsToDelete,
-			  } )
-			: null;
-		const taskListShown = await show_task_list( restApi, 'setup' );
-
-		// On a passing run the test deletes its own product, so there is nothing left here
-		// and no response to check. Asserting 200 unconditionally would pass whether a
-		// delete happened or not.
-		if ( deleteResponse !== null ) {
-			expect( deleteResponse.status ).toBe( 200 );
-			// The batch endpoint reports per-item failures in the body and still
-			// returns 200, so the status on its own says nothing about whether the
-			// products actually went.
-			// An extension filter can reshape these results, so an entry that is missing,
-			// errored or carries no id counts as not deleted rather than throwing here.
-			const batchDeleteResults = deleteResponse.data?.delete;
-			const deleted: Array< { id?: number; error?: unknown } | null > =
-				Array.isArray( batchDeleteResults ) ? batchDeleteResults : [];
-			const confirmedIds = new Set(
-				deleted
-					.filter( ( item ) => item && ! item.error )
-					.map( ( item ) => item?.id )
-			);
-			const unconfirmedIds = idsToDelete.filter(
-				( id ) => ! confirmedIds.has( id )
-			);
-			createdProductIds.splice(
-				0,
-				createdProductIds.length,
-				...unconfirmedIds
-			);
-			expect( deleted ).toHaveLength( idsToDelete.length );
-			expect( unconfirmedIds ).toEqual( [] );
-		}
-		expect( taskListShown ).toBe( true );
-	} );
-
 	test.afterAll( async ( { restApi } ) => {
 		await restApi.post( `${ WC_ADMIN_API_PATH }/onboarding/profile`, {
 			skipped: false,
 		} );
 	} );

-	test( 'Products page redirects to add product task when no products exist', async ( {
+	test( 'Add product task displays options for different product types', async ( {
 		page,
-		restApi,
 	} ) => {
-		const productName = `Add product task product ${ Date.now() }`;
-
+		// Navigate to the task list
 		await page.goto( 'wp-admin/admin.php?page=wc-admin&task=products' );
+
+		// Verify product type options are displayed
 		await expect(
 			page.getByRole( 'menuitem', { name: 'Physical product' } )
 		).toBeVisible();
@@ -165,7 +109,23 @@ test.describe( 'Add Product Task', () => {
 			/wp-has-current-submenu/
 		);

+		await page
+			.getByRole( 'menuitem', { name: 'Physical product' } )
+			.click();
+		await expect(
+			page.locator(
+				'#menu-posts-product .wp-submenu li.current > a[href="post-new.php?post_type=product"]'
+			)
+		).toBeVisible();
+	} );
+
+	test( 'Products page redirects to add product task when no products exist', async ( {
+		page,
+	} ) => {
+		// Navigate to All Products page
 		await page.goto( 'wp-admin/edit.php?post_type=product' );
+
+		// Verify redirect to add product task
 		await expect( page ).toHaveURL(
 			/.+path=%2Fadd-product.+task=products/
 		);
@@ -192,11 +152,57 @@ test.describe( 'Add Product Task', () => {
 		await expect(
 			page.locator( '#toplevel_page_woocommerce' )
 		).toHaveClass( /wp-has-current-submenu/ );
+	} );
+
+	test( 'Products page shows products table when products exist', async ( {
+		page,
+		restApi,
+	} ) => {
+		// Create a test product
+		await restApi.post( `${ WC_API_PATH }/products`, {
+			name: 'Test Product',
+			type: 'simple',
+			regular_price: '10.00',
+		} );
+
+		// Navigate to All Products page
+		await page.goto( 'wp-admin/edit.php?post_type=product' );
+
+		// Verify products table is visible
+		await expect( page.locator( '.wp-list-table' ) ).toBeVisible();
+		await expect(
+			page.getByRole( 'columnheader', { name: 'Name' } )
+		).toHaveCount( 2 );
+		await expect(
+			page.getByRole( 'columnheader', { name: 'SKU' } )
+		).toHaveCount( 2 );
+		await expect(
+			page.getByRole( 'columnheader', { name: 'Price' } )
+		).toHaveCount( 2 );
+		await expect(
+			page.locator( '.wp-list-table > tbody > tr' )
+		).toHaveCount( 1 );
+
+		// Clean up - delete test product
+		const products = await restApi.get( `${ WC_API_PATH }/products` );
+		for ( const product of products.data ) {
+			await restApi.delete( `${ WC_API_PATH }/products/${ product.id }`, {
+				force: true,
+			} );
+		}
+	} );

+	test( 'Products page redirects to add product task when no products exist and task list is hidden', async ( {
+		page,
+		restApi,
+	} ) => {
+		// Hide the task list
 		expect( await hide_task_list( restApi, 'setup' ) ).toBe( true );

+		// Navigate to All Products page
 		await page.goto( 'wp-admin/edit.php?post_type=product' );

+		// Verify redirect to add product task
 		await expect( page ).toHaveURL(
 			/.+path=%2Fadd-product.+task=products/
 		);
@@ -210,51 +216,7 @@ test.describe( 'Add Product Task', () => {
 			page.getByRole( 'menuitem', { name: 'Grouped product' } )
 		).toBeVisible();

+		// Reset task list to visible
 		expect( await show_task_list( restApi, 'setup' ) ).toBe( true );
-
-		const productResponse = await restApi.post(
-			`${ WC_API_PATH }/products`,
-			{
-				name: productName,
-				type: 'simple',
-				regular_price: '10.00',
-			}
-		);
-		const createdProductId = productResponse.data.id;
-		createdProductIds.push( createdProductId );
-		expect( productResponse.status ).toBe( 201 );
-		expect(
-			Number.isSafeInteger( createdProductId ) && createdProductId > 0
-		).toBe( true );
-
-		await page.goto( 'wp-admin/edit.php?post_type=product' );
-
-		await expect( page.locator( '.wp-list-table' ) ).toBeVisible();
-		await expect(
-			page.getByRole( 'link', { name: productName, exact: true } )
-		).toBeVisible();
-
-		const deleteResponse = await restApi.delete(
-			`${ WC_API_PATH }/products/${ createdProductId }`,
-			{ force: true }
-		);
-		expect( deleteResponse.status ).toBe( 200 );
-		// Stop tracking only once the delete is confirmed. Splicing first means a
-		// failed delete leaves a published product that the afterEach sweep no
-		// longer knows about -- in a spec whose whole premise is an empty catalog.
-		createdProductIds.splice(
-			createdProductIds.indexOf( createdProductId ),
-			1
-		);
-
-		await page.goto( 'wp-admin/admin.php?page=wc-admin&task=products' );
-		await page
-			.getByRole( 'menuitem', { name: 'Physical product' } )
-			.click();
-		await expect(
-			page.locator(
-				'#menu-posts-product .wp-submenu li.current > a[href="post-new.php?post_type=product"]'
-			)
-		).toBeVisible();
 	} );
 } );
diff --git a/plugins/woocommerce/tests/e2e/tests/onboarding/onboarding-wizard.spec.ts b/plugins/woocommerce/tests/e2e/tests/onboarding/onboarding-wizard.spec.ts
index 35bb6552996..4dbb6b59e94 100644
--- a/plugins/woocommerce/tests/e2e/tests/onboarding/onboarding-wizard.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/onboarding/onboarding-wizard.spec.ts
@@ -2,7 +2,7 @@
  * External dependencies
  */
 import { request } from '@playwright/test';
-import { WC_ADMIN_API_PATH } from '@woocommerce/e2e-utils-playwright';
+import type { Page } from '@playwright/test';

 /**
  * Internal dependencies
@@ -12,6 +12,12 @@ import { setOption } from '../../utils/options';
 import { setComingSoon } from '../../utils/coming-soon';
 import { ADMIN_STATE_PATH } from '../../playwright.config';

+const getPluginLocator = ( page: Page, slug: string ) => {
+	return page.locator(
+		`.woocommerce-profiler-plugins-plugin-card[data-slug="${ slug }"]`
+	);
+};
+
 test.use( { storageState: ADMIN_STATE_PATH } );

 test.afterAll( async ( { baseURL } ) => {
@@ -22,9 +28,7 @@ test.describe(
 	'Store owner can complete the core profiler',
 	{ tag: tags.SKIP_ON_EXTERNAL_ENV },
 	() => {
-		// Completing the profiler saves the store location, and the location list leaves out
-		// the selected option. Reset it before every test so each one can pick the same location.
-		test.beforeEach( async ( { baseURL } ) => {
+		test.beforeAll( async ( { baseURL } ) => {
 			try {
 				await setOption(
 					request,
@@ -32,12 +36,6 @@ test.describe(
 					'woocommerce_remote_variant_assignment',
 					'60'
 				);
-				await setOption(
-					request,
-					baseURL,
-					'woocommerce_default_country',
-					'US:CA'
-				);
 			} catch ( error ) {
 				console.log( error );
 			}
@@ -122,6 +120,12 @@ test.describe(
 						name: 'Get a boost with our free features',
 					} )
 				).toBeVisible();
+				// check that WooPayments is displayed because Australia is a supported country
+				await expect(
+					page.getByRole( 'heading', {
+						name: 'Get paid with WooPayments',
+					} )
+				).toBeVisible();
 				// skip this step so that no extensions are installed
 				await page
 					.getByRole( 'button', { name: 'Skip this step' } )
@@ -143,8 +147,7 @@ test.describe(
 				// dashboard shown
 				await expect(
 					page.getByRole( 'heading', {
-						name: 'Home',
-						exact: true,
+						name: 'Welcome to WooCommerce Core E2E Test Suite',
 					} )
 				).toBeVisible();

@@ -199,70 +202,18 @@ test.describe(
 			} );
 		} );

-		test( 'Can complete the core profiler with an extension selected', async ( {
+		test( 'Can complete the core profiler installing default extensions', async ( {
 			page,
-			restApi,
 		} ) => {
 			test.skip(
 				!! process.env.IS_MULTISITE,
 				'Test not working on a multisite setup, see https://github.com/woocommerce/woocommerce/issues/55066'
 			);
-
-			// Installing from WordPress.org would make this title depend on an outside
-			// service and leave real plugins on the shared site, so the two requests the
-			// profiler sends for the selection are answered here with the REST
-			// controller's success shape. What stays under test is the browser half:
-			// the selection reaches those requests, and a successful install finishes
-			// the profiler on the home screen.
-			const slug = 'google-listings-and-ads';
-			const pluginRequests: Array< { path: string; body: unknown } > = [];
-			await page.route(
-				( url ) =>
-					/\/wc-admin\/plugins\/(install|activate)(?:[?&]|$)/.test(
-						decodeURIComponent( url.toString() )
-					),
-				async ( route ) => {
-					const path = /\/wc-admin\/plugins\/activate(?:[?&]|$)/.test(
-						decodeURIComponent( route.request().url() )
-					)
-						? 'activate'
-						: 'install';
-					pluginRequests.push( {
-						path,
-						body: route.request().postDataJSON(),
-					} );
-					const errors = { errors: {}, error_data: {} };
-					await route.fulfill( {
-						json:
-							path === 'install'
-								? {
-										data: {
-											installed: [ slug ],
-											results: { [ slug ]: true },
-											install_time: { [ slug ]: 1 },
-											plugin_details: {},
-										},
-										errors,
-										success: true,
-								  }
-								: {
-										data: {
-											activated: [ slug ],
-											active: [ slug ],
-											plugin_details: {},
-										},
-										errors,
-										success: true,
-								  },
-					} );
-				}
-			);
-
 			await page.goto(
 				'wp-admin/admin.php?page=wc-admin&path=%2Fsetup-wizard'
 			);

-			await test.step( 'Walk the profiler to the extensions step', async () => {
+			await test.step( 'Intro page and opt in to data sharing', async () => {
 				await expect(
 					page.getByRole( 'heading', { name: 'Welcome to Woo!' } )
 				).toBeVisible();
@@ -274,7 +225,9 @@ test.describe(
 				await page
 					.getByRole( 'button', { name: 'Set up my store' } )
 					.click();
+			} );

+			await test.step( 'User profile information', async () => {
 				await expect(
 					page.getByRole( 'heading', {
 						name: 'Which one of these best describes you?',
@@ -282,32 +235,42 @@ test.describe(
 				).toBeVisible();
 				await page
 					.getByRole( 'radio' )
-					.filter( { hasText: 'just starting my business' } )
+					.filter( { hasText: 'already selling' } )
+					.click();
+				await page.getByLabel( 'Select an option' ).click();
+				await page
+					.getByRole( 'option' )
+					.filter( { hasText: 'selling offline' } )
 					.click();
 				await page.getByRole( 'button', { name: 'Continue' } ).click();
+			} );

+			await test.step( 'Business Information', async () => {
 				await expect(
 					page.getByRole( 'heading', {
 						name: 'Tell us a bit about your store',
 					} )
 				).toBeVisible();
+				await expect(
+					page.getByPlaceholder( 'Ex. My awesome store' )
+				).toHaveValue( 'WooCommerce Core E2E Test Suite' );
 				await page
 					.locator(
 						'form.woocommerce-profiler-business-information-form > div > div > div > div > input'
 					)
 					.first()
 					.click();
+				// select food and drink
 				await page
-					.getByRole( 'option', { name: 'Clothing and accessories' } )
+					.getByRole( 'option', { name: 'Food and drink' } )
 					.click();
-				// The location field is required.
+				// select a WooPayments incompatible location
 				await page.getByRole( 'combobox' ).last().click();
-				await page.getByRole( 'combobox' ).last().fill( 'Australia' );
+				await page.getByRole( 'combobox' ).last().fill( 'Afghanistan' );
 				await page
-					.getByRole( 'option', {
-						name: 'Australia — Northern Territory',
-					} )
+					.getByRole( 'option', { name: 'Afghanistan' } )
 					.click();
+
 				await page
 					.getByPlaceholder( 'wordpress@example.com' )
 					.fill( 'merchant@example.com' );
@@ -315,57 +278,208 @@ test.describe(
 				await page.getByRole( 'button', { name: 'Continue' } ).click();
 			} );

-			await test.step( 'Select one extension and continue', async () => {
+			await test.step( 'Extensions -- install some suggested extensions', async () => {
 				await expect(
 					page.getByRole( 'heading', {
 						name: 'Get a boost with our free features',
 					} )
 				).toBeVisible();
-				// Every recommendation that is not active starts selected. Clear them all,
-				// then pick one that needs no Jetpack connection, so the profiler ends on
-				// the home screen rather than the Jetpack authorization page.
-				const cards = page.locator(
-					'.woocommerce-profiler-plugins-plugin-card'
-				);
-				await expect( cards.first() ).toBeVisible();
-				for ( const checkbox of await cards
-					.getByRole( 'checkbox' )
-					.all() ) {
-					await checkbox.uncheck();
-				}
+				// check that WooPayments is not displayed because Afghanistan is not a supported country
 				await expect(
-					cards.locator( 'input[type="checkbox"]:checked' )
-				).toHaveCount( 0 );
-				await cards
-					.and( page.locator( `[data-slug="${ slug }"]` ) )
+					page.getByRole( 'heading', {
+						name: 'Get paid with WooPayments',
+					} )
+				).not.toBeAttached();
+
+				// select and install the rest of the extensions
+				try {
+					await page
+						.getByText(
+							'Boost content creation with Jetpack AI AssistantSave time on content creation'
+						)
+						.getByRole( 'checkbox' )
+						.uncheck( { timeout: 2000 } );
+				} catch ( e ) {
+					console.log(
+						'Checkbox not present for Jetpack AI Assistant'
+					);
+				}
+				try {
+					await getPluginLocator( page, 'pinterest-for-woocommerce' )
+						.getByRole( 'checkbox' )
+						.check( { timeout: 2000 } );
+				} catch ( e ) {
+					console.log( 'Checkbox not present for Pinterest' );
+				}
+				try {
+					await getPluginLocator( page, 'mailchimp-for-woocommerce' )
+						.getByRole( 'checkbox' )
+						.uncheck( { timeout: 2000 } );
+				} catch ( e ) {
+					console.log( 'Checkbox not present for MailChimp' );
+				}
+				await getPluginLocator( page, 'google-listings-and-ads' )
 					.getByRole( 'checkbox' )
-					.check();
+					.check( { timeout: 2000 } );
 				await page.getByRole( 'button', { name: 'Continue' } ).click();
 			} );

-			await test.step( 'Confirm the selection was installed and the profiler finished', async () => {
+			await test.step( 'Confirm that core profiler was completed and a couple of default extensions installed', async () => {
+				page.on( 'dialog', ( dialog ) => dialog.accept() );
+				// intermediate page shown
+				// the next two are soft assertions because depending on the extensions selected, they may or may not appear
+				// and we want the test to complete in order for cleanup to happen
+				await expect
+					.soft(
+						page
+							.getByRole( 'heading' )
+							.filter( { hasText: 'get your features ready' } )
+					)
+					.toBeVisible( { timeout: 30000 } );
+				await expect
+					.soft(
+						page
+							.getByRole( 'heading' )
+							.filter( { hasText: 'Extending your store' } )
+					)
+					.toBeVisible( { timeout: 30000 } );
+				// dashboard shown
+				await expect(
+					page.getByRole( 'heading', {
+						name: 'Welcome to WooCommerce Core E2E Test Suite',
+					} )
+				).toBeVisible( { timeout: 30000 } );
+				// go to the plugins page to make sure that extensions were installed
+				await page.goto( 'wp-admin/plugins.php?plugin_status=active' );
+				await expect(
+					page.getByRole( 'heading', {
+						name: 'Plugins',
+						exact: true,
+					} )
+				).toBeVisible();
+				// confirm that the optional plugins are present
+				try {
+					await expect(
+						page.locator(
+							`[data-slug="pinterest-for-woocommerce"]`
+						)
+					).toBeVisible();
+				} catch {
+					console.log(
+						`Pinterest is not found or not visible on the page`
+					);
+				}
+
+				try {
+					await expect(
+						page.locator( `[data-slug="google-listings-and-ads"]` )
+					).toBeVisible();
+				} catch {
+					console.log(
+						`Google for WooCommerce is not found or not visible on the page`
+					);
+				}
+
+				try {
+					await expect(
+						page.locator(
+							`[data-slug="mailchimp-for-woocommerce"]`
+						)
+					).toBeHidden();
+				} catch {
+					console.log( `MailChimp is found on the page` );
+				}
+
+				try {
+					await expect(
+						page.locator( `[data-slug="jetpack"]` )
+					).toBeHidden();
+				} catch {
+					console.log( `Jetpack is found on the page` );
+				}
+			} );
+
+			await test.step( 'Confirm that information from core profiler saved', async () => {
+				await page.goto( 'wp-admin/admin.php?page=wc-settings' );
 				await expect(
-					page.getByRole( 'heading', { name: 'Home', exact: true } )
+					page.getByRole( 'textbox', { name: 'Afghanistan' } )
 				).toBeVisible();
+				await expect(
+					page.getByRole( 'textbox', { name: 'Afghan afghani (؋)' } )
+				).toBeVisible();
+				await expect(
+					page.getByRole( 'textbox', { name: 'Left with space' } )
+				).toBeVisible();
+				await expect(
+					page.getByLabel( 'Thousand separator', { exact: true } )
+				).toHaveValue( '.' );
+				await expect(
+					page.getByLabel( 'Decimal separator', { exact: true } )
+				).toHaveValue( ',' );
+				await expect(
+					page.getByLabel( 'Number of decimals' )
+				).toHaveValue( '0' );
+			} );

-				expect( pluginRequests ).toEqual( [
-					{
-						path: 'install',
-						body: {
-							plugins: slug,
-							async: false,
-							source: 'core-profiler',
-						},
-					},
-					{ path: 'activate', body: { plugins: slug } },
-				] );
-
-				// The profiler records what the install reported, which only happens on
-				// the success path: a failed install returns to the extensions step.
-				const { data: profile } = await restApi.get(
-					`${ WC_ADMIN_API_PATH }/onboarding/profile`
-				);
-				expect( profile.business_extensions ).toEqual( [ slug ] );
+			await test.step( 'Clean up installed extensions', async () => {
+				const deactivateAndDeletePlugin = async ( slug: string ) => {
+					await page.goto( 'wp-admin/plugins.php' );
+					const pluginRow = page.locator(
+						`tr[data-slug="${ slug }"]`
+					);
+
+					// Skip if plugin is not present
+					if ( ! ( await pluginRow.isVisible() ) ) {
+						return;
+					}
+
+					// Deactivate if active
+					const deactivateLink = pluginRow.getByRole( 'link', {
+						name: 'Deactivate',
+						exact: true,
+					} );
+					if ( await deactivateLink.isVisible() ) {
+						await deactivateLink.click();
+						await expect(
+							page.getByText( 'Plugin deactivated.' )
+						).toBeVisible();
+					}
+
+					// Delete plugin
+					const deleteLink = pluginRow.getByRole( 'link', {
+						name: 'Delete',
+						exact: true,
+					} );
+					if ( await deleteLink.isVisible() ) {
+						try {
+							await deleteLink.click();
+							await expect(
+								page.getByText( 'was successfully deleted.' )
+							).toBeVisible( { timeout: 5000 } );
+						} catch ( e ) {
+							await page
+								.getByText( 'Yes, delete these files and data' )
+								.click();
+							await page
+								.getByText(
+									'The selected plugin has been deleted.'
+								)
+								.waitFor();
+						}
+					}
+				};
+
+				await deactivateAndDeletePlugin( 'google-listings-and-ads' );
+				await deactivateAndDeletePlugin( 'pinterest-for-woocommerce' );
+			} );
+
+			await test.step( 'Confirm that the store is in coming soon mode after completing the core profiler', async () => {
+				await page.goto( 'wp-admin/admin.php?page=wc-admin' );
+				await expect(
+					page
+						.getByRole( 'menuitem' )
+						.filter( { hasText: 'coming soon' } )
+				).toBeVisible();
 			} );
 		} );
 	}
@@ -408,8 +522,7 @@ test.describe(

 			await expect(
 				page.getByRole( 'heading', {
-					name: 'Home',
-					exact: true,
+					name: 'Welcome to WooCommerce Core E2E Test Suite',
 				} )
 			).toBeVisible();

diff --git a/plugins/woocommerce/tests/php/src/Admin/Features/OnboardingTasks/Tasks/ProductsTest.php b/plugins/woocommerce/tests/php/src/Admin/Features/OnboardingTasks/Tasks/ProductsTest.php
deleted file mode 100644
index 72ab4f1176c..00000000000
--- a/plugins/woocommerce/tests/php/src/Admin/Features/OnboardingTasks/Tasks/ProductsTest.php
+++ /dev/null
@@ -1,188 +0,0 @@
-<?php
-
-declare( strict_types = 1 );
-
-namespace Automattic\WooCommerce\Tests\Admin\Features\OnboardingTasks\Tasks;
-
-use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
-use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskList;
-use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\Products;
-use WC_Product_Simple;
-use WC_Unit_Test_Case;
-
-/**
- * Tests for the Products onboarding task.
- */
-class ProductsTest extends WC_Unit_Test_Case {
-
-	/**
-	 * The System Under Test.
-	 *
-	 * @var Products
-	 */
-	private $sut;
-
-	/**
-	 * The parent task list used by the Products task.
-	 *
-	 * @var TaskList
-	 */
-	private $task_list;
-
-	/**
-	 * The redirect interceptor registered for the test.
-	 *
-	 * @var callable
-	 */
-	private $redirect_interceptor;
-
-	/**
-	 * The location of an intercepted redirect.
-	 *
-	 * @var string|null
-	 */
-	private $redirect_location;
-
-	/**
-	 * The status of an intercepted redirect.
-	 *
-	 * @var int|null
-	 */
-	private $redirect_status;
-
-	/**
-	 * Set up test fixtures.
-	 */
-	public function setUp(): void {
-		parent::setUp();
-
-		delete_transient( Products::HAS_PRODUCT_TRANSIENT );
-		delete_option( Task::COMPLETED_OPTION );
-		update_option( TaskList::HIDDEN_OPTION, array( 'setup' ) );
-
-		$this->redirect_location    = null;
-		$this->redirect_status      = null;
-		$this->redirect_interceptor = function ( $location, $status ) {
-			$this->redirect_location = (string) $location;
-			$this->redirect_status   = (int) $status;
-
-			throw new \RuntimeException( 'Redirect intercepted.' );
-		};
-		add_filter( 'wp_redirect', $this->redirect_interceptor, 10, 2 );
-
-		$this->task_list = new TaskList(
-			array(
-				'id'        => 'setup',
-				'hidden_id' => 'setup',
-			)
-		);
-		$this->sut       = new Products( $this->task_list );
-	}
-
-	/**
-	 * @testdox Redirects an empty product list through the registered current_screen hook.
-	 */
-	public function test_redirects_empty_product_list_through_registered_current_screen_hook(): void {
-		$this->trigger_current_screen( 'edit-product' );
-
-		$this->assertSame(
-			admin_url( 'admin.php?page=wc-admin&path=/add-product&task=products' ),
-			$this->redirect_location,
-			'An empty product list should redirect to the Products task.'
-		);
-		$this->assertSame( 302, $this->redirect_status, 'The Products task redirect should use the default 302 status.' );
-	}
-
-	/**
-	 * @testdox Does not redirect non-product screens.
-	 *
-	 * @dataProvider non_product_screen_provider
-	 *
-	 * @param string $screen_id Screen ID to trigger.
-	 */
-	public function test_does_not_redirect_non_product_screens( string $screen_id ): void {
-		$this->trigger_current_screen( $screen_id );
-
-		$this->assertNull( $this->redirect_location, "The {$screen_id} screen should not redirect." );
-		$this->assertNull( $this->redirect_status, "The {$screen_id} screen should not set a redirect status." );
-	}
-
-	/**
-	 * Provides named non-product screens.
-	 *
-	 * @return array<string, array{screen_id: string}>
-	 */
-	public function non_product_screen_provider(): array {
-		return array(
-			'dashboard'         => array( 'screen_id' => 'dashboard' ),
-			'non-product posts' => array( 'screen_id' => 'edit-post' ),
-		);
-	}
-
-	/**
-	 * @testdox Does not redirect the product list when a published product exists.
-	 */
-	public function test_does_not_redirect_product_list_when_published_product_exists(): void {
-		$this->create_product( 'publish' );
-
-		$this->trigger_current_screen( 'edit-product' );
-
-		$this->assertNull( $this->redirect_location, 'A published product should keep the product list accessible.' );
-		$this->assertNull( $this->redirect_status, 'A published product should not produce a redirect status.' );
-	}
-
-	/**
-	 * @testdox Redirects the product list when only an auto-draft product exists.
-	 */
-	public function test_redirects_product_list_when_only_auto_draft_product_exists(): void {
-		$this->create_product( 'auto-draft' );
-
-		$this->trigger_current_screen( 'edit-product' );
-
-		$this->assertSame(
-			admin_url( 'admin.php?page=wc-admin&path=/add-product&task=products' ),
-			$this->redirect_location,
-			'An auto-draft alone should not keep the product list accessible.'
-		);
-		$this->assertSame( 302, $this->redirect_status, 'The auto-draft redirect should use the default 302 status.' );
-	}
-
-	/**
-	 * @testdox Is always accessible when its parent task list is hidden.
-	 */
-	public function test_is_always_accessible_when_parent_task_list_is_hidden(): void {
-		$this->assertTrue( $this->task_list->is_hidden(), 'The Products task parent list should use the persisted hidden-list state.' );
-		$this->assertTrue( $this->sut->is_always_accessible(), 'The Products task should remain accessible when its parent list is hidden.' );
-	}
-
-	/**
-	 * Creates a real simple product with the requested status.
-	 *
-	 * @param string $status Product status.
-	 * @return int Product ID.
-	 */
-	private function create_product( string $status ): int {
-		$product = new WC_Product_Simple();
-		$product->set_name( "Products task {$status} fixture" );
-		$product->set_status( $status );
-		$product_id = $product->save();
-		$this->assertGreaterThan( 0, $product_id, 'The product fixture should persist before the task reads it.' );
-
-		return $product_id;
-	}
-
-	/**
-	 * Triggers the registered current_screen callbacks for a WordPress screen.
-	 *
-	 * @param string $screen_id Screen ID to trigger.
-	 */
-	private function trigger_current_screen( string $screen_id ): void {
-		try {
-			set_current_screen( $screen_id );
-		} catch ( \RuntimeException $exception ) {
-			if ( 'Redirect intercepted.' !== $exception->getMessage() ) {
-				throw $exception;
-			}
-		}
-	}
-}
diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/RemoteFreeExtensions/DefaultFreeExtensionsTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/RemoteFreeExtensions/DefaultFreeExtensionsTest.php
index dd20fbb08ca..bc42af76103 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Admin/RemoteFreeExtensions/DefaultFreeExtensionsTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/RemoteFreeExtensions/DefaultFreeExtensionsTest.php
@@ -161,53 +161,6 @@ class DefaultFreeExtensionsTest extends WC_Unit_Test_Case {
 		);
 	}

-	/**
-	 * @testdox Core profiler WooPayments visibility should follow the store country.
-	 * @dataProvider core_profiler_woocommerce_payments_visibility_provider
-	 *
-	 * @param string $country        Store country and optional state.
-	 * @param bool   $should_include Whether WooPayments should be recommended.
-	 */
-	public function test_core_profiler_woocommerce_payments_visibility_by_country( string $country, bool $should_include ): void {
-		update_option( 'woocommerce_default_country', $country );
-		update_option( 'woocommerce_store_address', '1 Test Street' );
-		update_option( 'woocommerce_remote_variant_assignment', 60 );
-		update_option( 'active_plugins', array() );
-		update_option( 'woocommerce_onboarding_profile', array() );
-
-		$results = EvaluateExtension::evaluate_bundles(
-			DefaultFreeExtensions::get_all(),
-			array( 'obw/core-profiler' )
-		);
-
-		$this->assertSame( array(), $results['errors'], 'The real core profiler bundle should evaluate without errors.' );
-		$this->assertCount( 1, $results['bundles'], 'Only the core profiler bundle should be evaluated.' );
-		$plugin_slugs = array_map(
-			static function ( $plugin ) {
-				return $plugin->key;
-			},
-			$results['bundles'][0]['plugins']
-		);
-
-		if ( $should_include ) {
-			$this->assertContains( 'woocommerce-payments', $plugin_slugs );
-		} else {
-			$this->assertNotContains( 'woocommerce-payments', $plugin_slugs );
-		}
-	}
-
-	/**
-	 * Store countries for core profiler WooPayments visibility.
-	 *
-	 * @return array<string, array{string, bool}>
-	 */
-	public function core_profiler_woocommerce_payments_visibility_provider(): array {
-		return array(
-			'AU:NT' => array( 'AU:NT', true ),
-			'AF'    => array( 'AF', false ),
-		);
-	}
-
 	/**
 	 * Evaluates bundles passed as argument and extracts keys of recommended plugins.
 	 *