diff --git a/CHANGELOG.md b/CHANGELOG.md
index f95e2873a6..b1c0940fa3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [3.23.0](https://github.com/Parsely/wp-parsely/compare/3.22.1...3.23.0) - 2026-04-06
+
+### Added
+
+- Add `wp_parsely_tracker_url` filter ([#4108](https://github.com/Parsely/wp-parsely/pull/4108))
+
+### Changed
+
+- Update post list stats column to use icons ([#4136](https://github.com/Parsely/wp-parsely/pull/4136))
+
+### Dependency Updates
+
+- The list of all dependency updates for this release is available [here](https://github.com/Parsely/wp-parsely/pulls?q=is%3Apr+is%3Amerged+milestone%3A3.23.0+label%3ADeps).
+
## [3.22.1](https://github.com/Parsely/wp-parsely/compare/3.22.0...3.22.1) - 2026-03-30
### Fixed
diff --git a/README.md b/README.md
index e33776f989..23f3a05ff5 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Parse.ly
-Stable tag: 3.22.1
+Stable tag: 3.23.0
Requires at least: 6.0
Tested up to: 6.9
Requires PHP: 7.4
diff --git a/build/content-helper/post-list-stats-rtl.css b/build/content-helper/post-list-stats-rtl.css
index f253a4177c..1c41517e26 100644
--- a/build/content-helper/post-list-stats-rtl.css
+++ b/build/content-helper/post-list-stats-rtl.css
@@ -1 +1 @@
-.column-parsely-stats{width:200px}@media only screen and (max-width:991px){.column-parsely-stats{width:150px}}.column-parsely-stats .parsely-post-stats{color:#959da5;line-height:18px;min-height:54px}.column-parsely-stats .parsely-post-stats-placeholder{letter-spacing:2px}.column-parsely-stats .parsely-post-page-views{color:#000}
+.column-parsely-stats .parsely-post-stats{cursor:default;display:flex;flex-wrap:wrap;gap:2px 8px;min-height:54px}.column-parsely-stats .parsely-post-stats-placeholder{color:#959da5;letter-spacing:2px}.column-parsely-stats .parsely-post-avg-time,.column-parsely-stats .parsely-post-page-views,.column-parsely-stats .parsely-post-visitors{align-items:center;display:flex;gap:4px}.column-parsely-stats .parsely-post-avg-time .dashicons,.column-parsely-stats .parsely-post-page-views .dashicons,.column-parsely-stats .parsely-post-visitors .dashicons{font-size:16px;height:16px;width:16px}
diff --git a/build/content-helper/post-list-stats.asset.php b/build/content-helper/post-list-stats.asset.php
index 3d000ba2a7..4ba116213e 100644
--- a/build/content-helper/post-list-stats.asset.php
+++ b/build/content-helper/post-list-stats.asset.php
@@ -1 +1 @@
- array(), 'version' => '150ace49707b05c46bdb');
+ array('wp-i18n'), 'version' => '0f380989aa4951c0833b');
diff --git a/build/content-helper/post-list-stats.css b/build/content-helper/post-list-stats.css
index f253a4177c..1c41517e26 100644
--- a/build/content-helper/post-list-stats.css
+++ b/build/content-helper/post-list-stats.css
@@ -1 +1 @@
-.column-parsely-stats{width:200px}@media only screen and (max-width:991px){.column-parsely-stats{width:150px}}.column-parsely-stats .parsely-post-stats{color:#959da5;line-height:18px;min-height:54px}.column-parsely-stats .parsely-post-stats-placeholder{letter-spacing:2px}.column-parsely-stats .parsely-post-page-views{color:#000}
+.column-parsely-stats .parsely-post-stats{cursor:default;display:flex;flex-wrap:wrap;gap:2px 8px;min-height:54px}.column-parsely-stats .parsely-post-stats-placeholder{color:#959da5;letter-spacing:2px}.column-parsely-stats .parsely-post-avg-time,.column-parsely-stats .parsely-post-page-views,.column-parsely-stats .parsely-post-visitors{align-items:center;display:flex;gap:4px}.column-parsely-stats .parsely-post-avg-time .dashicons,.column-parsely-stats .parsely-post-page-views .dashicons,.column-parsely-stats .parsely-post-visitors .dashicons{font-size:16px;height:16px;width:16px}
diff --git a/build/content-helper/post-list-stats.js b/build/content-helper/post-list-stats.js
index 535ac41408..5936b41bfe 100644
--- a/build/content-helper/post-list-stats.js
+++ b/build/content-helper/post-list-stats.js
@@ -1 +1,4 @@
-!function(){"use strict";!function(){function s(){return document.querySelectorAll(".parsely-post-stats")}document.addEventListener("DOMContentLoaded",(function(){!function(){if(null===(n=s())||void 0===n||n.forEach((function(s){s.innerHTML="—"})),window.wpParselyPostsStatsResponse){var n,e,t,r,a=JSON.parse(window.wpParselyPostsStatsResponse);if(null==a?void 0:a.error)return e=a.error,void(null!==(r=document.querySelector(".wp-header-end"))&&(r.innerHTML+=(void 0===(t=e.htmlMessage)&&(t=""),'
'.concat(t,"
"))));(null==a?void 0:a.data)&&function(n){var e;n&&(null===(e=s())||void 0===e||e.forEach((function(s){var e=s.getAttribute("data-stats-key");if(null!==e&&void 0!==n[e]){var t=n[e];s.innerHTML="",t.page_views&&(s.innerHTML+=''.concat(t.page_views,"
")),t.visitors&&(s.innerHTML+=''.concat(t.visitors,"
")),t.avg_time&&(s.innerHTML+=''.concat(t.avg_time,"
"))}})))}(a.data)}}()}))}()}();
\ No newline at end of file
+!function(){"use strict";!function(){var s=window.wp.i18n;function e(s,e,n,i){return'')+'')+''.concat(n,"")+''.concat(i,"")+""}function n(){return document.querySelectorAll(".parsely-post-stats")}document.addEventListener("DOMContentLoaded",(function(){!function(){if(null===(i=n())||void 0===i||i.forEach((function(s){s.innerHTML="—"})),window.wpParselyPostsStatsResponse){var i,t,a,r,o=JSON.parse(window.wpParselyPostsStatsResponse);if(null==o?void 0:o.error)return t=o.error,void(null!==(r=document.querySelector(".wp-header-end"))&&(r.innerHTML+=(void 0===(a=t.htmlMessage)&&(a=""),''.concat(a,"
"))));(null==o?void 0:o.data)&&function(i){var t;i&&(null===(t=n())||void 0===t||t.forEach((function(n){var t=n.getAttribute("data-stats-key");if(null!==t&&void 0!==i[t]){var a=i[t];n.innerHTML="",a.page_views&&(n.innerHTML+=e("parsely-post-page-views","visibility",/* translators: %s: number of page views */ /* translators: %s: number of page views */
+(0,s.sprintf)((0,s.__)("Page views: %s","wp-parsely"),a.page_views),a.page_views)),a.visitors&&(n.innerHTML+=e("parsely-post-visitors","groups",/* translators: %s: number of visitors */ /* translators: %s: number of visitors */
+(0,s.sprintf)((0,s.__)("Visitors: %s","wp-parsely"),a.visitors),a.visitors)),a.avg_time&&(n.innerHTML+=e("parsely-post-avg-time","clock",/* translators: %s: average time spent on post */ /* translators: %s: average time spent on post */
+(0,s.sprintf)((0,s.__)("Avg. time: %s","wp-parsely"),a.avg_time),a.avg_time))}})))}(o.data)}}()}))}()}();
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 6cf3a1e153..f03bc3f1c7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "wp-parsely",
- "version": "3.22.1",
+ "version": "3.23.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wp-parsely",
- "version": "3.22.1",
+ "version": "3.23.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@types/js-cookie": "^3.0.6",
@@ -20627,16 +20627,16 @@
}
},
"node_modules/lodash": {
- "version": "4.17.23",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
- "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"dev": true,
"license": "MIT"
},
"node_modules/lodash-es": {
- "version": "4.17.23",
- "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz",
- "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
+ "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
"dev": true,
"license": "MIT"
},
@@ -21495,9 +21495,9 @@
}
},
"node_modules/node-forge": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz",
- "integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz",
+ "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==",
"dev": true,
"license": "(BSD-3-Clause OR GPL-2.0)",
"engines": {
diff --git a/package.json b/package.json
index 823bf22d06..1a0ff036c4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "wp-parsely",
- "version": "3.22.1",
+ "version": "3.23.0",
"private": true,
"description": "The Parse.ly plugin facilitates real-time and historical analytics to your content through a platform designed and built for digital publishing.",
"author": "parsely, hbbtstar, jblz, mikeyarce, GaryJ, parsely_mike, acicovic, mehmoodak, vaurdan",
@@ -94,7 +94,7 @@
"express": "^4.22.0",
"glob": "^10.5.0",
"js-yaml@^3": "3.14.2",
- "node-forge": "^1.3.2",
+ "node-forge": "^1.4.0",
"puppeteer-core": "^23.1.0",
"qs": "^6.14.1"
},
diff --git a/src/Utils/class-utils.php b/src/Utils/class-utils.php
index 625337c9af..faeaba24e5 100644
--- a/src/Utils/class-utils.php
+++ b/src/Utils/class-utils.php
@@ -280,7 +280,7 @@ public static function get_formatted_time( $seconds ): string {
return esc_html(
sprintf(
/* translators: 1: Number of seconds */
- __( '%1$d sec.', 'wp-parsely' ),
+ __( '%1$ds', 'wp-parsely' ),
round( $seconds )
)
);
diff --git a/src/class-parsely.php b/src/class-parsely.php
index 239cd18ab3..8d617b079a 100644
--- a/src/class-parsely.php
+++ b/src/class-parsely.php
@@ -373,8 +373,19 @@ public function get_rest_api_controller(): REST_API_Controller {
public function get_tracker_url(): string {
if ( $this->site_id_is_set() ) {
$tracker_url = 'https://cdn.parsely.com/keys/' . $this->get_site_id() . '/p.js';
- return esc_url( $tracker_url );
+
+ /**
+ * Filters the URL of the Parse.ly tracker script.
+ *
+ * The filtered value gets sanitized with {@see esc_url_raw()}.
+ *
+ * @since 3.23.0
+ *
+ * @param string $tracker_url The URL of the tracker script.
+ */
+ return esc_url_raw( apply_filters( 'wp_parsely_tracker_url', $tracker_url ) );
}
+
return '';
}
diff --git a/src/content-helper/post-list-stats/class-post-list-stats.php b/src/content-helper/post-list-stats/class-post-list-stats.php
index 9ea12d9a47..62808ee69e 100644
--- a/src/content-helper/post-list-stats/class-post-list-stats.php
+++ b/src/content-helper/post-list-stats/class-post-list-stats.php
@@ -162,7 +162,7 @@ public function enqueue_parsely_stats_styles(): void {
wp_enqueue_style(
static::get_style_id(),
$built_assets_url . 'post-list-stats.css',
- $admin_settings_asset['dependencies'],
+ array(),
$admin_settings_asset['version']
);
}
@@ -177,7 +177,7 @@ public function enqueue_parsely_stats_styles(): void {
*/
public function add_parsely_stats_column_on_list_view( array $columns ): array {
if ( $this->is_tracked_as_post_type() ) {
- $columns['parsely-stats'] = __( 'Parse.ly Stats (7d)', 'wp-parsely' );
+ $columns['parsely-stats'] = __( 'Parse.ly (7d)', 'wp-parsely' );
}
return $columns;
@@ -210,7 +210,7 @@ public function update_published_times_and_show_placeholder( string $column_name
$stats_key = $this->get_unique_stats_key_of_current_post();
?>
- ...
+ —
Utils::get_formatted_number( (string) $views ) . ' ' . _n( 'page view', 'page views', $views, 'wp-parsely' ),
- 'visitors' => Utils::get_formatted_number( (string) $visitors ) . ' ' . _n( 'visitor', 'visitors', $visitors, 'wp-parsely' ),
- 'avg_time' => Utils::get_formatted_time( $engaged_seconds ) . ' ' . __( 'avg time', 'wp-parsely' ),
+ 'page_views' => Utils::get_formatted_number( (string) $views ),
+ 'visitors' => Utils::get_formatted_number( (string) $visitors ),
+ 'avg_time' => Utils::get_formatted_time( $engaged_seconds ),
);
$parsely_stats_map[ $key ] = $stats;
diff --git a/src/content-helper/post-list-stats/post-list-stats.scss b/src/content-helper/post-list-stats/post-list-stats.scss
index 2fb0480f88..9c6c9c6259 100644
--- a/src/content-helper/post-list-stats/post-list-stats.scss
+++ b/src/content-helper/post-list-stats/post-list-stats.scss
@@ -1,21 +1,28 @@
.column-parsely-stats {
- width: 200px;
-
- @media only screen and (max-width: 991px) {
- width: 150px;
- }
-
.parsely-post-stats {
- color: #959da5;
+ cursor: default;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 2px 8px;
min-height: 54px;
- line-height: 18px;
}
.parsely-post-stats-placeholder {
+ color: #959da5;
letter-spacing: 2px;
}
- .parsely-post-page-views {
- color: #000;
+ .parsely-post-page-views,
+ .parsely-post-visitors,
+ .parsely-post-avg-time {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+
+ .dashicons {
+ font-size: 16px;
+ width: 16px;
+ height: 16px;
+ }
}
}
diff --git a/src/content-helper/post-list-stats/post-list-stats.ts b/src/content-helper/post-list-stats/post-list-stats.ts
index e05e9541c9..17e46e227c 100644
--- a/src/content-helper/post-list-stats/post-list-stats.ts
+++ b/src/content-helper/post-list-stats/post-list-stats.ts
@@ -1,3 +1,4 @@
+import { __, sprintf } from '@wordpress/i18n';
import { ParselyAPIError, ParselyAPIErrorInfo } from './common.interface';
export interface ParselyPostsStatsResponse extends ParselyAPIError {
@@ -71,19 +72,50 @@ function showParselyStats( parselyStatsMap: ParselyStatsMap ): void {
statsElement.innerHTML = '';
if ( stats.page_views ) {
- statsElement.innerHTML += `${ stats.page_views }
`;
+ statsElement.innerHTML += getStatSpan(
+ 'parsely-post-page-views', 'visibility',
+ /* translators: %s: number of page views */
+ sprintf( __( 'Page views: %s', 'wp-parsely' ), stats.page_views ),
+ stats.page_views
+ );
}
if ( stats.visitors ) {
- statsElement.innerHTML += `${ stats.visitors }
`;
+ statsElement.innerHTML += getStatSpan(
+ 'parsely-post-visitors', 'groups',
+ /* translators: %s: number of visitors */
+ sprintf( __( 'Visitors: %s', 'wp-parsely' ), stats.visitors ),
+ stats.visitors
+ );
}
if ( stats.avg_time ) {
- statsElement.innerHTML += `${ stats.avg_time }
`;
+ statsElement.innerHTML += getStatSpan(
+ 'parsely-post-avg-time', 'clock',
+ /* translators: %s: average time spent on post */
+ sprintf( __( 'Avg. time: %s', 'wp-parsely' ), stats.avg_time ),
+ stats.avg_time
+ );
}
} );
}
+/**
+ * Gets HTML for a single stat metric span with an icon and screen-reader label.
+ *
+ * @param {string} className CSS class for the outer span.
+ * @param {string} dashicon Dashicon class name (without `dashicons` prefix).
+ * @param {string} srText Full screen reader text (including the value).
+ * @param {string} value The metric value to display visually.
+ */
+function getStatSpan( className: string, dashicon: string, srText: string, value: string ): string {
+ return `` +
+ `` +
+ `${ srText }` +
+ `${ value }` +
+ ``;
+}
+
/**
* Shows Parse.ly Stats error as a wp-admin error notice.
*
diff --git a/tests/Integration/ContentHelper/ContentHelperPostListStatsTest.php b/tests/Integration/ContentHelper/ContentHelperPostListStatsTest.php
index 7ce13efc8a..29a497df34 100644
--- a/tests/Integration/ContentHelper/ContentHelperPostListStatsTest.php
+++ b/tests/Integration/ContentHelper/ContentHelperPostListStatsTest.php
@@ -41,7 +41,7 @@ final class ContentHelperPostListStatsTest extends ContentHelperFeatureTest {
*
* @var string
*/
- private static $parsely_stats_column_header = 'Parse.ly Stats (7d)';
+ private static $parsely_stats_column_header = 'Parse.ly (7d)';
/**
* Internal variable.
@@ -604,7 +604,7 @@ private function show_content_on_parsely_stats_column( array $posts, string $pos
* @return string
*/
private function get_parsely_stats_placeholder_content( string $key ): string {
- return " \n ...\n
\n ";
+ return " \n —\n
\n ";
}
/**
@@ -1118,6 +1118,22 @@ public function test_parsely_stats_response_on_valid_post_type_and_having_data_f
'avg_engaged' => 0.01,
),
),
+ array(
+ 'url' => 'http://example.com/2010/01/14/title-14-publish',
+ 'metrics' => array(
+ 'views' => 1,
+ 'visitors' => 1,
+ 'avg_engaged' => 1.0, // Exactly 60 seconds: s -> m:ss boundary.
+ ),
+ ),
+ array(
+ 'url' => 'http://example.com/2010/01/15/title-15-publish',
+ 'metrics' => array(
+ 'views' => 1,
+ 'visitors' => 1,
+ 'avg_engaged' => 60.0, // Exactly 3600 seconds: m:ss -> h:mm:ss boundary.
+ ),
+ ),
);
$res = $this->get_parsely_stats_response(
$posts,
@@ -1134,64 +1150,74 @@ public function test_parsely_stats_response_on_valid_post_type_and_having_data_f
self::assertSame(
array(
'/2010/01/01/title-1-publish' => array(
- 'page_views' => '0 page views',
- 'visitors' => '0 visitors',
- 'avg_time' => '0 sec. avg time',
+ 'page_views' => '0',
+ 'visitors' => '0',
+ 'avg_time' => '0s', // 0 seconds.
),
'/2010/01/02/title-2-publish' => array(
- 'page_views' => '1 page view',
- 'visitors' => '1 visitor',
- 'avg_time' => '0 sec. avg time', // 0.3 seconds.
+ 'page_views' => '1',
+ 'visitors' => '1',
+ 'avg_time' => '0s', // 0.3 seconds.
),
'/2010/01/03/title-3-publish' => array(
- 'page_views' => '1 page view',
- 'visitors' => '1 visitor',
- 'avg_time' => '1 sec. avg time', // 0.6 seconds.
+ 'page_views' => '1',
+ 'visitors' => '1',
+ 'avg_time' => '1s', // 0.6 seconds.
),
'/2010/01/04/title-4-publish' => array(
- 'page_views' => '1 page view',
- 'visitors' => '1 visitor',
- 'avg_time' => '59 sec. avg time', // 59 seconds.
+ 'page_views' => '1',
+ 'visitors' => '1',
+ 'avg_time' => '59s', // 59 seconds.
),
'/2010/01/05/title-5-publish' => array(
- 'page_views' => '1.1k page views',
- 'visitors' => '1.1M visitors',
- 'avg_time' => '1:00 avg time', // 59.52 seconds.
+ 'page_views' => '1.1k',
+ 'visitors' => '1.1M',
+ 'avg_time' => '1:00', // 59.52 seconds.
),
'/2010/01/06/title-6-publish' => array(
- 'page_views' => '1.1k page views',
- 'visitors' => '1.1M visitors',
- 'avg_time' => '1:00 avg time', // 59.7 seconds.
+ 'page_views' => '1.1k',
+ 'visitors' => '1.1M',
+ 'avg_time' => '1:00', // 59.7 seconds.
),
'/2010/01/07/title-7-publish' => array(
- 'page_views' => '1 page view',
- 'visitors' => '1 visitor',
- 'avg_time' => '1:05 avg time', // 65 seconds.
+ 'page_views' => '1',
+ 'visitors' => '1',
+ 'avg_time' => '1:05', // 65 seconds.
),
'/2010/01/08/title-8-publish' => array(
- 'page_views' => '1.1k page views',
- 'visitors' => '1.1M visitors',
- 'avg_time' => '1:06 avg time', // 66 seconds.
+ 'page_views' => '1.1k',
+ 'visitors' => '1.1M',
+ 'avg_time' => '1:06', // 66 seconds.
),
'/2010/01/09/title-9-publish' => array(
- 'page_views' => '1.1k page views',
- 'visitors' => '1.1M visitors',
- 'avg_time' => '1:01:05 avg time', // 3665 seconds.
+ 'page_views' => '1.1k',
+ 'visitors' => '1.1M',
+ 'avg_time' => '1:01:05', // 3665 seconds.
),
'/2010/01/11/title-11-publish' => array(
- 'page_views' => '1 page view',
- 'visitors' => '0 visitors',
- 'avg_time' => '0 sec. avg time',
+ 'page_views' => '1',
+ 'visitors' => '0',
+ 'avg_time' => '0s',
),
'/2010/01/12/title-12-publish' => array(
- 'page_views' => '0 page views',
- 'visitors' => '1 visitor',
- 'avg_time' => '0 sec. avg time',
+ 'page_views' => '0',
+ 'visitors' => '1',
+ 'avg_time' => '0s',
),
'/2010/01/13/title-13-publish' => array(
- 'page_views' => '0 page views',
- 'visitors' => '0 visitors',
- 'avg_time' => '1 sec. avg time', // 0.6 seconds.
+ 'page_views' => '0',
+ 'visitors' => '0',
+ 'avg_time' => '1s', // 0.6 seconds.
+ ),
+ '/2010/01/14/title-14-publish' => array(
+ 'page_views' => '1',
+ 'visitors' => '1',
+ 'avg_time' => '1:00', // Exactly 60 seconds: s -> m:ss boundary.
+ ),
+ '/2010/01/15/title-15-publish' => array(
+ 'page_views' => '1',
+ 'visitors' => '1',
+ 'avg_time' => '1:00:00', // Exactly 3600 seconds: m:ss -> h:mm:ss boundary.
),
),
$res['data'] ?? null
@@ -1238,9 +1264,9 @@ public function test_parsely_stats_response_on_valid_hierarchal_post_type_and_ha
self::assertSame(
array(
'/2010/01/01/title-1-publish' => array(
- 'page_views' => '1.1k page views',
- 'visitors' => '1.1M visitors',
- 'avg_time' => '1:06 avg time',
+ 'page_views' => '1.1k',
+ 'visitors' => '1.1M',
+ 'avg_time' => '1:06',
),
),
$res['data'] ?? null
diff --git a/tests/Integration/OtherTest.php b/tests/Integration/OtherTest.php
index b2f63ba764..575478e903 100644
--- a/tests/Integration/OtherTest.php
+++ b/tests/Integration/OtherTest.php
@@ -401,4 +401,29 @@ public function test_get_tracker_no_site_id(): void {
$expected = '';
self::assertSame( $expected, self::$parsely->get_tracker_url() );
}
+
+ /**
+ * Verifies that the tracker URL can be overridden via the
+ * wp_parsely_tracker_url filter.
+ *
+ * @since 3.23.0
+ *
+ * @covers \Parsely\Parsely::get_tracker_url
+ * @uses \Parsely\Parsely::site_id_is_set
+ * @uses \Parsely\Parsely::get_site_id
+ * @uses \Parsely\Parsely::get_options
+ */
+ public function test_get_tracker_url_filter(): void {
+ self::set_options( array( 'apikey' => self::VALID_SITE_ID ) );
+ $custom_url = 'https://my-first-party-cdn.example.com/p.js';
+
+ add_filter(
+ 'wp_parsely_tracker_url',
+ function () use ( $custom_url ): string {
+ return $custom_url;
+ }
+ );
+
+ self::assertSame( $custom_url, self::$parsely->get_tracker_url() );
+ }
}
diff --git a/tests/Unit/Utils/UtilsTest.php b/tests/Unit/Utils/UtilsTest.php
index fbf67132d6..252a7c51cb 100644
--- a/tests/Unit/Utils/UtilsTest.php
+++ b/tests/Unit/Utils/UtilsTest.php
@@ -159,27 +159,27 @@ public function test_get_formatted_time(): void {
$tests_data = array(
array(
'args' => array( 'seconds' => 0 ),
- 'expected_output' => '0 sec.',
+ 'expected_output' => '0s',
'msg' => 'Should show seconds.',
),
array(
'args' => array( 'seconds' => 0.5 ),
- 'expected_output' => '1 sec.',
+ 'expected_output' => '1s',
'msg' => 'Should show seconds.',
),
array(
'args' => array( 'seconds' => 0.5000 ),
- 'expected_output' => '1 sec.',
+ 'expected_output' => '1s',
'msg' => 'Should show seconds.',
),
array(
'args' => array( 'seconds' => 0.51 ),
- 'expected_output' => '1 sec.',
+ 'expected_output' => '1s',
'msg' => 'Should show seconds.',
),
array(
'args' => array( 'seconds' => 59 ),
- 'expected_output' => '59 sec.',
+ 'expected_output' => '59s',
'msg' => 'Should show seconds.',
),
array(
diff --git a/tests/e2e/utils.ts b/tests/e2e/utils.ts
index 66320221f9..282829219c 100644
--- a/tests/e2e/utils.ts
+++ b/tests/e2e/utils.ts
@@ -8,7 +8,7 @@ import { type Page } from '@playwright/test';
*/
import { Admin } from '@wordpress/e2e-test-utils-playwright';
-export const PLUGIN_VERSION = '3.22.1';
+export const PLUGIN_VERSION = '3.23.0';
export const VALID_SITE_ID = 'demoaccount.parsely.com';
export const INVALID_SITE_ID = 'invalid.parsely.com';
export const VALID_API_SECRET = 'valid_api_secret';
diff --git a/tests/js/content-helper/post-list-stats.test.tsx b/tests/js/content-helper/post-list-stats.test.tsx
index 2ed22d821b..08cbdecb9b 100644
--- a/tests/js/content-helper/post-list-stats.test.tsx
+++ b/tests/js/content-helper/post-list-stats.test.tsx
@@ -22,11 +22,11 @@ describe( 'src/content-helper/post-list-stats', () => {
window.wpParselyPostsStatsResponse = JSON.stringify( {
data: {
- 'key-1': { page_views: '0 page views', visitors: '0 visitors', avg_time: '0 sec. avg time' },
- 'key-3': { page_views: '3 page views', visitors: '3 visitors', avg_time: '3 sec. avg time' },
- 'key-4': { page_views: '4 page views' },
- 'key-5': { visitors: '5 visitors' },
- 'key-6': { avg_time: '6 sec. avg time' },
+ 'key-1': { page_views: '0', visitors: '0', avg_time: '0s' },
+ 'key-3': { page_views: '3', visitors: '3', avg_time: '3s' },
+ 'key-4': { page_views: '4' },
+ 'key-5': { visitors: '5' },
+ 'key-6': { avg_time: '6s' },
},
error: null,
} );
@@ -40,32 +40,32 @@ describe( 'src/content-helper/post-list-stats', () => {
expect( allPostStats.length ).toBe( 7 );
const postStat1 = getPostStatsElement( 'key-1' );
- expect( getPageViewsElement( postStat1 )?.textContent ).toBe( '0 page views' );
- expect( getVisitorsElement( postStat1 )?.textContent ).toBe( '0 visitors' );
- expect( getAvgTimeElement( postStat1 )?.textContent ).toBe( '0 sec. avg time' );
+ expect( getPageViewsElement( postStat1 )?.textContent ).toBe( 'Page views: 0' );
+ expect( getVisitorsElement( postStat1 )?.textContent ).toBe( 'Visitors: 0' );
+ expect( getAvgTimeElement( postStat1 )?.textContent ).toBe( 'Avg. time: 0s' );
const postStat2 = getPostStatsElement( 'key-2' );
expect( postStat2?.textContent ).toBe( '—' );
const postStat3 = getPostStatsElement( 'key-3' );
- expect( getPageViewsElement( postStat3 )?.textContent ).toBe( '3 page views' );
- expect( getVisitorsElement( postStat3 )?.textContent ).toBe( '3 visitors' );
- expect( getAvgTimeElement( postStat3 )?.textContent ).toBe( '3 sec. avg time' );
+ expect( getPageViewsElement( postStat3 )?.textContent ).toBe( 'Page views: 3' );
+ expect( getVisitorsElement( postStat3 )?.textContent ).toBe( 'Visitors: 3' );
+ expect( getAvgTimeElement( postStat3 )?.textContent ).toBe( 'Avg. time: 3s' );
const postStat4 = getPostStatsElement( 'key-4' );
- expect( getPageViewsElement( postStat4 )?.textContent ).toBe( '4 page views' );
+ expect( getPageViewsElement( postStat4 )?.textContent ).toBe( 'Page views: 4' );
expect( getVisitorsElement( postStat4 ) ).toBeNull();
expect( getAvgTimeElement( postStat4 ) ).toBeNull();
const postStat5 = getPostStatsElement( 'key-5' );
expect( getPageViewsElement( postStat5 ) ).toBeNull();
- expect( getVisitorsElement( postStat5 )?.textContent ).toBe( '5 visitors' );
+ expect( getVisitorsElement( postStat5 )?.textContent ).toBe( 'Visitors: 5' );
expect( getAvgTimeElement( postStat5 ) ).toBeNull();
const postStat6 = getPostStatsElement( 'key-6' );
expect( getPageViewsElement( postStat6 ) ).toBeNull();
expect( getVisitorsElement( postStat6 ) ).toBeNull();
- expect( getAvgTimeElement( postStat6 )?.textContent ).toBe( '6 sec. avg time' );
+ expect( getAvgTimeElement( postStat6 )?.textContent ).toBe( 'Avg. time: 6s' );
const postStat7 = getPostStatsElement( 'key-7' );
expect( postStat7?.textContent ).toBe( '—' );
@@ -123,7 +123,7 @@ function getParselyStatsPlaceholders( numOfPlaceholders: number ): React.JSX.Ele
placeholders.push(
// This placeholder content should be kept in sync with Post_List_Stats
- ...
+ —
);
}
@@ -144,15 +144,15 @@ function getPostStatsElement( key: string ): Element | null {
}
function getPageViewsElement( postStatElement: Element | null ): Element | null {
- return postStatElement ? postStatElement.querySelector( `.parsely-post-page-views` ) : null;
+ return postStatElement ? postStatElement.querySelector( '.parsely-post-page-views .screen-reader-text' ) : null;
}
function getVisitorsElement( postStatElement: Element | null ): Element | null {
- return postStatElement ? postStatElement.querySelector( `.parsely-post-visitors` ) : null;
+ return postStatElement ? postStatElement.querySelector( '.parsely-post-visitors .screen-reader-text' ) : null;
}
function getAvgTimeElement( postStatElement: Element | null ): Element | null {
- return postStatElement ? postStatElement.querySelector( `.parsely-post-avg-time` ) : null;
+ return postStatElement ? postStatElement.querySelector( '.parsely-post-avg-time .screen-reader-text' ) : null;
}
function getStatsErrorElement(): Element | null {
diff --git a/wp-parsely.php b/wp-parsely.php
index 59dbc003e4..28c368b689 100644
--- a/wp-parsely.php
+++ b/wp-parsely.php
@@ -11,7 +11,7 @@
* Plugin Name: Parse.ly
* Plugin URI: https://docs.parse.ly/wordpress
* Description: This plugin makes it a snap to add Parse.ly tracking code and metadata to your WordPress blog.
- * Version: 3.22.1
+ * Version: 3.23.0
* Author: Parse.ly
* Author URI: https://www.parse.ly
* Text Domain: wp-parsely
@@ -50,7 +50,7 @@
return;
}
-const PARSELY_VERSION = '3.22.1';
+const PARSELY_VERSION = '3.23.0';
const PARSELY_FILE = __FILE__;
const PARSELY_DATA_SCHEMA_VERSION = '1';
const PARSELY_CACHE_GROUP = 'wp-parsely';