@wordpress/env 10.38.1-next.v.0 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +158 -75
  2. package/lib/cli.js +85 -24
  3. package/lib/commands/clean.js +16 -38
  4. package/lib/commands/cleanup.js +79 -0
  5. package/lib/commands/destroy.js +33 -41
  6. package/lib/commands/index.js +6 -2
  7. package/lib/commands/logs.js +20 -62
  8. package/lib/commands/reset.js +46 -0
  9. package/lib/commands/run.js +16 -117
  10. package/lib/commands/start.js +78 -243
  11. package/lib/commands/status.js +160 -0
  12. package/lib/commands/stop.js +17 -19
  13. package/lib/config/get-config-from-environment-vars.js +2 -5
  14. package/lib/config/load-config.js +35 -5
  15. package/lib/config/parse-config.js +53 -21
  16. package/lib/config/post-process-config.js +38 -16
  17. package/lib/config/test/__snapshots__/config-integration.js.snap +16 -0
  18. package/lib/config/test/parse-config.js +33 -0
  19. package/lib/config/test/post-process-config.js +52 -0
  20. package/lib/download-sources.js +9 -53
  21. package/lib/{build-docker-compose-config.js → runtime/docker/build-docker-compose-config.js} +167 -128
  22. package/lib/runtime/docker/download-sources.js +63 -0
  23. package/lib/{download-wp-phpunit.js → runtime/docker/download-wp-phpunit.js} +4 -1
  24. package/lib/runtime/docker/index.js +863 -0
  25. package/lib/{init-config.js → runtime/docker/init-config.js} +22 -14
  26. package/lib/runtime/docker/wordpress.js +309 -0
  27. package/lib/runtime/errors.js +28 -0
  28. package/lib/runtime/index.js +91 -0
  29. package/lib/runtime/playground/blueprint-builder.js +158 -0
  30. package/lib/runtime/playground/index.js +530 -0
  31. package/lib/test/build-docker-compose-config.js +150 -3
  32. package/lib/test/cli.js +49 -13
  33. package/lib/wordpress.js +1 -297
  34. package/package.json +6 -3
  35. package/lib/commands/install-path.js +0 -21
  36. /package/lib/{get-host-user.js → runtime/docker/get-host-user.js} +0 -0
  37. /package/lib/{validate-run-container.js → runtime/docker/validate-run-container.js} +0 -0
@@ -0,0 +1,530 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * External dependencies
5
+ */
6
+ const fs = require( 'fs' ).promises;
7
+ const http = require( 'http' );
8
+ const path = require( 'path' );
9
+ const spawn = require( 'cross-spawn' );
10
+
11
+ /**
12
+ * Promisified dependencies
13
+ */
14
+ const { rimraf } = require( 'rimraf' );
15
+
16
+ /**
17
+ * Internal dependencies
18
+ */
19
+ const { buildBlueprint, getMountArgs } = require( './blueprint-builder' );
20
+ const { UnsupportedCommandError } = require( '../errors' );
21
+ const { downloadSource } = require( '../../download-sources' );
22
+
23
+ /**
24
+ * Playground runtime implementation for wp-env.
25
+ */
26
+ class PlaygroundRuntime {
27
+ constructor() {
28
+ this.serverProcess = null;
29
+ this.serverPort = null;
30
+ }
31
+
32
+ /**
33
+ * Get the name of this runtime.
34
+ *
35
+ * @return {string} Runtime name.
36
+ */
37
+ getName() {
38
+ return 'playground';
39
+ }
40
+
41
+ /**
42
+ * Get supported features for this runtime.
43
+ *
44
+ * @return {Object} Feature flags.
45
+ */
46
+ getFeatures() {
47
+ return {
48
+ testsEnvironment: false, // Single environment only
49
+ xdebug: true, // Supported via --xdebug flag
50
+ spx: false, // Not supported in WebAssembly
51
+ phpMyAdmin: false, // Supported on playground.wordpress.net but not in CLI yet
52
+ multisite: true, // Supported via Blueprint
53
+ customPhpVersion: true, // Supported via --php flag
54
+ persistentDatabase: false, // Could be supported via mounts (not yet implemented)
55
+ wpCli: true, // Limited support (not extensively tested)
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Check if Playground CLI is available.
61
+ *
62
+ * @return {Promise<boolean>} True if Playground CLI is available.
63
+ */
64
+ async isAvailable() {
65
+ // npx will fetch it if not installed locally
66
+ return true;
67
+ }
68
+
69
+ /**
70
+ * Get the warning message for destroy confirmation.
71
+ *
72
+ * @return {string} Warning message.
73
+ */
74
+ getDestroyWarningMessage() {
75
+ return 'WARNING! This will remove the WordPress Playground environment and all local files.';
76
+ }
77
+
78
+ /**
79
+ * Get the warning message for cleanup confirmation.
80
+ *
81
+ * @return {string} Warning message.
82
+ */
83
+ getCleanupWarningMessage() {
84
+ return 'WARNING! This will remove the WordPress Playground environment and all local files.';
85
+ }
86
+
87
+ /**
88
+ * Start the WordPress Playground environment.
89
+ *
90
+ * @param {Object} config The wp-env config object.
91
+ * @param {Object} options Start options.
92
+ * @param {Object} options.spinner A CLI spinner which indicates progress.
93
+ * @param {boolean} options.debug True if debug mode is enabled.
94
+ * @param {string} options.xdebug The Xdebug mode to set.
95
+ */
96
+ async start( config, { spinner, debug, xdebug } ) {
97
+ const envConfig = config.env.development;
98
+
99
+ spinner.text = 'Starting WordPress Playground.';
100
+
101
+ // Download remote sources (git/zip) if needed
102
+ const sources = [];
103
+ const addedSources = {};
104
+ const addSource = ( source ) => {
105
+ if (
106
+ source &&
107
+ ( source.type === 'git' || source.type === 'zip' ) &&
108
+ ! addedSources[ source.url ]
109
+ ) {
110
+ sources.push( source );
111
+ addedSources[ source.url ] = true;
112
+ }
113
+ };
114
+
115
+ // Collect all sources that need downloading
116
+ envConfig.pluginSources.forEach( addSource );
117
+ envConfig.themeSources.forEach( addSource );
118
+ Object.values( envConfig.mappings ).forEach( addSource );
119
+ addSource( envConfig.coreSource );
120
+
121
+ // Download sources if any exist
122
+ if ( sources.length > 0 ) {
123
+ spinner.text = 'Downloading sources.';
124
+
125
+ await Promise.all(
126
+ sources.map( ( source ) =>
127
+ downloadSource( source, {
128
+ onProgress: () => {}, // Progress tracking could be added
129
+ spinner,
130
+ debug,
131
+ } )
132
+ )
133
+ );
134
+ }
135
+
136
+ // Build and save blueprint
137
+ const blueprint = buildBlueprint( config );
138
+ const blueprintPath = path.join(
139
+ config.workDirectoryPath,
140
+ 'playground-blueprint.json'
141
+ );
142
+ await fs.mkdir( config.workDirectoryPath, { recursive: true } );
143
+ await fs.writeFile(
144
+ blueprintPath,
145
+ JSON.stringify( blueprint, null, 2 )
146
+ );
147
+
148
+ // Get mount arguments
149
+ const mountArgs = getMountArgs( config );
150
+
151
+ // Determine port
152
+ const port = envConfig.port || 8888;
153
+ const phpVersion = envConfig.phpVersion || '8.2';
154
+
155
+ // Build command arguments for direct execution
156
+ const cliArgs = [
157
+ 'server',
158
+ '--port',
159
+ String( port ),
160
+ '--php',
161
+ phpVersion,
162
+ '--blueprint',
163
+ blueprintPath,
164
+ '--login',
165
+ '--experimental-multi-worker',
166
+ ...mountArgs,
167
+ ];
168
+
169
+ if ( debug ) {
170
+ cliArgs.push( '--verbosity', 'debug' );
171
+ }
172
+
173
+ if ( xdebug ) {
174
+ cliArgs.push( '--xdebug' );
175
+ }
176
+
177
+ spinner.text = `Starting Playground on port ${ port }...`;
178
+
179
+ const siteUrl = `http://localhost:${ port }`;
180
+ const logFile = path.join( config.workDirectoryPath, 'playground.log' );
181
+ const pidFile = path.join( config.workDirectoryPath, 'playground.pid' );
182
+
183
+ // Use cross-spawn with detached mode for cross-platform support
184
+ // Create write stream for log file
185
+ const logFileStream = await fs.open( logFile, 'w' );
186
+
187
+ return new Promise( ( resolve, reject ) => {
188
+ const child = spawn( 'npx', [ '@wp-playground/cli', ...cliArgs ], {
189
+ detached: true,
190
+ stdio: [ 'ignore', logFileStream.fd, logFileStream.fd ],
191
+ env: { ...process.env, FORCE_COLOR: '0' },
192
+ } );
193
+
194
+ // Store child process reference
195
+ this.serverProcess = child;
196
+ this.serverPort = port;
197
+
198
+ // Save PID to file immediately so stop command can find the
199
+ // process even if startup fails before the server is ready.
200
+ fs.writeFile( pidFile, String( child.pid ) ).catch( () => {} );
201
+
202
+ // Allow parent to exit independently
203
+ child.unref();
204
+
205
+ // Track whether the process has exited so cleanup knows
206
+ // whether it still needs to kill it.
207
+ let processExited = false;
208
+
209
+ // If the process exits before the server is ready (e.g. blueprint
210
+ // validation error), reject immediately instead of waiting for
211
+ // the full 120-second timeout.
212
+ const earlyExitPromise = new Promise( ( _, rejectEarly ) => {
213
+ child.on( 'exit', ( code, signal ) => {
214
+ processExited = true;
215
+ const reason =
216
+ code !== null
217
+ ? `with code ${ code }`
218
+ : `from signal ${ signal }`;
219
+ rejectEarly(
220
+ new Error(
221
+ `Playground process exited unexpectedly ${ reason }.`
222
+ )
223
+ );
224
+ } );
225
+ } );
226
+
227
+ child.on( 'error', ( error ) => {
228
+ logFileStream.close();
229
+ reject(
230
+ new Error(
231
+ `Failed to start Playground: ${ error.message }`
232
+ )
233
+ );
234
+ } );
235
+
236
+ // Race: wait for server to respond vs process early exit.
237
+ Promise.race( [
238
+ this._waitForServer( port, 120000 ),
239
+ earlyExitPromise,
240
+ ] )
241
+ .then( async () => {
242
+ spinner.text = `WordPress Playground started at ${ siteUrl }`;
243
+
244
+ const message =
245
+ 'WordPress development site started at ' + siteUrl;
246
+
247
+ resolve( {
248
+ message,
249
+ siteUrl,
250
+ } );
251
+ } )
252
+ .catch( async ( error ) => {
253
+ // Kill the process if it is still running.
254
+ if ( ! processExited && this.serverProcess ) {
255
+ this.serverProcess.kill( 'SIGKILL' );
256
+ this.serverProcess = null;
257
+ }
258
+
259
+ // Clean up PID file
260
+ try {
261
+ await fs.unlink( pidFile );
262
+ } catch {
263
+ // Ignore if file doesn't exist
264
+ }
265
+
266
+ // Read log file for error details
267
+ let logContent = '';
268
+ try {
269
+ logContent = await fs.readFile( logFile, 'utf8' );
270
+ } catch {
271
+ // Ignore
272
+ }
273
+
274
+ await logFileStream.close();
275
+
276
+ reject(
277
+ new Error(
278
+ `${ error.message }\n\nPlayground log:\n${
279
+ logContent || '(no log output)'
280
+ }`
281
+ )
282
+ );
283
+ } );
284
+ } );
285
+ }
286
+
287
+ /**
288
+ * Stop the WordPress Playground environment.
289
+ *
290
+ * @param {Object} config The wp-env config object.
291
+ * @param {Object} options Stop options.
292
+ * @param {Object} options.spinner A CLI spinner which indicates progress.
293
+ */
294
+ async stop( config, { spinner } ) {
295
+ spinner.text = 'Stopping WordPress Playground.';
296
+
297
+ const pidFile = path.join( config.workDirectoryPath, 'playground.pid' );
298
+
299
+ // Try to read PID from file if process reference not available
300
+ let pid = this.serverProcess?.pid;
301
+ if ( ! pid ) {
302
+ try {
303
+ const pidContent = await fs.readFile( pidFile, 'utf8' );
304
+ pid = parseInt( pidContent.trim(), 10 );
305
+ } catch ( error ) {
306
+ // PID file doesn't exist or can't be read
307
+ spinner.text = 'Stopped WordPress Playground.';
308
+ return;
309
+ }
310
+ }
311
+
312
+ if ( pid ) {
313
+ try {
314
+ // Kill the entire process group (negative PID)
315
+ // This ensures both the npm process and child node process are killed
316
+ process.kill( -pid, 'SIGTERM' );
317
+
318
+ // Give it a moment for graceful shutdown
319
+ await new Promise( ( r ) => setTimeout( r, 1000 ) );
320
+
321
+ // Check if still running and force kill if needed
322
+ try {
323
+ process.kill( -pid, 0 ); // Check if process group exists
324
+ process.kill( -pid, 'SIGKILL' ); // Force kill entire group
325
+ } catch {
326
+ // Process group already terminated
327
+ }
328
+ } catch ( error ) {
329
+ // Process group doesn't exist or already terminated
330
+ }
331
+
332
+ // Clean up PID file
333
+ try {
334
+ await fs.unlink( pidFile );
335
+ } catch {
336
+ // Ignore if file doesn't exist
337
+ }
338
+
339
+ this.serverProcess = null;
340
+ this.serverPort = null;
341
+ }
342
+
343
+ spinner.text = 'Stopped WordPress Playground.';
344
+ }
345
+
346
+ /**
347
+ * Destroy the WordPress Playground environment.
348
+ *
349
+ * @param {Object} config The wp-env config object.
350
+ * @param {Object} options Destroy options.
351
+ * @param {Object} options.spinner A CLI spinner which indicates progress.
352
+ */
353
+ async destroy( config, { spinner } ) {
354
+ await this.stop( config, { spinner } );
355
+
356
+ spinner.text = 'Removing local files.';
357
+ await rimraf( config.workDirectoryPath );
358
+
359
+ spinner.text = 'Removed WordPress Playground environment.';
360
+ }
361
+
362
+ /**
363
+ * Cleanup the WordPress Playground environment.
364
+ *
365
+ * For Playground, cleanup is the same as destroy since there are no
366
+ * shared resources like Docker images to preserve.
367
+ *
368
+ * @param {Object} config The wp-env config object.
369
+ * @param {Object} options Cleanup options.
370
+ * @param {Object} options.spinner A CLI spinner which indicates progress.
371
+ */
372
+ async cleanup( config, { spinner } ) {
373
+ await this.stop( config, { spinner } );
374
+
375
+ spinner.text = 'Removing local files.';
376
+ await rimraf( config.workDirectoryPath );
377
+
378
+ spinner.text = 'Cleaned up WordPress Playground environment.';
379
+ }
380
+
381
+ /**
382
+ * Run a command in the Playground environment.
383
+ *
384
+ * @param {Object} config The wp-env config object.
385
+ * @param {Object} options Run options.
386
+ * @param {string} options.container The container to run the command in.
387
+ * @param {string[]} options.command The command to run.
388
+ * @param {string} options.envCwd The working directory.
389
+ * @param {Object} options.spinner A CLI spinner which indicates progress.
390
+ * @param {boolean} options.debug True if debug mode is enabled.
391
+ */
392
+ // eslint-disable-next-line no-unused-vars
393
+ async run( config, { container, command, envCwd, spinner, debug } ) {
394
+ throw new UnsupportedCommandError( 'run' );
395
+ }
396
+
397
+ /**
398
+ * Reset the WordPress database.
399
+ *
400
+ * @param {Object} config The wp-env config object.
401
+ * @param {Object} options Reset options.
402
+ * @param {Object} options.spinner A CLI spinner which indicates progress.
403
+ * @param {boolean} options.debug True if debug mode is enabled.
404
+ */
405
+ async clean( config, { spinner, debug } ) {
406
+ spinner.text = 'Resetting WordPress Playground environment.';
407
+
408
+ // For Playground, we restart the server to reset the database
409
+ await this.stop( config, { spinner } );
410
+ await this.start( config, { spinner, debug } );
411
+
412
+ spinner.text = 'Reset WordPress Playground environment.';
413
+ }
414
+
415
+ /**
416
+ * Get the status of the Playground environment.
417
+ *
418
+ * @param {Object} config The wp-env config object.
419
+ * @param {Object} options Status options.
420
+ * @param {Object} options.spinner A CLI spinner which indicates progress.
421
+ * @return {Promise<Object>} Status object with environment information.
422
+ */
423
+ async getStatus( config, { spinner } ) {
424
+ spinner.text = 'Getting environment status.';
425
+
426
+ const envConfig = config.env.development;
427
+ const port = envConfig.port || 8888;
428
+ const pidFile = path.join( config.workDirectoryPath, 'playground.pid' );
429
+
430
+ // Check if server is running.
431
+ let isRunning = false;
432
+
433
+ try {
434
+ const pidContent = await fs.readFile( pidFile, 'utf8' );
435
+ const pid = parseInt( pidContent.trim(), 10 );
436
+
437
+ // Check if process is still alive.
438
+ process.kill( pid, 0 );
439
+
440
+ // Check if server is responding.
441
+ await this._checkServer( port );
442
+ isRunning = true;
443
+ } catch {
444
+ // Process not running or server not responding.
445
+ }
446
+
447
+ return {
448
+ status: isRunning ? 'running' : 'stopped',
449
+ runtime: 'playground',
450
+ urls: {
451
+ development: isRunning ? `http://localhost:${ port }` : null,
452
+ },
453
+ ports: {
454
+ development: port,
455
+ },
456
+ config: {
457
+ multisite: envConfig.multisite,
458
+ xdebug: 'off',
459
+ },
460
+ configPath: config.configDirectoryPath,
461
+ installPath: config.workDirectoryPath,
462
+ };
463
+ }
464
+
465
+ /**
466
+ * Show logs from the Playground environment.
467
+ *
468
+ * @param {Object} config The wp-env config object.
469
+ * @param {Object} options Logs options.
470
+ * @param {string} options.environment The environment to show logs for.
471
+ * @param {boolean} options.watch If true, follow along with log output.
472
+ * @param {Object} options.spinner A CLI spinner which indicates progress.
473
+ * @param {boolean} options.debug True if debug mode is enabled.
474
+ */
475
+ // eslint-disable-next-line no-unused-vars
476
+ async logs( config, { environment, watch, spinner, debug } ) {
477
+ throw new UnsupportedCommandError( 'logs' );
478
+ }
479
+
480
+ /**
481
+ * Wait for the server to be ready.
482
+ *
483
+ * @param {number} port Port to check.
484
+ * @param {number} timeout Timeout in milliseconds.
485
+ * @return {Promise<void>}
486
+ */
487
+ async _waitForServer( port, timeout = 30000 ) {
488
+ const start = Date.now();
489
+
490
+ while ( Date.now() - start < timeout ) {
491
+ try {
492
+ await this._checkServer( port );
493
+ return;
494
+ } catch {
495
+ await new Promise( ( r ) => setTimeout( r, 500 ) );
496
+ }
497
+ }
498
+
499
+ throw new Error(
500
+ `Playground server did not start within ${
501
+ timeout / 1000
502
+ } seconds.`
503
+ );
504
+ }
505
+
506
+ /**
507
+ * Check if server is responding.
508
+ *
509
+ * @param {number} port Port to check.
510
+ * @return {Promise<void>}
511
+ */
512
+ _checkServer( port ) {
513
+ return new Promise( ( resolve, reject ) => {
514
+ const req = http.get( `http://localhost:${ port }`, ( res ) => {
515
+ if ( res.statusCode >= 200 && res.statusCode < 400 ) {
516
+ resolve();
517
+ } else {
518
+ reject( new Error( `Status: ${ res.statusCode }` ) );
519
+ }
520
+ } );
521
+ req.on( 'error', reject );
522
+ req.setTimeout( 1000, () => {
523
+ req.destroy();
524
+ reject( new Error( 'Timeout' ) );
525
+ } );
526
+ } );
527
+ }
528
+ }
529
+
530
+ module.exports = PlaygroundRuntime;
@@ -2,8 +2,8 @@
2
2
  /**
3
3
  * Internal dependencies
4
4
  */
5
- const buildDockerComposeConfig = require( '../build-docker-compose-config' );
6
- const getHostUser = require( '../get-host-user' );
5
+ const buildDockerComposeConfig = require( '../runtime/docker/build-docker-compose-config' );
6
+ const getHostUser = require( '../runtime/docker/get-host-user' );
7
7
 
8
8
  // The basic config keys which build docker compose config requires.
9
9
  const CONFIG = {
@@ -14,7 +14,7 @@ const CONFIG = {
14
14
  configDirectoryPath: '/path/to/config',
15
15
  };
16
16
 
17
- jest.mock( '../get-host-user', () => jest.fn() );
17
+ jest.mock( '../runtime/docker/get-host-user', () => jest.fn() );
18
18
  getHostUser.mockImplementation( () => {
19
19
  return {
20
20
  name: 'test',
@@ -131,4 +131,151 @@ describe( 'buildDockerComposeConfig', () => {
131
131
  expect( dockerConfig.volumes.wordpress ).toBe( undefined );
132
132
  expect( dockerConfig.volumes[ 'tests-wordpress' ] ).toBe( undefined );
133
133
  } );
134
+
135
+ it( 'should add healthcheck to mysql services', () => {
136
+ const config = buildDockerComposeConfig( {
137
+ workDirectoryPath: '/some/path',
138
+ env: {
139
+ development: {
140
+ port: 8888,
141
+ mysqlPort: 3306,
142
+ coreSource: null,
143
+ pluginSources: [],
144
+ themeSources: [],
145
+ mappings: {},
146
+ },
147
+ tests: {
148
+ port: 8889,
149
+ mysqlPort: 3307,
150
+ coreSource: null,
151
+ pluginSources: [],
152
+ themeSources: [],
153
+ mappings: {},
154
+ },
155
+ },
156
+ } );
157
+
158
+ expect( config.services.mysql.healthcheck ).toBeDefined();
159
+ expect( config.services.mysql.healthcheck.test ).toEqual( [
160
+ 'CMD',
161
+ 'healthcheck.sh',
162
+ '--connect',
163
+ '--innodb_initialized',
164
+ ] );
165
+ expect( config.services.mysql.healthcheck.interval ).toBe( '5s' );
166
+ expect( config.services.mysql.healthcheck.timeout ).toBe( '10s' );
167
+ expect( config.services.mysql.healthcheck.retries ).toBe( 12 );
168
+ expect( config.services.mysql.healthcheck.start_period ).toBe( '60s' );
169
+
170
+ // Verify MARIADB_AUTO_UPGRADE is set for existing installations
171
+ expect( config.services.mysql.environment.MARIADB_AUTO_UPGRADE ).toBe(
172
+ '1'
173
+ );
174
+
175
+ expect( config.services[ 'tests-mysql' ].healthcheck ).toBeDefined();
176
+ expect( config.services[ 'tests-mysql' ].healthcheck.test ).toEqual( [
177
+ 'CMD',
178
+ 'healthcheck.sh',
179
+ '--connect',
180
+ '--innodb_initialized',
181
+ ] );
182
+ expect(
183
+ config.services[ 'tests-mysql' ].environment.MARIADB_AUTO_UPGRADE
184
+ ).toBe( '1' );
185
+ } );
186
+
187
+ it( 'should use service_healthy condition for WordPress depends_on', () => {
188
+ const config = buildDockerComposeConfig( {
189
+ workDirectoryPath: '/some/path',
190
+ env: {
191
+ development: {
192
+ port: 8888,
193
+ mysqlPort: 3306,
194
+ coreSource: null,
195
+ pluginSources: [],
196
+ themeSources: [],
197
+ mappings: {},
198
+ },
199
+ tests: {
200
+ port: 8889,
201
+ mysqlPort: 3307,
202
+ coreSource: null,
203
+ pluginSources: [],
204
+ themeSources: [],
205
+ mappings: {},
206
+ },
207
+ },
208
+ } );
209
+
210
+ expect( config.services.wordpress.depends_on ).toEqual( {
211
+ mysql: { condition: 'service_healthy' },
212
+ } );
213
+ expect( config.services[ 'tests-wordpress' ].depends_on ).toEqual( {
214
+ 'tests-mysql': { condition: 'service_healthy' },
215
+ } );
216
+ } );
217
+
218
+ describe( 'testsEnvironment', () => {
219
+ it( 'should omit tests services when testsEnvironment is false', () => {
220
+ const dockerConfig = buildDockerComposeConfig( {
221
+ testsEnvironment: false,
222
+ workDirectoryPath: '/path',
223
+ env: {
224
+ development: CONFIG,
225
+ tests: CONFIG,
226
+ },
227
+ } );
228
+
229
+ // Development services should exist.
230
+ expect( dockerConfig.services.mysql ).toBeDefined();
231
+ expect( dockerConfig.services.wordpress ).toBeDefined();
232
+ expect( dockerConfig.services.cli ).toBeDefined();
233
+ expect( dockerConfig.services.phpmyadmin ).toBeDefined();
234
+
235
+ // Tests services should not exist.
236
+ expect( dockerConfig.services[ 'tests-mysql' ] ).toBeUndefined();
237
+ expect(
238
+ dockerConfig.services[ 'tests-wordpress' ]
239
+ ).toBeUndefined();
240
+ expect( dockerConfig.services[ 'tests-cli' ] ).toBeUndefined();
241
+ expect(
242
+ dockerConfig.services[ 'tests-phpmyadmin' ]
243
+ ).toBeUndefined();
244
+ } );
245
+
246
+ it( 'should omit tests volumes when testsEnvironment is false', () => {
247
+ const dockerConfig = buildDockerComposeConfig( {
248
+ testsEnvironment: false,
249
+ workDirectoryPath: '/path',
250
+ env: {
251
+ development: CONFIG,
252
+ tests: CONFIG,
253
+ },
254
+ } );
255
+
256
+ // Development volumes should exist.
257
+ expect( dockerConfig.volumes.wordpress ).toBeDefined();
258
+ expect( dockerConfig.volumes.mysql ).toBeDefined();
259
+ expect( dockerConfig.volumes[ 'user-home' ] ).toBeDefined();
260
+
261
+ // Tests volumes should not exist.
262
+ expect( dockerConfig.volumes[ 'tests-wordpress' ] ).toBeUndefined();
263
+ expect( dockerConfig.volumes[ 'mysql-test' ] ).toBeUndefined();
264
+ expect( dockerConfig.volumes[ 'tests-user-home' ] ).toBeUndefined();
265
+ } );
266
+
267
+ it( 'should include tests services by default', () => {
268
+ const dockerConfig = buildDockerComposeConfig( {
269
+ workDirectoryPath: '/path',
270
+ env: {
271
+ development: CONFIG,
272
+ tests: CONFIG,
273
+ },
274
+ } );
275
+
276
+ expect( dockerConfig.services[ 'tests-mysql' ] ).toBeDefined();
277
+ expect( dockerConfig.services[ 'tests-wordpress' ] ).toBeDefined();
278
+ expect( dockerConfig.services[ 'tests-cli' ] ).toBeDefined();
279
+ } );
280
+ } );
134
281
  } );