Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Opt-in: Allow the URL host to be set from the sites home URL rather than WP_TESTS_DOMAIN #614

Merged
merged 13 commits into from
Jan 15, 2025
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Added

- ✨ Experimental feature ✨: Use the home URL as the base URL for testing rather
than `WP_TESTS_DOMAIN`. This can be enabled by calling the
`with_experimental_testing_url_host()` method of the installation manager or
by setting the `MANTLE_EXPERIMENTAL_TESTING_USE_HOME_URL_HOST` environment
variable.

Once enabled, the home URL will be used as the base URL for testing rather
the hard-coded `WP_TESTS_DOMAIN`. It will also infer the HTTPS status from
the home URL.
- Added `with_option()`/`with_home_url()`/`with_site_url()` methods to the installation manager.

### Changed

- Disable `spatie/once`'s cache if found during unit testing.
Expand Down
46 changes: 46 additions & 0 deletions src/mantle/testing/class-installation-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,52 @@ public function with_active_plugins( array $plugins ): static {
return $this->plugins( $plugins );
}

/**
* Define if the testing suite should use the experimental feature that will
* use the site's home URL host as the HTTP host when making requests.
*
* Without enabling this feature, the HTTP host will be set to the value of
* the WP_TESTS_DOMAIN constant and all relative URLs will be calculated from
* that domain.
*
* In the next major release of Mantle, this feature will be enabled by default.
*
* @param bool $enable Whether to enable the experimental feature.
*/
public function with_experimental_testing_url_host( bool $enable = true ): static {
return $this->before(
fn () => putenv( 'MANTLE_EXPERIMENTAL_TESTING_USE_HOME_URL_HOST=' . ( $enable ? '1' : '0' ) ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv
);
}

/**
* Define a custom option to be set after the installation is loaded.
*
* @param string $option Option name.
* @param mixed $value Option value.
*/
public function with_option( string $option, mixed $value ): static {
return $this->loaded( fn () => update_option( $option, $value ) );
}

/**
* Define the site/home URLs to be set after the installation is loaded.
*
* @param string|null $home Home URL.
* @param string|null $site Site URL.
*/
public function with_url( ?string $home = null, ?string $site = null ): static {
if ( $home ) {
$this->with_option( 'home', $home );
}

if ( $site ) {
$this->with_option( 'siteurl', $site );
}

return $this;
}

/**
* Install the Mantle Testing Framework.
*
Expand Down
80 changes: 67 additions & 13 deletions src/mantle/testing/class-pending-testable-request.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class Pending_Testable_Request {
/**
* Indicates whether the request should be made over HTTPS.
*/
public bool $https = false;
public ?bool $forced_https = null;

/**
* The cookies for the request.
Expand Down Expand Up @@ -95,12 +95,15 @@ public function with_header( string $name, string $value ): static {
}

/**
* Define whether the request should be made over HTTPS.
* Define whether the request should be forced to be made over HTTPS.
*
* @param bool $value Whether to use HTTPS.
* This method will override the protocol of the URL passed when creating a
* testable request.
*
* @param bool|null $value Whether to use HTTPS.
*/
public function with_https( bool $value ): static {
$this->https = $value;
public function with_https( ?bool $value ): static {
$this->forced_https = $value;

return $this;
}
Expand Down Expand Up @@ -235,11 +238,14 @@ public function call( string $method, mixed $uri, array $parameters = [], array
$uri = $this->infer_url( $uri );
}

$scheme = $this->get_default_url_scheme();
$host = $this->get_default_url_host();

// Build a full URL from partial URIs.
if ( '/' === $uri[0] ) {
$url = 'https://' . WP_TESTS_DOMAIN . $uri;
$url = "{$scheme}://{$host}{$uri}";
} elseif ( false === strpos( $uri, '://' ) ) {
$url = 'https://' . WP_TESTS_DOMAIN . '/' . $uri;
$url = "{$scheme}://{$host}/{uri}";
} else {
$url = $uri;
}
Expand Down Expand Up @@ -431,11 +437,8 @@ protected function reset_request_state(): void {
}
}

if ( $this->https ) {
$_SERVER['HTTPS'] = 'on';
} else {
unset( $_SERVER['HTTPS'] );
}
// Clear the HTTPS flag which will be set as-needed by the call method.
unset( $_SERVER['HTTPS'] );

// phpcs:enable
}
Expand All @@ -452,8 +455,12 @@ protected function reset_request_state(): void {
protected function set_server_state( $method, $url, $server, $data, array $cookies = [] ): void {
// phpcs:disable WordPress.Security.NonceVerification
$_SERVER['REQUEST_METHOD'] = strtoupper( $method );
$_SERVER['SERVER_NAME'] = WP_TESTS_DOMAIN;
$_SERVER['SERVER_PORT'] = '80';

$_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'] = $this->is_experimental_use_home_url_host_enabled()
? wp_parse_url( home_url(), PHP_URL_HOST )
: WP_TESTS_DOMAIN;

unset( $_SERVER['PATH_INFO'] );

$parts = wp_parse_url( $url );
Expand All @@ -468,6 +475,11 @@ protected function set_server_state( $method, $url, $server, $data, array $cooki
$req = $url;
}

// Set HTTPS if it is being forced or if the URL being requested is HTTPS.
if ( $this->forced_https || ( isset( $parts['scheme'] ) && 'https' === $parts['scheme'] ) ) {
$_SERVER['HTTPS'] = 'on';
}

$_SERVER['REQUEST_URI'] = $req;

$_POST = $data;
Expand Down Expand Up @@ -536,6 +548,48 @@ protected function replace_rest_api(): void {
add_action( 'parse_request', [ $this, 'serve_rest_api_request' ] );
}

/**
* Get the default URL scheme.
*
* If the request is being overridden to use HTTPS via {@see with_https()},
* this will return 'https'. Otherwise, it will return the scheme of the home
* URL of the WordPress installation.
*/
protected function get_default_url_scheme(): string {
if ( $this->forced_https ) {
return 'https';
}

if ( ! $this->is_experimental_use_home_url_host_enabled() ) {
return 'http';
}

return wp_parse_url( home_url(), PHP_URL_SCHEME );
}

/**
* Get the default URL host.
*
* If the `MANTLE_EXPERIMENTAL_TESTING_USE_HOME_URL_HOST` environment variable
* is set, this will return the host of the home URL. Otherwise, it will
* return the host defined in the WordPress tests configuration.
*
* With the next major release of Mantle, we will be shifting to using the
* home URL host by default.
*/
protected function get_default_url_host(): string {
return $this->is_experimental_use_home_url_host_enabled()
? wp_parse_url( home_url(), PHP_URL_HOST )
: WP_TESTS_DOMAIN;
}

/**
* Check if the experimental testing URL host feature is enabled.
*/
protected function is_experimental_use_home_url_host_enabled(): bool {
return Utils::env_bool( 'MANTLE_EXPERIMENTAL_TESTING_USE_HOME_URL_HOST', false );
}

/**
* Server the REST API request if applicable.
*
Expand Down
4 changes: 2 additions & 2 deletions src/mantle/testing/concerns/trait-makes-http-requests.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@ public function with_header( string $name, string $value ): Pending_Testable_Req
/**
* Create a pending request with the HTTPS enabled/disabled.
*
* @param bool $value Whether to use HTTPS.
* @param bool|null $value Whether to use HTTPS.
*/
public function with_https( bool $value = true ): Pending_Testable_Request {
public function with_https( ?bool $value = true ): Pending_Testable_Request {
return $this->create_pending_request()->with_https( $value );
}

Expand Down
71 changes: 69 additions & 2 deletions tests/Testing/Concerns/MakesHttpRequestsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ class MakesHttpRequestsTest extends Framework_Test_Case {
protected function setUp(): void {
parent::setUp();

putenv( 'MANTLE_EXPERIMENTAL_TESTING_USE_HOME_URL_HOST=' );

remove_all_actions( 'template_redirect' );

update_option( 'home', 'http://' . WP_TESTS_DOMAIN );
}

public function test_get_home() {
Expand Down Expand Up @@ -357,7 +360,9 @@ public function test_wp_is_rest_endpoint() {
]
);

$this->get( rest_url( '/mantle/v1/' . __FUNCTION__ ) );
$this
->get( rest_url( '/mantle/v1/' . __FUNCTION__ ) )
->assertJsonPath( 'key', 'value here' );

$this->assertFalse( wp_is_rest_endpoint() );
}
Expand Down Expand Up @@ -389,7 +394,13 @@ public function test_match_snapshot_rest() {
] );
}

public function test_https_request() {
public function test_url_scheme_http_by_default() {
$this->get( '/' )->assertOk();

$this->assertEmpty( $_SERVER['HTTPS'] ?? '' );
}

public function test_url_scheme_https_opt_in() {
$this->get( '/' )->assertOk();

$this->assertEmpty( $_SERVER['HTTPS'] ?? '' );
Expand All @@ -399,6 +410,51 @@ public function test_https_request() {
$this->assertEquals( 'on', $_SERVER['HTTPS'] );
}

public function test_url_scheme_https_by_home_url() {
putenv( 'MANTLE_EXPERIMENTAL_TESTING_USE_HOME_URL_HOST=1' );

$home_url = get_option( 'home' );

$this->assertEquals( 'http://' . WP_TESTS_DOMAIN, $home_url );
$this->assertEquals( 'http://' . WP_TESTS_DOMAIN, home_url() );

update_option( 'home', 'https://' . WP_TESTS_DOMAIN );

$this->assertEquals( 'https://' . WP_TESTS_DOMAIN, home_url() );

$this->get( '/' )->assertOk();

$this->assertEquals( 'on', $_SERVER['HTTPS'] ?? '' );
}

#[Group( 'experimental' )]
#[Group( 'experiment-testing-url-host' )]
public function test_experimental_default_url_host() {
$this->get( '/' )->assertOk();

$this->assertEquals( 'http://' . WP_TESTS_DOMAIN, home_url() );
$this->assertEquals( WP_TESTS_DOMAIN, $_SERVER['HTTP_HOST'] );

$this->setup_experiment_testing_url_host();

$this->get( '/' )->assertOk();

$this->assertEquals( 'subdomain.' . WP_TESTS_DOMAIN, $_SERVER['HTTP_HOST'] );
}

#[Group( 'experimental' )]
#[Group( 'experiment-testing-url-host' )]
public function test_experimental_redirect_to() {
$this->setup_experiment_testing_url_host();

$this->app['router']->get(
'/route-to-redirect/',
fn () => redirect()->to( '/redirected/' ),
);

$this->get( '/route-to-redirect/' )->assertRedirect( '/redirected/' );
}

public function test_multiple_requests() {
$methods = collect( get_class_methods( $this ) )
->filter( fn ( $method ) => false === strpos( $method, '_snapshot_' ) )
Expand All @@ -411,9 +467,20 @@ public function test_multiple_requests() {
continue;
}

$this->setUp();

$this->$method();

$this->tearDown();
}
}

protected function setup_experiment_testing_url_host() {
putenv( 'MANTLE_EXPERIMENTAL_TESTING_USE_HOME_URL_HOST=1' );

update_option( 'home', 'https://subdomain.' . WP_TESTS_DOMAIN );
$this->assertEquals( 'https://subdomain.' . WP_TESTS_DOMAIN, home_url() );
}
}

class JsonSerializableMixedResourcesStub implements JsonSerializable {
Expand Down
Loading