From 2ea7f4ad7c24c216f05f4116d290f5f7703d33db Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 14:03:29 +0300 Subject: [PATCH 01/16] chore: read cloud rest test token from the active connection (cherry picked from commit e45500543e2b9719746b96f7be2ccabced5b9a5b) --- tests/unit/REST_API/REST_API_Cloud_Test.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/unit/REST_API/REST_API_Cloud_Test.php b/tests/unit/REST_API/REST_API_Cloud_Test.php index 2fd99e45..409a76db 100644 --- a/tests/unit/REST_API/REST_API_Cloud_Test.php +++ b/tests/unit/REST_API/REST_API_Cloud_Test.php @@ -151,7 +151,7 @@ public function mock_cloud_search_request( $preempt, array $parsed_args, string */ private function make_request( array $params ): WP_REST_Response { $request = new WP_REST_Request( 'GET', $this->endpoint ); - $request->add_header( 'Access-Control', 'csc-1a2b3c4d5e6f7g8h9i0j' ); + $request->add_header( 'Access-Control', $this->get_connection_token() ); foreach ( $params as $key => $value ) { $request->set_param( $key, $value ); @@ -160,6 +160,20 @@ private function make_request( array $params ): WP_REST_Response { return rest_do_request( $request ); } + /** + * Read the active cloud connection's local token. + * + * @return string + */ + private function get_connection_token(): string { + $plugin = \Code_Snippets\code_snippets(); + + $property = new \ReflectionProperty( $plugin, 'cloud_connection' ); + $property->setAccessible( true ); + + return $property->getValue( $plugin )->get_local_token(); + } + /** * The cloud REST endpoint uses the snippets Screen Options value when per_page is omitted. * From bdcbf782ecb4881067e97326c7245ab9c8e19653 Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 15:41:20 +0300 Subject: [PATCH 02/16] fix: derive playwright plugin slug from wp-env config (cherry picked from commit f4cef532ae309dbfe940e3d87226e115eb6887c0) --- scripts/test-setup-playwright.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/scripts/test-setup-playwright.ts b/scripts/test-setup-playwright.ts index 340f3baf..7ebdcd3b 100644 --- a/scripts/test-setup-playwright.ts +++ b/scripts/test-setup-playwright.ts @@ -1,6 +1,8 @@ #!/usr/bin/env ts-node import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { resolve } from 'node:path' const run = (cmd: string, args: readonly string[]) => { execFileSync(cmd, args, { stdio: 'inherit' }) @@ -8,6 +10,18 @@ const run = (cmd: string, args: readonly string[]) => { const runWpEnvCli = (args: readonly string[]) => run('npx', ['wp-env', 'run', 'cli', ...args]) +const getPluginSlug = (): string => { + const prefix = 'wp-content/plugins/' + const config = <{ mappings?: Record }>JSON.parse(readFileSync(resolve(process.cwd(), '.wp-env.json'), 'utf8')) + const mapping = Object.keys(config.mappings ?? {}).find(key => key.startsWith(prefix)) + + if (!mapping) { + throw new Error('No plugin mapping found in .wp-env.json') + } + + return mapping.slice(prefix.length) +} + const main = () => { // Ensure a clean slate for file-based execution tests: // - remove flat-file execution directory (stale indexes can break the WP site) @@ -16,7 +30,7 @@ const main = () => { // - delete all DB snippets with an E2E prefix (keeps list clean across runs) runWpEnvCli(['sh', '-lc', 'rm -rf wp-content/code-snippets']) - runWpEnvCli(['wp', 'plugin', 'activate', 'code-snippets']) + runWpEnvCli(['wp', 'plugin', 'activate', getPluginSlug()]) runWpEnvCli([ 'wp', From d84ff0164ce230794462a6876f63ebe3cabf3633 Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 15:41:01 +0300 Subject: [PATCH 03/16] fix: resolve webpack output and module paths from repo root (cherry picked from commit 3bf9fb0454298437a1a5b6d03a4fcf3c44f152ea) --- config/webpack/webpack-js.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/webpack/webpack-js.ts b/config/webpack/webpack-js.ts index fe741841..5c88861e 100644 --- a/config/webpack/webpack-js.ts +++ b/config/webpack/webpack-js.ts @@ -35,7 +35,7 @@ export const jsWebpackConfig: Configuration = { 'welcome': `${SOURCE_DIR}/welcome.ts` }, output: { - path: join(resolve(__dirname), '..', DEST_DIR), + path: join(resolve(__dirname), '..', '..', DEST_DIR), filename: '[name].js', clean: true }, @@ -58,7 +58,7 @@ export const jsWebpackConfig: Configuration = { ) }, resolve: { - modules: [resolve(__dirname, '..', 'node_modules')], + modules: [resolve(__dirname, '..', '..', 'node_modules')], extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'] }, module: { From a0846d79f4475a1d4f8504ae1098de77d636583d Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 16:45:37 +0300 Subject: [PATCH 04/16] chore: ignore local env file (cherry picked from commit 4c3b3c8ecee9562cd178dda3b2df26db8ca6b3e4) --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b07c6dfb..f6c368e0 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,6 @@ Thumbs.db .tmp tmp .continue -.codex \ No newline at end of file +.codex +# Local environment overrides (license keys, secrets) +/.env From ab2c00dda8f4ae7f5079040dbfd73082664a252a Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 16:45:21 +0300 Subject: [PATCH 05/16] chore: clear snippet state before each admin spec to prevent cross-test leakage --- tests/e2e/code-snippets-edit.spec.ts | 3 ++- tests/e2e/code-snippets-evaluation.spec.ts | 16 ++-------------- tests/e2e/code-snippets-list.spec.ts | 4 +++- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/tests/e2e/code-snippets-edit.spec.ts b/tests/e2e/code-snippets-edit.spec.ts index c04bab30..389b85c9 100644 --- a/tests/e2e/code-snippets-edit.spec.ts +++ b/tests/e2e/code-snippets-edit.spec.ts @@ -1,5 +1,5 @@ import { expect, test } from '@playwright/test' -import { SnippetsTestHelper } from './helpers/SnippetsTestHelper' +import { DEFAULT_E2E_SNIPPET_BASE_NAME, SnippetsTestHelper } from './helpers/SnippetsTestHelper' import { MESSAGES, SELECTORS, TIMEOUTS } from './helpers/constants' test.describe('Code Snippets Admin', () => { @@ -7,6 +7,7 @@ test.describe('Code Snippets Admin', () => { test.beforeEach(async ({ page }) => { helper = new SnippetsTestHelper(page) + await SnippetsTestHelper.cleanupSnippetsByPrefix(DEFAULT_E2E_SNIPPET_BASE_NAME) await helper.navigateToSnippetsAdmin() }) diff --git a/tests/e2e/code-snippets-evaluation.spec.ts b/tests/e2e/code-snippets-evaluation.spec.ts index 8f962c0f..7c65f3d8 100644 --- a/tests/e2e/code-snippets-evaluation.spec.ts +++ b/tests/e2e/code-snippets-evaluation.spec.ts @@ -92,21 +92,9 @@ test.describe('Code Snippets Evaluation', () => { test.beforeEach(async ({ page }) => { helper = new SnippetsTestHelper(page) snippetName = SnippetsTestHelper.makeUniqueSnippetName() - await helper.navigateToSnippetsAdmin() - // Ensure isolation: file-based execution runs from flat files, so we must delete old snippets - // via plugin operations (to keep flat files in sync), not via direct SQL. - await wpCli([ - 'eval', - ` - global $wpdb; - $table = $wpdb->prefix . 'snippets'; - $ids = $wpdb->get_col( - $wpdb->prepare( "SELECT id FROM {$table} WHERE name LIKE %s", "${DEFAULT_E2E_SNIPPET_BASE_NAME}%" ) - ); - foreach ( $ids as $id ) { \\Code_Snippets\\delete_snippet( intval( $id ), false ); } - ` - ]) + await SnippetsTestHelper.cleanupSnippetsByPrefix(DEFAULT_E2E_SNIPPET_BASE_NAME) + await helper.navigateToSnippetsAdmin() }) test('PHP snippet is evaluating correctly', async () => { diff --git a/tests/e2e/code-snippets-list.spec.ts b/tests/e2e/code-snippets-list.spec.ts index 24f8fd09..0875a080 100644 --- a/tests/e2e/code-snippets-list.spec.ts +++ b/tests/e2e/code-snippets-list.spec.ts @@ -1,6 +1,6 @@ import { readFileSync } from 'fs' import { expect, test } from '@playwright/test' -import { SnippetsTestHelper } from './helpers/SnippetsTestHelper' +import { DEFAULT_E2E_SNIPPET_BASE_NAME, SnippetsTestHelper } from './helpers/SnippetsTestHelper' import { SELECTORS } from './helpers/constants' import type { Page } from '@playwright/test' @@ -12,6 +12,7 @@ test.describe('Code Snippets List Page Actions', () => { test.beforeEach(async ({ page }) => { helper = new SnippetsTestHelper(page) snippetName = SnippetsTestHelper.makeUniqueSnippetName() + await SnippetsTestHelper.cleanupSnippetsByPrefix(DEFAULT_E2E_SNIPPET_BASE_NAME) await helper.navigateToSnippetsAdmin() await helper.createAndActivateSnippet({ @@ -325,6 +326,7 @@ test.describe('Manage table Screen Options', () => { test.beforeEach(async ({ page }) => { helper = new SnippetsTestHelper(page) snippetName = SnippetsTestHelper.makeUniqueSnippetName('E2E Screen Options') + await SnippetsTestHelper.cleanupSnippetsByPrefix(DEFAULT_E2E_SNIPPET_BASE_NAME) await helper.createAndActivateSnippet({ name: snippetName, code: "add_filter('show_admin_bar', '__return_false');" From 6ca49436282dc207423fddfc3ed6e2461c308af9 Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 17:19:53 +0300 Subject: [PATCH 06/16] chore: retry flaky playwright runs to stabilise admin interactions --- config/playwright/playwright.config.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/config/playwright/playwright.config.ts b/config/playwright/playwright.config.ts index b9b5cf6c..2f0ecff0 100644 --- a/config/playwright/playwright.config.ts +++ b/config/playwright/playwright.config.ts @@ -4,6 +4,9 @@ import { defineConfig, devices } from '@playwright/test' const WORKERS = 1 +const CI_RETRIES = 2 +const LOCAL_RETRIES = 1 + const TEST_TIMEOUT_SECONDS = 60 const ASSERT_TIMEOUT_SECONDS = 30 @@ -20,8 +23,8 @@ export default defineConfig({ snapshotPathTemplate: '{testDir}/{testFileDir}/{testFileName}-snapshots/{arg}-{platform}{ext}', fullyParallel: true, forbidOnly: !!process.env.CI, - retries: 0, - workers: process.env.CI ? WORKERS : WORKERS, + retries: process.env.CI ? CI_RETRIES : LOCAL_RETRIES, + workers: WORKERS, reporter: process.env.CI ? [ ['line'], From db707adc487ed3666fc9954369f7842aec39f233 Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 17:58:04 +0300 Subject: [PATCH 07/16] chore: extend quicknav setup hook timeout for slow snippet seeding --- tests/e2e/code-snippets-quicknav-admin-bar.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e/code-snippets-quicknav-admin-bar.spec.ts b/tests/e2e/code-snippets-quicknav-admin-bar.spec.ts index 366f979d..cb662cb6 100644 --- a/tests/e2e/code-snippets-quicknav-admin-bar.spec.ts +++ b/tests/e2e/code-snippets-quicknav-admin-bar.spec.ts @@ -16,6 +16,7 @@ test.describe('Admin Bar Snippets QuickNav', () => { let inactiveA: string test.beforeAll(async () => { + test.setTimeout(QUICKNAV_TEST_TIMEOUT_MS) await SnippetsTestHelper.setAdminBarQuickNavSettings({ enabled: true, perPage: QUICKNAV_PER_PAGE }) await SnippetsTestHelper.cleanupSnippetsByPrefix(QUICKNAV_PREFIX) From 2dfa317fe0176e01bad8ce84cfd33b92ed8c72cf Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 17:58:41 +0300 Subject: [PATCH 08/16] chore: confirm snippet save registered before asserting success --- tests/e2e/helpers/SnippetsTestHelper.ts | 32 +++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/tests/e2e/helpers/SnippetsTestHelper.ts b/tests/e2e/helpers/SnippetsTestHelper.ts index 90eb1179..62d9d7c2 100644 --- a/tests/e2e/helpers/SnippetsTestHelper.ts +++ b/tests/e2e/helpers/SnippetsTestHelper.ts @@ -19,6 +19,7 @@ const RANDOM_RADIX = 36 const RANDOM_SLICE_START = 2 const RANDOM_SLICE_END = 7 const CLICK_RETRIES = 3 +const SAVE_CONFIRM_RETRIES = 3 const AT_LEAST_ONE = 1 const getErrorMessage = (error: unknown): string => { @@ -288,7 +289,7 @@ export class SnippetsTestHelper { if ('save_and_activate' === action) { const activateButton = this.page.locator(BUTTONS.SAVE_AND_ACTIVATE).first() if (await activateButton.isVisible().catch(() => false)) { - await this.clickButton(/^Save and Activate$/i) + await this.clickSaveAndConfirm(/^Save and Activate$/i) return } @@ -297,7 +298,7 @@ export class SnippetsTestHelper { if (await inactiveToggle.isVisible().catch(() => false)) { await inactiveToggle.click({ timeout: TIMEOUTS.DEFAULT, force: true }) } - await this.clickButton(/^Save Snippet$/i) + await this.clickSaveAndConfirm(/^Save Snippet$/i) return } @@ -312,11 +313,34 @@ export class SnippetsTestHelper { await statusToggle.click({ timeout: TIMEOUTS.DEFAULT, force: true }) } } - await this.clickButton(/^Save Snippet$/i) + await this.clickSaveAndConfirm(/^Save Snippet$/i) return } - await this.clickButton(/^Save Snippet$/i) + await this.clickSaveAndConfirm(/^Save Snippet$/i) + } + + private async clickSaveAndConfirm(name: RegExp): Promise { + for (let attempt = 0; SAVE_CONFIRM_RETRIES > attempt; attempt++) { + await this.clickButton(name) + + const confirmed = await this.page.locator(SELECTORS.SUCCESS_MESSAGE).first() + .waitFor({ state: 'visible', timeout: TIMEOUTS.DEFAULT }) + .then(() => true) + .catch(() => false) + + if (confirmed) { + return + } + + const buttonStillPresent = await this.page.getByRole('button', { name }).first() + .isVisible() + .catch(() => false) + + if (!buttonStillPresent) { + return + } + } } /** From a2d80465875a9689501f4546458f16c4150205ca Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 18:14:02 +0300 Subject: [PATCH 09/16] chore: enable subsite snippets menu for multisite unit tests --- tests/unit/UnitTestCase.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/unit/UnitTestCase.php b/tests/unit/UnitTestCase.php index 934b5b2a..17e411b6 100644 --- a/tests/unit/UnitTestCase.php +++ b/tests/unit/UnitTestCase.php @@ -9,4 +9,18 @@ */ class UnitTestCase extends WP_UnitTestCase { + /** + * Set up before each test. + * + * @return void + */ + public function set_up() { + parent::set_up(); + + if ( is_multisite() ) { + $menu_items = get_site_option( 'menu_items', [] ); + $menu_items['snippets'] = 1; + update_site_option( 'menu_items', $menu_items ); + } + } } From b00b5c739be5574cb586e29c928ae7da38454db3 Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 18:35:20 +0300 Subject: [PATCH 10/16] fix: read the inserted snippet from its own network table (cherry picked from commit e196a1498c6cebd8e24dc0fd7d2baef044220c28) --- src/php/snippet-ops.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index 4f537566..e495774e 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -688,7 +688,7 @@ function save_snippet( $snippet ): ?Snippet { } $snippet->id = $wpdb->insert_id; - $updated = get_snippet( $snippet->id ); + $updated = get_snippet( $snippet->id, $snippet->network ); $updated->code_error = $snippet->code_error; $updated->code_error_trace = $snippet->code_error_trace; do_action( 'code_snippets/create_snippet', $updated, $table ); From ccef0156442cc7e0ca6e30572efefb9b7dc262ac Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 18:35:35 +0300 Subject: [PATCH 11/16] fix: treat unrecognised network scope values as network scoped (cherry picked from commit c1dd8a1827247ecde8ff902ef84ead6fffb147ba) --- src/php/REST_API/Snippets/Snippets_REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/php/REST_API/Snippets/Snippets_REST_Controller.php b/src/php/REST_API/Snippets/Snippets_REST_Controller.php index f5823db3..1117ce41 100644 --- a/src/php/REST_API/Snippets/Snippets_REST_Controller.php +++ b/src/php/REST_API/Snippets/Snippets_REST_Controller.php @@ -241,7 +241,7 @@ private function is_network_scoped_request( $request ): bool { } if ( is_string( $network ) ) { - return in_array( strtolower( $network ), [ '1', 'true', 'yes' ], true ); + return ! in_array( strtolower( $network ), [ '0', 'false', 'no', '' ], true ); } return (bool) $network; From 93b0a8b38e9901c577433ad1029dbe7406c79bdb Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 18:35:49 +0300 Subject: [PATCH 12/16] chore: pass the download payload into snippet resolution (cherry picked from commit eb386aa73fa66f265814dfc78b4130b163fb99ec) --- src/php/Admin/Menus/Manage_Menu.php | 8 +++++--- tests/unit/Admin/Menus/Manage_Menu_Test.php | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/php/Admin/Menus/Manage_Menu.php b/src/php/Admin/Menus/Manage_Menu.php index b9e82a00..bf7252cc 100644 --- a/src/php/Admin/Menus/Manage_Menu.php +++ b/src/php/Admin/Menus/Manage_Menu.php @@ -467,7 +467,8 @@ public function handle_bulk_download_request(): void { $this->send_download_error( __( 'The download request is no longer valid. Please refresh and try again.', 'code-snippets' ), 403 ); } - $snippets = $this->get_requested_download_snippets(); + $snippets_json = wp_unslash( filter_input( INPUT_POST, 'snippets', FILTER_DEFAULT ) ?? '' ); + $snippets = $this->get_requested_download_snippets( $snippets_json ); if ( $snippets instanceof WP_Error ) { $status = $snippets->get_error_data( 'status' ); @@ -505,10 +506,11 @@ private function is_bulk_download_request(): bool { /** * Resolve the snippets requested for download. * + * @param string $snippets_json JSON-encoded list of requested snippets. + * * @return Snippet[]|WP_Error */ - private function get_requested_download_snippets() { - $snippets_json = wp_unslash( filter_input( INPUT_POST, 'snippets', FILTER_DEFAULT ) ?? '' ); + private function get_requested_download_snippets( string $snippets_json ) { $payload = '' === $snippets_json ? [] : json_decode( $snippets_json, true ); if ( ! is_array( $payload ) ) { diff --git a/tests/unit/Admin/Menus/Manage_Menu_Test.php b/tests/unit/Admin/Menus/Manage_Menu_Test.php index b94bb1fd..d46a82ce 100644 --- a/tests/unit/Admin/Menus/Manage_Menu_Test.php +++ b/tests/unit/Admin/Menus/Manage_Menu_Test.php @@ -234,7 +234,7 @@ public function test_network_bulk_download_requires_network_cap(): void { ) ); - $_POST['snippets'] = wp_json_encode( + $snippets_json = wp_json_encode( array( array( 'id' => $snippet->id, @@ -246,7 +246,7 @@ public function test_network_bulk_download_requires_network_cap(): void { $menu = new Manage_Menu(); $method = new ReflectionMethod( $menu, 'get_requested_download_snippets' ); $method->setAccessible( true ); - $result = $method->invoke( $menu ); + $result = $method->invoke( $menu, $snippets_json ); $this->assertInstanceOf( WP_Error::class, $result ); $this->assertSame( 'code_snippets_forbidden_network_download', $result->get_error_code() ); From 2e17a966b123324d7ad25a8b1ce3b0a52c44d94e Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 18:36:02 +0300 Subject: [PATCH 13/16] fix: correct the shared network snippet read test route (cherry picked from commit 92e9b9d92cee5900e0f8a3f9d8ede1e7b804230f) --- .../REST_API/REST_API_Snippets_Shared_Network_Toggle_Test.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/REST_API/REST_API_Snippets_Shared_Network_Toggle_Test.php b/tests/unit/REST_API/REST_API_Snippets_Shared_Network_Toggle_Test.php index 6a0fe687..703c8eb1 100644 --- a/tests/unit/REST_API/REST_API_Snippets_Shared_Network_Toggle_Test.php +++ b/tests/unit/REST_API/REST_API_Snippets_Shared_Network_Toggle_Test.php @@ -338,7 +338,7 @@ public function test_subsite_admin_can_read_shared_network_snippet() { $response = $this->dispatch( 'GET', - "/$this->namespace/$this->base_route/$this->shared_snippet_id}", + "/$this->namespace/$this->base_route/$this->shared_snippet_id", [ 'network' => true ] ); From 1162836422a4275bb26a81303b180d82f0ef8ce6 Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 19 Jun 2026 20:27:27 +0300 Subject: [PATCH 14/16] chore: consolidate phpunit test env on WP_TESTS_DIR (cherry picked from commit 62d670c43f4e090a542484d2cc9eeb13631f5e9d) --- package.json | 2 +- tests/README.md | 2 +- tests/bootstrap.php | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 3222febf..0fac543e 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ }, "scripts": { "prepare": "husky install", - "test:php": "WP_TESTS_DIR=./.wp-tests-lib WP_DEVELOP_DIR=./.wp-core src/vendor/bin/phpunit -c phpunit.xml", + "test:php": "WP_TESTS_DIR=./.wp-tests-lib src/vendor/bin/phpunit -c phpunit.xml", "test:php:watch": "npm run test:php -- --testdox", "test:playwright": "playwright test -c config/playwright/playwright.config.ts", "test:playwright:debug": "npm run test:playwright -- --debug", diff --git a/tests/README.md b/tests/README.md index c28e119d..d1b51e49 100644 --- a/tests/README.md +++ b/tests/README.md @@ -46,7 +46,7 @@ npm run test:php:watch Or run PHPUnit directly: ```bash -WP_TESTS_DIR=./.wp-tests-lib WP_DEVELOP_DIR=./.wp-core src/vendor/bin/phpunit -c phpunit.xml +WP_TESTS_DIR=./.wp-tests-lib src/vendor/bin/phpunit -c phpunit.xml ``` ## What Gets Installed diff --git a/tests/bootstrap.php b/tests/bootstrap.php index c71de6ea..0160bb1a 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -14,9 +14,6 @@ function _get_tests_dir(): string { case (bool) getenv( 'WP_TESTS_DIR' ): return getenv( 'WP_TESTS_DIR' ); - case (bool) getenv( 'WP_DEVELOP_DIR' ): - return getenv( 'WP_DEVELOP_DIR' ) . '/tests/phpunit'; - case (bool) getenv( 'WP_PHPUNIT__DIR' ): return getenv( 'WP_PHPUNIT__DIR' ); From 30b96a5d4491527d84a79369d991c44a5acfa48b Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 19:02:57 +0300 Subject: [PATCH 15/16] fix: bust the phpunit composer cache when composer.json changes --- .github/workflows/phpunit-test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/phpunit-test.yml b/.github/workflows/phpunit-test.yml index 3fdf8f9e..d2789e82 100644 --- a/.github/workflows/phpunit-test.yml +++ b/.github/workflows/phpunit-test.yml @@ -44,6 +44,9 @@ jobs: if [ -f "src/composer.lock" ]; then cat "src/composer.lock" >> "$tmpfile" fi + if [ -f "src/composer.json" ]; then + cat "src/composer.json" >> "$tmpfile" + fi if [ -s "$tmpfile" ]; then deps_hash=$(shasum -a 1 "$tmpfile" | awk '{print $1}' | cut -c1-8) else From c1db55e72ed5912db187b5fb44025124ec261d25 Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 26 Jun 2026 19:35:51 +0300 Subject: [PATCH 16/16] fix: treat the activation error notice as a completed save in e2e helper --- tests/e2e/helpers/SnippetsTestHelper.ts | 4 ++-- tests/e2e/helpers/constants.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/e2e/helpers/SnippetsTestHelper.ts b/tests/e2e/helpers/SnippetsTestHelper.ts index 62d9d7c2..23b006e6 100644 --- a/tests/e2e/helpers/SnippetsTestHelper.ts +++ b/tests/e2e/helpers/SnippetsTestHelper.ts @@ -324,12 +324,12 @@ export class SnippetsTestHelper { for (let attempt = 0; SAVE_CONFIRM_RETRIES > attempt; attempt++) { await this.clickButton(name) - const confirmed = await this.page.locator(SELECTORS.SUCCESS_MESSAGE).first() + const settled = await this.page.locator(SELECTORS.SAVE_SETTLED_NOTICE).first() .waitFor({ state: 'visible', timeout: TIMEOUTS.DEFAULT }) .then(() => true) .catch(() => false) - if (confirmed) { + if (settled) { return } diff --git a/tests/e2e/helpers/constants.ts b/tests/e2e/helpers/constants.ts index a56135e4..abce8e89 100644 --- a/tests/e2e/helpers/constants.ts +++ b/tests/e2e/helpers/constants.ts @@ -5,6 +5,7 @@ export const SELECTORS = { LOCATION_SELECT: '.code-snippets-select-location', SUCCESS_MESSAGE: '.snippet-editor-sidebar .notice.updated', + SAVE_SETTLED_NOTICE: '.snippet-editor-sidebar .notice.updated, .code-snippets-notice.error', SNIPPETS_TABLE: '.wp-list-table', SNIPPET_ROW: '.wp-list-table tbody tr',