Skip to content

Commit d78438a

Browse files
authored
Merge pull request #718 from lucatume/v4-fix-716
fix #716
2 parents b56afb5 + 0d4c8f5 commit d78438a

File tree

28 files changed

+591
-149
lines changed

28 files changed

+591
-149
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
44

55
## [unreleased] Unreleased
66

7+
### Changed
8+
9+
- Fix non standard installation handling, hello Bedrock! (#716)
10+
711
## [4.1.6] 2024-04-07;
812

913
### Changed

bin/update_sqlite_plugin

+1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ unzip -o "$root_dir/includes/sqlite-database-integration.zip" -d "$root_dir/incl
1111
# Remove the zip file
1212
rm "$root_dir/includes/sqlite-database-integration.zip"
1313
git apply "${root_dir}"/config/patches/sqlite-database-integration/db.copy.patch
14+
git apply "${root_dir}"/config/patches/sqlite-database-integration/load.php.patch
1415

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
diff --git a/includes/sqlite-database-integration/load.php b/includes/sqlite-database-integration/load.php
2+
index 3af80903..ddc5a9b6 100644
3+
--- a/includes/sqlite-database-integration/load.php
4+
+++ b/includes/sqlite-database-integration/load.php
5+
@@ -12,7 +12,9 @@
6+
* @package wp-sqlite-integration
7+
*/
8+
9+
-define( 'SQLITE_MAIN_FILE', __FILE__ );
10+
+if (!defined('SQLITE_MAIN_FILE')) {
11+
+ define('SQLITE_MAIN_FILE', __FILE__);
12+
+}
13+
14+
require_once __DIR__ . '/admin-page.php';
15+
require_once __DIR__ . '/activate.php';

docs/modules/WPLoader.md

+45-47
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ When used in this mode, the module supports the following configuration paramete
4848
`WP_PLUGIN_DIR` constant.
4949
* `plugins` - a list of plugins to activate and load in the WordPress installation. Each plugin must be specified in a
5050
format like `hello.php` or `my-plugin/my-plugin.php` format.
51-
* `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.
51+
* `silentlyActivatePlugins` - a list of plugins to activate **silently**, without firing their activation hooks.
52+
Depending on the plugin, a silent activation might cause the plugin to not work correctly. The list must be in the
53+
same format as the `plugins` parameter and plugin should be activated silently only if they are not working correctly
54+
during normal activation and are known to work correctly when activated silently.
5255
* `bootstrapActions` - a list of actions or callables to call **after** WordPress is loaded and before the tests run.
5356
* `theme` - the theme to activate and load in the WordPress installation. The theme must be specified in slug format
5457
like
@@ -83,11 +86,29 @@ When used in this mode, the module supports the following configuration paramete
8386
* `WP_HTTP_BLOCK_EXTERNAL` - the `WP_HTTP_BLOCK_EXTERNAL` constant value to use when loading WordPress. If
8487
the `wpRootFolder` path points at a configured installation, containing the `wp-config.php` file, then the value of
8588
the constant in the configuration file will be used, else it will be randomly generated.
86-
* `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.
87-
* `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.
88-
* `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.
89-
* `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.
90-
* `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`.
89+
* `backupGlobals` - a boolean value to indicate if the global environment should be backed up before each test. Defaults
90+
to `true`. The globals' backup involves serialization of the global state, plugins or themes that define classes
91+
developed to prevent serialization of the global state will cause the tests to fail. Set this parameter to `false` to
92+
disable the global environment backup, or use a more refined approach setting the `backupGlobalsExcludeList` parameter
93+
below. Note that a test case that is explicitly setting the `backupGlobals` property will override this configuration
94+
parameter.
95+
* `backupGlobalsExcludeList` - a list of global variables to exclude from the global environment backup. The list must
96+
be in the form of array, and it will be merged to the list of globals excluded by default.
97+
* `backupStaticAttributes` - a boolean value to indicate if static attributes of classes should be backed up before each
98+
test. Defaults to `true`. The static attributes' backup involves serialization of the global state, plugins or themes
99+
that define classes developed to prevent serialization of the global state will cause the tests to fail. Set this
100+
parameter to `false` to disable the static attributes backup, or use a more refined approanch setting
101+
the `backupStaticAttributesExcludeList` parameter below. Note that a test case that is explicitly setting
102+
the `backupStaticAttributes` property will override this configuration parameter.
103+
* `backupStaticAttributesExcludeList` - a list of classes to exclude from the static attributes backup. The list must be
104+
in the form of map from class names to the array of method names to exclude from the backup. See an example below.
105+
* `skipInstall` - a boolean value to indicate if the WordPress installation should be skipped between runs, when already
106+
installed. Defaults to `false`. During boot, the `WPLoader` module will re-install WordPress and activate, on top of
107+
the fresh installation, any plugin and theme specified in the `plugins` and `theme` configuration parameters: this can
108+
be a time-consuming operation. Set this parameter to `true` to run the WordPress installation once and just load it on
109+
the following runs. To force the installation to run again, rerun the suite using the WPLoader module using
110+
the `--debug` flag or delete the `_wploader-state.sql` file in the suite directory. This configuration parameter is
111+
ignored when the `loadOnly` parameter is set to `true`.
91112

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

@@ -159,7 +180,8 @@ modules:
159180
theme: twentytwentythree
160181
```
161182
162-
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:
183+
The follow example configuration prevents the backup of globals and static attributes in all the tests of the suite that
184+
are not explicitly overriding the `backupGlobals` and `backupStaticAttributes` properties:
163185

164186
```yaml
165187
actor: IntegrationTester
@@ -219,45 +241,16 @@ modules:
219241
- instance
220242
- anotherStaticAttributeThatWillExplodeOnWakeup
221243
- AnotherPlugin\AnotherClass:
222-
- instance
223-
- yetAnotherStaticAttributeThatWillExplodeOnWakeup
244+
- instance
245+
- yetAnotherStaticAttributeThatWillExplodeOnWakeup
224246
```
225247

226248
### Handling a custom site structure
227249

228-
If you're working on a site project using a custom file structure, e.g. [Bedrock][4], you should
229-
use [a custom configuration](./../custom-configuration.md) and, together with that, configure the `WPLoader` module to
230-
load WordPress, plugins and themes code from the correct locations.
231-
Take care to point the `wpRootFolder` parameter to the directory containing the `wp-load.php` file,
232-
the `/var/my-site/web/wp` one in the following example, and the module will read the project configuration file to load
233-
the WordPress code from the correct location.
234-
235-
Here's an example of how the module should be configured to run integration tests on a Bedrock installation:
236-
237-
```yaml
238-
actor: IntegrationTester
239-
bootstrap: _bootstrap.php
240-
modules:
241-
enabled:
242-
- \Helper\Integration
243-
- lucatume\WPBrowser\Module\WPLoader:
244-
wpRootFolder: /var/my-site/web/wp
245-
dbUrl: mysql://root:root@mysql:3306/wordpress
246-
tablePrefix: test_
247-
domain: my-project.test
248-
adminEmail: admin@my-project.test
249-
title: 'Integration Tests'
250-
plugins:
251-
- hello.php
252-
- woocommerce/woocommerce.php
253-
- my-plugin/my-plugin.php
254-
theme: twentytwentythree
255-
```
250+
The setup process should _just work_ for standard and non-standard WordPress installations alike.
256251

257-
In general, pointing the `wpRootFolder` parameter to the directory containing the `wp-load.php` file should take care of
258-
loading WordPress code from the correct location.
259-
Should that not be the case, use the `configFile` parameter to point the module to the project test configuration file:
260-
a PHP file defining the constants and environment variables to use to load WordPress, plugins and themes correctly.
252+
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
253+
your site to run tests using the default configuration based on PHP built-in server, Chromedriver and SQLite database.
261254

262255
## Configuration with loadOnly: true
263256

@@ -330,36 +323,41 @@ The module provides the following methods:
330323
<!-- methods -->
331324

332325
#### factory
333-
Signature: `factory()` : `lucatume\WPBrowser\Module\WPLoader\FactoryStore`
326+
327+
Signature: `factory()` : `lucatume\WPBrowser\Module\WPLoader\FactoryStore`
334328

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

339333
#### getContentFolder
340-
Signature: `getContentFolder([string $path])` : `string`
334+
335+
Signature: `getContentFolder([string $path])` : `string`
341336

342337
Returns the absolute path to the WordPress content directory.
343338

344339
#### getInstallation
345-
Signature: `getInstallation()` : `lucatume\WPBrowser\WordPress\Installation`
346340

341+
Signature: `getInstallation()` : `lucatume\WPBrowser\WordPress\Installation`
347342

348343
#### getPluginsFolder
349-
Signature: `getPluginsFolder([string $path])` : `string`
344+
345+
Signature: `getPluginsFolder([string $path])` : `string`
350346

351347
Returns the absolute path to the plugins directory.
352348

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

356352
#### getThemesFolder
357-
Signature: `getThemesFolder([string $path])` : `string`
353+
354+
Signature: `getThemesFolder([string $path])` : `string`
358355

359356
Returns the absolute path to the themes directory.
360357

361358
#### getWpRootFolder
362-
Signature: `getWpRootFolder([?string $path])` : `string`
359+
360+
Signature: `getWpRootFolder([?string $path])` : `string`
363361

364362
Returns the absolute path to the WordPress root folder or a path within it..
365363
<!-- /methods -->

includes/cli-server/router.php

+41-24
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,57 @@
55
*/
66

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

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

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

1717
// Disable the MU upgrade routine.
1818
global $wp_filter;
1919
$wp_filter['do_mu_upgrade'][10][] = [
2020
'accepted_args' => 0,
21-
'function' => '__return_false'
21+
'function' => '__return_false'
2222
];
23-
24-
if ( file_exists( $root.$path ) ) {
25-
26-
// Enforces trailing slash, keeping links tidy in the admin
27-
if ( is_dir( $root.$path ) && ! str_ends_with( $path, '/' ) ) {
28-
header( "Location: $path/" );
29-
exit;
30-
}
31-
32-
// Runs PHP file if it exists
33-
if ( str_contains( $path, '.php' ) ) {
34-
chdir( dirname( $root.$path ) );
35-
require_once $root.$path;
36-
} else {
37-
return false;
38-
}
23+
$wpbrowserSiteurl = getenv('WPBROWSER_SITEURL');
24+
$wpbrowserHomeUrl = getenv('WPBROWSER_HOMEURL');
25+
if ($wpbrowserSiteurl) {
26+
$wp_filter['option_siteurl'][1000][] = [
27+
'accepted_args' => 0,
28+
'function' => function () use ($wpbrowserSiteurl) {
29+
return $wpbrowserSiteurl;
30+
}
31+
];
32+
}
33+
if (getenv('WPBROWSER_HOMEURL')) {
34+
$wp_filter['option_home'][1000][] = [
35+
'accepted_args' => 0,
36+
'function' => function () use ($wpbrowserHomeUrl) {
37+
return $wpbrowserHomeUrl;
38+
}
39+
];
40+
}
41+
unset($wpbrowserSiteurl, $wpbrowserHomeUrl);
42+
43+
if (file_exists($root . $path)) {
44+
// Enforces trailing slash, keeping links tidy in the admin
45+
if (is_dir($root . $path) && !str_ends_with($path, '/')) {
46+
header("Location: $path/");
47+
exit;
48+
}
49+
50+
// Runs PHP file if it exists
51+
if (str_contains($path, '.php')) {
52+
chdir(dirname($root . $path));
53+
require_once $root . $path;
54+
} else {
55+
return false;
56+
}
3957
} else {
40-
41-
// Otherwise, run `index.php`
42-
chdir( $root );
43-
require_once 'index.php';
58+
// Otherwise, run `index.php`
59+
chdir($root);
60+
require_once 'index.php';
4461
}

includes/sqlite-database-integration/load.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
* @package wp-sqlite-integration
1313
*/
1414

15-
define( 'SQLITE_MAIN_FILE', __FILE__ );
15+
if (!defined('SQLITE_MAIN_FILE')) {
16+
define('SQLITE_MAIN_FILE', __FILE__);
17+
}
1618

1719
require_once __DIR__ . '/admin-page.php';
1820
require_once __DIR__ . '/activate.php';

src/Project/SiteProject.php

+27-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use lucatume\WPBrowser\Exceptions\RuntimeException;
1313
use lucatume\WPBrowser\Extension\BuiltInServerController;
1414
use lucatume\WPBrowser\Extension\ChromeDriverController;
15+
use lucatume\WPBrowser\Template\Wpbrowser;
1516
use lucatume\WPBrowser\Utils\ChromedriverInstaller;
1617
use lucatume\WPBrowser\Utils\Codeception;
1718
use lucatume\WPBrowser\Utils\Filesystem as FS;
@@ -47,7 +48,7 @@ public function __construct(InputInterface $input, OutputInterface $output, prot
4748
);
4849
}
4950

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

5354
if ($installationState instanceof EmptyDir) {
@@ -95,7 +96,8 @@ public function setup(): void
9596
}
9697

9798
$contentDir = $this->installation->getContentDir();
98-
Installation::placeSqliteMuPlugin($this->installation->getMuPluginsDir(), $contentDir);
99+
$dropInPath = Installation::placeSqliteMuPlugin($this->installation->getMuPluginsDir(), $contentDir);
100+
Wpbrowser::setDropInPath($dropInPath);
99101

100102
$this->sayInfo('SQLite drop-in placed, installing WordPress ...');
101103
$serverLocalhostPort = Random::openLocalhostPort();
@@ -141,19 +143,37 @@ public function setup(): void
141143
142144
EOT;
143145

146+
$docRootEnvVar = "%WORDPRESS_ROOT_DIR%";
147+
$docRoot = dirname($this->installation->getWpConfigFilePath());
148+
$siteUrlRelativePath = '';
149+
$wpRootDir = $this->installation->getWpRootDir();
150+
if ($docRoot !== rtrim($wpRootDir, '/')) {
151+
$docRootRelativePath = FS::relativePath($this->workDir, $docRoot);
152+
$docRootEnvVar = '%WORDPRESS_DOCROOT%';
153+
$siteUrlRelativePath = rtrim('/' . ltrim(FS::relativePath($docRoot, $wpRootDir), '/'), '/');
154+
$this->testEnvironment->extraEnvFileContents .= <<<EOT
155+
156+
# The path to the directory that should be served on localhost, the one containing the wp-config.php file.
157+
WORDPRESS_DOCROOT=$docRootRelativePath
158+
159+
EOT;
160+
}
161+
144162
$this->testEnvironment->extensionsEnabled = [
145163
ChromeDriverController::class => [
146164
'port' => "%CHROMEDRIVER_PORT%",
147165
],
148166
BuiltInServerController::class => [
149167
'workers' => 5,
150168
'port' => "%BUILTIN_SERVER_PORT%",
151-
'docroot' => "%WORDPRESS_ROOT_DIR%",
169+
'docroot' => $docRootEnvVar,
152170
'env' => [
153171
'DATABASE_TYPE' => 'sqlite',
154172
'DB_ENGINE' => 'sqlite',
155173
'DB_DIR' => '%codecept_root_dir%' . DIRECTORY_SEPARATOR . $dataDirRelativePath,
156-
'DB_FILE' => 'db.sqlite'
174+
'DB_FILE' => 'db.sqlite',
175+
'WPBROWSER_SITEURL' => '%WORDPRESS_URL%' . $siteUrlRelativePath,
176+
'WPBROWSER_HOMEURL' => '%WORDPRESS_URL%'
157177
]
158178
]
159179

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

183204
private function getName(): string
184205
{
185-
return basename(dirname($this->workDir));
206+
return basename($this->workDir);
186207
}
187208

188209
private function scaffoldEndToEndActivationCest(): void

src/Project/TestEnvironment.php

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class TestEnvironment
1212
public string $wpTablePrefix = 'wp_';
1313
public string $wpUrl = 'http://wordpress.test';
1414
public string $wpDomain = 'wordpress.test';
15+
public string $wpAdminPath = '/wp-admin';
1516
public string $wpAdminUser = 'admin';
1617
public string $wpAdminPassword = 'password';
1718
public string $chromeDriverHost = 'localhost';

0 commit comments

Comments
 (0)