From 26f9122bee878e344635825226076c43d7995e0d Mon Sep 17 00:00:00 2001 From: Peter Petrov Date: Fri, 5 Apr 2024 12:12:09 +0300 Subject: [PATCH] Boost: Add e2e tests to Page Cache (#36501) * Add test for Page Cache meta visibility when module is inactive * Add test for Page Cache header when module is inactive * Add test for Page cache meta visibility when module is active * Add permalink tests for page cache * add changelog * Add cache header test * Update boost CLI to support page cache * Fix test * Update GH actions to include page cache tests * Fix page cache tests * Update file permissions in docker to be writable * Add pnpm script to make files used by page cache writable * Revert permission changes * Fix prepare script * Update script * Update page cache test to check header value as well --- .github/files/e2e-tests/e2e-matrix.js | 8 + .../src/js/features/page-cache/page-cache.tsx | 2 +- projects/plugins/boost/app/lib/class-cli.php | 21 ++- .../boost/changelog/add-more-e2e-tests | 4 + .../boost/tests/e2e/lib/pages/index.js | 1 + .../lib/pages/wp-admin/JetpackBoostPage.js | 15 ++ .../e2e/lib/pages/wp-admin/PermalinksPage.js | 23 +++ projects/plugins/boost/tests/e2e/package.json | 5 +- .../e2e/specs/page-cache/page-cache.test.js | 145 ++++++++++++++++++ 9 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 projects/plugins/boost/changelog/add-more-e2e-tests create mode 100644 projects/plugins/boost/tests/e2e/lib/pages/wp-admin/PermalinksPage.js create mode 100644 projects/plugins/boost/tests/e2e/specs/page-cache/page-cache.test.js diff --git a/.github/files/e2e-tests/e2e-matrix.js b/.github/files/e2e-tests/e2e-matrix.js index f52249d62eda2..0f0a7cb5c10b2 100644 --- a/.github/files/e2e-tests/e2e-matrix.js +++ b/.github/files/e2e-tests/e2e-matrix.js @@ -58,6 +58,14 @@ const projects = [ suite: '', buildGroup: 'jetpack-boost', }, + { + project: 'Jetpack Boost - Page Cache', + path: 'projects/plugins/boost/tests/e2e', + testArgs: [ 'specs/page-cache', '--retries=1' ], + targets: [ 'plugins/boost' ], + suite: '', + buildGroup: 'jetpack-boost', + }, { project: 'Jetpack Boost - Concatenate JS/CSS', path: 'projects/plugins/boost/tests/e2e', diff --git a/projects/plugins/boost/app/assets/src/js/features/page-cache/page-cache.tsx b/projects/plugins/boost/app/assets/src/js/features/page-cache/page-cache.tsx index 93c064f7b9159..3062021653adc 100644 --- a/projects/plugins/boost/app/assets/src/js/features/page-cache/page-cache.tsx +++ b/projects/plugins/boost/app/assets/src/js/features/page-cache/page-cache.tsx @@ -89,7 +89,7 @@ const Meta = () => { return ( pageCache && ( -
+
{ getSummary() }
diff --git a/projects/plugins/boost/app/lib/class-cli.php b/projects/plugins/boost/app/lib/class-cli.php index 3c8650b956431..8282340ee9956 100644 --- a/projects/plugins/boost/app/lib/class-cli.php +++ b/projects/plugins/boost/app/lib/class-cli.php @@ -11,6 +11,9 @@ use Automattic\Jetpack_Boost\Data_Sync\Getting_Started_Entry; use Automattic\Jetpack_Boost\Jetpack_Boost; +use Automattic\Jetpack_Boost\Modules\Optimizations\Page_Cache\Garbage_Collection; +use Automattic\Jetpack_Boost\Modules\Optimizations\Page_Cache\Page_Cache_Setup; +use Automattic\Jetpack_Boost\Modules\Optimizations\Page_Cache\Pre_WordPress\Boost_Cache_Settings; /** * Control your local Jetpack Boost installation. @@ -24,7 +27,7 @@ class CLI { */ private $jetpack_boost; - const MAKE_E2E_TESTS_WORK_MODULES = array( 'critical_css', 'render_blocking_js', 'minify_js', 'minify_css', 'image_cdn', 'image_guide' ); + const MAKE_E2E_TESTS_WORK_MODULES = array( 'critical_css', 'render_blocking_js', 'page_cache', 'minify_js', 'minify_css', 'image_cdn', 'image_guide' ); /** * CLI constructor. @@ -113,6 +116,22 @@ public function getting_started( $args ) { private function set_module_status( $module_slug, $status ) { ( new Status( $module_slug ) )->update( $status ); + if ( $module_slug === 'page_cache' && $status ) { + $setup_result = Page_Cache_Setup::run_setup(); + if ( is_wp_error( $setup_result ) ) { + \WP_CLI::error( + sprintf( + /* translators: %s refers to the error code */ + __( 'Setup: %s', 'jetpack-boost' ), + $setup_result->get_error_code() + ) + ); + } + + Garbage_Collection::activate(); + Boost_Cache_Settings::get_instance()->set( array( 'enabled' => true ) ); + } + $status_label = $status ? __( 'activated', 'jetpack-boost' ) : __( 'deactivated', 'jetpack-boost' ); /* translators: The %1$s refers to the module slug, %2$s refers to the module state (either activated or deactivated)*/ diff --git a/projects/plugins/boost/changelog/add-more-e2e-tests b/projects/plugins/boost/changelog/add-more-e2e-tests new file mode 100644 index 0000000000000..3362e8b82349d --- /dev/null +++ b/projects/plugins/boost/changelog/add-more-e2e-tests @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +Add end to end tests for modules. diff --git a/projects/plugins/boost/tests/e2e/lib/pages/index.js b/projects/plugins/boost/tests/e2e/lib/pages/index.js index 474734695266c..d5d5c1ddb7050 100644 --- a/projects/plugins/boost/tests/e2e/lib/pages/index.js +++ b/projects/plugins/boost/tests/e2e/lib/pages/index.js @@ -1,2 +1,3 @@ export { default as JetpackBoostPage } from './wp-admin/JetpackBoostPage.js'; +export { default as PermalinksPage } from './wp-admin/PermalinksPage.js'; export { default as FirstPostPage } from './frontend/FirstPostPage.js'; diff --git a/projects/plugins/boost/tests/e2e/lib/pages/wp-admin/JetpackBoostPage.js b/projects/plugins/boost/tests/e2e/lib/pages/wp-admin/JetpackBoostPage.js index 21925f1653f70..e8f28a30d7250 100644 --- a/projects/plugins/boost/tests/e2e/lib/pages/wp-admin/JetpackBoostPage.js +++ b/projects/plugins/boost/tests/e2e/lib/pages/wp-admin/JetpackBoostPage.js @@ -126,6 +126,21 @@ export default class JetpackBoostPage extends WpPage { return this.waitForElementToBeVisible( selector ); } + async isThePageCacheMetaInformationVisible() { + const selector = '[data-testid="page-cache-meta"]'; + return this.page.isVisible( selector ); + } + + async waitForPageCacheMetaInfoVisibility() { + const selector = '[data-testid="page-cache-meta"]'; + return this.waitForElementToBeVisible( selector, 3 * 60 * 1000 ); + } + + async waitForPageCachePermalinksErrorVisibility() { + const selector = '[data-testid="module-page_cache"] >> text=Permalink settings must be updated'; + return this.waitForElementToBeVisible( selector, 3 * 60 * 1000 ); + } + async isConcatenateJsMetaVisible() { const selector = '[data-testid="meta-minify_js_excludes"]'; return this.page.isVisible( selector ); diff --git a/projects/plugins/boost/tests/e2e/lib/pages/wp-admin/PermalinksPage.js b/projects/plugins/boost/tests/e2e/lib/pages/wp-admin/PermalinksPage.js new file mode 100644 index 0000000000000..75f1dc5ea89e4 --- /dev/null +++ b/projects/plugins/boost/tests/e2e/lib/pages/wp-admin/PermalinksPage.js @@ -0,0 +1,23 @@ +import WpPage from 'jetpack-e2e-commons/pages/wp-page.js'; +import { resolveSiteUrl } from 'jetpack-e2e-commons/helpers/utils-helper.js'; + +export default class PermalinksPage extends WpPage { + constructor( page ) { + const url = `${ resolveSiteUrl() }/wp-admin/options-permalink.php`; + super( page, { expectedSelectors: [ '.permalink-structure' ], url } ); + } + + async usePlainStructure() { + const selector = '[id="permalink-input-plain"]'; + await this.page.click( selector ); + await this.page.click( '[id="submit"]' ); + await this.waitForLoad(); + } + + async useDayNameStructure() { + const selector = '[id="permalink-input-day-name"]'; + await this.page.click( selector ); + await this.page.click( '[id="submit"]' ); + await this.waitForLoad(); + } +} diff --git a/projects/plugins/boost/tests/e2e/package.json b/projects/plugins/boost/tests/e2e/package.json index deff81a35bce4..f61ad66e688bf 100644 --- a/projects/plugins/boost/tests/e2e/package.json +++ b/projects/plugins/boost/tests/e2e/package.json @@ -18,14 +18,15 @@ "clean": "rm -rf output", "config:decrypt": "openssl enc -md sha1 -aes-256-cbc -d -pass env:CONFIG_KEY -in ./node_modules/jetpack-e2e-commons/config/encrypted.enc -out ./config/local.cjs", "distclean": "rm -rf node_modules", - "env:up": "e2e-env start --activate-plugins boost", + "env:up": "e2e-env start --activate-plugins boost && pnpm run prepare:e2e", "env:down": "e2e-env stop", "env:reset": "e2e-env reset --activate-plugins boost", "tunnel:up": "tunnel up", "tunnel:reset": "tunnel reset", "tunnel:down": "tunnel down", "pretest:run": "pnpm run clean", - "test:run": ". ./node_modules/jetpack-e2e-commons/bin/app-password.sh && playwright install && NODE_CONFIG_DIR='./config' ALLURE_RESULTS_DIR=./output/allure-results NODE_PATH=\"$PWD/node_modules\" playwright test --config=./playwright.config.mjs" + "test:run": ". ./node_modules/jetpack-e2e-commons/bin/app-password.sh && playwright install && NODE_CONFIG_DIR='./config' ALLURE_RESULTS_DIR=./output/allure-results NODE_PATH=\"$PWD/node_modules\" playwright test --config=./playwright.config.mjs", + "prepare:e2e": "pnpm jetpack docker --type e2e --name t1 exec-silent -- chown -R www-data .htaccess wp-config.php wp-content" }, "devDependencies": { "@playwright/test": "1.39.0", diff --git a/projects/plugins/boost/tests/e2e/specs/page-cache/page-cache.test.js b/projects/plugins/boost/tests/e2e/specs/page-cache/page-cache.test.js new file mode 100644 index 0000000000000..c11a43f319288 --- /dev/null +++ b/projects/plugins/boost/tests/e2e/specs/page-cache/page-cache.test.js @@ -0,0 +1,145 @@ +import { test, expect } from 'jetpack-e2e-commons/fixtures/base-test.js'; +import { boostPrerequisitesBuilder } from '../../lib/env/prerequisites.js'; +import { JetpackBoostPage, PermalinksPage } from '../../lib/pages/index.js'; +import { PostFrontendPage } from 'jetpack-e2e-commons/pages/index.js'; +import { WPLoginPage } from 'jetpack-e2e-commons/pages/wp-admin/index.js'; +import playwrightConfig from 'jetpack-e2e-commons/playwright.config.mjs'; +import { resolveSiteUrl } from 'jetpack-e2e-commons/helpers/utils-helper.js'; + +test.describe( 'Cache module', () => { + let page; + + test.beforeAll( async ( { browser } ) => { + page = await browser.newPage( playwrightConfig.use ); + await boostPrerequisitesBuilder( page ) + .withInactiveModules( [ + 'page_cache', // Make sure it's inactive. + ] ) + .withCleanEnv() + .withConnection( true ) + .build(); + + // Page Cache needs a pretty permalink structure to work properly. + const permalinksPage = await PermalinksPage.visit( page ); + await permalinksPage.useDayNameStructure(); + } ); + + // Disabling the module before each test, because each test will decide if + // it needs the module enabled or not. + test.beforeEach( async () => { + await boostPrerequisitesBuilder( page ).withInactiveModules( [ 'page_cache' ] ).build(); + } ); + + test.afterAll( async () => { + // Reset the environment for any other tests. + await boostPrerequisitesBuilder( page ).withCleanEnv().withConnection( true ).build(); + await page.close(); + } ); + + test( 'No Page Cache meta information should show on the admin when the module is inactive', async () => { + const jetpackBoostPage = await JetpackBoostPage.visit( page ); + expect( + await jetpackBoostPage.isThePageCacheMetaInformationVisible(), + 'Page Cache meta information should not be visible' + ).toBeFalsy(); + } ); + + // Make sure there's no cache header when module is disabled. + test( 'Page Cache header should not be present when module is inactive', async ( { + browser, + } ) => { + const newPage = await browser.newPage( playwrightConfig.use ); + const postFrontPage = await PostFrontendPage.visit( newPage ); + await postFrontPage.logout(); + + newPage.on( 'response', response => { + if ( response.url().replace( /\/$/, '' ) !== resolveSiteUrl().replace( /\/$/, '' ) ) { + return; + } + + expect( + response.headers().hasOwnProperty( 'X-Jetpack-Boost-Cache'.toLowerCase() ), + 'Page Cache header should not be present' + ).toBeFalsy(); + } ); + + await PostFrontendPage.visit( newPage ); + + await newPage.close(); + } ); + + // Make sure there's an error message when trying to enable Page Cache with plain permalinks. + test( 'Enabling Page Cache should show error notice when plain permalinks are enabled', async () => { + const loginPage = await WPLoginPage.visit( page ); + await loginPage.login(); + + const permalinksPage = await PermalinksPage.visit( page ); + await permalinksPage.usePlainStructure(); + + const jetpackBoostPage = await JetpackBoostPage.visit( page ); + await jetpackBoostPage.toggleModule( 'page_cache' ); + expect( + await jetpackBoostPage.waitForPageCachePermalinksErrorVisibility(), + 'Page Cache should show permalink error message when using plain permalink structure' + ).toBeTruthy(); + } ); + + // Make sure Page Cache meta is visible when module is active. + test( 'Page Cache meta information should show on the admin when the module is active', async () => { + const permalinksPage = await PermalinksPage.visit( page ); + await permalinksPage.useDayNameStructure(); + + // Activate the module. + const jetpackBoostPage = await JetpackBoostPage.visit( page ); + await jetpackBoostPage.toggleModule( 'page_cache' ); + + expect( + await jetpackBoostPage.waitForPageCacheMetaInfoVisibility(), + 'Page Cache meta information should be visible' + ).toBeTruthy(); + } ); + + // Make sure there's a cache header when module is enabled. + test( 'Page Cache header should be present when module is active', async ( { browser } ) => { + await boostPrerequisitesBuilder( page ).withActiveModules( [ 'page_cache' ] ).build(); + + const newPage = await browser.newPage( playwrightConfig.use ); + const postFrontPage = await PostFrontendPage.visit( newPage ); + await postFrontPage.logout(); + + let totalVisits = 0; + + newPage.on( 'response', response => { + if ( response.url().replace( /\/$/, '' ) !== resolveSiteUrl().replace( /\/$/, '' ) ) { + return; + } + + totalVisits++; + + const responseHeaders = response.headers(); + const cacheHeaderName = 'X-Jetpack-Boost-Cache'.toLowerCase(); + + // First visit should always be a miss. + if ( totalVisits === 1 ) { + expect( + responseHeaders.hasOwnProperty( cacheHeaderName ) && + responseHeaders[ cacheHeaderName ] === 'miss', + 'Page Cache header should be set to miss on first visit.' + ).toBeTruthy(); + } else { + expect( + responseHeaders.hasOwnProperty( cacheHeaderName ) && + responseHeaders[ cacheHeaderName ] === 'hit', + 'Page Cache header should be set to hit on second visit.' + ).toBeTruthy(); + } + } ); + + await PostFrontendPage.visit( newPage ); + + // Visit again to make sure the cache is hit. + await PostFrontendPage.visit( newPage ); + + await newPage.close(); + } ); +} );