| @@ -60,10 +60,11 @@ | ||
| 60 | 60 | // Provider-neutral: the panel runs whichever provider the site has |
| 61 | 61 | // configured (PageSpeed Insights by default, no API key needed). |
| 62 | 62 | // The Hub-run test has its own copy and is gated behind |
| 63 | 63 | // hub_speed_test_enabled(), so this line must not promise it. |
| 64 | - 'description' => __( 'Run a PageSpeed Insights or GTmetrix audit from the dashboard and keep the history next to your TTFB benchmark.', 'xspeed' ), | |
| 64 | + 'description' => __( 'Run a PageSpeed Insights or GTmetrix test and keep a history of the scores.', 'xspeed' ), | |
| 65 | 65 | 'custom_panel' => 'ScorePanel', |
| 66 | + 'group' => 'insights', | |
| 66 | 67 | ); |
| 67 | 68 | } |
| 68 | 69 | |
| 69 | 70 | public function settings_schema(): array { |
| @@ -79,9 +80,9 @@ | ||
| 79 | 80 | // run turns this on rather than refusing (#425). What it |
| 80 | 81 | // still guards is everything that is NOT a Test press — an |
| 81 | 82 | // optimize run measuring its own effect, for instance. |
| 82 | 83 | // Switch it off (REST/CLI) and nothing contacts a provider. |
| 83 | - 'description' => __( 'Turns on automatically the first time you run a speed test — the button press is the consent. Switch it off to stop every feature, including optimize runs, from contacting a score provider.', 'xspeed' ), | |
| 84 | + 'description' => __( 'Turns on the first time you run a speed test. Switch it off and no feature, not even an optimize run, contacts a test provider.', 'xspeed' ), | |
| 84 | 85 | // No dashboard control: the Test press manages it, and a |
| 85 | 86 | // visible switch that gates a button elsewhere was the |
| 86 | 87 | // confusion #425 removed. Hidden fields are skipped by the |
| 87 | 88 | // panel renderer and by settings search. |
| @@ -94,19 +95,20 @@ | ||
| 94 | 95 | 'option_labels' => array( |
| 95 | 96 | 'psi' => 'PageSpeed Insights', |
| 96 | 97 | 'gtmetrix' => 'GTmetrix', |
| 97 | 98 | ), |
| 98 | - 'label' => __( 'Provider', 'xspeed' ), | |
| 99 | - 'description' => __( 'PageSpeed Insights works without an API key. GTmetrix requires one.', 'xspeed' ), | |
| 99 | + 'label' => __( 'Test provider', 'xspeed' ), | |
| 100 | + 'description' => __( 'PageSpeed Insights works without an API key. GTmetrix needs one.', 'xspeed' ), | |
| 100 | 101 | ), |
| 101 | 102 | 'psi_api_key' => array( |
| 102 | 103 | 'type' => 'secret', |
| 103 | 104 | 'default' => '', |
| 104 | 105 | 'label' => __( 'PageSpeed API key (optional)', 'xspeed' ), |
| 105 | - 'description' => __( 'Only needed if you hit Google\'s anonymous rate limit. Free from cloud.google.com.', 'xspeed' ), | |
| 106 | + 'description' => __( 'Only needed if Google starts refusing tests without a key. You can get a free key at cloud.google.com.', 'xspeed' ), | |
| 106 | 107 | // Rendered as a trailing "Check the documentation" link — |
| 107 | 108 | // descriptions themselves are plain text (#111). |
| 108 | 109 | 'doc_url' => 'https://xspeedcache.com/docs/pagespeed-insights-integration/', |
| 110 | + 'advanced' => true, | |
| 109 | 111 | 'dependsOn' => array( |
| 110 | 112 | 'field' => 'provider', |
| 111 | 113 | 'value' => 'psi', |
| 112 | 114 | ), |
| @@ -114,9 +116,9 @@ | ||
| 114 | 116 | 'gtmetrix_api_key' => array( |
| 115 | 117 | 'type' => 'secret', |
| 116 | 118 | 'default' => '', |
| 117 | 119 | 'label' => __( 'GTmetrix API key', 'xspeed' ), |
| 118 | - 'description' => __( 'Required — GTmetrix has no anonymous mode. Found in your GTmetrix account settings.', 'xspeed' ), | |
| 120 | + 'description' => __( 'GTmetrix does not run tests without a key. Find it in your GTmetrix account settings.', 'xspeed' ), | |
| 119 | 121 | 'dependsOn' => array( |
| 120 | 122 | 'field' => 'provider', |
| 121 | 123 | 'value' => 'gtmetrix', |
| 122 | 124 | ), |
| @@ -130,10 +132,10 @@ | ||
| 130 | 132 | 'default_strategy' => array( |
| 131 | 133 | 'type' => 'enum', |
| 132 | 134 | 'default' => 'mobile', |
| 133 | 135 | 'options' => array( 'mobile', 'desktop' ), |
| 134 | - 'label' => __( 'Strategy', 'xspeed' ), | |
| 135 | - 'description' => __( 'PageSpeed Insights only. Mobile is what Google ranks on.', 'xspeed' ), | |
| 136 | + 'label' => __( 'Device', 'xspeed' ), | |
| 137 | + 'description' => __( 'Test as a phone or a desktop visitor. Google ranks sites on the mobile result.', 'xspeed' ), | |
| 136 | 138 | 'dependsOn' => array( |
| 137 | 139 | 'field' => 'provider', |
| 138 | 140 | 'value' => 'psi', |
| 139 | 141 | ), |
| @@ -252,9 +254,12 @@ | ||
| 252 | 254 | } |
| 253 | 255 | |
| 254 | 256 | $started = Scan::start( |
| 255 | 257 | (string) ( $request->get_param( 'url' ) ?? '' ), |
| 256 | - (bool) $request->get_param( 'fresh' ) | |
| 258 | + (bool) $request->get_param( 'fresh' ), | |
| 259 | + // `both` (default), `desktop` or `mobile` — which Lighthouse runs | |
| 260 | + // the engine spends and therefore which device is the headline. | |
| 261 | + (string) ( $request->get_param( 'strategy' ) ?? 'both' ) | |
| 257 | 262 | ); |
| 258 | 263 | if ( is_wp_error( $started ) ) { |
| 259 | 264 | return $started; |
| 260 | 265 | } |
| @@ -291,8 +296,9 @@ | ||
| 291 | 296 | 'status' => 'running', |
| 292 | 297 | 'scan_id' => $polled['scan_id'], |
| 293 | 298 | 'step' => $polled['step'], |
| 294 | 299 | 'latest' => Scan::latest(), |
| 300 | + 'stored' => Scan::stored_state(), | |
| 295 | 301 | 'visibility' => Scan::visibility(), |
| 296 | 302 | 'private_supported' => Scan::private_supported(), |
| 297 | 303 | ) |
| 298 | 304 | ); |
| @@ -300,8 +306,9 @@ | ||
| 300 | 306 | return rest_ensure_response( |
| 301 | 307 | array( |
| 302 | 308 | 'status' => 'complete', |
| 303 | 309 | 'latest' => $polled, |
| 310 | + 'stored' => 'ok', | |
| 304 | 311 | 'visibility' => Scan::visibility(), |
| 305 | 312 | 'private_supported' => Scan::private_supported(), |
| 306 | 313 | ) |
| 307 | 314 | ); |
| @@ -310,8 +317,9 @@ | ||
| 310 | 317 | return rest_ensure_response( |
| 311 | 318 | array( |
| 312 | 319 | 'status' => 'idle', |
| 313 | 320 | 'latest' => Scan::latest(), |
| 321 | + 'stored' => Scan::stored_state(), | |
| 314 | 322 | 'visibility' => Scan::visibility(), |
| 315 | 323 | 'private_supported' => Scan::private_supported(), |
| 316 | 324 | ) |
| 317 | 325 | ); |
| @@ -809,8 +817,15 @@ | ||
| 809 | 817 | 'description' => 'Force a new scan instead of reusing a recent cached report.', |
| 810 | 818 | 'optional' => true, |
| 811 | 819 | ), |
| 812 | 820 | array( |
| 821 | + 'type' => 'assoc', | |
| 822 | + 'name' => 'strategy', | |
| 823 | + 'description' => 'Which Lighthouse runs to spend: both (default; desktop graded as the headline, mobile graded alongside), desktop, or mobile. One device costs half the engine quota and becomes the headline.', | |
| 824 | + 'optional' => true, | |
| 825 | + 'options' => array( 'both', 'desktop', 'mobile' ), | |
| 826 | + ), | |
| 827 | + array( | |
| 813 | 828 | 'type' => 'flag', |
| 814 | 829 | 'name' => 'wait', |
| 815 | 830 | 'description' => 'Poll until the scan finishes instead of returning immediately.', |
| 816 | 831 | 'optional' => true, |
| @@ -861,9 +876,10 @@ | ||
| 861 | 876 | } |
| 862 | 877 | |
| 863 | 878 | $started = Scan::start( |
| 864 | 879 | (string) ( $assoc_args['target'] ?? '' ), |
| 865 | - ! empty( $assoc_args['fresh'] ) | |
| 880 | + ! empty( $assoc_args['fresh'] ), | |
| 881 | + (string) ( $assoc_args['strategy'] ?? 'both' ) | |
| 866 | 882 | ); |
| 867 | 883 | if ( is_wp_error( $started ) ) { |
| 868 | 884 | \WP_CLI::error( $started->get_error_message() ); |
| 869 | 885 | return; |
| @@ -912,8 +928,12 @@ | ||
| 912 | 928 | return; |
| 913 | 929 | } |
| 914 | 930 | |
| 915 | 931 | $latest = Scan::latest(); |
| 932 | + if ( null === $latest && 'unreadable' === Scan::stored_state() ) { | |
| 933 | + \WP_CLI::log( 'The last scan report could not be read back from the database and was discarded. Run `wp xspeed scan run --wait` again.' ); | |
| 934 | + return; | |
| 935 | + } | |
| 916 | 936 | if ( null === $latest ) { |
| 917 | 937 | \WP_CLI::log( 'No scan yet. Run `wp xspeed scan run --wait`.' ); |
| 918 | 938 | return; |
| 919 | 939 | } |
| @@ -933,20 +953,30 @@ | ||
| 933 | 953 | ); |
| 934 | 954 | foreach ( (array) ( $r['dimensions'] ?? array() ) as $key => $d ) { |
| 935 | 955 | \WP_CLI::log( sprintf( ' %-9s %3s/100 (%s of %s pts)', $key, $d['score'] ?? '-', $d['earned'] ?? '-', $d['weight'] ?? '-' ) ); |
| 936 | 956 | } |
| 937 | - $lh = $r['measured']['lighthouse'] ?? null; | |
| 957 | + // Read the PER-DEVICE fields, never `measured.lighthouse`: that one is | |
| 958 | + // the graded run's score, which is desktop on a default scan, so | |
| 959 | + // printing it as "mobile" reported desktop under a mobile label. | |
| 960 | + $lhm = $r['measured']['lighthouse_mobile'] ?? null; | |
| 938 | 961 | $lhd = $r['measured']['lighthouse_desktop'] ?? null; |
| 939 | - if ( null !== $lh || null !== $lhd ) { | |
| 940 | - // Both strategies: the engine measures both and they diverge | |
| 941 | - // widely, so reporting only mobile states the harsher number as | |
| 942 | - // though it were the whole picture. Labelled, and never as "the | |
| 943 | - // score": different scale. | |
| 962 | + if ( null !== $lhm || null !== $lhd ) { | |
| 963 | + // Name only the devices actually measured. A desktop-only scan | |
| 964 | + // never ran mobile, and a dash there reads as "scored zero" | |
| 965 | + // rather than "not run". | |
| 966 | + $parts = array(); | |
| 967 | + if ( null !== $lhd ) { | |
| 968 | + $parts[] = 'desktop ' . $lhd . '/100'; | |
| 969 | + } | |
| 970 | + if ( null !== $lhm ) { | |
| 971 | + $parts[] = 'mobile ' . $lhm . '/100'; | |
| 972 | + } | |
| 973 | + $graded = (string) ( $r['device'] ?? '' ); | |
| 944 | 974 | \WP_CLI::log( |
| 945 | 975 | sprintf( |
| 946 | - 'Lighthouse: mobile %s, desktop %s - a different scale, one check inside the score above.', | |
| 947 | - null === $lh ? '-' : $lh . '/100', | |
| 948 | - null === $lhd ? '-' : $lhd . '/100' | |
| 976 | + 'Lighthouse: %s - a different scale, one check inside the score above.%s', | |
| 977 | + implode( ', ', $parts ), | |
| 978 | + '' !== $graded ? ' Graded on ' . $graded . '.' : '' | |
| 949 | 979 | ) |
| 950 | 980 | ); |
| 951 | 981 | } |
| 952 | 982 | \WP_CLI::log( 'Report: ' . ( $r['report_url'] ?? '' ) ); |