Commit 4797836967e for woocommerce

commit 4797836967e9abbf8b04a139d23699db624b9435
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date:   Wed Sep 16 00:41:39 2026 +0300

    [tests] Demote 8 Settings > Emails E2E tests to PHPUnit and Jest (#68630)

    * test(email): Move Settings > Emails controls below E2E

    The Settings > Emails spec ran ten browser titles over the screen's
    own controls: the preview pane and its type select, the sender line
    in the preview header, the color palette and its reset, the font
    family select and the footer text field. Most of what they asserted
    is decided either by the settings array the screen is built from or
    by three small React controls, and only two of the ten walked a path
    a merchant would recognize as a journey.

    Move the settings contract to WC_Settings_Emails_Test: the color
    palette's five ids, titles and defaults, the absence of the legacy
    titles, the font family option list with its selected value, the
    footer field's type, default, placeholder and description, and the
    single-email preview mount that hides the type select. Add two Jest
    suites for the controls themselves: the palette's sync and undo
    behavior, the preview type select, and the preview header's sender
    line, mount fetch and subject refresh.

    Keep two installed journeys. The live preview a merchant sees while
    changing settings now carries the assertions of the separate preview
    title it absorbs, and the media-library image picker is unchanged.
    Both now turn the block email editor off before each test. The spec
    never set that option before, and both survivors need it off.

    Consolidates the mega-branch slices:
    - Slice 052: test(email): Move settings controls below E2E

    Three later commits come with it, each one written to make a
    mutation killable: the footer placeholder assertion, the split of
    the palette's observers into three independent cases, and the exact
    request count after a transient save.

    Refs TESTOPS-288
    Refs #68046

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    * test(email): Stop the preview settings test restoring base-class state

    The new email-preview test captured two from-address options and every
    email-setting transient row, then wrote all of them back in a finally
    block. Transients with no persistent object cache are options-table
    rows like any other, so the transaction rollback in tear_down() covers
    both sets.

    The ob_start()/ob_end_clean() pairs stay. Output buffering is process
    state the base class does not own, and the nesting level would carry
    into the next test.

    The other option restore in this file, on
    test_get_default_settings_with_block_email_editor_enabled, is trunk's
    and is left alone.

    Same 11 tests and 43 assertions before and after.

    Refs TESTOPS-288

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    * test(email): Pin desc_tip on the footer text setting

    The test asserted that {store_address} and {store_email} appear in the
    setting's desc, but not that desc_tip is true -- and desc_tip is what
    routes desc into the help tip the deleted E2E title actually read.
    Setting desc_tip to a string is a supported form that replaces the
    tooltip text, so the placeholder hints could vanish from the tooltip with
    both existing assertions still green.

    Refs TESTOPS-288

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    * test(settings): Pin the email color palette mount markup

    WC_Settings_Emails::email_color_palette() prints the element the
    settings script mounts the palette controls into, with the default
    colors and a theme.json flag as data attributes. Nothing asserted any of
    it, so a renamed id or a dropped attribute passed every test.

    Render it under a theme with theme.json and one without, and assert the
    id, both attributes and the auto-sync input. The defaults follow the
    active theme's palette, so the expected colors are read while that theme
    is active.

    Refs TESTOPS-288

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    * test(settings): Pin what the color palette fill reads from its mount

    registerSettingsEmailColorPaletteFill() parses data-default-colors,
    checks data-has-theme-json and reads the auto-sync input, but the palette
    control's Jest suite passes those props in directly.

    Seed the markup the PHP method prints and assert the props the fill
    renders with. Inverting the theme.json check or swapping two color keys
    fails it.

    Refs TESTOPS-288

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    * test(e2e): Keep the Send email preview title in the settings spec

    It is the only title that opens the test-email modal from Settings >
    Emails. The Jest and PHP owners cover the send itself but not the
    button-to-modal wiring, and the title is cheap in a spec that already
    runs in core-serial. Restored unchanged from trunk.

    Refs TESTOPS-288

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---------

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

diff --git a/plugins/woocommerce/changelog/testops-288-email-settings-and-rendering b/plugins/woocommerce/changelog/testops-288-email-settings-and-rendering
new file mode 100644
index 00000000000..e7894f3db07
--- /dev/null
+++ b/plugins/woocommerce/changelog/testops-288-email-settings-and-rendering
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+Comment: Move Settings > Emails control coverage below E2E; ten browser titles become two installed journeys.
+
diff --git a/plugins/woocommerce/client/admin/client/settings-email/__tests__/settings-email-color-palette-control.test.tsx b/plugins/woocommerce/client/admin/client/settings-email/__tests__/settings-email-color-palette-control.test.tsx
new file mode 100644
index 00000000000..c7d90d58862
--- /dev/null
+++ b/plugins/woocommerce/client/admin/client/settings-email/__tests__/settings-email-color-palette-control.test.tsx
@@ -0,0 +1,199 @@
+/**
+ * External dependencies
+ */
+import { fireEvent, render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
+/**
+ * Internal dependencies
+ */
+import { ResetStylesControl } from '../settings-email-color-palette-control';
+import type { DefaultColors } from '../settings-email-color-palette-slotfill';
+
+type JQueryAdapter = ( selector: string ) => {
+	length: number;
+	on: ( eventName: string, listener: () => void ) => void;
+	off: ( eventName: string, listener: () => void ) => void;
+};
+
+const jQueryAdapter: JQueryAdapter = ( selector ) => {
+	const elements = Array.from( document.querySelectorAll( selector ) );
+
+	return {
+		length: elements.length,
+		on: ( eventName, listener ) => {
+			elements.forEach( ( element ) =>
+				element.addEventListener( eventName, listener )
+			);
+		},
+		off: ( eventName, listener ) => {
+			elements.forEach( ( element ) =>
+				element.removeEventListener( eventName, listener )
+			);
+		},
+	};
+};
+
+const colorFields = [
+	{
+		id: 'woocommerce_email_base_color',
+		label: 'Accent',
+		key: 'baseColor',
+	},
+	{
+		id: 'woocommerce_email_background_color',
+		label: 'Email background',
+		key: 'bgColor',
+	},
+	{
+		id: 'woocommerce_email_body_background_color',
+		label: 'Content background',
+		key: 'bodyBgColor',
+	},
+	{
+		id: 'woocommerce_email_text_color',
+		label: 'Heading and text',
+		key: 'bodyTextColor',
+	},
+	{
+		id: 'woocommerce_email_footer_text_color',
+		label: 'Secondary text',
+		key: 'footerTextColor',
+	},
+] as const;
+
+const initialColors: DefaultColors = {
+	baseColor: '#111111',
+	bgColor: '#222222',
+	bodyBgColor: '#333333',
+	bodyTextColor: '#444444',
+	footerTextColor: '#555555',
+};
+
+const themeColors: DefaultColors = {
+	baseColor: '#a10000',
+	bgColor: '#b20000',
+	bodyBgColor: '#c30000',
+	bodyTextColor: '#d40000',
+	footerTextColor: '#e50000',
+};
+
+const changeAccent = () => {
+	fireEvent.change( screen.getByLabelText( 'Accent' ), {
+		target: { value: '#abcdef' },
+	} );
+};
+
+describe( 'ResetStylesControl', () => {
+	let settingsFixture = document.createElement( 'div' );
+	let autoSyncInput = document.createElement( 'input' );
+	let unmount: undefined | ( () => void );
+
+	const appendColorInputs = ( colors: DefaultColors ) => {
+		for ( const field of colorFields ) {
+			const label = document.createElement( 'label' );
+			const input = document.createElement( 'input' );
+
+			label.htmlFor = field.id;
+			label.textContent = field.label;
+			input.id = field.id;
+			input.value = colors[ field.key ];
+			settingsFixture?.append( label, input );
+		}
+	};
+
+	const expectColors = ( colors: DefaultColors ) => {
+		for ( const field of colorFields ) {
+			expect( screen.getByLabelText( field.label ) ).toHaveValue(
+				colors[ field.key ]
+			);
+		}
+	};
+
+	const renderWithThemeDefaults = () => {
+		const renderResult = render(
+			<ResetStylesControl
+				defaultColors={ initialColors }
+				hasThemeJson
+				autoSync
+				autoSyncInput={ autoSyncInput }
+			/>
+		);
+		unmount = renderResult.unmount;
+
+		renderResult.rerender(
+			<ResetStylesControl
+				defaultColors={ themeColors }
+				hasThemeJson
+				autoSync
+				autoSyncInput={ autoSyncInput }
+			/>
+		);
+	};
+
+	beforeEach( () => {
+		unmount = undefined;
+		settingsFixture = document.createElement( 'div' );
+		settingsFixture.setAttribute( 'aria-label', 'Email color settings' );
+		document.body.appendChild( settingsFixture );
+		appendColorInputs( initialColors );
+
+		autoSyncInput = document.createElement( 'input' );
+		autoSyncInput.type = 'hidden';
+		autoSyncInput.id = 'woocommerce_email_auto_sync_with_theme';
+		autoSyncInput.value = 'yes';
+		settingsFixture.appendChild( autoSyncInput );
+
+		Object.defineProperty( globalThis, 'jQuery', {
+			configurable: true,
+			value: jQueryAdapter,
+		} );
+	} );
+
+	afterEach( () => {
+		unmount?.();
+		settingsFixture.remove();
+		delete ( globalThis as typeof globalThis & { jQuery?: JQueryAdapter } )
+			.jQuery;
+	} );
+
+	it( 'shows sync and undo controls after a color change', () => {
+		renderWithThemeDefaults();
+		changeAccent();
+
+		expect( autoSyncInput ).toHaveValue( 'no' );
+		expect(
+			screen.getByRole( 'button', { name: 'Sync with theme' } )
+		).toBeVisible();
+		expect(
+			screen.getByRole( 'button', { name: 'Undo changes' } )
+		).toBeVisible();
+	} );
+
+	it( 'syncs theme defaults and re-enables auto-sync', async () => {
+		renderWithThemeDefaults();
+		changeAccent();
+
+		await userEvent.click(
+			screen.getByRole( 'button', { name: 'Sync with theme' } )
+		);
+
+		expectColors( themeColors );
+		expect( autoSyncInput ).toHaveValue( 'yes' );
+	} );
+
+	it( 'restores the initial colors and auto-sync setting with Undo', async () => {
+		renderWithThemeDefaults();
+		changeAccent();
+
+		await userEvent.click(
+			screen.getByRole( 'button', { name: 'Undo changes' } )
+		);
+
+		expectColors( initialColors );
+		expect( autoSyncInput ).toHaveValue( 'yes' );
+		expect(
+			screen.queryByRole( 'button', { name: 'Undo changes' } )
+		).not.toBeInTheDocument();
+	} );
+} );
diff --git a/plugins/woocommerce/client/admin/client/settings-email/__tests__/settings-email-color-palette-slotfill.test.tsx b/plugins/woocommerce/client/admin/client/settings-email/__tests__/settings-email-color-palette-slotfill.test.tsx
new file mode 100644
index 00000000000..6f3d4f92aab
--- /dev/null
+++ b/plugins/woocommerce/client/admin/client/settings-email/__tests__/settings-email-color-palette-slotfill.test.tsx
@@ -0,0 +1,81 @@
+/**
+ * External dependencies
+ */
+import { registerPlugin } from '@wordpress/plugins';
+
+/**
+ * Internal dependencies
+ */
+import { registerSettingsEmailColorPaletteFill } from '../settings-email-color-palette-slotfill';
+
+jest.mock( '@wordpress/plugins', () => ( {
+	registerPlugin: jest.fn(),
+} ) );
+
+const registerPluginMock = registerPlugin as jest.Mock;
+
+// The attribute shape WC_Settings_Emails::email_color_palette() prints.
+const phpDefaultColors = {
+	base: '#720eec',
+	bg: '#f7f7f7',
+	body_bg: '#ffffff',
+	body_text: '#1e1e1e',
+	footer_text: '#787c82',
+};
+
+const renderMount = ( hasThemeJson: boolean, autoSync: string ) => {
+	document.body.innerHTML = `
+		<div
+			id="wc_settings_email_color_palette_slotfill"
+			data-default-colors='${ JSON.stringify( phpDefaultColors ) }'
+			${ hasThemeJson ? 'data-has-theme-json' : '' }
+		></div>
+		<input type="hidden" id="woocommerce_email_auto_sync_with_theme" value="${ autoSync }" />
+	`;
+};
+
+const renderedFillProps = () => {
+	expect( registerPluginMock ).toHaveBeenCalledTimes( 1 );
+	const [ , settings ] = registerPluginMock.mock.calls[ 0 ];
+	return settings.render().props;
+};
+
+describe( 'registerSettingsEmailColorPaletteFill', () => {
+	afterEach( () => {
+		document.body.innerHTML = '';
+		registerPluginMock.mockClear();
+	} );
+
+	it.each( [
+		[ 'with', true, 'yes' ],
+		[ 'without', false, 'no' ],
+	] )(
+		'reads the PHP mount attributes %s theme.json',
+		( _label, hasThemeJson, autoSync ) => {
+			renderMount( hasThemeJson, autoSync );
+
+			registerSettingsEmailColorPaletteFill();
+
+			expect( renderedFillProps() ).toMatchObject( {
+				autoSync: autoSync === 'yes',
+				defaultColors: {
+					baseColor: '#720eec',
+					bgColor: '#f7f7f7',
+					bodyBgColor: '#ffffff',
+					bodyTextColor: '#1e1e1e',
+					footerTextColor: '#787c82',
+				},
+				hasThemeJson,
+			} );
+		}
+	);
+
+	it( 'does not register the fill without the auto-sync input', () => {
+		document.body.innerHTML =
+			'<div id="wc_settings_email_color_palette_slotfill"></div>';
+
+		registerSettingsEmailColorPaletteFill();
+
+		expect( registerPluginMock ).not.toHaveBeenCalled();
+	} );
+} );
diff --git a/plugins/woocommerce/client/admin/client/settings-email/__tests__/settings-email-preview-controls.test.tsx b/plugins/woocommerce/client/admin/client/settings-email/__tests__/settings-email-preview-controls.test.tsx
new file mode 100644
index 00000000000..fa77d04b261
--- /dev/null
+++ b/plugins/woocommerce/client/admin/client/settings-email/__tests__/settings-email-preview-controls.test.tsx
@@ -0,0 +1,207 @@
+/**
+ * External dependencies
+ */
+import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import apiFetch from '@wordpress/api-fetch';
+
+/**
+ * Internal dependencies
+ */
+import { EmailPreviewHeader } from '../settings-email-preview-header';
+import { emailPreviewNonce } from '../settings-email-preview-nonce';
+import { EmailPreviewType } from '../settings-email-preview-type';
+
+jest.mock( '@wordpress/api-fetch', () => jest.fn() );
+jest.mock( '../settings-email-preview-nonce', () => ( {
+	emailPreviewNonce: jest.fn(),
+} ) );
+
+const apiFetchMock = apiFetch as unknown as jest.Mock;
+const emailPreviewNonceMock = emailPreviewNonce as jest.MockedFunction<
+	typeof emailPreviewNonce
+>;
+
+const processingOrderType = 'WC_Email_Customer_Processing_Order';
+const resetPasswordType = 'WC_Email_Customer_Reset_Password';
+
+describe( 'Email preview controls', () => {
+	afterEach( () => {
+		apiFetchMock.mockReset();
+		emailPreviewNonceMock.mockReset();
+	} );
+
+	it( 'selects a different preview type', async () => {
+		const setEmailType = jest.fn();
+
+		render(
+			<EmailPreviewType
+				emailTypes={ [
+					{ label: 'Processing order', value: processingOrderType },
+					{ label: 'Reset password', value: resetPasswordType },
+				] }
+				emailType={ processingOrderType }
+				setEmailType={ setEmailType }
+			/>
+		);
+
+		const previewType = screen.getByRole( 'combobox', {
+			name: 'Email preview type',
+		} );
+		expect( previewType ).toHaveValue( processingOrderType );
+
+		await userEvent.selectOptions( previewType, resetPasswordType );
+
+		expect( setEmailType ).toHaveBeenCalledTimes( 1 );
+		expect( setEmailType.mock.calls[ 0 ][ 0 ] ).toBe( resetPasswordType );
+	} );
+} );
+
+describe( 'Email preview header', () => {
+	let settingsFixture = document.createElement( 'div' );
+	let fromNameInput = document.createElement( 'input' );
+	let fromAddressInput = document.createElement( 'input' );
+	let subjectInput = document.createElement( 'input' );
+	let unmount: undefined | ( () => void );
+
+	const appendSettingInput = (
+		id: string,
+		labelText: string,
+		value: string
+	) => {
+		const label = document.createElement( 'label' );
+		const input = document.createElement( 'input' );
+
+		label.htmlFor = id;
+		label.textContent = labelText;
+		input.id = id;
+		input.value = value;
+		settingsFixture.append( label, input );
+
+		return input;
+	};
+
+	beforeEach( () => {
+		unmount = undefined;
+		settingsFixture = document.createElement( 'div' );
+		settingsFixture.setAttribute( 'aria-label', 'Email settings' );
+		document.body.appendChild( settingsFixture );
+
+		fromNameInput = appendSettingInput(
+			'woocommerce_email_from_name',
+			'From name',
+			'Acme Store'
+		);
+		fromAddressInput = appendSettingInput(
+			'woocommerce_email_from_address',
+			'From address',
+			'orders@example.com'
+		);
+		subjectInput = appendSettingInput(
+			'woocommerce_customer_processing_order_subject',
+			'Email subject',
+			'Order received'
+		);
+
+		emailPreviewNonceMock.mockReturnValue( 'preview-nonce' );
+	} );
+
+	afterEach( () => {
+		unmount?.();
+		settingsFixture.remove();
+		apiFetchMock.mockReset();
+		emailPreviewNonceMock.mockReset();
+	} );
+
+	it( 'updates sender values from setting change events', async () => {
+		apiFetchMock.mockResolvedValue( { subject: 'Processing order' } );
+
+		( { unmount } = render(
+			<EmailPreviewHeader emailType={ processingOrderType } />
+		) );
+
+		await screen.findByRole( 'heading', { name: 'Processing order' } );
+		const sender = screen.getByText( /Acme Store/ );
+		expect( sender ).toHaveTextContent( 'Acme Store <orders@example.com>' );
+
+		fireEvent.change( fromNameInput, {
+			target: { value: 'Acme Warehouse' },
+		} );
+
+		await waitFor( () =>
+			expect( sender ).toHaveTextContent(
+				'Acme Warehouse <orders@example.com>'
+			)
+		);
+
+		fireEvent.change( fromAddressInput, {
+			target: { value: 'warehouse@example.com' },
+		} );
+
+		await waitFor( () =>
+			expect( sender ).toHaveTextContent(
+				'Acme Warehouse <warehouse@example.com>'
+			)
+		);
+	} );
+
+	it( 'refreshes the preview subject from settings events', async () => {
+		apiFetchMock
+			.mockResolvedValueOnce( { subject: 'Processing order received' } )
+			.mockResolvedValueOnce( { subject: 'Updated processing order' } );
+		const subjectUpdatedListener = jest.fn();
+		subjectInput.addEventListener(
+			'subject-updated',
+			subjectUpdatedListener
+		);
+
+		try {
+			( { unmount } = render(
+				<EmailPreviewHeader emailType={ processingOrderType } />
+			) );
+
+			await screen.findByRole( 'heading', {
+				name: 'Processing order received',
+			} );
+			expect( apiFetchMock ).toHaveBeenCalledWith( {
+				path: `wc-admin-email/settings/email/preview-subject?type=${ processingOrderType }&nonce=preview-nonce`,
+			} );
+			await waitFor( () =>
+				expect( subjectUpdatedListener ).toHaveBeenCalledTimes( 1 )
+			);
+			subjectUpdatedListener.mockClear();
+
+			fireEvent( subjectInput, new Event( 'transient-saved' ) );
+
+			await screen.findByRole( 'heading', {
+				name: 'Updated processing order',
+			} );
+			expect( apiFetchMock ).toHaveBeenLastCalledWith( {
+				path: `wc-admin-email/settings/email/preview-subject?type=${ processingOrderType }&nonce=preview-nonce`,
+			} );
+			expect( subjectUpdatedListener ).toHaveBeenCalledTimes( 1 );
+		} finally {
+			subjectInput.removeEventListener(
+				'subject-updated',
+				subjectUpdatedListener
+			);
+		}
+	} );
+
+	it( 'requests the preview subject once after a transient save', async () => {
+		apiFetchMock.mockResolvedValue( { subject: 'Processing order' } );
+
+		( { unmount } = render(
+			<EmailPreviewHeader emailType={ processingOrderType } />
+		) );
+
+		await screen.findByRole( 'heading', { name: 'Processing order' } );
+		apiFetchMock.mockClear();
+
+		fireEvent( subjectInput, new Event( 'transient-saved' ) );
+
+		await waitFor( () =>
+			expect( apiFetchMock ).toHaveBeenCalledTimes( 1 )
+		);
+	} );
+} );
diff --git a/plugins/woocommerce/tests/e2e/tests/email/settings-email.spec.ts b/plugins/woocommerce/tests/e2e/tests/email/settings-email.spec.ts
index 9a572ceae9e..558fae83d15 100644
--- a/plugins/woocommerce/tests/e2e/tests/email/settings-email.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/email/settings-email.spec.ts
@@ -7,6 +7,7 @@ import { test, expect, type Page } from '@playwright/test';
  * Internal dependencies
  */
 import { setFeatureEmailImprovementsFlag } from './helpers/set-email-improvements-feature-flag';
+import { disableEmailEditor } from '../email-editor/helpers/enable-email-editor-feature';
 import { tags } from '../../fixtures/fixtures';
 import { ADMIN_STATE_PATH } from '../../playwright.config';

@@ -21,157 +22,94 @@ test.describe( 'WooCommerce Email Settings', () => {

 	const storeName = 'WooCommerce Core E2E Test Suite';

-	test.afterAll( async ( { baseURL } ) => {
-		await setFeatureEmailImprovementsFlag( baseURL, 'no' );
+	test.beforeEach( async ( { baseURL } ) => {
+		await disableEmailEditor( baseURL );
 	} );

-	test( 'See email preview', async ( { page, baseURL } ) => {
+	test.afterAll( async ( { baseURL } ) => {
 		await setFeatureEmailImprovementsFlag( baseURL, 'no' );
-		const emailPreviewElement =
-			'#wc_settings_email_preview_slotfill iframe';
-		const emailSubjectElement = '.wc-settings-email-preview-header-subject';
-		const hasIframe = async () => {
-			return ( await page.locator( emailPreviewElement ).count() ) > 0;
-		};
-		const iframeContains = async ( text: string ) => {
-			const iframe = page.frameLocator( emailPreviewElement );
-			return iframe.getByText( text );
-		};
-		const getSubject = async () => {
-			return await page.locator( emailSubjectElement ).textContent();
-		};
-
-		await page.goto( 'wp-admin/admin.php?page=wc-settings&tab=email' );
-		expect( await hasIframe() ).toBeTruthy();
-
-		// Email content
-		await expect(
-			await iframeContains( 'Thank you for your order' )
-		).toBeVisible();
-		// Email subject
-		await expect( await getSubject() ).toContain(
-			`Your ${ storeName } order has been received!`
-		);
-
-		// Select different email type and check that iframe is updated
-		await page
-			.getByLabel( 'Email preview type' )
-			.selectOption( 'Reset password' );
-		// Email content
-		await expect(
-			await iframeContains( 'Someone has requested a new password' )
-		).toBeVisible();
-		// Email subject
-		await expect( await getSubject() ).toContain(
-			`Password Reset Request for ${ storeName }`
-		);
+		await disableEmailEditor( baseURL );
 	} );

-	test(
-		'Email sender options live change in email preview',
-		{ tag: [ tags.COULD_BE_LOWER_LEVEL_TEST ] },
-		async ( { page, baseURL } ) => {
-			await setFeatureEmailImprovementsFlag( baseURL, 'no' );
-			await page.goto( 'wp-admin/admin.php?page=wc-settings&tab=email' );
-
-			const fromNameElement = '#woocommerce_email_from_name';
-			const fromAddressElement = '#woocommerce_email_from_address';
-			const senderElement = '.wc-settings-email-preview-header-sender';
-
-			const getSender = async () => {
-				return await page.locator( senderElement ).textContent();
-			};
-
-			// Verify initial sender contains fromName and fromAddress
-			const initialFromName = await page
-				.locator( fromNameElement )
-				.inputValue();
-			const initialFromAddress = await page
-				.locator( fromAddressElement )
-				.inputValue();
-			let sender = await getSender();
-			expect( sender ).toContain( initialFromName );
-			expect( sender ).toContain( initialFromAddress );
-
-			// Change the fromName and verify the sender updates
-			const newFromName = 'New Name';
-			await page.fill( fromNameElement, newFromName );
-			await page.locator( fromNameElement ).blur();
-			sender = await getSender();
-			expect( sender ).toContain( newFromName );
-			expect( sender ).toContain( initialFromAddress );
-
-			// Change the fromAddress and verify the sender updates
-			const newFromAddress = 'new@example.com';
-			await page.fill( fromAddressElement, newFromAddress );
-			await page.locator( fromAddressElement ).blur();
-			sender = await getSender();
-			expect( sender ).toContain( newFromName );
-			expect( sender ).toContain( newFromAddress );
-		}
-	);
-
 	test(
 		'Live preview when changing email settings',
-		{ tag: tags.SKIP_ON_EXTERNAL_ENV },
+		{ tag: [ tags.SKIP_ON_EXTERNAL_ENV ] },
 		async ( { page, baseURL } ) => {
 			await setFeatureEmailImprovementsFlag( baseURL, 'no' );
 			await page.goto( 'wp-admin/admin.php?page=wc-settings&tab=email' );

-			// Wait for the iframe content to load
 			const iframeSelector = '#wc_settings_email_preview_slotfill iframe';
+			const iframe = page.frameLocator( iframeSelector );
+			const subject = page.locator(
+				'.wc-settings-email-preview-header-subject'
+			);

 			const iframeContainsHtml = async ( code: string ) => {
-				const iframe = page.frameLocator( iframeSelector );
 				const content = await iframe.locator( 'html' ).innerHTML();
 				return content.includes( code );
 			};

+			await expect(
+				iframe.getByText( 'Thank you for your order' )
+			).toBeVisible();
+			await expect( subject ).toContainText(
+				`Your ${ storeName } order has been received!`
+			);
+
+			await page
+				.getByLabel( 'Email preview type' )
+				.selectOption( 'Reset password' );
+			await expect(
+				iframe.getByText( 'Someone has requested a new password' )
+			).toBeVisible();
+			await expect( subject ).toContainText(
+				`Password Reset Request for ${ storeName }`
+			);
+
 			const baseColorId = 'woocommerce_email_base_color';
 			const baseColorValue = '#012345';

-			// Change email base color
-			await page.fill( `#${ baseColorId }`, baseColorValue );
+			await page.locator( `#${ baseColorId }` ).fill( baseColorValue );

 			await page.evaluate(
 				async ( args ) => {
 					const input = document.getElementById( args.baseColorId );
-					// Blur the input to trigger value change event
-					input.blur();
-
-					const iframe = document.querySelector(
+					const iframeElement = document.querySelector(
 						args.iframeSelector
 					);
-
-					// Wait for the transient to be saved
-					await new Promise( ( resolve ) => {
-						input.addEventListener(
-							'transient-saved',
-							() => resolve(),
-							{ once: true }
+					if ( ! input || ! iframeElement ) {
+						throw new Error(
+							'The live-preview inputs must be mounted.'
 						);
-					} );
-
-					// Wait for the iframe with email preview to reload
-					return new Promise( ( resolve ) => {
-						iframe.addEventListener( 'load', () => resolve(), {
-							once: true,
-						} );
-					} );
+					}
+
+					await Promise.all( [
+						new Promise( ( resolve ) => {
+							input.addEventListener(
+								'transient-saved',
+								() => resolve(),
+								{ once: true }
+							);
+						} ),
+						new Promise( ( resolve ) => {
+							iframeElement.addEventListener(
+								'load',
+								() => resolve(),
+								{
+									once: true,
+								}
+							);
+						} ),
+						Promise.resolve().then( () => input.blur() ),
+					] );
 				},
 				{ baseColorId, iframeSelector }
 			);

-			// Check that the iframe contains the new value
-			await expect(
-				await iframeContainsHtml( baseColorValue )
-			).toBeTruthy();
+			expect( await iframeContainsHtml( baseColorValue ) ).toBeTruthy();

-			// Check that the iframe does not contain any of the new values after page reload
 			await page.reload();
-			await expect(
-				await iframeContainsHtml( baseColorValue )
-			).toBeFalsy();
+			expect( await iframeContainsHtml( baseColorValue ) ).toBeFalsy();
 		}
 	);

@@ -209,90 +147,6 @@ test.describe( 'WooCommerce Email Settings', () => {
 		await expect( message ).toBeVisible();
 	} );

-	test(
-		'See specific email preview',
-		{ tag: [ tags.COULD_BE_LOWER_LEVEL_TEST ] },
-		async ( { page } ) => {
-			const emailPreviewElement =
-				'#wc_settings_email_preview_slotfill iframe';
-			const emailSubjectElement =
-				'.wc-settings-email-preview-header-subject';
-			const hasIframe = async () => {
-				return (
-					( await page.locator( emailPreviewElement ).count() ) > 0
-				);
-			};
-			const iframeContains = async ( text: string ) => {
-				const iframe = page.frameLocator( emailPreviewElement );
-				return iframe.getByText( text );
-			};
-			const getSubject = async () => {
-				return await page.locator( emailSubjectElement ).textContent();
-			};
-
-			await page.goto(
-				'wp-admin/admin.php?page=wc-settings&tab=email&section=wc_email_customer_processing_order'
-			);
-			expect( await hasIframe() ).toBeTruthy();
-
-			// Email content
-			await expect(
-				await iframeContains( 'Thank you for your order' )
-			).toBeVisible();
-			// Email subject
-			await expect( await getSubject() ).toContain(
-				`Your ${ storeName } order has been received!`
-			);
-
-			// Email type selector should not be visible
-			await expect( page.getByLabel( 'Email preview type' ) ).toHaveCount(
-				0
-			);
-
-			// Change subject and observe it's changed in the preview
-			const newSubject = 'New subject';
-			const subjectId = 'woocommerce_customer_processing_order_subject';
-
-			await page.fill( `#${ subjectId }`, newSubject );
-			await page.evaluate( async ( inputId ) => {
-				const input = document.getElementById( inputId );
-				input.blur();
-
-				await new Promise( ( resolve ) => {
-					input.addEventListener(
-						'transient-saved',
-						() => resolve(),
-						{ once: true }
-					);
-				} );
-
-				return new Promise( ( resolve ) => {
-					input.addEventListener(
-						'subject-updated',
-						() => resolve(),
-						{ once: true }
-					);
-				} );
-			}, subjectId );
-			await expect( await getSubject() ).toContain( 'New subject' );
-
-			// Reset the subject to default value
-			await page.fill( `#${ subjectId }`, '' );
-			await page.evaluate( async ( inputId ) => {
-				const input = document.getElementById( inputId );
-				input.blur();
-
-				return await new Promise( ( resolve ) => {
-					input.addEventListener(
-						'transient-saved',
-						() => resolve(),
-						{ once: true }
-					);
-				} );
-			}, subjectId );
-		}
-	);
-
 	test( 'Choose image in email image url field', async ( { page } ) => {
 		const logoImageElement = '.wc-settings-email-logo-image';
 		const uploadIconElement = '.wc-settings-email-select-image-icon';
@@ -313,161 +167,4 @@ test.describe( 'WooCommerce Email Settings', () => {
 		await expect( page.locator( logoImageElement ) ).toBeHidden();
 		await expect( page.locator( uploadIconElement ) ).toBeVisible();
 	} );
-
-	test(
-		'See color palette settings',
-		{ tag: [ tags.COULD_BE_LOWER_LEVEL_TEST ] },
-		async ( { page } ) => {
-			await page.goto( 'wp-admin/admin.php?page=wc-settings&tab=email' );
-
-			await expect(
-				page.getByText( 'Color palette', { exact: true } )
-			).toBeVisible();
-			await expect(
-				page.getByText( 'Accent', { exact: true } )
-			).toBeVisible();
-			await expect(
-				page.getByText( 'Email background', { exact: true } )
-			).toBeVisible();
-			await expect(
-				page.getByText( 'Content background', { exact: true } )
-			).toBeVisible();
-			await expect(
-				page.getByText( 'Heading & text', { exact: true } )
-			).toBeVisible();
-			await expect(
-				page.getByText( 'Secondary text', { exact: true } )
-			).toBeVisible();
-
-			await expect(
-				page.getByText( 'Base color', { exact: true } )
-			).toHaveCount( 0 );
-			await expect(
-				page.getByText( 'Background color', { exact: true } )
-			).toHaveCount( 0 );
-			await expect(
-				page.getByText( 'Body background color', { exact: true } )
-			).toHaveCount( 0 );
-			await expect(
-				page.getByText( 'Body text color', { exact: true } )
-			).toHaveCount( 0 );
-			await expect(
-				page.getByText( 'Footer text color', { exact: true } )
-			).toHaveCount( 0 );
-		}
-	);
-
-	test(
-		'See font family setting',
-		{ tag: [ tags.COULD_BE_LOWER_LEVEL_TEST ] },
-		async ( { page } ) => {
-			await page.goto( 'wp-admin/admin.php?page=wc-settings&tab=email' );
-
-			const fontFamilyElement = page.getByLabel( 'Font family' );
-			await expect( fontFamilyElement ).toBeVisible();
-
-			// Test standard font selection
-			await fontFamilyElement.selectOption( 'Times New Roman' );
-
-			// Test theme font selection
-			// await fontFamilyElement.selectOption( 'Inter' );
-		}
-	);
-
-	test( 'See updated footer text field', async ( { page } ) => {
-		await page.goto( 'wp-admin/admin.php?page=wc-settings&tab=email' );
-		const footerTextLabel = page.locator(
-			'css=label[for="woocommerce_email_footer_text"]'
-		);
-		await expect( footerTextLabel ).toBeVisible();
-
-		const tooltip = footerTextLabel.locator( 'span.woocommerce-help-tip' );
-		await expect( tooltip ).toHaveAttribute(
-			'aria-label',
-			expect.stringContaining( '{store_address}' )
-		);
-		await expect( tooltip ).toHaveAttribute(
-			'aria-label',
-			expect.stringContaining( '{store_email}' )
-		);
-	} );
-
-	test( 'Reset color palette with a feature flag', async ( {
-		page,
-		baseURL,
-	} ) => {
-		const resetButtonElement = '.wc-settings-email-color-palette-buttons';
-
-		await setFeatureEmailImprovementsFlag( baseURL, 'yes' );
-		await page.goto( 'wp-admin/admin.php?page=wc-settings&tab=email' );
-
-		await expect( page.locator( resetButtonElement ) ).toBeVisible();
-
-		// Change colors to make sure Reset button is active
-		const dummyColor = '#abcdef';
-		await page.fill( '#woocommerce_email_base_color', dummyColor );
-		await page.fill( '#woocommerce_email_background_color', dummyColor );
-		await page.fill(
-			'#woocommerce_email_body_background_color',
-			dummyColor
-		);
-		await page.fill( '#woocommerce_email_text_color', dummyColor );
-		await page.fill( '#woocommerce_email_footer_text_color', dummyColor );
-
-		// Reset colors to defaults
-		await page
-			.locator( resetButtonElement )
-			.getByText( 'Sync with theme', { exact: true } )
-			.click();
-
-		// Verify colors are reset
-		await expect(
-			page.locator( '#woocommerce_email_base_color' )
-		).not.toHaveValue( dummyColor );
-		await expect(
-			page.locator( '#woocommerce_email_background_color' )
-		).not.toHaveValue( dummyColor );
-		await expect(
-			page.locator( '#woocommerce_email_body_background_color' )
-		).not.toHaveValue( dummyColor );
-		await expect(
-			page.locator( '#woocommerce_email_text_color' )
-		).not.toHaveValue( dummyColor );
-		await expect(
-			page.locator( '#woocommerce_email_footer_text_color' )
-		).not.toHaveValue( dummyColor );
-
-		// Change colors to make sure Undo button is active
-		await page.fill( '#woocommerce_email_base_color', dummyColor );
-		await page.fill( '#woocommerce_email_background_color', dummyColor );
-		await page.fill(
-			'#woocommerce_email_body_background_color',
-			dummyColor
-		);
-		await page.fill( '#woocommerce_email_text_color', dummyColor );
-		await page.fill( '#woocommerce_email_footer_text_color', dummyColor );
-
-		// Undo changes
-		await page
-			.locator( resetButtonElement )
-			.getByText( 'Undo changes', { exact: true } )
-			.click();
-
-		// Verify changes are undone
-		await expect(
-			page.locator( '#woocommerce_email_base_color' )
-		).not.toHaveValue( dummyColor );
-		await expect(
-			page.locator( '#woocommerce_email_background_color' )
-		).not.toHaveValue( dummyColor );
-		await expect(
-			page.locator( '#woocommerce_email_body_background_color' )
-		).not.toHaveValue( dummyColor );
-		await expect(
-			page.locator( '#woocommerce_email_text_color' )
-		).not.toHaveValue( dummyColor );
-		await expect(
-			page.locator( '#woocommerce_email_footer_text_color' )
-		).not.toHaveValue( dummyColor );
-	} );
 } );
diff --git a/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-emails-test.php b/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-emails-test.php
index 72feaa15ec2..79f3135e397 100644
--- a/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-emails-test.php
+++ b/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-emails-test.php
@@ -5,6 +5,11 @@
  * @package WooCommerce\Tests\Settings
  */

+declare( strict_types = 1 );
+
+use Automattic\WooCommerce\Internal\Admin\EmailPreview\EmailPreview;
+use Automattic\WooCommerce\Internal\Email\EmailColors;
+use Automattic\WooCommerce\Internal\Email\EmailFont;
 use Automattic\WooCommerce\Testing\Tools\CodeHacking\Hacks\StaticMockerHack;

 require_once __DIR__ . '/class-wc-settings-unit-test-case.php';
@@ -98,6 +103,175 @@ class WC_Settings_Emails_Test extends WC_Settings_Unit_Test_Case {
 		$this->assertEquals( $expected, $setting_ids_and_types );
 	}

+	/**
+	 * @testdox Default email settings expose the current color palette contract.
+	 */
+	public function test_get_default_settings_exposes_current_color_palette_contract(): void {
+		$settings       = ( new WC_Settings_Emails() )->get_settings_for_section( '' );
+		$settings_by_id = $this->index_settings_by_id( $settings );
+		$default_colors = EmailColors::get_default_colors();
+
+		$expected = array(
+			'woocommerce_email_base_color'            => array( 'Accent', $default_colors['base'] ),
+			'woocommerce_email_background_color'      => array( 'Email background', $default_colors['bg'] ),
+			'woocommerce_email_body_background_color' => array( 'Content background', $default_colors['body_bg'] ),
+			'woocommerce_email_text_color'            => array( 'Heading & text', $default_colors['body_text'] ),
+			'woocommerce_email_footer_text_color'     => array( 'Secondary text', $default_colors['footer_text'] ),
+		);
+
+		foreach ( $expected as $id => $contract ) {
+			list( $title, $default ) = $contract;
+			$this->assertSame( $title, $settings_by_id[ $id ]['title'] );
+			$this->assertSame( $default, $settings_by_id[ $id ]['default'] );
+		}
+
+		$titles = array_column( $settings, 'title' );
+		$this->assertEmpty(
+			array_intersect(
+				array( 'Base color', 'Background color', 'Body background color', 'Body text color', 'Footer text color' ),
+				$titles
+			)
+		);
+	}
+
+	/**
+	 * @testdox The email font setting renders every supported font and the selected value.
+	 */
+	public function test_email_font_family_setting_contract(): void {
+		$settings_by_id = $this->index_settings_by_id( ( new WC_Settings_Emails() )->get_settings_for_section( '' ) );
+		$setting        = $settings_by_id['woocommerce_email_font_family'];
+
+		$this->assertSame( 'Font family', $setting['title'] );
+		$this->assertSame( 'email_font_family', $setting['type'] );
+		$this->assertSame( 'Helvetica', $setting['default'] );
+
+		$setting['field_name'] = $setting['id'];
+		$setting['value']      = 'Georgia';
+
+		ob_start();
+		try {
+			( new WC_Settings_Emails() )->email_font_family( $setting );
+			$output = (string) ob_get_contents();
+		} finally {
+			ob_end_clean();
+		}
+
+		$document = $this->load_html_document( '<table>' . $output . '</table>' );
+		$select   = $this->get_element_by_id( $document, 'woocommerce_email_font_family' );
+
+		$options = $select->getElementsByTagName( 'option' );
+		$this->assertCount( count( EmailFont::$font ), $options );
+
+		$rendered_fonts = array();
+		$selected       = array();
+		foreach ( $options as $option ) {
+			$rendered_fonts[ $option->getAttribute( 'value' ) ] = $option->getAttribute( 'data-font-family' );
+			if ( $option->hasAttribute( 'selected' ) ) {
+				$selected[] = $option->getAttribute( 'value' );
+			}
+		}
+
+		$this->assertSame( EmailFont::$font, $rendered_fonts );
+		$this->assertSame( array( 'Georgia' ), $selected );
+	}
+
+	/**
+	 * @testdox The email footer setting exposes the current placeholder contract.
+	 */
+	public function test_email_footer_setting_contract(): void {
+		$settings_by_id = $this->index_settings_by_id( ( new WC_Settings_Emails() )->get_settings_for_section( '' ) );
+		$setting        = $settings_by_id['woocommerce_email_footer_text'];
+
+		$this->assertSame( 'Footer text', $setting['title'] );
+		$this->assertSame( 'textarea', $setting['type'] );
+		$this->assertSame( '{site_title}<br />{store_address}', $setting['default'] );
+		$this->assertSame( 'N/A', $setting['placeholder'] );
+		$this->assertStringContainsString( '{store_address}', $setting['desc'] );
+		$this->assertStringContainsString( '{store_email}', $setting['desc'] );
+		// desc_tip is what routes desc into the help tip the deleted E2E title read.
+		// Setting it to a string is a supported form that replaces the tooltip text,
+		// which would drop the placeholder hints while the two assertions above stay
+		// green, so pin the boolean rather than just the description.
+		$this->assertTrue( $setting['desc_tip'] );
+	}
+
+	/**
+	 * @testdox A single email preview renders its exact type, content settings, URL, and sender values.
+	 */
+	public function test_email_preview_single_contract(): void {
+		update_option( 'woocommerce_email_from_name', 'Woo Test Store' );
+		update_option( 'woocommerce_email_from_address', 'orders@example.com' );
+
+		$email = WC_Emails::instance()->get_emails()[ WC_Email_Customer_Processing_Order::class ];
+
+		ob_start();
+		try {
+			( new WC_Settings_Emails() )->email_preview_single( $email );
+			$output = (string) ob_get_contents();
+		} finally {
+			ob_end_clean();
+		}
+
+		$document = $this->load_html_document( $output );
+		$mount    = $this->get_element_by_id( $document, 'wc_settings_email_preview_slotfill' );
+
+		$this->assertSame(
+			array(
+				array(
+					'label' => $email->get_title(),
+					'value' => WC_Email_Customer_Processing_Order::class,
+				),
+			),
+			json_decode( $mount->getAttribute( 'data-email-types' ), true )
+		);
+		$this->assertSame(
+			EmailPreview::get_email_content_setting_ids( $email->id ),
+			json_decode( $mount->getAttribute( 'data-email-setting-ids' ), true )
+		);
+		$this->assertSame(
+			html_entity_decode( wp_nonce_url( admin_url( '?preview_woocommerce_mail=true' ), 'preview-mail' ) ),
+			$mount->getAttribute( 'data-preview-url' )
+		);
+		$this->assertSame( 'Woo Test Store', $this->get_element_by_id( $document, 'woocommerce_email_from_name' )->getAttribute( 'value' ) );
+		$this->assertSame( 'orders@example.com', $this->get_element_by_id( $document, 'woocommerce_email_from_address' )->getAttribute( 'value' ) );
+	}
+
+	/**
+	 * @testdox The email color palette prints the React mount the settings script reads, with the default colors and the theme.json flag.
+	 *
+	 * @testWith ["twentytwentyfour", true]
+	 *           ["storefront", false]
+	 *
+	 * @param string $theme          Theme to activate.
+	 * @param bool   $has_theme_json Whether that theme ships a theme.json.
+	 */
+	public function test_email_color_palette_mount_contract( string $theme, bool $has_theme_json ): void {
+		update_option( 'woocommerce_feature_email_improvements_enabled', 'yes' );
+		$original_theme = get_stylesheet();
+
+		// switch_theme() writes options the rollback reverts, but the active theme is
+		// also read back through in-memory caches, so put it back by hand.
+		switch_theme( $theme );
+		// The defaults follow the active theme's palette, so read them while it is active.
+		$expected_colors = EmailColors::get_default_colors( true );
+		ob_start();
+		try {
+			( new WC_Settings_Emails() )->email_color_palette( array( 'title' => 'Color palette' ) );
+			$output = (string) ob_get_contents();
+		} finally {
+			ob_end_clean();
+			switch_theme( $original_theme );
+		}
+
+		// The method opens a form table for the color fields that follow it.
+		$document = $this->load_html_document( $output . '</table>' );
+		$mount    = $this->get_element_by_id( $document, 'wc_settings_email_color_palette_slotfill' );
+
+		$this->assertSame( $expected_colors, json_decode( $mount->getAttribute( 'data-default-colors' ), true ) );
+		$this->assertSame( $has_theme_json, $mount->hasAttribute( 'data-has-theme-json' ) );
+		$this->assertSame( 'no', $this->get_element_by_id( $document, 'woocommerce_email_auto_sync_with_theme' )->getAttribute( 'value' ) );
+	}
+
 	/**
 	 * @testdox get_settings('') should return reply-to settings when block email editor is enabled.
 	 */
@@ -147,7 +321,7 @@ class WC_Settings_Emails_Test extends WC_Settings_Unit_Test_Case {
 		$sut->method( 'run_email_admin_options' )
 			->will(
 				$this->returnCallback(
-					function( $email ) use ( &$admin_options_invoked, &$actual_email ) {
+					function ( $email ) use ( &$admin_options_invoked, &$actual_email ) {
 						$admin_options_invoked = true;
 						$actual_email          = $email;
 					}
@@ -178,16 +352,16 @@ class WC_Settings_Emails_Test extends WC_Settings_Unit_Test_Case {
 		$email = WC_Emails::instance()->get_emails()[ WC_Email_New_Order::class ];

 		$emails = $this->getMockBuilder( WC_Emails::class )
-								 ->setMethods( array( 'get_emails' ) )
-								 ->getMock();
+								->setMethods( array( 'get_emails' ) )
+								->getMock();

 		$emails->method( 'get_emails' )
-						 ->willReturn( array( WC_Email_New_Order::class => $email ) );
+						->willReturn( array( WC_Email_New_Order::class => $email ) );

 		StaticMockerHack::add_method_mocks(
 			array(
 				'WC_Emails' => array(
-					'instance' => function() use ( $emails ) {
+					'instance' => function () use ( $emails ) {
 						return $emails;
 					},
 				),
@@ -195,13 +369,13 @@ class WC_Settings_Emails_Test extends WC_Settings_Unit_Test_Case {
 		);

 		$sut = $this->getMockBuilder( WC_Settings_Emails::class )
-					   ->setMethods( array( 'save_settings_for_current_section' ) )
-					   ->getMock();
+						->setMethods( array( 'save_settings_for_current_section' ) )
+						->getMock();

 		$sut->method( 'save_settings_for_current_section' )
 						->will(
 							$this->returnCallback(
-								function() use ( &$save_settings_for_current_section_invoked ) {
+								function () use ( &$save_settings_for_current_section_invoked ) {
 									$save_settings_for_current_section_invoked = true;
 								}
 							)
@@ -212,4 +386,59 @@ class WC_Settings_Emails_Test extends WC_Settings_Unit_Test_Case {
 		$this->assertEquals( $expect_save_settings_for_current_section, $save_settings_for_current_section_invoked );
 		$this->assertEquals( '' === $section_name ? 0 : 1, did_action( 'woocommerce_update_options_email_new_order' ) );
 	}
+
+	/**
+	 * Index settings that expose an ID.
+	 *
+	 * @param array[] $settings Settings definitions.
+	 * @return array<string, array> Settings keyed by ID.
+	 */
+	private function index_settings_by_id( array $settings ): array {
+		$indexed = array();
+
+		foreach ( $settings as $setting ) {
+			if ( ! empty( $setting['id'] ) ) {
+				$indexed[ $setting['id'] ] = $setting;
+			}
+		}
+
+		return $indexed;
+	}
+
+	/**
+	 * Load rendered HTML into a DOM document.
+	 *
+	 * @param string $html Rendered HTML.
+	 * @return DOMDocument Parsed document.
+	 */
+	private function load_html_document( string $html ): DOMDocument {
+		$document                = new DOMDocument();
+		$previous_libxml_setting = libxml_use_internal_errors( true );
+		$loaded                  = $document->loadHTML( '<!DOCTYPE html><html><body>' . $html . '</body></html>' );
+		libxml_clear_errors();
+		libxml_use_internal_errors( $previous_libxml_setting );
+
+		if ( ! $loaded ) {
+			throw new RuntimeException( 'Rendered email settings markup should be parseable HTML.' );
+		}
+
+		return $document;
+	}
+
+	/**
+	 * Get a required element from rendered settings markup.
+	 *
+	 * @param DOMDocument $document Parsed document.
+	 * @param string      $id       Element ID.
+	 * @return DOMElement Required element.
+	 */
+	private function get_element_by_id( DOMDocument $document, string $id ): DOMElement {
+		$element = $document->getElementById( $id );
+
+		if ( ! $element instanceof DOMElement ) {
+			throw new RuntimeException( 'Expected rendered element was not found.' );
+		}
+
+		return $element;
+	}
 }