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

fix #716 #718

Merged
merged 12 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [unreleased] Unreleased

### Changed

- Fix non standard installation handling, hello Bedrock! (#716)

## [4.1.6] 2024-04-07;

### Changed
Expand Down
1 change: 1 addition & 0 deletions bin/update_sqlite_plugin
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ unzip -o "$root_dir/includes/sqlite-database-integration.zip" -d "$root_dir/incl
# Remove the zip file
rm "$root_dir/includes/sqlite-database-integration.zip"
git apply "${root_dir}"/config/patches/sqlite-database-integration/db.copy.patch
git apply "${root_dir}"/config/patches/sqlite-database-integration/load.php.patch

15 changes: 15 additions & 0 deletions config/patches/sqlite-database-integration/load.php.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
diff --git a/includes/sqlite-database-integration/load.php b/includes/sqlite-database-integration/load.php
index 3af80903..ddc5a9b6 100644
--- a/includes/sqlite-database-integration/load.php
+++ b/includes/sqlite-database-integration/load.php
@@ -12,7 +12,9 @@
* @package wp-sqlite-integration
*/

-define( 'SQLITE_MAIN_FILE', __FILE__ );
+if (!defined('SQLITE_MAIN_FILE')) {
+ define('SQLITE_MAIN_FILE', __FILE__);
+}

require_once __DIR__ . '/admin-page.php';
require_once __DIR__ . '/activate.php';
92 changes: 45 additions & 47 deletions docs/modules/WPLoader.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ When used in this mode, the module supports the following configuration paramete
`WP_PLUGIN_DIR` constant.
* `plugins` - a list of plugins to activate and load in the WordPress installation. Each plugin must be specified in a
format like `hello.php` or `my-plugin/my-plugin.php` format.
* `silentlyActivatePlugins` - a list of plugins to activate **silently**, without firing their activation hooks. Depending on the plugin, a silent activation might cause the plugin to not work correctly. The list must be in the same format as the `plugins` parameter and plugin should be activated silently only if they are not working correctly during normal activation and are known to work correctly when activated silently.
* `silentlyActivatePlugins` - a list of plugins to activate **silently**, without firing their activation hooks.
Depending on the plugin, a silent activation might cause the plugin to not work correctly. The list must be in the
same format as the `plugins` parameter and plugin should be activated silently only if they are not working correctly
during normal activation and are known to work correctly when activated silently.
* `bootstrapActions` - a list of actions or callables to call **after** WordPress is loaded and before the tests run.
* `theme` - the theme to activate and load in the WordPress installation. The theme must be specified in slug format
like
Expand Down Expand Up @@ -83,11 +86,29 @@ When used in this mode, the module supports the following configuration paramete
* `WP_HTTP_BLOCK_EXTERNAL` - the `WP_HTTP_BLOCK_EXTERNAL` constant value to use when loading WordPress. If
the `wpRootFolder` path points at a configured installation, containing the `wp-config.php` file, then the value of
the constant in the configuration file will be used, else it will be randomly generated.
* `backupGlobals` - a boolean value to indicate if the global environment should be backed up before each test. Defaults to `true`. The globals' backup involves serialization of the global state, plugins or themes that define classes developed to prevent serialization of the global state will cause the tests to fail. Set this parameter to `false` to disable the global environment backup, or use a more refined approach setting the `backupGlobalsExcludeList` parameter below. Note that a test case that is explicitly setting the `backupGlobals` property will override this configuration parameter.
* `backupGlobalsExcludeList` - a list of global variables to exclude from the global environment backup. The list must be in the form of array, and it will be merged to the list of globals excluded by default.
* `backupStaticAttributes` - a boolean value to indicate if static attributes of classes should be backed up before each test. Defaults to `true`. The static attributes' backup involves serialization of the global state, plugins or themes that define classes developed to prevent serialization of the global state will cause the tests to fail. Set this parameter to `false` to disable the static attributes backup, or use a more refined approanch setting the `backupStaticAttributesExcludeList` parameter below. Note that a test case that is explicitly setting the `backupStaticAttributes` property will override this configuration parameter.
* `backupStaticAttributesExcludeList` - a list of classes to exclude from the static attributes backup. The list must be in the form of map from class names to the array of method names to exclude from the backup. See an example below.
* `skipInstall` - a boolean value to indicate if the WordPress installation should be skipped between runs, when already installed. Defaults to `false`. During boot, the `WPLoader` module will re-install WordPress and activate, on top of the fresh installation, any plugin and theme specified in the `plugins` and `theme` configuration parameters: this can be a time-consuming operation. Set this parameter to `true` to run the WordPress installation once and just load it on the following runs. To force the installation to run again, rerun the suite using the WPLoader module using the `--debug` flag or delete the `_wploader-state.sql` file in the suite directory. This configuration parameter is ignored when the `loadOnly` parameter is set to `true`.
* `backupGlobals` - a boolean value to indicate if the global environment should be backed up before each test. Defaults
to `true`. The globals' backup involves serialization of the global state, plugins or themes that define classes
developed to prevent serialization of the global state will cause the tests to fail. Set this parameter to `false` to
disable the global environment backup, or use a more refined approach setting the `backupGlobalsExcludeList` parameter
below. Note that a test case that is explicitly setting the `backupGlobals` property will override this configuration
parameter.
* `backupGlobalsExcludeList` - a list of global variables to exclude from the global environment backup. The list must
be in the form of array, and it will be merged to the list of globals excluded by default.
* `backupStaticAttributes` - a boolean value to indicate if static attributes of classes should be backed up before each
test. Defaults to `true`. The static attributes' backup involves serialization of the global state, plugins or themes
that define classes developed to prevent serialization of the global state will cause the tests to fail. Set this
parameter to `false` to disable the static attributes backup, or use a more refined approanch setting
the `backupStaticAttributesExcludeList` parameter below. Note that a test case that is explicitly setting
the `backupStaticAttributes` property will override this configuration parameter.
* `backupStaticAttributesExcludeList` - a list of classes to exclude from the static attributes backup. The list must be
in the form of map from class names to the array of method names to exclude from the backup. See an example below.
* `skipInstall` - a boolean value to indicate if the WordPress installation should be skipped between runs, when already
installed. Defaults to `false`. During boot, the `WPLoader` module will re-install WordPress and activate, on top of
the fresh installation, any plugin and theme specified in the `plugins` and `theme` configuration parameters: this can
be a time-consuming operation. Set this parameter to `true` to run the WordPress installation once and just load it on
the following runs. To force the installation to run again, rerun the suite using the WPLoader module using
the `--debug` flag or delete the `_wploader-state.sql` file in the suite directory. This configuration parameter is
ignored when the `loadOnly` parameter is set to `true`.

This is an example of an integration suite configured to use the module:

Expand Down Expand Up @@ -159,7 +180,8 @@ modules:
theme: twentytwentythree
```

The follow example configuration prevents the backup of globals and static attributes in all the tests of the suite that are not explicitly overriding the `backupGlobals` and `backupStaticAttributes` properties:
The follow example configuration prevents the backup of globals and static attributes in all the tests of the suite that
are not explicitly overriding the `backupGlobals` and `backupStaticAttributes` properties:

```yaml
actor: IntegrationTester
Expand Down Expand Up @@ -219,45 +241,16 @@ modules:
- instance
- anotherStaticAttributeThatWillExplodeOnWakeup
- AnotherPlugin\AnotherClass:
- instance
- yetAnotherStaticAttributeThatWillExplodeOnWakeup
- instance
- yetAnotherStaticAttributeThatWillExplodeOnWakeup
```

### Handling a custom site structure

If you're working on a site project using a custom file structure, e.g. [Bedrock][4], you should
use [a custom configuration](./../custom-configuration.md) and, together with that, configure the `WPLoader` module to
load WordPress, plugins and themes code from the correct locations.
Take care to point the `wpRootFolder` parameter to the directory containing the `wp-load.php` file,
the `/var/my-site/web/wp` one in the following example, and the module will read the project configuration file to load
the WordPress code from the correct location.

Here's an example of how the module should be configured to run integration tests on a Bedrock installation:

```yaml
actor: IntegrationTester
bootstrap: _bootstrap.php
modules:
enabled:
- \Helper\Integration
- lucatume\WPBrowser\Module\WPLoader:
wpRootFolder: /var/my-site/web/wp
dbUrl: mysql://root:root@mysql:3306/wordpress
tablePrefix: test_
domain: my-project.test
adminEmail: admin@my-project.test
title: 'Integration Tests'
plugins:
- hello.php
- woocommerce/woocommerce.php
- my-plugin/my-plugin.php
theme: twentytwentythree
```
The setup process should _just work_ for standard and non-standard WordPress installations alike.

In general, pointing the `wpRootFolder` parameter to the directory containing the `wp-load.php` file should take care of
loading WordPress code from the correct location.
Should that not be the case, use the `configFile` parameter to point the module to the project test configuration file:
a PHP file defining the constants and environment variables to use to load WordPress, plugins and themes correctly.
Even if you're working on a site project using a custom file structure, e.g. [Bedrock][4], you will be able to set up
your site to run tests using the default configuration based on PHP built-in server, Chromedriver and SQLite database.

## Configuration with loadOnly: true

Expand Down Expand Up @@ -330,36 +323,41 @@ The module provides the following methods:
<!-- methods -->

#### factory
Signature: `factory()` : `lucatume\WPBrowser\Module\WPLoader\FactoryStore`

Signature: `factory()` : `lucatume\WPBrowser\Module\WPLoader\FactoryStore`

Accessor method to get the object storing the factories for things.
This method gives access to the same factories provided by the
[Core test suite](https://make.wordpress.org/core/handbook/testing/automated-testing/writing-phpunit-tests/).

#### getContentFolder
Signature: `getContentFolder([string $path])` : `string`

Signature: `getContentFolder([string $path])` : `string`

Returns the absolute path to the WordPress content directory.

#### getInstallation
Signature: `getInstallation()` : `lucatume\WPBrowser\WordPress\Installation`

Signature: `getInstallation()` : `lucatume\WPBrowser\WordPress\Installation`

#### getPluginsFolder
Signature: `getPluginsFolder([string $path])` : `string`

Signature: `getPluginsFolder([string $path])` : `string`

Returns the absolute path to the plugins directory.

The value will first look at the `WP_PLUGIN_DIR` constant, then the `pluginsFolder` configuration parameter
and will, finally, look in the default path from the WordPress root directory.

#### getThemesFolder
Signature: `getThemesFolder([string $path])` : `string`

Signature: `getThemesFolder([string $path])` : `string`

Returns the absolute path to the themes directory.

#### getWpRootFolder
Signature: `getWpRootFolder([?string $path])` : `string`

Signature: `getWpRootFolder([?string $path])` : `string`

Returns the absolute path to the WordPress root folder or a path within it..
<!-- /methods -->
Expand Down
65 changes: 41 additions & 24 deletions includes/cli-server/router.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,57 @@
*/

$root = $_SERVER['DOCUMENT_ROOT'];
$path = '/'. ltrim( parse_url( urldecode( $_SERVER['REQUEST_URI'] ),PHP_URL_PATH ), '/' );
$path = '/' . ltrim(parse_url(urldecode($_SERVER['REQUEST_URI']), PHP_URL_PATH), '/');

define('DB_ENGINE', getenv('DB_ENGINE') ?: 'mysql');

// Add a unique request ID to the headers.
$requestId = md5( microtime() );
$requestId = md5(microtime());
$_SERVER['REQUEST_ID'] = $requestId;
header( 'X-Request-ID: ' . $requestId );
header('X-Request-ID: ' . $requestId);

// Disable the MU upgrade routine.
global $wp_filter;
$wp_filter['do_mu_upgrade'][10][] = [
'accepted_args' => 0,
'function' => '__return_false'
'function' => '__return_false'
];

if ( file_exists( $root.$path ) ) {

// Enforces trailing slash, keeping links tidy in the admin
if ( is_dir( $root.$path ) && ! str_ends_with( $path, '/' ) ) {
header( "Location: $path/" );
exit;
}

// Runs PHP file if it exists
if ( str_contains( $path, '.php' ) ) {
chdir( dirname( $root.$path ) );
require_once $root.$path;
} else {
return false;
}
$wpbrowserSiteurl = getenv('WPBROWSER_SITEURL');
$wpbrowserHomeUrl = getenv('WPBROWSER_HOMEURL');
if ($wpbrowserSiteurl) {
$wp_filter['option_siteurl'][1000][] = [
'accepted_args' => 0,
'function' => function () use ($wpbrowserSiteurl) {
return $wpbrowserSiteurl;
}
];
}
if (getenv('WPBROWSER_HOMEURL')) {
$wp_filter['option_home'][1000][] = [
'accepted_args' => 0,
'function' => function () use ($wpbrowserHomeUrl) {
return $wpbrowserHomeUrl;
}
];
}
unset($wpbrowserSiteurl, $wpbrowserHomeUrl);

if (file_exists($root . $path)) {
// Enforces trailing slash, keeping links tidy in the admin
if (is_dir($root . $path) && !str_ends_with($path, '/')) {
header("Location: $path/");
exit;
}

// Runs PHP file if it exists
if (str_contains($path, '.php')) {
chdir(dirname($root . $path));
require_once $root . $path;
} else {
return false;
}
} else {

// Otherwise, run `index.php`
chdir( $root );
require_once 'index.php';
// Otherwise, run `index.php`
chdir($root);
require_once 'index.php';
}
4 changes: 3 additions & 1 deletion includes/sqlite-database-integration/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
* @package wp-sqlite-integration
*/

define( 'SQLITE_MAIN_FILE', __FILE__ );
if (!defined('SQLITE_MAIN_FILE')) {
define('SQLITE_MAIN_FILE', __FILE__);
}

require_once __DIR__ . '/admin-page.php';
require_once __DIR__ . '/activate.php';
Expand Down
33 changes: 27 additions & 6 deletions src/Project/SiteProject.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use lucatume\WPBrowser\Exceptions\RuntimeException;
use lucatume\WPBrowser\Extension\BuiltInServerController;
use lucatume\WPBrowser\Extension\ChromeDriverController;
use lucatume\WPBrowser\Template\Wpbrowser;
use lucatume\WPBrowser\Utils\ChromedriverInstaller;
use lucatume\WPBrowser\Utils\Codeception;
use lucatume\WPBrowser\Utils\Filesystem as FS;
Expand Down Expand Up @@ -47,7 +48,7 @@ public function __construct(InputInterface $input, OutputInterface $output, prot
);
}

$suggest = "Make sure you're initializing wp-browser at the root of your site project,".
$suggest = "Make sure you're initializing wp-browser at the root of your site project," .
" and that the directory contains the WordPress files and a wp-config.php file.";

if ($installationState instanceof EmptyDir) {
Expand Down Expand Up @@ -95,7 +96,8 @@ public function setup(): void
}

$contentDir = $this->installation->getContentDir();
Installation::placeSqliteMuPlugin($this->installation->getMuPluginsDir(), $contentDir);
$dropInPath = Installation::placeSqliteMuPlugin($this->installation->getMuPluginsDir(), $contentDir);
Wpbrowser::setDropInPath($dropInPath);

$this->sayInfo('SQLite drop-in placed, installing WordPress ...');
$serverLocalhostPort = Random::openLocalhostPort();
Expand Down Expand Up @@ -141,19 +143,37 @@ public function setup(): void

EOT;

$docRootEnvVar = "%WORDPRESS_ROOT_DIR%";
$docRoot = dirname($this->installation->getWpConfigFilePath());
$siteUrlRelativePath = '';
$wpRootDir = $this->installation->getWpRootDir();
if ($docRoot !== rtrim($wpRootDir, '/')) {
$docRootRelativePath = FS::relativePath($this->workDir, $docRoot);
$docRootEnvVar = '%WORDPRESS_DOCROOT%';
$siteUrlRelativePath = rtrim('/' . ltrim(FS::relativePath($docRoot, $wpRootDir), '/'), '/');
$this->testEnvironment->extraEnvFileContents .= <<<EOT

# The path to the directory that should be served on localhost, the one containing the wp-config.php file.
WORDPRESS_DOCROOT=$docRootRelativePath

EOT;
}

$this->testEnvironment->extensionsEnabled = [
ChromeDriverController::class => [
'port' => "%CHROMEDRIVER_PORT%",
],
BuiltInServerController::class => [
'workers' => 5,
'port' => "%BUILTIN_SERVER_PORT%",
'docroot' => "%WORDPRESS_ROOT_DIR%",
'docroot' => $docRootEnvVar,
'env' => [
'DATABASE_TYPE' => 'sqlite',
'DB_ENGINE' => 'sqlite',
'DB_DIR' => '%codecept_root_dir%' . DIRECTORY_SEPARATOR . $dataDirRelativePath,
'DB_FILE' => 'db.sqlite'
'DB_FILE' => 'db.sqlite',
'WPBROWSER_SITEURL' => '%WORDPRESS_URL%' . $siteUrlRelativePath,
'WPBROWSER_HOMEURL' => '%WORDPRESS_URL%'
]
]

Expand All @@ -163,7 +183,8 @@ public function setup(): void
$this->testEnvironment->customCommands[] = DevInfo::class;
$this->testEnvironment->customCommands[] = DevRestart::class;
$this->testEnvironment->customCommands[] = ChromedriverUpdate::class;
$this->testEnvironment->wpRootDir = '.';
$this->testEnvironment->wpRootDir = FS::relativePath($this->workDir, $wpRootDir) ?: '.';
$this->testEnvironment->wpAdminPath = '/' . ltrim(FS::relativePath($docRoot, $wpRootDir . '/wp-admin'), '/');
$this->testEnvironment->dbUrl = 'sqlite://' . implode(
DIRECTORY_SEPARATOR,
['%codecept_root_dir%', $dataDirRelativePath, 'db.sqlite']
Expand All @@ -182,7 +203,7 @@ public function getTestEnv(): ?TestEnvironment

private function getName(): string
{
return basename(dirname($this->workDir));
return basename($this->workDir);
}

private function scaffoldEndToEndActivationCest(): void
Expand Down
1 change: 1 addition & 0 deletions src/Project/TestEnvironment.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class TestEnvironment
public string $wpTablePrefix = 'wp_';
public string $wpUrl = 'http://wordpress.test';
public string $wpDomain = 'wordpress.test';
public string $wpAdminPath = '/wp-admin';
public string $wpAdminUser = 'admin';
public string $wpAdminPassword = 'password';
public string $chromeDriverHost = 'localhost';
Expand Down
Loading
Loading