<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[54722] trunk/tests/phpunit/tests/ajax: Tests: Rename classes in `phpunit/tests/ajax/` per the naming conventions.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/54722">54722</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/54722","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>SergeyBiryukov</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2022-10-30 01:05:06 +0000 (Sun, 30 Oct 2022)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Tests: Rename classes in `phpunit/tests/ajax/` per the naming conventions.

This updates the test classes to match the names of the functions being tested.

Includes moving the `@covers` tags from individual test methods to the class DocBlocks.

Reference: [https://make.wordpress.org/core/handbook/testing/automated-testing/writing-phpunit-tests/#naming-and-organization Writing PHP Tests: Naming and Organization].

Follow-up to <a href="https://core.trac.wordpress.org/changeset/47780">[47780]</a>, <a href="https://core.trac.wordpress.org/changeset/48911">[48911]</a>, <a href="https://core.trac.wordpress.org/changeset/49327">[49327]</a>, <a href="https://core.trac.wordpress.org/changeset/50291">[50291]</a>, <a href="https://core.trac.wordpress.org/changeset/50292">[50292]</a>, <a href="https://core.trac.wordpress.org/changeset/50342">[50342]</a>, <a href="https://core.trac.wordpress.org/changeset/50452">[50452]</a>, <a href="https://core.trac.wordpress.org/changeset/50453">[50453]</a>, <a href="https://core.trac.wordpress.org/changeset/50456">[50456]</a>, <a href="https://core.trac.wordpress.org/changeset/50967">[50967]</a>, <a href="https://core.trac.wordpress.org/changeset/50968">[50968]</a>, <a href="https://core.trac.wordpress.org/changeset/50969">[50969]</a>, <a href="https://core.trac.wordpress.org/changeset/51491">[51491]</a>, <a href="https://core.trac.wordpress.org/changeset/51492">[514
 92]</a>, <a href="https://core.trac.wordpress.org/changeset/51493">[51493]</a>, <a href="https://core.trac.wordpress.org/changeset/51623">[51623]</a>, <a href="https://core.trac.wordpress.org/changeset/51639">[51639]</a>, <a href="https://core.trac.wordpress.org/changeset/51646">[51646]</a>, <a href="https://core.trac.wordpress.org/changeset/51650">[51650]</a>, <a href="https://core.trac.wordpress.org/changeset/51651">[51651]</a>, <a href="https://core.trac.wordpress.org/changeset/51860">[51860]</a>, <a href="https://core.trac.wordpress.org/changeset/52264">[52264]</a>, <a href="https://core.trac.wordpress.org/changeset/52265">[52265]</a>, <a href="https://core.trac.wordpress.org/changeset/53489">[53489]</a>, <a href="https://core.trac.wordpress.org/changeset/53561">[53561]</a>, <a href="https://core.trac.wordpress.org/changeset/54704">[54704]</a>.

See <a href="https://core.trac.wordpress.org/ticket/56793">#56793</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunktestsphpunittestsajaxwpAjaxCropImagephp">trunk/tests/phpunit/tests/ajax/wpAjaxCropImage.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunktestsphpunittestsajaxwpAjaxAddMetaphp">trunk/tests/phpunit/tests/ajax/wpAjaxAddMeta.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxAddTagphp">trunk/tests/phpunit/tests/ajax/wpAjaxAddTag.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxAjaxTagSearchphp">trunk/tests/phpunit/tests/ajax/wpAjaxAjaxTagSearch.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxDeleteCommentphp">trunk/tests/phpunit/tests/ajax/wpAjaxDeleteComment.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxDeletePluginphp">trunk/tests/phpunit/tests/ajax/wpAjaxDeletePlugin.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxDimCommentphp">trunk/tests/phpunit/tests/ajax/wpAjaxDimComment.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxEditCommentphp">trunk/tests/phpunit/tests/ajax/wpAjaxEditComment.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxGetCommentsphp">trunk/tests/phpunit/tests/ajax/wpAjaxGetComments.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxHeartbeatphp">trunk/tests/phpunit/tests/ajax/wpAjaxHeartbeat.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxImageEditorphp">trunk/tests/phpunit/tests/ajax/wpAjaxImageEditor.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxInlineSavephp">trunk/tests/phpunit/tests/ajax/wpAjaxInlineSave.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxReplytoCommentphp">trunk/tests/phpunit/tests/ajax/wpAjaxReplytoComment.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxResponsephp">trunk/tests/phpunit/tests/ajax/wpAjaxResponse.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxSendAttachmentToEditorphp">trunk/tests/phpunit/tests/ajax/wpAjaxSendAttachmentToEditor.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxUpdatePluginphp">trunk/tests/phpunit/tests/ajax/wpAjaxUpdatePlugin.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxUpdateThemephp">trunk/tests/phpunit/tests/ajax/wpAjaxUpdateTheme.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxWpCompressionTestphp">trunk/tests/phpunit/tests/ajax/wpAjaxWpCompressionTest.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxWpPrivacyErasePersonalDataphp">trunk/tests/phpunit/tests/ajax/wpAjaxWpPrivacyErasePersonalData.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpAjaxWpPrivacyExportPersonalDataphp">trunk/tests/phpunit/tests/ajax/wpAjaxWpPrivacyExportPersonalData.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpCustomizeManagerphp">trunk/tests/phpunit/tests/ajax/wpCustomizeManager.php</a></li>
<li><a href="#trunktestsphpunittestsajaxwpCustomizeNavMenusphp">trunk/tests/phpunit/tests/ajax/wpCustomizeNavMenus.php</a></li>
</ul>

<h3>Removed Paths</h3>
<ul>
<li><a href="#trunktestsphpunittestsajaxAddMetaphp">trunk/tests/phpunit/tests/ajax/AddMeta.php</a></li>
<li><a href="#trunktestsphpunittestsajaxAddTagphp">trunk/tests/phpunit/tests/ajax/AddTag.php</a></li>
<li><a href="#trunktestsphpunittestsajaxAttachmentsphp">trunk/tests/phpunit/tests/ajax/Attachments.php</a></li>
<li><a href="#trunktestsphpunittestsajaxAutosavephp">trunk/tests/phpunit/tests/ajax/Autosave.php</a></li>
<li><a href="#trunktestsphpunittestsajaxCompressionphp">trunk/tests/phpunit/tests/ajax/Compression.php</a></li>
<li><a href="#trunktestsphpunittestsajaxCustomizeManagerphp">trunk/tests/phpunit/tests/ajax/CustomizeManager.php</a></li>
<li><a href="#trunktestsphpunittestsajaxCustomizeMenusphp">trunk/tests/phpunit/tests/ajax/CustomizeMenus.php</a></li>
<li><a href="#trunktestsphpunittestsajaxDeleteCommentphp">trunk/tests/phpunit/tests/ajax/DeleteComment.php</a></li>
<li><a href="#trunktestsphpunittestsajaxDeletePluginphp">trunk/tests/phpunit/tests/ajax/DeletePlugin.php</a></li>
<li><a href="#trunktestsphpunittestsajaxDimCommentphp">trunk/tests/phpunit/tests/ajax/DimComment.php</a></li>
<li><a href="#trunktestsphpunittestsajaxEditCommentphp">trunk/tests/phpunit/tests/ajax/EditComment.php</a></li>
<li><a href="#trunktestsphpunittestsajaxGetCommentsphp">trunk/tests/phpunit/tests/ajax/GetComments.php</a></li>
<li><a href="#trunktestsphpunittestsajaxManageThemesphp">trunk/tests/phpunit/tests/ajax/ManageThemes.php</a></li>
<li><a href="#trunktestsphpunittestsajaxMediaEditphp">trunk/tests/phpunit/tests/ajax/MediaEdit.php</a></li>
<li><a href="#trunktestsphpunittestsajaxPrivacyErasePersonalDataphp">trunk/tests/phpunit/tests/ajax/PrivacyErasePersonalData.php</a></li>
<li><a href="#trunktestsphpunittestsajaxPrivacyExportPersonalDataphp">trunk/tests/phpunit/tests/ajax/PrivacyExportPersonalData.php</a></li>
<li><a href="#trunktestsphpunittestsajaxQuickEditphp">trunk/tests/phpunit/tests/ajax/QuickEdit.php</a></li>
<li><a href="#trunktestsphpunittestsajaxReplytoCommentphp">trunk/tests/phpunit/tests/ajax/ReplytoComment.php</a></li>
<li><a href="#trunktestsphpunittestsajaxResponsephp">trunk/tests/phpunit/tests/ajax/Response.php</a></li>
<li><a href="#trunktestsphpunittestsajaxTagSearchphp">trunk/tests/phpunit/tests/ajax/TagSearch.php</a></li>
<li><a href="#trunktestsphpunittestsajaxUpdatePluginphp">trunk/tests/phpunit/tests/ajax/UpdatePlugin.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunktestsphpunittestsajaxAddMetaphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/AddMeta.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/AddMeta.php        2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/AddMeta.php  2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,77 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Add Meta AJAX functionality.
- *
- * @group ajax
- */
-class Tests_Ajax_AddMeta extends WP_Ajax_UnitTestCase {
-       /**
-        * @ticket 43559
-        *
-        * @covers ::wp_ajax_add_meta
-        * @covers ::add_post_meta
-        */
-       public function test_wp_ajax_add_meta_allows_empty_values_on_adding() {
-               $post = self::factory()->post->create();
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               $_POST = array(
-                       'post_id'              => $post,
-                       'metakeyinput'         => 'testkey',
-                       'metavalue'            => '',
-                       '_ajax_nonce-add-meta' => wp_create_nonce( 'add-meta' ),
-               );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'add-meta' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               $this->assertSame( '', get_post_meta( $post, 'testkey', true ) );
-       }
-
-       /**
-        * @ticket 43559
-        *
-        * @covers ::wp_ajax_add_meta
-        * @covers ::update_metadata_by_mid
-        */
-       public function test_wp_ajax_add_meta_allows_empty_values_on_updating() {
-               $post = self::factory()->post->create();
-
-               $meta_id = add_post_meta( $post, 'testkey', 'hello' );
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               $_POST = array(
-                       '_ajax_nonce-add-meta' => wp_create_nonce( 'add-meta' ),
-                       'post_id'              => $post,
-                       'meta'                 => array(
-                               $meta_id => array(
-                                       'key'   => 'testkey',
-                                       'value' => '',
-                               ),
-                       ),
-               );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'add-meta' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               $this->assertSame( '', get_post_meta( $post, 'testkey', true ) );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxAddTagphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/AddTag.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/AddTag.php 2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/AddTag.php   2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,156 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-/**
- * Admin ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Class for testing ajax add tag functionality.
- *
- * @group ajax
- */
-class Tests_Ajax_AddTag extends WP_Ajax_UnitTestCase {
-
-       /**
-        * @dataProvider data_add_tag
-        *
-        * @ticket 42937
-        *
-        * @covers ::wp_ajax_add_tag
-        * @covers ::wp_insert_term
-        *
-        * @param array                 $post_data Data to populate $_POST.
-        * @param string                $expected  Expected response.
-        * @param array|string|callable $callback  Optional. Callback to register to 'term_updated_messages'
-        *                                         filter. Default empty string (no callback).
-        */
-       public function test_add_tag( array $post_data, $expected, $callback = '' ) {
-               $this->_setRole( 'administrator' );
-
-               $_POST                     = $post_data;
-               $_POST['_wpnonce_add-tag'] = wp_create_nonce( 'add-tag' );
-
-               if ( ! empty( $callback ) ) {
-                       add_filter( 'term_updated_messages', $callback );
-               }
-
-               try {
-                       $this->_handleAjax( 'add-tag' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // The response message is in the `data` property in WP 5.9.
-               $this->assertSame( $expected, (string) $this->get_xml_response_taxonomy()->response_data );
-               // The response message is in the `supplemental->notice` property in WP 6.0+.
-               $this->assertSame( $expected, (string) $this->get_xml_response_taxonomy()->supplemental->notice );
-       }
-
-       /**
-        * Data provider.
-        *
-        * @return array
-        */
-       public function data_add_tag() {
-               return array(
-                       'add a category'                        => array(
-                               'post_data' => array(
-                                       'taxonomy'  => 'category',
-                                       'post_type' => 'post',
-                                       'screen'    => 'edit-category',
-                                       'action'    => 'add-tag',
-                                       'tag-name'  => 'blues',
-                               ),
-                               'expected'  => 'Category added.',
-                       ),
-                       'add a category with message filtering' => array(
-                               'post_data' => array(
-                                       'taxonomy'  => 'category',
-                                       'post_type' => 'post',
-                                       'screen'    => 'edit-category',
-                                       'action'    => 'add-tag',
-                                       'tag-name'  => 'techno',
-                               ),
-                               'expected'  => 'A new category added.',
-                               'callback'  => static function( array $messages ) {
-                                       $messages['category'][1] = 'A new category added.';
-                                       return $messages;
-                               },
-                       ),
-                       'add a post_tag'                        => array(
-                               'post_data' => array(
-                                       'taxonomy'  => 'post_tag',
-                                       'post_type' => 'post',
-                                       'screen'    => 'edit-post_tag',
-                                       'action'    => 'add-tag',
-                                       'tag-name'  => 'Louis Armstrong',
-                               ),
-                               'expected'  => 'Tag added.',
-                       ),
-               );
-       }
-
-       /**
-        * @ticket 42937
-        *
-        * @covers ::wp_ajax_add_tag
-        */
-       public function test_adding_category_without_capability_should_error() {
-               $this->_setRole( 'subscriber' );
-
-               $_POST['taxonomy']         = 'category';
-               $_POST['post_type']        = 'post';
-               $_POST['screen']           = 'edit-category';
-               $_POST['action']           = 'add-tag';
-               $_POST['tag - name']       = 'disco';
-               $_POST['_wpnonce_add-tag'] = wp_create_nonce( 'add-tag' );
-
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'add-tag' );
-       }
-
-       /**
-        * @ticket 42937
-        *
-        * @covers ::wp_ajax_add_tag
-        * @covers ::wp_insert_term
-        */
-       public function test_adding_existing_category_should_error() {
-               $this->_setRole( 'administrator' );
-
-               wp_insert_term( 'testcat', 'category' );
-
-               $_POST = array(
-                       'taxonomy'         => 'category',
-                       'post_type'        => 'post',
-                       'screen'           => 'edit-category',
-                       'action'           => 'add-tag',
-                       'tag-name'         => 'testcat',
-                       '_wpnonce_add-tag' => wp_create_nonce( 'add-tag' ),
-               );
-
-               try {
-                       $this->_handleAjax( 'add-tag' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               $expected = 'A term with the name provided already exists with this parent.';
-               $this->assertSame( $expected, (string) $this->get_xml_response_taxonomy()->wp_error );
-       }
-
-       /**
-        * Helper method to get the taxonomy's response or error.
-        *
-        * @since 5.9.0
-        *
-        * @return SimpleXMLElement Response or error object.
-        */
-       private function get_xml_response_taxonomy() {
-               $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
-
-               return $xml->response->taxonomy;
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxAttachmentsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/Attachments.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/Attachments.php    2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/Attachments.php      2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,104 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Ajax attachment handling.
- *
- * @group ajax
- */
-class Tests_Ajax_Attachments extends WP_Ajax_UnitTestCase {
-       /**
-        * @ticket 36578
-        *
-        * @covers ::wp_ajax_send_attachment_to_editor
-        * @covers ::get_image_send_to_editor
-        */
-       public function test_wp_ajax_send_attachment_to_editor_should_return_an_image() {
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               $filename = DIR_TESTDATA . '/images/canola.jpg';
-               $contents = file_get_contents( $filename );
-
-               $upload     = wp_upload_bits( wp_basename( $filename ), null, $contents );
-               $attachment = $this->_make_attachment( $upload );
-
-               // Set up a default request.
-               $_POST['nonce']      = wp_create_nonce( 'media-send-to-editor' );
-               $_POST['html']       = 'Bar Baz';
-               $_POST['post_id']    = 0;
-               $_POST['attachment'] = array(
-                       'id'         => $attachment,
-                       'align'      => 'left',
-                       'image-size' => 'large',
-                       'image_alt'  => 'Foo bar',
-                       'url'        => 'http://example.com/',
-               );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'send-attachment-to-editor' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = get_image_send_to_editor( $attachment, '', '', 'left', 'http://example.com/', false, 'large', 'Foo bar' );
-
-               // Ensure everything is correct.
-               $this->assertTrue( $response['success'] );
-               $this->assertSame( $expected, $response['data'] );
-       }
-
-       /**
-        * @ticket 36578
-        * @group ms-excluded
-        *
-        * @covers ::wp_ajax_send_attachment_to_editor
-        */
-       public function test_wp_ajax_send_attachment_to_editor_should_return_a_link() {
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               $filename = DIR_TESTDATA . '/formatting/entities.txt';
-               $contents = file_get_contents( $filename );
-
-               $upload     = wp_upload_bits( wp_basename( $filename ), null, $contents );
-               $attachment = $this->_make_attachment( $upload );
-
-               // Set up a default request.
-               $_POST['nonce']      = wp_create_nonce( 'media-send-to-editor' );
-               $_POST['html']       = 'Bar Baz';
-               $_POST['post_id']    = 0;
-               $_POST['attachment'] = array(
-                       'id'         => $attachment,
-                       'post_title' => 'Foo bar',
-                       'url'        => get_attachment_link( $attachment ),
-               );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'send-attachment-to-editor' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = sprintf(
-                       '<a href="%s" rel="attachment wp-att-%d">Foo bar</a>',
-                       get_attachment_link( $attachment ),
-                       $attachment
-               );
-
-               // Ensure everything is correct.
-               $this->assertTrue( $response['success'] );
-               $this->assertSame( $expected, $response['data'] );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxAutosavephp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/Autosave.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/Autosave.php       2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/Autosave.php 2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,170 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Ajax save draft functionality.
- *
- * @package    WordPress
- * @subpackage UnitTests
- * @since      3.4.0
- * @group      ajax
- */
-class Tests_Ajax_Autosave extends WP_Ajax_UnitTestCase {
-
-       /**
-        * Post
-        *
-        * @var mixed
-        */
-       protected $_post = null;
-
-       protected static $admin_id  = 0;
-       protected static $editor_id = 0;
-       protected static $post;
-       protected static $post_id;
-
-       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
-               self::$admin_id  = $factory->user->create( array( 'role' => 'administrator' ) );
-               self::$editor_id = $factory->user->create( array( 'role' => 'editor' ) );
-
-               // Set a user so the $post has 'post_author'.
-               wp_set_current_user( self::$admin_id );
-
-               self::$post_id = $factory->post->create( array( 'post_status' => 'draft' ) );
-               self::$post    = get_post( self::$post_id );
-       }
-
-       /**
-        * Tests autosaving a post.
-        *
-        * @covers ::wp_ajax_heartbeat
-        */
-       public function test_autosave_post() {
-               // The original post_author.
-               wp_set_current_user( self::$admin_id );
-
-               // Set up the $_POST request.
-               $md5   = md5( uniqid() );
-               $_POST = array(
-                       'action' => 'heartbeat',
-                       '_nonce' => wp_create_nonce( 'heartbeat-nonce' ),
-                       'data'   => array(
-                               'wp_autosave' => array(
-                                       'post_id'      => self::$post_id,
-                                       '_wpnonce'     => wp_create_nonce( 'update-post_' . self::$post_id ),
-                                       'post_content' => self::$post->post_content . PHP_EOL . $md5,
-                                       'post_type'    => 'post',
-                               ),
-                       ),
-               );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'heartbeat' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response, it is in heartbeat's response.
-               $response = json_decode( $this->_last_response, true );
-
-               // Ensure everything is correct.
-               $this->assertNotEmpty( $response['wp_autosave'] );
-               $this->assertTrue( $response['wp_autosave']['success'] );
-
-               // Check that the edit happened.
-               $post = get_post( self::$post_id );
-               $this->assertStringContainsString( $md5, $post->post_content );
-       }
-
-       /**
-        * Tests autosaving a locked post.
-        *
-        * @covers ::wp_ajax_heartbeat
-        */
-       public function test_autosave_locked_post() {
-               // Lock the post to another user.
-               wp_set_current_user( self::$editor_id );
-               wp_set_post_lock( self::$post_id );
-
-               wp_set_current_user( self::$admin_id );
-
-               // Ensure post is locked.
-               $this->assertEquals( self::$editor_id, wp_check_post_lock( self::$post_id ) );
-
-               // Set up the $_POST request.
-               $md5   = md5( uniqid() );
-               $_POST = array(
-                       'action' => 'heartbeat',
-                       '_nonce' => wp_create_nonce( 'heartbeat-nonce' ),
-                       'data'   => array(
-                               'wp_autosave' => array(
-                                       'post_id'      => self::$post_id,
-                                       '_wpnonce'     => wp_create_nonce( 'update-post_' . self::$post_id ),
-                                       'post_content' => self::$post->post_content . PHP_EOL . $md5,
-                                       'post_type'    => 'post',
-                               ),
-                       ),
-               );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'heartbeat' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               $response = json_decode( $this->_last_response, true );
-
-               // Ensure everything is correct.
-               $this->assertNotEmpty( $response['wp_autosave'] );
-               $this->assertTrue( $response['wp_autosave']['success'] );
-
-               // Check that the original post was NOT edited.
-               $post = get_post( self::$post_id );
-               $this->assertStringNotContainsString( $md5, $post->post_content );
-
-               // Check if the autosave post was created.
-               $autosave = wp_get_post_autosave( self::$post_id, get_current_user_id() );
-               $this->assertNotEmpty( $autosave );
-               $this->assertStringContainsString( $md5, $autosave->post_content );
-       }
-
-       /**
-        * Tests with an invalid nonce.
-        *
-        * @covers ::wp_ajax_heartbeat
-        */
-       public function test_with_invalid_nonce() {
-
-               wp_set_current_user( self::$admin_id );
-
-               // Set up the $_POST request.
-               $_POST = array(
-                       'action' => 'heartbeat',
-                       '_nonce' => wp_create_nonce( 'heartbeat-nonce' ),
-                       'data'   => array(
-                               'wp_autosave' => array(
-                                       'post_id'  => self::$post_id,
-                                       '_wpnonce' => substr( md5( uniqid() ), 0, 10 ),
-                               ),
-                       ),
-               );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'heartbeat' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               $response = json_decode( $this->_last_response, true );
-
-               $this->assertNotEmpty( $response['wp_autosave'] );
-               $this->assertFalse( $response['wp_autosave']['success'] );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxCompressionphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/Compression.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/Compression.php    2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/Compression.php      2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,236 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Ajax compression test functionality.
- *
- * @package    WordPress
- * @subpackage UnitTests
- * @since      3.4.0
- * @group      ajax
- */
-class Tests_Ajax_CompressionTest extends WP_Ajax_UnitTestCase {
-
-       /**
-        * Test as a logged out user
-        *
-        * @covers ::wp_ajax_wp_compression_test
-        */
-       public function test_logged_out() {
-               $this->logout();
-
-               // Set up a default request.
-               $_GET['test'] = 1;
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'wp-compression-test' );
-       }
-
-       /**
-        * Fetch the test text
-        *
-        * @covers ::wp_ajax_wp_compression_test
-        */
-       public function test_text() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_GET['test'] = 1;
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'wp-compression-test' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Ensure we found the right match.
-               $this->assertStringContainsString( 'wpCompressionTest', $this->_last_response );
-       }
-
-       /**
-        * Fetch the test text (gzdeflate)
-        *
-        * @requires function gzdeflate
-        *
-        * @covers ::wp_ajax_wp_compression_test
-        */
-       public function test_gzdeflate() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_GET['test']                    = 2;
-               $_SERVER['HTTP_ACCEPT_ENCODING'] = 'deflate';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'wp-compression-test' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Ensure we found the right match.
-               $this->assertStringContainsString( 'wpCompressionTest', gzinflate( $this->_last_response ) );
-       }
-
-       /**
-        * Fetch the test text (gzencode)
-        *
-        * @requires function gzencode
-        *
-        * @covers ::wp_ajax_wp_compression_test
-        */
-       public function test_gzencode() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_GET['test']                    = 2;
-               $_SERVER['HTTP_ACCEPT_ENCODING'] = 'gzip';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'wp-compression-test' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Ensure we found the right match.
-               $this->assertStringContainsString( 'wpCompressionTest', $this->_gzdecode( $this->_last_response ) );
-       }
-
-       /**
-        * Fetch the test text (unknown encoding)
-        *
-        * @covers ::wp_ajax_wp_compression_test
-        */
-       public function test_unknown_encoding() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_GET['test']                    = 2;
-               $_SERVER['HTTP_ACCEPT_ENCODING'] = 'unknown';
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'wp-compression-test' );
-       }
-
-       /**
-        * Set the 'can_compress_scripts' site option to true
-        *
-        * @covers ::wp_ajax_wp_compression_test
-        */
-       public function test_set_yes() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_GET['test'] = 'yes';
-
-               // Set the option to false.
-               update_site_option( 'can_compress_scripts', 0 );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'wp-compression-test' );
-               } catch ( WPAjaxDieStopException $e ) {
-                       unset( $e );
-               }
-
-               // Check the site option is not changed due to lack of nonce.
-               $this->assertSame( 0, get_site_option( 'can_compress_scripts' ) );
-
-               // Add a nonce.
-               $_GET['_ajax_nonce'] = wp_create_nonce( 'update_can_compress_scripts' );
-
-               // Retry the request.
-               try {
-                       $this->_handleAjax( 'wp-compression-test' );
-               } catch ( WPAjaxDieStopException $e ) {
-                       unset( $e );
-               }
-
-               // Check the site option is changed.
-               $this->assertSame( 1, get_site_option( 'can_compress_scripts' ) );
-       }
-
-       /**
-        * Set the 'can_compress_scripts' site option to false
-        *
-        * @covers ::wp_ajax_wp_compression_test
-        */
-       public function test_set_no() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_GET['test'] = 'no';
-
-               // Set the option to true.
-               update_site_option( 'can_compress_scripts', 1 );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'wp-compression-test' );
-               } catch ( WPAjaxDieStopException $e ) {
-                       unset( $e );
-               }
-
-               // Check the site option is not changed due to lack of nonce.
-               $this->assertSame( 1, get_site_option( 'can_compress_scripts' ) );
-
-               // Add a nonce.
-               $_GET['_ajax_nonce'] = wp_create_nonce( 'update_can_compress_scripts' );
-
-               // Retry the request.
-               try {
-                       $this->_handleAjax( 'wp-compression-test' );
-               } catch ( WPAjaxDieStopException $e ) {
-                       unset( $e );
-               }
-
-               // Check the site option is changed.
-               $this->assertSame( 0, get_site_option( 'can_compress_scripts' ) );
-       }
-
-       /**
-        * Undo gzencode.  This is ugly, but there's no stock gzdecode() function.
-        *
-        * @param string $encoded_data
-        * @return string
-        */
-       protected function _gzdecode( $encoded_data ) {
-
-               // Save the encoded data to a temp file.
-               $file = wp_tempnam( 'gzdecode' );
-               file_put_contents( $file, $encoded_data );
-
-               // Flush it to the output buffer and delete the temp file.
-               ob_start();
-               readgzfile( $file );
-               unlink( $file );
-
-               // Save the data stop buffering.
-               $data = ob_get_clean();
-
-               // Done.
-               return $data;
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxCustomizeManagerphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/CustomizeManager.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/CustomizeManager.php       2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/CustomizeManager.php 2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,768 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Testing Ajax customize manager functionality.
- *
- * @package    WordPress
- * @subpackage UnitTests
- * @since      4.3.0
- * @group      ajax
- */
-class Tests_Ajax_CustomizeManager extends WP_Ajax_UnitTestCase {
-
-       /**
-        * Instance of WP_Customize_Manager which is reset for each test.
-        *
-        * @var WP_Customize_Manager
-        */
-       public $wp_customize;
-
-       /**
-        * Admin user ID.
-        *
-        * @var int
-        */
-       protected static $admin_user_id;
-
-       /**
-        * Subscriber user ID.
-        *
-        * @var int
-        */
-       protected static $subscriber_user_id;
-
-       /**
-        * Last response parsed.
-        *
-        * @var array|null
-        */
-       protected $_last_response_parsed;
-
-       /**
-        * Set up before class.
-        *
-        * @param WP_UnitTest_Factory $factory Factory.
-        */
-       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
-               self::$subscriber_user_id = $factory->user->create( array( 'role' => 'subscriber' ) );
-               self::$admin_user_id      = $factory->user->create( array( 'role' => 'administrator' ) );
-       }
-
-       /**
-        * Set up the test fixture.
-        */
-       public function set_up() {
-               parent::set_up();
-               require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
-       }
-
-       /**
-        * Tear down.
-        */
-       public function tear_down() {
-               $_REQUEST = array();
-               parent::tear_down();
-       }
-
-       /**
-        * Helper to keep it DRY
-        *
-        * @param string $action Action.
-        */
-       protected function make_ajax_call( $action ) {
-               $this->_last_response_parsed = null;
-               $this->_last_response        = '';
-               try {
-                       $this->_handleAjax( $action );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-               if ( $this->_last_response ) {
-                       $this->_last_response_parsed = json_decode( $this->_last_response, true );
-               }
-       }
-
-       /**
-        * Overridden caps for user_has_cap.
-        *
-        * @var array
-        */
-       protected $overridden_caps = array();
-
-       /**
-        * Dynamically filter a user's capabilities.
-        *
-        * @param array $allcaps An array of all the user's capabilities.
-        * @return array All caps.
-        */
-       public function filter_user_has_cap( $allcaps ) {
-               $allcaps = array_merge( $allcaps, $this->overridden_caps );
-               return $allcaps;
-       }
-
-       /**
-        * Test WP_Customize_Manager::save().
-        *
-        * @ticket 30937
-        *
-        * @covers WP_Customize_Manager::save
-        */
-       public function test_save_failures() {
-               global $wp_customize;
-               $wp_customize = new WP_Customize_Manager();
-               $wp_customize->register_controls();
-               add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ) );
-
-               // Unauthenticated.
-               wp_set_current_user( 0 );
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'unauthenticated', $this->_last_response_parsed['data'] );
-
-               // Unauthorized.
-               wp_set_current_user( self::$subscriber_user_id );
-               $nonce             = wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() );
-               $_POST['nonce']    = $nonce;
-               $_GET['nonce']     = $nonce;
-               $_REQUEST['nonce'] = $nonce;
-               $exception         = null;
-               try {
-                       ob_start();
-                       $wp_customize->setup_theme();
-               } catch ( WPAjaxDieContinueException $e ) {
-                       $exception = $e;
-               }
-               $this->assertNotEmpty( $e );
-               $this->assertSame( '-1', $e->getMessage() );
-
-               // Not called setup_theme.
-               wp_set_current_user( self::$admin_user_id );
-               $nonce             = wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() );
-               $_POST['nonce']    = $nonce;
-               $_GET['nonce']     = $nonce;
-               $_REQUEST['nonce'] = $nonce;
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'not_preview', $this->_last_response_parsed['data'] );
-
-               // Bad nonce.
-               $_POST['nonce']    = 'bad';
-               $_GET['nonce']     = 'bad';
-               $_REQUEST['nonce'] = 'bad';
-               $wp_customize->setup_theme();
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'invalid_nonce', $this->_last_response_parsed['data'] );
-
-               // User cannot create.
-               $nonce                            = wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() );
-               $_POST['nonce']                   = $nonce;
-               $_GET['nonce']                    = $nonce;
-               $_REQUEST['nonce']                = $nonce;
-               $post_type_obj                    = get_post_type_object( 'customize_changeset' );
-               $post_type_obj->cap->create_posts = 'create_customize_changesets';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'cannot_create_changeset_post', $this->_last_response_parsed['data'] );
-               $this->overridden_caps[ $post_type_obj->cap->create_posts ] = true;
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $post_type_obj->cap->create_posts = 'customize'; // Restore.
-
-               // Changeset already published.
-               $wp_customize->set_post_value( 'blogname', 'Hello' );
-               $wp_customize->save_changeset_post( array( 'status' => 'publish' ) );
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'changeset_already_published', $this->_last_response_parsed['data']['code'] );
-               wp_update_post(
-                       array(
-                               'ID'          => $wp_customize->changeset_post_id(),
-                               'post_status' => 'auto-draft',
-                       )
-               );
-
-               // User cannot edit.
-               $post_type_obj                 = get_post_type_object( 'customize_changeset' );
-               $post_type_obj->cap->edit_post = 'edit_customize_changesets';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'cannot_edit_changeset_post', $this->_last_response_parsed['data'] );
-               $this->overridden_caps[ $post_type_obj->cap->edit_post ] = true;
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $post_type_obj->cap->edit_post = 'customize'; // Restore.
-
-               // Bad customize_changeset_data.
-               $_POST['customize_changeset_data'] = '[MALFORMED]';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'invalid_customize_changeset_data', $this->_last_response_parsed['data'] );
-
-               // Bad customize_changeset_status.
-               $_POST['customize_changeset_data']   = '{}';
-               $_POST['customize_changeset_status'] = 'unrecognized';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'bad_customize_changeset_status', $this->_last_response_parsed['data'] );
-
-               // Disallowed publish posts if not allowed.
-               $post_type_obj                       = get_post_type_object( 'customize_changeset' );
-               $post_type_obj->cap->publish_posts   = 'publish_customize_changesets';
-               $_POST['customize_changeset_status'] = 'publish';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'changeset_publish_unauthorized', $this->_last_response_parsed['data'] );
-               $_POST['customize_changeset_status'] = 'future';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'changeset_publish_unauthorized', $this->_last_response_parsed['data'] );
-               $post_type_obj->cap->publish_posts = 'customize'; // Restore.
-
-               // Validate date.
-               $_POST['customize_changeset_status'] = 'draft';
-               $_POST['customize_changeset_date']   = 'BAD DATE';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'bad_customize_changeset_date', $this->_last_response_parsed['data'] );
-               $_POST['customize_changeset_date'] = '2010-01-01 00:00:00';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'not_future_date', $this->_last_response_parsed['data']['code'] );
-               $_POST['customize_changeset_date'] = ( gmdate( 'Y' ) + 1 ) . '-01-01 00:00:00';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $_POST['customize_changeset_status'] = 'future';
-               $_POST['customize_changeset_date']   = '+10 minutes';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'future', get_post_status( $wp_customize->changeset_post_id() ) );
-               wp_update_post(
-                       array(
-                               'ID'          => $wp_customize->changeset_post_id(),
-                               'post_status' => 'auto-draft',
-                       )
-               );
-       }
-
-       /**
-        * Set up valid user state.
-        *
-        * @param string $uuid Changeset UUID.
-        * @return WP_Customize_Manager
-        */
-       protected function set_up_valid_state( $uuid = null ) {
-               global $wp_customize;
-               wp_set_current_user( self::$admin_user_id );
-               $wp_customize = new WP_Customize_Manager(
-                       array(
-                               'changeset_uuid' => $uuid,
-                       )
-               );
-               $wp_customize->register_controls();
-               $nonce             = wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() );
-               $_POST['nonce']    = $nonce;
-               $_GET['nonce']     = $nonce;
-               $_REQUEST['nonce'] = $nonce;
-               $wp_customize->setup_theme();
-               return $wp_customize;
-       }
-
-       /**
-        * Test WP_Customize_Manager::save().
-        *
-        * @ticket 30937
-        *
-        * @covers WP_Customize_Manager::save
-        */
-       public function test_save_success_publish_create() {
-               $wp_customize = $this->set_up_valid_state();
-
-               $_POST['customize_changeset_status'] = 'publish';
-               $_POST['customize_changeset_title']  = 'Success Changeset';
-               $_POST['customize_changeset_data']   = wp_json_encode(
-                       array(
-                               'blogname' => array(
-                                       'value' => 'Successful Site Title',
-                               ),
-                       )
-               );
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertIsArray( $this->_last_response_parsed['data'] );
-
-               $this->assertSame( 'publish', $this->_last_response_parsed['data']['changeset_status'] );
-               $this->assertArrayHasKey( 'next_changeset_uuid', $this->_last_response_parsed['data'] );
-               $this->assertTrue( wp_is_uuid( $this->_last_response_parsed['data']['next_changeset_uuid'], 4 ) );
-               $this->assertSame( 'Success Changeset', get_post( $wp_customize->changeset_post_id() )->post_title );
-               $this->assertSame( 'Successful Site Title', get_option( 'blogname' ) );
-       }
-
-       /**
-        * Test WP_Customize_Manager::save().
-        *
-        * @ticket 30937
-        *
-        * @covers WP_Customize_Manager::save
-        */
-       public function test_save_success_publish_edit() {
-               $uuid = wp_generate_uuid4();
-
-               $post_id      = self::factory()->post->create(
-                       array(
-                               'post_name'    => $uuid,
-                               'post_title'   => 'Original',
-                               'post_type'    => 'customize_changeset',
-                               'post_status'  => 'auto-draft',
-                               'post_content' => wp_json_encode(
-                                       array(
-                                               'blogname' => array(
-                                                       'value' => 'New Site Title',
-                                               ),
-                                       )
-                               ),
-                       )
-               );
-               $wp_customize = $this->set_up_valid_state( $uuid );
-
-               $_POST['customize_changeset_status'] = 'publish';
-               $_POST['customize_changeset_title']  = 'Published';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertIsArray( $this->_last_response_parsed['data'] );
-
-               $this->assertSame( 'publish', $this->_last_response_parsed['data']['changeset_status'] );
-               $this->assertArrayHasKey( 'next_changeset_uuid', $this->_last_response_parsed['data'] );
-               $this->assertTrue( wp_is_uuid( $this->_last_response_parsed['data']['next_changeset_uuid'], 4 ) );
-               $this->assertSame( 'New Site Title', get_option( 'blogname' ) );
-               $this->assertSame( 'Published', get_post( $post_id )->post_title );
-       }
-
-       /**
-        * Test WP_Customize_Manager::save().
-        *
-        * @ticket 38943
-        *
-        * @covers WP_Customize_Manager::save
-        */
-       public function test_success_save_post_date() {
-               $uuid         = wp_generate_uuid4();
-               $post_id      = self::factory()->post->create(
-                       array(
-                               'post_name'    => $uuid,
-                               'post_title'   => 'Original',
-                               'post_type'    => 'customize_changeset',
-                               'post_status'  => 'auto-draft',
-                               'post_content' => wp_json_encode(
-                                       array(
-                                               'blogname' => array(
-                                                       'value' => 'New Site Title',
-                                               ),
-                                       )
-                               ),
-                       )
-               );
-               $wp_customize = $this->set_up_valid_state( $uuid );
-
-               // Success future schedule date.
-               $future_date                         = ( gmdate( 'Y' ) + 1 ) . '-01-01 00:00:00';
-               $_POST['customize_changeset_status'] = 'future';
-               $_POST['customize_changeset_title']  = 'Future date';
-               $_POST['customize_changeset_date']   = $future_date;
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertArrayHasKey( 'changeset_date', $this->_last_response_parsed['data'] );
-               $changeset_post_schedule = get_post( $post_id );
-               $this->assertSame( $future_date, $changeset_post_schedule->post_date );
-
-               // Success future changeset change to draft keeping existing date.
-               unset( $_POST['customize_changeset_date'] );
-               $_POST['customize_changeset_status'] = 'draft';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertArrayNotHasKey( 'changeset_date', $this->_last_response_parsed['data'] );
-               $changeset_post_draft = get_post( $post_id );
-               $this->assertSame( $future_date, $changeset_post_draft->post_date );
-
-               // Success if date is not passed with schedule changeset and stored changeset have future date.
-               $_POST['customize_changeset_status'] = 'future';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertArrayHasKey( 'changeset_date', $this->_last_response_parsed['data'] );
-               $changeset_post_schedule = get_post( $post_id );
-               $this->assertSame( $future_date, $changeset_post_schedule->post_date );
-               // Success if draft with past date.
-               $now = current_time( 'mysql' );
-               wp_update_post(
-                       array(
-                               'ID'            => $post_id,
-                               'post_status'   => 'draft',
-                               'post_date'     => $now,
-                               'post_date_gmt' => get_gmt_from_date( $now ),
-                       )
-               );
-
-               // Fail if future request and existing date is past.
-               $_POST['customize_changeset_status'] = 'future';
-               unset( $_POST['customize_changeset_date'] );
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'not_future_date', $this->_last_response_parsed['data']['code'] );
-
-               // Success publish changeset reset date to current.
-               wp_update_post(
-                       array(
-                               'ID'            => $post_id,
-                               'post_status'   => 'future',
-                               'post_date'     => $future_date,
-                               'post_date_gmt' => get_gmt_from_date( $future_date ),
-                       )
-               );
-               unset( $_POST['customize_changeset_date'] );
-               $_POST['customize_changeset_status'] = 'publish';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertArrayHasKey( 'next_changeset_uuid', $this->_last_response_parsed['data'] );
-               $this->assertTrue( wp_is_uuid( $this->_last_response_parsed['data']['next_changeset_uuid'], 4 ) );
-               $changeset_post_publish = get_post( $post_id );
-               $this->assertNotEquals( $future_date, $changeset_post_publish->post_date );
-
-               // Check response when trying to update an already-published post.
-               $this->assertSame( 'trash', get_post_status( $post_id ) );
-               $_POST['customize_changeset_status'] = 'publish';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'changeset_already_published', $this->_last_response_parsed['data']['code'] );
-               $this->assertArrayHasKey( 'next_changeset_uuid', $this->_last_response_parsed['data'] );
-               $this->assertTrue( wp_is_uuid( $this->_last_response_parsed['data']['next_changeset_uuid'], 4 ) );
-       }
-
-       /**
-        * Test WP_Customize_Manager::save().
-        *
-        * @ticket 39896
-        *
-        * @covers WP_Customize_Manager::save
-        */
-       public function test_save_autosave() {
-               $uuid = wp_generate_uuid4();
-
-               $post_id = self::factory()->post->create(
-                       array(
-                               'post_name'    => $uuid,
-                               'post_type'    => 'customize_changeset',
-                               'post_status'  => 'draft',
-                               'post_content' => wp_json_encode(
-                                       array(
-                                               'blogname' => array(
-                                                       'value' => 'New Site Title',
-                                               ),
-                                       )
-                               ),
-                       )
-               );
-               $this->set_up_valid_state( $uuid );
-
-               $this->assertFalse( wp_get_post_autosave( $post_id ) );
-
-               $_POST['customize_changeset_data'] = wp_json_encode(
-                       array(
-                               'blogname' => array(
-                                       'value' => 'Autosaved Site Title',
-                               ),
-                       )
-               );
-
-               $_POST['customize_changeset_autosave'] = 'on';
-               $this->make_ajax_call( 'customize_save' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'draft', $this->_last_response_parsed['data']['changeset_status'] );
-               $autosave_revision = wp_get_post_autosave( $post_id );
-               $this->assertInstanceOf( 'WP_Post', $autosave_revision );
-
-               $this->assertStringContainsString( 'New Site Title', get_post( $post_id )->post_content );
-               $this->assertStringContainsString( 'Autosaved Site Title', $autosave_revision->post_content );
-       }
-
-       /**
-        * Test request for trashing a changeset.
-        *
-        * @ticket 39896
-        *
-        * @covers WP_Customize_Manager::handle_changeset_trash_request
-        */
-       public function test_handle_changeset_trash_request() {
-               $uuid         = wp_generate_uuid4();
-               $wp_customize = $this->set_up_valid_state( $uuid );
-
-               $this->make_ajax_call( 'customize_trash' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'invalid_nonce', $this->_last_response_parsed['data']['code'] );
-
-               $nonce             = wp_create_nonce( 'trash_customize_changeset' );
-               $_POST['nonce']    = $nonce;
-               $_GET['nonce']     = $nonce;
-               $_REQUEST['nonce'] = $nonce;
-               $this->make_ajax_call( 'customize_trash' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'non_existent_changeset', $this->_last_response_parsed['data']['code'] );
-
-               $wp_customize->register_controls(); // And settings too.
-               $wp_customize->set_post_value( 'blogname', 'HELLO' );
-               $wp_customize->save_changeset_post(
-                       array(
-                               'status' => 'save',
-                       )
-               );
-
-               add_filter( 'map_meta_cap', array( $this, 'return_do_not_allow' ) );
-               $this->make_ajax_call( 'customize_trash' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'changeset_trash_unauthorized', $this->_last_response_parsed['data']['code'] );
-               remove_filter( 'map_meta_cap', array( $this, 'return_do_not_allow' ) );
-
-               $lock_user_id  = static::factory()->user->create( array( 'role' => 'administrator' ) );
-               $previous_user = get_current_user_id();
-               wp_set_current_user( $lock_user_id );
-               $wp_customize->set_changeset_lock( $wp_customize->changeset_post_id() );
-               wp_set_current_user( $previous_user );
-               $this->make_ajax_call( 'customize_trash' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'changeset_locked', $this->_last_response_parsed['data']['code'] );
-               delete_post_meta( $wp_customize->changeset_post_id(), '_edit_lock' );
-
-               wp_update_post(
-                       array(
-                               'ID'          => $wp_customize->changeset_post_id(),
-                               'post_status' => 'trash',
-                       )
-               );
-               $this->make_ajax_call( 'customize_trash' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'changeset_already_trashed', $this->_last_response_parsed['data']['code'] );
-
-               wp_update_post(
-                       array(
-                               'ID'          => $wp_customize->changeset_post_id(),
-                               'post_status' => 'draft',
-                       )
-               );
-
-               $wp_trash_post_count = did_action( 'wp_trash_post' );
-               add_filter( 'pre_trash_post', '__return_false' );
-               $this->make_ajax_call( 'customize_trash' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'changeset_trash_failure', $this->_last_response_parsed['data']['code'] );
-               remove_filter( 'pre_trash_post', '__return_false' );
-               $this->assertSame( $wp_trash_post_count, did_action( 'wp_trash_post' ) );
-
-               $wp_trash_post_count = did_action( 'wp_trash_post' );
-               $this->assertSame( 'draft', get_post_status( $wp_customize->changeset_post_id() ) );
-               $this->make_ajax_call( 'customize_trash' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'trash', get_post_status( $wp_customize->changeset_post_id() ) );
-               $this->assertSame( $wp_trash_post_count + 1, did_action( 'wp_trash_post' ) );
-       }
-
-       /**
-        * Return caps array containing 'do_not_allow'.
-        *
-        * @return array Caps.
-        */
-       public function return_do_not_allow() {
-               return array( 'do_not_allow' );
-       }
-
-       /**
-        * Test request for dismissing autosave changesets.
-        *
-        * @ticket 39896
-        * @covers WP_Customize_Manager::handle_dismiss_autosave_or_lock_request
-        * @covers WP_Customize_Manager::dismiss_user_auto_draft_changesets
-        */
-       public function test_handle_dismiss_autosave_or_lock_request() {
-               $uuid          = wp_generate_uuid4();
-               $wp_customize  = $this->set_up_valid_state( $uuid );
-               $valid_user_id = get_current_user_id();
-
-               // Temporarily remove user to test requirement that user is logged in. See #42450.
-               wp_set_current_user( 0 );
-               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'unauthenticated', $this->_last_response_parsed['data'] );
-               wp_set_current_user( $valid_user_id );
-
-               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'invalid_nonce', $this->_last_response_parsed['data'] );
-
-               $nonce             = wp_create_nonce( 'customize_dismiss_autosave_or_lock' );
-               $_POST['nonce']    = $nonce;
-               $_GET['nonce']     = $nonce;
-               $_REQUEST['nonce'] = $nonce;
-
-               $_POST['dismiss_lock']    = true;
-               $_GET['dismiss_lock']     = true;
-               $_REQUEST['dismiss_lock'] = true;
-               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'no_changeset_to_dismiss_lock', $this->_last_response_parsed['data'] );
-
-               $_POST['dismiss_autosave']    = true;
-               $_GET['dismiss_autosave']     = true;
-               $_REQUEST['dismiss_autosave'] = true;
-               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'no_auto_draft_to_delete', $this->_last_response_parsed['data'] );
-
-               $other_user_id = self::factory()->user->create();
-
-               // Create auto-drafts.
-               $user_auto_draft_ids = array();
-               for ( $i = 0; $i < 3; $i++ ) {
-                       $user_auto_draft_ids[] = self::factory()->post->create(
-                               array(
-                                       'post_name'    => wp_generate_uuid4(),
-                                       'post_type'    => 'customize_changeset',
-                                       'post_status'  => 'auto-draft',
-                                       'post_author'  => self::$admin_user_id,
-                                       'post_content' => wp_json_encode( array() ),
-                               )
-                       );
-               }
-               $other_user_auto_draft_ids = array();
-               for ( $i = 0; $i < 3; $i++ ) {
-                       $other_user_auto_draft_ids[] = self::factory()->post->create(
-                               array(
-                                       'post_name'    => wp_generate_uuid4(),
-                                       'post_type'    => 'customize_changeset',
-                                       'post_status'  => 'auto-draft',
-                                       'post_author'  => $other_user_id,
-                                       'post_content' => wp_json_encode( array() ),
-                               )
-                       );
-               }
-               foreach ( array_merge( $user_auto_draft_ids, $other_user_auto_draft_ids ) as $post_id ) {
-                       $this->assertFalse( (bool) get_post_meta( $post_id, '_customize_restore_dismissed', true ) );
-               }
-               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'auto_draft_dismissed', $this->_last_response_parsed['data'] );
-               foreach ( $user_auto_draft_ids as $post_id ) {
-                       $this->assertSame( 'auto-draft', get_post_status( $post_id ) );
-                       $this->assertTrue( (bool) get_post_meta( $post_id, '_customize_restore_dismissed', true ) );
-               }
-               foreach ( $other_user_auto_draft_ids as $post_id ) {
-                       $this->assertSame( 'auto-draft', get_post_status( $post_id ) );
-                       $this->assertFalse( (bool) get_post_meta( $post_id, '_customize_restore_dismissed', true ) );
-               }
-
-               // Subsequent test results in none dismissed.
-               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'no_auto_draft_to_delete', $this->_last_response_parsed['data'] );
-
-               // Save a changeset as a draft.
-               $r = $wp_customize->save_changeset_post(
-                       array(
-                               'data'   => array(
-                                       'blogname' => array(
-                                               'value' => 'Foo',
-                                       ),
-                               ),
-                               'status' => 'draft',
-                       )
-               );
-
-               $_POST['dismiss_autosave']    = false;
-               $_GET['dismiss_autosave']     = false;
-               $_REQUEST['dismiss_autosave'] = false;
-               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'changeset_lock_dismissed', $this->_last_response_parsed['data'] );
-
-               $_POST['dismiss_autosave']    = true;
-               $_GET['dismiss_autosave']     = true;
-               $_REQUEST['dismiss_autosave'] = true;
-               $this->assertNotWPError( $r );
-               $this->assertFalse( wp_get_post_autosave( $wp_customize->changeset_post_id() ) );
-               $this->assertStringContainsString( 'Foo', get_post( $wp_customize->changeset_post_id() )->post_content );
-
-               // Since no autosave yet, confirm no action.
-               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'no_autosave_revision_to_delete', $this->_last_response_parsed['data'] );
-
-               // Add the autosave revision.
-               $r = $wp_customize->save_changeset_post(
-                       array(
-                               'data'     => array(
-                                       'blogname' => array(
-                                               'value' => 'Bar',
-                                       ),
-                               ),
-                               'autosave' => true,
-                       )
-               );
-               $this->assertNotWPError( $r );
-               $autosave_revision = wp_get_post_autosave( $wp_customize->changeset_post_id() );
-               $this->assertInstanceOf( 'WP_Post', $autosave_revision );
-               $this->assertStringContainsString( 'Foo', get_post( $wp_customize->changeset_post_id() )->post_content );
-               $this->assertStringContainsString( 'Bar', $autosave_revision->post_content );
-
-               // Confirm autosave gets deleted.
-               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'autosave_revision_deleted', $this->_last_response_parsed['data'] );
-               $this->assertFalse( wp_get_post_autosave( $wp_customize->changeset_post_id() ) );
-
-               // Since no autosave yet, confirm no action.
-               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'no_autosave_revision_to_delete', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * Test request for retrieving installed themes.
-        *
-        * @ticket 54549
-        * @covers WP_Customize_Manager::handle_load_themes_request
-        */
-       public function test_wp_ajax_customize_load_themes_action() {
-               $arguments = array(
-                       'changeset_uuid'     => false,
-                       'settings_previewed' => true,
-                       'branching'          => false,
-               );
-               new WP_Customize_Manager( $arguments );
-               wp_set_current_user( self::$admin_user_id );
-               $nonce                 = wp_create_nonce( 'switch_themes' );
-               $_POST['nonce']        = $nonce;
-               $_GET['nonce']         = $nonce;
-               $_REQUEST['nonce']     = $nonce;
-               $_POST['theme_action'] = 'installed';
-               $this->make_ajax_call( 'customize_load_themes' );
-               $response = $this->_last_response_parsed;
-               $this->assertIsArray( $response, 'Response is not an array' );
-
-               $this->assertArrayHasKey( 'success', $response, 'Response must have a "success" key' );
-               $this->assertTrue( $response['success'], 'Response was not "success"' );
-
-               $this->assertArrayHasKey( 'data', $response, 'Response must have a "data" key' );
-               $this->assertIsArray( $response['data'], 'The response "data" is not an array' );
-               $this->assertArrayHasKey( 'themes', $response['data'], 'The response data must have a "themes" key' );
-               $this->assertIsArray( $response['data']['themes'], 'Themes data is not an array' );
-               $this->assertNotEmpty( $response['data']['themes'], 'Themes data must not be empty' );
-
-               foreach ( $response['data']['themes'] as $theme ) {
-                       $this->assertIsArray( $theme, 'Theme is not an array' );
-                       $this->assertNotEmpty( $theme, 'Theme data must not be empty' );
-                       $this->assertArrayHasKey( 'id', $theme, 'Theme data must have an "id" key' );
-                       $this->assertNotEmpty( $theme['id'], 'Theme id cannot be empty' );
-
-                       $this->assertArrayHasKey( 'name', $theme, 'Theme data must have a "name" key' );
-                       $this->assertNotEmpty( $theme['name'], 'Theme name cannot be empty' );
-
-                       $this->assertArrayHasKey( 'blockTheme', $theme, 'Themes data must include information about blocks support' );
-               }
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxCustomizeMenusphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/CustomizeMenus.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/CustomizeMenus.php 2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/CustomizeMenus.php   2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,799 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Testing Ajax customize menus functionality.
- *
- * @package    WordPress
- * @subpackage UnitTests
- * @since      4.3.0
- * @group      ajax
- */
-class Tests_Ajax_CustomizeMenus extends WP_Ajax_UnitTestCase {
-
-       /**
-        * Instance of WP_Customize_Manager which is reset for each test.
-        *
-        * @var WP_Customize_Manager
-        */
-       public $wp_customize;
-
-       /**
-        * Page IDs.
-        *
-        * @var int[]
-        */
-       public static $pages;
-
-       /**
-        * Post IDs.
-        *
-        * @var int[]
-        */
-       public static $posts;
-
-       /**
-        * Term IDs.
-        *
-        * @var int[]
-        */
-       public static $terms;
-
-       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
-               // Make some post objects.
-               self::$posts = $factory->post->create_many( 5 );
-               self::$pages = $factory->post->create_many( 5, array( 'post_type' => 'page' ) );
-
-               // Some terms too.
-               self::$terms = $factory->term->create_many( 5 );
-       }
-
-       /**
-        * Set up the test fixture.
-        */
-       public function set_up() {
-               parent::set_up();
-               require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
-               wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
-               global $wp_customize;
-               $this->wp_customize = new WP_Customize_Manager();
-               $wp_customize       = $this->wp_customize;
-       }
-
-       /**
-        * Helper to keep it DRY
-        *
-        * @param string $action Action.
-        */
-       protected function make_ajax_call( $action ) {
-               // Make the request.
-               try {
-                       $this->_handleAjax( $action );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-       }
-
-       /**
-        * Testing capabilities check for ajax_load_available_items method
-        *
-        * @dataProvider data_ajax_load_available_items_cap_check
-        *
-        * @covers WP_Customize_Nav_Menus::ajax_load_available_items
-        *
-        * @param string $role              The role we're checking caps against.
-        * @param array  $expected_results  Expected results.
-        */
-       public function test_ajax_load_available_items_cap_check( $role, $expected_results ) {
-
-               if ( 'administrator' !== $role ) {
-                       // If we're not an admin, we should get a wp_die( -1 ).
-                       $this->expectException( 'WPAjaxDieStopException' );
-                       $this->expectExceptionMessage( '-1' );
-               }
-
-               wp_set_current_user( self::factory()->user->create( array( 'role' => $role ) ) );
-
-               $_POST = array(
-                       'action'                => 'load-available-menu-items-customizer',
-                       'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
-               );
-
-               $this->make_ajax_call( 'load-available-menu-items-customizer' );
-
-               // If we are an admin, we should get a proper response.
-               if ( 'administrator' === $role ) {
-                       // Get the results.
-                       $response = json_decode( $this->_last_response, true );
-
-                       $this->assertSame( $expected_results, $response );
-               }
-
-       }
-
-       /**
-        * Data provider for test_ajax_load_available_items_cap_check().
-        *
-        * Provides various post_args to induce error messages in the that can be
-        * compared to the expected_results.
-        *
-        * @since 4.3.0
-        *
-        * @return array {
-        *     @type array {
-        *         @string string $role             The role that will test caps for.
-        *         @array  array  $expected_results The expected results from the Ajax call.
-        *     }
-        * }
-        */
-       public function data_ajax_load_available_items_cap_check() {
-               return array(
-                       array(
-                               'subscriber',
-                               array(),
-                       ),
-                       array(
-                               'contributor',
-                               array(),
-                       ),
-                       array(
-                               'author',
-                               array(),
-                       ),
-                       array(
-                               'editor',
-                               array(),
-                       ),
-                       array(
-                               'administrator',
-                               array(
-                                       'success' => false,
-                                       'data'    => 'nav_menus_missing_type_or_object_parameter',
-                               ),
-                       ),
-               );
-       }
-
-       /**
-        * Testing the error messaging for ajax_load_available_items
-        *
-        * @dataProvider data_ajax_load_available_items_error_messages
-        *
-        * @covers WP_Customize_Nav_Menus::ajax_load_available_items
-        *
-        * @param array $post_args POST args.
-        * @param mixed $expected_results Expected results.
-        */
-       public function test_ajax_load_available_items_error_messages( $post_args, $expected_results ) {
-
-               $_POST = array_merge(
-                       array(
-                               'action'                => 'load-available-menu-items-customizer',
-                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
-                       ),
-                       $post_args
-               );
-
-               // Make the request.
-               $this->make_ajax_call( 'load-available-menu-items-customizer' );
-
-               // Get the results.
-               $response = json_decode( $this->_last_response, true );
-
-               $this->assertSame( $expected_results, $response );
-       }
-
-       /**
-        * Data provider for test_ajax_load_available_items_error_message().
-        *
-        * Provides various post_args to induce error messages in the that can be
-        * compared to the expected_results.
-        *
-        * @since 4.3.0
-        *
-        * @return array {
-        *     @type array {
-        * @array array $post_args        The arguments that will merged with the $_POST array.
-        * @array array $expected_results The expected results from the Ajax call.
-        *     }
-        * }
-        */
-       public function data_ajax_load_available_items_error_messages() {
-               return array(
-                       // Testing empty obj_type and type.
-                       array(
-                               array(
-                                       'type'   => '',
-                                       'object' => '',
-                               ),
-                               array(
-                                       'success' => false,
-                                       'data'    => 'nav_menus_missing_type_or_object_parameter',
-                               ),
-                       ),
-                       // Testing empty obj_type.
-                       array(
-                               array(
-                                       'type'   => 'post_type',
-                                       'object' => '',
-                               ),
-                               array(
-                                       'success' => false,
-                                       'data'    => 'nav_menus_missing_type_or_object_parameter',
-                               ),
-                       ),
-                       // Testing empty type.
-                       array(
-                               array(
-                                       'type'   => '',
-                                       'object' => 'post',
-                               ),
-                               array(
-                                       'success' => false,
-                                       'data'    => 'nav_menus_missing_type_or_object_parameter',
-                               ),
-                       ),
-                       // Testing empty type of a bulk request.
-                       array(
-                               array(
-                                       'item_types' => array(
-                                               array(
-                                                       'type'   => 'post_type',
-                                                       'object' => 'post',
-                                               ),
-                                               array(
-                                                       'type'   => 'post_type',
-                                                       'object' => '',
-                                               ),
-                                       ),
-                               ),
-                               array(
-                                       'success' => false,
-                                       'data'    => 'nav_menus_missing_type_or_object_parameter',
-                               ),
-                       ),
-                       // Testing incorrect type option.
-                       array(
-                               array(
-                                       'type'   => 'post_type',
-                                       'object' => 'invalid',
-                               ),
-                               array(
-                                       'success' => false,
-                                       'data'    => 'nav_menus_invalid_post_type',
-                               ),
-                       ),
-               );
-       }
-
-       /**
-        * Testing the success status.
-        *
-        * @dataProvider data_ajax_load_available_items_success_status
-        *
-        * @covers WP_Customize_Nav_Menus::ajax_load_available_items
-        *
-        * @param array $post_args       POST args.
-        * @param array $success_status  Success status.
-        */
-       public function test_ajax_load_available_items_success_status( $post_args, $success_status ) {
-
-               $_POST = array_merge(
-                       array(
-                               'action'                => 'load-available-menu-items-customizer',
-                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
-                       ),
-                       $post_args
-               );
-
-               // Make the request.
-               $this->make_ajax_call( 'load-available-menu-items-customizer' );
-
-               // Get the results.
-               $response = json_decode( $this->_last_response, true );
-               $this->assertSame( $success_status, $response['success'] );
-
-       }
-
-       /**
-        * Data provider for test_ajax_load_available_items_success_status().
-        *
-        * Provides various post_args to retrieve results and compare against
-        * the success status.
-        *
-        * @since 4.3.0
-        *
-        * @return array {
-        *     @type array {
-        *         @type array $post_args      The arguments that will merged with the $_POST array.
-        *         @type bool  $success_status The expected success status.
-        *     }
-        * }
-        */
-       public function data_ajax_load_available_items_success_status() {
-               return array(
-                       array(
-                               array(
-                                       'type'   => 'post_type',
-                                       'object' => 'post',
-                               ),
-                               true,
-                       ),
-                       array(
-                               array(
-                                       'type'   => 'post_type',
-                                       'object' => 'page',
-                               ),
-                               true,
-                       ),
-                       array(
-                               array(
-                                       'type'   => 'post_type',
-                                       'object' => 'custom',
-                               ),
-                               false,
-                       ),
-                       array(
-                               array(
-                                       'type'   => 'taxonomy',
-                                       'object' => 'post_tag',
-                               ),
-                               true,
-                       ),
-                       // Testing a bulk request.
-                       array(
-                               array(
-                                       'item_types' => array(
-                                               array(
-                                                       'type'   => 'post_type',
-                                                       'object' => 'post',
-                                               ),
-                                               array(
-                                                       'type'   => 'post_type',
-                                                       'object' => 'page',
-                                               ),
-                                       ),
-                               ),
-                               true,
-                       ),
-               );
-       }
-
-       /**
-        * Testing the array structure for a single item
-        *
-        * @dataProvider data_ajax_load_available_items_structure
-        *
-        * @covers WP_Customize_Nav_Menus::ajax_load_available_items
-        *
-        * @param array $post_args POST args.
-        */
-       public function test2_ajax_load_available_items_structure( $post_args ) {
-               do_action( 'customize_register', $this->wp_customize );
-
-               $expected_keys = array(
-                       'id',
-                       'title',
-                       'type',
-                       'type_label',
-                       'object',
-                       'object_id',
-                       'url',
-               );
-
-               $auto_draft_post = $this->wp_customize->nav_menus->insert_auto_draft_post(
-                       array(
-                               'post_title' => 'Test Auto Draft',
-                               'post_type'  => 'post',
-                       )
-               );
-               $this->wp_customize->set_post_value( 'nav_menus_created_posts', array( $auto_draft_post->ID ) );
-               $this->wp_customize->get_setting( 'nav_menus_created_posts' )->preview();
-
-               $_POST = array_merge(
-                       array(
-                               'action'                => 'load-available-menu-items-customizer',
-                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
-                       ),
-                       $post_args
-               );
-
-               // Make the request.
-               $this->make_ajax_call( 'load-available-menu-items-customizer' );
-
-               // Get the results.
-               $response = json_decode( $this->_last_response, true );
-
-               $this->assertNotEmpty( current( $response['data']['items'] ) );
-
-               // Get the second index to avoid the home page edge case.
-               $first_prop = current( $response['data']['items'] );
-               $test_item  = $first_prop[1];
-
-               foreach ( $expected_keys as $key ) {
-                       $this->assertArrayHasKey( $key, $test_item );
-                       $this->assertNotEmpty( $test_item[ $key ] );
-               }
-
-               // Special test for the home page.
-               if ( 'page' === $test_item['object'] ) {
-                       $first_prop = current( $response['data']['items'] );
-                       $home       = $first_prop[0];
-                       foreach ( $expected_keys as $key ) {
-                               if ( 'object_id' !== $key ) {
-                                       $this->assertArrayHasKey( $key, $home );
-                                       if ( 'object' !== $key ) {
-                                               $this->assertNotEmpty( $home[ $key ] );
-                                       }
-                               }
-                       }
-               } elseif ( 'post' === $test_item['object'] ) {
-                       $item_ids = wp_list_pluck( $response['data']['items']['post_type:post'], 'id' );
-                       $this->assertContains( 'post-' . $auto_draft_post->ID, $item_ids );
-               }
-       }
-
-       /**
-        * Data provider for test_ajax_load_available_items_structure().
-        *
-        * Provides various post_args to return a list of items to test the array structure of.
-        *
-        * @since 4.3.0
-        *
-        * @return array {
-        *     @type array {
-        *         @type array $post_args The arguments that will merged with the $_POST array.
-        *     }
-        * }
-        */
-       public function data_ajax_load_available_items_structure() {
-               return array(
-                       array(
-                               array(
-                                       'type'   => 'post_type',
-                                       'object' => 'post',
-                               ),
-                       ),
-                       array(
-                               array(
-                                       'type'   => 'post_type',
-                                       'object' => 'page',
-                               ),
-                       ),
-                       array(
-                               array(
-                                       'type'   => 'taxonomy',
-                                       'object' => 'post_tag',
-                               ),
-                       ),
-               );
-       }
-
-       /**
-        * Testing the error messages for ajax_search_available_items
-        *
-        * @dataProvider data_ajax_search_available_items_caps_check
-        *
-        * @covers WP_Customize_Nav_Menus::ajax_search_available_items
-        * @covers WP_Customize_Nav_Menus::search_available_items_query
-        *
-        * @param string $role             Role.
-        * @param array  $expected_results Expected results.
-        */
-       public function test_ajax_search_available_items_caps_check( $role, $expected_results ) {
-
-               if ( 'administrator' !== $role ) {
-                       // If we're not an admin, we should get a wp_die( -1 ).
-                       $this->expectException( 'WPAjaxDieStopException' );
-                       $this->expectExceptionMessage( '-1' );
-               }
-
-               wp_set_current_user( self::factory()->user->create( array( 'role' => $role ) ) );
-
-               $_POST = array(
-                       'action'                => 'search-available-menu-items-customizer',
-                       'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
-               );
-
-               $this->make_ajax_call( 'search-available-menu-items-customizer' );
-
-               // If we are an admin, we should get a proper response.
-               if ( 'administrator' === $role ) {
-                       // Get the results.
-                       $response = json_decode( $this->_last_response, true );
-
-                       $this->assertSame( $expected_results, $response );
-               }
-       }
-
-       /**
-        * Data provider for test_ajax_search_available_items_caps_check().
-        *
-        * Provides various post_args to induce error messages in the that can be
-        * compared to the expected_results.
-        *
-        * @since 4.3.0
-        *
-        * @todo Make this more DRY
-        *
-        * @return array {
-        *     @type array {
-        * @string string $role             The role that will test caps for.
-        * @array  array  $expected_results The expected results from the Ajax call.
-        *     }
-        * }
-        */
-       public function data_ajax_search_available_items_caps_check() {
-               return array(
-                       array(
-                               'subscriber',
-                               array(),
-                       ),
-                       array(
-                               'contributor',
-                               array(),
-                       ),
-                       array(
-                               'author',
-                               array(),
-                       ),
-                       array(
-                               'editor',
-                               array(),
-                       ),
-                       array(
-                               'administrator',
-                               array(
-                                       'success' => false,
-                                       'data'    => 'nav_menus_missing_search_parameter',
-                               ),
-                       ),
-               );
-       }
-
-       /**
-        * Testing the results of various searches
-        *
-        * @dataProvider data_ajax_search_available_items_results
-        *
-        * @covers WP_Customize_Nav_Menus::ajax_search_available_items
-        * @covers WP_Customize_Nav_Menus::search_available_items_query
-        *
-        * @param array $post_args        POST args.
-        * @param array $expected_results Expected results.
-        */
-       public function test_ajax_search_available_items_results( $post_args, $expected_results ) {
-               do_action( 'customize_register', $this->wp_customize );
-
-               self::factory()->post->create_many( 5, array( 'post_title' => 'Test Post' ) );
-               $included_auto_draft_post = $this->wp_customize->nav_menus->insert_auto_draft_post(
-                       array(
-                               'post_title' => 'Test Included Auto Draft',
-                               'post_type'  => 'post',
-                       )
-               );
-               $excluded_auto_draft_post = $this->wp_customize->nav_menus->insert_auto_draft_post(
-                       array(
-                               'post_title' => 'Excluded Auto Draft',
-                               'post_type'  => 'post',
-                       )
-               );
-               $this->wp_customize->set_post_value( 'nav_menus_created_posts', array( $included_auto_draft_post->ID, $excluded_auto_draft_post->ID ) );
-               $this->wp_customize->get_setting( 'nav_menus_created_posts' )->preview();
-
-               $_POST = array_merge(
-                       array(
-                               'action'                => 'search-available-menu-items-customizer',
-                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
-                       ),
-                       $post_args
-               );
-
-               $this->make_ajax_call( 'search-available-menu-items-customizer' );
-
-               $response = json_decode( $this->_last_response, true );
-
-               if ( isset( $post_args['search'] ) && 'test' === $post_args['search'] ) {
-                       $this->assertTrue( $response['success'] );
-                       $this->assertCount( 6, $response['data']['items'] );
-                       $item_ids = wp_list_pluck( $response['data']['items'], 'id' );
-                       $this->assertContains( 'post-' . $included_auto_draft_post->ID, $item_ids );
-                       $this->assertNotContains( 'post-' . $excluded_auto_draft_post->ID, $item_ids );
-               } else {
-                       $this->assertSame( $expected_results, $response );
-               }
-       }
-
-       /**
-        * Data provider for test_ajax_search_available_items_results().
-        *
-        * Provides various post_args to test the results.
-        *
-        * @since 4.3.0
-        *
-        * @return array {
-        *     @type array {
-        * @string string $post_args        The args that will be passed to Ajax.
-        * @array  array  $expected_results The expected results from the Ajax call.
-        *     }
-        * }
-        */
-       public function data_ajax_search_available_items_results() {
-               return array(
-                       array(
-                               array(),
-                               array(
-                                       'success' => false,
-                                       'data'    => 'nav_menus_missing_search_parameter',
-                               ),
-                       ),
-                       array(
-                               array(
-                                       'search' => 'all_the_things',
-                               ),
-                               array(
-                                       'success' => false,
-                                       'data'    => array(
-                                               'message' => 'No results found.',
-                                       ),
-                               ),
-                       ),
-                       array(
-                               array(
-                                       'search' => 'test',
-                               ),
-                               array(
-                                       'success' => true,
-                                       array(),
-                               ),
-                       ),
-               );
-       }
-
-       /**
-        * Testing successful ajax_insert_auto_draft_post() call.
-        *
-        * @covers WP_Customize_Nav_Menus::ajax_insert_auto_draft_post
-        * @covers WP_Customize_Nav_Menus::insert_auto_draft_post
-        */
-       public function test_ajax_insert_auto_draft_post_success() {
-               $_POST                = wp_slash(
-                       array(
-                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
-                               'params'                => array(
-                                       'post_type'  => 'post',
-                                       'post_title' => 'Hello World',
-                               ),
-                       )
-               );
-               $this->_last_response = '';
-               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
-               $response = json_decode( $this->_last_response, true );
-
-               $this->assertTrue( $response['success'] );
-               $this->assertArrayHasKey( 'post_id', $response['data'] );
-               $this->assertArrayHasKey( 'url', $response['data'] );
-               $post = get_post( $response['data']['post_id'] );
-               $this->assertSame( 'Hello World', $post->post_title );
-               $this->assertSame( 'post', $post->post_type );
-               $this->assertSame( '', $post->post_name );
-               $this->assertSame( 'hello-world', get_post_meta( $post->ID, '_customize_draft_post_name', true ) );
-               $this->assertSame( $this->wp_customize->changeset_uuid(), get_post_meta( $post->ID, '_customize_changeset_uuid', true ) );
-       }
-
-       /**
-        * Testing unsuccessful ajax_insert_auto_draft_post() call.
-        *
-        * @covers WP_Customize_Nav_Menus::ajax_insert_auto_draft_post
-        */
-       public function test_ajax_insert_auto_draft_failures() {
-               // No nonce.
-               $_POST                = array();
-               $this->_last_response = '';
-               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
-               $response = json_decode( $this->_last_response, true );
-               $this->assertFalse( $response['success'] );
-               $this->assertSame( 'bad_nonce', $response['data'] );
-
-               // Bad nonce.
-               $_POST                = wp_slash(
-                       array(
-                               'customize-menus-nonce' => 'bad',
-                       )
-               );
-               $this->_last_response = '';
-               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
-               $response = json_decode( $this->_last_response, true );
-               $this->assertFalse( $response['success'] );
-               $this->assertSame( 'bad_nonce', $response['data'] );
-
-               // Bad nonce.
-               wp_set_current_user( self::factory()->user->create( array( 'role' => 'subscriber' ) ) );
-               $_POST                = wp_slash(
-                       array(
-                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
-                       )
-               );
-               $this->_last_response = '';
-               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
-               $response = json_decode( $this->_last_response, true );
-               $this->assertFalse( $response['success'] );
-               $this->assertSame( 'customize_not_allowed', $response['data'] );
-
-               // Missing params.
-               wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
-               $_POST                = wp_slash(
-                       array(
-                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
-                       )
-               );
-               $this->_last_response = '';
-               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
-               $response = json_decode( $this->_last_response, true );
-               $this->assertFalse( $response['success'] );
-               $this->assertSame( 'missing_params', $response['data'] );
-
-               // insufficient_post_permissions.
-               register_post_type( 'privilege', array( 'capability_type' => 'privilege' ) );
-               $_POST                = wp_slash(
-                       array(
-                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
-                               'params'                => array(
-                                       'post_type' => 'privilege',
-                               ),
-                       )
-               );
-               $this->_last_response = '';
-               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
-               $response = json_decode( $this->_last_response, true );
-               $this->assertFalse( $response['success'] );
-               $this->assertSame( 'insufficient_post_permissions', $response['data'] );
-
-               // insufficient_post_permissions.
-               $_POST                = wp_slash(
-                       array(
-                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
-                               'params'                => array(
-                                       'post_type' => 'non-existent',
-                               ),
-                       )
-               );
-               $this->_last_response = '';
-               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
-               $response = json_decode( $this->_last_response, true );
-               $this->assertFalse( $response['success'] );
-               $this->assertSame( 'missing_post_type_param', $response['data'] );
-
-               // missing_post_title.
-               $_POST                = wp_slash(
-                       array(
-                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
-                               'params'                => array(
-                                       'post_type'  => 'post',
-                                       'post_title' => '    ',
-                               ),
-                       )
-               );
-               $this->_last_response = '';
-               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
-               $response = json_decode( $this->_last_response, true );
-               $this->assertFalse( $response['success'] );
-               $this->assertSame( 'missing_post_title', $response['data'] );
-
-               // illegal_params.
-               $_POST                = wp_slash(
-                       array(
-                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
-                               'params'                => array(
-                                       'post_type'    => 'post',
-                                       'post_title'   => 'OK',
-                                       'post_name'    => 'bad',
-                                       'post_content' => 'bad',
-                               ),
-                       )
-               );
-               $this->_last_response = '';
-               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
-               $response = json_decode( $this->_last_response, true );
-               $this->assertFalse( $response['success'] );
-               $this->assertSame( 'illegal_params', $response['data'] );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxDeleteCommentphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/DeleteComment.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/DeleteComment.php  2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/DeleteComment.php    2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,366 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Ajax comment functionality.
- *
- * @package    WordPress
- * @subpackage UnitTests
- * @since      3.4.0
- * @group      ajax
- */
-class Tests_Ajax_DeleteComment extends WP_Ajax_UnitTestCase {
-
-       /**
-        * List of comments.
-        *
-        * @var array
-        */
-       protected static $comments = array();
-
-       /**
-        * ID of a post.
-        *
-        * @var int
-        */
-       protected static $post_id;
-
-       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
-               self::$post_id = $factory->post->create();
-
-               $comment_ids    = $factory->comment->create_post_comments( self::$post_id, 8 );
-               self::$comments = array_map( 'get_comment', $comment_ids );
-       }
-
-       /**
-        * Clears the POST actions in between requests.
-        */
-       protected function _clear_post_action() {
-               unset( $_POST['trash'] );
-               unset( $_POST['untrash'] );
-               unset( $_POST['spam'] );
-               unset( $_POST['unspam'] );
-               unset( $_POST['delete'] );
-               $this->_last_response = '';
-       }
-
-       /*
-        * Test prototype
-        */
-
-       /**
-        * Tests as a privileged user (administrator).
-        *
-        * Expects test to pass.
-        *
-        * @covers ::wp_ajax_delete_comment
-        * @covers ::_wp_ajax_delete_comment_response
-        *
-        * @param WP_Comment $comment Comment object.
-        * @param string     $action  Action: 'trash', 'untrash', etc.
-        */
-       public function _test_as_admin( $comment, $action ) {
-
-               // Reset request.
-               $this->_clear_post_action();
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_POST['id']          = $comment->comment_ID;
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'delete-comment_' . $comment->comment_ID );
-               $_POST[ $action ]     = 1;
-               $_POST['_total']      = count( self::$comments );
-               $_POST['_per_page']   = 100;
-               $_POST['_page']       = 1;
-               $_POST['_url']        = admin_url( 'edit-comments.php' );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'delete-comment' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
-
-               // Ensure everything is correct.
-               $this->assertSame( $comment->comment_ID, (string) $xml->response[0]->comment['id'] );
-               $this->assertSame( 'delete-comment_' . $comment->comment_ID, (string) $xml->response['action'] );
-               $this->assertGreaterThanOrEqual( time() - 10, (int) $xml->response[0]->comment[0]->supplemental[0]->time[0] );
-               $this->assertLessThanOrEqual( time(), (int) $xml->response[0]->comment[0]->supplemental[0]->time[0] );
-
-               // 'trash', 'spam', 'delete' should make the total go down.
-               if ( in_array( $action, array( 'trash', 'spam', 'delete' ), true ) ) {
-                       $total = $_POST['_total'] - 1;
-
-                       // 'unspam', 'untrash' should make the total go up.
-               } elseif ( in_array( $action, array( 'untrash', 'unspam' ), true ) ) {
-                       $total = $_POST['_total'] + 1;
-               }
-
-               // The total is calculated based on a page break -OR- a random number. Let's look for both possible outcomes.
-               $comment_count = wp_count_comments( 0 );
-               $recalc_total  = $comment_count->total_comments;
-
-               // Check for either possible total.
-               $message = sprintf( 'returned value: %1$d $total: %2$d  $recalc_total: %3$d', (int) $xml->response[0]->comment[0]->supplemental[0]->total[0], $total, $recalc_total );
-               $this->assertContains( (int) $xml->response[0]->comment[0]->supplemental[0]->total[0], array( $total, $recalc_total ), $message );
-       }
-
-       /**
-        * Tests as a non-privileged user (subscriber).
-        *
-        * Expects test to fail.
-        *
-        * @covers ::wp_ajax_delete_comment
-        *
-        * @param WP_Comment $comment Comment object.
-        * @param string     $action  Action: 'trash', 'untrash', etc.
-        */
-       public function _test_as_subscriber( $comment, $action ) {
-
-               // Reset request.
-               $this->_clear_post_action();
-
-               // Become a subscriber.
-               $this->_setRole( 'subscriber' );
-
-               // Set up the $_POST request.
-               $_POST['id']          = $comment->comment_ID;
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'delete-comment_' . $comment->comment_ID );
-               $_POST[ $action ]     = 1;
-               $_POST['_total']      = count( self::$comments );
-               $_POST['_per_page']   = 100;
-               $_POST['_page']       = 1;
-               $_POST['_url']        = admin_url( 'edit-comments.php' );
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'delete-comment' );
-       }
-
-
-       /**
-        * Tests with a bad nonce.
-        *
-        * Expects test to fail.
-        *
-        * @covers ::wp_ajax_delete_comment
-        *
-        * @param WP_Comment $comment Comment object.
-        * @param string     $action  Action: 'trash', 'untrash', etc.
-        */
-       public function _test_with_bad_nonce( $comment, $action ) {
-
-               // Reset request.
-               $this->_clear_post_action();
-
-               // Become a subscriber.
-               $this->_setRole( 'administrator' );
-
-               // Set up the $_POST request.
-               $_POST['id']          = $comment->comment_ID;
-               $_POST['_ajax_nonce'] = wp_create_nonce( uniqid() );
-               $_POST[ $action ]     = 1;
-               $_POST['_total']      = count( self::$comments );
-               $_POST['_per_page']   = 100;
-               $_POST['_page']       = 1;
-               $_POST['_url']        = admin_url( 'edit-comments.php' );
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'delete-comment' );
-       }
-
-       /**
-        * Tests with a bad ID.
-        *
-        * Expects test to fail.
-        *
-        * @covers ::wp_ajax_delete_comment
-        *
-        * @param WP_Comment $comment Comment object.
-        * @param string     $action  Action: 'trash', 'untrash', etc.
-        */
-       public function _test_with_bad_id( $comment, $action ) {
-
-               // Reset request.
-               $this->_clear_post_action();
-
-               // Become a subscriber.
-               $this->_setRole( 'administrator' );
-
-               // Set up the $_POST request.
-               $_POST['id']          = 12346789;
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'delete-comment_12346789' );
-               $_POST[ $action ]     = 1;
-               $_POST['_total']      = count( self::$comments );
-               $_POST['_per_page']   = 100;
-               $_POST['_page']       = 1;
-               $_POST['_url']        = admin_url( 'edit-comments.php' );
-
-               // Make the request, look for a timestamp in the exception.
-               try {
-                       $this->_handleAjax( 'delete-comment' );
-                       $this->fail( 'Expected exception: WPAjaxDieStopException' );
-               } catch ( WPAjaxDieStopException $e ) {
-                       $this->assertSame( 10, strlen( $e->getMessage() ) );
-                       $this->assertIsNumeric( $e->getMessage() );
-               } catch ( Exception $e ) {
-                       $this->fail( 'Unexpected exception type: ' . get_class( $e ) );
-               }
-       }
-
-       /**
-        * Tests doubling the action (e.g. trash a trashed comment).
-        *
-        * Expects test to fail.
-        *
-        * @covers ::wp_ajax_delete_comment
-        *
-        * @param WP_Comment $comment Comment object.
-        * @param string     $action  Action: 'trash', 'untrash', etc.
-        */
-       public function _test_double_action( $comment, $action ) {
-
-               // Reset request.
-               $this->_clear_post_action();
-
-               // Become a subscriber.
-               $this->_setRole( 'administrator' );
-
-               // Set up the $_POST request.
-               $_POST['id']          = $comment->comment_ID;
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'delete-comment_' . $comment->comment_ID );
-               $_POST[ $action ]     = 1;
-               $_POST['_total']      = count( self::$comments );
-               $_POST['_per_page']   = 100;
-               $_POST['_page']       = 1;
-               $_POST['_url']        = admin_url( 'edit-comments.php' );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'delete-comment' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-               $this->_last_response = '';
-
-               // Force delete the comment.
-               if ( 'delete' === $action ) {
-                       wp_delete_comment( $comment->comment_ID, true );
-               }
-
-               // Make the request again, look for a timestamp in the exception.
-               try {
-                       $this->_handleAjax( 'delete-comment' );
-                       $this->fail( 'Expected exception: WPAjaxDieStopException' );
-               } catch ( WPAjaxDieStopException $e ) {
-                       $this->assertSame( 10, strlen( $e->getMessage() ) );
-                       $this->assertIsNumeric( $e->getMessage() );
-               } catch ( Exception $e ) {
-                       $this->fail( 'Unexpected exception type: ' . get_class( $e ) );
-               }
-       }
-
-       /**
-        * Deletes a comment as an administrator (expects success).
-        *
-        * @covers ::wp_ajax_delete_comment
-        * @covers ::_wp_ajax_delete_comment_response
-        */
-       public function test_ajax_comment_trash_actions_as_administrator() {
-               // Test trash/untrash.
-               $this->_test_as_admin( self::$comments[0], 'trash' );
-               $this->_test_as_admin( self::$comments[0], 'untrash' );
-
-               // Test spam/unspam.
-               $this->_test_as_admin( self::$comments[1], 'spam' );
-               $this->_test_as_admin( self::$comments[1], 'unspam' );
-
-               // Test delete.
-               $this->_test_as_admin( self::$comments[2], 'delete' );
-       }
-
-       /**
-        * Deletes a comment as a subscriber (expects permission denied).
-        *
-        * @covers ::wp_ajax_delete_comment
-        */
-       public function test_ajax_comment_trash_actions_as_subscriber() {
-               // Test trash/untrash.
-               $this->_test_as_subscriber( self::$comments[0], 'trash' );
-               $this->_test_as_subscriber( self::$comments[0], 'untrash' );
-
-               // Test spam/unspam.
-               $this->_test_as_subscriber( self::$comments[1], 'spam' );
-               $this->_test_as_subscriber( self::$comments[1], 'unspam' );
-
-               // Test delete.
-               $this->_test_as_subscriber( self::$comments[2], 'delete' );
-       }
-
-       /**
-        * Deletes a comment with no ID.
-        *
-        * @covers ::wp_ajax_delete_comment
-        * @covers ::_wp_ajax_delete_comment_response
-        */
-       public function test_ajax_trash_comment_no_id() {
-               // Test trash/untrash.
-               $this->_test_as_admin( self::$comments[0], 'trash' );
-               $this->_test_as_admin( self::$comments[0], 'untrash' );
-
-               // Test spam/unspam.
-               $this->_test_as_admin( self::$comments[1], 'spam' );
-               $this->_test_as_admin( self::$comments[1], 'unspam' );
-
-               // Test delete.
-               $this->_test_as_admin( self::$comments[2], 'delete' );
-       }
-
-       /**
-        * Deletes a comment with a bad nonce.
-        *
-        * @covers ::wp_ajax_delete_comment
-        */
-       public function test_ajax_trash_comment_bad_nonce() {
-               // Test trash/untrash.
-               $this->_test_with_bad_nonce( self::$comments[0], 'trash' );
-               $this->_test_with_bad_nonce( self::$comments[0], 'untrash' );
-
-               // Test spam/unspam.
-               $this->_test_with_bad_nonce( self::$comments[1], 'spam' );
-               $this->_test_with_bad_nonce( self::$comments[1], 'unspam' );
-
-               // Test delete.
-               $this->_test_with_bad_nonce( self::$comments[2], 'delete' );
-       }
-
-       /**
-        * Tests trashing an already trashed comment, etc.
-        *
-        * @covers ::wp_ajax_delete_comment
-        */
-       public function test_ajax_trash_double_action() {
-               // Test trash/untrash.
-               $this->_test_double_action( self::$comments[0], 'trash' );
-               $this->_test_double_action( self::$comments[0], 'untrash' );
-
-               // Test spam/unspam.
-               $this->_test_double_action( self::$comments[1], 'spam' );
-               $this->_test_double_action( self::$comments[1], 'unspam' );
-
-               // Test delete.
-               $this->_test_double_action( self::$comments[2], 'delete' );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxDeletePluginphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/DeletePlugin.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/DeletePlugin.php   2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/DeletePlugin.php     2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,165 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Ajax handler for deleting a plugin.
- *
- * @group ajax
- *
- * @covers ::wp_ajax_delete_plugin
- */
-class Tests_Ajax_Delete_Plugin extends WP_Ajax_UnitTestCase {
-
-       public function test_missing_nonce() {
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'delete-plugin' );
-       }
-
-       public function test_missing_plugin() {
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
-               $_POST['slug']        = 'foo';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'delete-plugin' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = array(
-                       'success' => false,
-                       'data'    => array(
-                               'slug'         => '',
-                               'errorCode'    => 'no_plugin_specified',
-                               'errorMessage' => 'No plugin specified.',
-                       ),
-               );
-
-               $this->assertSameSets( $expected, $response );
-       }
-
-       public function test_missing_slug() {
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
-               $_POST['plugin']      = 'foo/bar.php';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'delete-plugin' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = array(
-                       'success' => false,
-                       'data'    => array(
-                               'slug'         => '',
-                               'errorCode'    => 'no_plugin_specified',
-                               'errorMessage' => 'No plugin specified.',
-                       ),
-               );
-
-               $this->assertSameSets( $expected, $response );
-       }
-
-       public function test_missing_capability() {
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
-               $_POST['plugin']      = 'foo/bar.php';
-               $_POST['slug']        = 'foo';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'delete-plugin' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = array(
-                       'success' => false,
-                       'data'    => array(
-                               'delete'       => 'plugin',
-                               'slug'         => 'foo',
-                               'errorMessage' => 'Sorry, you are not allowed to delete plugins for this site.',
-                       ),
-               );
-
-               $this->assertSameSets( $expected, $response );
-       }
-
-       public function test_invalid_file() {
-               $this->_setRole( 'administrator' );
-
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
-               $_POST['plugin']      = '../foo/bar.php';
-               $_POST['slug']        = 'foo';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'delete-plugin' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = array(
-                       'success' => false,
-                       'data'    => array(
-                               'delete'       => 'plugin',
-                               'slug'         => 'foo',
-                               'errorMessage' => 'Sorry, you are not allowed to delete plugins for this site.',
-                       ),
-               );
-
-               $this->assertSameSets( $expected, $response );
-       }
-
-       /**
-        * @group ms-excluded
-        *
-        * @covers ::wp_ajax_delete_plugin
-        * @covers ::delete_plugins
-        */
-       public function test_delete_plugin() {
-               $this->_setRole( 'administrator' );
-
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
-               $_POST['plugin']      = 'foo.php';
-               $_POST['slug']        = 'foo';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'delete-plugin' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = array(
-                       'success' => true,
-                       'data'    => array(
-                               'delete'     => 'plugin',
-                               'slug'       => 'foo',
-                               'plugin'     => 'foo.php',
-                               'pluginName' => '',
-                       ),
-               );
-
-               $this->assertSameSets( $expected, $response );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxDimCommentphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/DimComment.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/DimComment.php     2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/DimComment.php       2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,241 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Ajax comment functionality
- *
- * @package    WordPress
- * @subpackage UnitTests
- * @since      3.4.0
- * @group      ajax
- *
- * @covers ::wp_ajax_dim_comment
- */
-class Tests_Ajax_DimComment extends WP_Ajax_UnitTestCase {
-
-       /**
-        * List of comments.
-        *
-        * @var array
-        */
-       protected $_comments = array();
-
-       /**
-        * Sets up the test fixture.
-        */
-       public function set_up() {
-               parent::set_up();
-               $post_id         = self::factory()->post->create();
-               $this->_comments = self::factory()->comment->create_post_comments( $post_id, 15 );
-               $this->_comments = array_map( 'get_comment', $this->_comments );
-       }
-
-       /**
-        * Clears the POST actions in between requests.
-        */
-       protected function _clear_post_action() {
-               unset( $_POST['id'] );
-               unset( $_POST['new'] );
-               $this->_last_response = '';
-       }
-
-       /*
-        * Test prototype
-        */
-
-       /**
-        * Tests as a privileged user (administrator).
-        *
-        * Expects test to pass.
-        *
-        * @param WP_Comment $comment Comment object.
-        */
-       public function _test_as_admin( $comment ) {
-
-               // Reset request.
-               $this->_clear_post_action();
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_POST['id']          = $comment->comment_ID;
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'approve-comment_' . $comment->comment_ID );
-               $_POST['_total']      = count( $this->_comments );
-               $_POST['_per_page']   = 100;
-               $_POST['_page']       = 1;
-               $_POST['_url']        = admin_url( 'edit-comments.php' );
-
-               // Save the comment status.
-               $prev_status = wp_get_comment_status( $comment->comment_ID );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'dim-comment' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
-
-               // Ensure everything is correct.
-               $this->assertSame( $comment->comment_ID, (string) $xml->response[0]->comment['id'] );
-               $this->assertSame( 'dim-comment_' . $comment->comment_ID, (string) $xml->response['action'] );
-               $this->assertGreaterThanOrEqual( time() - 10, (int) $xml->response[0]->comment[0]->supplemental[0]->time[0] );
-               $this->assertLessThanOrEqual( time(), (int) $xml->response[0]->comment[0]->supplemental[0]->time[0] );
-
-               // Check the status.
-               $current = wp_get_comment_status( $comment->comment_ID );
-               if ( in_array( $prev_status, array( 'unapproved', 'spam' ), true ) ) {
-                       $this->assertSame( 'approved', $current );
-               } else {
-                       $this->assertSame( 'unapproved', $current );
-               }
-
-               // The total is calculated based on a page break -OR- a random number. Let's look for both possible outcomes.
-               $comment_count = wp_count_comments( 0 );
-               $recalc_total  = $comment_count->total_comments;
-
-               // Delta is not specified, it will always be 1 lower than the request.
-               $total = $_POST['_total'] - 1;
-
-               // Check for either possible total.
-               $this->assertContains( (int) $xml->response[0]->comment[0]->supplemental[0]->total[0], array( $total, $recalc_total ) );
-       }
-
-       /**
-        * Tests as a non-privileged user (subscriber).
-        *
-        * Expects test to fail.
-        *
-        * @param WP_Comment $comment Comment object.
-        */
-       public function _test_as_subscriber( $comment ) {
-
-               // Reset request.
-               $this->_clear_post_action();
-
-               // Become a subscriber.
-               $this->_setRole( 'subscriber' );
-
-               // Set up the $_POST request.
-               $_POST['id']          = $comment->comment_ID;
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'approve-comment_' . $comment->comment_ID );
-               $_POST['_total']      = count( $this->_comments );
-               $_POST['_per_page']   = 100;
-               $_POST['_page']       = 1;
-               $_POST['_url']        = admin_url( 'edit-comments.php' );
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'dim-comment' );
-       }
-
-       /**
-        * Tests with a bad nonce.
-        *
-        * Expects test to fail.
-        *
-        * @param WP_Comment $comment Comment object.
-        */
-       public function _test_with_bad_nonce( $comment ) {
-
-               // Reset request.
-               $this->_clear_post_action();
-
-               // Become a subscriber.
-               $this->_setRole( 'administrator' );
-
-               // Set up the $_POST request.
-               $_POST['id']          = $comment->comment_ID;
-               $_POST['_ajax_nonce'] = wp_create_nonce( uniqid() );
-               $_POST['_total']      = count( $this->_comments );
-               $_POST['_per_page']   = 100;
-               $_POST['_page']       = 1;
-               $_POST['_url']        = admin_url( 'edit-comments.php' );
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'dim-comment' );
-       }
-
-       /**
-        * Tests with a bad ID.
-        *
-        * Expects test to fail.
-        */
-       public function test_with_bad_id() {
-
-               // Reset request.
-               $this->_clear_post_action();
-
-               // Become a subscriber.
-               $this->_setRole( 'administrator' );
-
-               // Set up the $_POST request.
-               $_POST['id']          = 12346789;
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'dim-comment_12346789' );
-               $_POST['_total']      = count( $this->_comments );
-               $_POST['_per_page']   = 100;
-               $_POST['_page']       = 1;
-               $_POST['_url']        = admin_url( 'edit-comments.php' );
-
-               // Make the request, look for a timestamp in the exception.
-               try {
-                       $this->_handleAjax( 'dim-comment' );
-                       $this->fail( 'Expected exception: WPAjaxDieContinueException' );
-               } catch ( WPAjaxDieContinueException $e ) {
-
-                       // Get the response.
-                       $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
-
-                       // Ensure everything is correct.
-                       $this->assertSame( '0', (string) $xml->response[0]->comment['id'] );
-                       $this->assertSame( 'dim-comment_0', (string) $xml->response['action'] );
-                       $this->assertStringContainsString( 'Comment ' . $_POST['id'] . ' does not exist', $this->_last_response );
-
-               } catch ( Exception $e ) {
-                       $this->fail( 'Unexpected exception type: ' . get_class( $e ) );
-               }
-       }
-
-       /**
-        * Dims a comment as an administrator (expects success).
-        */
-       public function test_ajax_comment_dim_actions_as_administrator() {
-               $comment = array_pop( $this->_comments );
-               $this->_test_as_admin( $comment );
-               $this->_test_as_admin( $comment );
-       }
-
-       /**
-        * Dims a comment as a subscriber (expects permission denied).
-        */
-       public function test_ajax_comment_dim_actions_as_subscriber() {
-               $comment = array_pop( $this->_comments );
-               $this->_test_as_subscriber( $comment );
-       }
-
-       /**
-        * Dims a comment with no ID.
-        */
-       public function test_ajax_dim_comment_no_id() {
-               $comment = array_pop( $this->_comments );
-               $this->_test_as_admin( $comment );
-       }
-
-       /**
-        * Dims a comment with a bad nonce.
-        */
-       public function test_ajax_dim_comment_bad_nonce() {
-               $comment = array_pop( $this->_comments );
-               $this->_test_with_bad_nonce( $comment );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxEditCommentphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/EditComment.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/EditComment.php    2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/EditComment.php      2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,244 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Ajax comment functionality.
- *
- * @package    WordPress
- * @subpackage UnitTests
- * @since      3.4.0
- * @group      ajax
- *
- * @covers ::wp_ajax_edit_comment
- */
-class Tests_Ajax_EditComment extends WP_Ajax_UnitTestCase {
-
-       /**
-        * A post with at least one comment.
-        *
-        * @var mixed
-        */
-       protected $_comment_post = null;
-
-       /**
-        * Sets up the test fixture.
-        */
-       public function set_up() {
-               parent::set_up();
-               $post_id = self::factory()->post->create();
-               self::factory()->comment->create_post_comments( $post_id, 5 );
-               $this->_comment_post = get_post( $post_id );
-       }
-
-       /**
-        * Gets comments as a privileged user (administrator).
-        *
-        * Expects test to pass.
-        */
-       public function test_as_admin() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Get a comment.
-               $comments = get_comments(
-                       array(
-                               'post_id' => $this->_comment_post->ID,
-                       )
-               );
-               $comment  = array_pop( $comments );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
-               $_POST['comment_ID']                  = $comment->comment_ID;
-               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'edit-comment' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
-
-               // Check the meta data.
-               $this->assertSame( '-1', (string) $xml->response[0]->edit_comment['position'] );
-               $this->assertSame( $comment->comment_ID, (string) $xml->response[0]->edit_comment['id'] );
-               $this->assertSame( 'edit-comment_' . $comment->comment_ID, (string) $xml->response['action'] );
-
-               // Check the payload.
-               $this->assertNotEmpty( (string) $xml->response[0]->edit_comment[0]->response_data );
-
-               // And supplemental is empty.
-               $this->assertEmpty( (string) $xml->response[0]->edit_comment[0]->supplemental );
-       }
-
-       /**
-        * @ticket 33154
-        */
-       public function test_editor_can_edit_orphan_comments() {
-               global $wpdb;
-
-               // Become an editor.
-               $this->_setRole( 'editor' );
-
-               // Get a comment.
-               $comments = get_comments(
-                       array(
-                               'post_id' => $this->_comment_post->ID,
-                       )
-               );
-               $comment  = array_pop( $comments );
-
-               // Manually update the comment_post_ID, because wp_update_comment() will prevent it..
-               $wpdb->update( $wpdb->comments, array( 'comment_post_ID' => 0 ), array( 'comment_ID' => $comment->comment_ID ) );
-               clean_comment_cache( $comment->comment_ID );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
-               $_POST['comment_ID']                  = $comment->comment_ID;
-               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'edit-comment' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
-
-               // Check the meta data.
-               $this->assertSame( '-1', (string) $xml->response[0]->edit_comment['position'] );
-               $this->assertSame( $comment->comment_ID, (string) $xml->response[0]->edit_comment['id'] );
-               $this->assertSame( 'edit-comment_' . $comment->comment_ID, (string) $xml->response['action'] );
-
-               // Check the payload.
-               $this->assertNotEmpty( (string) $xml->response[0]->edit_comment[0]->response_data );
-
-               // And supplemental is empty.
-               $this->assertEmpty( (string) $xml->response[0]->edit_comment[0]->supplemental );
-       }
-
-       /**
-        * Gets comments as a non-privileged user (subscriber).
-        *
-        * Expects test to fail.
-        */
-       public function test_as_subscriber() {
-
-               // Become a subscriber.
-               $this->_setRole( 'subscriber' );
-
-               // Get a comment.
-               $comments = get_comments(
-                       array(
-                               'post_id' => $this->_comment_post->ID,
-                       )
-               );
-               $comment  = array_pop( $comments );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
-               $_POST['comment_ID']                  = $comment->comment_ID;
-               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'edit-comment' );
-       }
-
-       /**
-        * Gets comments with a bad nonce.
-        *
-        * Expects test to fail.
-        */
-       public function test_bad_nonce() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Get a comment.
-               $comments = get_comments(
-                       array(
-                               'post_id' => $this->_comment_post->ID,
-                       )
-               );
-               $comment  = array_pop( $comments );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( uniqid() );
-               $_POST['comment_ID']                  = $comment->comment_ID;
-               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'get-comments' );
-       }
-
-       /**
-        * Gets comments for an invalid post.
-        *
-        * This should return valid XML.
-        */
-       public function test_invalid_comment() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
-               $_POST['comment_ID']                  = 123456789;
-               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'edit-comment' );
-       }
-
-       /**
-        * @ticket 39732
-        */
-       public function test_wp_update_comment_data_is_wp_error() {
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Get a comment.
-               $comments = get_comments(
-                       array(
-                               'post_id' => $this->_comment_post->ID,
-                       )
-               );
-               $comment  = array_pop( $comments );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
-               $_POST['comment_ID']                  = $comment->comment_ID;
-               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
-
-               // Simulate filter check error.
-               add_filter( 'wp_update_comment_data', array( $this, '_wp_update_comment_data_filter' ), 10, 3 );
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( 'wp_update_comment_data filter fails for this comment.' );
-               $this->_handleAjax( 'edit-comment' );
-       }
-
-       /**
-        * Blocks comments from being updated by returning WP_Error.
-        */
-       public function _wp_update_comment_data_filter( $data, $comment, $commentarr ) {
-               return new WP_Error( 'comment_wrong', 'wp_update_comment_data filter fails for this comment.', 500 );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxGetCommentsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/GetComments.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/GetComments.php    2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/GetComments.php      2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,160 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Ajax comment functionality.
- *
- * @package    WordPress
- * @subpackage UnitTests
- * @since      3.4.0
- * @group      ajax
- *
- * @covers ::wp_ajax_get_comments
- */
-class Tests_Ajax_GetComments extends WP_Ajax_UnitTestCase {
-
-       /**
-        * A post with at least one comment.
-        *
-        * @var mixed
-        */
-       protected static $comment_post = null;
-
-       /**
-        * A post with no comments.
-        *
-        * @var mixed
-        */
-       protected static $no_comment_post = null;
-
-       protected static $comment_ids = array();
-
-       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
-               self::$comment_post    = $factory->post->create_and_get();
-               self::$comment_ids     = $factory->comment->create_post_comments( self::$comment_post->ID, 5 );
-               self::$no_comment_post = $factory->post->create_and_get();
-       }
-
-       /**
-        * Gets comments as a privileged user (administrator).
-        *
-        * Expects test to pass.
-        */
-       public function test_as_admin() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'get-comments' );
-               $_POST['action']      = 'get-comments';
-               $_POST['p']           = self::$comment_post->ID;
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'get-comments' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
-
-               // Check the meta data.
-               $this->assertSame( '1', (string) $xml->response[0]->comments['position'] );
-               $this->assertSame( '0', (string) $xml->response[0]->comments['id'] );
-               $this->assertSame( 'get-comments_0', (string) $xml->response['action'] );
-
-               // Check the payload.
-               $this->assertNotEmpty( (string) $xml->response[0]->comments[0]->response_data );
-
-               // And supplemental is empty.
-               $this->assertEmpty( (string) $xml->response[0]->comments[0]->supplemental );
-       }
-
-       /**
-        * Gets comments as a non-privileged user (subscriber).
-        *
-        * Expects test to fail.
-        */
-       public function test_as_subscriber() {
-
-               // Become a subscriber.
-               $this->_setRole( 'subscriber' );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'get-comments' );
-               $_POST['action']      = 'get-comments';
-               $_POST['p']           = self::$comment_post->ID;
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'get-comments' );
-       }
-
-       /**
-        * Gets comments with a bad nonce.
-        *
-        * Expects test to fail.
-        */
-       public function test_bad_nonce() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce'] = wp_create_nonce( uniqid() );
-               $_POST['action']      = 'get-comments';
-               $_POST['p']           = self::$comment_post->ID;
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'get-comments' );
-       }
-
-       /**
-        * Gets comments for an invalid post.
-        *
-        * Bad post IDs are set to 0, this should return valid XML.
-        */
-       public function test_invalid_post() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'get-comments' );
-               $_POST['action']      = 'get-comments';
-               $_POST['p']           = 'b0rk';
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'get-comments' );
-       }
-
-       /**
-        * Gets comments for a post with no comments.
-        */
-       public function test_post_with_no_comments() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'get-comments' );
-               $_POST['action']      = 'get-comments';
-               $_POST['p']           = self::$no_comment_post->ID;
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '1' );
-               $this->_handleAjax( 'get-comments' );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxManageThemesphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/ManageThemes.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/ManageThemes.php   2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/ManageThemes.php     2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,179 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Ajax handler for instlaling, updating, and deleting themes.
- *
- * @group ajax
- *
- * @covers ::wp_ajax_update_theme
- */
-class Tests_Ajax_Manage_Themes extends WP_Ajax_UnitTestCase {
-       private $orig_theme_dir;
-       private $theme_root;
-
-       public function set_up() {
-               parent::set_up();
-
-               $this->theme_root     = DIR_TESTDATA . '/themedir1';
-               $this->orig_theme_dir = $GLOBALS['wp_theme_directories'];
-
-               // /themes is necessary as theme.php functions assume /themes is the root if there is only one root.
-               $GLOBALS['wp_theme_directories'] = array( WP_CONTENT_DIR . '/themes', $this->theme_root );
-
-               add_filter( 'theme_root', array( $this, 'filter_theme_root' ) );
-               add_filter( 'stylesheet_root', array( $this, 'filter_theme_root' ) );
-               add_filter( 'template_root', array( $this, 'filter_theme_root' ) );
-
-               wp_clean_themes_cache();
-               unset( $GLOBALS['wp_themes'] );
-       }
-
-       public function tear_down() {
-               $GLOBALS['wp_theme_directories'] = $this->orig_theme_dir;
-               remove_filter( 'theme_root', array( $this, 'filter_theme_root' ) );
-               remove_filter( 'stylesheet_root', array( $this, 'filter_theme_root' ) );
-               remove_filter( 'template_root', array( $this, 'filter_theme_root' ) );
-               wp_clean_themes_cache();
-               unset( $GLOBALS['wp_themes'] );
-
-               parent::tear_down();
-       }
-
-       /**
-        * Replace the normal theme root dir with our pre-made test dir.
-        */
-       public function filter_theme_root() {
-               return $this->theme_root;
-       }
-
-       public function test_missing_slug() {
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'update-theme' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = array(
-                       'success' => false,
-                       'data'    => array(
-                               'slug'         => '',
-                               'errorCode'    => 'no_theme_specified',
-                               'errorMessage' => 'No theme specified.',
-                       ),
-               );
-
-               $this->assertSameSets( $expected, $response );
-       }
-
-       public function test_missing_capability() {
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
-               $_POST['slug']        = 'foo';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'update-theme' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = array(
-                       'success' => false,
-                       'data'    => array(
-                               'update'       => 'theme',
-                               'slug'         => 'foo',
-                               'oldVersion'   => '',
-                               'newVersion'   => '',
-                               'errorMessage' => 'Sorry, you are not allowed to update themes for this site.',
-                       ),
-               );
-
-               $this->assertSameSets( $expected, $response );
-       }
-
-       /**
-        * @group ms-excluded
-        */
-       public function test_update_theme() {
-               $this->_setRole( 'administrator' );
-
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
-               $_POST['slug']        = 'twentyten';
-
-               // Make the request.
-               try {
-
-                       // Prevent wp_update_themes() from running.
-                       wp_installing( true );
-                       $this->_handleAjax( 'update-theme' );
-                       wp_installing( false );
-
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $theme    = wp_get_theme( 'twentyten' );
-               $expected = array(
-                       'success' => false,
-                       'data'    => array(
-                               'update'       => 'theme',
-                               'slug'         => 'twentyten',
-                               'oldVersion'   => $theme->get( 'Version' ),
-                               'newVersion'   => '',
-                               'debug'        => array( 'The theme is at the latest version.' ),
-                               'errorMessage' => 'The theme is at the latest version.',
-                       ),
-               );
-
-               $this->assertSameSets( $expected, $response );
-       }
-
-       /**
-        * @group ms-excluded
-        */
-       public function test_uppercase_theme_slug() {
-               $this->_setRole( 'administrator' );
-
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
-               $_POST['slug']        = 'camelCase';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'update-theme' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = array(
-                       'success' => false,
-                       'data'    => array(
-                               'update'       => 'theme',
-                               'slug'         => 'camelCase',
-                               'oldVersion'   => '1.0',
-                               'newVersion'   => '',
-                               'debug'        => array( 'The theme is at the latest version.' ),
-                               'errorMessage' => 'The theme is at the latest version.',
-                       ),
-               );
-
-               $this->assertSameSets( $expected, $response );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxMediaEditphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/MediaEdit.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/MediaEdit.php      2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/MediaEdit.php        2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,120 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Ajax media editing.
- *
- * @package    WordPress
- * @subpackage UnitTests
- * @since      3.5.0
- * @group      ajax
- *
- * @requires   function imagejpeg
- */
-class Tests_Ajax_MediaEdit extends WP_Ajax_UnitTestCase {
-
-       /**
-        * Tear down the test fixture.
-        */
-       public function tear_down() {
-               // Cleanup.
-               $this->remove_added_uploads();
-               parent::tear_down();
-       }
-
-       /**
-        * @ticket 22985
-        * @requires function imagejpeg
-        *
-        * @covers ::wp_insert_attachment
-        * @covers ::wp_save_image
-        */
-       public function testCropImageThumbnail() {
-               require_once ABSPATH . 'wp-admin/includes/image-edit.php';
-
-               $filename = DIR_TESTDATA . '/images/canola.jpg';
-               $contents = file_get_contents( $filename );
-
-               $upload = wp_upload_bits( wp_basename( $filename ), null, $contents );
-               $id     = $this->_make_attachment( $upload );
-
-               $_REQUEST['action']  = 'image-editor';
-               $_REQUEST['context'] = 'edit-attachment';
-               $_REQUEST['postid']  = $id;
-               $_REQUEST['target']  = 'thumbnail';
-               $_REQUEST['do']      = 'save';
-               $_REQUEST['history'] = '[{"c":{"x":5,"y":8,"w":289,"h":322}}]';
-
-               $media_meta = wp_get_attachment_metadata( $id );
-               $this->assertArrayHasKey( 'sizes', $media_meta, 'attachment should have size data' );
-               $this->assertArrayHasKey( 'medium', $media_meta['sizes'], 'attachment should have data for medium size' );
-               $ret = wp_save_image( $id );
-
-               $media_meta = wp_get_attachment_metadata( $id );
-               $this->assertArrayHasKey( 'sizes', $media_meta, 'cropped attachment should have size data' );
-               $this->assertArrayHasKey( 'medium', $media_meta['sizes'], 'cropped attachment should have data for medium size' );
-       }
-
-       /**
-        * @ticket 32171
-        * @requires function imagejpeg
-        *
-        * @covers ::wp_insert_attachment
-        * @covers ::wp_save_image
-        */
-       public function testImageEditOverwriteConstant() {
-               define( 'IMAGE_EDIT_OVERWRITE', true );
-
-               require_once ABSPATH . 'wp-admin/includes/image-edit.php';
-
-               $filename = DIR_TESTDATA . '/images/canola.jpg';
-               $contents = file_get_contents( $filename );
-
-               $upload = wp_upload_bits( wp_basename( $filename ), null, $contents );
-               $id     = $this->_make_attachment( $upload );
-
-               $_REQUEST['action']  = 'image-editor';
-               $_REQUEST['context'] = 'edit-attachment';
-               $_REQUEST['postid']  = $id;
-               $_REQUEST['target']  = 'all';
-               $_REQUEST['do']      = 'save';
-               $_REQUEST['history'] = '[{"c":{"x":5,"y":8,"w":289,"h":322}}]';
-
-               $ret = wp_save_image( $id );
-
-               $media_meta = wp_get_attachment_metadata( $id );
-               $sizes1     = $media_meta['sizes'];
-
-               $_REQUEST['history'] = '[{"c":{"x":5,"y":8,"w":189,"h":322}}]';
-
-               $ret = wp_save_image( $id );
-
-               $media_meta = wp_get_attachment_metadata( $id );
-               $sizes2     = $media_meta['sizes'];
-
-               $file_path = dirname( get_attached_file( $id ) );
-
-               $files_that_should_not_exist = array();
-
-               foreach ( $sizes1 as $key => $size ) {
-                       if ( $sizes2[ $key ]['file'] !== $size['file'] ) {
-                               $files_that_should_not_exist[] = $file_path . '/' . $size['file'];
-                       }
-               }
-
-               if ( ! empty( $files_that_should_not_exist ) ) {
-                       foreach ( $files_that_should_not_exist as $file ) {
-                               $this->assertFileDoesNotExist( $file, 'IMAGE_EDIT_OVERWRITE is leaving garbage image files behind.' );
-                       }
-               } else {
-                       /*
-                        * This assertion will always pass due to the "if" condition, but prevents this test
-                        * from being marked as "risky" due to the test not performing any assertions.
-                        */
-                       $this->assertSame( array(), $files_that_should_not_exist );
-               }
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxPrivacyErasePersonalDataphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/PrivacyErasePersonalData.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/PrivacyErasePersonalData.php       2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/PrivacyErasePersonalData.php 2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,839 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Testing Ajax handler for erasing personal data.
- *
- * @package WordPress\UnitTests
- * @since 5.2.0
- */
-
-/**
- * Tests_Ajax_PrivacyExportPersonalData class.
- *
- * @since 5.2.0
- *
- * @group ajax
- * @group privacy
- *
- * @covers ::wp_ajax_wp_privacy_erase_personal_data
- */
-class Tests_Ajax_PrivacyErasePersonalData extends WP_Ajax_UnitTestCase {
-
-       /**
-        * User Request ID.
-        *
-        * @since 5.2.0
-        *
-        * @var int $request_id
-        */
-       protected static $request_id;
-
-       /**
-        * User Request Email.
-        *
-        * @since 5.2.0
-        *
-        * @var string $request_email
-        */
-       protected static $request_email;
-
-       /**
-        * Ajax Action.
-        *
-        * @since 5.2.0
-        *
-        * @var string $action
-        */
-       protected static $action;
-
-       /**
-        * Eraser Index.
-        *
-        * @since 5.2.0
-        *
-        * @var int $eraser
-        */
-       protected static $eraser;
-
-       /**
-        * Eraser Key.
-        *
-        * @since 5.2.0
-        *
-        * @var string $eraser_key
-        */
-       protected static $eraser_key;
-
-       /**
-        * Eraser Friendly Name.
-        *
-        * @since 5.2.0
-        *
-        * @var string $eraser_friendly_name
-        */
-       protected static $eraser_friendly_name;
-
-       /**
-        * Page Index.
-        *
-        * @since 5.2.0
-        *
-        * @var int $page
-        */
-       protected static $page;
-
-       /**
-        * Last response parsed.
-        *
-        * @since 5.2.0
-        *
-        * @var array $_last_response_parsed
-        */
-       protected $_last_response_parsed;
-
-       /**
-        * An array key in the test eraser to unset.
-        *
-        * @since 5.2.0
-        *
-        * @var string $key_to_unset
-        */
-       protected $key_to_unset;
-
-       /**
-        * A value to change the test eraser callback to.
-        *
-        * @since 5.2.0
-        *
-        * @var string $new_callback_value
-        */
-       protected $new_callback_value;
-
-       /**
-        * Create user erase request fixtures.
-        *
-        * @param WP_UnitTest_Factory $factory Factory.
-        */
-       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
-               self::$request_email        = 'requester@example.com';
-               self::$request_id           = wp_create_user_request( self::$request_email, 'remove_personal_data' );
-               self::$action               = 'wp-privacy-erase-personal-data';
-               self::$eraser               = 1;
-               self::$eraser_key           = 'custom-eraser';
-               self::$eraser_friendly_name = 'Custom Eraser';
-               self::$page                 = 1;
-       }
-
-       /**
-        * Register a custom personal data eraser.
-        */
-       public function set_up() {
-               parent::set_up();
-
-               $this->key_to_unset = '';
-
-               // Make sure the erasers response is not modified and avoid sending emails.
-               remove_all_filters( 'wp_privacy_personal_data_erasure_page' );
-               remove_all_actions( 'wp_privacy_personal_data_erased' );
-
-               // Only use our custom privacy personal data eraser.
-               remove_all_filters( 'wp_privacy_personal_data_erasers' );
-               add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_custom_personal_data_eraser' ) );
-
-               $this->_setRole( 'administrator' );
-               // `erase_others_personal_data` meta cap in Multisite installation is only granted to those with `manage_network` capability.
-               if ( is_multisite() ) {
-                       grant_super_admin( get_current_user_id() );
-               }
-       }
-
-       /**
-        * Clean up after each test method.
-        */
-       public function tear_down() {
-               remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_custom_personal_data_eraser' ) );
-               $this->new_callback_value = '';
-
-               if ( is_multisite() ) {
-                       revoke_super_admin( get_current_user_id() );
-               }
-
-               parent::tear_down();
-       }
-
-       /**
-        * Helper method for changing the test eraser's callback function.
-        *
-        * @param string|array $callback New test eraser callback index value.
-        */
-       protected function _set_eraser_callback( $callback ) {
-               $this->new_callback_value = $callback;
-               add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_callback_value' ), 20 );
-       }
-
-       /**
-        * Change the test eraser callback to a specified value.
-        *
-        * @since 5.2.0
-        *
-        * @param array $erasers List of data erasers.
-        *
-        * @return array Array of data erasers.
-        */
-       public function filter_eraser_callback_value( $erasers ) {
-               $erasers[ self::$eraser_key ]['callback'] = $this->new_callback_value;
-
-               return $erasers;
-       }
-
-       /**
-        * Helper method for unsetting an array index in the test eraser.
-        *
-        * @param string|bool $key Test eraser key to unset.
-        */
-       protected function _unset_eraser_key( $key ) {
-               $this->key_to_unset = $key;
-               add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_unset_eraser_index' ), 20 );
-       }
-
-       /**
-        * Unsets an array key in the test eraser.
-        *
-        * If the key is false, the eraser is set to false.
-        *
-        * @since 5.2.0
-        *
-        * @param array $erasers Erasers.
-        *
-        * @return array Erasers.
-        */
-       public function filter_unset_eraser_index( $erasers ) {
-               if ( false === $this->key_to_unset ) {
-                       $erasers[ self::$eraser_key ] = false;
-               } elseif ( ! empty( $this->key_to_unset ) ) {
-                       unset( $erasers[ self::$eraser_key ][ $this->key_to_unset ] );
-               }
-
-               return $erasers;
-       }
-
-       /**
-        * Helper method for erasing a key from the eraser response.
-        *
-        * @since 5.2.0
-        *
-        * @param array $key Response key to unset.
-        */
-       protected function _unset_response_key( $key ) {
-               $this->key_to_unset = $key;
-               $this->_set_eraser_callback( array( $this, 'filter_unset_response_index' ) );
-       }
-
-       /**
-        * Unsets an array index in a response.
-        *
-        * @since 5.2.0
-        *
-        * @param string $email_address The requester's email address.
-        * @param int    $page          Page number.
-        *
-        * @return array Export data.
-        */
-       public function filter_unset_response_index( $email_address, $page = 1 ) {
-               $response = $this->callback_personal_data_eraser( $email_address, $page );
-
-               if ( ! empty( $this->key_to_unset ) ) {
-                       unset( $response[ $this->key_to_unset ] );
-               }
-
-               return $response;
-       }
-
-       /**
-        * The function should send an error when the request ID is missing.
-        *
-        * @since 5.2.0
-        *
-        * @ticket 43438
-        */
-       public function test_error_when_missing_request_id() {
-               $this->assertNotWPError( self::$request_id );
-
-               // Set up a request.
-               $this->_make_ajax_call(
-                       array(
-                               'id' => null, // Missing request ID.
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the request ID is less than 1.
-        *
-        * @since 5.2.0
-        *
-        * @ticket 43438
-        */
-       public function test_error_when_request_id_invalid() {
-               $this->assertNotWPError( self::$request_id );
-
-               // Set up a request.
-               $this->_make_ajax_call(
-                       array(
-                               'id' => -1, // Invalid request ID.
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the current user is missing required capabilities.
-        *
-        * @since 5.2.0
-        *
-        * @ticket 43438
-        */
-       public function test_error_when_current_user_missing_required_capabilities() {
-               $this->_setRole( 'author' );
-
-               $this->assertFalse( current_user_can( 'erase_others_personal_data' ) );
-               $this->assertFalse( current_user_can( 'delete_users' ) );
-
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Sorry, you are not allowed to perform this action.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * Test requests do not succeed on multisite when the current user is not a network admin.
-        *
-        * @ticket 43438
-        * @group multisite
-        * @group ms-required
-        */
-       public function test_error_when_current_user_missing_required_capabilities_multisite() {
-               revoke_super_admin( get_current_user_id() );
-
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Sorry, you are not allowed to perform this action.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the nonce does not validate.
-        *
-        * @since 5.2.0
-        */
-       public function test_failure_with_invalid_nonce() {
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-
-               $this->_make_ajax_call(
-                       array(
-                               'security' => 'invalid-nonce',
-                       )
-               );
-       }
-
-       /**
-        * The function should send an error when the request type is incorrect.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_incorrect_request_type() {
-               $request_id = wp_create_user_request(
-                       'export-request@example.com',
-                       'export_personal_data' // Incorrect request type, expects 'remove_personal_data'.
-               );
-
-               $this->_make_ajax_call(
-                       array(
-                               'security' => wp_create_nonce( 'wp-privacy-erase-personal-data-' . $request_id ),
-                               'id'       => $request_id,
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the request email is invalid.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_invalid_email() {
-               wp_update_post(
-                       array(
-                               'ID'         => self::$request_id,
-                               'post_title' => '', // Invalid requester's email address.
-                       )
-               );
-
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Invalid email address in request.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the eraser index is missing.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_missing_eraser_index() {
-               $this->_make_ajax_call(
-                       array(
-                               'eraser' => null, // Missing eraser index.
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Missing eraser index.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the page index is missing.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_missing_page_index() {
-               $this->_make_ajax_call(
-                       array(
-                               'page' => null, // Missing page index.
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the eraser index is negative.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_negative_eraser_index() {
-               $this->_make_ajax_call(
-                       array(
-                               'eraser' => -1, // Negative eraser index.
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Eraser index cannot be less than one.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the eraser index is out of range.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_eraser_index_out_of_range() {
-               $this->_make_ajax_call(
-                       array(
-                               'eraser' => PHP_INT_MAX, // Out of range eraser index.
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Eraser index is out of range.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the page index is less than one.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_page_index_less_than_one() {
-               $this->_make_ajax_call(
-                       array(
-                               'page' => 0, // Page index less than one.
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when an eraser is not an array.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_eraser_not_array() {
-               $this->_unset_eraser_key( false );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Expected an array describing the eraser at index %s.',
-                               self::$eraser
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * The function should send an error when an eraser is missing a friendly name.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_eraser_missing_friendly_name() {
-               $this->_unset_eraser_key( 'eraser_friendly_name' );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Eraser array at index %s does not include a friendly name.',
-                               self::$eraser
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * The function should send an error when an eraser is missing a callback.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_eraser_missing_callback() {
-               $this->_unset_eraser_key( 'callback' );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Eraser does not include a callback: %s.',
-                               self::$eraser_friendly_name
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * The function should send an error when an eraser, at a given index, has an invalid callback.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_eraser_index_invalid_callback() {
-               $this->_set_eraser_callback( false );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Eraser callback is not valid: %s.',
-                               self::$eraser_friendly_name
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * The function should send an error when an eraser, at a given index, is missing an array response.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_eraser_index_invalid_response() {
-               $this->_set_eraser_callback( '__return_null' );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Did not receive array from %1$s eraser (index %2$d).',
-                               self::$eraser_friendly_name,
-                               self::$eraser
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * The function should send an error when missing an items_removed index.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_eraser_items_removed_missing() {
-               $this->_unset_response_key( 'items_removed' );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Expected items_removed key in response array from %1$s eraser (index %2$d).',
-                               self::$eraser_friendly_name,
-                               self::$eraser
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * The function should send an error when missing an items_retained index.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_eraser_items_retained_missing() {
-               $this->_unset_response_key( 'items_retained' );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Expected items_retained key in response array from %1$s eraser (index %2$d).',
-                               self::$eraser_friendly_name,
-                               self::$eraser
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * The function should send an error when missing a messages index.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_eraser_messages_missing() {
-               $this->_unset_response_key( 'messages' );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Expected messages key in response array from %1$s eraser (index %2$d).',
-                               self::$eraser_friendly_name,
-                               self::$eraser
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * The function should send an error when the messages index is not an array.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_eraser_messages_not_array() {
-               $this->_set_eraser_callback( array( $this, 'filter_response_messages_invalid' ) );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Expected messages key to reference an array in response array from %1$s eraser (index %2$d).',
-                               self::$eraser_friendly_name,
-                               self::$eraser
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * Change the messages index to an invalid value (not an array).
-        *
-        * @since 5.2.0
-        *
-        * @param string $email_address The requester's email address.
-        * @param int    $page          Page number.
-        *
-        * @return array Export data.
-        */
-       public function filter_response_messages_invalid( $email_address, $page = 1 ) {
-               $response             = $this->callback_personal_data_eraser( $email_address, $page );
-               $response['messages'] = true;
-
-               return $response;
-       }
-
-       /**
-        * The function should send an error when an eraser is missing 'done' in array response.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_eraser_missing_done_response() {
-               $this->_unset_response_key( 'done' );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Expected done flag in response array from %1$s eraser (index %2$d).',
-                               self::$eraser_friendly_name,
-                               self::$eraser
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * The function should successfully send erasers response data when the current user has the required
-        * capabilities.
-        *
-        * @since 5.2.0
-        *
-        * @ticket 43438
-        */
-       public function test_success_when_current_user_has_required_capabilities() {
-               $this->assertTrue( current_user_can( 'erase_others_personal_data' ) );
-               $this->assertTrue( current_user_can( 'delete_users' ) );
-
-               $this->_make_ajax_call();
-
-               $this->assertSame(
-                       sprintf( 'A message regarding retained data for %s.', self::$request_email ),
-                       $this->_last_response_parsed['data']['messages'][0]
-               );
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertTrue( $this->_last_response_parsed['data']['items_removed'] );
-               $this->assertTrue( $this->_last_response_parsed['data']['items_retained'] );
-               $this->assertTrue( $this->_last_response_parsed['data']['done'] );
-       }
-
-       /**
-        * The function should successfully send erasers response data when no items to erase.
-        *
-        * @since 5.2.0
-        *
-        * @ticket 43438
-        */
-       public function test_success_when_no_items_to_erase() {
-
-               $this->_make_ajax_call( array( 'page' => 2 ) );
-
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertFalse( $this->_last_response_parsed['data']['items_removed'] );
-               $this->assertFalse( $this->_last_response_parsed['data']['items_retained'] );
-               $this->assertEmpty( $this->_last_response_parsed['data']['messages'] );
-               $this->assertTrue( $this->_last_response_parsed['data']['done'] );
-       }
-
-       /**
-        * Test that the function's output should be filterable with the `wp_privacy_personal_data_erasure_page` filter.
-        *
-        * @since 5.2.0
-        */
-       public function test_output_should_be_filterable() {
-               add_filter( 'wp_privacy_personal_data_erasure_page', array( $this, 'filter_eraser_data_response' ), 20, 6 );
-               $this->_make_ajax_call();
-
-               $expected_new_index = self::$request_email . '-' . self::$request_id . '-' . self::$eraser_key;
-
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'filtered removed', $this->_last_response_parsed['data']['items_removed'] );
-               $this->assertSame( 'filtered retained', $this->_last_response_parsed['data']['items_retained'] );
-               $this->assertSame( array( 'filtered messages' ), $this->_last_response_parsed['data']['messages'] );
-               $this->assertSame( 'filtered done', $this->_last_response_parsed['data']['done'] );
-               $this->assertSame( $expected_new_index, $this->_last_response_parsed['data']['new_index'] );
-       }
-
-       /**
-        * Filters the eraser response.
-        *
-        * @since 5.2.0
-        *
-        * @param array  $response        The personal data for the given eraser and page.
-        * @param int    $eraser_index    The index of the eraser that provided this data.
-        * @param string $email_address   The email address associated with this personal data.
-        * @param int    $page            The page for this response.
-        * @param int    $request_id      The privacy request post ID associated with this request.
-        * @param string $eraser_key      The key (slug) of the eraser that provided this data.
-        *
-        * @return array Filtered erase response.
-        */
-       public function filter_eraser_data_response( $response, $eraser_index, $email_address, $page, $request_id, $eraser_key ) {
-               $response['items_removed']  = 'filtered removed';
-               $response['items_retained'] = 'filtered retained';
-               $response['messages']       = array( 'filtered messages' );
-               $response['done']           = 'filtered done';
-               $response['new_index']      = $email_address . '-' . $request_id . '-' . $eraser_key;
-
-               return $response;
-       }
-
-       /**
-        * Register handler for a custom personal data eraser.
-        *
-        * @since 5.2.0
-        *
-        * @param array $erasers An array of personal data erasers.
-        *
-        * @return array An array of personal data erasers.
-        */
-       public function register_custom_personal_data_eraser( $erasers ) {
-               $erasers[ self::$eraser_key ] = array(
-                       'eraser_friendly_name' => self::$eraser_friendly_name,
-                       'callback'             => array( $this, 'callback_personal_data_eraser' ),
-               );
-               return $erasers;
-       }
-
-       /**
-        * Custom Personal Data Eraser.
-        *
-        * @since 5.2.0
-        *
-        * @param  string $email_address The comment author email address.
-        * @param  int    $page          Page number.
-        *
-        * @return array Erase data.
-        */
-       public function callback_personal_data_eraser( $email_address, $page = 1 ) {
-               if ( 1 === $page ) {
-                       return array(
-                               'items_removed'  => true,
-                               'items_retained' => true,
-                               'messages'       => array( sprintf( 'A message regarding retained data for %s.', $email_address ) ),
-                               'done'           => true,
-                       );
-               }
-
-               return array(
-                       'items_removed'  => false,
-                       'items_retained' => false,
-                       'messages'       => array(),
-                       'done'           => true,
-               );
-       }
-
-       /**
-        * Helper function for Ajax handler.
-        *
-        * @since 5.2.0
-        *
-        * @param array $args Ajax request arguments.
-        */
-       protected function _make_ajax_call( $args = array() ) {
-               $this->_last_response_parsed = null;
-               $this->_last_response        = '';
-
-               $defaults = array(
-                       'action'   => self::$action,
-                       'security' => wp_create_nonce( self::$action . '-' . self::$request_id ),
-                       'page'     => self::$page,
-                       'id'       => self::$request_id,
-                       'eraser'   => self::$eraser,
-               );
-
-               $_POST = wp_parse_args( $args, $defaults );
-
-               try {
-                       $this->_handleAjax( self::$action );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               if ( $this->_last_response ) {
-                       $this->_last_response_parsed = json_decode( $this->_last_response, true );
-               }
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxPrivacyExportPersonalDataphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/PrivacyExportPersonalData.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/PrivacyExportPersonalData.php      2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/PrivacyExportPersonalData.php        2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,842 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Testing Ajax handler for exporting personal data.
- *
- * @package WordPress\UnitTests
- * @since 5.2.0
- */
-
-/**
- * Tests_Ajax_PrivacyExportPersonalData class.
- *
- * @since 5.2.0
- *
- * @group ajax
- * @group privacy
- *
- * @covers ::wp_ajax_wp_privacy_export_personal_data
- */
-class Tests_Ajax_PrivacyExportPersonalData extends WP_Ajax_UnitTestCase {
-
-       /**
-        * User Request ID.
-        *
-        * @since 5.2.0
-        *
-        * @var int $request_id
-        */
-       protected static $request_id;
-
-       /**
-        * User Request Email.
-        *
-        * @since 5.2.0
-        *
-        * @var string $request_email
-        */
-       protected static $request_email;
-
-       /**
-        * Ajax Action.
-        *
-        * @since 5.2.0
-        *
-        * @var string $action
-        */
-       protected static $action;
-
-       /**
-        * Exporter Index.
-        *
-        * @since 5.2.0
-        *
-        * @var int $exporter
-        */
-       protected static $exporter;
-
-       /**
-        * Exporter Key.
-        *
-        * @since 5.2.0
-        *
-        * @var string $exporter_key
-        */
-       protected static $exporter_key;
-
-       /**
-        * Exporter Friendly Name.
-        *
-        * @since 5.2.0
-        *
-        * @var string $exporter_friendly_name
-        */
-       protected static $exporter_friendly_name;
-
-       /**
-        * Page Index.
-        *
-        * @since 5.2.0
-        *
-        * @var int $page
-        */
-       protected static $page;
-
-       /**
-        * Send As Email.
-        *
-        * @since 5.2.0
-        *
-        * @var bool $send_as_email
-        */
-       protected static $send_as_email;
-
-       /**
-        * Last response parsed.
-        *
-        * @since 5.2.0
-        *
-        * @var array $_last_response_parsed
-        */
-       protected $_last_response_parsed;
-
-       /**
-        * An array key in the test exporter to unset.
-        *
-        * @since 5.2.0
-        *
-        * @var string $key_to_unset
-        */
-       protected $key_to_unset;
-
-       /**
-        * A value to change the test exporter callback to.
-        *
-        * @since 5.2.0
-        *
-        * @var string $new_callback_value
-        */
-       protected $new_callback_value;
-
-       /**
-        * Create user export request fixtures.
-        *
-        * @since 5.2.0
-        *
-        * @param WP_UnitTest_Factory $factory Factory.
-        */
-       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
-               self::$request_email          = 'requester@example.com';
-               self::$request_id             = wp_create_user_request( self::$request_email, 'export_personal_data' );
-               self::$action                 = 'wp-privacy-export-personal-data';
-               self::$exporter               = 1;
-               self::$exporter_key           = 'custom-exporter';
-               self::$exporter_friendly_name = 'Custom Exporter';
-               self::$page                   = 1;
-               self::$send_as_email          = false;
-       }
-
-       /**
-        * Setup before each test method.
-        *
-        * @since 5.2.0
-        */
-       public function set_up() {
-               parent::set_up();
-
-               $this->key_to_unset       = '';
-               $this->new_callback_value = '';
-
-               // Make sure the exporter response is not modified and avoid e.g. writing export file to disk.
-               remove_all_filters( 'wp_privacy_personal_data_export_page' );
-
-               // Only use our custom privacy personal data exporter.
-               remove_all_filters( 'wp_privacy_personal_data_exporters' );
-               add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) );
-
-               $this->_setRole( 'administrator' );
-               // `export_others_personal_data` meta cap in Multisite installation is only granted to those with `manage_network` capability.
-               if ( is_multisite() ) {
-                       grant_super_admin( get_current_user_id() );
-               }
-       }
-
-       /**
-        * Clean up after each test method.
-        */
-       public function tear_down() {
-               remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) );
-
-               if ( is_multisite() ) {
-                       revoke_super_admin( get_current_user_id() );
-               }
-               parent::tear_down();
-       }
-
-       /**
-        * Helper method for changing the test exporter's callback function.
-        *
-        * @param string|array $callback New test exporter callback function.
-        */
-       protected function _set_exporter_callback( $callback ) {
-               $this->new_callback_value = $callback;
-               add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_value' ), 20 );
-       }
-
-       /**
-        * Change the test exporter callback to a specified value.
-        *
-        * @since 5.2.0
-        *
-        * @param array $exporters List of data exporters.
-        * @return array List of data exporters.
-        */
-       public function filter_exporter_callback_value( $exporters ) {
-               $exporters[ self::$exporter_key ]['callback'] = $this->new_callback_value;
-
-               return $exporters;
-       }
-
-       /**
-        * Helper method for unsetting an array index in the test exporter.
-        *
-        * @param string $key Test exporter key to unset.
-        */
-       protected function _unset_exporter_key( $key ) {
-               $this->key_to_unset = $key;
-               add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_unset_exporter_key' ), 20 );
-       }
-
-       /**
-        * Unset a specified key in the test exporter array.
-        *
-        * @param array $exporters List of data exporters.
-        *
-        * @return array List of data exporters.
-        */
-       public function filter_unset_exporter_key( $exporters ) {
-               if ( false === $this->key_to_unset ) {
-                       $exporters[ self::$exporter_key ] = false;
-               } elseif ( ! empty( $this->key_to_unset ) ) {
-                       unset( $exporters[ self::$exporter_key ][ $this->key_to_unset ] );
-               }
-
-               return $exporters;
-       }
-
-       /**
-        * The function should send an error when the request ID is missing.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_missing_request_id() {
-               $this->_make_ajax_call(
-                       array(
-                               'id' => null, // Missing request ID.
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the request ID is less than 1.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_invalid_id() {
-               $this->_make_ajax_call(
-                       array(
-                               'id' => -1, // Invalid request ID, less than 1.
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the current user is missing the required capability.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_current_user_missing_required_capability() {
-               $this->_setRole( 'author' );
-
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertFalse( current_user_can( 'export_others_personal_data' ) );
-               $this->assertSame( 'Sorry, you are not allowed to perform this action.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * Test requests do not succeed on multisite when the current user is not a network admin.
-        *
-        * @ticket 43438
-        * @group multisite
-        * @group ms-required
-        */
-       public function test_error_when_current_user_missing_required_capability_multisite() {
-               revoke_super_admin( get_current_user_id() );
-
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Sorry, you are not allowed to perform this action.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the nonce does not validate.
-        *
-        * @since 5.2.0
-        */
-       public function test_failure_with_invalid_nonce() {
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-
-               $this->_make_ajax_call(
-                       array(
-                               'security' => 'invalid-nonce',
-                       )
-               );
-       }
-
-       /**
-        * The function should send an error when the request type is incorrect.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_incorrect_request_type() {
-               $request_id = wp_create_user_request(
-                       'erase-request@example.com',
-                       'remove_personal_data' // Incorrect request type, expects 'export_personal_data'.
-               );
-
-               $this->_make_ajax_call(
-                       array(
-                               'security' => wp_create_nonce( 'wp-privacy-export-personal-data-' . $request_id ),
-                               'id'       => $request_id,
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the requester's email address is invalid.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_invalid_email_address() {
-               wp_update_post(
-                       array(
-                               'ID'         => self::$request_id,
-                               'post_title' => '', // Invalid requester's email address.
-                       )
-               );
-
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'A valid email address must be given.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the exporter index is missing.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_missing_exporter_index() {
-               $this->_make_ajax_call(
-                       array(
-                               'exporter' => null, // Missing exporter index.
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Missing exporter index.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the page index is missing.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_missing_page_index() {
-               $this->_make_ajax_call(
-                       array(
-                               'page' => null, // Missing page index.
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when an exporter has improperly used the `wp_privacy_personal_data_exporters` filter.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_exporter_has_improperly_used_exporters_filter() {
-               // Improper filter usage: returns false instead of an expected array.
-               add_filter( 'wp_privacy_personal_data_exporters', '__return_false', 999 );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'An exporter has improperly used the registration filter.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the exporter index is negative.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_negative_exporter_index() {
-               $this->_make_ajax_call(
-                       array(
-                               'exporter' => -1, // Negative exporter index.
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Exporter index cannot be negative.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the exporter index is out of range.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_exporter_index_out_of_range() {
-               $this->_make_ajax_call(
-                       array(
-                               'exporter' => PHP_INT_MAX, // Out of range exporter index.
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Exporter index is out of range.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when the page index is less than one.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_page_index_less_than_one() {
-               $this->_make_ajax_call(
-                       array(
-                               'page' => 0, // Page index less than one.
-                       )
-               );
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] );
-       }
-
-       /**
-        * The function should send an error when an exporter is not an array.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_exporter_not_array() {
-               $this->_unset_exporter_key( false );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Expected an array describing the exporter at index %s.',
-                               self::$exporter_key
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * The function should send an error when an exporter is missing a friendly name.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_exporter_missing_friendly_name() {
-               $this->_unset_exporter_key( 'exporter_friendly_name' );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Exporter array at index %s does not include a friendly name.',
-                               self::$exporter_key
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * The function should send an error when an exporter is missing a callback.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_exporter_missing_callback() {
-               $this->_unset_exporter_key( 'callback' );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Exporter does not include a callback: %s.',
-                               self::$exporter_friendly_name
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * The function should send an error when an exporter, at a given index, has an invalid callback.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_exporter_index_invalid_callback() {
-               $this->_set_exporter_callback( false );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Exporter callback is not a valid callback: %s.',
-                               self::$exporter_friendly_name
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * When an exporter callback returns a WP_Error, it should be passed as the error.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_exporter_callback_returns_wp_error() {
-               $this->_set_exporter_callback( array( $this, 'callback_return_wp_error' ) );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'passed_message', $this->_last_response_parsed['data'][0]['code'] );
-               $this->assertSame( 'This is a WP_Error message.', $this->_last_response_parsed['data'][0]['message'] );
-       }
-
-       /**
-        * Callback for exporter's response.
-        *
-        * @since 5.2.0
-        *
-        * @param string $email_address The requester's email address.
-        * @param int    $page          Page number.
-        * @return WP_Error WP_Error instance.
-        */
-       public function callback_return_wp_error( $email_address, $page = 1 ) {
-               return new WP_Error( 'passed_message', 'This is a WP_Error message.' );
-       }
-
-       /**
-        * The function should send an error when an exporter, at a given index, is missing an array response.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_exporter_index_invalid_response() {
-               $this->_set_exporter_callback( '__return_null' );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Expected response as an array from exporter: %s.',
-                               self::$exporter_friendly_name
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * The function should send an error when an exporter is missing data in array response.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_exporter_missing_data_response() {
-               $this->_set_exporter_callback( array( $this, 'callback_missing_data_response' ) );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Expected data in response array from exporter: %s.',
-                               self::$exporter_friendly_name
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * Callback for exporter's response.
-        *
-        * @since 5.2.0
-        *
-        * @param string $email_address The requester's email address.
-        * @param int    $page          Page number.
-        *
-        * @return array Export data.
-        */
-       public function callback_missing_data_response( $email_address, $page = 1 ) {
-               $response = $this->callback_custom_personal_data_exporter( $email_address, $page );
-               unset( $response['data'] ); // Missing data part of response.
-
-               return $response;
-       }
-
-       /**
-        * The function should send an error when an exporter is missing 'data' array in array response.
-        *
-        * @since 5.2.0
-        */
-       public function test_function_should_error_when_exporter_missing_data_array_response() {
-               $this->_set_exporter_callback( array( $this, 'callback_missing_data_array_response' ) );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Expected data array in response array from exporter: %s.',
-                               self::$exporter_friendly_name
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * Callback for exporter's response.
-        *
-        * @since 5.2.0
-        *
-        * @param  string $email_address The requester's email address.
-        * @param  int    $page          Page number.
-        *
-        * @return array Export data.
-        */
-       public function callback_missing_data_array_response( $email_address, $page = 1 ) {
-               $response         = $this->callback_custom_personal_data_exporter( $email_address, $page );
-               $response['data'] = false; // Not an array.
-               return $response;
-       }
-
-       /**
-        * The function should send an error when an exporter is missing 'done' in array response.
-        *
-        * @since 5.2.0
-        */
-       public function test_error_when_exporter_missing_done_response() {
-               $this->_set_exporter_callback( array( $this, 'callback_missing_done_response' ) );
-               $this->_make_ajax_call();
-
-               $this->assertFalse( $this->_last_response_parsed['success'] );
-               $this->assertSame(
-                       sprintf(
-                               'Expected done (boolean) in response array from exporter: %s.',
-                               self::$exporter_friendly_name
-                       ),
-                       $this->_last_response_parsed['data']
-               );
-       }
-
-       /**
-        * Remove the response's done flag.
-        *
-        * @since 5.2.0
-        *
-        * @param string $email_address The requester's email address.
-        * @param int    $page          Page number.
-        *
-        * @return array Export data.
-        */
-       public function callback_missing_done_response( $email_address, $page = 1 ) {
-               $response = $this->callback_custom_personal_data_exporter( $email_address, $page );
-               unset( $response['done'] );
-
-               return $response;
-       }
-
-       /**
-        * The function should successfully send exporter data response when the current user has the required capability.
-        *
-        * @since 5.2.0
-        */
-       public function test_succeeds_when_current_user_has_required_capability() {
-               $this->assertTrue( current_user_can( 'export_others_personal_data' ) );
-
-               $this->_make_ajax_call();
-
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertSame( 'custom-exporter-item-id', $this->_last_response_parsed['data']['data']['item_id'] );
-               $this->assertSame( 'Email', $this->_last_response_parsed['data']['data']['data'][0]['name'] );
-               $this->assertSame( self::$request_email, $this->_last_response_parsed['data']['data']['data'][0]['value'] );
-       }
-
-       /**
-        * The function should successfully send exporter data response when no items to export.
-        *
-        * @since 5.2.0
-        */
-       public function test_success_when_no_items_to_export() {
-
-               $this->_make_ajax_call( array( 'page' => 2 ) );
-
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertEmpty( $this->_last_response_parsed['data']['data'] );
-               $this->assertTrue( $this->_last_response_parsed['data']['done'] );
-       }
-
-       /**
-        * The function's output should be filterable with the `wp_privacy_personal_data_export_page` filter.
-        *
-        * @since 5.2.0
-        */
-       public function test_output_should_be_filterable() {
-               add_filter( 'wp_privacy_personal_data_export_page', array( $this, 'filter_exporter_data_response' ), 20, 7 );
-               $this->_make_ajax_call();
-
-               $expected_group_label = sprintf(
-                       '%s-%s-%s-%s-%s-%s',
-                       self::$exporter,
-                       self::$page,
-                       self::$request_email,
-                       self::$request_id,
-                       self::$send_as_email,
-                       self::$exporter_key
-               );
-
-               $this->assertTrue( $this->_last_response_parsed['success'] );
-               $this->assertSame( $expected_group_label, $this->_last_response_parsed['data']['group_label'] );
-               $this->assertSame( 'filtered_group_id', $this->_last_response_parsed['data']['group_id'] );
-               $this->assertSame( 'filtered_item_id', $this->_last_response_parsed['data']['item_id'] );
-               $this->assertSame( 'filtered_name', $this->_last_response_parsed['data']['data'][0]['name'] );
-               $this->assertSame( 'filtered_value', $this->_last_response_parsed['data']['data'][0]['value'] );
-       }
-
-       /**
-        * Filter exporter's data response.
-        *
-        * @since 5.2.0
-        *
-        * @param array  $response        The personal data for the given exporter and page.
-        * @param int    $exporter_index  The index of the exporter that provided this data.
-        * @param string $email_address   The email address associated with this personal data.
-        * @param int    $page            The page for this response.
-        * @param int    $request_id      The privacy request post ID associated with this request.
-        * @param bool   $send_as_email   Whether the final results of the export should be emailed to the user.
-        * @param string $exporter_key    The key (slug) of the exporter that provided this data.
-        *
-        * @return array The personal data for the given exporter and page.
-        */
-       public function filter_exporter_data_response( $response, $exporter_index, $email_address, $page, $request_id, $send_as_email, $exporter_key ) {
-               $group_label                  = sprintf(
-                       '%s-%s-%s-%s-%s-%s',
-                       $exporter_index,
-                       $page,
-                       $email_address,
-                       $request_id,
-                       $send_as_email,
-                       $exporter_key
-               );
-               $response['group_label']      = $group_label;
-               $response['group_id']         = 'filtered_group_id';
-               $response['item_id']          = 'filtered_item_id';
-               $response['data'][0]['name']  = 'filtered_name';
-               $response['data'][0]['value'] = 'filtered_value';
-
-               return $response;
-       }
-
-       /**
-        * Filter to register a custom personal data exporter.
-        *
-        * @since 5.2.0
-        *
-        * @param array $exporters An array of personal data exporters.
-        *
-        * @return array An array of personal data exporters.
-        */
-       public function filter_register_custom_personal_data_exporter( $exporters ) {
-               $exporters[ self::$exporter_key ] = array(
-                       'exporter_friendly_name' => self::$exporter_friendly_name,
-                       'callback'               => array( $this, 'callback_custom_personal_data_exporter' ),
-               );
-               return $exporters;
-       }
-
-       /**
-        * Callback for a custom personal data exporter.
-        *
-        * @since 5.2.0
-        *
-        * @param string $email_address The requester's email address.
-        * @param int    $page          Page number.
-        *
-        * @return array Export data response.
-        */
-       public function callback_custom_personal_data_exporter( $email_address, $page = 1 ) {
-               $data_to_export = array();
-
-               if ( 1 === $page ) {
-                       $data_to_export = array(
-                               'group_id'    => self::$exporter_key . '-group-id',
-                               'group_label' => self::$exporter_key . '-group-label',
-                               'item_id'     => self::$exporter_key . '-item-id',
-                               'data'        => array(
-                                       array(
-                                               'name'  => 'Email',
-                                               'value' => $email_address,
-                                       ),
-                               ),
-                       );
-               }
-
-               return array(
-                       'data' => $data_to_export,
-                       'done' => true,
-               );
-       }
-
-       /**
-        * Helper function for Ajax handler.
-        *
-        * @since 5.2.0
-        *
-        * @param array $args Ajax request arguments.
-        */
-       protected function _make_ajax_call( $args = array() ) {
-               $this->_last_response_parsed = null;
-               $this->_last_response        = '';
-
-               $defaults = array(
-                       'action'      => self::$action,
-                       'security'    => wp_create_nonce( self::$action . '-' . self::$request_id ),
-                       'exporter'    => self::$exporter,
-                       'page'        => self::$page,
-                       'sendAsEmail' => self::$send_as_email,
-                       'id'          => self::$request_id,
-               );
-
-               $_POST = wp_parse_args( $args, $defaults );
-
-               try {
-                       $this->_handleAjax( self::$action );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               if ( $this->_last_response ) {
-                       $this->_last_response_parsed = json_decode( $this->_last_response, true );
-               }
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxQuickEditphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/QuickEdit.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/QuickEdit.php      2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/QuickEdit.php        2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,89 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Quick Edit AJAX functionality.
- *
- * @group ajax
- */
-class Tests_Ajax_QuickEdit extends WP_Ajax_UnitTestCase {
-
-       /**
-        * @ticket 26948
-        *
-        * @covers ::wp_ajax_inline_save
-        * @covers ::edit_post
-        */
-       public function test_dont_process_terms_if_taxonomy_does_not_allow_show_on_quick_edit() {
-               register_taxonomy(
-                       'wptests_tax_1',
-                       'post',
-                       array(
-                               'show_in_quick_edit' => false,
-                               'hierarchical'       => true,
-                       )
-               );
-               register_taxonomy(
-                       'wptests_tax_2',
-                       'post',
-                       array(
-                               'show_in_quick_edit' => true,
-                               'hierarchical'       => true,
-                       )
-               );
-
-               $t1 = self::factory()->term->create(
-                       array(
-                               'taxonomy' => 'wptests_tax_1',
-                       )
-               );
-               $t2 = self::factory()->term->create(
-                       array(
-                               'taxonomy' => 'wptests_tax_2',
-                       )
-               );
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               $post = self::factory()->post->create_and_get(
-                       array(
-                               'post_author' => get_current_user_id(),
-                       )
-               );
-
-               // Set up a request.
-               $_POST['_inline_edit'] = wp_create_nonce( 'inlineeditnonce' );
-               $_POST['post_ID']      = $post->ID;
-               $_POST['post_type']    = $post->post_type;
-               $_POST['content']      = $post->post_content;
-               $_POST['excerpt']      = $post->post_excerpt;
-               $_POST['_status']      = $post->post_status;
-               $_POST['post_status']  = $post->post_status;
-               $_POST['screen']       = 'post';
-               $_POST['post_view']    = 'excerpt';
-               $_POST['tax_input']    = array(
-                       'wptests_tax_1' => array( $t1 ),
-                       'wptests_tax_2' => array( $t2 ),
-               );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'inline-save' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // 'wptests_tax_1' terms should have been refused.
-               $post_terms_1 = wp_get_object_terms( $post->ID, 'wptests_tax_1' );
-               $this->assertEmpty( $post_terms_1 );
-
-               // 'wptests_tax_2' terms should have been added successfully.
-               $post_terms_2 = wp_get_object_terms( $post->ID, 'wptests_tax_2' );
-               $this->assertSameSets( array( $t2 ), wp_list_pluck( $post_terms_2, 'term_id' ) );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxReplytoCommentphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/ReplytoComment.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/ReplytoComment.php 2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/ReplytoComment.php   2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,272 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Ajax comment functionality.
- *
- * @package    WordPress
- * @subpackage UnitTests
- * @since      3.4.0
- * @group      ajax
- *
- * @covers ::wp_ajax_replyto_comment
- */
-class Tests_Ajax_ReplytoComment extends WP_Ajax_UnitTestCase {
-
-       /**
-        * A post with at least one comment.
-        *
-        * @var mixed
-        */
-       protected static $comment_post = null;
-
-       /**
-        * Draft post.
-        *
-        * @var mixed
-        */
-       protected static $draft_post = null;
-
-       protected static $comment_ids = array();
-
-       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
-               self::$comment_post = $factory->post->create_and_get();
-               self::$comment_ids  = $factory->comment->create_post_comments( self::$comment_post->ID, 5 );
-               self::$draft_post   = $factory->post->create_and_get( array( 'post_status' => 'draft' ) );
-       }
-
-       public function tear_down() {
-               remove_filter( 'query', array( $this, '_block_comments' ) );
-               parent::tear_down();
-       }
-
-       /**
-        * Tests reply as a privileged user (administrator).
-        *
-        * Expects test to pass.
-        */
-       public function test_as_admin() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Get a comment.
-               $comments = get_comments(
-                       array(
-                               'post_id' => self::$comment_post->ID,
-                       )
-               );
-               $comment  = array_pop( $comments );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
-               $_POST['comment_ID']                  = $comment->comment_ID;
-               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
-               $_POST['comment_post_ID']             = self::$comment_post->ID;
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'replyto-comment' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
-
-               // Check the meta data.
-               $this->assertSame( '-1', (string) $xml->response[0]->comment['position'] );
-               $this->assertGreaterThan( 0, (int) $xml->response[0]->comment['id'] );
-               $this->assertNotEmpty( (string) $xml->response['action'] );
-
-               // Check the payload.
-               $this->assertNotEmpty( (string) $xml->response[0]->comment[0]->response_data );
-
-               // And supplemental is empty.
-               $this->assertEmpty( (string) $xml->response[0]->comment[0]->supplemental );
-       }
-
-       /**
-        * Tests reply as a non-privileged user (subscriber).
-        *
-        * Expects test to fail.
-        */
-       public function test_as_subscriber() {
-
-               // Become an administrator.
-               $this->_setRole( 'subscriber' );
-
-               // Get a comment.
-               $comments = get_comments(
-                       array(
-                               'post_id' => self::$comment_post->ID,
-                       )
-               );
-               $comment  = array_pop( $comments );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
-               $_POST['comment_ID']                  = $comment->comment_ID;
-               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
-               $_POST['comment_post_ID']             = self::$comment_post->ID;
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'replyto-comment' );
-       }
-
-       /**
-        * Tests reply using a bad nonce.
-        *
-        * Expects test to fail.
-        */
-       public function test_bad_nonce() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Get a comment.
-               $comments = get_comments(
-                       array(
-                               'post_id' => self::$comment_post->ID,
-                       )
-               );
-               $comment  = array_pop( $comments );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( uniqid() );
-               $_POST['comment_ID']                  = $comment->comment_ID;
-               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
-               $_POST['comment_post_ID']             = self::$comment_post->ID;
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'replyto-comment' );
-       }
-
-       /**
-        * Tests reply to an invalid post.
-        *
-        * Expects test to fail.
-        */
-       public function test_invalid_post() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
-               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
-               $_POST['comment_post_ID']             = 123456789;
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'replyto-comment' );
-       }
-
-       /**
-        * Tests reply to a draft post.
-        *
-        * Expects test to fail.
-        */
-       public function test_with_draft_post() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
-               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
-               $_POST['comment_post_ID']             = self::$draft_post->ID;
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( 'You cannot reply to a comment on a draft post.' );
-               $this->_handleAjax( 'replyto-comment' );
-       }
-
-       /**
-        * Tests reply to a post with a simulated database failure.
-        *
-        * Expects test to fail.
-        *
-        * @global $wpdb
-        */
-       public function test_blocked_comment() {
-               global $wpdb;
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
-               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
-               $_POST['comment_post_ID']             = self::$comment_post->ID;
-
-               // Block comments from being saved, simulate a DB error.
-               add_filter( 'query', array( $this, '_block_comments' ) );
-
-               // Make the request.
-               try {
-                       $wpdb->suppress_errors( true );
-                       $this->_handleAjax( 'replyto-comment' );
-                       $wpdb->suppress_errors( false );
-                       $this->fail();
-               } catch ( WPAjaxDieStopException $e ) {
-                       $wpdb->suppress_errors( false );
-                       $this->assertStringContainsString( '1', $e->getMessage() );
-               }
-       }
-
-       /**
-        * Blocks comments from being saved.
-        *
-        * @param string $sql
-        * @return string
-        */
-       public function _block_comments( $sql ) {
-               global $wpdb;
-               if ( false !== strpos( $sql, $wpdb->comments ) && 0 === stripos( trim( $sql ), 'INSERT INTO' ) ) {
-                       return '';
-               }
-               return $sql;
-       }
-
-       /**
-        * Tests blocking a comment from being saved on 'pre_comment_approved'.
-        *
-        * @ticket 39730
-        */
-       public function test_pre_comments_approved() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
-               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
-               $_POST['comment_post_ID']             = self::$comment_post->ID;
-
-               // Simulate filter check error.
-               add_filter( 'pre_comment_approved', array( $this, '_pre_comment_approved_filter' ), 10, 2 );
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( 'pre_comment_approved filter fails for new comment.' );
-               $this->_handleAjax( 'replyto-comment' );
-       }
-
-       /**
-        * Blocks comments from being saved on 'pre_comment_approved', by returning WP_Error.
-        */
-       public function _pre_comment_approved_filter( $approved, $commentdata ) {
-               return new WP_Error( 'comment_wrong', 'pre_comment_approved filter fails for new comment.', 403 );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxResponsephp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/Response.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/Response.php       2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/Response.php 2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,107 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Testing Ajax response class
- *
- * @package    WordPress
- * @subpackage UnitTests
- * @since      3.5.0
- * @group      ajax
- *
- * @covers WP_Ajax_Response::send
- */
-class Tests_Ajax_Response extends WP_UnitTestCase {
-
-       /**
-        * Saved error reporting level
-        *
-        * @var int
-        */
-       protected $_error_level = 0;
-
-       /**
-        * Set up the test fixture.
-        * Override wp_die(), pretend to be ajax, and suppres E_WARNINGs
-        */
-       public function set_up() {
-               parent::set_up();
-
-               add_filter( 'wp_die_ajax_handler', array( $this, 'getDieHandler' ), 1, 1 );
-               add_filter( 'wp_doing_ajax', '__return_true' );
-
-               // Suppress warnings from "Cannot modify header information - headers already sent by".
-               $this->_error_level = error_reporting();
-               error_reporting( $this->_error_level & ~E_WARNING );
-       }
-
-       /**
-        * Tear down the test fixture.
-        * Remove the wp_die() override, restore error reporting
-        */
-       public function tear_down() {
-               remove_filter( 'wp_die_ajax_handler', array( $this, 'getDieHandler' ), 1, 1 );
-               error_reporting( $this->_error_level );
-               parent::tear_down();
-       }
-
-       /**
-        * Return our callback handler
-        *
-        * @return callback
-        */
-       public function getDieHandler() {
-               return array( $this, 'dieHandler' );
-       }
-
-       /**
-        * Handler for wp_die()
-        * Don't die, just continue on.
-        *
-        * @param string $message
-        */
-       public function dieHandler( $message ) {
-       }
-
-       /**
-        * Test that charset in header matches blog_charset
-        * Note:  headers_list doesn't work properly in CLI mode, fall back on
-        * xdebug_get_headers if it's available
-        * Needs a separate process to get around the headers/output from the
-        * bootstrapper
-        *
-        * @ticket 19448
-        * @runInSeparateProcess
-        * @preserveGlobalState disabled
-        * @group xdebug
-        * @requires function xdebug_get_headers
-        */
-       public function test_response_charset_in_header() {
-
-               // Generate an Ajax response.
-               ob_start();
-               $ajax_response = new WP_Ajax_Response();
-               $ajax_response->send();
-
-               // Check the header.
-               $headers = xdebug_get_headers();
-               ob_end_clean();
-
-               $this->assertContains( 'Content-Type: text/xml; charset=' . get_option( 'blog_charset' ), $headers );
-       }
-
-       /**
-        * Test that charset in the xml tag matches blog_charset
-        *
-        * @ticket 19448
-        */
-       public function test_response_charset_in_xml() {
-
-               // Generate an Ajax response.
-               ob_start();
-               $ajax_response = new WP_Ajax_Response();
-               $ajax_response->send();
-
-               // Check the XML tag.
-               $contents = ob_get_clean();
-               $this->assertMatchesRegularExpression( '/<\?xml\s+version=\'1.0\'\s+encoding=\'' . preg_quote( get_option( 'blog_charset' ) ) . '\'\s+standalone=\'yes\'\?>/', $contents );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxTagSearchphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/TagSearch.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/TagSearch.php      2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/TagSearch.php        2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,195 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Ajax tag search functionality.
- *
- * @package    WordPress
- * @subpackage UnitTests
- * @since      3.4.0
- * @group      ajax
- *
- * @covers ::wp_ajax_ajax_tag_search
- */
-class Tests_Ajax_TagSearch extends WP_Ajax_UnitTestCase {
-
-       /**
-        * List of terms to insert on setup
-        *
-        * @var array
-        */
-       private static $terms = array(
-               'chattels',
-               'depo',
-               'energumen',
-               'figuriste',
-               'habergeon',
-               'impropriation',
-       );
-
-       private static $term_ids = array();
-
-       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
-               foreach ( self::$terms as $t ) {
-                       self::$term_ids[] = wp_insert_term( $t, 'post_tag' );
-               }
-       }
-
-       /**
-        * Test as an admin
-        */
-       public function test_post_tag() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_GET['tax'] = 'post_tag';
-               $_GET['q']   = 'chat';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'ajax-tag-search' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Ensure we found the right match.
-               $this->assertSame( $this->_last_response, 'chattels' );
-       }
-
-       /**
-        * Test with no results
-        */
-       public function test_no_results() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_GET['tax'] = 'post_tag';
-               $_GET['q']   = md5( uniqid() );
-
-               // Make the request.
-               // No output, so we get a stop exception.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '' );
-               $this->_handleAjax( 'ajax-tag-search' );
-       }
-
-       /**
-        * Test with commas
-        */
-       public function test_with_comma() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_GET['tax'] = 'post_tag';
-               $_GET['q']   = 'some,nonsense, terms,chat'; // Only the last term in the list is searched.
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'ajax-tag-search' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Ensure we found the right match.
-               $this->assertSame( $this->_last_response, 'chattels' );
-       }
-
-       /**
-        * Test as a logged out user
-        */
-       public function test_logged_out() {
-
-               // Log out.
-               wp_logout();
-
-               // Set up a default request.
-               $_GET['tax'] = 'post_tag';
-               $_GET['q']   = 'chat';
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'ajax-tag-search' );
-       }
-
-       /**
-        * Test with an invalid taxonomy type
-        */
-       public function test_invalid_tax() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_GET['tax'] = 'invalid-taxonomy';
-               $_GET['q']   = 'chat';
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '0' );
-               $this->_handleAjax( 'ajax-tag-search' );
-       }
-
-       /**
-        * Test as an unprivileged user
-        */
-       public function test_unprivileged_user() {
-
-               // Become a subscriber.
-               $this->_setRole( 'subscriber' );
-
-               // Set up a default request.
-               $_GET['tax'] = 'post_tag';
-               $_GET['q']   = 'chat';
-
-               // Make the request.
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'ajax-tag-search' );
-       }
-
-       /**
-        * Test the ajax_term_search_results filter
-        *
-        * @ticket 55606
-        */
-       public function test_ajax_term_search_results_filter() {
-
-               // Become an administrator.
-               $this->_setRole( 'administrator' );
-
-               // Set up a default request.
-               $_GET['tax'] = 'post_tag';
-               $_GET['q']   = 'chat';
-
-               // Add the ajax_term_search_results filter.
-               add_filter(
-                       'ajax_term_search_results',
-                       static function( $results, $tax, $s ) {
-                               return array( 'ajax_term_search_results was applied' );
-                       },
-                       10,
-                       3
-               );
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'ajax-tag-search', $_GET['tax'], $_GET['q'] );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Ensure we found the right match.
-               $this->assertSame( 'ajax_term_search_results was applied', $this->_last_response );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxUpdatePluginphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/phpunit/tests/ajax/UpdatePlugin.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/UpdatePlugin.php   2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/UpdatePlugin.php     2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,173 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Admin Ajax functions to be tested.
- */
-require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
-
-/**
- * Testing Ajax handler for updating a plugin.
- *
- * @group ajax
- *
- * @covers ::wp_ajax_update_plugin
- */
-class Tests_Ajax_Update_Plugin extends WP_Ajax_UnitTestCase {
-
-       public function test_missing_nonce() {
-               $this->expectException( 'WPAjaxDieStopException' );
-               $this->expectExceptionMessage( '-1' );
-               $this->_handleAjax( 'update-plugin' );
-       }
-
-       public function test_missing_plugin() {
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
-               $_POST['slug']        = 'foo';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'update-plugin' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = array(
-                       'success' => false,
-                       'data'    => array(
-                               'slug'         => '',
-                               'errorCode'    => 'no_plugin_specified',
-                               'errorMessage' => 'No plugin specified.',
-                       ),
-               );
-
-               $this->assertSameSets( $expected, $response );
-       }
-
-       public function test_missing_slug() {
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
-               $_POST['plugin']      = 'foo/bar.php';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'update-plugin' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = array(
-                       'success' => false,
-                       'data'    => array(
-                               'slug'         => '',
-                               'errorCode'    => 'no_plugin_specified',
-                               'errorMessage' => 'No plugin specified.',
-                       ),
-               );
-
-               $this->assertSameSets( $expected, $response );
-       }
-
-       public function test_missing_capability() {
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
-               $_POST['plugin']      = 'foo/bar.php';
-               $_POST['slug']        = 'foo';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'update-plugin' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = array(
-                       'success' => false,
-                       'data'    => array(
-                               'update'       => 'plugin',
-                               'slug'         => 'foo',
-                               'oldVersion'   => '',
-                               'newVersion'   => '',
-                               'errorMessage' => 'Sorry, you are not allowed to update plugins for this site.',
-                       ),
-               );
-
-               $this->assertSameSets( $expected, $response );
-       }
-
-       public function test_invalid_file() {
-               $this->_setRole( 'administrator' );
-
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
-               $_POST['plugin']      = '../foo/bar.php';
-               $_POST['slug']        = 'foo';
-
-               // Make the request.
-               try {
-                       $this->_handleAjax( 'update-plugin' );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = array(
-                       'success' => false,
-                       'data'    => array(
-                               'update'       => 'plugin',
-                               'slug'         => 'foo',
-                               'oldVersion'   => '',
-                               'newVersion'   => '',
-                               'errorMessage' => 'Sorry, you are not allowed to update plugins for this site.',
-                       ),
-               );
-
-               $this->assertSameSets( $expected, $response );
-       }
-
-       /**
-        * @group ms-excluded
-        */
-       public function test_update_plugin() {
-               $this->_setRole( 'administrator' );
-
-               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
-               $_POST['plugin']      = 'hello.php';
-               $_POST['slug']        = 'hello-dolly';
-
-               // Make the request.
-               try {
-                       // Prevent wp_update_plugins() from running.
-                       wp_installing( true );
-                       $this->_handleAjax( 'update-plugin' );
-                       wp_installing( false );
-               } catch ( WPAjaxDieContinueException $e ) {
-                       unset( $e );
-               }
-
-               // Get the response.
-               $response = json_decode( $this->_last_response, true );
-
-               $expected = array(
-                       'success' => false,
-                       'data'    => array(
-                               'update'       => 'plugin',
-                               'slug'         => 'hello-dolly',
-                               'oldVersion'   => 'Version 1.7.2',
-                               'newVersion'   => '',
-                               'plugin'       => 'hello.php',
-                               'pluginName'   => 'Hello Dolly',
-                               'debug'        => array( 'The plugin is at the latest version.' ),
-                               'errorMessage' => 'The plugin is at the latest version.',
-                       ),
-               );
-
-               $this->assertSameSets( $expected, $response );
-       }
-}
</del></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxAddMetaphpfromrev54721trunktestsphpunittestsajaxAddMetaphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxAddMeta.php (from rev 54721, trunk/tests/phpunit/tests/ajax/AddMeta.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxAddMeta.php                          (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxAddMeta.php    2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,78 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Add Meta AJAX functionality.
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_add_meta
+ */
+class Tests_Ajax_wpAjaxAddMeta extends WP_Ajax_UnitTestCase {
+
+       /**
+        * @ticket 43559
+        *
+        * @covers ::add_post_meta
+        */
+       public function test_wp_ajax_add_meta_allows_empty_values_on_adding() {
+               $post = self::factory()->post->create();
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               $_POST = array(
+                       'post_id'              => $post,
+                       'metakeyinput'         => 'testkey',
+                       'metavalue'            => '',
+                       '_ajax_nonce-add-meta' => wp_create_nonce( 'add-meta' ),
+               );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'add-meta' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               $this->assertSame( '', get_post_meta( $post, 'testkey', true ) );
+       }
+
+       /**
+        * @ticket 43559
+        *
+        * @covers ::update_metadata_by_mid
+        */
+       public function test_wp_ajax_add_meta_allows_empty_values_on_updating() {
+               $post = self::factory()->post->create();
+
+               $meta_id = add_post_meta( $post, 'testkey', 'hello' );
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               $_POST = array(
+                       '_ajax_nonce-add-meta' => wp_create_nonce( 'add-meta' ),
+                       'post_id'              => $post,
+                       'meta'                 => array(
+                               $meta_id => array(
+                                       'key'   => 'testkey',
+                                       'value' => '',
+                               ),
+                       ),
+               );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'add-meta' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               $this->assertSame( '', get_post_meta( $post, 'testkey', true ) );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxAddTagphpfromrev54721trunktestsphpunittestsajaxAddTagphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxAddTag.php (from rev 54721, trunk/tests/phpunit/tests/ajax/AddTag.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxAddTag.php                           (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxAddTag.php     2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,154 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Admin ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Class for testing ajax add tag functionality.
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_add_tag
+ */
+class Tests_Ajax_wpAjaxAddTag extends WP_Ajax_UnitTestCase {
+
+       /**
+        * @dataProvider data_add_tag
+        *
+        * @ticket 42937
+        *
+        * @covers ::wp_insert_term
+        *
+        * @param array                 $post_data Data to populate $_POST.
+        * @param string                $expected  Expected response.
+        * @param array|string|callable $callback  Optional. Callback to register to 'term_updated_messages'
+        *                                         filter. Default empty string (no callback).
+        */
+       public function test_add_tag( array $post_data, $expected, $callback = '' ) {
+               $this->_setRole( 'administrator' );
+
+               $_POST                     = $post_data;
+               $_POST['_wpnonce_add-tag'] = wp_create_nonce( 'add-tag' );
+
+               if ( ! empty( $callback ) ) {
+                       add_filter( 'term_updated_messages', $callback );
+               }
+
+               try {
+                       $this->_handleAjax( 'add-tag' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // The response message is in the `data` property in WP 5.9.
+               $this->assertSame( $expected, (string) $this->get_xml_response_taxonomy()->response_data );
+               // The response message is in the `supplemental->notice` property in WP 6.0+.
+               $this->assertSame( $expected, (string) $this->get_xml_response_taxonomy()->supplemental->notice );
+       }
+
+       /**
+        * Data provider.
+        *
+        * @return array
+        */
+       public function data_add_tag() {
+               return array(
+                       'add a category'                        => array(
+                               'post_data' => array(
+                                       'taxonomy'  => 'category',
+                                       'post_type' => 'post',
+                                       'screen'    => 'edit-category',
+                                       'action'    => 'add-tag',
+                                       'tag-name'  => 'blues',
+                               ),
+                               'expected'  => 'Category added.',
+                       ),
+                       'add a category with message filtering' => array(
+                               'post_data' => array(
+                                       'taxonomy'  => 'category',
+                                       'post_type' => 'post',
+                                       'screen'    => 'edit-category',
+                                       'action'    => 'add-tag',
+                                       'tag-name'  => 'techno',
+                               ),
+                               'expected'  => 'A new category added.',
+                               'callback'  => static function( array $messages ) {
+                                       $messages['category'][1] = 'A new category added.';
+                                       return $messages;
+                               },
+                       ),
+                       'add a post_tag'                        => array(
+                               'post_data' => array(
+                                       'taxonomy'  => 'post_tag',
+                                       'post_type' => 'post',
+                                       'screen'    => 'edit-post_tag',
+                                       'action'    => 'add-tag',
+                                       'tag-name'  => 'Louis Armstrong',
+                               ),
+                               'expected'  => 'Tag added.',
+                       ),
+               );
+       }
+
+       /**
+        * @ticket 42937
+        */
+       public function test_adding_category_without_capability_should_error() {
+               $this->_setRole( 'subscriber' );
+
+               $_POST['taxonomy']         = 'category';
+               $_POST['post_type']        = 'post';
+               $_POST['screen']           = 'edit-category';
+               $_POST['action']           = 'add-tag';
+               $_POST['tag - name']       = 'disco';
+               $_POST['_wpnonce_add-tag'] = wp_create_nonce( 'add-tag' );
+
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'add-tag' );
+       }
+
+       /**
+        * @ticket 42937
+        *
+        * @covers ::wp_insert_term
+        */
+       public function test_adding_existing_category_should_error() {
+               $this->_setRole( 'administrator' );
+
+               wp_insert_term( 'testcat', 'category' );
+
+               $_POST = array(
+                       'taxonomy'         => 'category',
+                       'post_type'        => 'post',
+                       'screen'           => 'edit-category',
+                       'action'           => 'add-tag',
+                       'tag-name'         => 'testcat',
+                       '_wpnonce_add-tag' => wp_create_nonce( 'add-tag' ),
+               );
+
+               try {
+                       $this->_handleAjax( 'add-tag' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               $expected = 'A term with the name provided already exists with this parent.';
+               $this->assertSame( $expected, (string) $this->get_xml_response_taxonomy()->wp_error );
+       }
+
+       /**
+        * Helper method to get the taxonomy's response or error.
+        *
+        * @since 5.9.0
+        *
+        * @return SimpleXMLElement Response or error object.
+        */
+       private function get_xml_response_taxonomy() {
+               $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
+
+               return $xml->response->taxonomy;
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxAjaxTagSearchphpfromrev54721trunktestsphpunittestsajaxTagSearchphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxAjaxTagSearch.php (from rev 54721, trunk/tests/phpunit/tests/ajax/TagSearch.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxAjaxTagSearch.php                            (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxAjaxTagSearch.php      2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,196 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Ajax tag search functionality.
+ *
+ * @package WordPress
+ * @subpackage UnitTests
+ * @since 3.4.0
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_ajax_tag_search
+ */
+class Tests_Ajax_wpAjaxAjaxTagSearch extends WP_Ajax_UnitTestCase {
+
+       /**
+        * List of terms to insert on setup
+        *
+        * @var array
+        */
+       private static $terms = array(
+               'chattels',
+               'depo',
+               'energumen',
+               'figuriste',
+               'habergeon',
+               'impropriation',
+       );
+
+       private static $term_ids = array();
+
+       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+               foreach ( self::$terms as $t ) {
+                       self::$term_ids[] = wp_insert_term( $t, 'post_tag' );
+               }
+       }
+
+       /**
+        * Test as an admin
+        */
+       public function test_post_tag() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_GET['tax'] = 'post_tag';
+               $_GET['q']   = 'chat';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'ajax-tag-search' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Ensure we found the right match.
+               $this->assertSame( $this->_last_response, 'chattels' );
+       }
+
+       /**
+        * Test with no results
+        */
+       public function test_no_results() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_GET['tax'] = 'post_tag';
+               $_GET['q']   = md5( uniqid() );
+
+               // Make the request.
+               // No output, so we get a stop exception.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '' );
+               $this->_handleAjax( 'ajax-tag-search' );
+       }
+
+       /**
+        * Test with commas
+        */
+       public function test_with_comma() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_GET['tax'] = 'post_tag';
+               $_GET['q']   = 'some,nonsense, terms,chat'; // Only the last term in the list is searched.
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'ajax-tag-search' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Ensure we found the right match.
+               $this->assertSame( $this->_last_response, 'chattels' );
+       }
+
+       /**
+        * Test as a logged out user
+        */
+       public function test_logged_out() {
+
+               // Log out.
+               wp_logout();
+
+               // Set up a default request.
+               $_GET['tax'] = 'post_tag';
+               $_GET['q']   = 'chat';
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'ajax-tag-search' );
+       }
+
+       /**
+        * Test with an invalid taxonomy type
+        */
+       public function test_invalid_tax() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_GET['tax'] = 'invalid-taxonomy';
+               $_GET['q']   = 'chat';
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '0' );
+               $this->_handleAjax( 'ajax-tag-search' );
+       }
+
+       /**
+        * Test as an unprivileged user
+        */
+       public function test_unprivileged_user() {
+
+               // Become a subscriber.
+               $this->_setRole( 'subscriber' );
+
+               // Set up a default request.
+               $_GET['tax'] = 'post_tag';
+               $_GET['q']   = 'chat';
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'ajax-tag-search' );
+       }
+
+       /**
+        * Test the ajax_term_search_results filter
+        *
+        * @ticket 55606
+        */
+       public function test_ajax_term_search_results_filter() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_GET['tax'] = 'post_tag';
+               $_GET['q']   = 'chat';
+
+               // Add the ajax_term_search_results filter.
+               add_filter(
+                       'ajax_term_search_results',
+                       static function( $results, $tax, $s ) {
+                               return array( 'ajax_term_search_results was applied' );
+                       },
+                       10,
+                       3
+               );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'ajax-tag-search', $_GET['tax'], $_GET['q'] );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Ensure we found the right match.
+               $this->assertSame( 'ajax_term_search_results was applied', $this->_last_response );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxCropImagephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/ajax/wpAjaxCropImage.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxCropImage.php        2022-10-29 17:10:29 UTC (rev 54721)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxCropImage.php  2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -11,6 +11,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * Class for testing ajax crop image functionality.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @group ajax
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ *
</ins><span class="cx" style="display: block; padding: 0 10px">  * @covers ::wp_ajax_crop_image
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> class Tests_Ajax_WpAjaxCropImage extends WP_Ajax_UnitTestCase {
</span></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxDeleteCommentphpfromrev54721trunktestsphpunittestsajaxDeleteCommentphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxDeleteComment.php (from rev 54721, trunk/tests/phpunit/tests/ajax/DeleteComment.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxDeleteComment.php                            (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxDeleteComment.php      2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,352 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Ajax comment functionality.
+ *
+ * @package WordPress
+ * @subpackage UnitTests
+ * @since 3.4.0
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_delete_comment
+ */
+class Tests_Ajax_wpAjaxDeleteComment extends WP_Ajax_UnitTestCase {
+
+       /**
+        * List of comments.
+        *
+        * @var array
+        */
+       protected static $comments = array();
+
+       /**
+        * ID of a post.
+        *
+        * @var int
+        */
+       protected static $post_id;
+
+       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+               self::$post_id = $factory->post->create();
+
+               $comment_ids    = $factory->comment->create_post_comments( self::$post_id, 8 );
+               self::$comments = array_map( 'get_comment', $comment_ids );
+       }
+
+       /**
+        * Clears the POST actions in between requests.
+        */
+       protected function _clear_post_action() {
+               unset( $_POST['trash'] );
+               unset( $_POST['untrash'] );
+               unset( $_POST['spam'] );
+               unset( $_POST['unspam'] );
+               unset( $_POST['delete'] );
+               $this->_last_response = '';
+       }
+
+       /*
+        * Test prototype
+        */
+
+       /**
+        * Tests as a privileged user (administrator).
+        *
+        * Expects test to pass.
+        *
+        * @covers ::_wp_ajax_delete_comment_response
+        *
+        * @param WP_Comment $comment Comment object.
+        * @param string     $action  Action: 'trash', 'untrash', etc.
+        */
+       public function _test_as_admin( $comment, $action ) {
+
+               // Reset request.
+               $this->_clear_post_action();
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_POST['id']          = $comment->comment_ID;
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'delete-comment_' . $comment->comment_ID );
+               $_POST[ $action ]     = 1;
+               $_POST['_total']      = count( self::$comments );
+               $_POST['_per_page']   = 100;
+               $_POST['_page']       = 1;
+               $_POST['_url']        = admin_url( 'edit-comments.php' );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'delete-comment' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
+
+               // Ensure everything is correct.
+               $this->assertSame( $comment->comment_ID, (string) $xml->response[0]->comment['id'] );
+               $this->assertSame( 'delete-comment_' . $comment->comment_ID, (string) $xml->response['action'] );
+               $this->assertGreaterThanOrEqual( time() - 10, (int) $xml->response[0]->comment[0]->supplemental[0]->time[0] );
+               $this->assertLessThanOrEqual( time(), (int) $xml->response[0]->comment[0]->supplemental[0]->time[0] );
+
+               // 'trash', 'spam', 'delete' should make the total go down.
+               if ( in_array( $action, array( 'trash', 'spam', 'delete' ), true ) ) {
+                       $total = $_POST['_total'] - 1;
+
+                       // 'unspam', 'untrash' should make the total go up.
+               } elseif ( in_array( $action, array( 'untrash', 'unspam' ), true ) ) {
+                       $total = $_POST['_total'] + 1;
+               }
+
+               // The total is calculated based on a page break -OR- a random number. Let's look for both possible outcomes.
+               $comment_count = wp_count_comments( 0 );
+               $recalc_total  = $comment_count->total_comments;
+
+               // Check for either possible total.
+               $message = sprintf( 'returned value: %1$d $total: %2$d  $recalc_total: %3$d', (int) $xml->response[0]->comment[0]->supplemental[0]->total[0], $total, $recalc_total );
+               $this->assertContains( (int) $xml->response[0]->comment[0]->supplemental[0]->total[0], array( $total, $recalc_total ), $message );
+       }
+
+       /**
+        * Tests as a non-privileged user (subscriber).
+        *
+        * Expects test to fail.
+        *
+        * @param WP_Comment $comment Comment object.
+        * @param string     $action  Action: 'trash', 'untrash', etc.
+        */
+       public function _test_as_subscriber( $comment, $action ) {
+
+               // Reset request.
+               $this->_clear_post_action();
+
+               // Become a subscriber.
+               $this->_setRole( 'subscriber' );
+
+               // Set up the $_POST request.
+               $_POST['id']          = $comment->comment_ID;
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'delete-comment_' . $comment->comment_ID );
+               $_POST[ $action ]     = 1;
+               $_POST['_total']      = count( self::$comments );
+               $_POST['_per_page']   = 100;
+               $_POST['_page']       = 1;
+               $_POST['_url']        = admin_url( 'edit-comments.php' );
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'delete-comment' );
+       }
+
+
+       /**
+        * Tests with a bad nonce.
+        *
+        * Expects test to fail.
+        *
+        * @param WP_Comment $comment Comment object.
+        * @param string     $action  Action: 'trash', 'untrash', etc.
+        */
+       public function _test_with_bad_nonce( $comment, $action ) {
+
+               // Reset request.
+               $this->_clear_post_action();
+
+               // Become a subscriber.
+               $this->_setRole( 'administrator' );
+
+               // Set up the $_POST request.
+               $_POST['id']          = $comment->comment_ID;
+               $_POST['_ajax_nonce'] = wp_create_nonce( uniqid() );
+               $_POST[ $action ]     = 1;
+               $_POST['_total']      = count( self::$comments );
+               $_POST['_per_page']   = 100;
+               $_POST['_page']       = 1;
+               $_POST['_url']        = admin_url( 'edit-comments.php' );
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'delete-comment' );
+       }
+
+       /**
+        * Tests with a bad ID.
+        *
+        * Expects test to fail.
+        *
+        * @param WP_Comment $comment Comment object.
+        * @param string     $action  Action: 'trash', 'untrash', etc.
+        */
+       public function _test_with_bad_id( $comment, $action ) {
+
+               // Reset request.
+               $this->_clear_post_action();
+
+               // Become a subscriber.
+               $this->_setRole( 'administrator' );
+
+               // Set up the $_POST request.
+               $_POST['id']          = 12346789;
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'delete-comment_12346789' );
+               $_POST[ $action ]     = 1;
+               $_POST['_total']      = count( self::$comments );
+               $_POST['_per_page']   = 100;
+               $_POST['_page']       = 1;
+               $_POST['_url']        = admin_url( 'edit-comments.php' );
+
+               // Make the request, look for a timestamp in the exception.
+               try {
+                       $this->_handleAjax( 'delete-comment' );
+                       $this->fail( 'Expected exception: WPAjaxDieStopException' );
+               } catch ( WPAjaxDieStopException $e ) {
+                       $this->assertSame( 10, strlen( $e->getMessage() ) );
+                       $this->assertIsNumeric( $e->getMessage() );
+               } catch ( Exception $e ) {
+                       $this->fail( 'Unexpected exception type: ' . get_class( $e ) );
+               }
+       }
+
+       /**
+        * Tests doubling the action (e.g. trash a trashed comment).
+        *
+        * Expects test to fail.
+        *
+        * @param WP_Comment $comment Comment object.
+        * @param string     $action  Action: 'trash', 'untrash', etc.
+        */
+       public function _test_double_action( $comment, $action ) {
+
+               // Reset request.
+               $this->_clear_post_action();
+
+               // Become a subscriber.
+               $this->_setRole( 'administrator' );
+
+               // Set up the $_POST request.
+               $_POST['id']          = $comment->comment_ID;
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'delete-comment_' . $comment->comment_ID );
+               $_POST[ $action ]     = 1;
+               $_POST['_total']      = count( self::$comments );
+               $_POST['_per_page']   = 100;
+               $_POST['_page']       = 1;
+               $_POST['_url']        = admin_url( 'edit-comments.php' );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'delete-comment' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+               $this->_last_response = '';
+
+               // Force delete the comment.
+               if ( 'delete' === $action ) {
+                       wp_delete_comment( $comment->comment_ID, true );
+               }
+
+               // Make the request again, look for a timestamp in the exception.
+               try {
+                       $this->_handleAjax( 'delete-comment' );
+                       $this->fail( 'Expected exception: WPAjaxDieStopException' );
+               } catch ( WPAjaxDieStopException $e ) {
+                       $this->assertSame( 10, strlen( $e->getMessage() ) );
+                       $this->assertIsNumeric( $e->getMessage() );
+               } catch ( Exception $e ) {
+                       $this->fail( 'Unexpected exception type: ' . get_class( $e ) );
+               }
+       }
+
+       /**
+        * Deletes a comment as an administrator (expects success).
+        *
+        * @covers ::_wp_ajax_delete_comment_response
+        */
+       public function test_ajax_comment_trash_actions_as_administrator() {
+               // Test trash/untrash.
+               $this->_test_as_admin( self::$comments[0], 'trash' );
+               $this->_test_as_admin( self::$comments[0], 'untrash' );
+
+               // Test spam/unspam.
+               $this->_test_as_admin( self::$comments[1], 'spam' );
+               $this->_test_as_admin( self::$comments[1], 'unspam' );
+
+               // Test delete.
+               $this->_test_as_admin( self::$comments[2], 'delete' );
+       }
+
+       /**
+        * Deletes a comment as a subscriber (expects permission denied).
+        */
+       public function test_ajax_comment_trash_actions_as_subscriber() {
+               // Test trash/untrash.
+               $this->_test_as_subscriber( self::$comments[0], 'trash' );
+               $this->_test_as_subscriber( self::$comments[0], 'untrash' );
+
+               // Test spam/unspam.
+               $this->_test_as_subscriber( self::$comments[1], 'spam' );
+               $this->_test_as_subscriber( self::$comments[1], 'unspam' );
+
+               // Test delete.
+               $this->_test_as_subscriber( self::$comments[2], 'delete' );
+       }
+
+       /**
+        * Deletes a comment with no ID.
+        *
+        * @covers ::_wp_ajax_delete_comment_response
+        */
+       public function test_ajax_trash_comment_no_id() {
+               // Test trash/untrash.
+               $this->_test_as_admin( self::$comments[0], 'trash' );
+               $this->_test_as_admin( self::$comments[0], 'untrash' );
+
+               // Test spam/unspam.
+               $this->_test_as_admin( self::$comments[1], 'spam' );
+               $this->_test_as_admin( self::$comments[1], 'unspam' );
+
+               // Test delete.
+               $this->_test_as_admin( self::$comments[2], 'delete' );
+       }
+
+       /**
+        * Deletes a comment with a bad nonce.
+        */
+       public function test_ajax_trash_comment_bad_nonce() {
+               // Test trash/untrash.
+               $this->_test_with_bad_nonce( self::$comments[0], 'trash' );
+               $this->_test_with_bad_nonce( self::$comments[0], 'untrash' );
+
+               // Test spam/unspam.
+               $this->_test_with_bad_nonce( self::$comments[1], 'spam' );
+               $this->_test_with_bad_nonce( self::$comments[1], 'unspam' );
+
+               // Test delete.
+               $this->_test_with_bad_nonce( self::$comments[2], 'delete' );
+       }
+
+       /**
+        * Tests trashing an already trashed comment, etc.
+        */
+       public function test_ajax_trash_double_action() {
+               // Test trash/untrash.
+               $this->_test_double_action( self::$comments[0], 'trash' );
+               $this->_test_double_action( self::$comments[0], 'untrash' );
+
+               // Test spam/unspam.
+               $this->_test_double_action( self::$comments[1], 'spam' );
+               $this->_test_double_action( self::$comments[1], 'unspam' );
+
+               // Test delete.
+               $this->_test_double_action( self::$comments[2], 'delete' );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxDeletePluginphpfromrev54721trunktestsphpunittestsajaxDeletePluginphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxDeletePlugin.php (from rev 54721, trunk/tests/phpunit/tests/ajax/DeletePlugin.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxDeletePlugin.php                             (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxDeletePlugin.php       2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,164 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Ajax handler for deleting a plugin.
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_delete_plugin
+ */
+class Tests_Ajax_wpAjaxDeletePlugin extends WP_Ajax_UnitTestCase {
+
+       public function test_missing_nonce() {
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'delete-plugin' );
+       }
+
+       public function test_missing_plugin() {
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
+               $_POST['slug']        = 'foo';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'delete-plugin' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = array(
+                       'success' => false,
+                       'data'    => array(
+                               'slug'         => '',
+                               'errorCode'    => 'no_plugin_specified',
+                               'errorMessage' => 'No plugin specified.',
+                       ),
+               );
+
+               $this->assertSameSets( $expected, $response );
+       }
+
+       public function test_missing_slug() {
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
+               $_POST['plugin']      = 'foo/bar.php';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'delete-plugin' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = array(
+                       'success' => false,
+                       'data'    => array(
+                               'slug'         => '',
+                               'errorCode'    => 'no_plugin_specified',
+                               'errorMessage' => 'No plugin specified.',
+                       ),
+               );
+
+               $this->assertSameSets( $expected, $response );
+       }
+
+       public function test_missing_capability() {
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
+               $_POST['plugin']      = 'foo/bar.php';
+               $_POST['slug']        = 'foo';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'delete-plugin' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = array(
+                       'success' => false,
+                       'data'    => array(
+                               'delete'       => 'plugin',
+                               'slug'         => 'foo',
+                               'errorMessage' => 'Sorry, you are not allowed to delete plugins for this site.',
+                       ),
+               );
+
+               $this->assertSameSets( $expected, $response );
+       }
+
+       public function test_invalid_file() {
+               $this->_setRole( 'administrator' );
+
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
+               $_POST['plugin']      = '../foo/bar.php';
+               $_POST['slug']        = 'foo';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'delete-plugin' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = array(
+                       'success' => false,
+                       'data'    => array(
+                               'delete'       => 'plugin',
+                               'slug'         => 'foo',
+                               'errorMessage' => 'Sorry, you are not allowed to delete plugins for this site.',
+                       ),
+               );
+
+               $this->assertSameSets( $expected, $response );
+       }
+
+       /**
+        * @group ms-excluded
+        *
+        * @covers ::delete_plugins
+        */
+       public function test_delete_plugin() {
+               $this->_setRole( 'administrator' );
+
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
+               $_POST['plugin']      = 'foo.php';
+               $_POST['slug']        = 'foo';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'delete-plugin' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = array(
+                       'success' => true,
+                       'data'    => array(
+                               'delete'     => 'plugin',
+                               'slug'       => 'foo',
+                               'plugin'     => 'foo.php',
+                               'pluginName' => '',
+                       ),
+               );
+
+               $this->assertSameSets( $expected, $response );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxDimCommentphpfromrev54721trunktestsphpunittestsajaxDimCommentphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxDimComment.php (from rev 54721, trunk/tests/phpunit/tests/ajax/DimComment.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxDimComment.php                               (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxDimComment.php 2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,242 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Ajax comment functionality
+ *
+ * @package WordPress
+ * @subpackage UnitTests
+ * @since 3.4.0
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_dim_comment
+ */
+class Tests_Ajax_wpAjaxDimComment extends WP_Ajax_UnitTestCase {
+
+       /**
+        * List of comments.
+        *
+        * @var array
+        */
+       protected $_comments = array();
+
+       /**
+        * Sets up the test fixture.
+        */
+       public function set_up() {
+               parent::set_up();
+               $post_id         = self::factory()->post->create();
+               $this->_comments = self::factory()->comment->create_post_comments( $post_id, 15 );
+               $this->_comments = array_map( 'get_comment', $this->_comments );
+       }
+
+       /**
+        * Clears the POST actions in between requests.
+        */
+       protected function _clear_post_action() {
+               unset( $_POST['id'] );
+               unset( $_POST['new'] );
+               $this->_last_response = '';
+       }
+
+       /*
+        * Test prototype
+        */
+
+       /**
+        * Tests as a privileged user (administrator).
+        *
+        * Expects test to pass.
+        *
+        * @param WP_Comment $comment Comment object.
+        */
+       public function _test_as_admin( $comment ) {
+
+               // Reset request.
+               $this->_clear_post_action();
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_POST['id']          = $comment->comment_ID;
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'approve-comment_' . $comment->comment_ID );
+               $_POST['_total']      = count( $this->_comments );
+               $_POST['_per_page']   = 100;
+               $_POST['_page']       = 1;
+               $_POST['_url']        = admin_url( 'edit-comments.php' );
+
+               // Save the comment status.
+               $prev_status = wp_get_comment_status( $comment->comment_ID );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'dim-comment' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
+
+               // Ensure everything is correct.
+               $this->assertSame( $comment->comment_ID, (string) $xml->response[0]->comment['id'] );
+               $this->assertSame( 'dim-comment_' . $comment->comment_ID, (string) $xml->response['action'] );
+               $this->assertGreaterThanOrEqual( time() - 10, (int) $xml->response[0]->comment[0]->supplemental[0]->time[0] );
+               $this->assertLessThanOrEqual( time(), (int) $xml->response[0]->comment[0]->supplemental[0]->time[0] );
+
+               // Check the status.
+               $current = wp_get_comment_status( $comment->comment_ID );
+               if ( in_array( $prev_status, array( 'unapproved', 'spam' ), true ) ) {
+                       $this->assertSame( 'approved', $current );
+               } else {
+                       $this->assertSame( 'unapproved', $current );
+               }
+
+               // The total is calculated based on a page break -OR- a random number. Let's look for both possible outcomes.
+               $comment_count = wp_count_comments( 0 );
+               $recalc_total  = $comment_count->total_comments;
+
+               // Delta is not specified, it will always be 1 lower than the request.
+               $total = $_POST['_total'] - 1;
+
+               // Check for either possible total.
+               $this->assertContains( (int) $xml->response[0]->comment[0]->supplemental[0]->total[0], array( $total, $recalc_total ) );
+       }
+
+       /**
+        * Tests as a non-privileged user (subscriber).
+        *
+        * Expects test to fail.
+        *
+        * @param WP_Comment $comment Comment object.
+        */
+       public function _test_as_subscriber( $comment ) {
+
+               // Reset request.
+               $this->_clear_post_action();
+
+               // Become a subscriber.
+               $this->_setRole( 'subscriber' );
+
+               // Set up the $_POST request.
+               $_POST['id']          = $comment->comment_ID;
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'approve-comment_' . $comment->comment_ID );
+               $_POST['_total']      = count( $this->_comments );
+               $_POST['_per_page']   = 100;
+               $_POST['_page']       = 1;
+               $_POST['_url']        = admin_url( 'edit-comments.php' );
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'dim-comment' );
+       }
+
+       /**
+        * Tests with a bad nonce.
+        *
+        * Expects test to fail.
+        *
+        * @param WP_Comment $comment Comment object.
+        */
+       public function _test_with_bad_nonce( $comment ) {
+
+               // Reset request.
+               $this->_clear_post_action();
+
+               // Become a subscriber.
+               $this->_setRole( 'administrator' );
+
+               // Set up the $_POST request.
+               $_POST['id']          = $comment->comment_ID;
+               $_POST['_ajax_nonce'] = wp_create_nonce( uniqid() );
+               $_POST['_total']      = count( $this->_comments );
+               $_POST['_per_page']   = 100;
+               $_POST['_page']       = 1;
+               $_POST['_url']        = admin_url( 'edit-comments.php' );
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'dim-comment' );
+       }
+
+       /**
+        * Tests with a bad ID.
+        *
+        * Expects test to fail.
+        */
+       public function test_with_bad_id() {
+
+               // Reset request.
+               $this->_clear_post_action();
+
+               // Become a subscriber.
+               $this->_setRole( 'administrator' );
+
+               // Set up the $_POST request.
+               $_POST['id']          = 12346789;
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'dim-comment_12346789' );
+               $_POST['_total']      = count( $this->_comments );
+               $_POST['_per_page']   = 100;
+               $_POST['_page']       = 1;
+               $_POST['_url']        = admin_url( 'edit-comments.php' );
+
+               // Make the request, look for a timestamp in the exception.
+               try {
+                       $this->_handleAjax( 'dim-comment' );
+                       $this->fail( 'Expected exception: WPAjaxDieContinueException' );
+               } catch ( WPAjaxDieContinueException $e ) {
+
+                       // Get the response.
+                       $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
+
+                       // Ensure everything is correct.
+                       $this->assertSame( '0', (string) $xml->response[0]->comment['id'] );
+                       $this->assertSame( 'dim-comment_0', (string) $xml->response['action'] );
+                       $this->assertStringContainsString( 'Comment ' . $_POST['id'] . ' does not exist', $this->_last_response );
+
+               } catch ( Exception $e ) {
+                       $this->fail( 'Unexpected exception type: ' . get_class( $e ) );
+               }
+       }
+
+       /**
+        * Dims a comment as an administrator (expects success).
+        */
+       public function test_ajax_comment_dim_actions_as_administrator() {
+               $comment = array_pop( $this->_comments );
+               $this->_test_as_admin( $comment );
+               $this->_test_as_admin( $comment );
+       }
+
+       /**
+        * Dims a comment as a subscriber (expects permission denied).
+        */
+       public function test_ajax_comment_dim_actions_as_subscriber() {
+               $comment = array_pop( $this->_comments );
+               $this->_test_as_subscriber( $comment );
+       }
+
+       /**
+        * Dims a comment with no ID.
+        */
+       public function test_ajax_dim_comment_no_id() {
+               $comment = array_pop( $this->_comments );
+               $this->_test_as_admin( $comment );
+       }
+
+       /**
+        * Dims a comment with a bad nonce.
+        */
+       public function test_ajax_dim_comment_bad_nonce() {
+               $comment = array_pop( $this->_comments );
+               $this->_test_with_bad_nonce( $comment );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxEditCommentphpfromrev54721trunktestsphpunittestsajaxEditCommentphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxEditComment.php (from rev 54721, trunk/tests/phpunit/tests/ajax/EditComment.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxEditComment.php                              (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxEditComment.php        2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,245 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Ajax comment functionality.
+ *
+ * @package WordPress
+ * @subpackage UnitTests
+ * @since 3.4.0
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_edit_comment
+ */
+class Tests_Ajax_wpAjaxEditComment extends WP_Ajax_UnitTestCase {
+
+       /**
+        * A post with at least one comment.
+        *
+        * @var mixed
+        */
+       protected $_comment_post = null;
+
+       /**
+        * Sets up the test fixture.
+        */
+       public function set_up() {
+               parent::set_up();
+               $post_id = self::factory()->post->create();
+               self::factory()->comment->create_post_comments( $post_id, 5 );
+               $this->_comment_post = get_post( $post_id );
+       }
+
+       /**
+        * Gets comments as a privileged user (administrator).
+        *
+        * Expects test to pass.
+        */
+       public function test_as_admin() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Get a comment.
+               $comments = get_comments(
+                       array(
+                               'post_id' => $this->_comment_post->ID,
+                       )
+               );
+               $comment  = array_pop( $comments );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
+               $_POST['comment_ID']                  = $comment->comment_ID;
+               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'edit-comment' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
+
+               // Check the meta data.
+               $this->assertSame( '-1', (string) $xml->response[0]->edit_comment['position'] );
+               $this->assertSame( $comment->comment_ID, (string) $xml->response[0]->edit_comment['id'] );
+               $this->assertSame( 'edit-comment_' . $comment->comment_ID, (string) $xml->response['action'] );
+
+               // Check the payload.
+               $this->assertNotEmpty( (string) $xml->response[0]->edit_comment[0]->response_data );
+
+               // And supplemental is empty.
+               $this->assertEmpty( (string) $xml->response[0]->edit_comment[0]->supplemental );
+       }
+
+       /**
+        * @ticket 33154
+        */
+       public function test_editor_can_edit_orphan_comments() {
+               global $wpdb;
+
+               // Become an editor.
+               $this->_setRole( 'editor' );
+
+               // Get a comment.
+               $comments = get_comments(
+                       array(
+                               'post_id' => $this->_comment_post->ID,
+                       )
+               );
+               $comment  = array_pop( $comments );
+
+               // Manually update the comment_post_ID, because wp_update_comment() will prevent it..
+               $wpdb->update( $wpdb->comments, array( 'comment_post_ID' => 0 ), array( 'comment_ID' => $comment->comment_ID ) );
+               clean_comment_cache( $comment->comment_ID );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
+               $_POST['comment_ID']                  = $comment->comment_ID;
+               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'edit-comment' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
+
+               // Check the meta data.
+               $this->assertSame( '-1', (string) $xml->response[0]->edit_comment['position'] );
+               $this->assertSame( $comment->comment_ID, (string) $xml->response[0]->edit_comment['id'] );
+               $this->assertSame( 'edit-comment_' . $comment->comment_ID, (string) $xml->response['action'] );
+
+               // Check the payload.
+               $this->assertNotEmpty( (string) $xml->response[0]->edit_comment[0]->response_data );
+
+               // And supplemental is empty.
+               $this->assertEmpty( (string) $xml->response[0]->edit_comment[0]->supplemental );
+       }
+
+       /**
+        * Gets comments as a non-privileged user (subscriber).
+        *
+        * Expects test to fail.
+        */
+       public function test_as_subscriber() {
+
+               // Become a subscriber.
+               $this->_setRole( 'subscriber' );
+
+               // Get a comment.
+               $comments = get_comments(
+                       array(
+                               'post_id' => $this->_comment_post->ID,
+                       )
+               );
+               $comment  = array_pop( $comments );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
+               $_POST['comment_ID']                  = $comment->comment_ID;
+               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'edit-comment' );
+       }
+
+       /**
+        * Gets comments with a bad nonce.
+        *
+        * Expects test to fail.
+        */
+       public function test_bad_nonce() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Get a comment.
+               $comments = get_comments(
+                       array(
+                               'post_id' => $this->_comment_post->ID,
+                       )
+               );
+               $comment  = array_pop( $comments );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( uniqid() );
+               $_POST['comment_ID']                  = $comment->comment_ID;
+               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'get-comments' );
+       }
+
+       /**
+        * Gets comments for an invalid post.
+        *
+        * This should return valid XML.
+        */
+       public function test_invalid_comment() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
+               $_POST['comment_ID']                  = 123456789;
+               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'edit-comment' );
+       }
+
+       /**
+        * @ticket 39732
+        */
+       public function test_wp_update_comment_data_is_wp_error() {
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Get a comment.
+               $comments = get_comments(
+                       array(
+                               'post_id' => $this->_comment_post->ID,
+                       )
+               );
+               $comment  = array_pop( $comments );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
+               $_POST['comment_ID']                  = $comment->comment_ID;
+               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
+
+               // Simulate filter check error.
+               add_filter( 'wp_update_comment_data', array( $this, '_wp_update_comment_data_filter' ), 10, 3 );
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( 'wp_update_comment_data filter fails for this comment.' );
+               $this->_handleAjax( 'edit-comment' );
+       }
+
+       /**
+        * Blocks comments from being updated by returning WP_Error.
+        */
+       public function _wp_update_comment_data_filter( $data, $comment, $commentarr ) {
+               return new WP_Error( 'comment_wrong', 'wp_update_comment_data filter fails for this comment.', 500 );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxGetCommentsphpfromrev54721trunktestsphpunittestsajaxGetCommentsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxGetComments.php (from rev 54721, trunk/tests/phpunit/tests/ajax/GetComments.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxGetComments.php                              (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxGetComments.php        2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,161 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Ajax comment functionality.
+ *
+ * @package WordPress
+ * @subpackage UnitTests
+ * @since 3.4.0
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_get_comments
+ */
+class Tests_Ajax_wpAjaxGetComments extends WP_Ajax_UnitTestCase {
+
+       /**
+        * A post with at least one comment.
+        *
+        * @var mixed
+        */
+       protected static $comment_post = null;
+
+       /**
+        * A post with no comments.
+        *
+        * @var mixed
+        */
+       protected static $no_comment_post = null;
+
+       protected static $comment_ids = array();
+
+       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+               self::$comment_post    = $factory->post->create_and_get();
+               self::$comment_ids     = $factory->comment->create_post_comments( self::$comment_post->ID, 5 );
+               self::$no_comment_post = $factory->post->create_and_get();
+       }
+
+       /**
+        * Gets comments as a privileged user (administrator).
+        *
+        * Expects test to pass.
+        */
+       public function test_as_admin() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'get-comments' );
+               $_POST['action']      = 'get-comments';
+               $_POST['p']           = self::$comment_post->ID;
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'get-comments' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
+
+               // Check the meta data.
+               $this->assertSame( '1', (string) $xml->response[0]->comments['position'] );
+               $this->assertSame( '0', (string) $xml->response[0]->comments['id'] );
+               $this->assertSame( 'get-comments_0', (string) $xml->response['action'] );
+
+               // Check the payload.
+               $this->assertNotEmpty( (string) $xml->response[0]->comments[0]->response_data );
+
+               // And supplemental is empty.
+               $this->assertEmpty( (string) $xml->response[0]->comments[0]->supplemental );
+       }
+
+       /**
+        * Gets comments as a non-privileged user (subscriber).
+        *
+        * Expects test to fail.
+        */
+       public function test_as_subscriber() {
+
+               // Become a subscriber.
+               $this->_setRole( 'subscriber' );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'get-comments' );
+               $_POST['action']      = 'get-comments';
+               $_POST['p']           = self::$comment_post->ID;
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'get-comments' );
+       }
+
+       /**
+        * Gets comments with a bad nonce.
+        *
+        * Expects test to fail.
+        */
+       public function test_bad_nonce() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce'] = wp_create_nonce( uniqid() );
+               $_POST['action']      = 'get-comments';
+               $_POST['p']           = self::$comment_post->ID;
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'get-comments' );
+       }
+
+       /**
+        * Gets comments for an invalid post.
+        *
+        * Bad post IDs are set to 0, this should return valid XML.
+        */
+       public function test_invalid_post() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'get-comments' );
+               $_POST['action']      = 'get-comments';
+               $_POST['p']           = 'b0rk';
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'get-comments' );
+       }
+
+       /**
+        * Gets comments for a post with no comments.
+        */
+       public function test_post_with_no_comments() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'get-comments' );
+               $_POST['action']      = 'get-comments';
+               $_POST['p']           = self::$no_comment_post->ID;
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '1' );
+               $this->_handleAjax( 'get-comments' );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxHeartbeatphpfromrev54721trunktestsphpunittestsajaxAutosavephp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxHeartbeat.php (from rev 54721, trunk/tests/phpunit/tests/ajax/Autosave.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxHeartbeat.php                                (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxHeartbeat.php  2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,167 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Ajax save draft functionality.
+ *
+ * @package WordPress
+ * @subpackage UnitTests
+ * @since 3.4.0
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_heartbeat
+ */
+class Tests_Ajax_wpAjaxHeartbeat extends WP_Ajax_UnitTestCase {
+
+       /**
+        * Post
+        *
+        * @var mixed
+        */
+       protected $_post = null;
+
+       protected static $admin_id  = 0;
+       protected static $editor_id = 0;
+       protected static $post;
+       protected static $post_id;
+
+       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+               self::$admin_id  = $factory->user->create( array( 'role' => 'administrator' ) );
+               self::$editor_id = $factory->user->create( array( 'role' => 'editor' ) );
+
+               // Set a user so the $post has 'post_author'.
+               wp_set_current_user( self::$admin_id );
+
+               self::$post_id = $factory->post->create( array( 'post_status' => 'draft' ) );
+               self::$post    = get_post( self::$post_id );
+       }
+
+       /**
+        * Tests autosaving a post.
+        */
+       public function test_autosave_post() {
+               // The original post_author.
+               wp_set_current_user( self::$admin_id );
+
+               // Set up the $_POST request.
+               $md5   = md5( uniqid() );
+               $_POST = array(
+                       'action' => 'heartbeat',
+                       '_nonce' => wp_create_nonce( 'heartbeat-nonce' ),
+                       'data'   => array(
+                               'wp_autosave' => array(
+                                       'post_id'      => self::$post_id,
+                                       '_wpnonce'     => wp_create_nonce( 'update-post_' . self::$post_id ),
+                                       'post_content' => self::$post->post_content . PHP_EOL . $md5,
+                                       'post_type'    => 'post',
+                               ),
+                       ),
+               );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'heartbeat' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response, it is in heartbeat's response.
+               $response = json_decode( $this->_last_response, true );
+
+               // Ensure everything is correct.
+               $this->assertNotEmpty( $response['wp_autosave'] );
+               $this->assertTrue( $response['wp_autosave']['success'] );
+
+               // Check that the edit happened.
+               $post = get_post( self::$post_id );
+               $this->assertStringContainsString( $md5, $post->post_content );
+       }
+
+       /**
+        * Tests autosaving a locked post.
+        */
+       public function test_autosave_locked_post() {
+               // Lock the post to another user.
+               wp_set_current_user( self::$editor_id );
+               wp_set_post_lock( self::$post_id );
+
+               wp_set_current_user( self::$admin_id );
+
+               // Ensure post is locked.
+               $this->assertEquals( self::$editor_id, wp_check_post_lock( self::$post_id ) );
+
+               // Set up the $_POST request.
+               $md5   = md5( uniqid() );
+               $_POST = array(
+                       'action' => 'heartbeat',
+                       '_nonce' => wp_create_nonce( 'heartbeat-nonce' ),
+                       'data'   => array(
+                               'wp_autosave' => array(
+                                       'post_id'      => self::$post_id,
+                                       '_wpnonce'     => wp_create_nonce( 'update-post_' . self::$post_id ),
+                                       'post_content' => self::$post->post_content . PHP_EOL . $md5,
+                                       'post_type'    => 'post',
+                               ),
+                       ),
+               );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'heartbeat' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               $response = json_decode( $this->_last_response, true );
+
+               // Ensure everything is correct.
+               $this->assertNotEmpty( $response['wp_autosave'] );
+               $this->assertTrue( $response['wp_autosave']['success'] );
+
+               // Check that the original post was NOT edited.
+               $post = get_post( self::$post_id );
+               $this->assertStringNotContainsString( $md5, $post->post_content );
+
+               // Check if the autosave post was created.
+               $autosave = wp_get_post_autosave( self::$post_id, get_current_user_id() );
+               $this->assertNotEmpty( $autosave );
+               $this->assertStringContainsString( $md5, $autosave->post_content );
+       }
+
+       /**
+        * Tests with an invalid nonce.
+        */
+       public function test_with_invalid_nonce() {
+
+               wp_set_current_user( self::$admin_id );
+
+               // Set up the $_POST request.
+               $_POST = array(
+                       'action' => 'heartbeat',
+                       '_nonce' => wp_create_nonce( 'heartbeat-nonce' ),
+                       'data'   => array(
+                               'wp_autosave' => array(
+                                       'post_id'  => self::$post_id,
+                                       '_wpnonce' => substr( md5( uniqid() ), 0, 10 ),
+                               ),
+                       ),
+               );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'heartbeat' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               $response = json_decode( $this->_last_response, true );
+
+               $this->assertNotEmpty( $response['wp_autosave'] );
+               $this->assertFalse( $response['wp_autosave']['success'] );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxImageEditorphpfromrev54721trunktestsphpunittestsajaxMediaEditphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxImageEditor.php (from rev 54721, trunk/tests/phpunit/tests/ajax/MediaEdit.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxImageEditor.php                              (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxImageEditor.php        2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,123 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Ajax media editing.
+ *
+ * @package WordPress
+ * @subpackage UnitTests
+ * @since 3.5.0
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_image_editor
+ *
+ * @requires function imagejpeg
+ */
+class Tests_Ajax_wpAjaxImageEditor extends WP_Ajax_UnitTestCase {
+
+       /**
+        * Tear down the test fixture.
+        */
+       public function tear_down() {
+               // Cleanup.
+               $this->remove_added_uploads();
+               parent::tear_down();
+       }
+
+       /**
+        * @ticket 22985
+        * @requires function imagejpeg
+        *
+        * @covers ::wp_insert_attachment
+        * @covers ::wp_save_image
+        */
+       public function testCropImageThumbnail() {
+               require_once ABSPATH . 'wp-admin/includes/image-edit.php';
+
+               $filename = DIR_TESTDATA . '/images/canola.jpg';
+               $contents = file_get_contents( $filename );
+
+               $upload = wp_upload_bits( wp_basename( $filename ), null, $contents );
+               $id     = $this->_make_attachment( $upload );
+
+               $_REQUEST['action']  = 'image-editor';
+               $_REQUEST['context'] = 'edit-attachment';
+               $_REQUEST['postid']  = $id;
+               $_REQUEST['target']  = 'thumbnail';
+               $_REQUEST['do']      = 'save';
+               $_REQUEST['history'] = '[{"c":{"x":5,"y":8,"w":289,"h":322}}]';
+
+               $media_meta = wp_get_attachment_metadata( $id );
+               $this->assertArrayHasKey( 'sizes', $media_meta, 'attachment should have size data' );
+               $this->assertArrayHasKey( 'medium', $media_meta['sizes'], 'attachment should have data for medium size' );
+               $ret = wp_save_image( $id );
+
+               $media_meta = wp_get_attachment_metadata( $id );
+               $this->assertArrayHasKey( 'sizes', $media_meta, 'cropped attachment should have size data' );
+               $this->assertArrayHasKey( 'medium', $media_meta['sizes'], 'cropped attachment should have data for medium size' );
+       }
+
+       /**
+        * @ticket 32171
+        * @requires function imagejpeg
+        *
+        * @covers ::wp_insert_attachment
+        * @covers ::wp_save_image
+        */
+       public function testImageEditOverwriteConstant() {
+               define( 'IMAGE_EDIT_OVERWRITE', true );
+
+               require_once ABSPATH . 'wp-admin/includes/image-edit.php';
+
+               $filename = DIR_TESTDATA . '/images/canola.jpg';
+               $contents = file_get_contents( $filename );
+
+               $upload = wp_upload_bits( wp_basename( $filename ), null, $contents );
+               $id     = $this->_make_attachment( $upload );
+
+               $_REQUEST['action']  = 'image-editor';
+               $_REQUEST['context'] = 'edit-attachment';
+               $_REQUEST['postid']  = $id;
+               $_REQUEST['target']  = 'all';
+               $_REQUEST['do']      = 'save';
+               $_REQUEST['history'] = '[{"c":{"x":5,"y":8,"w":289,"h":322}}]';
+
+               $ret = wp_save_image( $id );
+
+               $media_meta = wp_get_attachment_metadata( $id );
+               $sizes1     = $media_meta['sizes'];
+
+               $_REQUEST['history'] = '[{"c":{"x":5,"y":8,"w":189,"h":322}}]';
+
+               $ret = wp_save_image( $id );
+
+               $media_meta = wp_get_attachment_metadata( $id );
+               $sizes2     = $media_meta['sizes'];
+
+               $file_path = dirname( get_attached_file( $id ) );
+
+               $files_that_should_not_exist = array();
+
+               foreach ( $sizes1 as $key => $size ) {
+                       if ( $sizes2[ $key ]['file'] !== $size['file'] ) {
+                               $files_that_should_not_exist[] = $file_path . '/' . $size['file'];
+                       }
+               }
+
+               if ( ! empty( $files_that_should_not_exist ) ) {
+                       foreach ( $files_that_should_not_exist as $file ) {
+                               $this->assertFileDoesNotExist( $file, 'IMAGE_EDIT_OVERWRITE is leaving garbage image files behind.' );
+                       }
+               } else {
+                       /*
+                        * This assertion will always pass due to the "if" condition, but prevents this test
+                        * from being marked as "risky" due to the test not performing any assertions.
+                        */
+                       $this->assertSame( array(), $files_that_should_not_exist );
+               }
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxInlineSavephpfromrev54721trunktestsphpunittestsajaxQuickEditphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxInlineSave.php (from rev 54721, trunk/tests/phpunit/tests/ajax/QuickEdit.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxInlineSave.php                               (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxInlineSave.php 2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,90 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Quick Edit AJAX functionality.
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_inline_save
+ */
+class Tests_Ajax_wpAjaxInlineSave extends WP_Ajax_UnitTestCase {
+
+       /**
+        * @ticket 26948
+        *
+        * @covers ::edit_post
+        */
+       public function test_dont_process_terms_if_taxonomy_does_not_allow_show_on_quick_edit() {
+               register_taxonomy(
+                       'wptests_tax_1',
+                       'post',
+                       array(
+                               'show_in_quick_edit' => false,
+                               'hierarchical'       => true,
+                       )
+               );
+               register_taxonomy(
+                       'wptests_tax_2',
+                       'post',
+                       array(
+                               'show_in_quick_edit' => true,
+                               'hierarchical'       => true,
+                       )
+               );
+
+               $t1 = self::factory()->term->create(
+                       array(
+                               'taxonomy' => 'wptests_tax_1',
+                       )
+               );
+               $t2 = self::factory()->term->create(
+                       array(
+                               'taxonomy' => 'wptests_tax_2',
+                       )
+               );
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               $post = self::factory()->post->create_and_get(
+                       array(
+                               'post_author' => get_current_user_id(),
+                       )
+               );
+
+               // Set up a request.
+               $_POST['_inline_edit'] = wp_create_nonce( 'inlineeditnonce' );
+               $_POST['post_ID']      = $post->ID;
+               $_POST['post_type']    = $post->post_type;
+               $_POST['content']      = $post->post_content;
+               $_POST['excerpt']      = $post->post_excerpt;
+               $_POST['_status']      = $post->post_status;
+               $_POST['post_status']  = $post->post_status;
+               $_POST['screen']       = 'post';
+               $_POST['post_view']    = 'excerpt';
+               $_POST['tax_input']    = array(
+                       'wptests_tax_1' => array( $t1 ),
+                       'wptests_tax_2' => array( $t2 ),
+               );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'inline-save' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // 'wptests_tax_1' terms should have been refused.
+               $post_terms_1 = wp_get_object_terms( $post->ID, 'wptests_tax_1' );
+               $this->assertEmpty( $post_terms_1 );
+
+               // 'wptests_tax_2' terms should have been added successfully.
+               $post_terms_2 = wp_get_object_terms( $post->ID, 'wptests_tax_2' );
+               $this->assertSameSets( array( $t2 ), wp_list_pluck( $post_terms_2, 'term_id' ) );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxReplytoCommentphpfromrev54721trunktestsphpunittestsajaxReplytoCommentphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxReplytoComment.php (from rev 54721, trunk/tests/phpunit/tests/ajax/ReplytoComment.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxReplytoComment.php                           (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxReplytoComment.php     2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,273 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Ajax comment functionality.
+ *
+ * @package WordPress
+ * @subpackage UnitTests
+ * @since 3.4.0
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_replyto_comment
+ */
+class Tests_Ajax_wpAjaxReplytoComment extends WP_Ajax_UnitTestCase {
+
+       /**
+        * A post with at least one comment.
+        *
+        * @var mixed
+        */
+       protected static $comment_post = null;
+
+       /**
+        * Draft post.
+        *
+        * @var mixed
+        */
+       protected static $draft_post = null;
+
+       protected static $comment_ids = array();
+
+       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+               self::$comment_post = $factory->post->create_and_get();
+               self::$comment_ids  = $factory->comment->create_post_comments( self::$comment_post->ID, 5 );
+               self::$draft_post   = $factory->post->create_and_get( array( 'post_status' => 'draft' ) );
+       }
+
+       public function tear_down() {
+               remove_filter( 'query', array( $this, '_block_comments' ) );
+               parent::tear_down();
+       }
+
+       /**
+        * Tests reply as a privileged user (administrator).
+        *
+        * Expects test to pass.
+        */
+       public function test_as_admin() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Get a comment.
+               $comments = get_comments(
+                       array(
+                               'post_id' => self::$comment_post->ID,
+                       )
+               );
+               $comment  = array_pop( $comments );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
+               $_POST['comment_ID']                  = $comment->comment_ID;
+               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
+               $_POST['comment_post_ID']             = self::$comment_post->ID;
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'replyto-comment' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
+
+               // Check the meta data.
+               $this->assertSame( '-1', (string) $xml->response[0]->comment['position'] );
+               $this->assertGreaterThan( 0, (int) $xml->response[0]->comment['id'] );
+               $this->assertNotEmpty( (string) $xml->response['action'] );
+
+               // Check the payload.
+               $this->assertNotEmpty( (string) $xml->response[0]->comment[0]->response_data );
+
+               // And supplemental is empty.
+               $this->assertEmpty( (string) $xml->response[0]->comment[0]->supplemental );
+       }
+
+       /**
+        * Tests reply as a non-privileged user (subscriber).
+        *
+        * Expects test to fail.
+        */
+       public function test_as_subscriber() {
+
+               // Become an administrator.
+               $this->_setRole( 'subscriber' );
+
+               // Get a comment.
+               $comments = get_comments(
+                       array(
+                               'post_id' => self::$comment_post->ID,
+                       )
+               );
+               $comment  = array_pop( $comments );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
+               $_POST['comment_ID']                  = $comment->comment_ID;
+               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
+               $_POST['comment_post_ID']             = self::$comment_post->ID;
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'replyto-comment' );
+       }
+
+       /**
+        * Tests reply using a bad nonce.
+        *
+        * Expects test to fail.
+        */
+       public function test_bad_nonce() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Get a comment.
+               $comments = get_comments(
+                       array(
+                               'post_id' => self::$comment_post->ID,
+                       )
+               );
+               $comment  = array_pop( $comments );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( uniqid() );
+               $_POST['comment_ID']                  = $comment->comment_ID;
+               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
+               $_POST['comment_post_ID']             = self::$comment_post->ID;
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'replyto-comment' );
+       }
+
+       /**
+        * Tests reply to an invalid post.
+        *
+        * Expects test to fail.
+        */
+       public function test_invalid_post() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
+               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
+               $_POST['comment_post_ID']             = 123456789;
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'replyto-comment' );
+       }
+
+       /**
+        * Tests reply to a draft post.
+        *
+        * Expects test to fail.
+        */
+       public function test_with_draft_post() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
+               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
+               $_POST['comment_post_ID']             = self::$draft_post->ID;
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( 'You cannot reply to a comment on a draft post.' );
+               $this->_handleAjax( 'replyto-comment' );
+       }
+
+       /**
+        * Tests reply to a post with a simulated database failure.
+        *
+        * Expects test to fail.
+        *
+        * @global $wpdb
+        */
+       public function test_blocked_comment() {
+               global $wpdb;
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
+               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
+               $_POST['comment_post_ID']             = self::$comment_post->ID;
+
+               // Block comments from being saved, simulate a DB error.
+               add_filter( 'query', array( $this, '_block_comments' ) );
+
+               // Make the request.
+               try {
+                       $wpdb->suppress_errors( true );
+                       $this->_handleAjax( 'replyto-comment' );
+                       $wpdb->suppress_errors( false );
+                       $this->fail();
+               } catch ( WPAjaxDieStopException $e ) {
+                       $wpdb->suppress_errors( false );
+                       $this->assertStringContainsString( '1', $e->getMessage() );
+               }
+       }
+
+       /**
+        * Blocks comments from being saved.
+        *
+        * @param string $sql
+        * @return string
+        */
+       public function _block_comments( $sql ) {
+               global $wpdb;
+               if ( false !== strpos( $sql, $wpdb->comments ) && 0 === stripos( trim( $sql ), 'INSERT INTO' ) ) {
+                       return '';
+               }
+               return $sql;
+       }
+
+       /**
+        * Tests blocking a comment from being saved on 'pre_comment_approved'.
+        *
+        * @ticket 39730
+        */
+       public function test_pre_comments_approved() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_POST['_ajax_nonce-replyto-comment'] = wp_create_nonce( 'replyto-comment' );
+               $_POST['content']                     = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
+               $_POST['comment_post_ID']             = self::$comment_post->ID;
+
+               // Simulate filter check error.
+               add_filter( 'pre_comment_approved', array( $this, '_pre_comment_approved_filter' ), 10, 2 );
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( 'pre_comment_approved filter fails for new comment.' );
+               $this->_handleAjax( 'replyto-comment' );
+       }
+
+       /**
+        * Blocks comments from being saved on 'pre_comment_approved', by returning WP_Error.
+        */
+       public function _pre_comment_approved_filter( $approved, $commentdata ) {
+               return new WP_Error( 'comment_wrong', 'pre_comment_approved filter fails for new comment.', 403 );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxResponsephpfromrev54721trunktestsphpunittestsajaxResponsephp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxResponse.php (from rev 54721, trunk/tests/phpunit/tests/ajax/Response.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxResponse.php                         (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxResponse.php   2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,108 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Testing Ajax response class
+ *
+ * @package WordPress
+ * @subpackage UnitTests
+ * @since 3.5.0
+ *
+ * @group ajax
+ *
+ * @covers WP_Ajax_Response::send
+ */
+class Tests_Ajax_wpAjaxResponse extends WP_UnitTestCase {
+
+       /**
+        * Saved error reporting level
+        *
+        * @var int
+        */
+       protected $_error_level = 0;
+
+       /**
+        * Set up the test fixture.
+        * Override wp_die(), pretend to be ajax, and suppres E_WARNINGs
+        */
+       public function set_up() {
+               parent::set_up();
+
+               add_filter( 'wp_die_ajax_handler', array( $this, 'getDieHandler' ), 1, 1 );
+               add_filter( 'wp_doing_ajax', '__return_true' );
+
+               // Suppress warnings from "Cannot modify header information - headers already sent by".
+               $this->_error_level = error_reporting();
+               error_reporting( $this->_error_level & ~E_WARNING );
+       }
+
+       /**
+        * Tear down the test fixture.
+        * Remove the wp_die() override, restore error reporting
+        */
+       public function tear_down() {
+               remove_filter( 'wp_die_ajax_handler', array( $this, 'getDieHandler' ), 1, 1 );
+               error_reporting( $this->_error_level );
+               parent::tear_down();
+       }
+
+       /**
+        * Return our callback handler
+        *
+        * @return callback
+        */
+       public function getDieHandler() {
+               return array( $this, 'dieHandler' );
+       }
+
+       /**
+        * Handler for wp_die()
+        * Don't die, just continue on.
+        *
+        * @param string $message
+        */
+       public function dieHandler( $message ) {
+       }
+
+       /**
+        * Test that charset in header matches blog_charset
+        * Note:  headers_list doesn't work properly in CLI mode, fall back on
+        * xdebug_get_headers if it's available
+        * Needs a separate process to get around the headers/output from the
+        * bootstrapper
+        *
+        * @ticket 19448
+        * @runInSeparateProcess
+        * @preserveGlobalState disabled
+        * @group xdebug
+        * @requires function xdebug_get_headers
+        */
+       public function test_response_charset_in_header() {
+
+               // Generate an Ajax response.
+               ob_start();
+               $ajax_response = new WP_Ajax_Response();
+               $ajax_response->send();
+
+               // Check the header.
+               $headers = xdebug_get_headers();
+               ob_end_clean();
+
+               $this->assertContains( 'Content-Type: text/xml; charset=' . get_option( 'blog_charset' ), $headers );
+       }
+
+       /**
+        * Test that charset in the xml tag matches blog_charset
+        *
+        * @ticket 19448
+        */
+       public function test_response_charset_in_xml() {
+
+               // Generate an Ajax response.
+               ob_start();
+               $ajax_response = new WP_Ajax_Response();
+               $ajax_response->send();
+
+               // Check the XML tag.
+               $contents = ob_get_clean();
+               $this->assertMatchesRegularExpression( '/<\?xml\s+version=\'1.0\'\s+encoding=\'' . preg_quote( get_option( 'blog_charset' ) ) . '\'\s+standalone=\'yes\'\?>/', $contents );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxSendAttachmentToEditorphpfromrev54721trunktestsphpunittestsajaxAttachmentsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxSendAttachmentToEditor.php (from rev 54721, trunk/tests/phpunit/tests/ajax/Attachments.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxSendAttachmentToEditor.php                           (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxSendAttachmentToEditor.php     2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,104 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Ajax attachment handling.
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_send_attachment_to_editor
+ */
+class Tests_Ajax_wpAjaxSendAttachmentToEditor extends WP_Ajax_UnitTestCase {
+
+       /**
+        * @ticket 36578
+        *
+        * @covers ::get_image_send_to_editor
+        */
+       public function test_wp_ajax_send_attachment_to_editor_should_return_an_image() {
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               $filename = DIR_TESTDATA . '/images/canola.jpg';
+               $contents = file_get_contents( $filename );
+
+               $upload     = wp_upload_bits( wp_basename( $filename ), null, $contents );
+               $attachment = $this->_make_attachment( $upload );
+
+               // Set up a default request.
+               $_POST['nonce']      = wp_create_nonce( 'media-send-to-editor' );
+               $_POST['html']       = 'Bar Baz';
+               $_POST['post_id']    = 0;
+               $_POST['attachment'] = array(
+                       'id'         => $attachment,
+                       'align'      => 'left',
+                       'image-size' => 'large',
+                       'image_alt'  => 'Foo bar',
+                       'url'        => 'http://example.com/',
+               );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'send-attachment-to-editor' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = get_image_send_to_editor( $attachment, '', '', 'left', 'http://example.com/', false, 'large', 'Foo bar' );
+
+               // Ensure everything is correct.
+               $this->assertTrue( $response['success'] );
+               $this->assertSame( $expected, $response['data'] );
+       }
+
+       /**
+        * @ticket 36578
+        * @group ms-excluded
+        */
+       public function test_wp_ajax_send_attachment_to_editor_should_return_a_link() {
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               $filename = DIR_TESTDATA . '/formatting/entities.txt';
+               $contents = file_get_contents( $filename );
+
+               $upload     = wp_upload_bits( wp_basename( $filename ), null, $contents );
+               $attachment = $this->_make_attachment( $upload );
+
+               // Set up a default request.
+               $_POST['nonce']      = wp_create_nonce( 'media-send-to-editor' );
+               $_POST['html']       = 'Bar Baz';
+               $_POST['post_id']    = 0;
+               $_POST['attachment'] = array(
+                       'id'         => $attachment,
+                       'post_title' => 'Foo bar',
+                       'url'        => get_attachment_link( $attachment ),
+               );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'send-attachment-to-editor' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = sprintf(
+                       '<a href="%s" rel="attachment wp-att-%d">Foo bar</a>',
+                       get_attachment_link( $attachment ),
+                       $attachment
+               );
+
+               // Ensure everything is correct.
+               $this->assertTrue( $response['success'] );
+               $this->assertSame( $expected, $response['data'] );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxUpdatePluginphpfromrev54721trunktestsphpunittestsajaxUpdatePluginphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxUpdatePlugin.php (from rev 54721, trunk/tests/phpunit/tests/ajax/UpdatePlugin.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxUpdatePlugin.php                             (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxUpdatePlugin.php       2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,173 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Ajax handler for updating a plugin.
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_update_plugin
+ */
+class Tests_Ajax_wpAjaxUpdatePlugin extends WP_Ajax_UnitTestCase {
+
+       public function test_missing_nonce() {
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'update-plugin' );
+       }
+
+       public function test_missing_plugin() {
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
+               $_POST['slug']        = 'foo';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'update-plugin' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = array(
+                       'success' => false,
+                       'data'    => array(
+                               'slug'         => '',
+                               'errorCode'    => 'no_plugin_specified',
+                               'errorMessage' => 'No plugin specified.',
+                       ),
+               );
+
+               $this->assertSameSets( $expected, $response );
+       }
+
+       public function test_missing_slug() {
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
+               $_POST['plugin']      = 'foo/bar.php';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'update-plugin' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = array(
+                       'success' => false,
+                       'data'    => array(
+                               'slug'         => '',
+                               'errorCode'    => 'no_plugin_specified',
+                               'errorMessage' => 'No plugin specified.',
+                       ),
+               );
+
+               $this->assertSameSets( $expected, $response );
+       }
+
+       public function test_missing_capability() {
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
+               $_POST['plugin']      = 'foo/bar.php';
+               $_POST['slug']        = 'foo';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'update-plugin' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = array(
+                       'success' => false,
+                       'data'    => array(
+                               'update'       => 'plugin',
+                               'slug'         => 'foo',
+                               'oldVersion'   => '',
+                               'newVersion'   => '',
+                               'errorMessage' => 'Sorry, you are not allowed to update plugins for this site.',
+                       ),
+               );
+
+               $this->assertSameSets( $expected, $response );
+       }
+
+       public function test_invalid_file() {
+               $this->_setRole( 'administrator' );
+
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
+               $_POST['plugin']      = '../foo/bar.php';
+               $_POST['slug']        = 'foo';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'update-plugin' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = array(
+                       'success' => false,
+                       'data'    => array(
+                               'update'       => 'plugin',
+                               'slug'         => 'foo',
+                               'oldVersion'   => '',
+                               'newVersion'   => '',
+                               'errorMessage' => 'Sorry, you are not allowed to update plugins for this site.',
+                       ),
+               );
+
+               $this->assertSameSets( $expected, $response );
+       }
+
+       /**
+        * @group ms-excluded
+        */
+       public function test_update_plugin() {
+               $this->_setRole( 'administrator' );
+
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
+               $_POST['plugin']      = 'hello.php';
+               $_POST['slug']        = 'hello-dolly';
+
+               // Make the request.
+               try {
+                       // Prevent wp_update_plugins() from running.
+                       wp_installing( true );
+                       $this->_handleAjax( 'update-plugin' );
+                       wp_installing( false );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = array(
+                       'success' => false,
+                       'data'    => array(
+                               'update'       => 'plugin',
+                               'slug'         => 'hello-dolly',
+                               'oldVersion'   => 'Version 1.7.2',
+                               'newVersion'   => '',
+                               'plugin'       => 'hello.php',
+                               'pluginName'   => 'Hello Dolly',
+                               'debug'        => array( 'The plugin is at the latest version.' ),
+                               'errorMessage' => 'The plugin is at the latest version.',
+                       ),
+               );
+
+               $this->assertSameSets( $expected, $response );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxUpdateThemephpfromrev54721trunktestsphpunittestsajaxManageThemesphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxUpdateTheme.php (from rev 54721, trunk/tests/phpunit/tests/ajax/ManageThemes.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxUpdateTheme.php                              (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxUpdateTheme.php        2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,179 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Ajax handler for installing, updating, and deleting themes.
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_update_theme
+ */
+class Tests_Ajax_wpAjaxUpdateTheme extends WP_Ajax_UnitTestCase {
+       private $orig_theme_dir;
+       private $theme_root;
+
+       public function set_up() {
+               parent::set_up();
+
+               $this->theme_root     = DIR_TESTDATA . '/themedir1';
+               $this->orig_theme_dir = $GLOBALS['wp_theme_directories'];
+
+               // /themes is necessary as theme.php functions assume /themes is the root if there is only one root.
+               $GLOBALS['wp_theme_directories'] = array( WP_CONTENT_DIR . '/themes', $this->theme_root );
+
+               add_filter( 'theme_root', array( $this, 'filter_theme_root' ) );
+               add_filter( 'stylesheet_root', array( $this, 'filter_theme_root' ) );
+               add_filter( 'template_root', array( $this, 'filter_theme_root' ) );
+
+               wp_clean_themes_cache();
+               unset( $GLOBALS['wp_themes'] );
+       }
+
+       public function tear_down() {
+               $GLOBALS['wp_theme_directories'] = $this->orig_theme_dir;
+               remove_filter( 'theme_root', array( $this, 'filter_theme_root' ) );
+               remove_filter( 'stylesheet_root', array( $this, 'filter_theme_root' ) );
+               remove_filter( 'template_root', array( $this, 'filter_theme_root' ) );
+               wp_clean_themes_cache();
+               unset( $GLOBALS['wp_themes'] );
+
+               parent::tear_down();
+       }
+
+       /**
+        * Replace the normal theme root dir with our pre-made test dir.
+        */
+       public function filter_theme_root() {
+               return $this->theme_root;
+       }
+
+       public function test_missing_slug() {
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'update-theme' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = array(
+                       'success' => false,
+                       'data'    => array(
+                               'slug'         => '',
+                               'errorCode'    => 'no_theme_specified',
+                               'errorMessage' => 'No theme specified.',
+                       ),
+               );
+
+               $this->assertSameSets( $expected, $response );
+       }
+
+       public function test_missing_capability() {
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
+               $_POST['slug']        = 'foo';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'update-theme' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = array(
+                       'success' => false,
+                       'data'    => array(
+                               'update'       => 'theme',
+                               'slug'         => 'foo',
+                               'oldVersion'   => '',
+                               'newVersion'   => '',
+                               'errorMessage' => 'Sorry, you are not allowed to update themes for this site.',
+                       ),
+               );
+
+               $this->assertSameSets( $expected, $response );
+       }
+
+       /**
+        * @group ms-excluded
+        */
+       public function test_update_theme() {
+               $this->_setRole( 'administrator' );
+
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
+               $_POST['slug']        = 'twentyten';
+
+               // Make the request.
+               try {
+
+                       // Prevent wp_update_themes() from running.
+                       wp_installing( true );
+                       $this->_handleAjax( 'update-theme' );
+                       wp_installing( false );
+
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $theme    = wp_get_theme( 'twentyten' );
+               $expected = array(
+                       'success' => false,
+                       'data'    => array(
+                               'update'       => 'theme',
+                               'slug'         => 'twentyten',
+                               'oldVersion'   => $theme->get( 'Version' ),
+                               'newVersion'   => '',
+                               'debug'        => array( 'The theme is at the latest version.' ),
+                               'errorMessage' => 'The theme is at the latest version.',
+                       ),
+               );
+
+               $this->assertSameSets( $expected, $response );
+       }
+
+       /**
+        * @group ms-excluded
+        */
+       public function test_uppercase_theme_slug() {
+               $this->_setRole( 'administrator' );
+
+               $_POST['_ajax_nonce'] = wp_create_nonce( 'updates' );
+               $_POST['slug']        = 'camelCase';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'update-theme' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Get the response.
+               $response = json_decode( $this->_last_response, true );
+
+               $expected = array(
+                       'success' => false,
+                       'data'    => array(
+                               'update'       => 'theme',
+                               'slug'         => 'camelCase',
+                               'oldVersion'   => '1.0',
+                               'newVersion'   => '',
+                               'debug'        => array( 'The theme is at the latest version.' ),
+                               'errorMessage' => 'The theme is at the latest version.',
+                       ),
+               );
+
+               $this->assertSameSets( $expected, $response );
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxWpCompressionTestphpfromrev54721trunktestsphpunittestsajaxCompressionphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxWpCompressionTest.php (from rev 54721, trunk/tests/phpunit/tests/ajax/Compression.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxWpCompressionTest.php                                (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxWpCompressionTest.php  2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,225 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Admin Ajax functions to be tested.
+ */
+require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
+
+/**
+ * Testing Ajax compression test functionality.
+ *
+ * @package WordPress
+ * @subpackage UnitTests
+ * @since 3.4.0
+ *
+ * @group ajax
+ *
+ * @covers ::wp_ajax_wp_compression_test
+ */
+class Tests_Ajax_wpAjaxWpCompressionTest extends WP_Ajax_UnitTestCase {
+
+       /**
+        * Test as a logged out user
+        */
+       public function test_logged_out() {
+               $this->logout();
+
+               // Set up a default request.
+               $_GET['test'] = 1;
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'wp-compression-test' );
+       }
+
+       /**
+        * Fetch the test text
+        */
+       public function test_text() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_GET['test'] = 1;
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'wp-compression-test' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Ensure we found the right match.
+               $this->assertStringContainsString( 'wpCompressionTest', $this->_last_response );
+       }
+
+       /**
+        * Fetch the test text (gzdeflate)
+        *
+        * @requires function gzdeflate
+        */
+       public function test_gzdeflate() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_GET['test']                    = 2;
+               $_SERVER['HTTP_ACCEPT_ENCODING'] = 'deflate';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'wp-compression-test' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Ensure we found the right match.
+               $this->assertStringContainsString( 'wpCompressionTest', gzinflate( $this->_last_response ) );
+       }
+
+       /**
+        * Fetch the test text (gzencode)
+        *
+        * @requires function gzencode
+        */
+       public function test_gzencode() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_GET['test']                    = 2;
+               $_SERVER['HTTP_ACCEPT_ENCODING'] = 'gzip';
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'wp-compression-test' );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               // Ensure we found the right match.
+               $this->assertStringContainsString( 'wpCompressionTest', $this->_gzdecode( $this->_last_response ) );
+       }
+
+       /**
+        * Fetch the test text (unknown encoding)
+        */
+       public function test_unknown_encoding() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_GET['test']                    = 2;
+               $_SERVER['HTTP_ACCEPT_ENCODING'] = 'unknown';
+
+               // Make the request.
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+               $this->_handleAjax( 'wp-compression-test' );
+       }
+
+       /**
+        * Set the 'can_compress_scripts' site option to true
+        */
+       public function test_set_yes() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_GET['test'] = 'yes';
+
+               // Set the option to false.
+               update_site_option( 'can_compress_scripts', 0 );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'wp-compression-test' );
+               } catch ( WPAjaxDieStopException $e ) {
+                       unset( $e );
+               }
+
+               // Check the site option is not changed due to lack of nonce.
+               $this->assertSame( 0, get_site_option( 'can_compress_scripts' ) );
+
+               // Add a nonce.
+               $_GET['_ajax_nonce'] = wp_create_nonce( 'update_can_compress_scripts' );
+
+               // Retry the request.
+               try {
+                       $this->_handleAjax( 'wp-compression-test' );
+               } catch ( WPAjaxDieStopException $e ) {
+                       unset( $e );
+               }
+
+               // Check the site option is changed.
+               $this->assertSame( 1, get_site_option( 'can_compress_scripts' ) );
+       }
+
+       /**
+        * Set the 'can_compress_scripts' site option to false
+        */
+       public function test_set_no() {
+
+               // Become an administrator.
+               $this->_setRole( 'administrator' );
+
+               // Set up a default request.
+               $_GET['test'] = 'no';
+
+               // Set the option to true.
+               update_site_option( 'can_compress_scripts', 1 );
+
+               // Make the request.
+               try {
+                       $this->_handleAjax( 'wp-compression-test' );
+               } catch ( WPAjaxDieStopException $e ) {
+                       unset( $e );
+               }
+
+               // Check the site option is not changed due to lack of nonce.
+               $this->assertSame( 1, get_site_option( 'can_compress_scripts' ) );
+
+               // Add a nonce.
+               $_GET['_ajax_nonce'] = wp_create_nonce( 'update_can_compress_scripts' );
+
+               // Retry the request.
+               try {
+                       $this->_handleAjax( 'wp-compression-test' );
+               } catch ( WPAjaxDieStopException $e ) {
+                       unset( $e );
+               }
+
+               // Check the site option is changed.
+               $this->assertSame( 0, get_site_option( 'can_compress_scripts' ) );
+       }
+
+       /**
+        * Undo gzencode.  This is ugly, but there's no stock gzdecode() function.
+        *
+        * @param string $encoded_data
+        * @return string
+        */
+       protected function _gzdecode( $encoded_data ) {
+
+               // Save the encoded data to a temp file.
+               $file = wp_tempnam( 'gzdecode' );
+               file_put_contents( $file, $encoded_data );
+
+               // Flush it to the output buffer and delete the temp file.
+               ob_start();
+               readgzfile( $file );
+               unlink( $file );
+
+               // Save the data stop buffering.
+               $data = ob_get_clean();
+
+               // Done.
+               return $data;
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxWpPrivacyErasePersonalDataphpfromrev54721trunktestsphpunittestsajaxPrivacyErasePersonalDataphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxWpPrivacyErasePersonalData.php (from rev 54721, trunk/tests/phpunit/tests/ajax/PrivacyErasePersonalData.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxWpPrivacyErasePersonalData.php                               (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxWpPrivacyErasePersonalData.php 2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,839 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Testing Ajax handler for erasing personal data.
+ *
+ * @package WordPress\UnitTests
+ * @since 5.2.0
+ */
+
+/**
+ * Tests_Ajax_PrivacyExportPersonalData class.
+ *
+ * @since 5.2.0
+ *
+ * @group ajax
+ * @group privacy
+ *
+ * @covers ::wp_ajax_wp_privacy_erase_personal_data
+ */
+class Tests_Ajax_wpAjaxWpPrivacyErasePersonalData extends WP_Ajax_UnitTestCase {
+
+       /**
+        * User Request ID.
+        *
+        * @since 5.2.0
+        *
+        * @var int $request_id
+        */
+       protected static $request_id;
+
+       /**
+        * User Request Email.
+        *
+        * @since 5.2.0
+        *
+        * @var string $request_email
+        */
+       protected static $request_email;
+
+       /**
+        * Ajax Action.
+        *
+        * @since 5.2.0
+        *
+        * @var string $action
+        */
+       protected static $action;
+
+       /**
+        * Eraser Index.
+        *
+        * @since 5.2.0
+        *
+        * @var int $eraser
+        */
+       protected static $eraser;
+
+       /**
+        * Eraser Key.
+        *
+        * @since 5.2.0
+        *
+        * @var string $eraser_key
+        */
+       protected static $eraser_key;
+
+       /**
+        * Eraser Friendly Name.
+        *
+        * @since 5.2.0
+        *
+        * @var string $eraser_friendly_name
+        */
+       protected static $eraser_friendly_name;
+
+       /**
+        * Page Index.
+        *
+        * @since 5.2.0
+        *
+        * @var int $page
+        */
+       protected static $page;
+
+       /**
+        * Last response parsed.
+        *
+        * @since 5.2.0
+        *
+        * @var array $_last_response_parsed
+        */
+       protected $_last_response_parsed;
+
+       /**
+        * An array key in the test eraser to unset.
+        *
+        * @since 5.2.0
+        *
+        * @var string $key_to_unset
+        */
+       protected $key_to_unset;
+
+       /**
+        * A value to change the test eraser callback to.
+        *
+        * @since 5.2.0
+        *
+        * @var string $new_callback_value
+        */
+       protected $new_callback_value;
+
+       /**
+        * Create user erase request fixtures.
+        *
+        * @param WP_UnitTest_Factory $factory Factory.
+        */
+       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+               self::$request_email        = 'requester@example.com';
+               self::$request_id           = wp_create_user_request( self::$request_email, 'remove_personal_data' );
+               self::$action               = 'wp-privacy-erase-personal-data';
+               self::$eraser               = 1;
+               self::$eraser_key           = 'custom-eraser';
+               self::$eraser_friendly_name = 'Custom Eraser';
+               self::$page                 = 1;
+       }
+
+       /**
+        * Register a custom personal data eraser.
+        */
+       public function set_up() {
+               parent::set_up();
+
+               $this->key_to_unset = '';
+
+               // Make sure the erasers response is not modified and avoid sending emails.
+               remove_all_filters( 'wp_privacy_personal_data_erasure_page' );
+               remove_all_actions( 'wp_privacy_personal_data_erased' );
+
+               // Only use our custom privacy personal data eraser.
+               remove_all_filters( 'wp_privacy_personal_data_erasers' );
+               add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_custom_personal_data_eraser' ) );
+
+               $this->_setRole( 'administrator' );
+               // `erase_others_personal_data` meta cap in Multisite installation is only granted to those with `manage_network` capability.
+               if ( is_multisite() ) {
+                       grant_super_admin( get_current_user_id() );
+               }
+       }
+
+       /**
+        * Clean up after each test method.
+        */
+       public function tear_down() {
+               remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_custom_personal_data_eraser' ) );
+               $this->new_callback_value = '';
+
+               if ( is_multisite() ) {
+                       revoke_super_admin( get_current_user_id() );
+               }
+
+               parent::tear_down();
+       }
+
+       /**
+        * Helper method for changing the test eraser's callback function.
+        *
+        * @param string|array $callback New test eraser callback index value.
+        */
+       protected function _set_eraser_callback( $callback ) {
+               $this->new_callback_value = $callback;
+               add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_callback_value' ), 20 );
+       }
+
+       /**
+        * Change the test eraser callback to a specified value.
+        *
+        * @since 5.2.0
+        *
+        * @param array $erasers List of data erasers.
+        *
+        * @return array Array of data erasers.
+        */
+       public function filter_eraser_callback_value( $erasers ) {
+               $erasers[ self::$eraser_key ]['callback'] = $this->new_callback_value;
+
+               return $erasers;
+       }
+
+       /**
+        * Helper method for unsetting an array index in the test eraser.
+        *
+        * @param string|bool $key Test eraser key to unset.
+        */
+       protected function _unset_eraser_key( $key ) {
+               $this->key_to_unset = $key;
+               add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_unset_eraser_index' ), 20 );
+       }
+
+       /**
+        * Unsets an array key in the test eraser.
+        *
+        * If the key is false, the eraser is set to false.
+        *
+        * @since 5.2.0
+        *
+        * @param array $erasers Erasers.
+        *
+        * @return array Erasers.
+        */
+       public function filter_unset_eraser_index( $erasers ) {
+               if ( false === $this->key_to_unset ) {
+                       $erasers[ self::$eraser_key ] = false;
+               } elseif ( ! empty( $this->key_to_unset ) ) {
+                       unset( $erasers[ self::$eraser_key ][ $this->key_to_unset ] );
+               }
+
+               return $erasers;
+       }
+
+       /**
+        * Helper method for erasing a key from the eraser response.
+        *
+        * @since 5.2.0
+        *
+        * @param array $key Response key to unset.
+        */
+       protected function _unset_response_key( $key ) {
+               $this->key_to_unset = $key;
+               $this->_set_eraser_callback( array( $this, 'filter_unset_response_index' ) );
+       }
+
+       /**
+        * Unsets an array index in a response.
+        *
+        * @since 5.2.0
+        *
+        * @param string $email_address The requester's email address.
+        * @param int    $page          Page number.
+        *
+        * @return array Export data.
+        */
+       public function filter_unset_response_index( $email_address, $page = 1 ) {
+               $response = $this->callback_personal_data_eraser( $email_address, $page );
+
+               if ( ! empty( $this->key_to_unset ) ) {
+                       unset( $response[ $this->key_to_unset ] );
+               }
+
+               return $response;
+       }
+
+       /**
+        * The function should send an error when the request ID is missing.
+        *
+        * @since 5.2.0
+        *
+        * @ticket 43438
+        */
+       public function test_error_when_missing_request_id() {
+               $this->assertNotWPError( self::$request_id );
+
+               // Set up a request.
+               $this->_make_ajax_call(
+                       array(
+                               'id' => null, // Missing request ID.
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the request ID is less than 1.
+        *
+        * @since 5.2.0
+        *
+        * @ticket 43438
+        */
+       public function test_error_when_request_id_invalid() {
+               $this->assertNotWPError( self::$request_id );
+
+               // Set up a request.
+               $this->_make_ajax_call(
+                       array(
+                               'id' => -1, // Invalid request ID.
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the current user is missing required capabilities.
+        *
+        * @since 5.2.0
+        *
+        * @ticket 43438
+        */
+       public function test_error_when_current_user_missing_required_capabilities() {
+               $this->_setRole( 'author' );
+
+               $this->assertFalse( current_user_can( 'erase_others_personal_data' ) );
+               $this->assertFalse( current_user_can( 'delete_users' ) );
+
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Sorry, you are not allowed to perform this action.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * Test requests do not succeed on multisite when the current user is not a network admin.
+        *
+        * @ticket 43438
+        * @group multisite
+        * @group ms-required
+        */
+       public function test_error_when_current_user_missing_required_capabilities_multisite() {
+               revoke_super_admin( get_current_user_id() );
+
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Sorry, you are not allowed to perform this action.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the nonce does not validate.
+        *
+        * @since 5.2.0
+        */
+       public function test_failure_with_invalid_nonce() {
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+
+               $this->_make_ajax_call(
+                       array(
+                               'security' => 'invalid-nonce',
+                       )
+               );
+       }
+
+       /**
+        * The function should send an error when the request type is incorrect.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_incorrect_request_type() {
+               $request_id = wp_create_user_request(
+                       'export-request@example.com',
+                       'export_personal_data' // Incorrect request type, expects 'remove_personal_data'.
+               );
+
+               $this->_make_ajax_call(
+                       array(
+                               'security' => wp_create_nonce( 'wp-privacy-erase-personal-data-' . $request_id ),
+                               'id'       => $request_id,
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the request email is invalid.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_invalid_email() {
+               wp_update_post(
+                       array(
+                               'ID'         => self::$request_id,
+                               'post_title' => '', // Invalid requester's email address.
+                       )
+               );
+
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Invalid email address in request.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the eraser index is missing.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_missing_eraser_index() {
+               $this->_make_ajax_call(
+                       array(
+                               'eraser' => null, // Missing eraser index.
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Missing eraser index.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the page index is missing.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_missing_page_index() {
+               $this->_make_ajax_call(
+                       array(
+                               'page' => null, // Missing page index.
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the eraser index is negative.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_negative_eraser_index() {
+               $this->_make_ajax_call(
+                       array(
+                               'eraser' => -1, // Negative eraser index.
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Eraser index cannot be less than one.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the eraser index is out of range.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_eraser_index_out_of_range() {
+               $this->_make_ajax_call(
+                       array(
+                               'eraser' => PHP_INT_MAX, // Out of range eraser index.
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Eraser index is out of range.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the page index is less than one.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_page_index_less_than_one() {
+               $this->_make_ajax_call(
+                       array(
+                               'page' => 0, // Page index less than one.
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when an eraser is not an array.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_eraser_not_array() {
+               $this->_unset_eraser_key( false );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Expected an array describing the eraser at index %s.',
+                               self::$eraser
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * The function should send an error when an eraser is missing a friendly name.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_eraser_missing_friendly_name() {
+               $this->_unset_eraser_key( 'eraser_friendly_name' );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Eraser array at index %s does not include a friendly name.',
+                               self::$eraser
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * The function should send an error when an eraser is missing a callback.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_eraser_missing_callback() {
+               $this->_unset_eraser_key( 'callback' );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Eraser does not include a callback: %s.',
+                               self::$eraser_friendly_name
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * The function should send an error when an eraser, at a given index, has an invalid callback.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_eraser_index_invalid_callback() {
+               $this->_set_eraser_callback( false );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Eraser callback is not valid: %s.',
+                               self::$eraser_friendly_name
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * The function should send an error when an eraser, at a given index, is missing an array response.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_eraser_index_invalid_response() {
+               $this->_set_eraser_callback( '__return_null' );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Did not receive array from %1$s eraser (index %2$d).',
+                               self::$eraser_friendly_name,
+                               self::$eraser
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * The function should send an error when missing an items_removed index.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_eraser_items_removed_missing() {
+               $this->_unset_response_key( 'items_removed' );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Expected items_removed key in response array from %1$s eraser (index %2$d).',
+                               self::$eraser_friendly_name,
+                               self::$eraser
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * The function should send an error when missing an items_retained index.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_eraser_items_retained_missing() {
+               $this->_unset_response_key( 'items_retained' );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Expected items_retained key in response array from %1$s eraser (index %2$d).',
+                               self::$eraser_friendly_name,
+                               self::$eraser
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * The function should send an error when missing a messages index.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_eraser_messages_missing() {
+               $this->_unset_response_key( 'messages' );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Expected messages key in response array from %1$s eraser (index %2$d).',
+                               self::$eraser_friendly_name,
+                               self::$eraser
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * The function should send an error when the messages index is not an array.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_eraser_messages_not_array() {
+               $this->_set_eraser_callback( array( $this, 'filter_response_messages_invalid' ) );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Expected messages key to reference an array in response array from %1$s eraser (index %2$d).',
+                               self::$eraser_friendly_name,
+                               self::$eraser
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * Change the messages index to an invalid value (not an array).
+        *
+        * @since 5.2.0
+        *
+        * @param string $email_address The requester's email address.
+        * @param int    $page          Page number.
+        *
+        * @return array Export data.
+        */
+       public function filter_response_messages_invalid( $email_address, $page = 1 ) {
+               $response             = $this->callback_personal_data_eraser( $email_address, $page );
+               $response['messages'] = true;
+
+               return $response;
+       }
+
+       /**
+        * The function should send an error when an eraser is missing 'done' in array response.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_eraser_missing_done_response() {
+               $this->_unset_response_key( 'done' );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Expected done flag in response array from %1$s eraser (index %2$d).',
+                               self::$eraser_friendly_name,
+                               self::$eraser
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * The function should successfully send erasers response data when the current user has the required
+        * capabilities.
+        *
+        * @since 5.2.0
+        *
+        * @ticket 43438
+        */
+       public function test_success_when_current_user_has_required_capabilities() {
+               $this->assertTrue( current_user_can( 'erase_others_personal_data' ) );
+               $this->assertTrue( current_user_can( 'delete_users' ) );
+
+               $this->_make_ajax_call();
+
+               $this->assertSame(
+                       sprintf( 'A message regarding retained data for %s.', self::$request_email ),
+                       $this->_last_response_parsed['data']['messages'][0]
+               );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertTrue( $this->_last_response_parsed['data']['items_removed'] );
+               $this->assertTrue( $this->_last_response_parsed['data']['items_retained'] );
+               $this->assertTrue( $this->_last_response_parsed['data']['done'] );
+       }
+
+       /**
+        * The function should successfully send erasers response data when no items to erase.
+        *
+        * @since 5.2.0
+        *
+        * @ticket 43438
+        */
+       public function test_success_when_no_items_to_erase() {
+
+               $this->_make_ajax_call( array( 'page' => 2 ) );
+
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertFalse( $this->_last_response_parsed['data']['items_removed'] );
+               $this->assertFalse( $this->_last_response_parsed['data']['items_retained'] );
+               $this->assertEmpty( $this->_last_response_parsed['data']['messages'] );
+               $this->assertTrue( $this->_last_response_parsed['data']['done'] );
+       }
+
+       /**
+        * Test that the function's output should be filterable with the `wp_privacy_personal_data_erasure_page` filter.
+        *
+        * @since 5.2.0
+        */
+       public function test_output_should_be_filterable() {
+               add_filter( 'wp_privacy_personal_data_erasure_page', array( $this, 'filter_eraser_data_response' ), 20, 6 );
+               $this->_make_ajax_call();
+
+               $expected_new_index = self::$request_email . '-' . self::$request_id . '-' . self::$eraser_key;
+
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'filtered removed', $this->_last_response_parsed['data']['items_removed'] );
+               $this->assertSame( 'filtered retained', $this->_last_response_parsed['data']['items_retained'] );
+               $this->assertSame( array( 'filtered messages' ), $this->_last_response_parsed['data']['messages'] );
+               $this->assertSame( 'filtered done', $this->_last_response_parsed['data']['done'] );
+               $this->assertSame( $expected_new_index, $this->_last_response_parsed['data']['new_index'] );
+       }
+
+       /**
+        * Filters the eraser response.
+        *
+        * @since 5.2.0
+        *
+        * @param array  $response        The personal data for the given eraser and page.
+        * @param int    $eraser_index    The index of the eraser that provided this data.
+        * @param string $email_address   The email address associated with this personal data.
+        * @param int    $page            The page for this response.
+        * @param int    $request_id      The privacy request post ID associated with this request.
+        * @param string $eraser_key      The key (slug) of the eraser that provided this data.
+        *
+        * @return array Filtered erase response.
+        */
+       public function filter_eraser_data_response( $response, $eraser_index, $email_address, $page, $request_id, $eraser_key ) {
+               $response['items_removed']  = 'filtered removed';
+               $response['items_retained'] = 'filtered retained';
+               $response['messages']       = array( 'filtered messages' );
+               $response['done']           = 'filtered done';
+               $response['new_index']      = $email_address . '-' . $request_id . '-' . $eraser_key;
+
+               return $response;
+       }
+
+       /**
+        * Register handler for a custom personal data eraser.
+        *
+        * @since 5.2.0
+        *
+        * @param array $erasers An array of personal data erasers.
+        *
+        * @return array An array of personal data erasers.
+        */
+       public function register_custom_personal_data_eraser( $erasers ) {
+               $erasers[ self::$eraser_key ] = array(
+                       'eraser_friendly_name' => self::$eraser_friendly_name,
+                       'callback'             => array( $this, 'callback_personal_data_eraser' ),
+               );
+               return $erasers;
+       }
+
+       /**
+        * Custom Personal Data Eraser.
+        *
+        * @since 5.2.0
+        *
+        * @param  string $email_address The comment author email address.
+        * @param  int    $page          Page number.
+        *
+        * @return array Erase data.
+        */
+       public function callback_personal_data_eraser( $email_address, $page = 1 ) {
+               if ( 1 === $page ) {
+                       return array(
+                               'items_removed'  => true,
+                               'items_retained' => true,
+                               'messages'       => array( sprintf( 'A message regarding retained data for %s.', $email_address ) ),
+                               'done'           => true,
+                       );
+               }
+
+               return array(
+                       'items_removed'  => false,
+                       'items_retained' => false,
+                       'messages'       => array(),
+                       'done'           => true,
+               );
+       }
+
+       /**
+        * Helper function for Ajax handler.
+        *
+        * @since 5.2.0
+        *
+        * @param array $args Ajax request arguments.
+        */
+       protected function _make_ajax_call( $args = array() ) {
+               $this->_last_response_parsed = null;
+               $this->_last_response        = '';
+
+               $defaults = array(
+                       'action'   => self::$action,
+                       'security' => wp_create_nonce( self::$action . '-' . self::$request_id ),
+                       'page'     => self::$page,
+                       'id'       => self::$request_id,
+                       'eraser'   => self::$eraser,
+               );
+
+               $_POST = wp_parse_args( $args, $defaults );
+
+               try {
+                       $this->_handleAjax( self::$action );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               if ( $this->_last_response ) {
+                       $this->_last_response_parsed = json_decode( $this->_last_response, true );
+               }
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpAjaxWpPrivacyExportPersonalDataphpfromrev54721trunktestsphpunittestsajaxPrivacyExportPersonalDataphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpAjaxWpPrivacyExportPersonalData.php (from rev 54721, trunk/tests/phpunit/tests/ajax/PrivacyExportPersonalData.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpAjaxWpPrivacyExportPersonalData.php                              (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpAjaxWpPrivacyExportPersonalData.php        2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,842 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Testing Ajax handler for exporting personal data.
+ *
+ * @package WordPress\UnitTests
+ * @since 5.2.0
+ */
+
+/**
+ * Tests_Ajax_PrivacyExportPersonalData class.
+ *
+ * @since 5.2.0
+ *
+ * @group ajax
+ * @group privacy
+ *
+ * @covers ::wp_ajax_wp_privacy_export_personal_data
+ */
+class Tests_Ajax_wpAjaxWpPrivacyExportPersonalData extends WP_Ajax_UnitTestCase {
+
+       /**
+        * User Request ID.
+        *
+        * @since 5.2.0
+        *
+        * @var int $request_id
+        */
+       protected static $request_id;
+
+       /**
+        * User Request Email.
+        *
+        * @since 5.2.0
+        *
+        * @var string $request_email
+        */
+       protected static $request_email;
+
+       /**
+        * Ajax Action.
+        *
+        * @since 5.2.0
+        *
+        * @var string $action
+        */
+       protected static $action;
+
+       /**
+        * Exporter Index.
+        *
+        * @since 5.2.0
+        *
+        * @var int $exporter
+        */
+       protected static $exporter;
+
+       /**
+        * Exporter Key.
+        *
+        * @since 5.2.0
+        *
+        * @var string $exporter_key
+        */
+       protected static $exporter_key;
+
+       /**
+        * Exporter Friendly Name.
+        *
+        * @since 5.2.0
+        *
+        * @var string $exporter_friendly_name
+        */
+       protected static $exporter_friendly_name;
+
+       /**
+        * Page Index.
+        *
+        * @since 5.2.0
+        *
+        * @var int $page
+        */
+       protected static $page;
+
+       /**
+        * Send As Email.
+        *
+        * @since 5.2.0
+        *
+        * @var bool $send_as_email
+        */
+       protected static $send_as_email;
+
+       /**
+        * Last response parsed.
+        *
+        * @since 5.2.0
+        *
+        * @var array $_last_response_parsed
+        */
+       protected $_last_response_parsed;
+
+       /**
+        * An array key in the test exporter to unset.
+        *
+        * @since 5.2.0
+        *
+        * @var string $key_to_unset
+        */
+       protected $key_to_unset;
+
+       /**
+        * A value to change the test exporter callback to.
+        *
+        * @since 5.2.0
+        *
+        * @var string $new_callback_value
+        */
+       protected $new_callback_value;
+
+       /**
+        * Create user export request fixtures.
+        *
+        * @since 5.2.0
+        *
+        * @param WP_UnitTest_Factory $factory Factory.
+        */
+       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+               self::$request_email          = 'requester@example.com';
+               self::$request_id             = wp_create_user_request( self::$request_email, 'export_personal_data' );
+               self::$action                 = 'wp-privacy-export-personal-data';
+               self::$exporter               = 1;
+               self::$exporter_key           = 'custom-exporter';
+               self::$exporter_friendly_name = 'Custom Exporter';
+               self::$page                   = 1;
+               self::$send_as_email          = false;
+       }
+
+       /**
+        * Setup before each test method.
+        *
+        * @since 5.2.0
+        */
+       public function set_up() {
+               parent::set_up();
+
+               $this->key_to_unset       = '';
+               $this->new_callback_value = '';
+
+               // Make sure the exporter response is not modified and avoid e.g. writing export file to disk.
+               remove_all_filters( 'wp_privacy_personal_data_export_page' );
+
+               // Only use our custom privacy personal data exporter.
+               remove_all_filters( 'wp_privacy_personal_data_exporters' );
+               add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) );
+
+               $this->_setRole( 'administrator' );
+               // `export_others_personal_data` meta cap in Multisite installation is only granted to those with `manage_network` capability.
+               if ( is_multisite() ) {
+                       grant_super_admin( get_current_user_id() );
+               }
+       }
+
+       /**
+        * Clean up after each test method.
+        */
+       public function tear_down() {
+               remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) );
+
+               if ( is_multisite() ) {
+                       revoke_super_admin( get_current_user_id() );
+               }
+               parent::tear_down();
+       }
+
+       /**
+        * Helper method for changing the test exporter's callback function.
+        *
+        * @param string|array $callback New test exporter callback function.
+        */
+       protected function _set_exporter_callback( $callback ) {
+               $this->new_callback_value = $callback;
+               add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_value' ), 20 );
+       }
+
+       /**
+        * Change the test exporter callback to a specified value.
+        *
+        * @since 5.2.0
+        *
+        * @param array $exporters List of data exporters.
+        * @return array List of data exporters.
+        */
+       public function filter_exporter_callback_value( $exporters ) {
+               $exporters[ self::$exporter_key ]['callback'] = $this->new_callback_value;
+
+               return $exporters;
+       }
+
+       /**
+        * Helper method for unsetting an array index in the test exporter.
+        *
+        * @param string $key Test exporter key to unset.
+        */
+       protected function _unset_exporter_key( $key ) {
+               $this->key_to_unset = $key;
+               add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_unset_exporter_key' ), 20 );
+       }
+
+       /**
+        * Unset a specified key in the test exporter array.
+        *
+        * @param array $exporters List of data exporters.
+        *
+        * @return array List of data exporters.
+        */
+       public function filter_unset_exporter_key( $exporters ) {
+               if ( false === $this->key_to_unset ) {
+                       $exporters[ self::$exporter_key ] = false;
+               } elseif ( ! empty( $this->key_to_unset ) ) {
+                       unset( $exporters[ self::$exporter_key ][ $this->key_to_unset ] );
+               }
+
+               return $exporters;
+       }
+
+       /**
+        * The function should send an error when the request ID is missing.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_missing_request_id() {
+               $this->_make_ajax_call(
+                       array(
+                               'id' => null, // Missing request ID.
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the request ID is less than 1.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_invalid_id() {
+               $this->_make_ajax_call(
+                       array(
+                               'id' => -1, // Invalid request ID, less than 1.
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the current user is missing the required capability.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_current_user_missing_required_capability() {
+               $this->_setRole( 'author' );
+
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertFalse( current_user_can( 'export_others_personal_data' ) );
+               $this->assertSame( 'Sorry, you are not allowed to perform this action.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * Test requests do not succeed on multisite when the current user is not a network admin.
+        *
+        * @ticket 43438
+        * @group multisite
+        * @group ms-required
+        */
+       public function test_error_when_current_user_missing_required_capability_multisite() {
+               revoke_super_admin( get_current_user_id() );
+
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Sorry, you are not allowed to perform this action.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the nonce does not validate.
+        *
+        * @since 5.2.0
+        */
+       public function test_failure_with_invalid_nonce() {
+               $this->expectException( 'WPAjaxDieStopException' );
+               $this->expectExceptionMessage( '-1' );
+
+               $this->_make_ajax_call(
+                       array(
+                               'security' => 'invalid-nonce',
+                       )
+               );
+       }
+
+       /**
+        * The function should send an error when the request type is incorrect.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_incorrect_request_type() {
+               $request_id = wp_create_user_request(
+                       'erase-request@example.com',
+                       'remove_personal_data' // Incorrect request type, expects 'export_personal_data'.
+               );
+
+               $this->_make_ajax_call(
+                       array(
+                               'security' => wp_create_nonce( 'wp-privacy-export-personal-data-' . $request_id ),
+                               'id'       => $request_id,
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the requester's email address is invalid.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_invalid_email_address() {
+               wp_update_post(
+                       array(
+                               'ID'         => self::$request_id,
+                               'post_title' => '', // Invalid requester's email address.
+                       )
+               );
+
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'A valid email address must be given.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the exporter index is missing.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_missing_exporter_index() {
+               $this->_make_ajax_call(
+                       array(
+                               'exporter' => null, // Missing exporter index.
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Missing exporter index.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the page index is missing.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_missing_page_index() {
+               $this->_make_ajax_call(
+                       array(
+                               'page' => null, // Missing page index.
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when an exporter has improperly used the `wp_privacy_personal_data_exporters` filter.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_exporter_has_improperly_used_exporters_filter() {
+               // Improper filter usage: returns false instead of an expected array.
+               add_filter( 'wp_privacy_personal_data_exporters', '__return_false', 999 );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'An exporter has improperly used the registration filter.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the exporter index is negative.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_negative_exporter_index() {
+               $this->_make_ajax_call(
+                       array(
+                               'exporter' => -1, // Negative exporter index.
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Exporter index cannot be negative.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the exporter index is out of range.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_exporter_index_out_of_range() {
+               $this->_make_ajax_call(
+                       array(
+                               'exporter' => PHP_INT_MAX, // Out of range exporter index.
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Exporter index is out of range.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when the page index is less than one.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_page_index_less_than_one() {
+               $this->_make_ajax_call(
+                       array(
+                               'page' => 0, // Page index less than one.
+                       )
+               );
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * The function should send an error when an exporter is not an array.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_exporter_not_array() {
+               $this->_unset_exporter_key( false );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Expected an array describing the exporter at index %s.',
+                               self::$exporter_key
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * The function should send an error when an exporter is missing a friendly name.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_exporter_missing_friendly_name() {
+               $this->_unset_exporter_key( 'exporter_friendly_name' );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Exporter array at index %s does not include a friendly name.',
+                               self::$exporter_key
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * The function should send an error when an exporter is missing a callback.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_exporter_missing_callback() {
+               $this->_unset_exporter_key( 'callback' );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Exporter does not include a callback: %s.',
+                               self::$exporter_friendly_name
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * The function should send an error when an exporter, at a given index, has an invalid callback.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_exporter_index_invalid_callback() {
+               $this->_set_exporter_callback( false );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Exporter callback is not a valid callback: %s.',
+                               self::$exporter_friendly_name
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * When an exporter callback returns a WP_Error, it should be passed as the error.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_exporter_callback_returns_wp_error() {
+               $this->_set_exporter_callback( array( $this, 'callback_return_wp_error' ) );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'passed_message', $this->_last_response_parsed['data'][0]['code'] );
+               $this->assertSame( 'This is a WP_Error message.', $this->_last_response_parsed['data'][0]['message'] );
+       }
+
+       /**
+        * Callback for exporter's response.
+        *
+        * @since 5.2.0
+        *
+        * @param string $email_address The requester's email address.
+        * @param int    $page          Page number.
+        * @return WP_Error WP_Error instance.
+        */
+       public function callback_return_wp_error( $email_address, $page = 1 ) {
+               return new WP_Error( 'passed_message', 'This is a WP_Error message.' );
+       }
+
+       /**
+        * The function should send an error when an exporter, at a given index, is missing an array response.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_exporter_index_invalid_response() {
+               $this->_set_exporter_callback( '__return_null' );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Expected response as an array from exporter: %s.',
+                               self::$exporter_friendly_name
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * The function should send an error when an exporter is missing data in array response.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_exporter_missing_data_response() {
+               $this->_set_exporter_callback( array( $this, 'callback_missing_data_response' ) );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Expected data in response array from exporter: %s.',
+                               self::$exporter_friendly_name
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * Callback for exporter's response.
+        *
+        * @since 5.2.0
+        *
+        * @param string $email_address The requester's email address.
+        * @param int    $page          Page number.
+        *
+        * @return array Export data.
+        */
+       public function callback_missing_data_response( $email_address, $page = 1 ) {
+               $response = $this->callback_custom_personal_data_exporter( $email_address, $page );
+               unset( $response['data'] ); // Missing data part of response.
+
+               return $response;
+       }
+
+       /**
+        * The function should send an error when an exporter is missing 'data' array in array response.
+        *
+        * @since 5.2.0
+        */
+       public function test_function_should_error_when_exporter_missing_data_array_response() {
+               $this->_set_exporter_callback( array( $this, 'callback_missing_data_array_response' ) );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Expected data array in response array from exporter: %s.',
+                               self::$exporter_friendly_name
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * Callback for exporter's response.
+        *
+        * @since 5.2.0
+        *
+        * @param  string $email_address The requester's email address.
+        * @param  int    $page          Page number.
+        *
+        * @return array Export data.
+        */
+       public function callback_missing_data_array_response( $email_address, $page = 1 ) {
+               $response         = $this->callback_custom_personal_data_exporter( $email_address, $page );
+               $response['data'] = false; // Not an array.
+               return $response;
+       }
+
+       /**
+        * The function should send an error when an exporter is missing 'done' in array response.
+        *
+        * @since 5.2.0
+        */
+       public function test_error_when_exporter_missing_done_response() {
+               $this->_set_exporter_callback( array( $this, 'callback_missing_done_response' ) );
+               $this->_make_ajax_call();
+
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame(
+                       sprintf(
+                               'Expected done (boolean) in response array from exporter: %s.',
+                               self::$exporter_friendly_name
+                       ),
+                       $this->_last_response_parsed['data']
+               );
+       }
+
+       /**
+        * Remove the response's done flag.
+        *
+        * @since 5.2.0
+        *
+        * @param string $email_address The requester's email address.
+        * @param int    $page          Page number.
+        *
+        * @return array Export data.
+        */
+       public function callback_missing_done_response( $email_address, $page = 1 ) {
+               $response = $this->callback_custom_personal_data_exporter( $email_address, $page );
+               unset( $response['done'] );
+
+               return $response;
+       }
+
+       /**
+        * The function should successfully send exporter data response when the current user has the required capability.
+        *
+        * @since 5.2.0
+        */
+       public function test_succeeds_when_current_user_has_required_capability() {
+               $this->assertTrue( current_user_can( 'export_others_personal_data' ) );
+
+               $this->_make_ajax_call();
+
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'custom-exporter-item-id', $this->_last_response_parsed['data']['data']['item_id'] );
+               $this->assertSame( 'Email', $this->_last_response_parsed['data']['data']['data'][0]['name'] );
+               $this->assertSame( self::$request_email, $this->_last_response_parsed['data']['data']['data'][0]['value'] );
+       }
+
+       /**
+        * The function should successfully send exporter data response when no items to export.
+        *
+        * @since 5.2.0
+        */
+       public function test_success_when_no_items_to_export() {
+
+               $this->_make_ajax_call( array( 'page' => 2 ) );
+
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertEmpty( $this->_last_response_parsed['data']['data'] );
+               $this->assertTrue( $this->_last_response_parsed['data']['done'] );
+       }
+
+       /**
+        * The function's output should be filterable with the `wp_privacy_personal_data_export_page` filter.
+        *
+        * @since 5.2.0
+        */
+       public function test_output_should_be_filterable() {
+               add_filter( 'wp_privacy_personal_data_export_page', array( $this, 'filter_exporter_data_response' ), 20, 7 );
+               $this->_make_ajax_call();
+
+               $expected_group_label = sprintf(
+                       '%s-%s-%s-%s-%s-%s',
+                       self::$exporter,
+                       self::$page,
+                       self::$request_email,
+                       self::$request_id,
+                       self::$send_as_email,
+                       self::$exporter_key
+               );
+
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertSame( $expected_group_label, $this->_last_response_parsed['data']['group_label'] );
+               $this->assertSame( 'filtered_group_id', $this->_last_response_parsed['data']['group_id'] );
+               $this->assertSame( 'filtered_item_id', $this->_last_response_parsed['data']['item_id'] );
+               $this->assertSame( 'filtered_name', $this->_last_response_parsed['data']['data'][0]['name'] );
+               $this->assertSame( 'filtered_value', $this->_last_response_parsed['data']['data'][0]['value'] );
+       }
+
+       /**
+        * Filter exporter's data response.
+        *
+        * @since 5.2.0
+        *
+        * @param array  $response        The personal data for the given exporter and page.
+        * @param int    $exporter_index  The index of the exporter that provided this data.
+        * @param string $email_address   The email address associated with this personal data.
+        * @param int    $page            The page for this response.
+        * @param int    $request_id      The privacy request post ID associated with this request.
+        * @param bool   $send_as_email   Whether the final results of the export should be emailed to the user.
+        * @param string $exporter_key    The key (slug) of the exporter that provided this data.
+        *
+        * @return array The personal data for the given exporter and page.
+        */
+       public function filter_exporter_data_response( $response, $exporter_index, $email_address, $page, $request_id, $send_as_email, $exporter_key ) {
+               $group_label                  = sprintf(
+                       '%s-%s-%s-%s-%s-%s',
+                       $exporter_index,
+                       $page,
+                       $email_address,
+                       $request_id,
+                       $send_as_email,
+                       $exporter_key
+               );
+               $response['group_label']      = $group_label;
+               $response['group_id']         = 'filtered_group_id';
+               $response['item_id']          = 'filtered_item_id';
+               $response['data'][0]['name']  = 'filtered_name';
+               $response['data'][0]['value'] = 'filtered_value';
+
+               return $response;
+       }
+
+       /**
+        * Filter to register a custom personal data exporter.
+        *
+        * @since 5.2.0
+        *
+        * @param array $exporters An array of personal data exporters.
+        *
+        * @return array An array of personal data exporters.
+        */
+       public function filter_register_custom_personal_data_exporter( $exporters ) {
+               $exporters[ self::$exporter_key ] = array(
+                       'exporter_friendly_name' => self::$exporter_friendly_name,
+                       'callback'               => array( $this, 'callback_custom_personal_data_exporter' ),
+               );
+               return $exporters;
+       }
+
+       /**
+        * Callback for a custom personal data exporter.
+        *
+        * @since 5.2.0
+        *
+        * @param string $email_address The requester's email address.
+        * @param int    $page          Page number.
+        *
+        * @return array Export data response.
+        */
+       public function callback_custom_personal_data_exporter( $email_address, $page = 1 ) {
+               $data_to_export = array();
+
+               if ( 1 === $page ) {
+                       $data_to_export = array(
+                               'group_id'    => self::$exporter_key . '-group-id',
+                               'group_label' => self::$exporter_key . '-group-label',
+                               'item_id'     => self::$exporter_key . '-item-id',
+                               'data'        => array(
+                                       array(
+                                               'name'  => 'Email',
+                                               'value' => $email_address,
+                                       ),
+                               ),
+                       );
+               }
+
+               return array(
+                       'data' => $data_to_export,
+                       'done' => true,
+               );
+       }
+
+       /**
+        * Helper function for Ajax handler.
+        *
+        * @since 5.2.0
+        *
+        * @param array $args Ajax request arguments.
+        */
+       protected function _make_ajax_call( $args = array() ) {
+               $this->_last_response_parsed = null;
+               $this->_last_response        = '';
+
+               $defaults = array(
+                       'action'      => self::$action,
+                       'security'    => wp_create_nonce( self::$action . '-' . self::$request_id ),
+                       'exporter'    => self::$exporter,
+                       'page'        => self::$page,
+                       'sendAsEmail' => self::$send_as_email,
+                       'id'          => self::$request_id,
+               );
+
+               $_POST = wp_parse_args( $args, $defaults );
+
+               try {
+                       $this->_handleAjax( self::$action );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+
+               if ( $this->_last_response ) {
+                       $this->_last_response_parsed = json_decode( $this->_last_response, true );
+               }
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpCustomizeManagerphpfromrev54721trunktestsphpunittestsajaxCustomizeManagerphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpCustomizeManager.php (from rev 54721, trunk/tests/phpunit/tests/ajax/CustomizeManager.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpCustomizeManager.php                             (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpCustomizeManager.php       2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,769 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Testing Ajax customize manager functionality.
+ *
+ * @package WordPress
+ * @subpackage UnitTests
+ * @since 4.3.0
+ *
+ * @group ajax
+ */
+class Tests_Ajax_wpCustomizeManager extends WP_Ajax_UnitTestCase {
+
+       /**
+        * Instance of WP_Customize_Manager which is reset for each test.
+        *
+        * @var WP_Customize_Manager
+        */
+       public $wp_customize;
+
+       /**
+        * Admin user ID.
+        *
+        * @var int
+        */
+       protected static $admin_user_id;
+
+       /**
+        * Subscriber user ID.
+        *
+        * @var int
+        */
+       protected static $subscriber_user_id;
+
+       /**
+        * Last response parsed.
+        *
+        * @var array|null
+        */
+       protected $_last_response_parsed;
+
+       /**
+        * Set up before class.
+        *
+        * @param WP_UnitTest_Factory $factory Factory.
+        */
+       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+               self::$subscriber_user_id = $factory->user->create( array( 'role' => 'subscriber' ) );
+               self::$admin_user_id      = $factory->user->create( array( 'role' => 'administrator' ) );
+       }
+
+       /**
+        * Set up the test fixture.
+        */
+       public function set_up() {
+               parent::set_up();
+               require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
+       }
+
+       /**
+        * Tear down.
+        */
+       public function tear_down() {
+               $_REQUEST = array();
+               parent::tear_down();
+       }
+
+       /**
+        * Helper to keep it DRY
+        *
+        * @param string $action Action.
+        */
+       protected function make_ajax_call( $action ) {
+               $this->_last_response_parsed = null;
+               $this->_last_response        = '';
+               try {
+                       $this->_handleAjax( $action );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+               if ( $this->_last_response ) {
+                       $this->_last_response_parsed = json_decode( $this->_last_response, true );
+               }
+       }
+
+       /**
+        * Overridden caps for user_has_cap.
+        *
+        * @var array
+        */
+       protected $overridden_caps = array();
+
+       /**
+        * Dynamically filter a user's capabilities.
+        *
+        * @param array $allcaps An array of all the user's capabilities.
+        * @return array All caps.
+        */
+       public function filter_user_has_cap( $allcaps ) {
+               $allcaps = array_merge( $allcaps, $this->overridden_caps );
+               return $allcaps;
+       }
+
+       /**
+        * Test WP_Customize_Manager::save().
+        *
+        * @ticket 30937
+        *
+        * @covers WP_Customize_Manager::save
+        */
+       public function test_save_failures() {
+               global $wp_customize;
+               $wp_customize = new WP_Customize_Manager();
+               $wp_customize->register_controls();
+               add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ) );
+
+               // Unauthenticated.
+               wp_set_current_user( 0 );
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'unauthenticated', $this->_last_response_parsed['data'] );
+
+               // Unauthorized.
+               wp_set_current_user( self::$subscriber_user_id );
+               $nonce             = wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() );
+               $_POST['nonce']    = $nonce;
+               $_GET['nonce']     = $nonce;
+               $_REQUEST['nonce'] = $nonce;
+               $exception         = null;
+               try {
+                       ob_start();
+                       $wp_customize->setup_theme();
+               } catch ( WPAjaxDieContinueException $e ) {
+                       $exception = $e;
+               }
+               $this->assertNotEmpty( $e );
+               $this->assertSame( '-1', $e->getMessage() );
+
+               // Not called setup_theme.
+               wp_set_current_user( self::$admin_user_id );
+               $nonce             = wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() );
+               $_POST['nonce']    = $nonce;
+               $_GET['nonce']     = $nonce;
+               $_REQUEST['nonce'] = $nonce;
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'not_preview', $this->_last_response_parsed['data'] );
+
+               // Bad nonce.
+               $_POST['nonce']    = 'bad';
+               $_GET['nonce']     = 'bad';
+               $_REQUEST['nonce'] = 'bad';
+               $wp_customize->setup_theme();
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'invalid_nonce', $this->_last_response_parsed['data'] );
+
+               // User cannot create.
+               $nonce                            = wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() );
+               $_POST['nonce']                   = $nonce;
+               $_GET['nonce']                    = $nonce;
+               $_REQUEST['nonce']                = $nonce;
+               $post_type_obj                    = get_post_type_object( 'customize_changeset' );
+               $post_type_obj->cap->create_posts = 'create_customize_changesets';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'cannot_create_changeset_post', $this->_last_response_parsed['data'] );
+               $this->overridden_caps[ $post_type_obj->cap->create_posts ] = true;
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $post_type_obj->cap->create_posts = 'customize'; // Restore.
+
+               // Changeset already published.
+               $wp_customize->set_post_value( 'blogname', 'Hello' );
+               $wp_customize->save_changeset_post( array( 'status' => 'publish' ) );
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'changeset_already_published', $this->_last_response_parsed['data']['code'] );
+               wp_update_post(
+                       array(
+                               'ID'          => $wp_customize->changeset_post_id(),
+                               'post_status' => 'auto-draft',
+                       )
+               );
+
+               // User cannot edit.
+               $post_type_obj                 = get_post_type_object( 'customize_changeset' );
+               $post_type_obj->cap->edit_post = 'edit_customize_changesets';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'cannot_edit_changeset_post', $this->_last_response_parsed['data'] );
+               $this->overridden_caps[ $post_type_obj->cap->edit_post ] = true;
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $post_type_obj->cap->edit_post = 'customize'; // Restore.
+
+               // Bad customize_changeset_data.
+               $_POST['customize_changeset_data'] = '[MALFORMED]';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'invalid_customize_changeset_data', $this->_last_response_parsed['data'] );
+
+               // Bad customize_changeset_status.
+               $_POST['customize_changeset_data']   = '{}';
+               $_POST['customize_changeset_status'] = 'unrecognized';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'bad_customize_changeset_status', $this->_last_response_parsed['data'] );
+
+               // Disallowed publish posts if not allowed.
+               $post_type_obj                       = get_post_type_object( 'customize_changeset' );
+               $post_type_obj->cap->publish_posts   = 'publish_customize_changesets';
+               $_POST['customize_changeset_status'] = 'publish';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'changeset_publish_unauthorized', $this->_last_response_parsed['data'] );
+               $_POST['customize_changeset_status'] = 'future';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'changeset_publish_unauthorized', $this->_last_response_parsed['data'] );
+               $post_type_obj->cap->publish_posts = 'customize'; // Restore.
+
+               // Validate date.
+               $_POST['customize_changeset_status'] = 'draft';
+               $_POST['customize_changeset_date']   = 'BAD DATE';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'bad_customize_changeset_date', $this->_last_response_parsed['data'] );
+               $_POST['customize_changeset_date'] = '2010-01-01 00:00:00';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'not_future_date', $this->_last_response_parsed['data']['code'] );
+               $_POST['customize_changeset_date'] = ( gmdate( 'Y' ) + 1 ) . '-01-01 00:00:00';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $_POST['customize_changeset_status'] = 'future';
+               $_POST['customize_changeset_date']   = '+10 minutes';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'future', get_post_status( $wp_customize->changeset_post_id() ) );
+               wp_update_post(
+                       array(
+                               'ID'          => $wp_customize->changeset_post_id(),
+                               'post_status' => 'auto-draft',
+                       )
+               );
+       }
+
+       /**
+        * Set up valid user state.
+        *
+        * @param string $uuid Changeset UUID.
+        * @return WP_Customize_Manager
+        */
+       protected function set_up_valid_state( $uuid = null ) {
+               global $wp_customize;
+               wp_set_current_user( self::$admin_user_id );
+               $wp_customize = new WP_Customize_Manager(
+                       array(
+                               'changeset_uuid' => $uuid,
+                       )
+               );
+               $wp_customize->register_controls();
+               $nonce             = wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() );
+               $_POST['nonce']    = $nonce;
+               $_GET['nonce']     = $nonce;
+               $_REQUEST['nonce'] = $nonce;
+               $wp_customize->setup_theme();
+               return $wp_customize;
+       }
+
+       /**
+        * Test WP_Customize_Manager::save().
+        *
+        * @ticket 30937
+        *
+        * @covers WP_Customize_Manager::save
+        */
+       public function test_save_success_publish_create() {
+               $wp_customize = $this->set_up_valid_state();
+
+               $_POST['customize_changeset_status'] = 'publish';
+               $_POST['customize_changeset_title']  = 'Success Changeset';
+               $_POST['customize_changeset_data']   = wp_json_encode(
+                       array(
+                               'blogname' => array(
+                                       'value' => 'Successful Site Title',
+                               ),
+                       )
+               );
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertIsArray( $this->_last_response_parsed['data'] );
+
+               $this->assertSame( 'publish', $this->_last_response_parsed['data']['changeset_status'] );
+               $this->assertArrayHasKey( 'next_changeset_uuid', $this->_last_response_parsed['data'] );
+               $this->assertTrue( wp_is_uuid( $this->_last_response_parsed['data']['next_changeset_uuid'], 4 ) );
+               $this->assertSame( 'Success Changeset', get_post( $wp_customize->changeset_post_id() )->post_title );
+               $this->assertSame( 'Successful Site Title', get_option( 'blogname' ) );
+       }
+
+       /**
+        * Test WP_Customize_Manager::save().
+        *
+        * @ticket 30937
+        *
+        * @covers WP_Customize_Manager::save
+        */
+       public function test_save_success_publish_edit() {
+               $uuid = wp_generate_uuid4();
+
+               $post_id      = self::factory()->post->create(
+                       array(
+                               'post_name'    => $uuid,
+                               'post_title'   => 'Original',
+                               'post_type'    => 'customize_changeset',
+                               'post_status'  => 'auto-draft',
+                               'post_content' => wp_json_encode(
+                                       array(
+                                               'blogname' => array(
+                                                       'value' => 'New Site Title',
+                                               ),
+                                       )
+                               ),
+                       )
+               );
+               $wp_customize = $this->set_up_valid_state( $uuid );
+
+               $_POST['customize_changeset_status'] = 'publish';
+               $_POST['customize_changeset_title']  = 'Published';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertIsArray( $this->_last_response_parsed['data'] );
+
+               $this->assertSame( 'publish', $this->_last_response_parsed['data']['changeset_status'] );
+               $this->assertArrayHasKey( 'next_changeset_uuid', $this->_last_response_parsed['data'] );
+               $this->assertTrue( wp_is_uuid( $this->_last_response_parsed['data']['next_changeset_uuid'], 4 ) );
+               $this->assertSame( 'New Site Title', get_option( 'blogname' ) );
+               $this->assertSame( 'Published', get_post( $post_id )->post_title );
+       }
+
+       /**
+        * Test WP_Customize_Manager::save().
+        *
+        * @ticket 38943
+        *
+        * @covers WP_Customize_Manager::save
+        */
+       public function test_success_save_post_date() {
+               $uuid         = wp_generate_uuid4();
+               $post_id      = self::factory()->post->create(
+                       array(
+                               'post_name'    => $uuid,
+                               'post_title'   => 'Original',
+                               'post_type'    => 'customize_changeset',
+                               'post_status'  => 'auto-draft',
+                               'post_content' => wp_json_encode(
+                                       array(
+                                               'blogname' => array(
+                                                       'value' => 'New Site Title',
+                                               ),
+                                       )
+                               ),
+                       )
+               );
+               $wp_customize = $this->set_up_valid_state( $uuid );
+
+               // Success future schedule date.
+               $future_date                         = ( gmdate( 'Y' ) + 1 ) . '-01-01 00:00:00';
+               $_POST['customize_changeset_status'] = 'future';
+               $_POST['customize_changeset_title']  = 'Future date';
+               $_POST['customize_changeset_date']   = $future_date;
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertArrayHasKey( 'changeset_date', $this->_last_response_parsed['data'] );
+               $changeset_post_schedule = get_post( $post_id );
+               $this->assertSame( $future_date, $changeset_post_schedule->post_date );
+
+               // Success future changeset change to draft keeping existing date.
+               unset( $_POST['customize_changeset_date'] );
+               $_POST['customize_changeset_status'] = 'draft';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertArrayNotHasKey( 'changeset_date', $this->_last_response_parsed['data'] );
+               $changeset_post_draft = get_post( $post_id );
+               $this->assertSame( $future_date, $changeset_post_draft->post_date );
+
+               // Success if date is not passed with schedule changeset and stored changeset have future date.
+               $_POST['customize_changeset_status'] = 'future';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertArrayHasKey( 'changeset_date', $this->_last_response_parsed['data'] );
+               $changeset_post_schedule = get_post( $post_id );
+               $this->assertSame( $future_date, $changeset_post_schedule->post_date );
+               // Success if draft with past date.
+               $now = current_time( 'mysql' );
+               wp_update_post(
+                       array(
+                               'ID'            => $post_id,
+                               'post_status'   => 'draft',
+                               'post_date'     => $now,
+                               'post_date_gmt' => get_gmt_from_date( $now ),
+                       )
+               );
+
+               // Fail if future request and existing date is past.
+               $_POST['customize_changeset_status'] = 'future';
+               unset( $_POST['customize_changeset_date'] );
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'not_future_date', $this->_last_response_parsed['data']['code'] );
+
+               // Success publish changeset reset date to current.
+               wp_update_post(
+                       array(
+                               'ID'            => $post_id,
+                               'post_status'   => 'future',
+                               'post_date'     => $future_date,
+                               'post_date_gmt' => get_gmt_from_date( $future_date ),
+                       )
+               );
+               unset( $_POST['customize_changeset_date'] );
+               $_POST['customize_changeset_status'] = 'publish';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertArrayHasKey( 'next_changeset_uuid', $this->_last_response_parsed['data'] );
+               $this->assertTrue( wp_is_uuid( $this->_last_response_parsed['data']['next_changeset_uuid'], 4 ) );
+               $changeset_post_publish = get_post( $post_id );
+               $this->assertNotEquals( $future_date, $changeset_post_publish->post_date );
+
+               // Check response when trying to update an already-published post.
+               $this->assertSame( 'trash', get_post_status( $post_id ) );
+               $_POST['customize_changeset_status'] = 'publish';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'changeset_already_published', $this->_last_response_parsed['data']['code'] );
+               $this->assertArrayHasKey( 'next_changeset_uuid', $this->_last_response_parsed['data'] );
+               $this->assertTrue( wp_is_uuid( $this->_last_response_parsed['data']['next_changeset_uuid'], 4 ) );
+       }
+
+       /**
+        * Test WP_Customize_Manager::save().
+        *
+        * @ticket 39896
+        *
+        * @covers WP_Customize_Manager::save
+        */
+       public function test_save_autosave() {
+               $uuid = wp_generate_uuid4();
+
+               $post_id = self::factory()->post->create(
+                       array(
+                               'post_name'    => $uuid,
+                               'post_type'    => 'customize_changeset',
+                               'post_status'  => 'draft',
+                               'post_content' => wp_json_encode(
+                                       array(
+                                               'blogname' => array(
+                                                       'value' => 'New Site Title',
+                                               ),
+                                       )
+                               ),
+                       )
+               );
+               $this->set_up_valid_state( $uuid );
+
+               $this->assertFalse( wp_get_post_autosave( $post_id ) );
+
+               $_POST['customize_changeset_data'] = wp_json_encode(
+                       array(
+                               'blogname' => array(
+                                       'value' => 'Autosaved Site Title',
+                               ),
+                       )
+               );
+
+               $_POST['customize_changeset_autosave'] = 'on';
+               $this->make_ajax_call( 'customize_save' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'draft', $this->_last_response_parsed['data']['changeset_status'] );
+               $autosave_revision = wp_get_post_autosave( $post_id );
+               $this->assertInstanceOf( 'WP_Post', $autosave_revision );
+
+               $this->assertStringContainsString( 'New Site Title', get_post( $post_id )->post_content );
+               $this->assertStringContainsString( 'Autosaved Site Title', $autosave_revision->post_content );
+       }
+
+       /**
+        * Test request for trashing a changeset.
+        *
+        * @ticket 39896
+        *
+        * @covers WP_Customize_Manager::handle_changeset_trash_request
+        */
+       public function test_handle_changeset_trash_request() {
+               $uuid         = wp_generate_uuid4();
+               $wp_customize = $this->set_up_valid_state( $uuid );
+
+               $this->make_ajax_call( 'customize_trash' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'invalid_nonce', $this->_last_response_parsed['data']['code'] );
+
+               $nonce             = wp_create_nonce( 'trash_customize_changeset' );
+               $_POST['nonce']    = $nonce;
+               $_GET['nonce']     = $nonce;
+               $_REQUEST['nonce'] = $nonce;
+               $this->make_ajax_call( 'customize_trash' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'non_existent_changeset', $this->_last_response_parsed['data']['code'] );
+
+               $wp_customize->register_controls(); // And settings too.
+               $wp_customize->set_post_value( 'blogname', 'HELLO' );
+               $wp_customize->save_changeset_post(
+                       array(
+                               'status' => 'save',
+                       )
+               );
+
+               add_filter( 'map_meta_cap', array( $this, 'return_do_not_allow' ) );
+               $this->make_ajax_call( 'customize_trash' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'changeset_trash_unauthorized', $this->_last_response_parsed['data']['code'] );
+               remove_filter( 'map_meta_cap', array( $this, 'return_do_not_allow' ) );
+
+               $lock_user_id  = static::factory()->user->create( array( 'role' => 'administrator' ) );
+               $previous_user = get_current_user_id();
+               wp_set_current_user( $lock_user_id );
+               $wp_customize->set_changeset_lock( $wp_customize->changeset_post_id() );
+               wp_set_current_user( $previous_user );
+               $this->make_ajax_call( 'customize_trash' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'changeset_locked', $this->_last_response_parsed['data']['code'] );
+               delete_post_meta( $wp_customize->changeset_post_id(), '_edit_lock' );
+
+               wp_update_post(
+                       array(
+                               'ID'          => $wp_customize->changeset_post_id(),
+                               'post_status' => 'trash',
+                       )
+               );
+               $this->make_ajax_call( 'customize_trash' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'changeset_already_trashed', $this->_last_response_parsed['data']['code'] );
+
+               wp_update_post(
+                       array(
+                               'ID'          => $wp_customize->changeset_post_id(),
+                               'post_status' => 'draft',
+                       )
+               );
+
+               $wp_trash_post_count = did_action( 'wp_trash_post' );
+               add_filter( 'pre_trash_post', '__return_false' );
+               $this->make_ajax_call( 'customize_trash' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'changeset_trash_failure', $this->_last_response_parsed['data']['code'] );
+               remove_filter( 'pre_trash_post', '__return_false' );
+               $this->assertSame( $wp_trash_post_count, did_action( 'wp_trash_post' ) );
+
+               $wp_trash_post_count = did_action( 'wp_trash_post' );
+               $this->assertSame( 'draft', get_post_status( $wp_customize->changeset_post_id() ) );
+               $this->make_ajax_call( 'customize_trash' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'trash', get_post_status( $wp_customize->changeset_post_id() ) );
+               $this->assertSame( $wp_trash_post_count + 1, did_action( 'wp_trash_post' ) );
+       }
+
+       /**
+        * Return caps array containing 'do_not_allow'.
+        *
+        * @return array Caps.
+        */
+       public function return_do_not_allow() {
+               return array( 'do_not_allow' );
+       }
+
+       /**
+        * Test request for dismissing autosave changesets.
+        *
+        * @ticket 39896
+        * @covers WP_Customize_Manager::handle_dismiss_autosave_or_lock_request
+        * @covers WP_Customize_Manager::dismiss_user_auto_draft_changesets
+        */
+       public function test_handle_dismiss_autosave_or_lock_request() {
+               $uuid          = wp_generate_uuid4();
+               $wp_customize  = $this->set_up_valid_state( $uuid );
+               $valid_user_id = get_current_user_id();
+
+               // Temporarily remove user to test requirement that user is logged in. See #42450.
+               wp_set_current_user( 0 );
+               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'unauthenticated', $this->_last_response_parsed['data'] );
+               wp_set_current_user( $valid_user_id );
+
+               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'invalid_nonce', $this->_last_response_parsed['data'] );
+
+               $nonce             = wp_create_nonce( 'customize_dismiss_autosave_or_lock' );
+               $_POST['nonce']    = $nonce;
+               $_GET['nonce']     = $nonce;
+               $_REQUEST['nonce'] = $nonce;
+
+               $_POST['dismiss_lock']    = true;
+               $_GET['dismiss_lock']     = true;
+               $_REQUEST['dismiss_lock'] = true;
+               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'no_changeset_to_dismiss_lock', $this->_last_response_parsed['data'] );
+
+               $_POST['dismiss_autosave']    = true;
+               $_GET['dismiss_autosave']     = true;
+               $_REQUEST['dismiss_autosave'] = true;
+               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'no_auto_draft_to_delete', $this->_last_response_parsed['data'] );
+
+               $other_user_id = self::factory()->user->create();
+
+               // Create auto-drafts.
+               $user_auto_draft_ids = array();
+               for ( $i = 0; $i < 3; $i++ ) {
+                       $user_auto_draft_ids[] = self::factory()->post->create(
+                               array(
+                                       'post_name'    => wp_generate_uuid4(),
+                                       'post_type'    => 'customize_changeset',
+                                       'post_status'  => 'auto-draft',
+                                       'post_author'  => self::$admin_user_id,
+                                       'post_content' => wp_json_encode( array() ),
+                               )
+                       );
+               }
+               $other_user_auto_draft_ids = array();
+               for ( $i = 0; $i < 3; $i++ ) {
+                       $other_user_auto_draft_ids[] = self::factory()->post->create(
+                               array(
+                                       'post_name'    => wp_generate_uuid4(),
+                                       'post_type'    => 'customize_changeset',
+                                       'post_status'  => 'auto-draft',
+                                       'post_author'  => $other_user_id,
+                                       'post_content' => wp_json_encode( array() ),
+                               )
+                       );
+               }
+               foreach ( array_merge( $user_auto_draft_ids, $other_user_auto_draft_ids ) as $post_id ) {
+                       $this->assertFalse( (bool) get_post_meta( $post_id, '_customize_restore_dismissed', true ) );
+               }
+               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'auto_draft_dismissed', $this->_last_response_parsed['data'] );
+               foreach ( $user_auto_draft_ids as $post_id ) {
+                       $this->assertSame( 'auto-draft', get_post_status( $post_id ) );
+                       $this->assertTrue( (bool) get_post_meta( $post_id, '_customize_restore_dismissed', true ) );
+               }
+               foreach ( $other_user_auto_draft_ids as $post_id ) {
+                       $this->assertSame( 'auto-draft', get_post_status( $post_id ) );
+                       $this->assertFalse( (bool) get_post_meta( $post_id, '_customize_restore_dismissed', true ) );
+               }
+
+               // Subsequent test results in none dismissed.
+               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'no_auto_draft_to_delete', $this->_last_response_parsed['data'] );
+
+               // Save a changeset as a draft.
+               $r = $wp_customize->save_changeset_post(
+                       array(
+                               'data'   => array(
+                                       'blogname' => array(
+                                               'value' => 'Foo',
+                                       ),
+                               ),
+                               'status' => 'draft',
+                       )
+               );
+
+               $_POST['dismiss_autosave']    = false;
+               $_GET['dismiss_autosave']     = false;
+               $_REQUEST['dismiss_autosave'] = false;
+               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'changeset_lock_dismissed', $this->_last_response_parsed['data'] );
+
+               $_POST['dismiss_autosave']    = true;
+               $_GET['dismiss_autosave']     = true;
+               $_REQUEST['dismiss_autosave'] = true;
+               $this->assertNotWPError( $r );
+               $this->assertFalse( wp_get_post_autosave( $wp_customize->changeset_post_id() ) );
+               $this->assertStringContainsString( 'Foo', get_post( $wp_customize->changeset_post_id() )->post_content );
+
+               // Since no autosave yet, confirm no action.
+               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'no_autosave_revision_to_delete', $this->_last_response_parsed['data'] );
+
+               // Add the autosave revision.
+               $r = $wp_customize->save_changeset_post(
+                       array(
+                               'data'     => array(
+                                       'blogname' => array(
+                                               'value' => 'Bar',
+                                       ),
+                               ),
+                               'autosave' => true,
+                       )
+               );
+               $this->assertNotWPError( $r );
+               $autosave_revision = wp_get_post_autosave( $wp_customize->changeset_post_id() );
+               $this->assertInstanceOf( 'WP_Post', $autosave_revision );
+               $this->assertStringContainsString( 'Foo', get_post( $wp_customize->changeset_post_id() )->post_content );
+               $this->assertStringContainsString( 'Bar', $autosave_revision->post_content );
+
+               // Confirm autosave gets deleted.
+               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
+               $this->assertTrue( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'autosave_revision_deleted', $this->_last_response_parsed['data'] );
+               $this->assertFalse( wp_get_post_autosave( $wp_customize->changeset_post_id() ) );
+
+               // Since no autosave yet, confirm no action.
+               $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
+               $this->assertFalse( $this->_last_response_parsed['success'] );
+               $this->assertSame( 'no_autosave_revision_to_delete', $this->_last_response_parsed['data'] );
+       }
+
+       /**
+        * Test request for retrieving installed themes.
+        *
+        * @ticket 54549
+        * @covers WP_Customize_Manager::handle_load_themes_request
+        */
+       public function test_wp_ajax_customize_load_themes_action() {
+               $arguments = array(
+                       'changeset_uuid'     => false,
+                       'settings_previewed' => true,
+                       'branching'          => false,
+               );
+               new WP_Customize_Manager( $arguments );
+               wp_set_current_user( self::$admin_user_id );
+               $nonce                 = wp_create_nonce( 'switch_themes' );
+               $_POST['nonce']        = $nonce;
+               $_GET['nonce']         = $nonce;
+               $_REQUEST['nonce']     = $nonce;
+               $_POST['theme_action'] = 'installed';
+               $this->make_ajax_call( 'customize_load_themes' );
+               $response = $this->_last_response_parsed;
+               $this->assertIsArray( $response, 'Response is not an array' );
+
+               $this->assertArrayHasKey( 'success', $response, 'Response must have a "success" key' );
+               $this->assertTrue( $response['success'], 'Response was not "success"' );
+
+               $this->assertArrayHasKey( 'data', $response, 'Response must have a "data" key' );
+               $this->assertIsArray( $response['data'], 'The response "data" is not an array' );
+               $this->assertArrayHasKey( 'themes', $response['data'], 'The response data must have a "themes" key' );
+               $this->assertIsArray( $response['data']['themes'], 'Themes data is not an array' );
+               $this->assertNotEmpty( $response['data']['themes'], 'Themes data must not be empty' );
+
+               foreach ( $response['data']['themes'] as $theme ) {
+                       $this->assertIsArray( $theme, 'Theme is not an array' );
+                       $this->assertNotEmpty( $theme, 'Theme data must not be empty' );
+                       $this->assertArrayHasKey( 'id', $theme, 'Theme data must have an "id" key' );
+                       $this->assertNotEmpty( $theme['id'], 'Theme id cannot be empty' );
+
+                       $this->assertArrayHasKey( 'name', $theme, 'Theme data must have a "name" key' );
+                       $this->assertNotEmpty( $theme['name'], 'Theme name cannot be empty' );
+
+                       $this->assertArrayHasKey( 'blockTheme', $theme, 'Themes data must include information about blocks support' );
+               }
+       }
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestsajaxwpCustomizeNavMenusphpfromrev54721trunktestsphpunittestsajaxCustomizeMenusphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/tests/phpunit/tests/ajax/wpCustomizeNavMenus.php (from rev 54721, trunk/tests/phpunit/tests/ajax/CustomizeMenus.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/wpCustomizeNavMenus.php                            (rev 0)
+++ trunk/tests/phpunit/tests/ajax/wpCustomizeNavMenus.php      2022-10-30 01:05:06 UTC (rev 54722)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,800 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Testing Ajax customize menus functionality.
+ *
+ * @package WordPress
+ * @subpackage UnitTests
+ * @since 4.3.0
+ *
+ * @group ajax
+ */
+class Tests_Ajax_wpCustomizeNavMenus extends WP_Ajax_UnitTestCase {
+
+       /**
+        * Instance of WP_Customize_Manager which is reset for each test.
+        *
+        * @var WP_Customize_Manager
+        */
+       public $wp_customize;
+
+       /**
+        * Page IDs.
+        *
+        * @var int[]
+        */
+       public static $pages;
+
+       /**
+        * Post IDs.
+        *
+        * @var int[]
+        */
+       public static $posts;
+
+       /**
+        * Term IDs.
+        *
+        * @var int[]
+        */
+       public static $terms;
+
+       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+               // Make some post objects.
+               self::$posts = $factory->post->create_many( 5 );
+               self::$pages = $factory->post->create_many( 5, array( 'post_type' => 'page' ) );
+
+               // Some terms too.
+               self::$terms = $factory->term->create_many( 5 );
+       }
+
+       /**
+        * Set up the test fixture.
+        */
+       public function set_up() {
+               parent::set_up();
+               require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
+               wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
+               global $wp_customize;
+               $this->wp_customize = new WP_Customize_Manager();
+               $wp_customize       = $this->wp_customize;
+       }
+
+       /**
+        * Helper to keep it DRY
+        *
+        * @param string $action Action.
+        */
+       protected function make_ajax_call( $action ) {
+               // Make the request.
+               try {
+                       $this->_handleAjax( $action );
+               } catch ( WPAjaxDieContinueException $e ) {
+                       unset( $e );
+               }
+       }
+
+       /**
+        * Testing capabilities check for ajax_load_available_items method
+        *
+        * @dataProvider data_ajax_load_available_items_cap_check
+        *
+        * @covers WP_Customize_Nav_Menus::ajax_load_available_items
+        *
+        * @param string $role              The role we're checking caps against.
+        * @param array  $expected_results  Expected results.
+        */
+       public function test_ajax_load_available_items_cap_check( $role, $expected_results ) {
+
+               if ( 'administrator' !== $role ) {
+                       // If we're not an admin, we should get a wp_die( -1 ).
+                       $this->expectException( 'WPAjaxDieStopException' );
+                       $this->expectExceptionMessage( '-1' );
+               }
+
+               wp_set_current_user( self::factory()->user->create( array( 'role' => $role ) ) );
+
+               $_POST = array(
+                       'action'                => 'load-available-menu-items-customizer',
+                       'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+               );
+
+               $this->make_ajax_call( 'load-available-menu-items-customizer' );
+
+               // If we are an admin, we should get a proper response.
+               if ( 'administrator' === $role ) {
+                       // Get the results.
+                       $response = json_decode( $this->_last_response, true );
+
+                       $this->assertSame( $expected_results, $response );
+               }
+
+       }
+
+       /**
+        * Data provider for test_ajax_load_available_items_cap_check().
+        *
+        * Provides various post_args to induce error messages in the that can be
+        * compared to the expected_results.
+        *
+        * @since 4.3.0
+        *
+        * @return array {
+        *     @type array {
+        *         @string string $role             The role that will test caps for.
+        *         @array  array  $expected_results The expected results from the Ajax call.
+        *     }
+        * }
+        */
+       public function data_ajax_load_available_items_cap_check() {
+               return array(
+                       array(
+                               'subscriber',
+                               array(),
+                       ),
+                       array(
+                               'contributor',
+                               array(),
+                       ),
+                       array(
+                               'author',
+                               array(),
+                       ),
+                       array(
+                               'editor',
+                               array(),
+                       ),
+                       array(
+                               'administrator',
+                               array(
+                                       'success' => false,
+                                       'data'    => 'nav_menus_missing_type_or_object_parameter',
+                               ),
+                       ),
+               );
+       }
+
+       /**
+        * Testing the error messaging for ajax_load_available_items
+        *
+        * @dataProvider data_ajax_load_available_items_error_messages
+        *
+        * @covers WP_Customize_Nav_Menus::ajax_load_available_items
+        *
+        * @param array $post_args POST args.
+        * @param mixed $expected_results Expected results.
+        */
+       public function test_ajax_load_available_items_error_messages( $post_args, $expected_results ) {
+
+               $_POST = array_merge(
+                       array(
+                               'action'                => 'load-available-menu-items-customizer',
+                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                       ),
+                       $post_args
+               );
+
+               // Make the request.
+               $this->make_ajax_call( 'load-available-menu-items-customizer' );
+
+               // Get the results.
+               $response = json_decode( $this->_last_response, true );
+
+               $this->assertSame( $expected_results, $response );
+       }
+
+       /**
+        * Data provider for test_ajax_load_available_items_error_message().
+        *
+        * Provides various post_args to induce error messages in the that can be
+        * compared to the expected_results.
+        *
+        * @since 4.3.0
+        *
+        * @return array {
+        *     @type array {
+        * @array array $post_args        The arguments that will merged with the $_POST array.
+        * @array array $expected_results The expected results from the Ajax call.
+        *     }
+        * }
+        */
+       public function data_ajax_load_available_items_error_messages() {
+               return array(
+                       // Testing empty obj_type and type.
+                       array(
+                               array(
+                                       'type'   => '',
+                                       'object' => '',
+                               ),
+                               array(
+                                       'success' => false,
+                                       'data'    => 'nav_menus_missing_type_or_object_parameter',
+                               ),
+                       ),
+                       // Testing empty obj_type.
+                       array(
+                               array(
+                                       'type'   => 'post_type',
+                                       'object' => '',
+                               ),
+                               array(
+                                       'success' => false,
+                                       'data'    => 'nav_menus_missing_type_or_object_parameter',
+                               ),
+                       ),
+                       // Testing empty type.
+                       array(
+                               array(
+                                       'type'   => '',
+                                       'object' => 'post',
+                               ),
+                               array(
+                                       'success' => false,
+                                       'data'    => 'nav_menus_missing_type_or_object_parameter',
+                               ),
+                       ),
+                       // Testing empty type of a bulk request.
+                       array(
+                               array(
+                                       'item_types' => array(
+                                               array(
+                                                       'type'   => 'post_type',
+                                                       'object' => 'post',
+                                               ),
+                                               array(
+                                                       'type'   => 'post_type',
+                                                       'object' => '',
+                                               ),
+                                       ),
+                               ),
+                               array(
+                                       'success' => false,
+                                       'data'    => 'nav_menus_missing_type_or_object_parameter',
+                               ),
+                       ),
+                       // Testing incorrect type option.
+                       array(
+                               array(
+                                       'type'   => 'post_type',
+                                       'object' => 'invalid',
+                               ),
+                               array(
+                                       'success' => false,
+                                       'data'    => 'nav_menus_invalid_post_type',
+                               ),
+                       ),
+               );
+       }
+
+       /**
+        * Testing the success status.
+        *
+        * @dataProvider data_ajax_load_available_items_success_status
+        *
+        * @covers WP_Customize_Nav_Menus::ajax_load_available_items
+        *
+        * @param array $post_args       POST args.
+        * @param array $success_status  Success status.
+        */
+       public function test_ajax_load_available_items_success_status( $post_args, $success_status ) {
+
+               $_POST = array_merge(
+                       array(
+                               'action'                => 'load-available-menu-items-customizer',
+                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                       ),
+                       $post_args
+               );
+
+               // Make the request.
+               $this->make_ajax_call( 'load-available-menu-items-customizer' );
+
+               // Get the results.
+               $response = json_decode( $this->_last_response, true );
+               $this->assertSame( $success_status, $response['success'] );
+
+       }
+
+       /**
+        * Data provider for test_ajax_load_available_items_success_status().
+        *
+        * Provides various post_args to retrieve results and compare against
+        * the success status.
+        *
+        * @since 4.3.0
+        *
+        * @return array {
+        *     @type array {
+        *         @type array $post_args      The arguments that will merged with the $_POST array.
+        *         @type bool  $success_status The expected success status.
+        *     }
+        * }
+        */
+       public function data_ajax_load_available_items_success_status() {
+               return array(
+                       array(
+                               array(
+                                       'type'   => 'post_type',
+                                       'object' => 'post',
+                               ),
+                               true,
+                       ),
+                       array(
+                               array(
+                                       'type'   => 'post_type',
+                                       'object' => 'page',
+                               ),
+                               true,
+                       ),
+                       array(
+                               array(
+                                       'type'   => 'post_type',
+                                       'object' => 'custom',
+                               ),
+                               false,
+                       ),
+                       array(
+                               array(
+                                       'type'   => 'taxonomy',
+                                       'object' => 'post_tag',
+                               ),
+                               true,
+                       ),
+                       // Testing a bulk request.
+                       array(
+                               array(
+                                       'item_types' => array(
+                                               array(
+                                                       'type'   => 'post_type',
+                                                       'object' => 'post',
+                                               ),
+                                               array(
+                                                       'type'   => 'post_type',
+                                                       'object' => 'page',
+                                               ),
+                                       ),
+                               ),
+                               true,
+                       ),
+               );
+       }
+
+       /**
+        * Testing the array structure for a single item
+        *
+        * @dataProvider data_ajax_load_available_items_structure
+        *
+        * @covers WP_Customize_Nav_Menus::ajax_load_available_items
+        *
+        * @param array $post_args POST args.
+        */
+       public function test2_ajax_load_available_items_structure( $post_args ) {
+               do_action( 'customize_register', $this->wp_customize );
+
+               $expected_keys = array(
+                       'id',
+                       'title',
+                       'type',
+                       'type_label',
+                       'object',
+                       'object_id',
+                       'url',
+               );
+
+               $auto_draft_post = $this->wp_customize->nav_menus->insert_auto_draft_post(
+                       array(
+                               'post_title' => 'Test Auto Draft',
+                               'post_type'  => 'post',
+                       )
+               );
+               $this->wp_customize->set_post_value( 'nav_menus_created_posts', array( $auto_draft_post->ID ) );
+               $this->wp_customize->get_setting( 'nav_menus_created_posts' )->preview();
+
+               $_POST = array_merge(
+                       array(
+                               'action'                => 'load-available-menu-items-customizer',
+                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                       ),
+                       $post_args
+               );
+
+               // Make the request.
+               $this->make_ajax_call( 'load-available-menu-items-customizer' );
+
+               // Get the results.
+               $response = json_decode( $this->_last_response, true );
+
+               $this->assertNotEmpty( current( $response['data']['items'] ) );
+
+               // Get the second index to avoid the home page edge case.
+               $first_prop = current( $response['data']['items'] );
+               $test_item  = $first_prop[1];
+
+               foreach ( $expected_keys as $key ) {
+                       $this->assertArrayHasKey( $key, $test_item );
+                       $this->assertNotEmpty( $test_item[ $key ] );
+               }
+
+               // Special test for the home page.
+               if ( 'page' === $test_item['object'] ) {
+                       $first_prop = current( $response['data']['items'] );
+                       $home       = $first_prop[0];
+                       foreach ( $expected_keys as $key ) {
+                               if ( 'object_id' !== $key ) {
+                                       $this->assertArrayHasKey( $key, $home );
+                                       if ( 'object' !== $key ) {
+                                               $this->assertNotEmpty( $home[ $key ] );
+                                       }
+                               }
+                       }
+               } elseif ( 'post' === $test_item['object'] ) {
+                       $item_ids = wp_list_pluck( $response['data']['items']['post_type:post'], 'id' );
+                       $this->assertContains( 'post-' . $auto_draft_post->ID, $item_ids );
+               }
+       }
+
+       /**
+        * Data provider for test_ajax_load_available_items_structure().
+        *
+        * Provides various post_args to return a list of items to test the array structure of.
+        *
+        * @since 4.3.0
+        *
+        * @return array {
+        *     @type array {
+        *         @type array $post_args The arguments that will merged with the $_POST array.
+        *     }
+        * }
+        */
+       public function data_ajax_load_available_items_structure() {
+               return array(
+                       array(
+                               array(
+                                       'type'   => 'post_type',
+                                       'object' => 'post',
+                               ),
+                       ),
+                       array(
+                               array(
+                                       'type'   => 'post_type',
+                                       'object' => 'page',
+                               ),
+                       ),
+                       array(
+                               array(
+                                       'type'   => 'taxonomy',
+                                       'object' => 'post_tag',
+                               ),
+                       ),
+               );
+       }
+
+       /**
+        * Testing the error messages for ajax_search_available_items
+        *
+        * @dataProvider data_ajax_search_available_items_caps_check
+        *
+        * @covers WP_Customize_Nav_Menus::ajax_search_available_items
+        * @covers WP_Customize_Nav_Menus::search_available_items_query
+        *
+        * @param string $role             Role.
+        * @param array  $expected_results Expected results.
+        */
+       public function test_ajax_search_available_items_caps_check( $role, $expected_results ) {
+
+               if ( 'administrator' !== $role ) {
+                       // If we're not an admin, we should get a wp_die( -1 ).
+                       $this->expectException( 'WPAjaxDieStopException' );
+                       $this->expectExceptionMessage( '-1' );
+               }
+
+               wp_set_current_user( self::factory()->user->create( array( 'role' => $role ) ) );
+
+               $_POST = array(
+                       'action'                => 'search-available-menu-items-customizer',
+                       'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+               );
+
+               $this->make_ajax_call( 'search-available-menu-items-customizer' );
+
+               // If we are an admin, we should get a proper response.
+               if ( 'administrator' === $role ) {
+                       // Get the results.
+                       $response = json_decode( $this->_last_response, true );
+
+                       $this->assertSame( $expected_results, $response );
+               }
+       }
+
+       /**
+        * Data provider for test_ajax_search_available_items_caps_check().
+        *
+        * Provides various post_args to induce error messages in the that can be
+        * compared to the expected_results.
+        *
+        * @since 4.3.0
+        *
+        * @todo Make this more DRY
+        *
+        * @return array {
+        *     @type array {
+        * @string string $role             The role that will test caps for.
+        * @array  array  $expected_results The expected results from the Ajax call.
+        *     }
+        * }
+        */
+       public function data_ajax_search_available_items_caps_check() {
+               return array(
+                       array(
+                               'subscriber',
+                               array(),
+                       ),
+                       array(
+                               'contributor',
+                               array(),
+                       ),
+                       array(
+                               'author',
+                               array(),
+                       ),
+                       array(
+                               'editor',
+                               array(),
+                       ),
+                       array(
+                               'administrator',
+                               array(
+                                       'success' => false,
+                                       'data'    => 'nav_menus_missing_search_parameter',
+                               ),
+                       ),
+               );
+       }
+
+       /**
+        * Testing the results of various searches
+        *
+        * @dataProvider data_ajax_search_available_items_results
+        *
+        * @covers WP_Customize_Nav_Menus::ajax_search_available_items
+        * @covers WP_Customize_Nav_Menus::search_available_items_query
+        *
+        * @param array $post_args        POST args.
+        * @param array $expected_results Expected results.
+        */
+       public function test_ajax_search_available_items_results( $post_args, $expected_results ) {
+               do_action( 'customize_register', $this->wp_customize );
+
+               self::factory()->post->create_many( 5, array( 'post_title' => 'Test Post' ) );
+               $included_auto_draft_post = $this->wp_customize->nav_menus->insert_auto_draft_post(
+                       array(
+                               'post_title' => 'Test Included Auto Draft',
+                               'post_type'  => 'post',
+                       )
+               );
+               $excluded_auto_draft_post = $this->wp_customize->nav_menus->insert_auto_draft_post(
+                       array(
+                               'post_title' => 'Excluded Auto Draft',
+                               'post_type'  => 'post',
+                       )
+               );
+               $this->wp_customize->set_post_value( 'nav_menus_created_posts', array( $included_auto_draft_post->ID, $excluded_auto_draft_post->ID ) );
+               $this->wp_customize->get_setting( 'nav_menus_created_posts' )->preview();
+
+               $_POST = array_merge(
+                       array(
+                               'action'                => 'search-available-menu-items-customizer',
+                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                       ),
+                       $post_args
+               );
+
+               $this->make_ajax_call( 'search-available-menu-items-customizer' );
+
+               $response = json_decode( $this->_last_response, true );
+
+               if ( isset( $post_args['search'] ) && 'test' === $post_args['search'] ) {
+                       $this->assertTrue( $response['success'] );
+                       $this->assertCount( 6, $response['data']['items'] );
+                       $item_ids = wp_list_pluck( $response['data']['items'], 'id' );
+                       $this->assertContains( 'post-' . $included_auto_draft_post->ID, $item_ids );
+                       $this->assertNotContains( 'post-' . $excluded_auto_draft_post->ID, $item_ids );
+               } else {
+                       $this->assertSame( $expected_results, $response );
+               }
+       }
+
+       /**
+        * Data provider for test_ajax_search_available_items_results().
+        *
+        * Provides various post_args to test the results.
+        *
+        * @since 4.3.0
+        *
+        * @return array {
+        *     @type array {
+        * @string string $post_args        The args that will be passed to Ajax.
+        * @array  array  $expected_results The expected results from the Ajax call.
+        *     }
+        * }
+        */
+       public function data_ajax_search_available_items_results() {
+               return array(
+                       array(
+                               array(),
+                               array(
+                                       'success' => false,
+                                       'data'    => 'nav_menus_missing_search_parameter',
+                               ),
+                       ),
+                       array(
+                               array(
+                                       'search' => 'all_the_things',
+                               ),
+                               array(
+                                       'success' => false,
+                                       'data'    => array(
+                                               'message' => 'No results found.',
+                                       ),
+                               ),
+                       ),
+                       array(
+                               array(
+                                       'search' => 'test',
+                               ),
+                               array(
+                                       'success' => true,
+                                       array(),
+                               ),
+                       ),
+               );
+       }
+
+       /**
+        * Testing successful ajax_insert_auto_draft_post() call.
+        *
+        * @covers WP_Customize_Nav_Menus::ajax_insert_auto_draft_post
+        * @covers WP_Customize_Nav_Menus::insert_auto_draft_post
+        */
+       public function test_ajax_insert_auto_draft_post_success() {
+               $_POST                = wp_slash(
+                       array(
+                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                               'params'                => array(
+                                       'post_type'  => 'post',
+                                       'post_title' => 'Hello World',
+                               ),
+                       )
+               );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+
+               $this->assertTrue( $response['success'] );
+               $this->assertArrayHasKey( 'post_id', $response['data'] );
+               $this->assertArrayHasKey( 'url', $response['data'] );
+               $post = get_post( $response['data']['post_id'] );
+               $this->assertSame( 'Hello World', $post->post_title );
+               $this->assertSame( 'post', $post->post_type );
+               $this->assertSame( '', $post->post_name );
+               $this->assertSame( 'hello-world', get_post_meta( $post->ID, '_customize_draft_post_name', true ) );
+               $this->assertSame( $this->wp_customize->changeset_uuid(), get_post_meta( $post->ID, '_customize_changeset_uuid', true ) );
+       }
+
+       /**
+        * Testing unsuccessful ajax_insert_auto_draft_post() call.
+        *
+        * @covers WP_Customize_Nav_Menus::ajax_insert_auto_draft_post
+        */
+       public function test_ajax_insert_auto_draft_failures() {
+               // No nonce.
+               $_POST                = array();
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertSame( 'bad_nonce', $response['data'] );
+
+               // Bad nonce.
+               $_POST                = wp_slash(
+                       array(
+                               'customize-menus-nonce' => 'bad',
+                       )
+               );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertSame( 'bad_nonce', $response['data'] );
+
+               // Bad nonce.
+               wp_set_current_user( self::factory()->user->create( array( 'role' => 'subscriber' ) ) );
+               $_POST                = wp_slash(
+                       array(
+                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                       )
+               );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertSame( 'customize_not_allowed', $response['data'] );
+
+               // Missing params.
+               wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
+               $_POST                = wp_slash(
+                       array(
+                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                       )
+               );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertSame( 'missing_params', $response['data'] );
+
+               // insufficient_post_permissions.
+               register_post_type( 'privilege', array( 'capability_type' => 'privilege' ) );
+               $_POST                = wp_slash(
+                       array(
+                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                               'params'                => array(
+                                       'post_type' => 'privilege',
+                               ),
+                       )
+               );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertSame( 'insufficient_post_permissions', $response['data'] );
+
+               // insufficient_post_permissions.
+               $_POST                = wp_slash(
+                       array(
+                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                               'params'                => array(
+                                       'post_type' => 'non-existent',
+                               ),
+                       )
+               );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertSame( 'missing_post_type_param', $response['data'] );
+
+               // missing_post_title.
+               $_POST                = wp_slash(
+                       array(
+                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                               'params'                => array(
+                                       'post_type'  => 'post',
+                                       'post_title' => '    ',
+                               ),
+                       )
+               );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertSame( 'missing_post_title', $response['data'] );
+
+               // illegal_params.
+               $_POST                = wp_slash(
+                       array(
+                               'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                               'params'                => array(
+                                       'post_type'    => 'post',
+                                       'post_title'   => 'OK',
+                                       'post_name'    => 'bad',
+                                       'post_content' => 'bad',
+                               ),
+                       )
+               );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertSame( 'illegal_params', $response['data'] );
+       }
+}
</ins></span></pre>
</div>
</div>

</body>
</html>