Skip to content

Commit

Permalink
Merge pull request #750 from lucatume/v4-fix-745
Browse files Browse the repository at this point in the history
feat(WPLoader) read content locations from config
  • Loading branch information
lucatume authored Aug 24, 2024
2 parents d69450b + 8113b58 commit c738cfa
Show file tree
Hide file tree
Showing 7 changed files with 715 additions and 181 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

- Ensure the `WPLoader` module will initialize correctly when used in `loadOnly` mode not using the `EventDispatcherBridge` extension. (thanks @lxbdr)
- Support loading the `wpdb` class from either the `class-wpdb.php` file or the `wp-db.php` one, supporting older versions of WordPress (thanks @BrianHenryIE)
- Read content, plugins and mu-plugins directories paths from the `WPLoader` configuration parameters correctly.

## [4.2.5] 2024-06-26;

Expand Down
27 changes: 21 additions & 6 deletions docs/modules/WPLoader.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## WPLoader module

// @todo update this

A module to load WordPress and make its code available in tests.

Depending on the value of the `loadOnly` configuration parameter, the module will behave differently:
Expand Down Expand Up @@ -45,7 +47,18 @@ When used in this mode, the module supports the following configuration paramete
* `configFile` - a configuration file, or a set of configuration files, to load before the tests to further customize
and control the WordPress testing environment.
* `pluginsFolder` - the path to the plugins folder to use when loading WordPress. Equivalent to defining the
`WP_PLUGIN_DIR` constant.
`WP_PLUGIN_DIR` constant. If both this parameter and the `WP_PLUGIN_DIR` parameter are set, the `WP_PLUGIN_DIR`
parameter will override the value of this one.
* `WP_CONTENT_DIR` - the path to the content folder to use when loading WordPress in the context of tests. If the
installation used by the `WPLoader` module defines a `WP_CONTENT_DIR` constant in its `wp-config.php` file, the module
will throw an exception if this parameter is set. Setting this parameter will affect the `WP_PLUGIN_DIR` and the `WPMU_PLUGIN_DIR`
parameters.
* `WP_PLUGIN_DIR` - the path to the plugins folder to use when loading WordPress in the context of tests. If the
installation used by the `WPLoader` module defines a `WP_PLUGIN_DIR` constant in its `wp-config.php` file, the module
will throw an exception if this parameter is set.
* `WPMU_PLUGIN_DIR` - the path to the mu-plugins folder to use when loading WordPress in the context of tests. If the
installation used by the `WPLoader` module defines a `WPMU_PLUGIN_DIR` constant in its `wp-config.php` file, the module
will throw an exception if this parameter is set.
* `plugins` - a list of plugins to activate and load in the WordPress installation. If the plugin is located in the
WordPress installation plugins directory, then the plugin name can be specified using the `directory/file.php` format.
If the plugin is located in an arbitrary path inside or outiside of the WordPress installation or project, then the
Expand All @@ -58,7 +71,9 @@ When used in this mode, the module supports the following configuration paramete
* `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 can be specified in slug format,
e.g., `twentytwentythree`, to load it from the WordPress installation themes directory. Alternatively, the theme can
be specified as an absolute or relative path to a theme folder, e.g., `/home/themes/my-theme` or `vendor/acme/vendor-theme`. To use both a parent and ha child theme from arbitrary absolute or relative paths, define the `theme` parameter as an array of theme paths, e.g., `['/home/themes/parent-theme', '.']`.
be specified as an absolute or relative path to a theme folder, e.g., `/home/themes/my-theme`
or `vendor/acme/vendor-theme`. To use both a parent and ha child theme from arbitrary absolute or relative paths,
define the `theme` parameter as an array of theme paths, e.g., `['/home/themes/parent-theme', '.']`.
* `AUTH_KEY` - the `AUTH_KEY` 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.
Expand Down Expand Up @@ -132,9 +147,9 @@ modules:
title: 'Integration Tests'
plugins:
# This plugin will be loaded from the WordPress installation plugins directory.
- hello.php
- hello.php
# This plugin will be loaded from an arbitrary absolute path.
- /home/plugins/woocommerce/woocommerce.php
- /home/plugins/woocommerce/woocommerce.php
# This plugin will be loaded from an arbitrary relative path inside the project root folder.
- vendor/acme/project/plugin.php
# This plugin will be loaded from the project root folder.
Expand Down Expand Up @@ -163,7 +178,7 @@ modules:
- my-plugin.php
- vendor/acme/project/plugin.php
# Parent theme from the WordPress installation themes directory, child theme from absolute path.
theme: [twentytwentythree, /home/themes/my-theme]
theme: [ twentytwentythree, /home/themes/my-theme ]
```
The following example configuration uses a SQLite database and loads a database fixture before the tests run:
Expand All @@ -189,7 +204,7 @@ modules:
- hello.php
- woocommerce/woocommerce.php
- my-plugin/my-plugin.php
theme:
theme:
# Parent theme from relative path.
- vendor/acme/parent-theme
# Child theme from the current working directory.
Expand Down
79 changes: 69 additions & 10 deletions src/Module/WPLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,32 @@ public function _didLoadWordPress(): bool
return $this->didLoadWordPress;
}

/**
* Get the absolute path to the mu-plugins directory.
*
* The value will first look at the `WPMU_PLUGIN_DIR` constant, then the `WP_CONTENT_DIR` configuration parameter,
* and will, finally, look in the default path from the WordPress root directory.
*
* @param string $path
*
* @return string
* @since TBD
*/
public function getMuPluginsFolder(string $path = ''): string
{
/** @var array{WPMU_PLUGIN_DIR?: string, WP_CONTENT_DIR?: string} $config */
$config = $this->config;
$candidates = array_filter([
$config['WPMU_PLUGIN_DIR'] ?? null,
isset($config['WP_CONTENT_DIR']) ? rtrim($config['WP_CONTENT_DIR'], '\\/') . '/mu-plugins' : null,
$this->installation->getMuPluginsDir()
]);
/** @var string $muPluginsDir */
$muPluginsDir = reset($candidates);

return rtrim($muPluginsDir, '\\/') . '/' . ($path ? ltrim($path, '\\/') : '');
}

protected function validateConfig(): void
{
// Coming from required fields, the values are now defined.
Expand Down Expand Up @@ -440,10 +466,10 @@ public function _initialize(): void
$this->installation = new Installation($wpRootDir);
}

if ($db instanceof SqliteDatabase && !is_file($this->installation->getContentDir('db.php'))) {
if ($db instanceof SqliteDatabase && !is_file($this->getContentFolder('db.php'))) {
Installation::placeSqliteMuPlugin(
$this->installation->getMuPluginsDir(),
$this->installation->getContentDir()
$this->getMuPluginsFolder(),
$this->getContentFolder()
);
}

Expand Down Expand Up @@ -476,7 +502,16 @@ public function _initialize(): void
if ($this->installation->isConfigured()) {
foreach (['WP_CONTENT_DIR', 'WP_PLUGIN_DIR', 'WPMU_PLUGIN_DIR'] as $pathConst) {
$constValue = $this->installation->getState()->getConstant($pathConst);

if ($constValue && is_string($constValue)) {
if (isset($config[$pathConst])) {
throw new ModuleConfigException(
$this,
"Both the installation wp-config.php file and the module configuration define a " .
"{$pathConst} constant: only one can be set."
);
}

$config[$pathConst] = $constValue;
}
}
Expand Down Expand Up @@ -610,8 +645,9 @@ public function _loadWordPress(?bool $loadOnly = null): void
/**
* 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.
* The value will first look at the `WP_PLUGIN_DIR` constant, then the `pluginsFolder` configuration parameter,
* then the `WP_CONTENT_DIR` configuration parameter, and will, finally, look in the default path from the
* WordPress root directory.
*
* @example
* ```php
Expand All @@ -626,7 +662,18 @@ public function _loadWordPress(?bool $loadOnly = null): void
*/
public function getPluginsFolder(string $path = ''): string
{
return $this->installation->getPluginsDir($path);
/** @var array{pluginsFolder?: string, WP_PLUGIN_DIR?: string,WP_CONTENT_DIR?: string} $config */
$config = $this->config;
$candidates = array_filter([
$config['WP_PLUGIN_DIR'] ?? null,
$config['pluginsFolder'] ?? null,
isset($config['WP_CONTENT_DIR']) ? rtrim($config['WP_CONTENT_DIR'], '\\/') . '/plugins' : null,
$this->installation->getPluginsDir()
]);
/** @var string $pluginDir */
$pluginDir = reset($candidates);

return rtrim($pluginDir, '\\/') . '/' . ($path ? ltrim($path, '\\/') : '');
}

/**
Expand Down Expand Up @@ -856,6 +903,9 @@ private function loadConfigFiles(): void
/**
* Returns the absolute path to the WordPress content directory.
*
* The value will first look at the `WP_CONTENT_DIR` configuration parameter, and will, finally, look in the
* default path from the WordPress root directory.
*
* @example
* ```php
* $content = $this->getContentFolder();
Expand All @@ -869,7 +919,16 @@ private function loadConfigFiles(): void
*/
public function getContentFolder(string $path = ''): string
{
return $this->installation->getContentDir($path);
/** @var array{WP_CONTENT_DIR?: string} $config */
$config = $this->config;
$candidates = array_filter([
$config['WP_CONTENT_DIR'] ?? null,
$this->installation->getContentDir()
]);
/** @var string $contentDir */
$contentDir = reset($candidates);

return rtrim($contentDir, '\\/') . '/' . ($path ? ltrim($path, '\\/') : '');
}

private function getCodeExecutionFactory(): CodeExecutionFactory
Expand Down Expand Up @@ -1083,7 +1142,7 @@ private function activatePluginsTheme(array $plugins): array
wp_cache_delete('alloptions', 'options');

// Do not include external plugins, it would create issues at this stage.
$pluginsDir = $this->installation->getPluginsDir();
$pluginsDir = $this->getPluginsFolder();

return array_values(
array_filter(
Expand Down Expand Up @@ -1125,7 +1184,7 @@ private function muActivatePluginsTheme(array $plugins): array
wp_cache_delete("1::active_sitewide_plugins", 'site-options');

// Do not include external plugins, it would create issues at this stage.
$pluginsDir = $this->installation->getPluginsDir();
$pluginsDir = $this->getPluginsFolder();
$validPlugins = array_values(
array_filter(
$plugins,
Expand Down Expand Up @@ -1168,7 +1227,7 @@ private function includeAllPlugins(array $plugins, bool $isMultisite): void
$activePlugins = [];
}

$pluginsDir = $this->installation->getPluginsDir();
$pluginsDir = $this->getPluginsFolder();

foreach ($plugins as $plugin) {
if (!is_file($pluginsDir . "/$plugin")) {
Expand Down
100 changes: 100 additions & 0 deletions tests/_support/Traits/InstallationMocks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

namespace lucatume\WPBrowser\Tests\Traits;

use lucatume\WPBrowser\Utils\Env;
use lucatume\WPBrowser\Utils\Filesystem as FS;

trait InstallationMocks
{
use TmpFilesCleanup;

/**
* @return array{0: string, 1: string}
*/
private function makeMockConfiguredInstallation(string $phpExtra = ''): array
{
$dbUser = Env::get('WORDPRESS_DB_USER');
$dbPassword = Env::get('WORDPRESS_DB_PASSWORD');
$dbLocalhostPort = Env::get('WORDPRESS_DB_LOCALHOST_PORT');
$dbName = Env::get('WORDPRESS_DB_NAME');
$wpRootFolder = FS::tmpDir('wploader_', [
'wp-includes' => [
'version.php' => <<< PHP
<?php
\$wp_version = '6.5';
\$wp_db_version = 57155;
\$tinymce_version = '49110-20201110';
\$required_php_version = '7.0.0';
\$required_mysql_version = '5.5.5';
PHP
],
'wp-config.php' => <<< PHP
<?php
define('DB_NAME', '$dbName');
define('DB_USER', '$dbUser');
define('DB_PASSWORD', '$dbPassword');
define('DB_HOST', '127.0.0.1:$dbLocalhostPort');
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
global \$table_prefix;
\$table_prefix = 'wp_';
define('AUTH_KEY', 'auth-key-salt');
define('SECURE_AUTH_KEY', 'secure-auth-key-salt');
define('LOGGED_IN_KEY', 'logged-in-key-salt');
define('NONCE_KEY', 'nonce-key-salt');
define('AUTH_SALT', 'auth-salt');
define('SECURE_AUTH_SALT', 'secure-auth-salt');
define('LOGGED_IN_SALT', 'logged-in-salt');
define('NONCE_SALT', 'nonce-salt');
$phpExtra
PHP,
'wp-settings.php' => '<?php',
'wp-load.php' => '<?php do_action("wp_loaded");',
]);
$dbUrl = sprintf(
'mysql://%s:%s@127.0.0.1:%d/%s',
$dbUser,
$dbPassword,
$dbLocalhostPort,
$dbName
);

return [$wpRootFolder, $dbUrl];
}

/**
* @return array{0: string, 1: string}
*/
private function makeMockScaffoldedInstallation(): array
{
$dbUser = Env::get('WORDPRESS_DB_USER');
$dbPassword = Env::get('WORDPRESS_DB_PASSWORD');
$dbLocalhostPort = Env::get('WORDPRESS_DB_LOCALHOST_PORT');
$dbName = Env::get('WORDPRESS_DB_NAME');
$wpRootFolder = FS::tmpDir('wploader_', [
'wp-includes' => [
'version.php' => <<< PHP
<?php
\$wp_version = '6.5';
\$wp_db_version = 57155;
\$tinymce_version = '49110-20201110';
\$required_php_version = '7.0.0';
\$required_mysql_version = '5.5.5';
PHP
],
'wp-settings.php' => '<?php',
'wp-load.php' => '<?php do_action("wp_loaded");',
]);
$dbUrl = sprintf(
'mysql://%s:%s@127.0.0.1:%d/%s',
$dbUser,
$dbPassword,
$dbLocalhostPort,
$dbName
);

return [$wpRootFolder, $dbUrl];
}

}
Loading

0 comments on commit c738cfa

Please sign in to comment.