diff --git a/tests/phpunit/tests/admin/includes/plugin-install/InstallScreenFunctions_Test.php b/tests/phpunit/tests/admin/includes/plugin-install/InstallScreenFunctions_Test.php new file mode 100644 index 0000000000000..fae1633c9065f --- /dev/null +++ b/tests/phpunit/tests/admin/includes/plugin-install/InstallScreenFunctions_Test.php @@ -0,0 +1,435 @@ +original['request'] = $_REQUEST; + $this->original['get'] = $_GET; + $this->original['wp_list_table'] = $GLOBALS['wp_list_table'] ?? null; + + $_REQUEST = array(); + $_GET = array(); + + $this->api_requests = array(); + $this->api_response = array( + 'seo' => array( + 'name' => 'SEO', + 'slug' => 'seo', + 'count' => 1, + ), + 'forms' => array( + 'name' => 'Contact Forms', + 'slug' => 'contact-forms', + 'count' => 25, + ), + ); + + add_filter( 'pre_http_request', array( $this, 'answer_api' ), 10, 3 ); + add_filter( 'wp_trigger_error_trigger_error', '__return_false' ); + } + + /** + * Restores the globals and the request data. + */ + public function tear_down() { + $_REQUEST = $this->original['request']; + $_GET = $this->original['get']; + + $GLOBALS['wp_list_table'] = $this->original['wp_list_table']; + + parent::tear_down(); + } + + /** + * Answers the requests to WordPress.org, and records them. + * + * @param false|array|WP_Error $preempt The response. + * @param array $args The arguments of the request. + * @param string $url The URL of the request. + * @return array|WP_Error The response. + */ + public function answer_api( $preempt, $args, $url ) { + parse_str( (string) wp_parse_url( $url, PHP_URL_QUERY ), $query ); + + $this->api_requests[] = array( $query['action'] ?? '', $query['request'] ?? array() ); + + if ( is_wp_error( $this->api_response ) ) { + return $this->api_response; + } + + return array( + 'headers' => array(), + 'body' => wp_json_encode( $this->api_response ), + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + 'cookies' => array(), + 'filename' => null, + ); + } + + /** + * Tests that the popular tags come from the API. + * + * @ticket 66558 + * + * @covers ::install_popular_tags + */ + public function test_install_popular_tags_should_ask_the_api_for_the_hot_tags() { + $tags = install_popular_tags( array( 'number' => 5 ) ); + + $this->assertEquals( (object) $this->api_response, $tags ); + $this->assertCount( 1, $this->api_requests ); + $this->assertSame( 'hot_tags', $this->api_requests[0][0] ); + $this->assertSame( '5', $this->api_requests[0][1]['number'] ); + } + + /** + * Tests that the popular tags are cached for three hours. + * + * @ticket 66558 + * + * @covers ::install_popular_tags + */ + public function test_install_popular_tags_should_cache_the_tags_by_arguments() { + $args = array( 'number' => 5 ); + + install_popular_tags( $args ); + + $this->assertEquals( (object) $this->api_response, get_site_transient( 'poptags_' . md5( serialize( $args ) ) ), 'The tags should be cached.' ); + + // With an external object cache, the lifetime is kept by the cache and not in the options. + if ( ! wp_using_ext_object_cache() ) { + $expires = (int) get_site_option( '_site_transient_timeout_poptags_' . md5( serialize( $args ) ) ); + + $this->assertEqualsWithDelta( time() + ( 3 * HOUR_IN_SECONDS ), $expires, 10, 'The tags should be cached for three hours.' ); + } + + $this->api_response = array(); + + $this->assertCount( 2, (array) install_popular_tags( $args ), 'The cached tags should be returned.' ); + $this->assertCount( 1, $this->api_requests, 'The API should be asked once.' ); + + install_popular_tags( array( 'number' => 10 ) ); + + $this->assertCount( 2, $this->api_requests, 'Other arguments should not use the cache.' ); + } + + /** + * Tests that an error is returned and not cached. + * + * @ticket 66558 + * + * @covers ::install_popular_tags + */ + public function test_install_popular_tags_should_return_the_error_of_the_api_without_caching_it() { + $this->api_response = new WP_Error( 'plugins_api_failed', 'The API failed.' ); + + $tags = install_popular_tags(); + + $this->assertWPError( $tags ); + $this->assertFalse( get_site_transient( 'poptags_' . md5( serialize( array() ) ) ), 'The error should not be cached.' ); + } + + /** + * Tests the dashboard of the plugin installer. + * + * @ticket 66558 + * + * @covers ::install_dashboard + */ + public function test_install_dashboard_should_output_the_popular_tags() { + $GLOBALS['wp_list_table'] = new class() { + /** + * Outputs the table. + */ + public function display() { + echo '
'; + } + }; + + $output = get_echo( 'install_dashboard' ); + + $this->assertStringContainsString( '', $output, 'The table should be output.' ); + $this->assertStringContainsString( '