diff --git a/docs/functions-testing-tools/function-stubs.md b/docs/functions-testing-tools/function-stubs.md index b6163b8..7f01e17 100644 --- a/docs/functions-testing-tools/function-stubs.md +++ b/docs/functions-testing-tools/function-stubs.md @@ -88,7 +88,7 @@ Functions\stubs( ); ``` -### Pre-defined stubs for escaping functions +### Pre-defined stubs for WP escaping functions To stub WordPress escaping functions is a very common usage for `Functions\stubs`. @@ -111,7 +111,7 @@ By calling `Functions\stubEscapeFunctions()`, for _all_ of the functions listed It will _not_ be the exact same escape mechanism that WordPress would apply, but "similar enough" for unit tests purpose and could still be helpful to discover some bugs. -### Pre-defined stubs for translation functions +### Pre-defined stubs for WP translation functions Another common usage for `Functions\stubs`, since its introduction, has been to stub translation functions. @@ -141,6 +141,40 @@ Only for functions that both translate and escape \(`esc_html__()`, `esc_html_x( Please note how `Functions\stubTranslationFunctions()` creates stubs for functions that _echo_ translated text, something not easily doable with `Functions\stubs()` alone. +### Pre-defined stubs for WP URL functions + +One very common and repetitive task testing WordPress code without WordPress loaded is to stub +WordPress URLs, a task made easy by the function: + +**`Functions\stubWpUrlFunctions()`** + +When called, it will create a stub for _all_ the following functions: + +* `home_url()` +* `get_home_url()` +* `site_url()` +* `get_site_url()` +* `admin_url()` +* `get_admin_url()` +* `content_url()` +* `rest_url()` +* `get_rest_url()` +* `includes_url()` +* `network_home_url()` +* `network_site_url()` +* `network_admin_url()` +* `user_admin_url()` +* `wp_login_url()` + +The function accepts as first argument the domain to use for subbing the URLs, default to `example.org`. + +E.g., calling `Functions\stubWpUrlFunctions()`, the `home_url()` function returns `https://example.org`, +but calling `Functions\stubWpUrlFunctions('acme.com')`, the `home_url()` function returns `https://acme.org`. + +The function also accepts a second parameter to force the usage of the HTTPS protocol. By default, that +second parameter is `null` which makes the stub use HTTPS, _unless_ the `is_ssl()` function is defined (stubbed), and +it returns `false`. + ### Gotcha for `Functions\stubs` #### Functions that returns null diff --git a/inc/api.php b/inc/api.php index e5c41c7..f00b263 100644 --- a/inc/api.php +++ b/inc/api.php @@ -45,6 +45,7 @@ function tearDown() use Brain\Monkey\Container; use Brain\Monkey\Expectation\EscapeHelper; use Brain\Monkey\Expectation\FunctionStubFactory; + use Brain\Monkey\Expectation\UrlsHelper; use Brain\Monkey\Name\FunctionName; /** @@ -182,6 +183,39 @@ function stubEscapeFunctions() ] ); } + + /** + * Stub URL-related functions with default behavior. + */ + function stubWpUrlFunctions($domain = 'example.org', $use_https = null) + { + $helper = new UrlsHelper($domain, $use_https); + + stubs([ + 'home_url' => $helper->stubUrlCallback(), + 'get_home_url' => $helper->stubUrlForSiteCallback(), + 'site_url' => $helper->stubUrlCallback(), + 'get_site_url' => $helper->stubUrlForSiteCallback(), + 'admin_url' => $helper->stubUrlCallback('wp-admin', 'admin'), + 'get_admin_url' => $helper->stubUrlForSiteCallback('wp-admin', 'admin'), + 'content_url' => $helper->stubUrlCallback('wp-content', null, false), + 'rest_url' => $helper->stubUrlCallback('wp-json'), + 'get_rest_url' => $helper->stubUrlForSiteCallback('wp-json'), + 'includes_url' => $helper->stubUrlCallback('wp-includes'), + 'network_home_url' => $helper->stubUrlCallback(), + 'network_site_url' => $helper->stubUrlCallback(), + 'network_admin_url' => $helper->stubUrlCallback('wp-admin/network', 'admin'), + 'user_admin_url' => $helper->stubUrlCallback('wp-admin/user', 'admin'), + 'wp_login_url' => static function ($redirect = '', $force_reauth = false) use ($helper) { + $callback = $helper->stubUrlCallback(); + $url = $callback('/wp-login.php', 'login'); + $has_redirect = ($redirect !== '') && is_string($redirect); + $url .= $has_redirect ? '?redirect_to=' . urlencode($redirect) : ''; + $url .= $force_reauth ? ($has_redirect ? '&reauth=1' : '?reauth=1') : ''; + return $url; + }, + ]); + } } namespace Brain\Monkey\Actions { diff --git a/src/Expectation/UrlsHelper.php b/src/Expectation/UrlsHelper.php new file mode 100644 index 0000000..20b4f90 --- /dev/null +++ b/src/Expectation/UrlsHelper.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Brain\Monkey\Expectation; + +class UrlsHelper +{ + const DEFAULT_DOMAIN = 'example.org'; + + /** + * @var string + */ + private $domain; + + /** + * @var bool|null + */ + private $use_https; + + /** + * @param mixed $domain + * @param mixed $use_https + */ + public function __construct($domain = null, $use_https = null) + { + $this->domain = (is_string($domain) && $domain !== '') ? $domain : self::DEFAULT_DOMAIN; + $this->use_https = ($use_https === null) + ? null + : (bool)filter_var($use_https, FILTER_VALIDATE_BOOLEAN); + } + + /** + * @param mixed $base_path + * @param mixed $def_schema + * @return \Closure + */ + public function stubUrlForSiteCallback($base_path = '', $def_schema = null) + { + return function ($site_id, $path = '', $schema = null) use ($base_path, $def_schema) { + if (is_string($def_schema) && ($def_schema !== '') && ($schema === null)) { + $schema = $def_schema; + } + return $this->build_url( + $this->build_relative_path($base_path, $path), + $this->determineSchema($schema) + ); + }; + } + + /** + * @param mixed $base_path + * @param mixed $def_schema + * @param mixed $use_schema_arg + * @return \Closure + */ + public function stubUrlCallback($base_path = '', $def_schema = null, $use_schema_arg = true) + { + return function ($path = '', $schema = null) use ($base_path, $def_schema, $use_schema_arg) { + ($def_schema && $schema === null) and $schema = $def_schema; + return $this->build_url( + $this->build_relative_path($base_path, $path), + $this->determineSchema($use_schema_arg ? $schema : null) + ); + }; + } + + /** + * @param string $relative + * @param string|null $schema + * @return string + */ + private function build_url($relative, $schema) + { + return ($schema === null) + ? (($relative === '') ? '/' : $relative) + : $schema . $this->domain . $relative; + } + + /** + * @param mixed $base_path + * @param mixed $path + * @return string + */ + private function build_relative_path($base_path, $path) + { + $path = (($path !== '') && is_string($path)) + ? '/' . ltrim($path, '/') + : ''; + $base_path = (($base_path !== '') && is_string($base_path)) + ? '/' . trim($base_path, '/') + : ''; + + return $base_path . $path; + } + + /** + * @param mixed $schema_argument + * @return string|null + */ + private function determineSchema($schema_argument = null) + { + if ($schema_argument === 'relative') { + return null; + } + + $use_https = $this->use_https; + $is_ssl = function_exists('is_ssl') ? \is_ssl() : true; + if ($use_https === null && !in_array($schema_argument, ['http', 'https'], true)) { + $use_https = $is_ssl; + if ( + !$use_https + && in_array($schema_argument, ['admin', 'login', 'login_post', 'rpc']) + && function_exists('force_ssl_admin') + ) { + $use_https = \force_ssl_admin(); + } + } + if ($schema_argument === 'http') { + $use_https = false; + } + + return $use_https ? 'https://' : 'http://'; + } +} diff --git a/tests/cases/unit/Api/FunctionsTest.php b/tests/cases/unit/Api/FunctionsTest.php index a7e0097..210c283 100644 --- a/tests/cases/unit/Api/FunctionsTest.php +++ b/tests/cases/unit/Api/FunctionsTest.php @@ -431,4 +431,64 @@ public function dataStubsEscapeXml() ], ]; } + + public function testStubWpUrlFunctionsWithDefaults() + { + Functions\stubWpUrlFunctions(); + + static::assertSame('https://example.org', home_url()); + static::assertSame('https://example.org/', home_url('/')); + static::assertSame('https://example.org/', get_home_url(1, '/')); + static::assertSame('https://example.org/', site_url('/')); + static::assertSame('https://example.org/', get_site_url(2, '/')); + static::assertSame('https://example.org/wp-admin/post-new.php', admin_url('/post-new.php')); + static::assertSame('https://example.org/wp-admin/post-new.php', admin_url('post-new.php')); + static::assertSame('/wp-admin/post-new.php', admin_url('/post-new.php', 'relative')); + static::assertSame('https://example.org/wp-admin/', get_admin_url(1, '/')); + static::assertSame('https://example.org/wp-content/plugins/foo/img.jpg', content_url('/plugins/foo/img.jpg')); + static::assertSame('https://example.org/wp-json', rest_url()); + static::assertSame('https://example.org/wp-json/wp/', get_rest_url(1, '/wp/')); + static::assertSame('https://example.org/wp-includes', includes_url()); + static::assertSame('https://example.org', network_home_url()); + static::assertSame('https://example.org', network_site_url()); + static::assertSame('https://example.org/wp-admin/network', network_admin_url()); + static::assertSame('https://example.org/wp-admin/user/', user_admin_url('/')); + } + + public function testStubWpUrlFunctionsWithSettings() + { + Functions\stubWpUrlFunctions('wikipedia.org', false); + + static::assertSame('http://wikipedia.org', home_url()); + static::assertSame('http://wikipedia.org/wp-admin/post-new.php', admin_url('/post-new.php')); + } + + public function testStubWpUrlViaIsSslFunctions() + { + Functions\when('is_ssl')->justReturn(false); + Functions\when('force_ssl_admin')->justReturn(true); + Functions\stubWpUrlFunctions(); + + static::assertSame('http://example.org', home_url()); + static::assertSame('https://example.org/wp-admin/', admin_url('/')); + static::assertSame('https://example.org/wp-admin/network', network_admin_url()); + static::assertSame('https://example.org/wp-login.php', wp_login_url()); + } + + public function testStubWpLoginUrl() + { + Functions\stubWpUrlFunctions(); + Functions\when('is_ssl')->justReturn(true); + + static::assertSame('https://example.org/wp-login.php', wp_login_url()); + static::assertSame('https://example.org/wp-login.php?reauth=1', wp_login_url('', true)); + static::assertSame( + 'https://example.org/wp-login.php?redirect_to=https%3A%2F%2Fexample.org', + wp_login_url('https://example.org') + ); + static::assertSame( + 'https://example.org/wp-login.php?redirect_to=https%3A%2F%2Fexample.org&reauth=1', + wp_login_url('https://example.org', true) + ); + } }