From 1dafd53d20ed21f5ec90ea6960b9d8c84aed035e Mon Sep 17 00:00:00 2001
From: Sergey Biryukov <sergeybiryukov@git.wordpress.org>
Date: Mon, 29 Mar 2021 19:35:36 +0000
Subject: [PATCH] Code Modernization: Check if the `_export_data_grouped` post
 meta is an array when generating a personal data export file.

This avoids a fatal error on PHP 8 in `wp_privacy_generate_personal_data_export_file()` if the `_export_data_grouped` post meta exists but is not an array.

Additionally, refactor unit tests for the function to:
* Reduce redundant code
* Switch to data provider
* Test on the full HTML output instead of select pieces of the output
* Expand unhappy path coverage

Follow-up to [43012], [44786], [47146], [47278].

Props hellofromTonya, jrf, xknown.
See #51423.

git-svn-id: https://develop.svn.wordpress.org/trunk@50613 602fd350-edb4-49c9-b593-d223f7449a82
---
 src/wp-admin/includes/privacy-tools.php       |  24 +-
 ...pPrivacyGeneratePersonalDataExportFile.php | 707 +++++++++---------
 2 files changed, 353 insertions(+), 378 deletions(-)

diff --git a/src/wp-admin/includes/privacy-tools.php b/src/wp-admin/includes/privacy-tools.php
index 472c3de9ba..3f69366020 100644
--- a/src/wp-admin/includes/privacy-tools.php
+++ b/src/wp-admin/includes/privacy-tools.php
@@ -362,9 +362,6 @@ function wp_privacy_generate_personal_data_export_file( $request_id ) {
 		$email_address
 	);
 
-	// And now, all the Groups.
-	$groups = get_post_meta( $request_id, '_export_data_grouped', true );
-
 	// First, build an "About" group on the fly for this report.
 	$about_group = array(
 		/* translators: Header for the About section in a personal data export. */
@@ -393,10 +390,25 @@ function wp_privacy_generate_personal_data_export_file( $request_id ) {
 		),
 	);
 
-	// Merge in the special about group.
-	$groups = array_merge( array( 'about' => $about_group ), $groups );
+	// And now, all the Groups.
+	$groups = get_post_meta( $request_id, '_export_data_grouped', true );
+	if ( is_array( $groups ) ) {
+		// Merge in the special "About" group.
+		$groups       = array_merge( array( 'about' => $about_group ), $groups );
+		$groups_count = count( $groups );
+	} else {
+		if ( false !== $groups ) {
+			_doing_it_wrong(
+				__FUNCTION__,
+				/* translators: %s: Post meta key. */
+				sprintf( __( 'The %s post meta must be an array.' ), '<code>_export_data_grouped</code>' ),
+				'5.8.0'
+			);
+		}
 
-	$groups_count = count( $groups );
+		$groups       = null;
+		$groups_count = 0;
+	}
 
 	// Convert the groups to JSON format.
 	$groups_json = wp_json_encode( $groups );
diff --git a/tests/phpunit/tests/privacy/wpPrivacyGeneratePersonalDataExportFile.php b/tests/phpunit/tests/privacy/wpPrivacyGeneratePersonalDataExportFile.php
index 68d1136cc0..89cb1eeac7 100644
--- a/tests/phpunit/tests/privacy/wpPrivacyGeneratePersonalDataExportFile.php
+++ b/tests/phpunit/tests/privacy/wpPrivacyGeneratePersonalDataExportFile.php
@@ -213,6 +213,75 @@ class Tests_Privacy_WpPrivacyGeneratePersonalDataExportFile extends WP_UnitTestC
 		wp_privacy_generate_personal_data_export_file( self::$export_request_id );
 	}
 
+	/**
+	 * @ticket 51423
+	 *
+	 * @dataProvider data_export_data_grouped_invalid_type
+	 *
+	 * @param mixed $groups '_export_data_grouped' post meta value.
+	 */
+	public function test_doing_it_wrong_for_export_data_grouped_invalid_type( $groups ) {
+		update_post_meta( self::$export_request_id, '_export_data_grouped', $groups );
+
+		$this->setExpectedIncorrectUsage( 'wp_privacy_generate_personal_data_export_file' );
+
+		wp_privacy_generate_personal_data_export_file( self::$export_request_id );
+	}
+
+	public function data_export_data_grouped_invalid_type() {
+		return array(
+			array( 10 ),
+			array( 'WordPress' ),
+			array( null ),
+			array( true ),
+			array( false ),
+			array( new stdClass() ),
+			array( serialize( array( 10, 'WordPress', null, true, false ) ) ),
+			array(
+				json_encode(
+					array(
+						'user' => array(
+							'group_label'       => 'User',
+							'group_description' => 'User&#8217;s profile data.',
+							'items'             => array(
+								'user-1' => array(
+									array(
+										'name'  => 'User ID',
+										'value' => 1,
+									),
+									array(
+										'name'  => 'User Login Name',
+										'value' => 'user_login',
+									),
+									array(
+										'name'  => 'User Nice Name',
+										'value' => 'User Name',
+									),
+									array(
+										'name'  => 'User Email',
+										'value' => 'export-requester@example.com',
+									),
+									array(
+										'name'  => 'User Registration Date',
+										'value' => '2020-01-31 19:29:29',
+									),
+									array(
+										'name'  => 'User Display Name',
+										'value' => 'User Name',
+									),
+									array(
+										'name'  => 'User Nickname',
+										'value' => 'User',
+									),
+								),
+							),
+						),
+					)
+				),
+			),
+		);
+	}
+
 	/**
 	 * Test that an index.php file can be added to the export directory.
 	 *
@@ -240,38 +309,93 @@ class Tests_Privacy_WpPrivacyGeneratePersonalDataExportFile extends WP_UnitTestC
 	 * Test the export HTML file has all the expected parts.
 	 *
 	 * @ticket 44233
+	 * @ticket 46894
+	 * @ticket 51423
+	 *
+	 * @dataProvider data_contents
+	 *
+	 * @param mixed    $groups           '_export_data_grouped' post meta value.
+	 * @param string[] $expected_content Optional. Expected content. Use "html" key for this test.
 	 */
-	public function test_html_contents() {
-		$this->expectOutputString( '' );
-		wp_privacy_generate_personal_data_export_file( self::$export_request_id );
-		$this->assertTrue( file_exists( $this->export_file_name ) );
+	public function test_html_contents( $groups, array $expected_content = array() ) {
+		// Set the _doing_it_wrong assertion.
+		if ( ! is_array( $groups ) ) {
+			$this->setExpectedIncorrectUsage( 'wp_privacy_generate_personal_data_export_file' );
+		}
 
-		$report_dir = trailingslashit( self::$exports_dir . 'test_contents' );
-		mkdir( $report_dir );
+		$request    = wp_get_user_request( self::$export_request_id );
+		$report_dir = $this->setup_export_contents_test( $groups );
 
-		$zip        = new ZipArchive();
-		$opened_zip = $zip->open( $this->export_file_name );
-		$this->assertTrue( $opened_zip );
+		$this->assertFileExists( $report_dir . 'index.html' );
+		$actual_contents = file_get_contents( $report_dir . 'index.html' );
 
-		$zip->extractTo( $report_dir );
-		$zip->close();
-		$this->assertTrue( file_exists( $report_dir . 'index.html' ) );
+		$expected  = "<!DOCTYPE html>\n";
+		$expected .= "<html>\n";
+		$expected .= "<head>\n";
+		$expected .= "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n";
+		$expected .= "<style type='text/css'>body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }th { padding: 5px; text-align: left; width: 20%; }td { padding: 5px; }tr:nth-child(odd) { background-color: #fafafa; }.return-to-top { text-align: right; }</style><title>Personal Data Export for {$request->email}</title></head>\n";
+		$expected .= "<body>\n";
+		$expected .= '<h1 id="top">Personal Data Export</h1>';
 
-		$report_contents = file_get_contents( $report_dir . 'index.html' );
-		$request         = wp_get_user_request( self::$export_request_id );
+		if ( is_array( $groups ) && isset( $expected_content['html'] ) ) {
+			$expected .= $this->replace_timestamp_placeholder( $actual_contents, $expected_content['html'] );
+		}
 
-		$this->assertContains( '<h1 id="top">Personal Data Export</h1>', $report_contents );
-		$this->assertContains( '<h2 id="about-about">About</h2>', $report_contents );
-		$this->assertContains( $request->email, $report_contents );
+		$expected .= "</body>\n";
+		$expected .= "</html>\n";
+
+		$this->assertSame( $expected, $actual_contents );
 	}
 
 	/**
 	 * Test the export JSON file has all the expected parts.
 	 *
 	 * @ticket 49029
+	 * @ticket 46894
+	 * @ticket 51423
+	 *
+	 * @dataProvider data_contents
+	 *
+	 * @param mixed    $groups           '_export_data_grouped' post meta value.
+	 * @param string[] $expected_content Optional. Expected content. Use "json" key for this test.
 	 */
-	public function test_json_contents() {
+	public function test_json_contents( $groups, array $expected_content = array() ) {
+		// Set the _doing_it_wrong assertion.
+		if ( ! is_array( $groups ) ) {
+			$this->setExpectedIncorrectUsage( 'wp_privacy_generate_personal_data_export_file' );
+		}
+
+		$request    = wp_get_user_request( self::$export_request_id );
+		$report_dir = $this->setup_export_contents_test( $groups );
+
+		$this->assertFileExists( $report_dir . 'index.html' );
+		$actual_json = file_get_contents( $report_dir . 'export.json' );
+
+		$expected = '{"Personal Data Export for ' . $request->email . '":';
+		if ( ! is_array( $groups ) ) {
+			$expected .= 'null}';
+		} else {
+			// "About" group: to avoid time difference, use the report's "on" timestamp.
+			$about_group = '{"about":{"group_label":"About","group_description":"Overview of export report.","items":{"about-1":[{"name":"Report generated for","value":"' . $request->email . '"},{"name":"For site","value":"Test Blog"},{"name":"At URL","value":"http:\/\/example.org"},{"name":"On","value":"{{TIMESTAMP}}"}]}}';
+			$expected   .= $this->replace_timestamp_placeholder( $actual_json, $about_group );
+			if ( isset( $expected_content['json'] ) ) {
+				$expected .= $expected_content['json'];
+			}
+			$expected .= '}}';
+		}
+
+		$this->assertSame( $expected, $actual_json );
+	}
+
+	private function setup_export_contents_test( $export_data_grouped = null ) {
+		if ( null === $export_data_grouped ) {
+			delete_post_meta( self::$export_request_id, '_export_data_grouped' );
+		} else {
+			update_post_meta( self::$export_request_id, '_export_data_grouped', $export_data_grouped );
+		}
+
 		$this->expectOutputString( '' );
+
 		wp_privacy_generate_personal_data_export_file( self::$export_request_id );
 		$this->assertTrue( file_exists( $this->export_file_name ) );
 
@@ -285,373 +409,212 @@ class Tests_Privacy_WpPrivacyGeneratePersonalDataExportFile extends WP_UnitTestC
 		$zip->extractTo( $report_dir );
 		$zip->close();
 
-		$request = wp_get_user_request( self::$export_request_id );
-
-		$this->assertTrue( file_exists( $report_dir . 'export.json' ) );
-
-		$report_contents_json = file_get_contents( $report_dir . 'export.json' );
-
-		$this->assertContains( '"Personal Data Export for ' . $request->email . '"', $report_contents_json );
-		$this->assertContains( '"about"', $report_contents_json );
+		return $report_dir;
 	}
 
 	/**
-	 * Test the export HTML file containing one export group has no table of contents.
+	 * Replace expected content's timestamp placeholder with the actual content's timestamp.
 	 *
-	 * @ticket 46894
+	 * Used when the expected content has a placeholder, i.e. used to avoid second time differences
+	 * between the test and code.
+	 *
+	 * @param string $actual_content   Content with the actual timestamp.
+	 * @param string $expected_content Expected content that has the timestamp placeholder
+	 *                                 to be replaced with the actual timestamp.
+	 * @return string Updated expected content on success; else original expected content.
 	 */
-	public function test_single_group_export_no_toc_or_return_to_top() {
-		$this->expectOutputString( '' );
-		wp_privacy_generate_personal_data_export_file( self::$export_request_id );
-		$this->assertTrue( file_exists( $this->export_file_name ) );
+	private function replace_timestamp_placeholder( $actual_content, $expected_content ) {
+		$placeholder_pos = stripos( $expected_content, '{{TIMESTAMP}}' );
+		if ( false === $placeholder_pos ) {
+			return $expected_content;
+		}
 
-		$report_dir = trailingslashit( self::$exports_dir . 'test_contents' );
-		mkdir( $report_dir );
+		$needle     = substr( $expected_content, 0, $placeholder_pos );
+		$needle_pos = strpos( $actual_content, $needle ) + strlen( $needle );
+		$timestamp  = substr( $actual_content, $needle_pos, 19 );
 
-		$zip        = new ZipArchive();
-		$opened_zip = $zip->open( $this->export_file_name );
-		$this->assertTrue( $opened_zip );
-
-		$zip->extractTo( $report_dir );
-		$zip->close();
-		$this->assertTrue( file_exists( $report_dir . 'index.html' ) );
-
-		$report_contents = file_get_contents( $report_dir . 'index.html' );
-		$request         = wp_get_user_request( self::$export_request_id );
-
-		$this->assertNotContains( '<div id="table_of_contents">', $report_contents );
-		$this->assertNotContains( '<div class="return-to-top">', $report_contents );
-		$this->assertContains( $request->email, $report_contents );
+		return str_replace( '{{TIMESTAMP}}', $timestamp, $expected_content );
 	}
 
-	/**
-	 * Test the export HTML file containing ore than one export group has a table of contents.
-	 *
-	 * @ticket 46894
-	 */
-	public function test_multiple_group_export_has_toc_and_return_to_top() {
-		$this->expectOutputString( '' );
-
-		// Setup Export Data to contain multiple groups
-		$export_data_grouped = array(
-			'user' => array(
-				'group_label'       => 'User',
-				'group_description' => 'User&#8217;s profile data.',
-				'items'             => array(
-					'user-1' => array(
-						array(
-							'name'  => 'User ID',
-							'value' => 1,
-						),
-						array(
-							'name'  => 'User Login Name',
-							'value' => 'user_login',
-						),
-						array(
-							'name'  => 'User Nice Name',
-							'value' => 'User Name',
-						),
-						array(
-							'name'  => 'User Email',
-							'value' => 'export-requester@example.com',
-						),
-						array(
-							'name'  => 'User Registration Date',
-							'value' => '2020-01-31 19:29:29',
-						),
-						array(
-							'name'  => 'User Display Name',
-							'value' => 'User Name',
-						),
-						array(
-							'name'  => 'User Nickname',
-							'value' => 'User',
+	public function data_contents() {
+		return array(
+			// Unhappy path.
+			'should contain null when integer'           => array(
+				'groups' => 10,
+			),
+			'should contain null when boolean'           => array(
+				'groups' => true,
+			),
+			'should contain null when string'            => array(
+				'groups' => 'string',
+			),
+			'should contain null when object'            => array(
+				'groups' => new stdClass(),
+			),
+			'should contain only about when _export_data_grouped does not exist' => array(
+				'groups' => null,
+			),
+			'should contain only about when empty array' => array(
+				'groups'           => array(),
+				'expected_content' => array(
+					'html' => '<h2 id="about-about">About</h2><p>Overview of export report.</p><div><table><tbody><tr><th>Report generated for</th><td>export-requester@example.com</td></tr><tr><th>For site</th><td>Test Blog</td></tr><tr><th>At URL</th><td><a href="http://example.org">http://example.org</a></td></tr><tr><th>On</th><td>{{TIMESTAMP}}</td></tr></tbody></table></div>',
+				),
+			),
+			// Happy path.
+			'should contain about and export data groups when single group exists' => array(
+				'groups'           => array(
+					'user' => array(
+						'group_label'       => 'User',
+						'group_description' => 'User&#8217;s profile data.',
+						'items'             => array(
+							'user-1' => array(
+								array(
+									'name'  => 'User ID',
+									'value' => 1,
+								),
+								array(
+									'name'  => 'User Login Name',
+									'value' => 'user_login',
+								),
+								array(
+									'name'  => 'User Nice Name',
+									'value' => 'User Name',
+								),
+								array(
+									'name'  => 'User Email',
+									'value' => 'export-requester@example.com',
+								),
+								array(
+									'name'  => 'User Registration Date',
+									'value' => '2020-01-31 19:29:29',
+								),
+								array(
+									'name'  => 'User Display Name',
+									'value' => 'User Name',
+								),
+								array(
+									'name'  => 'User Nickname',
+									'value' => 'User',
+								),
+							),
 						),
 					),
 				),
+				'expected_content' => array(
+					'html' => '<div id="table_of_contents"><h2>Table of Contents</h2><ul><li><a href="#about-about">About</a></li><li><a href="#user-user">User</a></li></ul></div><h2 id="about-about">About</h2><p>Overview of export report.</p><div><table><tbody><tr><th>Report generated for</th><td>export-requester@example.com</td></tr><tr><th>For site</th><td>Test Blog</td></tr><tr><th>At URL</th><td><a href="http://example.org">http://example.org</a></td></tr><tr><th>On</th><td>{{TIMESTAMP}}</td></tr></tbody></table><div class="return-to-top"><a href="#top"><span aria-hidden="true">&uarr; </span> Go to top</a></div></div><h2 id="user-user">User</h2><p>User&#8217;s profile data.</p><div><table><tbody><tr><th>User ID</th><td>1</td></tr><tr><th>User Login Name</th><td>user_login</td></tr><tr><th>User Nice Name</th><td>User Name</td></tr><tr><th>User Email</th><td>export-requester@example.com</td></tr><tr><th>User Registration Date</th><td>2020-01-31 19:29:29</td></tr><tr><th>User Display Name</th><td>User Name</td></tr><tr><th>User Nickname</th><td>User</td></tr></tbody></table><div class="return-to-top"><a href="#top"><span aria-hidden="true">&uarr; </span> Go to top</a></div></div>',
+					'json' => ',"user":{"group_label":"User","group_description":"User&#8217;s profile data.","items":{"user-1":[{"name":"User ID","value":1},{"name":"User Login Name","value":"user_login"},{"name":"User Nice Name","value":"User Name"},{"name":"User Email","value":"export-requester@example.com"},{"name":"User Registration Date","value":"2020-01-31 19:29:29"},{"name":"User Display Name","value":"User Name"},{"name":"User Nickname","value":"User"}]}}',
+				),
+			),
+			'should contain about and export data groups when multiple groups exist' => array(
+				'groups'           => array(
+					'user'     => array(
+						'group_label'       => 'User',
+						'group_description' => 'User&#8217;s profile data.',
+						'items'             => array(
+							'user-1' => array(
+								array(
+									'name'  => 'User ID',
+									'value' => 1,
+								),
+								array(
+									'name'  => 'User Login Name',
+									'value' => 'user_login',
+								),
+								array(
+									'name'  => 'User Nice Name',
+									'value' => 'User Name',
+								),
+								array(
+									'name'  => 'User Email',
+									'value' => 'export-requester@example.com',
+								),
+								array(
+									'name'  => 'User Registration Date',
+									'value' => '2020-01-31 19:29:29',
+								),
+								array(
+									'name'  => 'User Display Name',
+									'value' => 'User Name',
+								),
+								array(
+									'name'  => 'User Nickname',
+									'value' => 'User',
+								),
+							),
+						),
+					),
+					'comments' => array(
+						'group_label'       => 'Comments',
+						'group_description' => 'User&#8217;s comment data.',
+						'items'             => array(
+							'comment-2' => array(
+								array(
+									'name'  => 'Comment Author',
+									'value' => 'User Name',
+								),
+								array(
+									'name'  => 'Comment Author Email',
+									'value' => 'export-requester@example.com',
+								),
+								array(
+									'name'  => 'Comment Author IP',
+									'value' => '::1',
+								),
+								array(
+									'name'  => 'Comment Author User Agent',
+									'value' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36',
+								),
+								array(
+									'name'  => 'Comment Date',
+									'value' => '2020-01-31 19:55:19',
+								),
+								array(
+									'name'  => 'Comment Content',
+									'value' => 'Test',
+								),
+								array(
+									'name'  => 'Comment URL',
+									'value' => '<a href="http://localhost:8888/46894/2020/01/31/hello-world/#comment-2" target="_blank" rel="noopener">http://localhost:8888/46894/2020/01/31/hello-world/#comment-2</a>',
+								),
+							),
+							'comment-3' => array(
+								array(
+									'name'  => 'Comment Author',
+									'value' => 'User Name',
+								),
+								array(
+									'name'  => 'Comment Author Email',
+									'value' => 'export-requester@example.com',
+								),
+								array(
+									'name'  => 'Comment Author IP',
+									'value' => '::1',
+								),
+								array(
+									'name'  => 'Comment Author User Agent',
+									'value' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36',
+								),
+								array(
+									'name'  => 'Comment Date',
+									'value' => '2020-01-31 20:55:19',
+								),
+								array(
+									'name'  => 'Comment Content',
+									'value' => 'Test #2',
+								),
+								array(
+									'name'  => 'Comment URL',
+									'value' => '<a href="http://localhost:8888/46894/2020/01/31/hello-world/#comment-3" target="_blank" rel="noopener">http://localhost:8888/46894/2020/01/31/hello-world/#comment-3</a>',
+								),
+							),
+						),
+					),
+				),
+				'expected_content' => array(
+					'html' => '<div id="table_of_contents"><h2>Table of Contents</h2><ul><li><a href="#about-about">About</a></li><li><a href="#user-user">User</a></li><li><a href="#comments-comments">Comments <span class="count">(2)</span></a></li></ul></div><h2 id="about-about">About</h2><p>Overview of export report.</p><div><table><tbody><tr><th>Report generated for</th><td>export-requester@example.com</td></tr><tr><th>For site</th><td>Test Blog</td></tr><tr><th>At URL</th><td><a href="http://example.org">http://example.org</a></td></tr><tr><th>On</th><td>{{TIMESTAMP}}</td></tr></tbody></table><div class="return-to-top"><a href="#top"><span aria-hidden="true">&uarr; </span> Go to top</a></div></div><h2 id="user-user">User</h2><p>User&#8217;s profile data.</p><div><table><tbody><tr><th>User ID</th><td>1</td></tr><tr><th>User Login Name</th><td>user_login</td></tr><tr><th>User Nice Name</th><td>User Name</td></tr><tr><th>User Email</th><td>export-requester@example.com</td></tr><tr><th>User Registration Date</th><td>2020-01-31 19:29:29</td></tr><tr><th>User Display Name</th><td>User Name</td></tr><tr><th>User Nickname</th><td>User</td></tr></tbody></table><div class="return-to-top"><a href="#top"><span aria-hidden="true">&uarr; </span> Go to top</a></div></div><h2 id="comments-comments">Comments <span class="count">(2)</span></h2><p>User&#8217;s comment data.</p><div><table><tbody><tr><th>Comment Author</th><td>User Name</td></tr><tr><th>Comment Author Email</th><td>export-requester@example.com</td></tr><tr><th>Comment Author IP</th><td>::1</td></tr><tr><th>Comment Author User Agent</th><td>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36</td></tr><tr><th>Comment Date</th><td>2020-01-31 19:55:19</td></tr><tr><th>Comment Content</th><td>Test</td></tr><tr><th>Comment URL</th><td><a href="http://localhost:8888/46894/2020/01/31/hello-world/#comment-2">http://localhost:8888/46894/2020/01/31/hello-world/#comment-2</a></td></tr></tbody></table><table><tbody><tr><th>Comment Author</th><td>User Name</td></tr><tr><th>Comment Author Email</th><td>export-requester@example.com</td></tr><tr><th>Comment Author IP</th><td>::1</td></tr><tr><th>Comment Author User Agent</th><td>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36</td></tr><tr><th>Comment Date</th><td>2020-01-31 20:55:19</td></tr><tr><th>Comment Content</th><td>Test #2</td></tr><tr><th>Comment URL</th><td><a href="http://localhost:8888/46894/2020/01/31/hello-world/#comment-3">http://localhost:8888/46894/2020/01/31/hello-world/#comment-3</a></td></tr></tbody></table><div class="return-to-top"><a href="#top"><span aria-hidden="true">&uarr; </span> Go to top</a></div></div>',
+					'json' => ',"user":{"group_label":"User","group_description":"User&#8217;s profile data.","items":{"user-1":[{"name":"User ID","value":1},{"name":"User Login Name","value":"user_login"},{"name":"User Nice Name","value":"User Name"},{"name":"User Email","value":"export-requester@example.com"},{"name":"User Registration Date","value":"2020-01-31 19:29:29"},{"name":"User Display Name","value":"User Name"},{"name":"User Nickname","value":"User"}]}},"comments":{"group_label":"Comments","group_description":"User&#8217;s comment data.","items":{"comment-2":[{"name":"Comment Author","value":"User Name"},{"name":"Comment Author Email","value":"export-requester@example.com"},{"name":"Comment Author IP","value":"::1"},{"name":"Comment Author User Agent","value":"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/79.0.3945.130 Safari\/537.36"},{"name":"Comment Date","value":"2020-01-31 19:55:19"},{"name":"Comment Content","value":"Test"},{"name":"Comment URL","value":"<a href=\"http:\/\/localhost:8888\/46894\/2020\/01\/31\/hello-world\/#comment-2\" target=\"_blank\" rel=\"noopener\">http:\/\/localhost:8888\/46894\/2020\/01\/31\/hello-world\/#comment-2<\/a>"}],"comment-3":[{"name":"Comment Author","value":"User Name"},{"name":"Comment Author Email","value":"export-requester@example.com"},{"name":"Comment Author IP","value":"::1"},{"name":"Comment Author User Agent","value":"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/79.0.3945.130 Safari\/537.36"},{"name":"Comment Date","value":"2020-01-31 20:55:19"},{"name":"Comment Content","value":"Test #2"},{"name":"Comment URL","value":"<a href=\"http:\/\/localhost:8888\/46894\/2020\/01\/31\/hello-world\/#comment-3\" target=\"_blank\" rel=\"noopener\">http:\/\/localhost:8888\/46894\/2020\/01\/31\/hello-world\/#comment-3<\/a>"}]}}',
+				),
 			),
 		);
-		update_post_meta( self::$export_request_id, '_export_data_grouped', $export_data_grouped );
-
-		// Generate Export File
-		wp_privacy_generate_personal_data_export_file( self::$export_request_id );
-		$this->assertTrue( file_exists( $this->export_file_name ) );
-
-		// Cleam-up for subsequent tests
-		update_post_meta( self::$export_request_id, '_export_data_grouped', array() );
-
-		$report_dir = trailingslashit( self::$exports_dir . 'test_contents' );
-		mkdir( $report_dir );
-
-		$zip        = new ZipArchive();
-		$opened_zip = $zip->open( $this->export_file_name );
-		$this->assertTrue( $opened_zip );
-
-		$zip->extractTo( $report_dir );
-		$zip->close();
-		$this->assertTrue( file_exists( $report_dir . 'index.html' ) );
-
-		$report_contents = file_get_contents( $report_dir . 'index.html' );
-		$request         = wp_get_user_request( self::$export_request_id );
-
-		$this->assertContains( '<div id="table_of_contents">', $report_contents );
-		$this->assertContains( '<h2 id="user-user">User</h2>', $report_contents );
-		$this->assertContains( '<div class="return-to-top">', $report_contents );
-		$this->assertContains( $request->email, $report_contents );
-	}
-
-	/**
-	 * Test the export HTML file containing multiple export groups with multiple group items
-	 * has a table of contents with group count.
-	 *
-	 * @ticket 46894
-	 */
-	public function test_multiple_group_export_multiple_items_group_count_in_toc() {
-		$this->expectOutputString( '' );
-
-		// Setup Export Data to contain multiple groups
-		$export_data_grouped = array(
-			'user'     => array(
-				'group_label'       => 'User',
-				'group_description' => 'User&#8217;s profile data.',
-				'items'             => array(
-					'user-1' => array(
-						array(
-							'name'  => 'User ID',
-							'value' => 1,
-						),
-						array(
-							'name'  => 'User Login Name',
-							'value' => 'user_login',
-						),
-						array(
-							'name'  => 'User Nice Name',
-							'value' => 'User Name',
-						),
-						array(
-							'name'  => 'User Email',
-							'value' => 'export-requester@example.com',
-						),
-						array(
-							'name'  => 'User Registration Date',
-							'value' => '2020-01-31 19:29:29',
-						),
-						array(
-							'name'  => 'User Display Name',
-							'value' => 'User Name',
-						),
-						array(
-							'name'  => 'User Nickname',
-							'value' => 'User',
-						),
-					),
-				),
-			),
-			'comments' => array(
-				'group_label'       => 'Comments',
-				'group_description' => 'User&#8217;s comment data.',
-				'items'             => array(
-					'comment-2' => array(
-						array(
-							'name'  => 'Comment Author',
-							'value' => 'User Name',
-						),
-						array(
-							'name'  => 'Comment Author Email',
-							'value' => 'export-requester@example.com',
-						),
-						array(
-							'name'  => 'Comment Author IP',
-							'value' => '::1',
-						),
-						array(
-							'name'  => 'Comment Author User Agent',
-							'value' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36',
-						),
-						array(
-							'name'  => 'Comment Date',
-							'value' => '2020-01-31 19:55:19',
-						),
-						array(
-							'name'  => 'Comment Content',
-							'value' => 'Test',
-						),
-						array(
-							'name'  => 'Comment URL',
-							'value' => '<a href="http://localhost:8888/46894/2020/01/31/hello-world/#comment-2" target="_blank" rel="noopener">http://localhost:8888/46894/2020/01/31/hello-world/#comment-2</a>',
-						),
-					),
-					'comment-3' => array(
-						array(
-							'name'  => 'Comment Author',
-							'value' => 'User Name',
-						),
-						array(
-							'name'  => 'Comment Author Email',
-							'value' => 'export-requester@example.com',
-						),
-						array(
-							'name'  => 'Comment Author IP',
-							'value' => '::1',
-						),
-						array(
-							'name'  => 'Comment Author User Agent',
-							'value' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36',
-						),
-						array(
-							'name'  => 'Comment Date',
-							'value' => '2020-01-31 20:55:19',
-						),
-						array(
-							'name'  => 'Comment Content',
-							'value' => 'Test #2',
-						),
-						array(
-							'name'  => 'Comment URL',
-							'value' => '<a href="http://localhost:8888/46894/2020/01/31/hello-world/#comment-3" target="_blank" rel="noopener">http://localhost:8888/46894/2020/01/31/hello-world/#comment-3</a>',
-						),
-					),
-				),
-			),
-		);
-		update_post_meta( self::$export_request_id, '_export_data_grouped', $export_data_grouped );
-
-		// Generate Export File
-		wp_privacy_generate_personal_data_export_file( self::$export_request_id );
-		$this->assertTrue( file_exists( $this->export_file_name ) );
-
-		// Cleam-up for subsequent tests
-		update_post_meta( self::$export_request_id, '_export_data_grouped', array() );
-
-		$report_dir = trailingslashit( self::$exports_dir . 'test_contents' );
-		mkdir( $report_dir );
-
-		$zip        = new ZipArchive();
-		$opened_zip = $zip->open( $this->export_file_name );
-		$this->assertTrue( $opened_zip );
-
-		$zip->extractTo( $report_dir );
-		$zip->close();
-		$this->assertTrue( file_exists( $report_dir . 'index.html' ) );
-
-		$report_contents = file_get_contents( $report_dir . 'index.html' );
-		$request         = wp_get_user_request( self::$export_request_id );
-
-		$this->assertContains( '<div id="table_of_contents">', $report_contents );
-		$this->assertContains( '<a href="#comments-comments">Comments <span class="count">(2)</span></a>', $report_contents );
-		$this->assertContains( $request->email, $report_contents );
-	}
-
-	/**
-	 * Test the export HTML file containing multiple export groups with no multiple group items
-	 * has a table of contents without group count.
-	 *
-	 * @ticket 46894
-	 */
-	public function test_multiple_group_export_single_items_no_group_count_in_toc() {
-		$this->expectOutputString( '' );
-
-		// Setup Export Data to contain multiple groups
-		$export_data_grouped = array(
-			'user'     => array(
-				'group_label'       => 'User',
-				'group_description' => 'User&#8217;s profile data.',
-				'items'             => array(
-					'user-1' => array(
-						array(
-							'name'  => 'User ID',
-							'value' => 1,
-						),
-						array(
-							'name'  => 'User Login Name',
-							'value' => 'user_login',
-						),
-						array(
-							'name'  => 'User Nice Name',
-							'value' => 'User Name',
-						),
-						array(
-							'name'  => 'User Email',
-							'value' => 'export-requester@example.com',
-						),
-						array(
-							'name'  => 'User Registration Date',
-							'value' => '2020-01-31 19:29:29',
-						),
-						array(
-							'name'  => 'User Display Name',
-							'value' => 'User Name',
-						),
-						array(
-							'name'  => 'User Nickname',
-							'value' => 'User',
-						),
-					),
-				),
-			),
-			'comments' => array(
-				'group_label'       => 'Comments',
-				'group_description' => 'User&#8217;s comment data.',
-				'items'             => array(
-					'comment-2' => array(
-						array(
-							'name'  => 'Comment Author',
-							'value' => 'User Name',
-						),
-						array(
-							'name'  => 'Comment Author Email',
-							'value' => 'export-requester@example.com',
-						),
-						array(
-							'name'  => 'Comment Author IP',
-							'value' => '::1',
-						),
-						array(
-							'name'  => 'Comment Author User Agent',
-							'value' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36',
-						),
-						array(
-							'name'  => 'Comment Date',
-							'value' => '2020-01-31 19:55:19',
-						),
-						array(
-							'name'  => 'Comment Content',
-							'value' => 'Test',
-						),
-						array(
-							'name'  => 'Comment URL',
-							'value' => '<a href="http://localhost:8888/46894/2020/01/31/hello-world/#comment-2" target="_blank" rel="noopener">http://localhost:8888/46894/2020/01/31/hello-world/#comment-2</a>',
-						),
-					),
-				),
-			),
-		);
-		update_post_meta( self::$export_request_id, '_export_data_grouped', $export_data_grouped );
-
-		// Generate Export File
-		wp_privacy_generate_personal_data_export_file( self::$export_request_id );
-		$this->assertTrue( file_exists( $this->export_file_name ) );
-
-		// Cleam-up for subsequent tests
-		update_post_meta( self::$export_request_id, '_export_data_grouped', array() );
-
-		$report_dir = trailingslashit( self::$exports_dir . 'test_contents' );
-		mkdir( $report_dir );
-
-		$zip        = new ZipArchive();
-		$opened_zip = $zip->open( $this->export_file_name );
-		$this->assertTrue( $opened_zip );
-
-		$zip->extractTo( $report_dir );
-		$zip->close();
-		$this->assertTrue( file_exists( $report_dir . 'index.html' ) );
-
-		$report_contents = file_get_contents( $report_dir . 'index.html' );
-		$request         = wp_get_user_request( self::$export_request_id );
-
-		$this->assertContains( '<div id="table_of_contents">', $report_contents );
-		$this->assertNotContains( '<span class="count">', $report_contents );
-		$this->assertContains( $request->email, $report_contents );
-
 	}
 }