diff --git a/src/js/_enqueues/wp/updates.js b/src/js/_enqueues/wp/updates.js
index 59d0ef2e6b1cb..7055a91cc1f34 100644
--- a/src/js/_enqueues/wp/updates.js
+++ b/src/js/_enqueues/wp/updates.js
@@ -347,8 +347,17 @@
if ( 'undefined' !== typeof response.debug && window.console && window.console.log ) {
_.map( response.debug, function( message ) {
- // Remove all HTML tags and write a message to the console.
- window.console.log( wp.sanitize.stripTagsAndEncodeText( message ) );
+ if ( 'string' !== typeof message ) {
+ return;
+ }
+
+ /*
+ * Remove all HTML tags and decode character references, as the
+ * console displays plain text rather than HTML.
+ */
+ window.console.log(
+ new DOMParser().parseFromString( message, 'text/html' ).body.textContent
+ );
} );
}
};
diff --git a/tests/qunit/fixtures/updates.js b/tests/qunit/fixtures/updates.js
index 7e31370814880..3a65a35f1b0ff 100644
--- a/tests/qunit/fixtures/updates.js
+++ b/tests/qunit/fixtures/updates.js
@@ -59,5 +59,13 @@ window._wpUpdatesSettings = {
};
window._wpUpdatesItemCounts = {
plugins: {},
- totals: {}
+ totals: {
+ counts: {
+ plugins: 2,
+ themes: 0,
+ wordpress: 0,
+ translations: 0,
+ total: 2
+ }
+ }
};
diff --git a/tests/qunit/index.html b/tests/qunit/index.html
index 82524daa01227..37e6c843ea18a 100644
--- a/tests/qunit/index.html
+++ b/tests/qunit/index.html
@@ -84,6 +84,7 @@
+
@@ -97,6 +98,7 @@
+
@@ -157,6 +159,7 @@
+
diff --git a/tests/qunit/wp-admin/js/updates-ajax-always.js b/tests/qunit/wp-admin/js/updates-ajax-always.js
new file mode 100644
index 0000000000000..f133454b564a1
--- /dev/null
+++ b/tests/qunit/wp-admin/js/updates-ajax-always.js
@@ -0,0 +1,62 @@
+/* global QUnit, wp, sinon */
+
+QUnit.module( 'wp.updates.ajaxAlways', {
+ beforeEach: function() {
+ sinon.stub( window.console, 'log' );
+ sinon.stub( wp.updates, 'queueChecker' );
+ },
+ afterEach: function() {
+ window.console.log.restore();
+ wp.updates.queueChecker.restore();
+ wp.updates.ajaxLocked = false;
+ }
+} );
+
+QUnit.test( 'Debug messages are logged with character references decoded', function( assert ) {
+ wp.updates.ajaxAlways( {
+ debug: [ 'Downloading installation package from https://example.org/plugin.zip…' ]
+ } );
+
+ assert.ok( window.console.log.calledOnce, 'One message should be logged.' );
+ assert.strictEqual(
+ window.console.log.getCall( 0 ).args[0],
+ 'Downloading installation package from https://example.org/plugin.zip…',
+ 'HTML tags should be stripped and the ellipsis decoded.'
+ );
+} );
+
+QUnit.test( 'Debug messages decode named character references', function( assert ) {
+ wp.updates.ajaxAlways( {
+ debug: [ 'Tom & Jerry <3' ]
+ } );
+
+ assert.strictEqual( window.console.log.getCall( 0 ).args[0], 'Tom & Jerry <3' );
+} );
+
+QUnit.test( 'Debug messages do not execute markup', function( assert ) {
+ window.updatesAjaxAlwaysXss = false;
+
+ wp.updates.ajaxAlways( {
+ debug: [ '
Done' ]
+ } );
+
+ assert.strictEqual( window.console.log.getCall( 0 ).args[0], 'Done' );
+ assert.strictEqual( window.updatesAjaxAlwaysXss, false, 'Event handlers in debug messages should not run.' );
+
+ delete window.updatesAjaxAlwaysXss;
+} );
+
+QUnit.test( 'Non-string debug messages are skipped', function( assert ) {
+ wp.updates.ajaxAlways( {
+ debug: [ null, 123, { foo: 'bar' }, 'Done.' ]
+ } );
+
+ assert.ok( window.console.log.calledOnce, 'Only the string message should be logged.' );
+ assert.strictEqual( window.console.log.getCall( 0 ).args[0], 'Done.' );
+} );
+
+QUnit.test( 'Nothing is logged without debug messages', function( assert ) {
+ wp.updates.ajaxAlways( {} );
+
+ assert.notOk( window.console.log.called );
+} );