From e08b2a4110a25d8ee2724150dec34a4d75318660 Mon Sep 17 00:00:00 2001 From: Lance Willett Date: Mon, 12 Oct 2026 12:08:48 +0200 Subject: [PATCH 1/3] Tests: Verify timezone persistence and restore the original setting --- .../general-settings-invalid-timezone.test.js | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/tests/e2e/specs/general-settings-invalid-timezone.test.js b/tests/e2e/specs/general-settings-invalid-timezone.test.js index 9dca6306a4767..cbc3139f6185f 100644 --- a/tests/e2e/specs/general-settings-invalid-timezone.test.js +++ b/tests/e2e/specs/general-settings-invalid-timezone.test.js @@ -2,16 +2,39 @@ import { test, expect } from '@wordpress/e2e-test-utils-playwright'; test.describe( 'Settings -> General', () => { const invalidTimezones = [ '', '0', 'Barry/Gary' ]; + let originalTimezone; + + test.beforeEach( async ( { admin, page } ) => { + originalTimezone = undefined; + await admin.visitAdminPage( '/options-general.php' ); + originalTimezone = await page.locator( '#timezone_string' ).inputValue(); + } ); + + test.afterEach( async ( { admin, page } ) => { + if ( originalTimezone === undefined ) { + return; + } + + // Restore through a fresh form, including numeric UTC-offset selections. + await admin.visitAdminPage( '/options-general.php' ); + await page.locator( '#timezone_string' ).selectOption( originalTimezone ); + await page.locator( '#submit' ).click(); + await expect( page.locator( '#setting-error-settings_updated' ) ).toContainText( 'Settings saved.' ); + + await admin.visitAdminPage( '/options-general.php' ); + await expect( page.locator( '#timezone_string' ) ).toHaveValue( originalTimezone ); + } ); for ( const invalidTimezone of invalidTimezones ) { test( `Does not allow saving an invalid timezone string with "${invalidTimezone}"`, async ( { admin, page } ) => { - await admin.visitAdminPage( '/options-general.php' ); + const validTimezone = originalTimezone === 'Europe/Lisbon' ? 'Africa/Libreville' : 'Europe/Lisbon'; - // Set the timezone to a valid value. - await page.locator( '#timezone_string' ).evaluate( timezone => timezone.value = 'Europe/Lisbon' ); + // Set the timezone to a different valid value. + await page.locator( '#timezone_string' ).selectOption( validTimezone ); // Save changes. await page.locator( '#submit' ).click(); + await expect( page.locator( '#setting-error-settings_updated' ) ).toContainText( 'Settings saved.' ); // Set the timezone to an invalid value. await page.locator( '#timezone_string' ).evaluate( ( timezone, invalidTimezone ) => { @@ -22,7 +45,12 @@ test.describe( 'Settings -> General', () => { // Save changes. await page.locator( '#submit' ).click(); - await expect( page.locator( '#timezone_string' ) ).toHaveValue( 'Europe/Lisbon' ); + await expect( page.locator( '#setting-error-settings_updated' ) ).toContainText( 'The timezone you have entered is not valid. Please select a valid timezone.' ); + await expect( page.locator( '#timezone_string' ) ).toHaveValue( validTimezone ); + + // Verify the saved value on a fresh page, not just the submit response. + await admin.visitAdminPage( '/options-general.php' ); + await expect( page.locator( '#timezone_string' ) ).toHaveValue( validTimezone ); } ); } } ); From 9d6a06c4ca462c4b701571bc210c307bf6af6451 Mon Sep 17 00:00:00 2001 From: Lance Willett Date: Fri, 16 Oct 2026 10:29:16 -0700 Subject: [PATCH 2/3] Build/Test Tools: Preserve stored timezone options in E2E cleanup. --- .../general-settings-invalid-timezone.test.js | 49 +++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/tests/e2e/specs/general-settings-invalid-timezone.test.js b/tests/e2e/specs/general-settings-invalid-timezone.test.js index cbc3139f6185f..cda0b548882f9 100644 --- a/tests/e2e/specs/general-settings-invalid-timezone.test.js +++ b/tests/e2e/specs/general-settings-invalid-timezone.test.js @@ -1,33 +1,52 @@ +import { execFileSync } from 'node:child_process'; +import { join } from 'node:path'; import { test, expect } from '@wordpress/e2e-test-utils-playwright'; +function timezoneOptions( values ) { + const restore = values === undefined ? '' : ` + $values = json_decode( base64_decode( '${ Buffer.from( JSON.stringify( values ) ).toString( 'base64' ) }' ), true ); + foreach ( $values as $option => $value ) { + update_option( $option, $value ); + } + `; + + return JSON.parse( execFileSync( process.execPath, [ + join( process.cwd(), 'tools/local-env/scripts/docker.js' ), + 'exec', '--user', 'wp_php', 'cli', 'wp', 'eval', + ` + // Read and restore the stored offset, not the value calculated for a named timezone. + remove_filter( 'pre_option_gmt_offset', 'wp_timezone_override_offset' ); + ${ restore } + echo wp_json_encode( array( + 'timezone_string' => get_option( 'timezone_string' ), + 'gmt_offset' => get_option( 'gmt_offset' ), + ) ); + `, + ], { encoding: 'utf-8' } ) ); +} + test.describe( 'Settings -> General', () => { const invalidTimezones = [ '', '0', 'Barry/Gary' ]; - let originalTimezone; + let originalOptions; - test.beforeEach( async ( { admin, page } ) => { - originalTimezone = undefined; + test.beforeEach( async ( { admin } ) => { + originalOptions = undefined; + originalOptions = timezoneOptions(); await admin.visitAdminPage( '/options-general.php' ); - originalTimezone = await page.locator( '#timezone_string' ).inputValue(); } ); - test.afterEach( async ( { admin, page } ) => { - if ( originalTimezone === undefined ) { + test.afterEach( async () => { + if ( originalOptions === undefined ) { return; } - // Restore through a fresh form, including numeric UTC-offset selections. - await admin.visitAdminPage( '/options-general.php' ); - await page.locator( '#timezone_string' ).selectOption( originalTimezone ); - await page.locator( '#submit' ).click(); - await expect( page.locator( '#setting-error-settings_updated' ) ).toContainText( 'Settings saved.' ); - - await admin.visitAdminPage( '/options-general.php' ); - await expect( page.locator( '#timezone_string' ) ).toHaveValue( originalTimezone ); + // The settings form can normalize stored UTC offsets, so restore the options directly. + expect( timezoneOptions( originalOptions ) ).toEqual( originalOptions ); } ); for ( const invalidTimezone of invalidTimezones ) { test( `Does not allow saving an invalid timezone string with "${invalidTimezone}"`, async ( { admin, page } ) => { - const validTimezone = originalTimezone === 'Europe/Lisbon' ? 'Africa/Libreville' : 'Europe/Lisbon'; + const validTimezone = originalOptions.timezone_string === 'Europe/Lisbon' ? 'Africa/Libreville' : 'Europe/Lisbon'; // Set the timezone to a different valid value. await page.locator( '#timezone_string' ).selectOption( validTimezone ); From defad98462479c256c7da0ebc9ee434dffe31047 Mon Sep 17 00:00:00 2001 From: Lance Willett Date: Mon, 19 Oct 2026 14:28:04 -0700 Subject: [PATCH 3/3] Tests: Target the browser site when restoring timezone options. --- .../general-settings-invalid-timezone.test.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/e2e/specs/general-settings-invalid-timezone.test.js b/tests/e2e/specs/general-settings-invalid-timezone.test.js index cda0b548882f9..5e3fd653066a9 100644 --- a/tests/e2e/specs/general-settings-invalid-timezone.test.js +++ b/tests/e2e/specs/general-settings-invalid-timezone.test.js @@ -2,7 +2,7 @@ import { execFileSync } from 'node:child_process'; import { join } from 'node:path'; import { test, expect } from '@wordpress/e2e-test-utils-playwright'; -function timezoneOptions( values ) { +function timezoneOptions( baseURL, values ) { const restore = values === undefined ? '' : ` $values = json_decode( base64_decode( '${ Buffer.from( JSON.stringify( values ) ).toString( 'base64' ) }' ), true ); foreach ( $values as $option => $value ) { @@ -12,7 +12,7 @@ function timezoneOptions( values ) { return JSON.parse( execFileSync( process.execPath, [ join( process.cwd(), 'tools/local-env/scripts/docker.js' ), - 'exec', '--user', 'wp_php', 'cli', 'wp', 'eval', + 'exec', '--user', 'wp_php', 'cli', 'wp', `--url=${ baseURL }`, 'eval', ` // Read and restore the stored offset, not the value calculated for a named timezone. remove_filter( 'pre_option_gmt_offset', 'wp_timezone_override_offset' ); @@ -22,26 +22,26 @@ function timezoneOptions( values ) { 'gmt_offset' => get_option( 'gmt_offset' ), ) ); `, - ], { encoding: 'utf-8' } ) ); + ], { encoding: 'utf-8', env: { ...process.env, DOTENV_CONFIG_QUIET: 'true' } } ) ); } test.describe( 'Settings -> General', () => { const invalidTimezones = [ '', '0', 'Barry/Gary' ]; let originalOptions; - test.beforeEach( async ( { admin } ) => { + test.beforeEach( async ( { admin, baseURL } ) => { originalOptions = undefined; - originalOptions = timezoneOptions(); + originalOptions = timezoneOptions( baseURL ); await admin.visitAdminPage( '/options-general.php' ); } ); - test.afterEach( async () => { + test.afterEach( async ( { baseURL } ) => { if ( originalOptions === undefined ) { return; } // The settings form can normalize stored UTC offsets, so restore the options directly. - expect( timezoneOptions( originalOptions ) ).toEqual( originalOptions ); + expect( timezoneOptions( baseURL, originalOptions ) ).toEqual( originalOptions ); } ); for ( const invalidTimezone of invalidTimezones ) {