From 44c386abe4ff56381c08fb97be359bde9ebe0ae5 Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Sun, 15 Aug 2021 11:18:18 +0300 Subject: [PATCH 01/17] Reduce number of calls to Settings::get_table(), ctl_locale and ctl_table filters. --- src/php/class-main.php | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/php/class-main.php b/src/php/class-main.php index a36d789..be9ed76 100644 --- a/src/php/class-main.php +++ b/src/php/class-main.php @@ -285,11 +285,11 @@ public function sanitize_filename( $filename, $filename_raw ) { * Fix string encoding on MacOS. * * @param string $string String. + * @param array $table Conversion table. * * @return string */ - private function fix_mac_string( $string ) { - $table = $this->get_filtered_table(); + private function fix_mac_string( $string, $table ) { $fix_table = Conversion_Tables::get_fix_table_for_mac(); $fix = []; @@ -329,15 +329,6 @@ protected function split_chinese_string( $string, $table ) { return $string; } - /** - * Get transliteration table. - * - * @return array - */ - private function get_filtered_table() { - return (array) apply_filters( 'ctl_table', $this->settings->get_table() ); - } - /** * Transliterate string using a table. * @@ -346,9 +337,9 @@ private function get_filtered_table() { * @return string */ public function transliterate( $string ) { - $table = $this->get_filtered_table(); + $table = (array) apply_filters( 'ctl_table', $this->settings->get_table() ); - $string = $this->fix_mac_string( $string ); + $string = $this->fix_mac_string( $string, $table ); $string = $this->split_chinese_string( $string, $table ); return strtr( $string, $table ); From 5cc8ee048200697e387f741a8eab6d00a9acf7a5 Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Sun, 15 Aug 2021 12:03:47 +0300 Subject: [PATCH 02/17] Fix issue #102. --- src/php/Settings/Abstracts/SettingsBase.php | 9 ++++++ .../Settings/Abstracts/SettingsBaseTest.php | 32 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/php/Settings/Abstracts/SettingsBase.php b/src/php/Settings/Abstracts/SettingsBase.php index acf94f2..7cf8f68 100644 --- a/src/php/Settings/Abstracts/SettingsBase.php +++ b/src/php/Settings/Abstracts/SettingsBase.php @@ -395,6 +395,15 @@ public function setup_sections() { * Setup tabs section. */ public function setup_tabs_section() { + /** + * Protection from the bug in \Automattic\Jetpack\Sync\Sender::get_items_to_send(), + * which sets screen without loading of wp-admin/includes/template.php, + * where add_settings_section() is defined. + */ + if ( ! function_exists( 'add_settings_section' ) ) { + return; + } + $tab = $this->get_active_tab(); add_settings_section( diff --git a/tests/phpunit/Settings/Abstracts/SettingsBaseTest.php b/tests/phpunit/Settings/Abstracts/SettingsBaseTest.php index 0ecf20a..af47fe0 100644 --- a/tests/phpunit/Settings/Abstracts/SettingsBaseTest.php +++ b/tests/phpunit/Settings/Abstracts/SettingsBaseTest.php @@ -179,6 +179,22 @@ public function dp_test_is_main_menu_page() { ]; } + /** + * Test tab_name(). + * + * @throws ReflectionException ReflectionException. + */ + public function test_tab_name() { + $class_name = 'SomeClassName'; + + $subject = Mockery::mock( SettingsBase::class )->makePartial()->shouldAllowMockingProtectedMethods(); + $subject->shouldReceive( 'get_class_name' )->with()->once()->andReturn( $class_name ); + + $this->set_method_accessibility( $subject, 'tab_name' ); + + self::assertSame( $class_name, $subject->tab_name() ); + } + /** * Test get_class_name(). * @@ -492,6 +508,22 @@ public function dp_test_setup_sections() { ]; } + /** + * Test setup_tabs_section() without add_settings_section(). + */ + public function test_setup_tabs_section_without_add_settings_section() { + FunctionMocker::replace( + 'function_exists', + static function ( $function ) { + return 'add_settings_section' !== $function; + } + ); + + $subject = Mockery::mock( SettingsBase::class )->makePartial(); + + $subject->setup_tabs_section(); + } + /** * Test setup_tabs_section(). */ From 5f2a36f7cef92d4e669fc4a349b2cd97f9395162 Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Sun, 15 Aug 2021 13:01:32 +0300 Subject: [PATCH 03/17] Do not try to determine language from wpml more than once. --- src/php/class-main.php | 8 ++++---- tests/phpunit/tests/class-test-main.php | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/php/class-main.php b/src/php/class-main.php index be9ed76..59a292e 100644 --- a/src/php/class-main.php +++ b/src/php/class-main.php @@ -600,16 +600,16 @@ public function wpml_locale_filter( $locale ) { return $this->wpml_locale; } + $this->wpml_locale = $locale; + $language_code = wpml_get_current_language(); $languages = apply_filters( 'wpml_active_languages', null ); - if ( isset( $languages[ $language_code ] ) ) { + if ( isset( $languages[ $language_code ]['default_locale'] ) ) { $this->wpml_locale = $languages[ $language_code ]['default_locale']; - - return $this->wpml_locale; } - return $locale; + return $this->wpml_locale; } /** diff --git a/tests/phpunit/tests/class-test-main.php b/tests/phpunit/tests/class-test-main.php index bc05fcb..a98dede 100644 --- a/tests/phpunit/tests/class-test-main.php +++ b/tests/phpunit/tests/class-test-main.php @@ -1215,16 +1215,14 @@ public function test_wpml_locale_filter( $locale, $language_code, $expected ) { ], ]; - $times = array_key_exists( $language_code, $languages ) ? 1 : 2; - - WP_Mock::userFunction( 'wpml_get_current_language' )->times( $times )->with()->andReturn( $language_code ); + WP_Mock::userFunction( 'wpml_get_current_language' )->times( 1 )->with()->andReturn( $language_code ); WP_Mock::onFilter( 'wpml_active_languages' )->with( null )->reply( $languages ); $subject = Mockery::mock( Main::class )->makePartial(); self::assertSame( $expected, $subject->wpml_locale_filter( $locale ) ); - // Make sure that we do call wpml_get_current_language() anymore if language code exists. + // Make sure that we do not call wpml_get_current_language() more than once. self::assertSame( $expected, $subject->wpml_locale_filter( $locale ) ); } From 28135db9ff55457eaf01adbee0ab304f385a7002 Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Sun, 15 Aug 2021 13:08:31 +0300 Subject: [PATCH 04/17] Add description to readme.txt. --- readme.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/readme.txt b/readme.txt index 02f522d..f7403d1 100644 --- a/readme.txt +++ b/readme.txt @@ -188,6 +188,10 @@ Yes you can! == Changelog == += 5.2.2 (15.08.2021) = +* Fix issue caused by the bug in Jetpack sync. +* Optimize code related to WPML locale filtering. + = 5.2.1 (29.07.2021) = * Determine WPML language only once to improve performance. * Avoid notice on bad SQL request when taxonomies are empty. From 7abc3cad16af9a99c70511f889b199d34e9db015 Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Mon, 6 Sep 2021 10:38:58 +0300 Subject: [PATCH 05/17] Fix endless page loading with WPML. (cherry picked from commit 8718ae12df607d2db613381be7ac6a9c6202f491) --- src/php/class-main.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/php/class-main.php b/src/php/class-main.php index 59a292e..18a0f28 100644 --- a/src/php/class-main.php +++ b/src/php/class-main.php @@ -167,6 +167,8 @@ public function init_hooks() { } if ( class_exists( SitePress::class ) ) { + $this->wpml_locale = $this->get_wpml_locale(); + // We cannot use locale filter here // as WPML reverts locale at PHP_INT_MAX in \WPML\ST\MO\Hooks\LanguageSwitch::filterLocale. add_filter( 'ctl_locale', [ $this, 'wpml_locale_filter' ], - PHP_INT_MAX ); @@ -600,16 +602,19 @@ public function wpml_locale_filter( $locale ) { return $this->wpml_locale; } - $this->wpml_locale = $locale; + return $locale; + } + /** + * Get wpml locale. + * + * @return string|null + */ + private function get_wpml_locale() { $language_code = wpml_get_current_language(); $languages = apply_filters( 'wpml_active_languages', null ); - if ( isset( $languages[ $language_code ]['default_locale'] ) ) { - $this->wpml_locale = $languages[ $language_code ]['default_locale']; - } - - return $this->wpml_locale; + return isset( $languages[ $language_code ] ) ? $languages[ $language_code ]['default_locale'] : null; } /** From ec6247b580dbbb5445982d921e2c39cc69bc0c8e Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Mon, 6 Sep 2021 10:50:10 +0300 Subject: [PATCH 06/17] Fix function mocker version. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5013a0d..969c004 100644 --- a/composer.json +++ b/composer.json @@ -60,7 +60,7 @@ }, "require-dev": { "roave/security-advisories": "dev-master", - "lucatume/function-mocker": "^1.3", + "lucatume/function-mocker": "dev-master", "10up/wp_mock": "0.2 - 0.4", "phpunit/phpunit": "5.7 - 9.5", "squizlabs/php_codesniffer": "^3.6", From dcd6971a735f196b709c966fcad4728aed24868a Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Mon, 6 Sep 2021 11:16:41 +0300 Subject: [PATCH 07/17] Update tests for changes wpml_locale_filter. --- composer.json | 10 ++++---- src/php/class-main.php | 4 +-- tests/phpunit/tests/class-test-main.php | 34 ++++++++++++++++++++----- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/composer.json b/composer.json index 969c004..9329ae9 100644 --- a/composer.json +++ b/composer.json @@ -55,19 +55,19 @@ "prefer-stable": true, "config": { "platform": { - "php": "5.6" + "php": "7.3" } }, "require-dev": { "roave/security-advisories": "dev-master", - "lucatume/function-mocker": "dev-master", - "10up/wp_mock": "0.2 - 0.4", - "phpunit/phpunit": "5.7 - 9.5", "squizlabs/php_codesniffer": "^3.6", "phpcompatibility/php-compatibility": "^9.3", "phpcompatibility/phpcompatibility-wp": "^2.1", "wp-coding-standards/wpcs": "^2.3", - "php-coveralls/php-coveralls": "^v2.4" + "php-coveralls/php-coveralls": "^v2.4", + "lucatume/function-mocker": "^1.3", + "phpunit/phpunit": "^9.5", + "10up/wp_mock": "^0.4.2" }, "autoload": { "classmap": [ diff --git a/src/php/class-main.php b/src/php/class-main.php index 18a0f28..9d03d29 100644 --- a/src/php/class-main.php +++ b/src/php/class-main.php @@ -99,7 +99,7 @@ class Main { * * @var string */ - private $wpml_locale; + protected $wpml_locale; /** * Main constructor. @@ -610,7 +610,7 @@ public function wpml_locale_filter( $locale ) { * * @return string|null */ - private function get_wpml_locale() { + protected function get_wpml_locale() { $language_code = wpml_get_current_language(); $languages = apply_filters( 'wpml_active_languages', null ); diff --git a/tests/phpunit/tests/class-test-main.php b/tests/phpunit/tests/class-test-main.php index a98dede..159b647 100644 --- a/tests/phpunit/tests/class-test-main.php +++ b/tests/phpunit/tests/class-test-main.php @@ -231,9 +231,12 @@ function ( $name ) { * @param boolean $sitepress WPML is active. * * @dataProvider dp_test_init_hooks + * @throws ReflectionException ReflectionException. */ public function test_init_hooks( $polylang, $sitepress ) { - $subject = Mockery::mock( Main::class )->makePartial(); + $wpml_locale = 'en_US'; + + $subject = Mockery::mock( Main::class )->makePartial()->shouldAllowMockingProtectedMethods(); WP_Mock::expectFilterAdded( 'sanitize_title', [ $subject, 'sanitize_title' ], 9, 3 ); WP_Mock::expectFilterAdded( 'sanitize_file_name', [ $subject, 'sanitize_filename' ], 10, 2 ); @@ -263,12 +266,18 @@ function ( $class ) use ( $polylang, $sitepress ) { } if ( $sitepress ) { + $subject->shouldReceive( 'get_wpml_locale' )->andReturn( $wpml_locale ); + WP_Mock::expectFilterAdded( 'ctl_locale', [ $subject, 'wpml_locale_filter' ], - PHP_INT_MAX ); } else { WP_Mock::expectFilterNotAdded( 'ctl_locale', [ $subject, 'wpml_locale_filter' ] ); } $subject->init_hooks(); + + if ( $sitepress ) { + self::assertSame( $wpml_locale, $this->get_protected_property( $subject, 'wpml_locale' ) ); + } } /** @@ -1152,6 +1161,20 @@ function ( $type, $var_name, $filter ) use ( $term_lang_choice ) { /** * Test wpml_locale_filter(). + */ + public function test_wpml_locale_filter() { + $subject = Mockery::mock( Main::class )->makePartial(); + + $locale = 'en_US'; + self::assertSame( $locale, $subject->wpml_locale_filter( $locale ) ); + + $new_locale = 'ru_RU'; + $this->set_protected_property( $subject, 'wpml_locale', $new_locale ); + self::assertSame( $new_locale, $subject->wpml_locale_filter( $locale ) ); + } + + /** + * Test get_wpml_locale(). * * @param string $locale Current locale. * @param string $language_code Current language code. @@ -1159,7 +1182,7 @@ function ( $type, $var_name, $filter ) use ( $term_lang_choice ) { * * @dataProvider dp_test_wpml_locale_filter */ - public function test_wpml_locale_filter( $locale, $language_code, $expected ) { + public function test_get_wpml_locale( $locale, $language_code, $expected ) { $languages = [ 'be' => [ @@ -1220,10 +1243,7 @@ public function test_wpml_locale_filter( $locale, $language_code, $expected ) { $subject = Mockery::mock( Main::class )->makePartial(); - self::assertSame( $expected, $subject->wpml_locale_filter( $locale ) ); - - // Make sure that we do not call wpml_get_current_language() more than once. - self::assertSame( $expected, $subject->wpml_locale_filter( $locale ) ); + self::assertSame( $expected, $subject->get_wpml_locale( $locale ) ); } /** @@ -1234,7 +1254,7 @@ public function test_wpml_locale_filter( $locale, $language_code, $expected ) { public function dp_test_wpml_locale_filter() { return [ 'Existing language code, return locale from wpml' => [ 'en_US', 'ru', 'ru_RU' ], - 'Not existing language code, return from current' => [ 'en_US', 'some', 'en_US' ], + 'Not existing language code, return null' => [ 'en_US', 'some', null ], ]; } From aa6976007b6031a93914857c38ec5b841a9b9fb5 Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Mon, 6 Sep 2021 11:44:58 +0300 Subject: [PATCH 08/17] Fix endless page loading with WPML. --- readme.txt | 1 + src/php/class-main.php | 23 ++++++-- src/php/class-request.php | 109 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 src/php/class-request.php diff --git a/readme.txt b/readme.txt index f7403d1..6d1e7ea 100644 --- a/readme.txt +++ b/readme.txt @@ -191,6 +191,7 @@ Yes you can! = 5.2.2 (15.08.2021) = * Fix issue caused by the bug in Jetpack sync. * Optimize code related to WPML locale filtering. +* Fix endless loading of a taxonomy page with WPML. = 5.2.1 (29.07.2021) = * Determine WPML language only once to improve performance. diff --git a/src/php/class-main.php b/src/php/class-main.php index 9d03d29..8d37498 100644 --- a/src/php/class-main.php +++ b/src/php/class-main.php @@ -24,6 +24,13 @@ */ class Main { + /** + * Request type. + * + * @var Request + */ + protected $request; + /** * Plugin settings. * @@ -105,6 +112,12 @@ class Main { * Main constructor. */ public function __construct() { + $this->request = new Request(); + + if ( $this->request->is_frontend() ) { + return; + } + $this->settings = new Settings(); $this->admin_notices = new Admin_Notices(); $requirements = new Requirements( $this->settings, $this->admin_notices ); @@ -123,7 +136,7 @@ public function __construct() { $this->admin_notices ); - if ( defined( 'WP_CLI' ) && constant( 'WP_CLI' ) ) { + if ( $this->request->is_cli() ) { $this->cli = new WP_CLI( $this->converter ); } @@ -136,7 +149,11 @@ public function __construct() { * @noinspection PhpUndefinedClassInspection */ public function init() { - if ( defined( 'WP_CLI' ) && constant( 'WP_CLI' ) ) { + if ( $this->request->is_frontend() ) { + return; + } + + if ( $this->request->is_cli() ) { try { /** * Method WP_CLI::add_command() accepts class as callable. @@ -511,7 +528,7 @@ public function pll_locale_filter( $locale ) { * @return false|null|string */ private function pll_locale_filter_with_rest() { - if ( ! defined( 'REST_REQUEST' ) || ! constant( 'REST_REQUEST' ) ) { + if ( ! $this->request->is_rest() ) { return null; } diff --git a/src/php/class-request.php b/src/php/class-request.php new file mode 100644 index 0000000..51585ed --- /dev/null +++ b/src/php/class-request.php @@ -0,0 +1,109 @@ +is_cli() || $this->is_rest() ); + } + + /** + * Check if it is a CLI request + * + * @return bool + */ + public function is_cli() { + return defined( 'WP_CLI' ) && constant( 'WP_CLI' ); + } + + /** + * Checks if the current request is a WP REST API request. + * + * Case #1: After WP_REST_Request initialisation + * Case #2: Support "plain" permalink settings + * Case #3: It can happen that WP_Rewrite is not yet initialized, + * so do this (wp-settings.php) + * Case #4: URL Path begins with wp-json/ (your REST prefix) + * Also supports WP installations in subfolders + * + * @return bool + * @author matzeeable + */ + public function is_rest() { + if ( ! isset( $_SERVER['REQUEST_URI'] ) ) { + return false; + } + + // Case #1. + if ( defined( 'REST_REQUEST' ) && constant( 'REST_REQUEST' ) ) { + $this->rest_route = $this->get_rest_route(); + + return true; + } + + // Case #2. + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $rest_route = isset( $_GET['rest_route'] ) ? + filter_input( INPUT_GET, 'rest_route', FILTER_SANITIZE_STRING ) : + ''; + + if ( $rest_route ) { + $this->rest_route = ltrim( $rest_route, '/' ); + + return true; + } + + // Case #3. + global $wp_rewrite; + + if ( null === $wp_rewrite ) { + // @codeCoverageIgnoreStart + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $wp_rewrite = new WP_Rewrite(); + // @codeCoverageIgnoreEnd + } + + $this->rest_route = $this->get_rest_route(); + + // Case #4. + return (bool) $this->rest_route; + } + + /** + * Get REST route. + * Returns route if it is a REST request, otherwise empty string. + * + * @return string + */ + protected function get_rest_route() { + $current_path = wp_parse_url( add_query_arg( [] ), PHP_URL_PATH ); + $rest_path = wp_parse_url( trailingslashit( rest_url() ), PHP_URL_PATH ); + + $is_rest = 0 === strpos( $current_path, $rest_path ); + + return $is_rest ? substr( $current_path, strlen( $rest_path ) ) : ''; + } +} From 13d6c41493e277e9e134846129cde36a2166e8c7 Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Mon, 6 Sep 2021 12:26:41 +0300 Subject: [PATCH 09/17] Update tests for Main class. --- src/php/class-main.php | 1 + tests/phpunit/tests/class-test-main.php | 185 ++++++++++++------------ 2 files changed, 94 insertions(+), 92 deletions(-) diff --git a/src/php/class-main.php b/src/php/class-main.php index 8d37498..710591b 100644 --- a/src/php/class-main.php +++ b/src/php/class-main.php @@ -495,6 +495,7 @@ public function pll_locale_filter( $locale ) { if ( false === $rest_locale ) { return $locale; } + if ( $rest_locale ) { $this->pll_locale = $rest_locale; diff --git a/tests/phpunit/tests/class-test-main.php b/tests/phpunit/tests/class-test-main.php index 159b647..4659b6b 100644 --- a/tests/phpunit/tests/class-test-main.php +++ b/tests/phpunit/tests/class-test-main.php @@ -58,11 +58,22 @@ public function tearDown(): void { public function test_constructor() { $classname = Main::class; + // Test when requirements are met and not frontend. + $requirements_met = true; + $is_frontend = false; + + $request = Mockery::mock( 'overload:' . Request::class ); + $request->shouldReceive( 'is_frontend' )->with()->andReturnUsing( + function () use ( &$is_frontend ) { + return $is_frontend; + } + ); + $request->shouldReceive( 'is_cli' )->with()->andReturn( true ); + Mockery::mock( 'overload:' . Settings::class ); Mockery::mock( 'overload:' . Admin_Notices::class ); - $requirements = Mockery::mock( 'overload:' . Requirements::class ); - $requirements_met = true; + $requirements = Mockery::mock( 'overload:' . Requirements::class ); $requirements->shouldReceive( 'are_requirements_met' )->with()->andReturnUsing( function () use ( &$requirements_met ) { return $requirements_met; @@ -75,28 +86,6 @@ function () use ( &$requirements_met ) { Mockery::mock( 'overload:' . WP_CLI::class ); Mockery::mock( 'overload:' . ACF::class ); - FunctionMocker::replace( - 'defined', - function ( $name ) { - if ( 'WP_CLI' === $name ) { - return true; - } - - return null; - } - ); - - FunctionMocker::replace( - 'constant', - function ( $name ) { - if ( 'WP_CLI' === $name ) { - return true; - } - - return null; - } - ); - // Get mock, without the constructor being called. $mock = $this->getMockBuilder( $classname )->disableOriginalConstructor()->getMock(); @@ -105,6 +94,7 @@ function ( $name ) { $constructor = $reflected_class->getConstructor(); $constructor->invoke( $mock ); + self::assertInstanceOf( Request::class, $this->get_protected_property( $mock, 'request' ) ); self::assertInstanceOf( Settings::class, $this->get_protected_property( $mock, 'settings' ) ); self::assertInstanceOf( Admin_Notices::class, $this->get_protected_property( $mock, 'admin_notices' ) ); self::assertInstanceOf( Post_Conversion_Process::class, $this->get_protected_property( $mock, 'process_all_posts' ) ); @@ -113,6 +103,7 @@ function ( $name ) { self::assertInstanceOf( WP_CLI::class, $this->get_protected_property( $mock, 'cli' ) ); self::assertInstanceOf( ACF::class, $this->get_protected_property( $mock, 'acf' ) ); + // Test when requirements are not met. $requirements_met = false; // Get mock, without the constructor being called. @@ -123,6 +114,7 @@ function ( $name ) { $constructor = $reflected_class->getConstructor(); $constructor->invoke( $mock ); + self::assertInstanceOf( Request::class, $this->get_protected_property( $mock, 'request' ) ); self::assertInstanceOf( Settings::class, $this->get_protected_property( $mock, 'settings' ) ); self::assertInstanceOf( Admin_Notices::class, $this->get_protected_property( $mock, 'admin_notices' ) ); self::assertNull( $this->get_protected_property( $mock, 'process_all_posts' ) ); @@ -130,46 +122,72 @@ function ( $name ) { self::assertNull( $this->get_protected_property( $mock, 'converter' ) ); self::assertNull( $this->get_protected_property( $mock, 'cli' ) ); self::assertNull( $this->get_protected_property( $mock, 'acf' ) ); + + // Test on frontend. + $is_frontend = true; + + // Get mock, without the constructor being called. + $mock = $this->getMockBuilder( $classname )->disableOriginalConstructor()->getMock(); + + // Now call the constructor. + $reflected_class = new ReflectionClass( $classname ); + $constructor = $reflected_class->getConstructor(); + $constructor->invoke( $mock ); + + self::assertInstanceOf( Request::class, $this->get_protected_property( $mock, 'request' ) ); + self::assertNull( $this->get_protected_property( $mock, 'settings' ) ); + self::assertNull( $this->get_protected_property( $mock, 'admin_notices' ) ); + self::assertNull( $this->get_protected_property( $mock, 'process_all_posts' ) ); + self::assertNull( $this->get_protected_property( $mock, 'process_all_terms' ) ); + self::assertNull( $this->get_protected_property( $mock, 'converter' ) ); + self::assertNull( $this->get_protected_property( $mock, 'cli' ) ); + self::assertNull( $this->get_protected_property( $mock, 'acf' ) ); } /** * Test init() + * + * @throws ReflectionException ReflectionException. */ public function test_init() { + $request = Mockery::mock( Request::class ); + $request->shouldReceive( 'is_frontend' )->andReturn( false ); + $request->shouldReceive( 'is_cli' )->andReturn( false ); + $subject = Mockery::mock( Main::class )->makePartial(); + $this->set_protected_property( $subject, 'request', $request ); $subject->shouldReceive( 'init_hooks' )->once(); $subject->init(); } /** - * Test init() with CLI when CLI throws an Exception + * Test init() on frontend + * + * @throws ReflectionException ReflectionException. */ - public function test_init_with_cli_error() { + public function test_init_on_frontend() { + $request = Mockery::mock( Request::class ); + $request->shouldReceive( 'is_frontend' )->andReturn( true ); + $subject = Mockery::mock( Main::class )->makePartial(); + $this->set_protected_property( $subject, 'request', $request ); $subject->shouldReceive( 'init_hooks' )->never(); - FunctionMocker::replace( - 'defined', - function ( $name ) { - if ( 'WP_CLI' === $name ) { - return true; - } - - return null; - } - ); + $subject->init(); + } - FunctionMocker::replace( - 'constant', - function ( $name ) { - if ( 'WP_CLI' === $name ) { - return true; - } + /** + * Test init() with CLI when CLI throws an Exception + */ + public function test_init_with_cli_error() { + $request = Mockery::mock( Request::class ); + $request->shouldReceive( 'is_frontend' )->andReturn( false ); + $request->shouldReceive( 'is_cli' )->andReturn( true ); - return null; - } - ); + $subject = Mockery::mock( Main::class )->makePartial(); + $this->set_protected_property( $subject, 'request', $request ); + $subject->shouldReceive( 'init_hooks' )->never(); $add_command = FunctionMocker::replace( '\WP_CLI::add_command', @@ -189,31 +207,14 @@ function () { * @noinspection PhpRedundantOptionalArgumentInspection */ public function test_init_with_cli() { + $request = Mockery::mock( Request::class ); + $request->shouldReceive( 'is_frontend' )->andReturn( false ); + $request->shouldReceive( 'is_cli' )->andReturn( true ); + $subject = Mockery::mock( Main::class )->makePartial(); + $this->set_protected_property( $subject, 'request', $request ); $subject->shouldReceive( 'init_hooks' )->once(); - FunctionMocker::replace( - 'defined', - function ( $name ) { - if ( 'WP_CLI' === $name ) { - return true; - } - - return null; - } - ); - - FunctionMocker::replace( - 'constant', - function ( $name ) { - if ( 'WP_CLI' === $name ) { - return true; - } - - return null; - } - ); - $add_command = FunctionMocker::replace( '\WP_CLI::add_command', null @@ -937,21 +938,11 @@ public function test_pll_locale_filter_with_rest() { $pll_locale = 'ru'; $data = ''; - $subject = Mockery::mock( Main::class )->makePartial(); - - FunctionMocker::replace( - 'defined', - function ( $constant_name ) { - return 'REST_REQUEST' === $constant_name; - } - ); + $request = Mockery::mock( Request::class ); + $request->shouldReceive( 'is_rest' )->andReturn( true ); - FunctionMocker::replace( - 'constant', - function ( $name ) { - return 'REST_REQUEST' === $name; - } - ); + $subject = Mockery::mock( Main::class )->makePartial(); + $this->set_protected_property( $subject, 'request', $request ); $rest_server = new WP_REST_Server(); WP_Mock::userFunction( 'rest_get_server' )->andReturn( $rest_server ); @@ -969,7 +960,7 @@ function () use ( &$data ) { self::assertSame( $pll_locale, $subject->pll_locale_filter( $locale ) ); // Test that result is cached. - FunctionMocker::replace( 'defined' ); + $request->shouldReceive( 'is_rest' )->andReturn( false ); self::assertSame( $pll_locale, $subject->pll_locale_filter( $locale ) ); } @@ -979,9 +970,11 @@ function () use ( &$data ) { public function test_pll_locale_filter_on_frontend() { $locale = 'en_US'; - $subject = Mockery::mock( Main::class )->makePartial(); + $request = Mockery::mock( Request::class ); + $request->shouldReceive( 'is_rest' )->andReturn( false ); - FunctionMocker::replace( 'defined' ); + $subject = Mockery::mock( Main::class )->makePartial(); + $this->set_protected_property( $subject, 'request', $request ); WP_Mock::userFunction( 'is_admin' )->with()->andReturn( false ); @@ -996,9 +989,11 @@ public function test_pll_locale_filter_with_classic_editor_and_post_id() { $pll_locale = 'ru'; $post_id = 23; - $subject = Mockery::mock( Main::class )->makePartial(); + $request = Mockery::mock( Request::class ); + $request->shouldReceive( 'is_rest' )->andReturn( false ); - FunctionMocker::replace( 'defined' ); + $subject = Mockery::mock( Main::class )->makePartial(); + $this->set_protected_property( $subject, 'request', $request ); WP_Mock::userFunction( 'is_admin' )->with()->andReturn( true ); @@ -1036,9 +1031,11 @@ public function test_pll_locale_filter_with_classic_editor_and_pll_post_id() { $pll_locale = 'ru'; $post_id = 23; - $subject = Mockery::mock( Main::class )->makePartial(); + $request = Mockery::mock( Request::class ); + $request->shouldReceive( 'is_rest' )->andReturn( false ); - FunctionMocker::replace( 'defined' ); + $subject = Mockery::mock( Main::class )->makePartial(); + $this->set_protected_property( $subject, 'request', $request ); WP_Mock::userFunction( 'is_admin' )->with()->andReturn( true ); @@ -1076,9 +1073,11 @@ public function test_pll_locale_filter_with_classic_editor_and_post() { $pll_locale = 'ru'; $post_id = 23; - $subject = Mockery::mock( Main::class )->makePartial(); + $request = Mockery::mock( Request::class ); + $request->shouldReceive( 'is_rest' )->andReturn( false ); - FunctionMocker::replace( 'defined' ); + $subject = Mockery::mock( Main::class )->makePartial(); + $this->set_protected_property( $subject, 'request', $request ); WP_Mock::userFunction( 'is_admin' )->with()->andReturn( true ); @@ -1118,9 +1117,11 @@ public function test_pll_locale_filter_with_term() { $pll_locale = 'ru'; $term_lang_choice = 92; - $subject = Mockery::mock( Main::class )->makePartial(); + $request = Mockery::mock( Request::class ); + $request->shouldReceive( 'is_rest' )->andReturn( false ); - FunctionMocker::replace( 'defined' ); + $subject = Mockery::mock( Main::class )->makePartial(); + $this->set_protected_property( $subject, 'request', $request ); WP_Mock::userFunction( 'is_admin' )->with()->andReturn( true ); From 37752a9c50b228501287c34a4ed62701db4fc6b2 Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Mon, 6 Sep 2021 13:45:23 +0300 Subject: [PATCH 10/17] Add support for switching language in WPML. --- src/php/class-main.php | 17 +++++ tests/phpunit/tests/class-test-main.php | 94 ++++++++++++++++++++++++- 2 files changed, 109 insertions(+), 2 deletions(-) diff --git a/src/php/class-main.php b/src/php/class-main.php index 710591b..1de2fb4 100644 --- a/src/php/class-main.php +++ b/src/php/class-main.php @@ -189,6 +189,8 @@ public function init_hooks() { // We cannot use locale filter here // as WPML reverts locale at PHP_INT_MAX in \WPML\ST\MO\Hooks\LanguageSwitch::filterLocale. add_filter( 'ctl_locale', [ $this, 'wpml_locale_filter' ], - PHP_INT_MAX ); + + add_action( 'wpml_language_has_switched', [ $this, 'wpml_language_has_switched' ], 10, 3 ); } } @@ -635,6 +637,21 @@ protected function get_wpml_locale() { return isset( $languages[ $language_code ] ) ? $languages[ $language_code ]['default_locale'] : null; } + /** + * Save switched locale. + * + * @param null|string $language_code Language code to switch into. + * @param bool|string $cookie_lang Optionally also switch the cookie language to the value given. + * @param string $original_language Original language. + * + * @noinspection PhpUnusedParameterInspection + */ + public function wpml_language_has_switched( $language_code, $cookie_lang, $original_language ) { + $languages = apply_filters( 'wpml_active_languages', null ); + + $this->wpml_locale = isset( $languages[ $language_code ] ) ? $languages[ $language_code ]['default_locale'] : null; + } + /** * Changes array of items into string of items, separated by comma and sql-escaped * diff --git a/tests/phpunit/tests/class-test-main.php b/tests/phpunit/tests/class-test-main.php index 4659b6b..f8ee411 100644 --- a/tests/phpunit/tests/class-test-main.php +++ b/tests/phpunit/tests/class-test-main.php @@ -270,6 +270,10 @@ function ( $class ) use ( $polylang, $sitepress ) { $subject->shouldReceive( 'get_wpml_locale' )->andReturn( $wpml_locale ); WP_Mock::expectFilterAdded( 'ctl_locale', [ $subject, 'wpml_locale_filter' ], - PHP_INT_MAX ); + WP_Mock::expectActionAdded( 'wpml_language_has_switched', [ + $subject, + 'wpml_language_has_switched', + ], 10, 3 ); } else { WP_Mock::expectFilterNotAdded( 'ctl_locale', [ $subject, 'wpml_locale_filter' ] ); } @@ -1254,8 +1258,94 @@ public function test_get_wpml_locale( $locale, $language_code, $expected ) { */ public function dp_test_wpml_locale_filter() { return [ - 'Existing language code, return locale from wpml' => [ 'en_US', 'ru', 'ru_RU' ], - 'Not existing language code, return null' => [ 'en_US', 'some', null ], + 'Existing language code, return from wpml' => [ 'en_US', 'ru', 'ru_RU' ], + 'Not existing language code, return null' => [ 'en_US', 'some', null ], + ]; + } + + /** + * Test wpml_language_has_switched(). + * + * @param string $language_code Current language code. + * @param string $expected Expected. + * + * @dataProvider dp_test_wpml_language_has_switched + * @throws ReflectionException + */ + public function test_wpml_language_has_switched( $language_code, $expected ) { + $languages = [ + 'be' => + [ + 'code' => 'be', + 'id' => '64', + 'english_name' => 'Belarusian', + 'native_name' => 'Belarusian', + 'major' => '0', + 'active' => '1', + 'default_locale' => 'be_BY', + 'encode_url' => '0', + 'tag' => 'be', + 'display_name' => 'Belarusian', + ], + 'en' => + [ + 'code' => 'en', + 'id' => '1', + 'english_name' => 'English', + 'native_name' => 'English', + 'major' => '1', + 'active' => '1', + 'default_locale' => 'en_US', + 'encode_url' => '0', + 'tag' => 'en', + 'display_name' => 'English', + ], + 'ru' => + [ + 'code' => 'ru', + 'id' => '46', + 'english_name' => 'Russian', + 'native_name' => 'Русский', + 'major' => '1', + 'active' => '1', + 'default_locale' => 'ru_RU', + 'encode_url' => '0', + 'tag' => 'ru', + 'display_name' => 'Russian', + ], + 'uk' => + [ + 'code' => 'uk', + 'id' => '55', + 'english_name' => 'Ukrainian', + 'native_name' => 'Ukrainian', + 'major' => '0', + 'active' => '1', + 'default_locale' => 'uk', + 'encode_url' => '0', + 'tag' => 'uk', + 'display_name' => 'Ukrainian', + ], + ]; + + WP_Mock::onFilter( 'wpml_active_languages' )->with( null )->reply( $languages ); + + $subject = Mockery::mock( Main::class )->makePartial(); + + $subject->wpml_language_has_switched( $language_code, 'some cookie', 'en_US' ); + self::assertSame( $expected, $this->get_protected_property( $subject, 'wpml_locale' ) ); + } + + /** + * Data provider for test_wpml_language_has_switched(). + * + * @return array + */ + public function dp_test_wpml_language_has_switched() { + return [ + 'Existing language code' => [ 'ru', 'ru_RU' ], + 'Not existing language code' => [ 'some', null ], + 'Null language code' => [ null, null ], ]; } From 16b16465476498cbebebd258214dfcc446a9c362 Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Mon, 6 Sep 2021 14:17:53 +0300 Subject: [PATCH 11/17] Some WPCS in Main and Test_Main. --- src/php/class-main.php | 4 ++- tests/phpunit/tests/class-test-main.php | 36 +++++++++++++++++++------ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/php/class-main.php b/src/php/class-main.php index 1de2fb4..00820e5 100644 --- a/src/php/class-main.php +++ b/src/php/class-main.php @@ -303,7 +303,7 @@ public function sanitize_filename( $filename, $filename_raw ) { } /** - * Fix string encoding on MacOS. + * Fix string encoding on macOS. * * @param string $string String. * @param array $table Conversion table. @@ -372,6 +372,8 @@ public function transliterate( $string ) { * @link https://kagg.eu/how-to-catch-gutenberg/ * * @return bool + * + * @noinspection PhpIncludeInspection */ private function is_classic_editor_plugin_active() { // @codeCoverageIgnoreStart diff --git a/tests/phpunit/tests/class-test-main.php b/tests/phpunit/tests/class-test-main.php index f8ee411..54ebf9d 100644 --- a/tests/phpunit/tests/class-test-main.php +++ b/tests/phpunit/tests/class-test-main.php @@ -37,6 +37,8 @@ class Test_Main extends Cyr_To_Lat_TestCase { /** * End test + * + * @noinspection PhpLanguageLevelInspection */ public function tearDown(): void { // phpcs:disable WordPress.Security.NonceVerification.Missing @@ -179,6 +181,8 @@ public function test_init_on_frontend() { /** * Test init() with CLI when CLI throws an Exception + * + * @throws ReflectionException ReflectionException. */ public function test_init_with_cli_error() { $request = Mockery::mock( Request::class ); @@ -204,6 +208,7 @@ function () { /** * Test init() with CLI * + * @throws ReflectionException ReflectionException. * @noinspection PhpRedundantOptionalArgumentInspection */ public function test_init_with_cli() { @@ -270,10 +275,12 @@ function ( $class ) use ( $polylang, $sitepress ) { $subject->shouldReceive( 'get_wpml_locale' )->andReturn( $wpml_locale ); WP_Mock::expectFilterAdded( 'ctl_locale', [ $subject, 'wpml_locale_filter' ], - PHP_INT_MAX ); - WP_Mock::expectActionAdded( 'wpml_language_has_switched', [ - $subject, + WP_Mock::expectActionAdded( 'wpml_language_has_switched', - ], 10, 3 ); + [ $subject, 'wpml_language_has_switched' ], + 10, + 3 + ); } else { WP_Mock::expectFilterNotAdded( 'ctl_locale', [ $subject, 'wpml_locale_filter' ] ); } @@ -420,10 +427,10 @@ public function test_sanitize_title_for_insert_term( $title, $term, $expected ) $wpdb->terms = 'wp_terms'; $wpdb->term_taxonomy = 'wp_term_taxonomy'; - $request = "SELECT slug FROM {$wpdb->terms} t LEFT JOIN {$wpdb->term_taxonomy} tt + $request = "SELECT slug FROM $wpdb->terms t LEFT JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id WHERE t.name = %s"; - $prepared_request = 'SELECT slug FROM ' . $wpdb->terms . " t LEFT JOIN {$wpdb->term_taxonomy} tt + $prepared_request = 'SELECT slug FROM ' . $wpdb->terms . " t LEFT JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id WHERE t.name = " . $title; $sql = $prepared_request . ' AND tt.taxonomy IN (' . $prepared_tax . ')'; @@ -480,10 +487,10 @@ public function test_sanitize_title_for_get_terms( $title, $term, $taxonomies, $ $wpdb->terms = 'wp_terms'; $wpdb->term_taxonomy = 'wp_term_taxonomy'; - $request = "SELECT slug FROM {$wpdb->terms} t LEFT JOIN {$wpdb->term_taxonomy} tt + $request = "SELECT slug FROM $wpdb->terms t LEFT JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id WHERE t.name = %s"; - $prepared_request = 'SELECT slug FROM ' . $wpdb->terms . " t LEFT JOIN {$wpdb->term_taxonomy} tt + $prepared_request = 'SELECT slug FROM ' . $wpdb->terms . " t LEFT JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id WHERE t.name = " . $title; @@ -936,6 +943,8 @@ public function dp_test_sanitize_post_name() { /** * Test pll_locale_filter() with REST. + * + * @throws ReflectionException ReflectionException. */ public function test_pll_locale_filter_with_rest() { $locale = 'en_US'; @@ -970,6 +979,8 @@ function () use ( &$data ) { /** * Test pll_locale_filter() on frontend. + * + * @throws ReflectionException ReflectionException. */ public function test_pll_locale_filter_on_frontend() { $locale = 'en_US'; @@ -987,6 +998,8 @@ public function test_pll_locale_filter_on_frontend() { /** * Test pll_locale_filter() with classic editor and post_id. + * + * @throws ReflectionException ReflectionException. */ public function test_pll_locale_filter_with_classic_editor_and_post_id() { $locale = 'en_US'; @@ -1029,6 +1042,8 @@ function ( $type, $var_name, $filter ) use ( $post_id ) { /** * Test pll_locale_filter() with classic editor and pll_post_id. + * + * @throws ReflectionException ReflectionException. */ public function test_pll_locale_filter_with_classic_editor_and_pll_post_id() { $locale = 'en_US'; @@ -1071,6 +1086,8 @@ function ( $type, $var_name, $filter ) use ( $post_id ) { /** * Test pll_locale_filter() with classic editor and post. + * + * @throws ReflectionException ReflectionException. */ public function test_pll_locale_filter_with_classic_editor_and_post() { $locale = 'en_US'; @@ -1114,6 +1131,7 @@ function ( $type, $var_name, $filter ) use ( $post_id ) { /** * Test pll_locale_filter() with term. * + * @throws ReflectionException ReflectionException. * @noinspection PhpUndefinedFieldInspection */ public function test_pll_locale_filter_with_term() { @@ -1166,6 +1184,8 @@ function ( $type, $var_name, $filter ) use ( $term_lang_choice ) { /** * Test wpml_locale_filter(). + * + * @throws ReflectionException ReflectionException. */ public function test_wpml_locale_filter() { $subject = Mockery::mock( Main::class )->makePartial(); @@ -1270,7 +1290,7 @@ public function dp_test_wpml_locale_filter() { * @param string $expected Expected. * * @dataProvider dp_test_wpml_language_has_switched - * @throws ReflectionException + * @throws ReflectionException ReflectionException. */ public function test_wpml_language_has_switched( $language_code, $expected ) { $languages = [ From 788aa639a2182c7d4d13229db970ce8ce30a59af Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Mon, 6 Sep 2021 14:45:27 +0300 Subject: [PATCH 12/17] Fix composer.json. --- composer.json | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 9329ae9..f8590b1 100644 --- a/composer.json +++ b/composer.json @@ -45,29 +45,25 @@ { "type": "vcs", "url": "https://github.com/humbug/php-scoper.git" - }, - { - "type": "vcs", - "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git" } ], "minimum-stability": "dev", "prefer-stable": true, "config": { "platform": { - "php": "7.3" + "php": "5.6" } }, "require-dev": { "roave/security-advisories": "dev-master", + "10up/wp_mock": "0.2 - 0.4", + "lucatume/function-mocker": "dev-master", + "phpunit/phpunit": "5.7 - 9.5", "squizlabs/php_codesniffer": "^3.6", "phpcompatibility/php-compatibility": "^9.3", "phpcompatibility/phpcompatibility-wp": "^2.1", "wp-coding-standards/wpcs": "^2.3", - "php-coveralls/php-coveralls": "^v2.4", - "lucatume/function-mocker": "^1.3", - "phpunit/phpunit": "^9.5", - "10up/wp_mock": "^0.4.2" + "php-coveralls/php-coveralls": "^v2.4" }, "autoload": { "classmap": [ From ade70dceb3d29636621f108395f9c0c6283dcbba Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Mon, 6 Sep 2021 14:50:55 +0300 Subject: [PATCH 13/17] Add compser cache to the CI. --- .github/workflows/ci.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5ea3a7..bb7fac7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,20 @@ jobs: - name: Checkout uses: actions/checkout@v2 + - name: Get Composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Set up Composer caching + uses: actions/cache@v2 + env: + cache-name: cache-composer-dependencies + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + - name: Install PHP uses: shivammathur/setup-php@v2 with: From 6c417a16ac8fb9e3067aee99f00df54451e57013 Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Mon, 6 Sep 2021 14:58:07 +0300 Subject: [PATCH 14/17] Add composer cache to the deploy-to-wp-org.yml. Use better Upload Release Asset action. --- .github/workflows/deploy-to-wp-org.yml | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy-to-wp-org.yml b/.github/workflows/deploy-to-wp-org.yml index 3ccceef..f0850a0 100644 --- a/.github/workflows/deploy-to-wp-org.yml +++ b/.github/workflows/deploy-to-wp-org.yml @@ -12,6 +12,20 @@ jobs: - name: Checkout code uses: actions/checkout@v2 + - name: Get Composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Set up Composer caching + uses: actions/cache@v2 + env: + cache-name: cache-composer-dependencies + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + - name: Install dependencies in prod version run: | composer config github-oauth.github.com ${{ secrets.GITHUB_TOKEN }} @@ -29,11 +43,8 @@ jobs: SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} - name: Upload release asset - uses: actions/upload-release-asset@v1 + uses: softprops/action-gh-release@v1 + with: + files: ${{github.workspace}}/${{ github.event.repository.name }}.zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ github.event.release.upload_url }} - asset_path: ${{github.workspace}}/${{ github.event.repository.name }}.zip - asset_name: ${{ github.event.repository.name }}.zip - asset_content_type: application/zip From 49f1397d1413855eb1c1a7b210d22bdcf6bbcf92 Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Mon, 6 Sep 2021 15:05:01 +0300 Subject: [PATCH 15/17] Bump up version to 5.2.2. --- cyr-to-lat.php | 4 ++-- readme.txt | 3 ++- tests/phpunit/bootstrap.php | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cyr-to-lat.php b/cyr-to-lat.php index 0494630..359fe6f 100644 --- a/cyr-to-lat.php +++ b/cyr-to-lat.php @@ -10,7 +10,7 @@ * Plugin Name: Cyr-To-Lat * Plugin URI: https://wordpress.org/plugins/cyr2lat/ * Description: Convert Non-Latin characters in post and term slugs to Latin characters. Useful for creating human-readable URLs. Based on the original plugin by Anton Skorobogatov. - * Version: 5.2.1 + * Version: 5.2.2 * Requires at least: 5.1 * Requires PHP: 5.6.20 * Author: Sergey Biryukov, Mikhail Kobzarev, Igor Gergel @@ -36,7 +36,7 @@ /** * Plugin version. */ -define( 'CYR_TO_LAT_VERSION', '5.2.1' ); +define( 'CYR_TO_LAT_VERSION', '5.2.2' ); /** * Path to the plugin dir. diff --git a/readme.txt b/readme.txt index 6d1e7ea..77005c8 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: SergeyBiryukov, mihdan, karevn, webvitaly, kaggdesign Tags: cyrillic, belorussian, ukrainian, bulgarian, macedonian, georgian, kazakh, latin, l10n, russian, cyr-to-lat, cyr2lat, rustolat, slugs, translations, transliteration Requires at least: 5.1 Tested up to: 5.8 -Stable tag: 5.2.1 +Stable tag: 5.2.2 Requires PHP: 5.6.20 Convert Non-Latin characters in post, page and term slugs to Latin characters. @@ -192,6 +192,7 @@ Yes you can! * Fix issue caused by the bug in Jetpack sync. * Optimize code related to WPML locale filtering. * Fix endless loading of a taxonomy page with WPML. +* Fix 'nothing found' on a taxonomy page with WPML. = 5.2.1 (29.07.2021) = * Determine WPML language only once to improve performance. diff --git a/tests/phpunit/bootstrap.php b/tests/phpunit/bootstrap.php index 4eb0f2b..f1fe68d 100644 --- a/tests/phpunit/bootstrap.php +++ b/tests/phpunit/bootstrap.php @@ -42,7 +42,7 @@ /** * Plugin version. */ -define( 'CYR_TO_LAT_TEST_VERSION', '5.2.1' ); +define( 'CYR_TO_LAT_TEST_VERSION', '5.2.2' ); /** * Path to the plugin dir. From f09d262b375e2ad745c6242681ce9d862a2395c4 Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Mon, 6 Sep 2021 16:04:28 +0300 Subject: [PATCH 16/17] Add tests for Request class. --- src/php/class-request.php | 22 +- tests/phpunit/tests/class-test-request.php | 240 +++++++++++++++++++++ 2 files changed, 242 insertions(+), 20 deletions(-) create mode 100644 tests/phpunit/tests/class-test-request.php diff --git a/src/php/class-request.php b/src/php/class-request.php index 51585ed..af47113 100644 --- a/src/php/class-request.php +++ b/src/php/class-request.php @@ -14,13 +14,6 @@ */ class Request { - /** - * REST route. - * - * @var string - */ - public $rest_route = ''; - /** * Is frontend. * @@ -59,20 +52,11 @@ public function is_rest() { // Case #1. if ( defined( 'REST_REQUEST' ) && constant( 'REST_REQUEST' ) ) { - $this->rest_route = $this->get_rest_route(); - return true; } // Case #2. - // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $rest_route = isset( $_GET['rest_route'] ) ? - filter_input( INPUT_GET, 'rest_route', FILTER_SANITIZE_STRING ) : - ''; - - if ( $rest_route ) { - $this->rest_route = ltrim( $rest_route, '/' ); - + if ( filter_input( INPUT_GET, 'rest_route', FILTER_SANITIZE_STRING ) ) { return true; } @@ -86,10 +70,8 @@ public function is_rest() { // @codeCoverageIgnoreEnd } - $this->rest_route = $this->get_rest_route(); - // Case #4. - return (bool) $this->rest_route; + return (bool) $this->get_rest_route(); } /** diff --git a/tests/phpunit/tests/class-test-request.php b/tests/phpunit/tests/class-test-request.php new file mode 100644 index 0000000..1b19f24 --- /dev/null +++ b/tests/phpunit/tests/class-test-request.php @@ -0,0 +1,240 @@ +with()->andReturn( $ajax ); + WP_Mock::userFunction( 'is_admin' )->with()->andReturn( $admin ); + + $subject = Mockery::mock( Request::class )->makePartial()->shouldAllowMockingProtectedMethods(); + $subject->shouldReceive( 'is_cli' )->with()->andReturn( $cli ); + $subject->shouldReceive( 'is_rest' )->with()->andReturn( $rest ); + + self::assertSame( $expected, $subject->is_frontend() ); + } + + /** + * Data provider for test_is_frontend(). + * + * @return array + */ + public function dp_test_is_frontend() { + return [ + [ false, false, false, false, true ], + [ false, false, false, true, false ], + [ false, false, true, false, false ], + [ false, false, true, true, false ], + [ false, true, false, false, false ], + [ false, true, false, true, false ], + [ false, true, true, false, false ], + [ false, true, true, true, false ], + [ true, false, false, false, false ], + [ true, false, false, true, false ], + [ true, false, true, false, false ], + [ true, false, true, true, false ], + [ true, true, false, false, false ], + [ true, true, false, true, false ], + [ true, true, true, false, false ], + [ true, true, true, true, false ], + ]; + } + + /** + * Test is_cli(). + * + * @param bool $defined Is constant WP_CLI defined. + * @param bool $constant Its value. + * @param bool $expected Expected. + * + * @dataProvider dp_test_is_cli + */ + public function test_is_cli( $defined, $constant, $expected ) { + FunctionMocker::replace( 'defined', $defined ); + + FunctionMocker::replace( 'constant', $constant ); + + $subject = new Request(); + + self::assertSame( $expected, $subject->is_cli() ); + } + + /** + * Data provider for test_is_cli(). + * + * @return array + */ + public function dp_test_is_cli() { + return [ + [ false, null, false ], + [ true, false, false ], + [ true, true, true ], + ]; + } + + /** + * Test is_rest() when no request_uri. + */ + public function test_is_rest_no_request_uri() { + $subject = new Request(); + + self::assertFalse( $subject->is_rest() ); + } + + /** + * Test is_rest(), case 1. + */ + public function test_is_rest_case_1() { + $subject = new Request(); + + $_SERVER['REQUEST_URI'] = '/wp-json/wp/v2/some-route'; + + FunctionMocker::replace( + 'defined', + function ( $constant_name ) { + return 'REST_REQUEST' === $constant_name; + } + ); + + FunctionMocker::replace( + 'constant', + function ( $name ) { + return 'REST_REQUEST' === $name; + } + ); + + self::assertTrue( $subject->is_rest() ); + } + + /** + * Test is_rest(), case 2. + */ + public function test_is_rest_case_2() { + $subject = new Request(); + + $_SERVER['REQUEST_URI'] = '/wp-json/wp/v2/some-route'; + + FunctionMocker::replace( + 'filter_input', + function ( $type, $var_name, $filter ) { + return ( + INPUT_GET === $type && + 'rest_route' === $var_name && + FILTER_SANITIZE_STRING === $filter + ); + } + ); + + self::assertTrue( $subject->is_rest() ); + } + + /** + * Test is_rest(), case 3 and 4. + * + * @noinspection PhpUndefinedMethodInspection + */ + public function test_is_rest_case_3_and_4() { + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $GLOBALS['wp_rewrite'] = Mockery::mock( 'WP_Rewrite' ); + + $rest_route = '/wp/v2/posts'; + $_SERVER['REQUEST_URI'] = '/wp-json' . $rest_route; + + $subject = Mockery::mock( Request::class )->makePartial()->shouldAllowMockingProtectedMethods(); + $subject->shouldReceive( 'get_rest_route' )->andReturnUsing( + function () use ( &$rest_route ) { + return $rest_route; + } + ); + + self::assertTrue( $subject->is_rest() ); + + $rest_route = ''; + $_SERVER['REQUEST_URI'] = '/' . $rest_route; + + self::assertFalse( $subject->is_rest() ); + } + + /** + * Test get_rest_route(). + * + * @param string $current_path Current path. + * @param string $expected Expected. + * + * @dataProvider dp_test_get_rest_route + * @noinspection PhpUndefinedMethodInspection + */ + public function test_get_rest_route( $current_path, $expected ) { + $current_url = 'https://test.test' . $current_path; + + $rest_path = '/wp-json'; + $rest_url = 'https://test.test' . $rest_path . '/'; + + WP_Mock::userFunction( 'add_query_arg' )->with( [] )->andReturn( $current_url ); + WP_Mock::userFunction( 'wp_parse_url' )->with( $current_url, PHP_URL_PATH )->andReturn( $current_path ); + + WP_Mock::userFunction( 'rest_url' )->andReturn( $rest_url ); + WP_Mock::userFunction( 'trailingslashit' )->andReturnUsing( + function ( $string ) { + return rtrim( $string, '/' ) . '/'; + } + ); + WP_Mock::userFunction( 'wp_parse_url' )->with( $rest_url, PHP_URL_PATH )->andReturn( $rest_path ); + + $subject = Mockery::mock( Request::class )->makePartial(); + + self::assertSame( $expected, $subject->get_rest_route() ); + } + + /** + * Data provider for it_gets_rest_route. + * + * @return array + */ + public function dp_test_get_rest_route() { + return [ + 'rest request' => [ '/wp-json/wp/v2/posts', '/wp/v2/posts' ], + 'some request' => [ '/some-request', '' ], + ]; + } +} From bc8c6e5483cee2bae83097f56be014ecca4ac9d7 Mon Sep 17 00:00:00 2001 From: KAGG Design Date: Mon, 6 Sep 2021 16:15:10 +0300 Subject: [PATCH 17/17] Fix WPCS in the Test_Request class. --- tests/phpunit/tests/class-test-request.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/phpunit/tests/class-test-request.php b/tests/phpunit/tests/class-test-request.php index 1b19f24..c22ca56 100644 --- a/tests/phpunit/tests/class-test-request.php +++ b/tests/phpunit/tests/class-test-request.php @@ -5,6 +5,8 @@ * @package cyr-to-lat */ +// phpcs:disable PHPCompatibility.FunctionDeclarations.NewReturnTypeDeclarations.voidFound + namespace Cyr_To_Lat; use Mockery;