Skip to content

Conversation

@theaminuli
Copy link
Member

@theaminuli theaminuli commented Nov 27, 2025

What:

Skip stylesheet link elements that have empty href attributes during the Enqueued Assets Health Check audit.

Why:

Some themes and plugins (like Mesmerize Companion) intentionally use empty href attributes with data-href attributes as a performance optimization technique to defer CSS loading. These stylesheets are non-render-blocking by design, but were being incorrectly flagged as errors ("A valid URL was not provided") in the Site Health Status, negatively impacting site scores.

How:

Added a check for empty string href values in perflab_aea_audit_blocking_assets(). The condition now reads:

if ( ! is_string( $href ) ) {
	continue;
}
$href = trim( $href );
if ( '' === $href ) {
	continue;
}

This skips stylesheets with href="" from being audited, similar to how async CSS with non-standard media attributes is already excluded.

Files changed:

helper.php

Fixes #2278

Updated the check for the href attribute in perflab_aea_audit_blocking_assets to skip processing when href is an empty string, preventing potential issues with invalid asset URLs.
@github-actions
Copy link

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: theaminuli <theaminuldev@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@codecov
Copy link

codecov bot commented Nov 27, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 68.87%. Comparing base (4988642) to head (dccaa9a).
⚠️ Report is 21 commits behind head on trunk.

Additional details and impacted files
@@            Coverage Diff             @@
##            trunk    #2281      +/-   ##
==========================================
+ Coverage   68.85%   68.87%   +0.02%     
==========================================
  Files          90       90              
  Lines        7612     7618       +6     
==========================================
+ Hits         5241     5247       +6     
  Misses       2371     2371              
Flag Coverage Δ
multisite 68.87% <100.00%> (+0.02%) ⬆️
single 35.41% <0.00%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@westonruter westonruter added this to the performance-lab n.e.x.t milestone Nov 27, 2025
@westonruter westonruter added [Type] Bug An existing feature is broken [Plugin] Performance Lab Issue relates to work in the Performance Lab Plugin only labels Nov 27, 2025
@westonruter
Copy link
Member

Thank you for the PR. Let's also update the unit test to add an assertion for this change.

theaminuli and others added 2 commits November 27, 2025 21:52
Co-authored-by: Weston Ruter <westonruter@gmail.com>
Introduces a test case to verify that styles enqueued with an empty href are handled correctly. This addresses issue WordPress#2278 and ensures that such styles are not included in the audited assets.
@theaminuli
Copy link
Member Author

theaminuli commented Nov 27, 2025

Test Added

I've added test coverage for the empty href attribute fix in the test_perflab_aea_audit_enqueued_styles() test method.

What the test does:

  • Enqueues a stylesheet with style-empty-href handle
  • Verifies that this stylesheet is properly skipped from the audit and doesn't appear in the results

Test assertion:

$empty_href_style = array_filter(
	$assets['styles'],
	static function ( $item ) {
		return 'https://empty-href-style.com' === $item['src'];
	}
);
$this->assertEmpty( $empty_href_style );

This ensures that themes/plugins using performance optimization techniques with href="" won't trigger false positives in Site Health checks.

Thanks to @westonruter for the valuable feedback on implementing this test case!

Files changed:

test-audit-enqueued-assets.php

Corrects whitespace in the test for enqueued assets and adds a docblock line for clarity. No functional changes to the test logic.
Comment on lines 187 to 191
$tag = str_replace(
'href=\'https://empty-href-style.com\'',
"href=''",
$tag
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the HTML Tag Processor for this as it is more robust than doing a replacement.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 65b2746

Copilot AI review requested due to automatic review settings January 6, 2026 16:57
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 181 to 196
function ( $tag, $handle ) {
if ( 'style-no-href' === $handle ) {
$tag = str_replace( 'href=\'https://no-href-style.com\'', ' ', $tag );
$processor = new WP_HTML_Tag_Processor( $tag );
$this->assertTrue( $processor->next_tag( 'LINK' ), 'Expected a LINK to be present.' );
$this->assertSame( 'stylesheet', $processor->get_attribute( 'rel' ), 'Expected LINK to be a stylesheet.' );
$processor->remove_attribute( 'href' );
$tag = $processor->get_updated_html();
} elseif ( 'style-empty-href' === $handle ) {
$processor = new WP_HTML_Tag_Processor( $tag );
$this->assertTrue( $processor->next_tag( 'LINK' ), 'Expected a LINK to be present.' );
$this->assertSame( 'stylesheet', $processor->get_attribute( 'rel' ), 'Expected LINK to be a stylesheet.' );
$processor->set_attribute( 'href', '' );
$tag = $processor->get_updated_html();
}
return $tag;
},
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change from static function to function is necessary here because the callback now uses $this to access test assertion methods (lines 184-185, 190-191). However, this pattern is inconsistent with other filter callbacks in this test file, which all use static function (e.g., lines 62, 232, 240, 248, 256, 264).

While the test assertions inside the filter callback can help catch issues earlier, they create coupling between the filter and test context and break the established pattern. Consider either:

  1. Removing the assertions from inside the filter callback (they're not strictly necessary since the test verifies the end result), or
  2. If assertions are needed for debugging, extract the HTML manipulation logic into a helper method that can be called with $this context while keeping the assertions, making the pattern more explicit.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, thanks.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

westonruter and others added 3 commits January 6, 2026 16:40
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@westonruter westonruter requested a review from Copilot January 7, 2026 00:54
@westonruter westonruter merged commit 1f31042 into WordPress:trunk Jan 7, 2026
24 checks passed
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Plugin] Performance Lab Issue relates to work in the Performance Lab Plugin only [Type] Bug An existing feature is broken

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enqueued Assets Site Health test not fully accounting for async CSS

2 participants