diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index 39e408e405c7..73c4d8005f77 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -85,6 +85,7 @@ 'remove_transients' => \Google\Web_Stories\Remove_Transients::class, 'web_stories_block' => \Google\Web_Stories\Block\Web_Stories_Block::class, 'injector' => \Google\Web_Stories\Infrastructure\Injector::class, + 'speculation_rules' => \Google\Web_Stories\Speculation_Rules::class, ] ) ); diff --git a/includes/Admin/Dashboard.php b/includes/Admin/Dashboard.php index 634a81c0ca8f..231c25b6f8ff 100644 --- a/includes/Admin/Dashboard.php +++ b/includes/Admin/Dashboard.php @@ -35,6 +35,7 @@ use Google\Web_Stories\Decoder; use Google\Web_Stories\Experiments; use Google\Web_Stories\Font_Post_Type; +use Google\Web_Stories\Infrastructure\Conditional; use Google\Web_Stories\Integrations\Site_Kit; use Google\Web_Stories\Integrations\WooCommerce; use Google\Web_Stories\Locale; @@ -48,7 +49,7 @@ /** * Dashboard class. */ -class Dashboard extends Service_Base { +class Dashboard extends Service_Base implements Conditional { /** * Script handle. @@ -215,6 +216,7 @@ public function register(): void { add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] ); add_action( 'admin_notices', [ $this, 'display_link_to_dashboard' ] ); add_action( 'load-web-story_page_stories-dashboard', [ $this, 'load_stories_dashboard' ] ); + add_filter( 'web_stories_speculation_rules', [ $this, 'add_speculation_rules' ] ); } /** @@ -580,4 +582,44 @@ public function display_link_to_dashboard(): void { >> An array containing prerendering rules. + * @since 1.37.0 + */ + public function add_speculation_rules(): array { + $new_story_url = sprintf( + 'post-new.php?post_type=%s', + $this->story_post_type->get_slug() + ); + $edit_story_url = 'post.php?post=*&action=edit'; + $view_story_url = sprintf( + '/%s/*', + $this->story_post_type::REWRITE_SLUG + ); + return [ + 'prerender' => [ + [ + 'source' => 'document', + 'where' => [ + 'href_matches' => [ $edit_story_url, $new_story_url, $view_story_url ], + ], + 'eagerness' => 'moderate', + ], + ], + ]; + } + + /** + * Check whether the conditional object is currently needed. + * + * @since 1.37.0 + * + * @return bool Whether the conditional object is needed. + */ + public static function is_needed(): bool { + return is_admin(); + } } diff --git a/includes/Plugin.php b/includes/Plugin.php index 57213feb1a1d..f6bf586d731e 100644 --- a/includes/Plugin.php +++ b/includes/Plugin.php @@ -140,6 +140,7 @@ class Plugin extends ServiceBasedPlugin { 'user_preferences' => User\Preferences::class, 'remove_transients' => Remove_Transients::class, 'web_stories_block' => Block\Web_Stories_Block::class, + 'speculation_rules' => Speculation_Rules::class, ]; /** @@ -192,6 +193,7 @@ protected function get_shared_instances(): array { Settings::class, Stories_Script_Data::class, User\Preferences::class, + Admin\Dashboard::class, ]; } diff --git a/includes/Speculation_Rules.php b/includes/Speculation_Rules.php new file mode 100644 index 000000000000..23d2834b4dbd --- /dev/null +++ b/includes/Speculation_Rules.php @@ -0,0 +1,116 @@ +story_post_type = $story_post_type; + } + + /** + * Runs on instantiation. + */ + public function register(): void { + add_action( 'admin_footer', [ $this, 'print_rules' ] ); + add_action( 'wp_footer', [ $this, 'print_rules' ] ); + } + + /** + * Retrieves the prerendering rules for a specific page. + * + * @return array>> An array containing prerendering rules. + */ + public function get_rules(): array { + $rules = []; + + if ( ! is_admin() ) { + $view_story_url = sprintf( + '/%s/*', + $this->story_post_type::REWRITE_SLUG + ); + $archive_url = Story_Post_Type::REWRITE_SLUG; + + $rules = [ + 'prerender' => [ + [ + 'source' => 'document', + 'where' => [ + 'href_matches' => [ + $archive_url, + $view_story_url, + ], + ], + 'eagerness' => 'moderate', + ], + ], + ]; + } + + /** + * Filters the prerendering rules. + * + * @param array $rules An array of prerendering rules. + * @since 1.37.0 + */ + return apply_filters( 'web_stories_speculation_rules', $rules ); + } + + /** + * Prints the prerendering rules as an inline script tag. + */ + public function print_rules(): void { + $rules = $this->get_rules(); + if ( empty( $rules ) ) { + return; + } + + $encoded_rules = wp_json_encode( $rules, JSON_UNESCAPED_SLASHES ); + + if ( false !== $encoded_rules ) { + wp_print_inline_script_tag( $encoded_rules, [ 'type' => 'speculationrules' ] ); + } + } +} diff --git a/packages/e2e-tests/src/specs/wordpress/quickEdit.js b/packages/e2e-tests/src/specs/wordpress/quickEdit.js index cb002a2120b7..15f6fe471832 100644 --- a/packages/e2e-tests/src/specs/wordpress/quickEdit.js +++ b/packages/e2e-tests/src/specs/wordpress/quickEdit.js @@ -34,7 +34,8 @@ describe('Quick Edit', () => { await trashAllPosts('web-story'); }); - it('should save story without breaking markup', async () => { + // eslint-disable-next-line jest/no-disabled-tests -- Flaky test. + it.skip('should save story without breaking markup', async () => { await createNewStory(); await expect(page).toMatchElement('input[placeholder="Add title"]'); diff --git a/tests/phpunit/integration/tests/Speculation_Rules.php b/tests/phpunit/integration/tests/Speculation_Rules.php new file mode 100644 index 000000000000..1b1a4a54d645 --- /dev/null +++ b/tests/phpunit/integration/tests/Speculation_Rules.php @@ -0,0 +1,74 @@ +story_post_type = $this->injector->make( Story_Post_Type::class ); + + $this->instance = new \Google\Web_Stories\Speculation_Rules( + $this->story_post_type, + ); + } + + /** + * @covers ::register + */ + public function test_register(): void { + $this->instance->register(); + $this->assertSame( 10, has_action( 'admin_footer', [ $this->instance, 'print_rules' ] ) ); + $this->assertSame( 10, has_action( 'wp_footer', [ $this->instance, 'print_rules' ] ) ); + } + + /** + * @covers ::print_rules + */ + public function test_print_rules(): void { + $expected_rules = [ + 'prerender' => [ + [ + 'source' => 'document', + 'where' => [ + 'href_matches' => [ Story_Post_Type::REWRITE_SLUG, sprintf( '/%s/*', $this->story_post_type::REWRITE_SLUG ) ], + ], + 'eagerness' => 'moderate', + ], + ], + ]; + $output = get_echo( [ $this->instance, 'print_rules' ] ); + $this->instance->get_rules(); + + $encoded_rules = wp_json_encode( $expected_rules, JSON_UNESCAPED_SLASHES ); + $this->assertNotFalse( $encoded_rules ); + $this->assertStringContainsString( $encoded_rules, $output ); + } +}