diff --git a/docs/examples/bootstrap.php b/docs/examples/bootstrap.php index ac0abb95220a1..84448cec7e862 100644 --- a/docs/examples/bootstrap.php +++ b/docs/examples/bootstrap.php @@ -1,12 +1,6 @@ =' ) && - ( ! class_exists( PHPUnit\Runner\Version::class ) || version_compare( PHPUnit\Runner\Version::id(), '9.3', '<' ) ) -) { - if ( ! class_exists( PHPUnit\Framework\MockObject\InvocationMocker::class, false ) && - file_exists( "$_tests_dir/includes/phpunit7/MockObject/InvocationMocker.php" ) - ) { - // phpcs:disable WordPressVIPMinimum.Files.IncludingFile.NotAbsolutePath - require "$_tests_dir/includes/phpunit7/MockObject/Builder/NamespaceMatch.php"; - require "$_tests_dir/includes/phpunit7/MockObject/Builder/ParametersMatch.php"; - require "$_tests_dir/includes/phpunit7/MockObject/InvocationMocker.php"; - require "$_tests_dir/includes/phpunit7/MockObject/MockMethod.php"; - // phpcs:enable - } else { - fprintf( - STDOUT, - "Warning: PHPUnit <9.3 is not compatible with PHP 8.0+, and the hack could not be loaded.\n Class %s exists: %s\n File %s exists: %s\n", - PHPUnit\Framework\MockObject\InvocationMocker::class, - class_exists( PHPUnit\Framework\MockObject\InvocationMocker::class, false ) ? 'yes (bad)' : 'no (good)', - "$_tests_dir/includes/phpunit7/MockObject/InvocationMocker.php", - file_exists( "$_tests_dir/includes/phpunit7/MockObject/InvocationMocker.php" ) ? 'yes (good)' : 'no (bad)' - ); - } +if ( ! is_readable( $_plugin_root . '/vendor/autoload.php' ) ) { + echo 'The plugin is not ready for testing.' . PHP_EOL; + echo PHP_EOL; + echo 'Composer dependencies must be installed.' . PHP_EOL; + exit( 1 ); } // Give access to tests_add_filter() function. @@ -96,7 +70,3 @@ function _manually_load_plugin() { // Load Composer autoloader. require $_plugin_root . '/vendor/autoload.php'; - -// Using the Speed Trap Listener provided by WordPress Core testing suite to expose -// slowest running tests. See the configuration in phpunit.xml.dist. -require $_tests_dir . '/includes/listener-loader.php'; diff --git a/docs/monorepo.md b/docs/monorepo.md index 1c9de07d76635..cc89d804ecd74 100644 --- a/docs/monorepo.md +++ b/docs/monorepo.md @@ -234,8 +234,7 @@ WordPress plugins generally want to run within WordPress. All monorepo plugins a Tests will be run against the latest version of WordPress using the variety of supported PHP versions, and against the previous and master versions of WordPress using the PHP version in `.github/versions.sh`. The environment variable `WP_BRANCH` will be set to 'latest', 'previous', or 'master' accordingly. If you have tests that only need to be run once, run them when `WP_BRANCH` is 'latest'. - -Note that the state of WordPress's own PHPUnit integration is currently in flux. For WordPress 5.8 and earlier you need to both use `yoast/phpunit-polyfills` to supply polyfills and need to run with PHPUnit < 8.0 (even on PHP 8, where monkey-patching is required), while for 5.9 you can use `yoast/phpunit-polyfills` normally. Your best bet for the moment is to copy what Jetpack is doing; once the situation has stabilized, we'll update this documentation and [the example bootstrap.php](./examples/bootstrap.php). +When implementing tests within a new plugin, you can follow the example set in [the example bootstrap.php](./examples/bootstrap.php). ### JavaScript tests diff --git a/projects/plugins/jetpack/changelog/update-wp-compat-60-rm-old b/projects/plugins/jetpack/changelog/update-wp-compat-60-rm-old new file mode 100644 index 0000000000000..f995f2ed3dc8b --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-wp-compat-60-rm-old @@ -0,0 +1,4 @@ +Significance: patch +Type: compat + +General: remove backwards compatibility code now that Jetpack requires WordPress 5.9. diff --git a/projects/plugins/jetpack/extensions/blocks/button/edit.js b/projects/plugins/jetpack/extensions/blocks/button/edit.js index d69a4fe200a0f..b243a0dec829e 100644 --- a/projects/plugins/jetpack/extensions/blocks/button/edit.js +++ b/projects/plugins/jetpack/extensions/blocks/button/edit.js @@ -42,12 +42,6 @@ export function ButtonEdit( props ) { usePassthroughAttributes( { attributes, clientId, setAttributes } ); - const onChange = value => { - // TODO: Remove `replace` once minimum Gutenberg version is 8.0 (to fully support `disableLineBreaks`) - const newValue = 'input' === element ? value.replace( /
/gim, ' ' ) : value; - setAttributes( { text: newValue } ); - }; - useEffect( () => { // Reset button width if switching to left or right (floated) alignment for first time. const alignmentChanged = previousAlign !== align; @@ -98,7 +92,7 @@ export function ButtonEdit( props ) { allowedFormats={ 'input' === element ? [] : undefined } className={ buttonClasses } disableLineBreaks={ 'input' === element } - onChange={ onChange } + onChange={ value => setAttributes( { text: value } ) } placeholder={ placeholder || __( 'Add text…', 'jetpack' ) } style={ buttonStyles } value={ text } diff --git a/projects/plugins/jetpack/extensions/blocks/story/player/lib/use-merge-refs.js b/projects/plugins/jetpack/extensions/blocks/story/player/lib/use-merge-refs.js deleted file mode 100644 index ecf154c9b4bfc..0000000000000 --- a/projects/plugins/jetpack/extensions/blocks/story/player/lib/use-merge-refs.js +++ /dev/null @@ -1,79 +0,0 @@ -/** - * WordPress dependencies - */ -import { useRef, useCallback, useLayoutEffect } from '@wordpress/element'; - -/** @typedef {import('@wordpress/element').RefObject} RefObject */ -/** @typedef {import('@wordpress/element').RefCallback} RefCallback */ - -/** - * Merges refs into one ref callback. Ensures the merged ref callbacks are only - * called when it changes (as a result of a `useCallback` dependency update) or - * when the ref value changes. If you don't wish a ref callback to be called on - * every render, wrap it with `useCallback( ref, [] )`. - * Dependencies can be added, but when a dependency changes, the old ref - * callback will be called with `null` and the new ref callback will be called - * with the same node. - * - * @param {Array} refs - The refs to be merged. - * @returns {RefCallback} The merged ref callback. - */ -export default function useMergeRefs( refs ) { - const element = useRef( null ); - const didElementChange = useRef( false ); - const previousRefs = useRef( refs ); - const currentRefs = useRef( refs ); - - // Update on render before the ref callback is called, so the ref callback - // always has access to the current refs. - currentRefs.current = refs; - - // If any of the refs change, call the previous ref with `null` and the new - // ref with the node, except when the element changes in the same cycle, in - // which case the ref callbacks will already have been called. - useLayoutEffect( () => { - refs.forEach( ( ref, index ) => { - const previousRef = previousRefs.current[ index ]; - - if ( - typeof ref === 'function' && - ref !== previousRef && - didElementChange.current === false - ) { - previousRef( null ); - ref( element.current ); - } - } ); - - previousRefs.current = refs; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, refs ); - - // No dependencies, must be reset after every render so ref callbacks are - // correctly called after a ref change. - useLayoutEffect( () => { - didElementChange.current = false; - } ); - - // There should be no dependencies so that `callback` is only called when - // the node changes. - return useCallback( value => { - // Update the element so it can be used when calling ref callbacks on a - // dependency change. - element.current = value; - didElementChange.current = true; - - // When an element changes, the current ref callback should be called - // with the new element and the previous one with `null`. - const refsToUpdate = value ? currentRefs.current : previousRefs.current; - - // Update the latest refs. - refsToUpdate.forEach( ref => { - if ( typeof ref === 'function' ) { - ref( value ); - } else if ( ref && ref.hasOwnProperty( 'current' ) ) { - ref.current = value; - } - } ); - }, [] ); -} diff --git a/projects/plugins/jetpack/extensions/blocks/story/player/modal/frame.js b/projects/plugins/jetpack/extensions/blocks/story/player/modal/frame.js index 0789d71044b2e..71755b6b8c9bf 100644 --- a/projects/plugins/jetpack/extensions/blocks/story/player/modal/frame.js +++ b/projects/plugins/jetpack/extensions/blocks/story/player/modal/frame.js @@ -2,10 +2,12 @@ * WordPress dependencies */ import { ESCAPE, SPACE } from '@wordpress/keycodes'; -import { useFocusReturn, useConstrainedTabbing, useFocusOnMount } from '@wordpress/compose'; - -// TODO: replace with `import { useMergeRefs } from '@wordpress/compose';` when package is upgraded to ^3.24.4 -import useMergeRefs from '../lib/use-merge-refs'; +import { + useFocusReturn, + useConstrainedTabbing, + useFocusOnMount, + useMergeRefs, +} from '@wordpress/compose'; export default function ModalFrame( { overlayClassName, diff --git a/projects/plugins/jetpack/extensions/shared/jetpack-plugin-sidebar.scss b/projects/plugins/jetpack/extensions/shared/jetpack-plugin-sidebar.scss index 7a942fca29665..f951066f2b862 100644 --- a/projects/plugins/jetpack/extensions/shared/jetpack-plugin-sidebar.scss +++ b/projects/plugins/jetpack/extensions/shared/jetpack-plugin-sidebar.scss @@ -19,9 +19,7 @@ } .edit-post-pinned-plugins { - .components-icon-button, // Gutenberg < 7.2.0. @TODO: Remove once we drop support - .components-button.has-icon // Gutenberg >= 7.2.0 - { + .components-button.has-icon { &:not( .is-toggled ), &:hover, &.is-toggled, diff --git a/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-users-endpoint.php b/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-users-endpoint.php index e0bd3cc9f5f79..d5cb1a378971a 100644 --- a/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-users-endpoint.php +++ b/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-users-endpoint.php @@ -142,12 +142,6 @@ public function callback( $path = '', $blog_id = 0 ) { if ( $authors_only ) { $query['capability'] = array( 'edit_posts' ); - // To-do: remove this once Jetpack requires WordPress 5.9. - global $wp_version; - if ( version_compare( $wp_version, '5.9-alpha', '<' ) ) { - $query['who'] = 'authors'; - unset( $query['capability'] ); - } } if ( ! empty( $args['search'] ) ) { diff --git a/projects/plugins/jetpack/modules/shortcodes/slideshow.php b/projects/plugins/jetpack/modules/shortcodes/slideshow.php index 68e64e83dab81..e2e780da32dfc 100644 --- a/projects/plugins/jetpack/modules/shortcodes/slideshow.php +++ b/projects/plugins/jetpack/modules/shortcodes/slideshow.php @@ -216,39 +216,20 @@ public function slideshow_js( $attr ) { // Enqueue scripts. $this->enqueue_scripts(); - $output = ''; - - if ( defined( 'JSON_HEX_AMP' ) ) { - // This is nice to have, but not strictly necessary since we use _wp_specialchars() below. - $gallery = wp_json_encode( $attr['gallery'], JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ); // phpcs:ignore PHPCompatibility - } else { - $gallery = wp_json_encode( $attr['gallery'] ); - } - - $output .= '

' . esc_html__( 'This slideshow requires JavaScript.', 'jetpack' ) . '

'; + $output = '

' . esc_html__( 'This slideshow requires JavaScript.', 'jetpack' ) . '

'; /* - * The input to json_encode() above can contain '"'. - * - * For calls to json_encode() lacking the JSON_HEX_AMP option, - * that '"' is left unaltered. Running '"' through esc_attr() - * also leaves it unaltered since esc_attr() does not double-encode. - * - * This means we end up with an attribute like - * `data-gallery="{"foo":"""}"`, - * which is interpreted by the browser as `{"foo":"""}`, - * which cannot be JSON decoded. - * - * The preferred workaround is to include the JSON_HEX_AMP (and friends) - * options, but these are not available until 5.3.0. - * Alternatively, we can use _wp_specialchars( , , , true ) instead of - * esc_attr(), which will double-encode. - * - * Since we can't rely on JSON_HEX_AMP, we do both. - * - * @todo Update when minimum is PHP 5.3+ + * Checking for JSON_HEX_AMP and friends here allows us to get rid of + * '"', that can sometimes be included in the JSON input in some languages like French. */ - $gallery_attributes = _wp_specialchars( wp_check_invalid_utf8( $gallery ), ENT_QUOTES, false, true ); + $gallery_attributes = _wp_specialchars( + wp_check_invalid_utf8( + wp_json_encode( $attr['gallery'], JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ) + ), + ENT_QUOTES, + false, + true + ); $output .= sprintf( '
', diff --git a/projects/plugins/jetpack/modules/widget-visibility/widget-conditions.php b/projects/plugins/jetpack/modules/widget-visibility/widget-conditions.php index dad9a2415f938..78f9da83a7443 100644 --- a/projects/plugins/jetpack/modules/widget-visibility/widget-conditions.php +++ b/projects/plugins/jetpack/modules/widget-visibility/widget-conditions.php @@ -239,13 +239,6 @@ public static function widget_admin_setup() { 'fields' => array( 'ID', 'display_name' ), ); - // To-do: remove this once Jetpack requires WordPress 5.9. - global $wp_version; - if ( version_compare( $wp_version, '5.9-alpha', '<' ) ) { - $authors_args['who'] = 'authors'; - unset( $authors_args['capability'] ); - } - $authors = get_users( $authors_args ); foreach ( $authors as $author ) { diff --git a/projects/plugins/jetpack/modules/widgets/authors.php b/projects/plugins/jetpack/modules/widgets/authors.php index ecdb86d9fa8c2..d56f2ec321ca4 100644 --- a/projects/plugins/jetpack/modules/widgets/authors.php +++ b/projects/plugins/jetpack/modules/widgets/authors.php @@ -125,13 +125,6 @@ public function widget( $args, $instance ) { ) ); - // To-do: remove this once Jetpack requires WordPress 5.9. - global $wp_version; - if ( version_compare( $wp_version, '5.9-alpha', '<' ) ) { - $get_author_params['who'] = 'authors'; - unset( $get_author_params['capability'] ); - } - $authors = get_users( $get_author_params ); echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped diff --git a/projects/plugins/jetpack/tests/action-phpunit.sh b/projects/plugins/jetpack/tests/action-phpunit.sh index cc4ba9853a081..a349522c8b5d0 100755 --- a/projects/plugins/jetpack/tests/action-phpunit.sh +++ b/projects/plugins/jetpack/tests/action-phpunit.sh @@ -2,5 +2,4 @@ set -eo pipefail -. tests/maybe-downgrade-phpunit.sh exec phpunit "$@" diff --git a/projects/plugins/jetpack/tests/action-test-coverage.sh b/projects/plugins/jetpack/tests/action-test-coverage.sh index 2bdeb4b30b1c8..10200d6ff03ed 100755 --- a/projects/plugins/jetpack/tests/action-test-coverage.sh +++ b/projects/plugins/jetpack/tests/action-test-coverage.sh @@ -2,8 +2,6 @@ set -eo pipefail -. tests/maybe-downgrade-phpunit.sh - PLUGINDIR="$PWD" cd "$MONOREPO_BASE/projects/plugins/jetpack" diff --git a/projects/plugins/jetpack/tests/action-test-php.sh b/projects/plugins/jetpack/tests/action-test-php.sh index 33789244ce591..bc3ee23ff869f 100755 --- a/projects/plugins/jetpack/tests/action-test-php.sh +++ b/projects/plugins/jetpack/tests/action-test-php.sh @@ -2,8 +2,6 @@ set -eo pipefail -. tests/maybe-downgrade-phpunit.sh - echo "::group::Jetpack tests" phpunit echo "::endgroup::" diff --git a/projects/plugins/jetpack/tests/maybe-downgrade-phpunit.sh b/projects/plugins/jetpack/tests/maybe-downgrade-phpunit.sh deleted file mode 100644 index e51a8d397b2f4..0000000000000 --- a/projects/plugins/jetpack/tests/maybe-downgrade-phpunit.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -# Detect versions of WordPress that still require an ancient version of PHPUnit, and -# if necessary downgrade to a version that will work. -# @todo: Remove this once we drop support for WordPress 5.8. Also probably merge find-test-root.php back into bootstrap.php. -TEST_ROOT=$(php -r 'echo require "tests/php/find-test-root.php";') -if grep -q --fixed $'version_compare( $phpunit_version, \'8.0\', \'>=\' )' "$TEST_ROOT/includes/bootstrap.php" && - ! phpunit --version | grep -q '^PHPUnit [567]\.' -then - echo "The version of WordPress under test requires PHPUnit < 8.0, while the installed version is" - phpunit --version - - TMP1="$( composer.json - echo "$TMP2" > composer.lock - - printf 'Now using ' - phpunit --version -else - printf 'Using ' - phpunit --version -fi diff --git a/projects/plugins/jetpack/tests/php/bootstrap.php b/projects/plugins/jetpack/tests/php/bootstrap.php index 2f3b87ee8d03d..319cf416bfc8d 100644 --- a/projects/plugins/jetpack/tests/php/bootstrap.php +++ b/projects/plugins/jetpack/tests/php/bootstrap.php @@ -17,7 +17,46 @@ */ define( 'TESTING_IN_JETPACK', true ); -$test_root = require __DIR__ . '/find-test-root.php'; +// Support for: +// 1. `WP_DEVELOP_DIR` environment variable. +// 2. Plugin installed inside of WordPress.org developer checkout. +// 3. Tests checked out to /tmp. +if ( false !== getenv( 'WP_DEVELOP_DIR' ) ) { + // Defined on command line. + $test_root = getenv( 'WP_DEVELOP_DIR' ); + if ( file_exists( "$test_root/tests/phpunit/" ) ) { + $test_root .= '/tests/phpunit/'; + } +} elseif ( file_exists( '../../../../tests/phpunit/includes/bootstrap.php' ) ) { + // Installed inside wordpress-develop. + $test_root = '../../../../tests/phpunit'; +} elseif ( file_exists( '/vagrant/www/wordpress-develop/public_html/tests/phpunit/includes/bootstrap.php' ) ) { + // VVV. + $test_root = '/vagrant/www/wordpress-develop/public_html/tests/phpunit'; +} elseif ( file_exists( '/srv/www/wordpress-trunk/public_html/tests/phpunit/includes/bootstrap.php' ) ) { + // VVV 3.0. + $test_root = '/srv/www/wordpress-trunk/public_html/tests/phpunit'; +} elseif ( file_exists( '/tmp/wordpress-develop/tests/phpunit/includes/bootstrap.php' ) ) { + // Manual checkout & Jetpack's docker environment. + $test_root = '/tmp/wordpress-develop/tests/phpunit'; +} elseif ( file_exists( '/tmp/wordpress-tests-lib/includes/bootstrap.php' ) ) { + // Legacy tests. + $test_root = '/tmp/wordpress-tests-lib'; +} + +if ( ! isset( $test_root ) || ! file_exists( $test_root . '/includes/bootstrap.php' ) ) { + fprintf( + STDERR, + <<<'EOF' +Failed to automatically locate WordPress or wordpress-develop to run tests. + +Set the WP_DEVELOP_DIR environment variable to point to a copy of WordPress +or wordpress-develop. +EOF + ); + exit( 1 ); +} + echo "Using test root $test_root\n"; $jp_autoloader = __DIR__ . '/../../vendor/autoload.php'; @@ -32,34 +71,6 @@ require $jp_autoloader; -// WordPress until recently required PHPUnit 7.5 or earlier and hacks around a few things to -// make it work with PHP 8. Unfortunately for MockObjects they do it via -// composer.json rather than bootstrap.php, so we have to manually do it here. -// @todo: Remove this once either WP backports their bootstrap changes to 5.8.1 or they release 5.8.2. -if ( version_compare( PHP_VERSION, '8.0', '>=' ) && - ( ! class_exists( PHPUnit\Runner\Version::class ) || version_compare( PHPUnit\Runner\Version::id(), '9.3', '<' ) ) -) { - if ( ! class_exists( PHPUnit\Framework\MockObject\InvocationMocker::class, false ) && - file_exists( "$test_root/includes/phpunit7/MockObject/InvocationMocker.php" ) - ) { - // phpcs:disable WordPressVIPMinimum.Files.IncludingFile.NotAbsolutePath - require "$test_root/includes/phpunit7/MockObject/Builder/NamespaceMatch.php"; - require "$test_root/includes/phpunit7/MockObject/Builder/ParametersMatch.php"; - require "$test_root/includes/phpunit7/MockObject/InvocationMocker.php"; - require "$test_root/includes/phpunit7/MockObject/MockMethod.php"; - // phpcs:enable - } else { - fprintf( - STDOUT, - "Warning: PHPUnit <9.3 is not compatible with PHP 8.0+, and the hack could not be loaded.\n Class %s exists: %s\n File %s exists: %s\n", - PHPUnit\Framework\MockObject\InvocationMocker::class, - class_exists( PHPUnit\Framework\MockObject\InvocationMocker::class, false ) ? 'yes (bad)' : 'no (good)', - "$test_root/includes/phpunit7/MockObject/InvocationMocker.php", - file_exists( "$test_root/includes/phpunit7/MockObject/InvocationMocker.php" ) ? 'yes (good)' : 'no (bad)' - ); - } -} - if ( '1' !== getenv( 'WP_MULTISITE' ) && ( ! defined( 'WP_TESTS_MULTISITE' ) || ! WP_TESTS_MULTISITE ) ) { echo 'To run Jetpack multisite, use -c tests/php.multisite.xml' . PHP_EOL; echo "Disregard Core's -c tests/phpunit/multisite.xml notice below." . PHP_EOL; diff --git a/projects/plugins/jetpack/tests/php/find-test-root.php b/projects/plugins/jetpack/tests/php/find-test-root.php deleted file mode 100644 index 232f36b9fae2c..0000000000000 --- a/projects/plugins/jetpack/tests/php/find-test-root.php +++ /dev/null @@ -1,48 +0,0 @@ -setExpectedDeprecated( $file_path ); $mock = $this->getMockBuilder( stdClass::class )