From b8e61be664364d7cabb29c500f96eef9a7126ea0 Mon Sep 17 00:00:00 2001 From: Rami Yushuvaev Date: Fri, 8 May 2026 21:01:19 +0300 Subject: [PATCH 1/4] feat(new): insert code at the start of the body tag --- .../fields/SnippetLocationInput.tsx | 10 ++++--- src/js/types/Snippet.ts | 4 +-- src/php/Integration/Evaluate_Content.php | 14 ++++++++-- src/php/Model/Snippet.php | 10 +++++-- src/php/Utils/i18n.php | 6 ++++- src/php/snippet-ops.php | 2 +- tests/e2e/code-snippets-evaluation.spec.ts | 26 +++++++++++++++++++ tests/e2e/helpers/constants.ts | 3 ++- 8 files changed, 63 insertions(+), 12 deletions(-) diff --git a/src/js/components/EditMenu/SnippetForm/fields/SnippetLocationInput.tsx b/src/js/components/EditMenu/SnippetForm/fields/SnippetLocationInput.tsx index fe4a943d4..b3642c191 100644 --- a/src/js/components/EditMenu/SnippetForm/fields/SnippetLocationInput.tsx +++ b/src/js/components/EditMenu/SnippetForm/fields/SnippetLocationInput.tsx @@ -14,10 +14,12 @@ const SCOPE_ICONS: Record = { 'single-use': 'clock', 'content': 'shortcode', 'head-content': 'editor-code', + 'body-content': 'editor-code', 'footer-content': 'editor-code', 'admin-css': 'dashboard', 'site-css': 'admin-customizer', 'site-head-js': 'media-code', + 'site-body-js': 'media-code', 'site-footer-js': 'media-code' } @@ -27,12 +29,14 @@ const SCOPE_DESCRIPTIONS: Record = { 'front-end': __('Only run on site front-end', 'code-snippets'), 'single-use': __('Only run once', 'code-snippets'), 'content': __('Where inserted in editor', 'code-snippets'), - 'head-content': __('In site section', 'code-snippets'), + 'head-content': __('In site header ( section)', 'code-snippets'), + 'body-content': __('In site content (start of )', 'code-snippets'), 'footer-content': __('In site footer (end of )', 'code-snippets'), 'site-css': __('Site front-end', 'code-snippets'), 'admin-css': __('Administration area', 'code-snippets'), - 'site-footer-js': __('In site footer (end of )', 'code-snippets'), - 'site-head-js': __('In site section', 'code-snippets') + 'site-head-js': __('In site header ( section)', 'code-snippets'), + 'site-body-js': __('In site content (start of )', 'code-snippets'), + 'site-footer-js': __('In site footer (end of )', 'code-snippets') } export const SnippetLocationInput: React.FC = () => { diff --git a/src/js/types/Snippet.ts b/src/js/types/Snippet.ts index 711a525e6..7d757d170 100644 --- a/src/js/types/Snippet.ts +++ b/src/js/types/Snippet.ts @@ -28,8 +28,8 @@ export type SnippetScope = typeof SNIPPET_TYPE_SCOPES[SnippetType][number] export const SNIPPET_TYPE_SCOPES = { php: ['global', 'admin', 'front-end', 'single-use'], - html: ['content', 'head-content', 'footer-content'], + html: ['content', 'head-content', 'body-content', 'footer-content'], css: ['admin-css', 'site-css'], - js: ['site-head-js', 'site-footer-js'], + js: ['site-head-js', 'site-body-js', 'site-footer-js'], cond: ['condition'] } diff --git a/src/php/Integration/Evaluate_Content.php b/src/php/Integration/Evaluate_Content.php index b8d92763e..2cf608fd9 100644 --- a/src/php/Integration/Evaluate_Content.php +++ b/src/php/Integration/Evaluate_Content.php @@ -43,6 +43,7 @@ public function __construct( DB $db ) { */ public function init() { add_action( 'wp_head', [ $this, 'load_head_content' ] ); + add_action( 'wp_body_open', [ $this, 'load_body_content' ] ); add_action( 'wp_footer', [ $this, 'load_footer_content' ] ); } @@ -63,7 +64,7 @@ private function print_content_snippets( string $scope ) { } } } else { - $scopes = [ 'head-content', 'footer-content' ]; + $scopes = [ 'head-content', 'body-content', 'footer-content' ]; if ( is_null( $this->active_snippets ) ) { $this->active_snippets = $this->db->fetch_active_snippets( $scopes ); @@ -87,6 +88,15 @@ public function load_head_content() { $this->print_content_snippets( 'head-content' ); } + /** + * Print body content snippets (requires theme to call wp_body_open). + * + * @return void + */ + public function load_body_content() { + $this->print_content_snippets( 'body-content' ); + } + /** * Print footer content snippets. * @@ -106,7 +116,7 @@ private function populate_active_snippets_from_flat_files() { $dir_name = $handler->get_dir_name(); $ext = $handler->get_file_extension(); - $scopes = [ 'head-content', 'footer-content' ]; + $scopes = [ 'head-content', 'body-content', 'footer-content' ]; $all_snippets = Snippet_Files::get_active_snippets_from_flat_files( $scopes, $dir_name ); foreach ( $all_snippets as $snippet ) { diff --git a/src/php/Model/Snippet.php b/src/php/Model/Snippet.php index 1a37b7149..20c0f6f12 100644 --- a/src/php/Model/Snippet.php +++ b/src/php/Model/Snippet.php @@ -312,9 +312,9 @@ protected function get_tags_list(): string { public static function get_all_scopes(): array { return array( 'global', 'admin', 'front-end', 'single-use', - 'content', 'head-content', 'footer-content', + 'content', 'head-content', 'body-content', 'footer-content', 'admin-css', 'site-css', - 'site-head-js', 'site-footer-js', + 'site-head-js', 'site-body-js', 'site-footer-js', 'condition', ); } @@ -332,10 +332,12 @@ public static function get_scope_icons(): array { 'single-use' => 'clock', 'content' => 'shortcode', 'head-content' => 'editor-code', + 'body-content' => 'editor-code', 'footer-content' => 'editor-code', 'admin-css' => 'dashboard', 'site-css' => 'admin-customizer', 'site-head-js' => 'media-code', + 'site-body-js' => 'media-code', 'site-footer-js' => 'media-code', 'condition' => 'randomize', ); @@ -361,6 +363,8 @@ protected function get_scope_name(): string { return __( 'Content', 'code-snippets' ); case 'head-content': return __( 'Head content', 'code-snippets' ); + case 'body-content': + return __( 'Body content', 'code-snippets' ); case 'footer-content': return __( 'Footer content', 'code-snippets' ); case 'admin-css': @@ -369,6 +373,8 @@ protected function get_scope_name(): string { return __( 'Front-end styles', 'code-snippets' ); case 'site-head-js': return __( 'Head scripts', 'code-snippets' ); + case 'site-body-js': + return __( 'Body scripts', 'code-snippets' ); case 'site-footer-js': return __( 'Footer scripts', 'code-snippets' ); } diff --git a/src/php/Utils/i18n.php b/src/php/Utils/i18n.php index 42083b687..75f3d606e 100644 --- a/src/php/Utils/i18n.php +++ b/src/php/Utils/i18n.php @@ -24,7 +24,11 @@ 'site-css' => __( 'Site front-end stylesheet', 'code-snippets' ), 'admin-css' => __( 'Administration area stylesheet', 'code-snippets' ), 'site-head-js' => __( 'JavaScript loaded in the site <head> section', 'code-snippets' ), - 'site-footer-js' => __( 'JavaScript loaded just before the closing </body> tag', 'code-snippets' ), + 'site-body-js' => __( 'JavaScript loaded at the start of the <body> tag', 'code-snippets' ), + 'site-footer-js' => __( 'JavaScript loaded at the end of the <body> tag', 'code-snippets' ), + 'head-content' => __( 'HTML output in the <head> section', 'code-snippets' ), + 'body-content' => __( 'HTML output at the start of the <body> tag', 'code-snippets' ), + 'footer-content' => __( 'HTML output at the end of the <body> tag', 'code-snippets' ), ); // class-content-widget.php. diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index cc2e4f46d..ab99efc4a 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -64,7 +64,7 @@ function clean_active_snippets_cache( string $table_name, $scopes = false ) { $scope_groups = $scopes ? [ $scopes ] : [ - [ 'head-content', 'footer-content' ], + [ 'head-content', 'body-content', 'footer-content' ], [ 'global', 'single-use', 'front-end' ], [ 'global', 'single-use', 'admin' ], ]; diff --git a/tests/e2e/code-snippets-evaluation.spec.ts b/tests/e2e/code-snippets-evaluation.spec.ts index dc7f4b63d..51b5bad1d 100644 --- a/tests/e2e/code-snippets-evaluation.spec.ts +++ b/tests/e2e/code-snippets-evaluation.spec.ts @@ -187,6 +187,32 @@ test.describe('Code Snippets Evaluation', () => { await helper.expectElementCount('text=Hello World HTML snippet in header!', 1) }) + test('HTML snippet is evaluating correctly at body start', async () => { + await helper.createAndActivateSnippet({ + name: snippetName, + code: '

Hello World HTML snippet in body!

', + type: 'HTML', + location: 'SITE_BODY' + }) + + await helper.navigateToFrontend() + await helper.expectTextVisible('Hello World HTML snippet in body!') + await helper.expectElementCount('text=Hello World HTML snippet in body!', 1) + }) + + test('HTML snippet is evaluating correctly at body end', async () => { + await helper.createAndActivateSnippet({ + name: snippetName, + code: '

Hello World HTML snippet in end!

', + type: 'HTML', + location: 'SITE_FOOTER' + }) + + await helper.navigateToFrontend() + await helper.expectTextVisible('Hello World HTML snippet in footer!') + await helper.expectElementCount('text=Hello World HTML snippet in footer!', 1) + }) + test('HTML snippet works with shortcode in editor', async ({ page }) => { const snippetId = await createHtmlSnippetForEditor(helper, page, snippetName) const pageUrl = await createPageWithShortcode(page, snippetId, snippetName) diff --git a/tests/e2e/helpers/constants.ts b/tests/e2e/helpers/constants.ts index 604a5a174..c8cdd94f7 100644 --- a/tests/e2e/helpers/constants.ts +++ b/tests/e2e/helpers/constants.ts @@ -43,8 +43,9 @@ export const SNIPPET_TYPES = { } export const SNIPPET_LOCATIONS = { - SITE_FOOTER: 'In site footer (end of )', SITE_HEADER: 'In site section', + SITE_BODY: 'In site content (start of )', + SITE_FOOTER: 'In site footer (end of )', IN_EDITOR: 'Where inserted in editor', ADMIN_ONLY: 'Only run in administration area', FRONTEND_ONLY: 'Only run on site front-end', From 35fb0800603ab623a019b51174b3e308fa7c472a Mon Sep 17 00:00:00 2001 From: Rami Yushuvaev <92088692+rami-elementor@users.noreply.github.com> Date: Fri, 8 May 2026 22:33:04 -0700 Subject: [PATCH 2/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/e2e/helpers/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/helpers/constants.ts b/tests/e2e/helpers/constants.ts index c8cdd94f7..800e28d3a 100644 --- a/tests/e2e/helpers/constants.ts +++ b/tests/e2e/helpers/constants.ts @@ -43,7 +43,7 @@ export const SNIPPET_TYPES = { } export const SNIPPET_LOCATIONS = { - SITE_HEADER: 'In site section', + SITE_HEADER: 'In site header ( section)', SITE_BODY: 'In site content (start of )', SITE_FOOTER: 'In site footer (end of )', IN_EDITOR: 'Where inserted in editor', From 1cfebdc277e10f8b6a42c89422340c3ffc21a4d1 Mon Sep 17 00:00:00 2001 From: Rami Yushuvaev Date: Sat, 9 May 2026 08:38:16 +0300 Subject: [PATCH 3/4] Update tests --- tests/e2e/code-snippets-evaluation.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/e2e/code-snippets-evaluation.spec.ts b/tests/e2e/code-snippets-evaluation.spec.ts index 51b5bad1d..7e3dffecf 100644 --- a/tests/e2e/code-snippets-evaluation.spec.ts +++ b/tests/e2e/code-snippets-evaluation.spec.ts @@ -190,27 +190,27 @@ test.describe('Code Snippets Evaluation', () => { test('HTML snippet is evaluating correctly at body start', async () => { await helper.createAndActivateSnippet({ name: snippetName, - code: '

Hello World HTML snippet in body!

', + code: '

Hello World HTML snippet in body start!

', type: 'HTML', location: 'SITE_BODY' }) await helper.navigateToFrontend() - await helper.expectTextVisible('Hello World HTML snippet in body!') - await helper.expectElementCount('text=Hello World HTML snippet in body!', 1) + await helper.expectTextVisible('Hello World HTML snippet in body start!') + await helper.expectElementCount('text=Hello World HTML snippet in body start!', 1) }) test('HTML snippet is evaluating correctly at body end', async () => { await helper.createAndActivateSnippet({ name: snippetName, - code: '

Hello World HTML snippet in end!

', + code: '

Hello World HTML snippet in body end!

', type: 'HTML', location: 'SITE_FOOTER' }) await helper.navigateToFrontend() - await helper.expectTextVisible('Hello World HTML snippet in footer!') - await helper.expectElementCount('text=Hello World HTML snippet in footer!', 1) + await helper.expectTextVisible('Hello World HTML snippet in body end!') + await helper.expectElementCount('text=Hello World HTML snippet in body end!', 1) }) test('HTML snippet works with shortcode in editor', async ({ page }) => { From b1935960e7af6da0b5b220742b50df4623875322 Mon Sep 17 00:00:00 2001 From: Rami Yushuvaev Date: Sat, 9 May 2026 09:23:43 +0300 Subject: [PATCH 4/4] Update tests --- tests/e2e/code-snippets-evaluation.spec.ts | 2 + tests/e2e/helpers/SnippetsTestHelper.ts | 50 ++++++++++++++++++++++ tests/e2e/helpers/constants.ts | 3 +- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/tests/e2e/code-snippets-evaluation.spec.ts b/tests/e2e/code-snippets-evaluation.spec.ts index 7e3dffecf..2c29f1606 100644 --- a/tests/e2e/code-snippets-evaluation.spec.ts +++ b/tests/e2e/code-snippets-evaluation.spec.ts @@ -198,6 +198,7 @@ test.describe('Code Snippets Evaluation', () => { await helper.navigateToFrontend() await helper.expectTextVisible('Hello World HTML snippet in body start!') await helper.expectElementCount('text=Hello World HTML snippet in body start!', 1) + await helper.expectTextBeforeElement('Hello World HTML snippet in body start!', SELECTORS.THEME_MAIN_WRAPPER) }) test('HTML snippet is evaluating correctly at body end', async () => { @@ -211,6 +212,7 @@ test.describe('Code Snippets Evaluation', () => { await helper.navigateToFrontend() await helper.expectTextVisible('Hello World HTML snippet in body end!') await helper.expectElementCount('text=Hello World HTML snippet in body end!', 1) + await helper.expectTextAfterElement('Hello World HTML snippet in body end!', SELECTORS.THEME_MAIN_WRAPPER) }) test('HTML snippet works with shortcode in editor', async ({ page }) => { diff --git a/tests/e2e/helpers/SnippetsTestHelper.ts b/tests/e2e/helpers/SnippetsTestHelper.ts index 7933ae7f8..6b835e600 100644 --- a/tests/e2e/helpers/SnippetsTestHelper.ts +++ b/tests/e2e/helpers/SnippetsTestHelper.ts @@ -497,6 +497,56 @@ export class SnippetsTestHelper { await expect(this.page.locator('body')).not.toContainText(text) } + async expectTextBeforeElement(text: string, selector: string): Promise { + const precedes = await this.page.evaluate( + ({ text, selector }) => { + const node = document.evaluate( + `//p[contains(text(),"${text}")]`, + document, + null, + XPathResult.FIRST_ORDERED_NODE_TYPE, + null + ).singleNodeValue + + const reference = document.querySelector(selector) + + if (!node || !reference) { + return null + } + + return !!(reference.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_PRECEDING) + }, + { text, selector } + ) + + expect(precedes).toBe(true) + } + + async expectTextAfterElement(text: string, selector: string): Promise { + const follows = await this.page.evaluate( + ({ text, selector }) => { + const node = document.evaluate( + `//p[contains(text(),"${text}")]`, + document, + null, + XPathResult.FIRST_ORDERED_NODE_TYPE, + null + ).singleNodeValue + + const reference = document.querySelector(selector) + + if (!node || !reference) { + return null + } + + return !!(reference.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_FOLLOWING) + }, + { text, selector } + ) + + expect(follows).toBe(true) + } + /** * Create a complete snippet with save and activate */ diff --git a/tests/e2e/helpers/constants.ts b/tests/e2e/helpers/constants.ts index 800e28d3a..3e8df7ef2 100644 --- a/tests/e2e/helpers/constants.ts +++ b/tests/e2e/helpers/constants.ts @@ -16,7 +16,8 @@ export const SELECTORS = { DELETE_ACTION: '.row-actions button:has-text("Trash")', EXPORT_ACTION: '.row-actions button:has-text("Export")', - ADMIN_BAR: '#wpadminbar' + ADMIN_BAR: '#wpadminbar', + THEME_MAIN_WRAPPER: '.wp-site-blocks' } export const TIMEOUTS = {