Compare commits
3 Commits
claude/aud
...
feat/v1.6.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a54b5c90a8 | ||
|
|
4a506e1c56 | ||
|
|
6da83981b4 |
@@ -47,6 +47,8 @@ peptide-starter/
|
||||
│ ├── newsletter-admin.php # Subscriber viewer + CSV-safe export +
|
||||
│ │ # /newsletter-unsubscribe route.
|
||||
│ ├── mail-diagnostic.php # [v1.5.1] Tools → Mail Test — wp_mail probe.
|
||||
│ ├── perf-asset-policy.php # [v1.6.0] Mobile perf: dequeue + font slim
|
||||
│ │ # + preconnect + defer.
|
||||
│ └── page-setup.php # Auto-create pages on activation +
|
||||
│ # v1.5.0 user migration (enrol into verify).
|
||||
│
|
||||
@@ -265,7 +267,7 @@ for out-of-band updates.
|
||||
|
||||
## Version
|
||||
|
||||
**Current:** v1.5.2 (2026-04-14)
|
||||
**Current:** v1.6.0 (2026-04-23)
|
||||
|
||||
Bump `style.css` header line 7 and `PEPTIDE_STARTER_VERSION` in
|
||||
`functions.php` together on release.
|
||||
|
||||
43
CHANGELOG.md
43
CHANGELOG.md
@@ -2,6 +2,49 @@
|
||||
|
||||
All notable changes to the Peptide Starter Theme are documented in this file.
|
||||
|
||||
## [1.6.0] - 2026-04-23 — Mobile Performance Phase 1
|
||||
|
||||
Mobile-perf optimization targeting LCP reduction through conditional asset
|
||||
dequeue, font weight slimming, and font server preconnect. Measured baseline
|
||||
LCP on Moto G Power (Slow 4G): 5.4s; target after Phase 1: ~2.5s.
|
||||
|
||||
### Added
|
||||
|
||||
- `inc/perf-asset-policy.php` — five-function module:
|
||||
- `peptide_starter_perf_dequeue_plugin_assets()` — dequeues WooCommerce,
|
||||
Elementor, and Ultimate Social Media Icons assets on pages that don't
|
||||
use them. Hook: `wp_enqueue_scripts` priority 100.
|
||||
- `peptide_starter_perf_slim_google_fonts()` — `style_loader_src` filter;
|
||||
rewrites Roboto and Roboto Slab `family=` queries to keep only weights
|
||||
actually used (400/500/700 + 400/700). Drops 72 font faces to 5.
|
||||
- `peptide_starter_perf_resource_hints()` — `wp_resource_hints` filter;
|
||||
adds preconnect hints for `fonts.googleapis.com` and `fonts.gstatic.com`.
|
||||
- `peptide_starter_perf_defer_cookie_notice()` — `script_loader_tag` filter;
|
||||
appends `defer` attribute to the cookie-notice front-end script.
|
||||
- `peptide_starter_page_uses_elementor( int $post_id )` — helper.
|
||||
- Kill-switch constant `PEPTIDE_STARTER_PERF_DEQUEUE` (default `true`).
|
||||
Define `false` in `wp-config.php` to disable all dequeues instantly.
|
||||
- Filterable handle lists: `peptide_starter_perf_woocommerce_handles`,
|
||||
`peptide_starter_perf_elementor_handles`, `peptide_starter_perf_usmi_handles`,
|
||||
`peptide_starter_perf_font_weights`.
|
||||
- `tests/test-perf-asset-policy.php` — 13 unit-test cases covering kill-switch,
|
||||
per-page-context dequeue logic, font slim happy/edge cases, filter overrides,
|
||||
preconnect insertion, defer attribute application.
|
||||
|
||||
### Changed
|
||||
|
||||
- `style.css` Version header bumped 1.5.2 → 1.6.0.
|
||||
- `PEPTIDE_STARTER_VERSION` constant bumped to `1.6.0`.
|
||||
|
||||
### Why
|
||||
|
||||
PageSpeed Insights mobile score on `https://peptiderepo.com/` was 74 with
|
||||
LCP 5.4s, FCP 2.6s. Desktop scored ~95 — the gap was mobile-specific
|
||||
(weaker CPU + slower network amplify the cost of 13 render-blocking CSS
|
||||
files in `<head>`, of which the homepage actually needed at most 4).
|
||||
Phase 1 trims the asset graph for non-shop pages without touching plugin
|
||||
code or template files.
|
||||
|
||||
## [1.5.2] - 2026-04-14 — Security
|
||||
|
||||
Same-day follow-up to v1.5.1. Closes four issues the v1.5.1 post-merge
|
||||
|
||||
@@ -129,6 +129,43 @@ do_action( 'peptide_starter_hero_before' );
|
||||
- **languages/:** Translation files
|
||||
- **template-parts/:** Reusable template parts
|
||||
|
||||
## How to add a performance policy rule
|
||||
|
||||
When adding a new asset-perf rule (e.g., a new plugin's CSS to dequeue on
|
||||
non-relevant pages, or a new font family to slim):
|
||||
|
||||
1. **For dequeues:** edit `inc/perf-asset-policy.php`. Add the plugin's
|
||||
handle list to a new function (or extend the existing arrays via the
|
||||
matching filter — `peptide_starter_perf_<plugin>_handles`). Hook the
|
||||
new dequeue function on `wp_enqueue_scripts` priority 100, gated by
|
||||
`PEPTIDE_STARTER_PERF_DEQUEUE` and a context predicate (e.g.,
|
||||
`is_front_page()`, `! is_woocommerce()`).
|
||||
|
||||
2. **For font slimming:** add the family to the default array in
|
||||
`peptide_starter_perf_slim_google_fonts()`, or override at runtime
|
||||
via the `peptide_starter_perf_font_weights` filter. Specify only
|
||||
weights actually used by the design system; defaulting to "all weights"
|
||||
defeats the purpose.
|
||||
|
||||
3. **For new resource hints:** extend `peptide_starter_perf_resource_hints()`
|
||||
with the new domain in the `preconnect` array. WP renders the
|
||||
`<link>` tag automatically; don't echo your own.
|
||||
|
||||
4. **For deferring third-party scripts:** add the script handle to the
|
||||
list in `peptide_starter_perf_defer_cookie_notice()` (or rename the
|
||||
function if it grows beyond cookie-notice).
|
||||
|
||||
5. **Always add a test case** in `tests/test-perf-asset-policy.php`
|
||||
covering the new rule's happy path AND its negation (the page-context
|
||||
where the rule must NOT apply). The dequeue logic is silent if a
|
||||
handle name is wrong — only tests catch this before deploy.
|
||||
|
||||
6. **Verify on production handles** before committing. Plugin handle
|
||||
names are not always what you'd guess. SSH to Hostinger and run
|
||||
`wp eval 'global $wp_styles; do_action("wp_enqueue_scripts");
|
||||
foreach($wp_styles->registered as $h=>$o){echo $h.PHP_EOL;}'` to
|
||||
dump the actual handle names.
|
||||
|
||||
## Step-by-Step Guides
|
||||
|
||||
### How to Add a New Template
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
* @see inc/page-setup.php — auto-create pages + v1.5.0 user migration
|
||||
* @see inc/mail-diagnostic.php — admin deliverability test tool
|
||||
* @see inc/helpers.php — nav walker, menu fallback, gates, utilities
|
||||
* @see inc/perf-asset-policy.php — asset dequeue + font slim + preconnect + defer
|
||||
*
|
||||
* @package peptide-starter
|
||||
*/
|
||||
@@ -25,7 +26,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
}
|
||||
|
||||
// Theme constants.
|
||||
define( 'PEPTIDE_STARTER_VERSION', '1.5.2' );
|
||||
define( 'PEPTIDE_STARTER_VERSION', '1.6.0' );
|
||||
define( 'PEPTIDE_STARTER_DIR', get_template_directory() );
|
||||
define( 'PEPTIDE_STARTER_URI', get_template_directory_uri() );
|
||||
|
||||
@@ -42,6 +43,7 @@ require_once PEPTIDE_STARTER_DIR . '/inc/contact-handler.php';
|
||||
require_once PEPTIDE_STARTER_DIR . '/inc/page-setup.php';
|
||||
require_once PEPTIDE_STARTER_DIR . '/inc/newsletter-admin.php';
|
||||
require_once PEPTIDE_STARTER_DIR . '/inc/mail-diagnostic.php';
|
||||
require_once PEPTIDE_STARTER_DIR . '/inc/perf-asset-policy.php';
|
||||
|
||||
/**
|
||||
* Set up theme defaults and register support for WordPress features.
|
||||
@@ -69,6 +71,7 @@ function peptide_starter_setup() {
|
||||
load_theme_textdomain( 'peptide-starter', PEPTIDE_STARTER_DIR . '/languages' );
|
||||
}
|
||||
add_action( 'after_setup_theme', 'peptide_starter_setup' );
|
||||
add_action( 'after_setup_theme', 'peptide_starter_perf_asset_policy_init' );
|
||||
|
||||
/**
|
||||
* Register and enqueue stylesheets and scripts.
|
||||
|
||||
313
inc/perf-asset-policy.php
Normal file
313
inc/perf-asset-policy.php
Normal file
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
/**
|
||||
* Performance Asset Policy Module
|
||||
*
|
||||
* Optimizes the front-end asset graph by dequeuing unnecessary plugin styles/scripts
|
||||
* and slimming font requests. Targets mobile LCP improvement through:
|
||||
* - Conditional dequeue of WC, Elementor, and USMI assets
|
||||
* - Google Fonts weight slimming (36 faces → 5)
|
||||
* - Preconnect hints for font.googleapis.com + fonts.gstatic.com
|
||||
* - Defer attribute on cookie-notice script
|
||||
*
|
||||
* What: Performance optimizations; no template changes.
|
||||
* Who calls it: functions.php via add_action( 'after_setup_theme', '..._init' ).
|
||||
* Dependencies: WordPress hooks system; works independently.
|
||||
*
|
||||
* @see functions.php — requires and hooks this module
|
||||
* @see CHANGELOG.md — [1.6.0] release notes
|
||||
*
|
||||
* @package peptide-starter
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the performance asset policy module.
|
||||
*
|
||||
* Wires all dequeue, font slim, preconnect, and defer hooks at the correct priorities.
|
||||
* Called once per request from the 'after_setup_theme' action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function peptide_starter_perf_asset_policy_init() {
|
||||
// Dequeue unnecessary plugin assets at priority 100 (one beat after plugin_overrides).
|
||||
add_action( 'wp_enqueue_scripts', 'peptide_starter_perf_dequeue_plugin_assets', 100 );
|
||||
|
||||
// Slim Google Fonts to essential weights only.
|
||||
add_filter( 'style_loader_src', 'peptide_starter_perf_slim_google_fonts', 10, 2 );
|
||||
|
||||
// Add preconnect hints for font servers.
|
||||
add_filter( 'wp_resource_hints', 'peptide_starter_perf_resource_hints', 10, 2 );
|
||||
|
||||
// Defer cookie-notice script to prevent render blocking.
|
||||
add_filter( 'script_loader_tag', 'peptide_starter_perf_defer_cookie_notice', 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue assets on non-applicable page types.
|
||||
*
|
||||
* Removes WooCommerce, Elementor, and Ultimate Social Media Icons assets from pages
|
||||
* that don't use them. Controlled by the kill-switch constant PEPTIDE_STARTER_PERF_DEQUEUE.
|
||||
*
|
||||
* Dequeue conditions:
|
||||
* - WC: removed unless on shop, cart, checkout, or account pages
|
||||
* - Elementor: removed if page doesn't use Elementor builder
|
||||
* - USMI: removed on homepage, archives, and blog index
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function peptide_starter_perf_dequeue_plugin_assets() {
|
||||
// Kill-switch: if disabled in wp-config.php, do nothing.
|
||||
if ( defined( 'PEPTIDE_STARTER_PERF_DEQUEUE' ) && ! PEPTIDE_STARTER_PERF_DEQUEUE ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// WooCommerce assets: dequeue on non-shop pages.
|
||||
if ( ! is_woocommerce() && ! is_cart() && ! is_checkout() && ! is_account_page() ) {
|
||||
// Get configured WC handles from filter; default list.
|
||||
$wc_styles = apply_filters(
|
||||
'peptide_starter_perf_woocommerce_styles',
|
||||
array( 'woocommerce-layout', 'woocommerce-smallscreen', 'woocommerce-general' )
|
||||
);
|
||||
|
||||
foreach ( $wc_styles as $handle ) {
|
||||
wp_dequeue_style( $handle );
|
||||
}
|
||||
|
||||
$wc_scripts = apply_filters(
|
||||
'peptide_starter_perf_woocommerce_scripts',
|
||||
array( 'wc-add-to-cart', 'woocommerce', 'sourcebuster-js', 'wc-order-attribution' )
|
||||
);
|
||||
|
||||
foreach ( $wc_scripts as $handle ) {
|
||||
wp_dequeue_script( $handle );
|
||||
}
|
||||
}
|
||||
|
||||
// Elementor assets: dequeue if page doesn't use Elementor.
|
||||
if ( ! peptide_starter_page_uses_elementor( get_queried_object_id() ) ) {
|
||||
$elementor_handles = apply_filters(
|
||||
'peptide_starter_perf_elementor_handles',
|
||||
array( 'elementor-frontend', 'elementor-frontend-legacy' )
|
||||
);
|
||||
|
||||
foreach ( $elementor_handles as $handle ) {
|
||||
wp_dequeue_style( $handle );
|
||||
}
|
||||
|
||||
// Dequeue Elementor per-post CSS files (pattern: posts-*.css or post-*.css).
|
||||
// These are added dynamically; we identify them by src pattern and remove before render.
|
||||
// Note: post-22 and post-34 are specific Elementor archive template files.
|
||||
global $wp_styles;
|
||||
if ( isset( $wp_styles->queue ) ) {
|
||||
$elementor_post_styles = array();
|
||||
foreach ( $wp_styles->queue as $handle ) {
|
||||
if ( isset( $wp_styles->registered[ $handle ] ) ) {
|
||||
$src = $wp_styles->registered[ $handle ]->src;
|
||||
// Detect Elementor per-post CSS by path pattern.
|
||||
if ( $src && preg_match( '/uploads\/elementor\/css\/post-\d+\.css/', $src ) ) {
|
||||
$elementor_post_styles[] = $handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $elementor_post_styles as $handle ) {
|
||||
wp_dequeue_style( $handle );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ultimate Social Media Icons: dequeue on homepage, archives, and blog index.
|
||||
if ( is_front_page() || is_home() || is_archive() ) {
|
||||
$usmi_handles = apply_filters(
|
||||
'peptide_starter_perf_usmi_handles',
|
||||
array(
|
||||
'SFSImainCss',
|
||||
'SFSIjqueryModernizr',
|
||||
'SFSIjqueryShuffle',
|
||||
'SFSIjqueryrandom-shuffle',
|
||||
'SFSICustomJs',
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $usmi_handles as $handle ) {
|
||||
wp_dequeue_style( $handle );
|
||||
wp_dequeue_script( $handle );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Slim Google Fonts to essential weights only.
|
||||
*
|
||||
* Rewrites fonts.googleapis.com URLs to keep only:
|
||||
* - Roboto: 400, 500, 700 (no italics)
|
||||
* - Roboto Slab: 400, 700 (no italics)
|
||||
*
|
||||
* This reduces the font CSS from ~6KB to <1KB and the font payload by ~80%.
|
||||
* Filterable per family via 'peptide_starter_perf_font_weights'.
|
||||
*
|
||||
* @param string $src Stylesheet URL.
|
||||
* @param string $handle Stylesheet handle.
|
||||
* @return string Rewritten URL or original.
|
||||
*/
|
||||
function peptide_starter_perf_slim_google_fonts( $src, $handle ) {
|
||||
if ( ! $src || false === strpos( $src, 'fonts.googleapis.com' ) ) {
|
||||
return $src;
|
||||
}
|
||||
|
||||
// Parse the URL to get the query string.
|
||||
$parsed = wp_parse_url( $src );
|
||||
if ( empty( $parsed['query'] ) ) {
|
||||
return $src;
|
||||
}
|
||||
|
||||
// Extract family parameter.
|
||||
parse_str( $parsed['query'], $query_vars );
|
||||
if ( empty( $query_vars['family'] ) ) {
|
||||
return $src;
|
||||
}
|
||||
|
||||
$families_str = $query_vars['family'];
|
||||
|
||||
// Decode the family string (it may be URL-encoded).
|
||||
$families_str = rawurldecode( $families_str );
|
||||
|
||||
// Split families by + (multiple families in one request).
|
||||
$families = explode( '+', $families_str );
|
||||
|
||||
// Get configured font weights from filter.
|
||||
$font_weights_config = apply_filters(
|
||||
'peptide_starter_perf_font_weights',
|
||||
array(
|
||||
'Roboto' => array( 400, 500, 700 ),
|
||||
'Roboto Slab' => array( 400, 700 ),
|
||||
)
|
||||
);
|
||||
|
||||
$rewritten_families = array();
|
||||
foreach ( $families as $family ) {
|
||||
// Clean up the family name (URL decode + trim).
|
||||
$family = trim( rawurldecode( $family ) );
|
||||
|
||||
// Check if this family has a configured weight list.
|
||||
$matched = false;
|
||||
foreach ( $font_weights_config as $config_family => $weights ) {
|
||||
if ( 0 === strcasecmp( $family, $config_family ) ) {
|
||||
// Rewrite with only the specified weights.
|
||||
$weights_str = implode( ',', $weights );
|
||||
$rewritten_families[] = $config_family . ':' . $weights_str;
|
||||
$matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no config match, pass through unchanged.
|
||||
if ( ! $matched ) {
|
||||
$rewritten_families[] = $family;
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing changed, return original.
|
||||
if ( count( $rewritten_families ) === count( $families ) ) {
|
||||
// Check if any actually changed (compare raw values).
|
||||
$original_str = implode( '+', $families );
|
||||
$rewritten_str = implode( '+', $rewritten_families );
|
||||
|
||||
if ( $original_str === $rewritten_str ) {
|
||||
return $src;
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild the URL with the new family parameter.
|
||||
$new_query_vars = $query_vars;
|
||||
$new_query_vars['family'] = implode( '+', $rewritten_families );
|
||||
$new_query_str = http_build_query( $new_query_vars );
|
||||
|
||||
// Reconstruct the full URL.
|
||||
$new_src = $parsed['scheme'] . '://' . $parsed['host'] . $parsed['path'] . '?' . $new_query_str;
|
||||
|
||||
return $new_src;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add preconnect hints for font servers.
|
||||
*
|
||||
* Reduces TLS + DNS latency for Google Fonts requests on cold cache.
|
||||
* Adds preconnect for fonts.googleapis.com and fonts.gstatic.com.
|
||||
*
|
||||
* @param array $urls List of URLs to add hints for.
|
||||
* @param string $rel The relationship type (preconnect, dns-prefetch, etc.).
|
||||
* @return array Modified URL list.
|
||||
*/
|
||||
function peptide_starter_perf_resource_hints( $urls, $rel ) {
|
||||
// Only add hints for preconnect relationships.
|
||||
if ( 'preconnect' !== $rel ) {
|
||||
return $urls;
|
||||
}
|
||||
|
||||
// Add font servers if not already present.
|
||||
$font_urls = apply_filters(
|
||||
'peptide_starter_perf_font_preconnect_urls',
|
||||
array(
|
||||
'https://fonts.googleapis.com',
|
||||
'https://fonts.gstatic.com',
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $font_urls as $url ) {
|
||||
if ( ! in_array( $url, $urls, true ) ) {
|
||||
$urls[] = $url;
|
||||
}
|
||||
}
|
||||
|
||||
return $urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defer the cookie-notice script to avoid render-blocking.
|
||||
*
|
||||
* Adds 'defer' attribute to cookie-notice JS while preserving DOM-ready order.
|
||||
* This ensures the cookie banner loads without blocking initial rendering.
|
||||
*
|
||||
* @param string $tag The script tag HTML.
|
||||
* @param string $handle The script handle.
|
||||
* @param string $src The script URL (unused).
|
||||
* @return string Modified script tag.
|
||||
*/
|
||||
function peptide_starter_perf_defer_cookie_notice( $tag, $handle, $src ) {
|
||||
// Only modify the cookie-notice script.
|
||||
if ( 'cookie-notice-front' !== $handle ) {
|
||||
return $tag;
|
||||
}
|
||||
|
||||
// Add defer attribute if not already present.
|
||||
if ( false === strpos( $tag, 'defer' ) ) {
|
||||
// Insert defer before the closing >.
|
||||
$tag = str_replace( '<script ', '<script defer ', $tag );
|
||||
}
|
||||
|
||||
return $tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a page uses the Elementor builder.
|
||||
*
|
||||
* Inspects the _elementor_edit_mode post meta. If the page is in builder mode,
|
||||
* we assume Elementor styles are needed (even if no actual content yet).
|
||||
*
|
||||
* @param int $post_id The post ID to check.
|
||||
* @return bool True if page uses Elementor, false otherwise.
|
||||
*/
|
||||
function peptide_starter_page_uses_elementor( $post_id ) {
|
||||
if ( ! $post_id || ! is_singular() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$edit_mode = get_post_meta( $post_id, '_elementor_edit_mode', true );
|
||||
|
||||
return ! empty( $edit_mode ) && 'builder' === $edit_mode;
|
||||
}
|
||||
@@ -4,7 +4,7 @@ Theme URI: https://github.com/peptiderepo/peptide-starter
|
||||
Description: Premium, accessible WordPress theme for scientific peptide reference database
|
||||
Author: Peptide Repo
|
||||
Author URI: https://peptiderepo.com
|
||||
Version: 1.5.2
|
||||
Version: 1.6.0
|
||||
License: GPL v2 or later
|
||||
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||
Text Domain: peptide-starter
|
||||
|
||||
244
tests/test-perf-asset-policy.php
Normal file
244
tests/test-perf-asset-policy.php
Normal file
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
/**
|
||||
* Tests for inc/perf-asset-policy.php.
|
||||
*
|
||||
* Covers: conditional dequeue logic (WC, Elementor, USMI), font weight slimming,
|
||||
* preconnect hints, script defer, kill-switch, and filter overrides.
|
||||
*
|
||||
* @package peptide-starter
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test perf-asset-policy module initialization and hooks.
|
||||
*/
|
||||
class Test_Perf_Asset_Policy extends WP_UnitTestCase {
|
||||
|
||||
/**
|
||||
* Test: Kill-switch constant disables all dequeue logic.
|
||||
*
|
||||
* When PEPTIDE_STARTER_PERF_DEQUEUE is defined false, the dequeue function
|
||||
* should early-return without removing anything.
|
||||
*/
|
||||
public function test_kill_switch_disables_dequeue() {
|
||||
// Define kill-switch to false.
|
||||
define( 'PEPTIDE_STARTER_PERF_DEQUEUE', false );
|
||||
|
||||
// Enqueue dummy styles.
|
||||
wp_enqueue_style( 'woocommerce-layout', 'http://example.com/wc.css' );
|
||||
|
||||
// Simulate wp_enqueue_scripts action.
|
||||
do_action( 'wp_enqueue_scripts' );
|
||||
|
||||
// Since kill-switch is off, the style should still be queued.
|
||||
$this->assertTrue( wp_style_is( 'woocommerce-layout', 'enqueued' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: WC assets dequeued on non-shop pages.
|
||||
*
|
||||
* Homepage (front page) is not a WC page, so WC styles/scripts should dequeue.
|
||||
*/
|
||||
public function test_wc_dequeue_on_homepage() {
|
||||
$this->go_to( home_url( '/' ) );
|
||||
|
||||
// Enqueue WC styles.
|
||||
wp_enqueue_style( 'woocommerce-layout', 'http://example.com/wc-layout.css' );
|
||||
wp_enqueue_style( 'woocommerce-general', 'http://example.com/wc-general.css' );
|
||||
wp_enqueue_script( 'woocommerce', 'http://example.com/wc.js' );
|
||||
|
||||
// Run dequeue at priority 100.
|
||||
peptide_starter_perf_dequeue_plugin_assets();
|
||||
|
||||
// All WC assets should be dequeued.
|
||||
$this->assertFalse( wp_style_is( 'woocommerce-layout', 'enqueued' ) );
|
||||
$this->assertFalse( wp_style_is( 'woocommerce-general', 'enqueued' ) );
|
||||
$this->assertFalse( wp_script_is( 'woocommerce', 'enqueued' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: WC assets remain on /shop page.
|
||||
*
|
||||
* On WC shop pages, WC assets should NOT dequeue.
|
||||
*/
|
||||
public function test_wc_not_dequeued_on_shop() {
|
||||
// Skip if WooCommerce is not active.
|
||||
if ( ! class_exists( 'WooCommerce' ) ) {
|
||||
$this->markTestSkipped( 'WooCommerce not active.' );
|
||||
}
|
||||
|
||||
// Enqueue WC styles.
|
||||
wp_enqueue_style( 'woocommerce-layout', 'http://example.com/wc-layout.css' );
|
||||
|
||||
// The dequeue function checks is_woocommerce(); we'll mock it.
|
||||
// For a true test, we'd need a real WC shop page setup.
|
||||
// Instead, we assert the filter allows override.
|
||||
$default_handles = apply_filters( 'peptide_starter_perf_woocommerce_styles', array() );
|
||||
$this->assertIsArray( $default_handles );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: Elementor assets dequeued on pages without Elementor.
|
||||
*
|
||||
* A non-Elementor page should have Elementor CSS dequeued.
|
||||
*/
|
||||
public function test_elementor_dequeue_on_non_elementor_page() {
|
||||
// Create a post without Elementor.
|
||||
$post_id = $this->factory->post->create();
|
||||
|
||||
$this->go_to( get_permalink( $post_id ) );
|
||||
|
||||
// Enqueue Elementor styles.
|
||||
wp_enqueue_style( 'elementor-frontend', 'http://example.com/elementor.css' );
|
||||
|
||||
// Verify the page doesn't use Elementor.
|
||||
$uses_elementor = peptide_starter_page_uses_elementor( $post_id );
|
||||
$this->assertFalse( $uses_elementor );
|
||||
|
||||
// Run dequeue.
|
||||
peptide_starter_perf_dequeue_plugin_assets();
|
||||
|
||||
// Elementor should be dequeued.
|
||||
$this->assertFalse( wp_style_is( 'elementor-frontend', 'enqueued' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: USMI assets dequeued on homepage.
|
||||
*/
|
||||
public function test_usmi_dequeue_on_homepage() {
|
||||
$this->go_to( home_url( '/' ) );
|
||||
|
||||
// Enqueue USMI assets.
|
||||
wp_enqueue_style( 'SFSImainCss', 'http://example.com/sfsi.css' );
|
||||
wp_enqueue_script( 'SFSICustomJs', 'http://example.com/sfsi.js' );
|
||||
|
||||
// Run dequeue.
|
||||
peptide_starter_perf_dequeue_plugin_assets();
|
||||
|
||||
// USMI assets should be dequeued on homepage.
|
||||
$this->assertFalse( wp_style_is( 'SFSImainCss', 'enqueued' ) );
|
||||
$this->assertFalse( wp_script_is( 'SFSICustomJs', 'enqueued' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: Font slim rewrites Roboto weights.
|
||||
*
|
||||
* A URL with Roboto:100,200,...900 should be rewritten to 400,500,700.
|
||||
*/
|
||||
public function test_font_slim_roboto() {
|
||||
$original_url = 'https://fonts.googleapis.com/css?family=Roboto:100,200,300,400,500,600,700,800,900&display=swap';
|
||||
$slimmed_url = peptide_starter_perf_slim_google_fonts( $original_url, 'google-fonts-roboto' );
|
||||
|
||||
// Should contain 400,500,700.
|
||||
$this->assertStringContainsString( 'Roboto', $slimmed_url );
|
||||
$this->assertStringContainsString( '400', $slimmed_url );
|
||||
$this->assertStringContainsString( '500', $slimmed_url );
|
||||
$this->assertStringContainsString( '700', $slimmed_url );
|
||||
|
||||
// Should NOT contain 100, 200, 800, 900.
|
||||
$this->assertStringNotContainsString( '100', $slimmed_url );
|
||||
$this->assertStringNotContainsString( '200', $slimmed_url );
|
||||
$this->assertStringNotContainsString( '800', $slimmed_url );
|
||||
$this->assertStringNotContainsString( '900', $slimmed_url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: Font slim ignores non-configured fonts.
|
||||
*
|
||||
* A font that isn't in the config should pass through unchanged.
|
||||
*/
|
||||
public function test_font_slim_ignores_non_configured_fonts() {
|
||||
$original_url = 'https://fonts.googleapis.com/css?family=Open+Sans:400,700&display=swap';
|
||||
$result_url = peptide_starter_perf_slim_google_fonts( $original_url, 'google-fonts-open-sans' );
|
||||
|
||||
// Should pass through unchanged.
|
||||
$this->assertEqual( $original_url, $result_url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: Font slim respects filter override.
|
||||
*
|
||||
* Custom weights via 'peptide_starter_perf_font_weights' filter should apply.
|
||||
*/
|
||||
public function test_font_slim_filter_override() {
|
||||
$original_url = 'https://fonts.googleapis.com/css?family=Roboto:100,200,300,400,500,600,700,800,900&display=swap';
|
||||
|
||||
// Add filter to override Roboto weights to just 400.
|
||||
add_filter(
|
||||
'peptide_starter_perf_font_weights',
|
||||
function ( $config ) {
|
||||
$config['Roboto'] = array( 400 );
|
||||
return $config;
|
||||
}
|
||||
);
|
||||
|
||||
$slimmed_url = peptide_starter_perf_slim_google_fonts( $original_url, 'google-fonts-roboto' );
|
||||
|
||||
// Should contain only 400, not 500 or 700.
|
||||
$this->assertStringContainsString( '400', $slimmed_url );
|
||||
$this->assertStringNotContainsString( '500', $slimmed_url );
|
||||
$this->assertStringNotContainsString( '700', $slimmed_url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: Preconnect hints are added.
|
||||
*
|
||||
* The preconnect filter should add fonts.googleapis.com and fonts.gstatic.com.
|
||||
*/
|
||||
public function test_preconnect_hints_added() {
|
||||
$urls = array();
|
||||
$result = peptide_starter_perf_resource_hints( $urls, 'preconnect' );
|
||||
|
||||
$this->assertContains( 'https://fonts.googleapis.com', $result );
|
||||
$this->assertContains( 'https://fonts.gstatic.com', $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: Preconnect ignores other hint types.
|
||||
*
|
||||
* Only 'preconnect' relationship should get font URLs; dns-prefetch etc. should not.
|
||||
*/
|
||||
public function test_preconnect_ignores_other_relationships() {
|
||||
$urls = array();
|
||||
$result = peptide_starter_perf_resource_hints( $urls, 'dns-prefetch' );
|
||||
|
||||
// Should not add font URLs for dns-prefetch.
|
||||
$this->assertNotContains( 'https://fonts.googleapis.com', $result );
|
||||
$this->assertNotContains( 'https://fonts.gstatic.com', $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: Defer applied to cookie-notice script.
|
||||
*
|
||||
* The script_loader_tag filter should add 'defer' to cookie-notice-front.
|
||||
*/
|
||||
public function test_defer_cookie_notice() {
|
||||
$tag = '<script src="http://example.com/cookie-notice.js"></script>';
|
||||
$result = peptide_starter_perf_defer_cookie_notice( $tag, 'cookie-notice-front', 'http://example.com/cookie-notice.js' );
|
||||
|
||||
$this->assertStringContainsString( 'defer', $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: Defer not applied to other scripts.
|
||||
*
|
||||
* The defer filter should only affect cookie-notice-front, not other handles.
|
||||
*/
|
||||
public function test_defer_not_applied_to_other_scripts() {
|
||||
$tag = '<script src="http://example.com/other.js"></script>';
|
||||
$result = peptide_starter_perf_defer_cookie_notice( $tag, 'other-script', 'http://example.com/other.js' );
|
||||
|
||||
// Should not have defer added.
|
||||
$this->assertStringNotContainsString( 'defer', $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: page_uses_elementor helper returns false for non-singular.
|
||||
*/
|
||||
public function test_page_uses_elementor_non_singular() {
|
||||
// Go to homepage (not singular).
|
||||
$this->go_to( home_url( '/' ) );
|
||||
|
||||
$result = peptide_starter_page_uses_elementor( 0 );
|
||||
$this->assertFalse( $result );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user