Make WordPress Core

Ticket #43438: 43438.13.diff

File 43438.13.diff, 30.8 KB (added by birgire, 7 years ago)
  • tests/phpunit/includes/testcase-ajax.php

    diff --git tests/phpunit/includes/testcase-ajax.php tests/phpunit/includes/testcase-ajax.php
    index bf5a5bc..736d362 100644
    abstract class WP_Ajax_UnitTestCase extends WP_UnitTestCase { 
    119119                'delete-theme',
    120120                'install-theme',
    121121                'get-post-thumbnail-html',
     122                'wp-privacy-export-personal-data',
     123                'wp-privacy-erase-personal-data',
    122124        );
    123125
    124126        public static function setUpBeforeClass() {
  • new file tests/phpunit/tests/ajax/PrivacyErasePersonalData.php

    diff --git tests/phpunit/tests/ajax/PrivacyErasePersonalData.php tests/phpunit/tests/ajax/PrivacyErasePersonalData.php
    new file mode 100644
    index 0000000..ea048f9
    - +  
     1<?php
     2/**
     3 * Testing Ajax handler for erasing personal data.
     4 *
     5 * @package WordPress\UnitTests
     6 *
     7 * @since 4.9.7
     8 */
     9
     10/**
     11 * Tests_Ajax_PrivacyExportPersonalData class.
     12 *
     13 * @since 4.9.7
     14 *
     15 * @group ajax
     16 * @group privacy
     17 * @covers wp_ajax_wp_privacy_erase_personal_data
     18 */
     19class Tests_Ajax_PrivacyErasePersonalData extends WP_Ajax_UnitTestCase {
     20        /**
     21         * User Request ID.
     22         *
     23         * @since 4.9.7
     24         *
     25         * @var int
     26         */
     27        protected static $request_id;
     28
     29        /**
     30         * User Request Email.
     31         *
     32         * @since 4.9.7
     33         *
     34         * @var string
     35         */
     36        protected static $request_email;
     37
     38        /**
     39         * Create user erase request fixtures.
     40         *
     41         * @param WP_UnitTest_Factory $factory Factory.
     42         */
     43        public static function wpSetUpBeforeClass( $factory ) {
     44                self::$request_email = 'requester@example.com';
     45                self::$request_id    = wp_create_user_request( self::$request_email, 'remove_personal_data' );
     46        }
     47
     48        /**
     49         * Register a custom personal data eraser.
     50         */
     51        public function setUp() {
     52                parent::setUp();
     53
     54                // Make sure the erasers response is not modified and avoid e.g. sending emails.
     55                remove_all_filters( 'wp_privacy_personal_data_erasure_page' );
     56                remove_all_actions( 'wp_privacy_personal_data_erased' );
     57
     58                // Only use our custom privacy personal data eraser.
     59                remove_all_filters( 'wp_privacy_personal_data_erasers' );
     60                add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_custom_personal_data_eraser' ) );
     61        }
     62
     63        /**
     64         * The function should successfully send exporters response data when the current user has the required capabilites.
     65         *
     66         * @ticket 43438
     67         */
     68        public function test_wp_ajax_wp_privacy_export_personal_data_should_success_when_current_user_has_required_capabilities() {
     69                $this->_setRole( 'administrator' );
     70                $this->assertTrue( current_user_can( 'erase_others_personal_data' ) );
     71                $this->assertTrue( current_user_can( 'delete_users' ) );
     72
     73                // Set up a request.
     74                $_POST['action']   = 'wp-privacy-erase-personal-data';
     75                $_POST['security'] = wp_create_nonce( 'wp-privacy-erase-personal-data-' . self::$request_id );
     76                $_POST['eraser']   = 1;
     77                $_POST['page']     = 1;
     78                $_POST['id']       = self::$request_id;
     79
     80                // Make the request.
     81                try {
     82                        $this->_handleAjax( $_POST['action'] );
     83                } catch ( WPAjaxDieContinueException $e ) {
     84                        unset( $e );
     85                }
     86
     87                // Get the response.
     88                $response = json_decode( $this->_last_response, true );
     89
     90                $this->assertTrue( $response['success'] );
     91                $this->assertSame( 'A message regarding retained data for requester@example.com.', $response['data']['messages'][0] );
     92                $this->assertTrue( $response['data']['items_removed'] );
     93                $this->assertTrue( $response['data']['items_retained'] );
     94                $this->assertTrue( $response['data']['done'] );
     95        }
     96
     97        /**
     98         * The function should send an error when the current user is missing required capabilities.
     99         *
     100         * @ticket 43438
     101         */
     102        public function test_wp_ajax_wp_privacy_erase_personal_data_should_error_when_current_user_missing_required_capabilities() {
     103                $this->_setRole( 'author' );
     104                $this->assertFalse( current_user_can( 'erase_others_personal_data' ) );
     105                $this->assertFalse( current_user_can( 'delete_users' ) );
     106
     107                // Set up a request.
     108                $_POST['action']   = 'wp-privacy-erase-personal-data';
     109                $_POST['security'] = wp_create_nonce( 'wp-privacy-erase-personal-data-' . self::$request_id );
     110                $_POST['eraser']   = 1;
     111                $_POST['page']     = 1;
     112                $_POST['id']       = self::$request_id;
     113
     114                // Make the request.
     115                try {
     116                        $this->_handleAjax( $_POST['action'] );
     117                } catch ( WPAjaxDieContinueException $e ) {
     118                        unset( $e );
     119                }
     120
     121                // Get the response.
     122                $response = json_decode( $this->_last_response, true );
     123
     124                $this->assertFalse( $response['success'] );
     125                $this->assertSame( 'Invalid request.', $response['data'] );
     126        }
     127
     128        /**
     129         * The function should send error when the request ID is missing.
     130         *
     131         * @ticket 43438
     132         */
     133        public function test_wp_ajax_wp_privacy_erase_personal_data_should_error_when_missing_request_id() {
     134                $this->_setRole( 'administrator' );
     135
     136                $this->assertNotWPError( self::$request_id );
     137
     138                // Set up a request.
     139                $_POST['action']   = 'wp-privacy-erase-personal-data';
     140                $_POST['security'] = wp_create_nonce( 'wp-privacy-erase-personal-data-' . self::$request_id );
     141                $_POST['eraser']   = 1;
     142                $_POST['page']     = 1;
     143                $_POST['id']       = null; // Missing request ID.
     144
     145                // Make the request.
     146                try {
     147                        $this->_handleAjax( $_POST['action'] );
     148                } catch ( WPAjaxDieContinueException $e ) {
     149                        unset( $e );
     150                }
     151
     152                // Get the response.
     153                $response = json_decode( $this->_last_response, true );
     154
     155                $this->assertFalse( $response['success'] );
     156                $this->assertSame( 'Missing request ID.', $response['data'] );
     157        }
     158
     159        /**
     160        * Register handler for a custom personal data exporter.
     161        *
     162        * @since 4.9.7
     163        *
     164        * @param  array $erasers An array of personal data erasers.
     165        * @return array $erasers An array of personal data erasers.
     166        */
     167        public function register_custom_personal_data_eraser( $erasers ) {
     168                $erasers['custom_exporter'] = array(
     169                        'eraser_friendly_name' => __( 'Custom Eraser' ),
     170                        'callback'             => array( $this, 'custom_personal_data_eraser' ),
     171                );
     172                return $erasers;
     173        }
     174
     175        /**
     176        * Custom Personal Data Eraser.
     177        *
     178        * @since 4.9.7
     179        *
     180        * @param  string $email_address The comment author email address.
     181        * @param  int    $page          Page number.
     182        * @return array  $return        Erase data.
     183        */
     184        public function custom_personal_data_eraser( $email_address, $page = 1 ) {
     185                if ( 1 === $page ) {
     186                        return array(
     187                                'items_removed'  => true,
     188                                'items_retained' => true,
     189                                'messages'       => array( sprintf( 'A message regarding retained data for %s.', $email_address ) ),
     190                                'done'           => true,
     191                        );
     192                }
     193
     194                return array(
     195                        'items_removed'  => false,
     196                        'items_retained' => false,
     197                        'messages'       => array(),
     198                        'done'           => true,
     199                );
     200        }
     201
     202}
  • new file tests/phpunit/tests/ajax/PrivacyExportPersonalData.php

    diff --git tests/phpunit/tests/ajax/PrivacyExportPersonalData.php tests/phpunit/tests/ajax/PrivacyExportPersonalData.php
    new file mode 100644
    index 0000000..1ce56ae
    - +  
     1<?php
     2/**
     3 * Testing Ajax handler for exporting personal data.
     4 *
     5 * @package WordPress\UnitTests
     6 *
     7 * @since 4.9.7
     8 */
     9
     10/**
     11 * Tests_Ajax_PrivacyExportPersonalData class.
     12 *
     13 * @since 4.9.7
     14 *
     15 * @group ajax
     16 * @group privacy
     17 * @covers wp_ajax_wp_privacy_export_personal_data
     18 */
     19class Tests_Ajax_PrivacyExportPersonalData extends WP_Ajax_UnitTestCase {
     20        /**
     21         * User Request ID.
     22         *
     23         * @since 4.9.7
     24         *
     25         * @var int
     26         */
     27        protected static $request_id;
     28
     29        /**
     30         * User Request Email.
     31         *
     32         * @since 4.9.7
     33         *
     34         * @var string
     35         */
     36        protected static $request_email;
     37
     38        /**
     39         * Ajax Action.
     40         *
     41         * @since 4.9.7
     42         *
     43         * @var string
     44         */
     45        protected static $action;
     46
     47        /**
     48         * Exporter Index.
     49         *
     50         * @since 4.9.7
     51         *
     52         * @var int
     53         */
     54        protected static $exporter;
     55
     56        /**
     57         * Exporter Key.
     58         *
     59         * @since 4.9.7
     60         *
     61         * @var string
     62         */
     63        protected static $exporter_key;
     64
     65        /**
     66         * Exporter Friendly Name.
     67         *
     68         * @since 4.9.7
     69         *
     70         * @var string
     71         */
     72        protected static $exporter_friendly_name;
     73
     74        /**
     75         * Page Index.
     76         *
     77         * @since 4.9.7
     78         *
     79         * @var int
     80         */
     81        protected static $page;
     82
     83        /**
     84         * Send As Email.
     85         *
     86         * @since 4.9.7
     87         *
     88         * @var bool
     89         */
     90        protected static $send_as_email;
     91
     92        /**
     93         * Last response parsed.
     94         *
     95         * @since 4.9.7
     96         *
     97         * @var array|null
     98         */
     99        protected $_last_response_parsed;
     100
     101        /**
     102         * Create user export request fixtures.
     103         *
     104         * @since 4.9.7
     105         *
     106         * @param WP_UnitTest_Factory $factory Factory.
     107         */
     108        public static function wpSetUpBeforeClass( $factory ) {
     109                self::$request_email          = 'requester@example.com';
     110                self::$request_id             = wp_create_user_request( self::$request_email, 'export_personal_data' );
     111                self::$action                 = 'wp-privacy-export-personal-data';
     112                self::$exporter               = 1;
     113                self::$exporter_key           = 'custom-exporter';
     114                self::$exporter_friendly_name = 'Custom Exporter';
     115                self::$page                   = 1;
     116                self::$send_as_email          = false;
     117        }
     118
     119        /**
     120         * Setup before each test method.
     121         *
     122         * @since 4.9.7
     123         */
     124        public function setUp() {
     125                parent::setUp();
     126
     127                // Make sure the exporter response is not modified and avoid e.g. writing export file to disk.
     128                remove_all_filters( 'wp_privacy_personal_data_export_page' );
     129
     130                // Only use our custom privacy personal data exporter.
     131                remove_all_filters( 'wp_privacy_personal_data_exporters' );
     132                add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) );
     133        }
     134
     135        /**
     136         * Clean up before each test method.
     137         */
     138        public function tearDown() {
     139                parent::tearDown();
     140                remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) );
     141                $_POST = array();
     142        }
     143
     144        /**
     145         * The function should successfully send exporter data response when the current user has the required capability.
     146         *
     147         * @since 4.9.7
     148         */
     149        public function test_function_should_success_when_current_user_has_required_capability() {
     150                $this->_setRole( 'administrator' );
     151
     152                $this->_make_ajax_call();
     153
     154                $this->assertTrue( $this->_last_response_parsed['success'] );
     155                $this->assertTrue( current_user_can( 'export_others_personal_data' ) );
     156                $this->assertSame( 'custom-exporter-item-id', $this->_last_response_parsed['data']['data']['item_id'] );
     157                $this->assertSame( 'Email', $this->_last_response_parsed['data']['data']['data'][0]['name'] );
     158                $this->assertSame( self::$request_email, $this->_last_response_parsed['data']['data']['data'][0]['value'] );
     159        }
     160
     161        /**
     162         * The function should error when the current user is missing the required capability.
     163         *
     164         * @since 4.9.7
     165         */
     166        public function test_function_should_error_when_current_user_missing_required_capability() {
     167                $this->_setRole( 'author' );
     168
     169                $this->_make_ajax_call();
     170
     171                $this->assertFalse( $this->_last_response_parsed['success'] );
     172                $this->assertFalse( current_user_can( 'export_others_personal_data' ) );
     173                $this->assertSame( 'Invalid request.', $this->_last_response_parsed['data'] );
     174        }
     175
     176        /**
     177         * The function should send error when the request ID is missing.
     178         *
     179         * @since 4.9.7
     180         */
     181        public function test_function_should_error_when_missing_request_id() {
     182                $this->_setRole( 'administrator' );
     183
     184                $this->_make_ajax_call(
     185                        array(
     186                                'id' => null, // Missing request ID.
     187                        )
     188                );
     189
     190                $this->assertFalse( $this->_last_response_parsed['success'] );
     191                $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] );
     192        }
     193
     194        /**
     195         * The function should send error when the request ID is invalid.
     196         *
     197         * @since 4.9.7
     198         */
     199        public function test_function_should_error_when_invalid_id() {
     200                $this->_setRole( 'administrator' );
     201
     202                $this->_make_ajax_call(
     203                        array(
     204                                'id' => -1, // Invalid request ID.
     205                        )
     206                );
     207
     208                $this->assertFalse( $this->_last_response_parsed['success'] );
     209                $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] );
     210        }
     211
     212        /**
     213         * The function should send error when the requester's email address is invalid.
     214         *
     215         * @since 4.9.7
     216
     217         */
     218        public function test_function_should_error_when_invalid_email_address() {
     219                $this->_setRole( 'administrator' );
     220                $request_id = wp_create_user_request( 'invalid-requester-email@example.com', 'export_personal_data' );
     221                wp_update_post(
     222                        array(
     223                                'ID'         => $request_id,
     224                                'post_title' => '', // Invalid requester's email address.
     225                        )
     226                );
     227
     228                $this->_make_ajax_call(
     229                        array(
     230                                'security' => wp_create_nonce( 'wp-privacy-export-personal-data-' . $request_id ),
     231                                'id'       => $request_id,
     232                        )
     233                );
     234
     235                $this->assertFalse( $this->_last_response_parsed['success'] );
     236                $this->assertSame( 'A valid email address must be given.', $this->_last_response_parsed['data'] );
     237        }
     238
     239        /**
     240         * The function should send error when the exporter index is missing.
     241         *
     242         * @since 4.9.7
     243         */
     244        public function test_function_should_error_when_missing_exporter_index() {
     245                $this->_setRole( 'administrator' );
     246
     247                $this->_make_ajax_call(
     248                        array(
     249                                'exporter' => null, // Missing exporter index.
     250                        )
     251                );
     252
     253                $this->assertFalse( $this->_last_response_parsed['success'] );
     254                $this->assertSame( 'Missing exporter index.', $this->_last_response_parsed['data'] );
     255        }
     256
     257        /**
     258         * The function should send error when the page index is missing.
     259         *
     260         * @since 4.9.7
     261         */
     262        public function test_function_should_error_when_missing_page_index() {
     263                $this->_setRole( 'administrator' );
     264
     265                $this->_make_ajax_call(
     266                        array(
     267                                'page' => null, // Missing page index.
     268                        )
     269                );
     270
     271                $this->assertFalse( $this->_last_response_parsed['success'] );
     272                $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] );
     273        }
     274
     275        /**
     276         * The function should send error when an exporter has improperly used the `wp_privacy_personal_data_exporters` filter.
     277         *
     278         * @since 4.9.7
     279         */
     280        public function test_function_should_error_when_exporter_has_improperly_used_exporters_filter() {
     281                $this->_setRole( 'administrator' );
     282
     283                // Improper filter usage: returns false instead of an expected array.
     284                add_filter( 'wp_privacy_personal_data_exporters', '__return_false', 999 );
     285                $this->_make_ajax_call();
     286                remove_filter( 'wp_privacy_personal_data_exporters', '__return_false', 999 );
     287
     288                $this->assertFalse( $this->_last_response_parsed['success'] );
     289                $this->assertSame( 'An exporter has improperly used the registration filter.', $this->_last_response_parsed['data'] );
     290        }
     291
     292        /**
     293         * The function should send error when the exporter index is negative.
     294         *
     295         * @since 4.9.7
     296         */
     297        public function test_function_should_error_when_negative_exporter_index() {
     298                $this->_setRole( 'administrator' );
     299
     300                $this->_make_ajax_call(
     301                        array(
     302                                'exporter' => -1, // Negative exporter index.
     303                        )
     304                );
     305
     306                $this->assertFalse( $this->_last_response_parsed['success'] );
     307                $this->assertSame( 'Exporter index cannot be negative.', $this->_last_response_parsed['data'] );
     308        }
     309
     310        /**
     311         * The function should send error when the exporter index is out of range.
     312         *
     313         * @since 4.9.7
     314         */
     315        public function test_function_should_error_when_exporter_index_out_of_range() {
     316                $this->_setRole( 'administrator' );
     317
     318                $this->_make_ajax_call(
     319                        array(
     320                                'exporter' => PHP_INT_MAX, // Out of range exporter index.
     321                        )
     322                );
     323
     324                $this->assertFalse( $this->_last_response_parsed['success'] );
     325                $this->assertSame( 'Exporter index out of range.', $this->_last_response_parsed['data'] );
     326        }
     327
     328        /**
     329         * The function should send error when the page index is less than one.
     330         *
     331         * @since 4.9.7
     332         */
     333        public function test_function_should_error_when_page_index_less_than_one() {
     334                $this->_setRole( 'administrator' );
     335
     336                $this->_make_ajax_call(
     337                        array(
     338                                'page' => 0, // Page index less than one.
     339                        )
     340                );
     341
     342                $this->assertFalse( $this->_last_response_parsed['success'] );
     343                $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] );
     344        }
     345
     346        /**
     347         * The function should send error when an exporter is not an array.
     348         *
     349         * @since 4.9.7
     350         */
     351        public function test_function_should_error_when_exporter_not_array() {
     352                $this->_setRole( 'administrator' );
     353
     354                add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_not_array' ), 20 );
     355                $this->_make_ajax_call();
     356                remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_not_array' ), 20 );
     357
     358                $this->assertFalse( $this->_last_response_parsed['success'] );
     359                $this->assertSame(
     360                        sprintf(
     361                                'Expected an array describing the exporter at index %s.',
     362                                self::$exporter_key
     363                        ),
     364                        $this->_last_response_parsed['data']
     365                );
     366        }
     367
     368        /**
     369         * Filter the array of exporters.
     370         *
     371         * @since 4.9.7
     372         *
     373         * @param  array $exporters Exporters.
     374         *
     375         * @return array $exporters Exporters.
     376         */
     377        public function filter_exporter_not_array( $exporters ) {
     378                $exporters[ self::$exporter_key ] = false;
     379                return $exporters;
     380        }
     381
     382        /**
     383         * The function should send error when an exporter is missing a friendly name.
     384         *
     385         * @since 4.9.7
     386         */
     387        public function test_function_should_error_when_exporter_missing_friendly_name() {
     388                $this->_setRole( 'administrator' );
     389
     390                add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_friendly_name' ), 20 );
     391                $this->_make_ajax_call();
     392                remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_friendly_name' ), 20 );
     393
     394                $this->assertFalse( $this->_last_response_parsed['success'] );
     395                $this->assertSame(
     396                        sprintf(
     397                                'Exporter array at index %s does not include a friendly name.',
     398                                self::$exporter_key
     399                        ),
     400                        $this->_last_response_parsed['data']
     401                );
     402        }
     403
     404
     405        /**
     406         * Filter the array of exporters.
     407         *
     408         * @since 4.9.7
     409         *
     410         * @param  array $exporters Exporters.
     411         *
     412         * @return array $exporters Exporters.
     413         */
     414        public function filter_exporter_missing_friendly_name( $exporters ) {
     415                unset( $exporters[ self::$exporter_key ]['exporter_friendly_name'] ); // Missing an exporter's friendly name.
     416                return $exporters;
     417        }
     418
     419        /**
     420         * The function should send error when an exporter is missing a callback.
     421         *
     422         * @since 4.9.7
     423         */
     424        public function test_function_should_error_when_exporter_missing_callback() {
     425                $this->_setRole( 'administrator' );
     426
     427                add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_callback' ), 20 );
     428                $this->_make_ajax_call();
     429                remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_callback' ), 20 );
     430
     431                $this->assertFalse( $this->_last_response_parsed['success'] );
     432                $this->assertSame(
     433                        sprintf(
     434                                'Exporter does not include a callback: %s.',
     435                                self::$exporter_friendly_name
     436                        ),
     437                        $this->_last_response_parsed['data']
     438                );
     439        }
     440
     441        /**
     442         * Filter the array of exporters.
     443         *
     444         * @since 4.9.7
     445         *
     446         * @param  array $exporters Exporters.
     447         *
     448         * @return array $exporters Exporters.
     449         */
     450        public function filter_exporter_missing_callback( $exporters ) {
     451                unset( $exporters[ self::$exporter_key ]['callback'] ); // Missing an exporter's callback.
     452                return $exporters;
     453        }
     454
     455        /**
     456         * The function should send error when an exporter, at a given index, has an invalid callback.
     457         *
     458         * @since 4.9.7
     459
     460         */
     461        public function test_function_should_error_when_exporter_index_invalid_callback() {
     462                $this->_setRole( 'administrator' );
     463
     464                add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_index_invalid_callback' ), 20 );
     465                $this->_make_ajax_call();
     466                remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_index_invalid_callback' ), 20 );
     467
     468                $this->assertFalse( $this->_last_response_parsed['success'] );
     469                $this->assertSame(
     470                        sprintf(
     471                                'Exporter callback is not a valid callback: %s.',
     472                                self::$exporter_friendly_name
     473                        ),
     474                        $this->_last_response_parsed['data']
     475                );
     476        }
     477
     478        /**
     479         * Filter the array of exporters.
     480         *
     481         * @since 4.9.7
     482         *
     483         * @param  array $exporters Exporters.
     484         *
     485         * @return array $exporters Exporters.
     486         */
     487        public function filter_exporter_index_invalid_callback( $exporters ) {
     488                $exporters[ self::$exporter_key ]['callback'] = false; // The exporter's callback isn't callable here.
     489                return $exporters;
     490        }
     491
     492        /**
     493         * The function should send error when an exporter, at a given index, is missing an array response.
     494         *
     495         * @since 4.9.7
     496         */
     497        public function test_function_should_error_when_exporter_index_invalid_response() {
     498                $this->_setRole( 'administrator' );
     499
     500                add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_index_invalid_response' ), 20 );
     501                $this->_make_ajax_call();
     502                remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_index_invalid_response' ), 20 );
     503
     504                $this->assertFalse( $this->_last_response_parsed['success'] );
     505                $this->assertSame(
     506                        sprintf(
     507                                'Expected response as an array from exporter: %s.',
     508                                self::$exporter_friendly_name
     509                        ),
     510                        $this->_last_response_parsed['data']
     511                );
     512        }
     513
     514        /**
     515         * Filter the array of exporters.
     516         *
     517         * @since 4.9.7
     518         *
     519         * @param  array $exporters Exporters.
     520         *
     521         * @return array $exporters Exporters.
     522         */
     523        public function filter_exporter_index_invalid_response( $exporters ) {
     524                $exporters[ self::$exporter_key ]['callback'] = '__return_null'; // A callback with an invalid response.
     525                return $exporters;
     526        }
     527
     528        /**
     529         * The function should send error when an exporter is missing data in array response.
     530         *
     531         * @since 4.9.7
     532         */
     533        public function test_function_should_error_when_exporter_missing_data_response() {
     534                $this->_setRole( 'administrator' );
     535
     536                add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_data_response' ), 20 );
     537                $this->_make_ajax_call();
     538                remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_data_response' ), 20 );
     539
     540                $this->assertFalse( $this->_last_response_parsed['success'] );
     541                $this->assertSame(
     542                        sprintf(
     543                                'Expected data in response array from exporter: %s.',
     544                                self::$exporter_friendly_name
     545                        ),
     546                        $this->_last_response_parsed['data']
     547                );
     548        }
     549
     550        /**
     551         * Filter the array of exporters.
     552         *
     553         * @since 4.9.7
     554         *
     555         * @param  array $exporters Exporters.
     556         *
     557         * @return array $exporters Exporters.
     558         */
     559        public function filter_exporter_missing_data_response( $exporters ) {
     560                $exporters[ self::$exporter_key ]['callback'] = array( $this, 'callback_missing_data_response' );
     561                return $exporters;
     562        }
     563
     564        /**
     565         * Callback for exporter's response.
     566         *
     567         * @since 4.9.7
     568         *
     569         * @param  string $email_address The requester's email address.
     570         * @param  int    $page          Page number.
     571         *
     572         * @return array $return Export data.
     573         */
     574        public function callback_missing_data_response( $email_address, $page = 1 ) {
     575                $response = $this->callback_custom_personal_data_exporter( $email_address, $page );
     576                unset( $response['data'] ); // Missing data part of response.
     577                return $response;
     578        }
     579
     580        /**
     581         * The function should send error when an exporter is missing 'done' in array response.
     582         *
     583         * @since 4.9.7
     584         */
     585        public function test_function_should_error_when_exporter_missing_done_response() {
     586                $this->_setRole( 'administrator' );
     587
     588                add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_done_response' ), 20 );
     589                $this->_make_ajax_call();
     590                remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_done_response' ), 20 );
     591
     592                $this->assertFalse( $this->_last_response_parsed['success'] );
     593                $this->assertSame(
     594                        sprintf(
     595                                'Expected done (boolean) in response array from exporter: %s.',
     596                                self::$exporter_friendly_name
     597                        ),
     598                        $this->_last_response_parsed['data']
     599                );
     600        }
     601
     602        /**
     603         * Filter the array of exporters.
     604         *
     605         * @since 4.9.7
     606         *
     607         * @param  array $exporters Exporters.
     608         *
     609         * @return array $exporters Exporters.
     610         */
     611        public function filter_exporter_missing_done_response( $exporters ) {
     612                $exporters[ self::$exporter_key ]['callback'] = array( $this, 'callback_missing_done_response' );
     613                return $exporters;
     614        }
     615
     616        /**
     617         * Callback for exporter's response.
     618         *
     619         * @since 4.9.7
     620         *
     621         * @param  string $email_address The requester's email address.
     622         * @param  int    $page          Page number.
     623         *
     624         * @return array $return Export data.
     625         */
     626        public function callback_missing_done_response( $email_address, $page = 1 ) {
     627                $response = $this->callback_custom_personal_data_exporter( $email_address, $page );
     628                unset( $response['done'] ); // Missing done part of response.
     629                return $response;
     630        }
     631
     632        /**
     633         * The function should send error when an exporter is missing 'data' array in array response.
     634         *
     635         * @since 4.9.7
     636         */
     637        public function test_function_should_error_when_exporter_missing_data_array_response() {
     638                $this->_setRole( 'administrator' );
     639
     640                add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_data_array_response' ), 20 );
     641                $this->_make_ajax_call();
     642                remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_data_array_response' ), 20 );
     643
     644                $this->assertFalse( $this->_last_response_parsed['success'] );
     645                $this->assertSame(
     646                        sprintf(
     647                                'Expected data array in response array from exporter: %s.',
     648                                self::$exporter_friendly_name
     649                        ),
     650                        $this->_last_response_parsed['data']
     651                );
     652        }
     653
     654        /**
     655         * Filter the array of exporters.
     656         *
     657         * @since 4.9.7
     658         *
     659         * @param  array $exporters Exporters.
     660         *
     661         * @return array $exporters Exporters.
     662         */
     663        public function filter_exporter_missing_data_array_response( $exporters ) {
     664                $exporters[ self::$exporter_key ]['callback'] = array( $this, 'callback_missing_data_array_response' );
     665                return $exporters;
     666        }
     667
     668        /**
     669         * Callback for exporter's response.
     670         *
     671         * @since 4.9.7
     672         *
     673         * @param  string $email_address The requester's email address.
     674         * @param  int    $page          Page number.
     675         *
     676         * @return array $return Export data.
     677         */
     678        public function callback_missing_data_array_response( $email_address, $page = 1 ) {
     679                $response         = $this->callback_custom_personal_data_exporter( $email_address, $page );
     680                $response['data'] = false; // Not an array.
     681                return $response;
     682        }
     683
     684        /**
     685         * The function's output should be filterable with the `wp_privacy_personal_data_export_page` filter.
     686         *
     687         * @since 4.9.7
     688         */
     689        public function test_function_output_should_be_filterable() {
     690                $this->_setRole( 'administrator' );
     691
     692                add_filter( 'wp_privacy_personal_data_export_page', array( $this, 'filter_exporter_data_response' ), 20, 7 );
     693                $this->_make_ajax_call();
     694                remove_filter( 'wp_privacy_personal_data_export_page', array( $this, 'filter_exporter_data_response' ), 20 );
     695
     696                $expected_group_label = sprintf(
     697                        '%s-%s-%s-%s-%s-%s',
     698                        self::$exporter,
     699                        self::$page,
     700                        self::$request_email,
     701                        self::$request_id,
     702                        self::$send_as_email,
     703                        self::$exporter_key
     704                );
     705
     706                $this->assertTrue( $this->_last_response_parsed['success'] );
     707                $this->assertSame( $expected_group_label, $this->_last_response_parsed['data']['group_label'] );
     708                $this->assertSame( 'filtered_group_id', $this->_last_response_parsed['data']['group_id'] );
     709                $this->assertSame( 'filtered_item_id', $this->_last_response_parsed['data']['item_id'] );
     710                $this->assertSame( 'filtered_name', $this->_last_response_parsed['data']['data'][0]['name'] );
     711                $this->assertSame( 'filtered_value', $this->_last_response_parsed['data']['data'][0]['value'] );
     712        }
     713
     714        /**
     715         * Filter exporter's data response.
     716         *
     717         * @since 4.9.7
     718         *
     719         * @param array  $response        The personal data for the given exporter and page.
     720         * @param int    $exporter_index  The index of the exporter that provided this data.
     721         * @param string $email_address   The email address associated with this personal data.
     722         * @param int    $page            The page for this response.
     723         * @param int    $request_id      The privacy request post ID associated with this request.
     724         * @param bool   $send_as_email   Whether the final results of the export should be emailed to the user.
     725         * @param string $exporter_key    The key (slug) of the exporter that provided this data.
     726         *
     727         * @return array $response The personal data for the given exporter and page.
     728         */
     729        public function filter_exporter_data_response( $response, $exporter_index, $email_address, $page, $request_id, $send_as_email, $exporter_key ) {
     730                $group_label                  = sprintf(
     731                        '%s-%s-%s-%s-%s-%s',
     732                        $exporter_index,
     733                        $page,
     734                        $email_address,
     735                        $request_id,
     736                        $send_as_email,
     737                        $exporter_key
     738                );
     739                $response['group_label']      = $group_label;
     740                $response['group_id']         = 'filtered_group_id';
     741                $response['item_id']          = 'filtered_item_id';
     742                $response['data'][0]['name']  = 'filtered_name';
     743                $response['data'][0]['value'] = 'filtered_value';
     744
     745                return $response;
     746        }
     747
     748        /**
     749        * Filter to register a custom personal data exporter.
     750        *
     751        * @since 4.9.7
     752        *
     753        * @param  array $exporters An array of personal data exporters.
     754        *
     755        * @return array $exporters An array of personal data exporters.
     756        */
     757        public function filter_register_custom_personal_data_exporter( $exporters ) {
     758                $exporters[ self::$exporter_key ] = array(
     759                        'exporter_friendly_name' => self::$exporter_friendly_name,
     760                        'callback'               => array( $this, 'callback_custom_personal_data_exporter' ),
     761                );
     762                return $exporters;
     763        }
     764
     765        /**
     766        * Callback for a custom personal data exporter.
     767        *
     768        * @since 4.9.7
     769        *
     770        * @param  string $email_address The requester's email address.
     771        * @param  int    $page          Page number.
     772        *
     773        * @return array $response Export data response.
     774        */
     775        public function callback_custom_personal_data_exporter( $email_address, $page = 1 ) {
     776                $data_to_export = array();
     777
     778                if ( 1 === $page ) {
     779                        $data_to_export = array(
     780                                'group_id'    => self::$exporter_key . '-group-id',
     781                                'group_label' => self::$exporter_key . '-group-label',
     782                                'item_id'     => self::$exporter_key . '-item-id',
     783                                'data'        => array(
     784                                        array(
     785                                                'name'  => 'Email',
     786                                                'value' => $email_address,
     787                                        ),
     788                                ),
     789                        );
     790                }
     791
     792                return array(
     793                        'data' => $data_to_export,
     794                        'done' => true,
     795                );
     796        }
     797
     798        /**
     799         * Helper function for ajax handler.
     800         *
     801         * @since 4.9.7
     802         *
     803         * @param array $args Ajax request arguments.
     804         */
     805        protected function _make_ajax_call( $args = array() ) {
     806                $this->_last_response_parsed = null;
     807                $this->_last_response        = '';
     808
     809                $defaults = array(
     810                        'action'      => self::$action,
     811                        'security'    => wp_create_nonce( self::$action . '-' . self::$request_id ),
     812                        'exporter'    => self::$exporter,
     813                        'page'        => self::$page,
     814                        'sendAsEmail' => self::$send_as_email,
     815                        'id'          => self::$request_id,
     816                );
     817
     818                $_POST = wp_parse_args( $args, $defaults );
     819
     820                try {
     821                        $this->_handleAjax( self::$action );
     822                } catch ( WPAjaxDieContinueException $e ) {
     823                        unset( $e );
     824                }
     825                if ( $this->_last_response ) {
     826                        $this->_last_response_parsed = json_decode( $this->_last_response, true );
     827                }
     828        }
     829
     830}