@wordpress/env 6.0.0 → 7.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 (54) hide show
  1. package/README.md +109 -43
  2. package/lib/build-docker-compose-config.js +67 -96
  3. package/lib/cache.js +1 -0
  4. package/lib/cli.js +16 -3
  5. package/lib/commands/clean.js +14 -1
  6. package/lib/commands/destroy.js +12 -37
  7. package/lib/commands/index.js +1 -0
  8. package/lib/commands/install-path.js +1 -0
  9. package/lib/commands/logs.js +1 -0
  10. package/lib/commands/run.js +59 -3
  11. package/lib/commands/start.js +30 -8
  12. package/lib/commands/stop.js +1 -0
  13. package/lib/config/add-or-replace-port.js +12 -3
  14. package/lib/config/db-env.js +1 -0
  15. package/lib/config/detect-directory-type.js +1 -1
  16. package/lib/config/get-cache-directory.js +39 -0
  17. package/lib/config/get-config-from-environment-vars.js +86 -0
  18. package/lib/config/index.js +7 -5
  19. package/lib/config/load-config.js +92 -0
  20. package/lib/config/merge-configs.js +104 -0
  21. package/lib/config/parse-config.js +418 -157
  22. package/lib/config/parse-source-string.js +164 -0
  23. package/lib/config/post-process-config.js +202 -0
  24. package/lib/config/read-raw-config-file.js +7 -33
  25. package/lib/config/test/__snapshots__/config-integration.js.snap +271 -0
  26. package/lib/config/test/add-or-replace-port.js +29 -1
  27. package/lib/config/test/config-integration.js +143 -0
  28. package/lib/config/test/get-cache-directory.js +57 -0
  29. package/lib/config/test/merge-configs.js +111 -0
  30. package/lib/config/test/parse-config.js +342 -0
  31. package/lib/config/test/parse-source-string.js +154 -0
  32. package/lib/config/test/post-process-config.js +295 -0
  33. package/lib/config/test/read-raw-config-file.js +19 -63
  34. package/lib/config/test/validate-config.js +305 -0
  35. package/lib/config/validate-config.js +93 -54
  36. package/lib/env.js +2 -0
  37. package/lib/execute-after-setup.js +51 -0
  38. package/lib/get-host-user.js +31 -0
  39. package/lib/init-config.js +181 -63
  40. package/lib/md5.js +1 -0
  41. package/lib/parse-xdebug-mode.js +1 -0
  42. package/lib/retry.js +1 -0
  43. package/lib/test/__snapshots__/md5.js.snap +9 -0
  44. package/lib/test/build-docker-compose-config.js +134 -0
  45. package/lib/test/cache.js +154 -0
  46. package/lib/test/cli.js +149 -0
  47. package/lib/test/execute-after-setup.js +66 -0
  48. package/lib/test/md5.js +35 -0
  49. package/lib/test/parse-xdebug-mode.js +41 -0
  50. package/lib/wordpress.js +4 -19
  51. package/package.json +2 -2
  52. package/lib/config/config.js +0 -353
  53. package/lib/config/test/__snapshots__/config.js.snap +0 -83
  54. package/lib/config/test/config.js +0 -1241
@@ -1,1241 +0,0 @@
1
- /* eslint-disable jest/no-conditional-expect */
2
- /**
3
- * External dependencies
4
- */
5
- const { readFile, stat } = require( 'fs' ).promises;
6
- const os = require( 'os' );
7
- const { join, resolve } = require( 'path' );
8
-
9
- /**
10
- * Internal dependencies
11
- */
12
- const { readConfig, ValidationError } = require( '..' );
13
- const detectDirectoryType = require( '../detect-directory-type' );
14
-
15
- jest.mock( 'fs', () => ( {
16
- promises: {
17
- readFile: jest.fn(),
18
- stat: jest.fn().mockReturnValue( Promise.resolve( false ) ),
19
- },
20
- } ) );
21
-
22
- // This mocks a small response with a format matching the stable-check API.
23
- // It makes getLatestWordPressVersion resolve to "100.0.0".
24
- jest.mock( 'got', () =>
25
- jest.fn( ( url ) => ( {
26
- json: () => {
27
- if ( url === 'https://api.wordpress.org/core/stable-check/1.0/' ) {
28
- return Promise.resolve( {
29
- '1.0': 'insecure',
30
- '99.1.1': 'outdated',
31
- '100.0.0': 'latest',
32
- '100.0.1': 'fancy',
33
- } );
34
- }
35
- },
36
- } ) )
37
- );
38
-
39
- jest.mock( '../detect-directory-type', () => jest.fn() );
40
-
41
- describe( 'readConfig', () => {
42
- beforeEach( () => {
43
- jest.clearAllMocks();
44
- } );
45
-
46
- describe( 'config file', () => {
47
- it( 'should throw a validation error if config is invalid JSON', async () => {
48
- readFile.mockImplementation( () => Promise.resolve( '{' ) );
49
- expect.assertions( 2 );
50
- try {
51
- await readConfig( '.wp-env.json' );
52
- } catch ( error ) {
53
- expect( error ).toBeInstanceOf( ValidationError );
54
- expect( error.message ).toContain( 'Invalid .wp-env.json' );
55
- }
56
- } );
57
-
58
- it( 'should throw a validation error if config cannot be read', async () => {
59
- readFile.mockImplementation( () =>
60
- Promise.reject( { message: 'Uh oh!' } )
61
- );
62
- expect.assertions( 2 );
63
- try {
64
- await readConfig( '.wp-env.json' );
65
- } catch ( error ) {
66
- expect( error ).toBeInstanceOf( ValidationError );
67
- expect( error.message ).toContain(
68
- 'Could not read .wp-env.json'
69
- );
70
- }
71
- } );
72
-
73
- it( 'should throw a validation error if WP_SITEURL is not a valid URL', async () => {
74
- readFile.mockImplementation( () =>
75
- Promise.resolve(
76
- JSON.stringify( {
77
- config: {
78
- WP_SITEURL: 'test',
79
- },
80
- } )
81
- )
82
- );
83
- expect.assertions( 2 );
84
- try {
85
- await readConfig( '.wp-env.json' );
86
- } catch ( error ) {
87
- expect( error ).toBeInstanceOf( ValidationError );
88
- expect( error.message ).toContain( 'must be a valid URL' );
89
- }
90
- } );
91
-
92
- it( 'should throw a validation error if WP_HOME is not a valid URL', async () => {
93
- readFile.mockImplementation( () =>
94
- Promise.resolve(
95
- JSON.stringify( {
96
- config: {
97
- WP_SITEURL: 'test',
98
- },
99
- } )
100
- )
101
- );
102
- expect.assertions( 2 );
103
- try {
104
- await readConfig( '.wp-env.json' );
105
- } catch ( error ) {
106
- expect( error ).toBeInstanceOf( ValidationError );
107
- expect( error.message ).toContain( 'must be a valid URL' );
108
- }
109
- } );
110
-
111
- it( 'should infer a core config when ran from a core directory', async () => {
112
- readFile.mockImplementation( () =>
113
- Promise.reject( { code: 'ENOENT' } )
114
- );
115
- detectDirectoryType.mockImplementation( () => 'core' );
116
- const config = await readConfig( '.wp-env.json' );
117
- expect( config.env.development.coreSource.type ).toBe( 'local' );
118
- expect( config.env.tests.coreSource ).not.toBeNull();
119
- expect( config.env.development.pluginSources ).toHaveLength( 0 );
120
- expect( config.env.development.themeSources ).toHaveLength( 0 );
121
- } );
122
-
123
- it( 'should use the most recent stable WordPress version for the default core source', async () => {
124
- readFile.mockImplementation( () =>
125
- Promise.resolve( JSON.stringify( {} ) )
126
- );
127
- const config = await readConfig( '.wp-env.json' );
128
-
129
- const expected = {
130
- url: 'https://github.com/WordPress/WordPress.git',
131
- type: 'git',
132
- basename: 'WordPress',
133
- ref: '100.0.0', // From the mock of https at the top of the file.
134
- };
135
-
136
- expect( config.env.development.coreSource ).toMatchObject(
137
- expected
138
- );
139
- expect( config.env.tests.coreSource ).toMatchObject( expected );
140
- } );
141
-
142
- it( 'should infer a plugin config when ran from a plugin directory', async () => {
143
- readFile.mockImplementation( () =>
144
- Promise.reject( { code: 'ENOENT' } )
145
- );
146
- detectDirectoryType.mockImplementation( () => 'plugin' );
147
- const config = await readConfig( '.wp-env.json' );
148
- expect( config.env.development.coreSource.type ).toBe( 'git' );
149
- expect( config.env.development.pluginSources ).toHaveLength( 1 );
150
- expect( config.env.tests.pluginSources ).toHaveLength( 1 );
151
- expect( config.env.development.themeSources ).toHaveLength( 0 );
152
- } );
153
-
154
- it( 'should infer a theme config when ran from a theme directory', async () => {
155
- readFile.mockImplementation( () =>
156
- Promise.reject( { code: 'ENOENT' } )
157
- );
158
- detectDirectoryType.mockImplementation( () => 'theme' );
159
- const config = await readConfig( '.wp-env.json' );
160
- expect( config.env.development.coreSource.type ).toBe( 'git' );
161
- expect( config.env.tests.coreSource.type ).toBe( 'git' );
162
- expect( config.env.development.themeSources ).toHaveLength( 1 );
163
- expect( config.env.tests.themeSources ).toHaveLength( 1 );
164
- expect( config.env.development.pluginSources ).toHaveLength( 0 );
165
- expect( config.env.tests.pluginSources ).toHaveLength( 0 );
166
- } );
167
-
168
- it( 'should use the WP_ENV_HOME environment variable only if specified', async () => {
169
- readFile.mockImplementation( () =>
170
- Promise.resolve( JSON.stringify( {} ) )
171
- );
172
- const oldEnvHome = process.env.WP_ENV_HOME;
173
-
174
- expect.assertions( 2 );
175
-
176
- process.env.WP_ENV_HOME = 'here/is/a/path';
177
- const configWith = await readConfig( '.wp-env.json' );
178
- expect(
179
- configWith.workDirectoryPath.includes(
180
- join( 'here', 'is', 'a', 'path' )
181
- )
182
- ).toBe( true );
183
-
184
- process.env.WP_ENV_HOME = undefined;
185
- const configWithout = await readConfig( '.wp-env.json' );
186
- expect(
187
- configWithout.workDirectoryPath.includes(
188
- join( 'here', 'is', 'a', 'path' )
189
- )
190
- ).toBe( false );
191
-
192
- process.env.WP_ENV_HOME = oldEnvHome;
193
- } );
194
-
195
- it( 'should use the WP_ENV_HOME environment variable on Linux', async () => {
196
- readFile.mockImplementation( () =>
197
- Promise.resolve( JSON.stringify( {} ) )
198
- );
199
- const oldEnvHome = process.env.WP_ENV_HOME;
200
- const oldOsPlatform = os.platform;
201
- os.platform = () => 'linux';
202
-
203
- expect.assertions( 2 );
204
-
205
- process.env.WP_ENV_HOME = 'here/is/a/path';
206
- const configWith = await readConfig( '.wp-env.json' );
207
- expect(
208
- configWith.workDirectoryPath.includes(
209
- join( 'here', 'is', 'a', 'path' )
210
- )
211
- ).toBe( true );
212
-
213
- process.env.WP_ENV_HOME = undefined;
214
- const configWithout = await readConfig( '.wp-env.json' );
215
- expect(
216
- configWithout.workDirectoryPath.includes(
217
- join( 'here', 'is', 'a', 'path' )
218
- )
219
- ).toBe( false );
220
-
221
- process.env.WP_ENV_HOME = oldEnvHome;
222
- os.platform = oldOsPlatform;
223
- } );
224
-
225
- it( 'should use a non-private folder with Snap-installed Docker', async () => {
226
- readFile.mockImplementation( () =>
227
- Promise.resolve( JSON.stringify( {} ) )
228
- );
229
- stat.mockReturnValue( Promise.resolve( true ) );
230
-
231
- expect.assertions( 2 );
232
-
233
- const config = await readConfig( '.wp-env.json' );
234
- expect( config.workDirectoryPath.includes( '.wp-env' ) ).toBe(
235
- false
236
- );
237
- expect( config.workDirectoryPath.includes( 'wp-env' ) ).toBe(
238
- true
239
- );
240
- } );
241
-
242
- it( 'should match snapshot', async () => {
243
- // Note: did not add sources to this config because they include absolute
244
- // paths which would be different elsewhere.
245
- readFile.mockImplementation( () =>
246
- Promise.resolve(
247
- JSON.stringify( {
248
- port: 2000,
249
- config: {
250
- TEST_VAL1: 1,
251
- TEST_VAL2: 'hello',
252
- TEST_VAL3: false,
253
- },
254
- env: {
255
- development: {
256
- config: {
257
- TEST: 100,
258
- },
259
- },
260
- tests: {
261
- port: 1000,
262
- config: {
263
- TEST: 200,
264
- },
265
- },
266
- },
267
- } )
268
- )
269
- );
270
- const config = await readConfig( '.wp-env.json' );
271
- // Remove generated values which are different on other machines.
272
- delete config.dockerComposeConfigPath;
273
- delete config.workDirectoryPath;
274
-
275
- // This encodes both the version of WordPress (which can change frequently)
276
- // as well as the wp-env directory which is unique on every machine.
277
- delete config.env.development.coreSource;
278
- delete config.env.tests.coreSource;
279
- expect( config ).toMatchSnapshot();
280
- } );
281
- } );
282
-
283
- describe( 'source parsing', () => {
284
- it( "should throw a validation error if 'core' is not a string", async () => {
285
- readFile.mockImplementation( () =>
286
- Promise.resolve( JSON.stringify( { core: 123 } ) )
287
- );
288
- expect.assertions( 2 );
289
- try {
290
- await readConfig( '.wp-env.json' );
291
- } catch ( error ) {
292
- expect( error ).toBeInstanceOf( ValidationError );
293
- expect( error.message ).toContain( 'must be null or a string' );
294
- }
295
- } );
296
-
297
- it( "should throw a validation error if 'plugins' is not an array of strings", async () => {
298
- readFile.mockImplementation( () =>
299
- Promise.resolve(
300
- JSON.stringify( { plugins: [ 'test', 123 ] } )
301
- )
302
- );
303
- expect.assertions( 2 );
304
- try {
305
- await readConfig( '.wp-env.json' );
306
- } catch ( error ) {
307
- expect( error ).toBeInstanceOf( ValidationError );
308
- expect( error.message ).toContain(
309
- 'must be an array of strings'
310
- );
311
- }
312
- } );
313
-
314
- it( "should throw a validation error if 'themes' is not an array of strings", async () => {
315
- readFile.mockImplementation( () =>
316
- Promise.resolve( JSON.stringify( { themes: [ 'test', 123 ] } ) )
317
- );
318
- expect.assertions( 2 );
319
- try {
320
- await readConfig( '.wp-env.json' );
321
- } catch ( error ) {
322
- expect( error ).toBeInstanceOf( ValidationError );
323
- expect( error.message ).toContain(
324
- 'must be an array of strings'
325
- );
326
- }
327
- } );
328
-
329
- it( 'should parse local sources', async () => {
330
- readFile.mockImplementation( () =>
331
- Promise.resolve(
332
- JSON.stringify( {
333
- plugins: [
334
- './relative',
335
- '../parent',
336
- `${ os.homedir() }/home`,
337
- ],
338
- } )
339
- )
340
- );
341
- const config = await readConfig( '.wp-env.json' );
342
-
343
- expect( config.env.development ).toMatchObject( {
344
- pluginSources: [
345
- {
346
- type: 'local',
347
- path: expect.stringMatching( /^(\/|\\).*relative$/ ),
348
- basename: 'relative',
349
- },
350
- {
351
- type: 'local',
352
- path: expect.stringMatching( /^(\/|\\).*parent$/ ),
353
- basename: 'parent',
354
- },
355
- {
356
- type: 'local',
357
- path: expect.stringMatching( /^(\/|\\).*home$/ ),
358
- basename: 'home',
359
- },
360
- ],
361
- } );
362
- expect( config.env.tests ).toMatchObject( {
363
- pluginSources: [
364
- {
365
- type: 'local',
366
- path: expect.stringMatching( /^(\/|\\).*relative$/ ),
367
- basename: 'relative',
368
- },
369
- {
370
- type: 'local',
371
- path: expect.stringMatching( /^(\/|\\).*parent$/ ),
372
- basename: 'parent',
373
- },
374
- {
375
- type: 'local',
376
- path: expect.stringMatching( /^(\/|\\).*home$/ ),
377
- basename: 'home',
378
- },
379
- ],
380
- } );
381
- } );
382
-
383
- it( 'should override plugins/themes on an environment level', async () => {
384
- readFile.mockImplementation( () =>
385
- Promise.resolve(
386
- JSON.stringify( {
387
- plugins: [ './test1', './foo2' ],
388
- themes: [ './test2', './foo' ],
389
- env: {
390
- development: {
391
- plugins: [ './test1a' ],
392
- themes: [ './test2a' ],
393
- },
394
- tests: {
395
- plugins: [ './test1b' ],
396
- themes: [ './test2b' ],
397
- },
398
- },
399
- } )
400
- )
401
- );
402
- const config = await readConfig( '.wp-env.json' );
403
- expect( config.env.development.pluginSources ).toEqual( [
404
- {
405
- type: 'local',
406
- path: expect.stringMatching( /^(\/|\\).*test1a$/ ),
407
- basename: 'test1a',
408
- },
409
- ] );
410
- expect( config.env.development.themeSources ).toEqual( [
411
- {
412
- type: 'local',
413
- path: expect.stringMatching( /^(\/|\\).*test2a$/ ),
414
- basename: 'test2a',
415
- },
416
- ] );
417
- expect( config.env.tests.pluginSources ).toEqual( [
418
- {
419
- type: 'local',
420
- path: expect.stringMatching( /^(\/|\\).*test1b$/ ),
421
- basename: 'test1b',
422
- },
423
- ] );
424
- expect( config.env.tests.themeSources ).toEqual( [
425
- {
426
- type: 'local',
427
- path: expect.stringMatching( /^(\/|\\).*test2b$/ ),
428
- basename: 'test2b',
429
- },
430
- ] );
431
- } );
432
-
433
- it( "should set testsPath on the 'core' source", async () => {
434
- readFile.mockImplementation( () =>
435
- Promise.resolve( JSON.stringify( { core: './relative' } ) )
436
- );
437
- const config = await readConfig( '.wp-env.json' );
438
- expect( config.env.development ).toMatchObject( {
439
- coreSource: {
440
- type: 'local',
441
- path: expect.stringMatching( /^(\/|\\).*relative$/ ),
442
- testsPath: expect.stringMatching(
443
- /^(\/|\\).*tests-relative$/
444
- ),
445
- },
446
- } );
447
- expect( config.env.tests ).toMatchObject( {
448
- coreSource: {
449
- type: 'local',
450
- path: expect.stringMatching( /^(\/|\\).*relative$/ ),
451
- testsPath: expect.stringMatching(
452
- /^(\/|\\).*tests-relative$/
453
- ),
454
- },
455
- } );
456
- } );
457
-
458
- it( 'should parse GitHub sources', async () => {
459
- readFile.mockImplementation( () =>
460
- Promise.resolve(
461
- JSON.stringify( {
462
- plugins: [
463
- 'WordPress/gutenberg',
464
- 'WordPress/gutenberg#trunk',
465
- 'WordPress/gutenberg#5.0',
466
- 'WordPress/theme-experiments/tt1-blocks#tt1-blocks@0.4.3',
467
- ],
468
- } )
469
- )
470
- );
471
- const config = await readConfig( '.wp-env.json' );
472
- const matchObj = {
473
- pluginSources: [
474
- {
475
- type: 'git',
476
- url: 'https://github.com/WordPress/gutenberg.git',
477
- ref: undefined,
478
- path: expect.stringMatching( /^(\/|\\).*gutenberg$/ ),
479
- basename: 'gutenberg',
480
- },
481
- {
482
- type: 'git',
483
- url: 'https://github.com/WordPress/gutenberg.git',
484
- ref: 'trunk',
485
- path: expect.stringMatching( /^(\/|\\).*gutenberg$/ ),
486
- basename: 'gutenberg',
487
- },
488
- {
489
- type: 'git',
490
- url: 'https://github.com/WordPress/gutenberg.git',
491
- ref: '5.0',
492
- path: expect.stringMatching( /^(\/|\\).*gutenberg$/ ),
493
- basename: 'gutenberg',
494
- },
495
- {
496
- type: 'git',
497
- url: 'https://github.com/WordPress/theme-experiments.git',
498
- ref: 'tt1-blocks@0.4.3',
499
- path: expect.stringMatching(
500
- /^(\/|\\).*theme-experiments(\/|\\)tt1-blocks$/
501
- ),
502
- basename: 'tt1-blocks',
503
- },
504
- ],
505
- };
506
- expect( config.env.tests ).toMatchObject( matchObj );
507
- expect( config.env.development ).toMatchObject( matchObj );
508
- } );
509
-
510
- it( 'should parse wordpress.org sources', async () => {
511
- readFile.mockImplementation( () =>
512
- Promise.resolve(
513
- JSON.stringify( {
514
- plugins: [
515
- 'https://downloads.wordpress.org/plugin/gutenberg.zip',
516
- 'https://downloads.wordpress.org/plugin/gutenberg.8.1.0.zip',
517
- 'https://downloads.wordpress.org/theme/twentytwenty.zip',
518
- 'https://downloads.wordpress.org/theme/twentytwenty.1.3.zip',
519
- ],
520
- } )
521
- )
522
- );
523
- const config = await readConfig( '.wp-env.json' );
524
- const matchObj = {
525
- pluginSources: [
526
- {
527
- type: 'zip',
528
- url: 'https://downloads.wordpress.org/plugin/gutenberg.zip',
529
- path: expect.stringMatching( /^(\/|\\).*gutenberg$/ ),
530
- basename: 'gutenberg',
531
- },
532
- {
533
- type: 'zip',
534
- url: 'https://downloads.wordpress.org/plugin/gutenberg.8.1.0.zip',
535
- path: expect.stringMatching( /^(\/|\\).*gutenberg$/ ),
536
- basename: 'gutenberg',
537
- },
538
- {
539
- type: 'zip',
540
- url: 'https://downloads.wordpress.org/theme/twentytwenty.zip',
541
- path: expect.stringMatching(
542
- /^(\/|\\).*twentytwenty$/
543
- ),
544
- basename: 'twentytwenty',
545
- },
546
- {
547
- type: 'zip',
548
- url: 'https://downloads.wordpress.org/theme/twentytwenty.1.3.zip',
549
- path: expect.stringMatching(
550
- /^(\/|\\).*twentytwenty$/
551
- ),
552
- basename: 'twentytwenty',
553
- },
554
- ],
555
- };
556
- expect( config.env.development ).toMatchObject( matchObj );
557
- expect( config.env.tests ).toMatchObject( matchObj );
558
- } );
559
-
560
- it( 'should parse zip sources', async () => {
561
- readFile.mockImplementation( () =>
562
- Promise.resolve(
563
- JSON.stringify( {
564
- plugins: [
565
- 'https://www.example.com/test/path/to/gutenberg.zip',
566
- 'https://www.example.com/test/path/to/gutenberg.8.1.0.zip',
567
- 'https://www.example.com/test/path/to/gutenberg.8.1.0.zip?auth=thisIsAString&token=secondString',
568
- 'https://www.example.com/test/path/to/twentytwenty.zip',
569
- 'https://www.example.com/test/path/to/twentytwenty.1.3.zip',
570
- 'https://example.com/twentytwenty.1.3.zip',
571
- ],
572
- } )
573
- )
574
- );
575
- const config = await readConfig( '.wp-env.json' );
576
- const matchObj = {
577
- pluginSources: [
578
- {
579
- type: 'zip',
580
- url: 'https://www.example.com/test/path/to/gutenberg.zip',
581
- path: expect.stringMatching( /^(\/|\\).*gutenberg$/ ),
582
- basename: 'gutenberg',
583
- },
584
- {
585
- type: 'zip',
586
- url: 'https://www.example.com/test/path/to/gutenberg.8.1.0.zip',
587
- path: expect.stringMatching(
588
- /^(\/|\\).*gutenberg.8.1.0$/
589
- ),
590
- basename: 'gutenberg.8.1.0',
591
- },
592
- {
593
- type: 'zip',
594
- url: 'https://www.example.com/test/path/to/gutenberg.8.1.0.zip?auth=thisIsAString&token=secondString',
595
- path: expect.stringMatching(
596
- /^(\/|\\).*gutenberg.8.1.0$/
597
- ),
598
- basename: 'gutenberg.8.1.0',
599
- },
600
- {
601
- type: 'zip',
602
- url: 'https://www.example.com/test/path/to/twentytwenty.zip',
603
- path: expect.stringMatching(
604
- /^(\/|\\).*twentytwenty$/
605
- ),
606
- basename: 'twentytwenty',
607
- },
608
- {
609
- type: 'zip',
610
- url: 'https://www.example.com/test/path/to/twentytwenty.1.3.zip',
611
- path: expect.stringMatching(
612
- /^(\/|\\).*twentytwenty.1.3$/
613
- ),
614
- basename: 'twentytwenty.1.3',
615
- },
616
- {
617
- type: 'zip',
618
- url: 'https://example.com/twentytwenty.1.3.zip',
619
- path: expect.stringMatching(
620
- /^(\/|\\).*twentytwenty.1.3$/
621
- ),
622
- basename: 'twentytwenty.1.3',
623
- },
624
- ],
625
- };
626
- expect( config.env.development ).toMatchObject( matchObj );
627
- expect( config.env.tests ).toMatchObject( matchObj );
628
- } );
629
-
630
- it( 'should throw a validaton error if there is an unknown source', async () => {
631
- readFile.mockImplementation( () =>
632
- Promise.resolve( JSON.stringify( { plugins: [ 'invalid' ] } ) )
633
- );
634
- expect.assertions( 2 );
635
- try {
636
- await readConfig( '.wp-env.json' );
637
- } catch ( error ) {
638
- expect( error ).toBeInstanceOf( ValidationError );
639
- expect( error.message ).toContain(
640
- 'Invalid or unrecognized source'
641
- );
642
- }
643
- } );
644
- } );
645
-
646
- describe( 'mappings parsing', () => {
647
- it( 'should parse mappings into sources', async () => {
648
- readFile.mockImplementation( () =>
649
- Promise.resolve(
650
- JSON.stringify( {
651
- mappings: {
652
- test: './relative',
653
- test2: 'WordPress/gutenberg#trunk',
654
- },
655
- } )
656
- )
657
- );
658
- const config = await readConfig( '.wp-env.json' );
659
- const matchObj = {
660
- test: {
661
- type: 'local',
662
- path: expect.stringMatching( /^(\/|\\).*relative$/ ),
663
- basename: 'relative',
664
- },
665
- test2: {
666
- type: 'git',
667
- path: expect.stringMatching( /^(\/|\\).*gutenberg$/ ),
668
- basename: 'gutenberg',
669
- },
670
- };
671
- expect( config.env.development.mappings ).toMatchObject( matchObj );
672
- expect( config.env.development.mappings ).toMatchObject( matchObj );
673
- } );
674
-
675
- it( 'should throw a validaton error if there is an invalid mapping', async () => {
676
- readFile.mockImplementation( () =>
677
- Promise.resolve(
678
- JSON.stringify( { mappings: { test: 'false' } } )
679
- )
680
- );
681
- expect.assertions( 2 );
682
- try {
683
- await readConfig( '.wp-env.json' );
684
- } catch ( error ) {
685
- expect( error ).toBeInstanceOf( ValidationError );
686
- expect( error.message ).toContain(
687
- 'Invalid or unrecognized source'
688
- );
689
- }
690
- } );
691
-
692
- it( 'throws an error if a mapping is badly formatted', async () => {
693
- readFile.mockImplementation( () =>
694
- Promise.resolve(
695
- JSON.stringify( {
696
- mappings: { test: null },
697
- } )
698
- )
699
- );
700
- expect.assertions( 2 );
701
- try {
702
- await readConfig( '.wp-env.json' );
703
- } catch ( error ) {
704
- expect( error ).toBeInstanceOf( ValidationError );
705
- expect( error.message ).toContain(
706
- 'Invalid .wp-env.json: "mappings.test" should be a string.'
707
- );
708
- }
709
- } );
710
-
711
- it( 'throws an error if mappings is not an object', async () => {
712
- readFile.mockImplementation( () =>
713
- Promise.resolve(
714
- JSON.stringify( {
715
- mappings: 'not object',
716
- } )
717
- )
718
- );
719
- expect.assertions( 2 );
720
- try {
721
- await readConfig( '.wp-env.json' );
722
- } catch ( error ) {
723
- expect( error ).toBeInstanceOf( ValidationError );
724
- expect( error.message ).toContain(
725
- 'Invalid .wp-env.json: "mappings" must be an object.'
726
- );
727
- }
728
- } );
729
-
730
- it( 'should return an empty mappings object if none are passed', async () => {
731
- readFile.mockImplementation( () =>
732
- Promise.resolve( JSON.stringify( { mappings: {} } ) )
733
- );
734
- const config = await readConfig( '.wp-env.json' );
735
- expect( config.env.development.mappings ).toEqual( {} );
736
- expect( config.env.tests.mappings ).toEqual( {} );
737
- } );
738
-
739
- it( 'should merge mappings from different environments', async () => {
740
- readFile.mockImplementation( () =>
741
- Promise.resolve(
742
- JSON.stringify( {
743
- mappings: {
744
- test1: '/test1',
745
- },
746
- env: {
747
- tests: {
748
- mappings: {
749
- test2: '/test2',
750
- },
751
- },
752
- development: {
753
- mappings: {
754
- test3: '/test3',
755
- },
756
- },
757
- },
758
- } )
759
- )
760
- );
761
- const config = await readConfig( '.wp-env.json' );
762
- expect( config.env.development.mappings ).toEqual( {
763
- test1: {
764
- basename: 'test1',
765
- // resolve is required to remove drive letters on Windows.
766
- path: resolve( '/test1' ),
767
- type: 'local',
768
- },
769
- test3: {
770
- basename: 'test3',
771
- path: resolve( '/test3' ),
772
- type: 'local',
773
- },
774
- } );
775
- expect( config.env.tests.mappings ).toEqual( {
776
- test1: {
777
- basename: 'test1',
778
- path: resolve( '/test1' ),
779
- type: 'local',
780
- },
781
- test2: {
782
- basename: 'test2',
783
- path: resolve( '/test2' ),
784
- type: 'local',
785
- },
786
- } );
787
- } );
788
-
789
- it( 'should override less specific mappings with more specific mappings', async () => {
790
- readFile.mockImplementation( () =>
791
- Promise.resolve(
792
- JSON.stringify( {
793
- mappings: {
794
- test: '/test1',
795
- },
796
- env: {
797
- tests: {
798
- mappings: {
799
- test: '/test2',
800
- },
801
- },
802
- development: {
803
- mappings: {
804
- test: '/test3',
805
- },
806
- },
807
- },
808
- } )
809
- )
810
- );
811
- const config = await readConfig( '.wp-env.json' );
812
- expect( config.env.development.mappings ).toEqual( {
813
- test: {
814
- basename: 'test3',
815
- path: resolve( '/test3' ),
816
- type: 'local',
817
- },
818
- } );
819
- expect( config.env.tests.mappings ).toEqual( {
820
- test: {
821
- basename: 'test2',
822
- path: resolve( '/test2' ),
823
- type: 'local',
824
- },
825
- } );
826
- } );
827
- } );
828
-
829
- describe( 'port number parsing', () => {
830
- it( 'should throw a validaton error if the ports are not numbers', async () => {
831
- expect.assertions( 10 );
832
- await testPortNumberValidation( 'port', 'string' );
833
- await testPortNumberValidation( 'testsPort', [], 'env.tests.' );
834
- await testPortNumberValidation( 'port', {} );
835
- await testPortNumberValidation( 'testsPort', false, 'env.tests.' );
836
- await testPortNumberValidation( 'port', null );
837
- } );
838
-
839
- it( 'should throw a validaton error if the ports are the same', async () => {
840
- expect.assertions( 2 );
841
- readFile.mockImplementation( () =>
842
- Promise.resolve(
843
- JSON.stringify( { port: 8888, testsPort: 8888 } )
844
- )
845
- );
846
- try {
847
- await readConfig( '.wp-env.json' );
848
- } catch ( error ) {
849
- expect( error ).toBeInstanceOf( ValidationError );
850
- expect( error.message ).toContain(
851
- 'Invalid .wp-env.json: Each port value must be unique.'
852
- );
853
- }
854
- } );
855
-
856
- it( 'should parse custom ports', async () => {
857
- readFile.mockImplementation( () =>
858
- Promise.resolve(
859
- JSON.stringify( {
860
- port: 1000,
861
- testsPort: 2000,
862
- } )
863
- )
864
- );
865
- const config = await readConfig( '.wp-env.json' );
866
- // Custom port is overridden while testsPort gets the deault value.
867
- expect( config ).toMatchObject( {
868
- env: {
869
- development: {
870
- port: 1000,
871
- },
872
- tests: {
873
- port: 2000,
874
- },
875
- },
876
- } );
877
- } );
878
-
879
- it( 'certain wp-config values should include the port number', async () => {
880
- readFile.mockImplementation( () =>
881
- Promise.resolve(
882
- JSON.stringify( {
883
- port: 1000,
884
- testsPort: 2000,
885
- } )
886
- )
887
- );
888
- const config = await readConfig( '.wp-env.json' );
889
- // Custom port is overridden while testsPort gets the deault value.
890
- expect( config ).toMatchObject( {
891
- env: {
892
- development: {
893
- port: 1000,
894
- config: {
895
- WP_TESTS_DOMAIN: 'localhost:1000',
896
- WP_SITEURL: 'http://localhost:1000',
897
- WP_HOME: 'http://localhost:1000',
898
- },
899
- },
900
- tests: {
901
- port: 2000,
902
- config: {
903
- WP_TESTS_DOMAIN: 'localhost:2000',
904
- WP_SITEURL: 'http://localhost:2000',
905
- WP_HOME: 'http://localhost:2000',
906
- },
907
- },
908
- },
909
- } );
910
- } );
911
-
912
- it( 'should not overwrite port number for WP_HOME if set', async () => {
913
- readFile.mockImplementation( () =>
914
- Promise.resolve(
915
- JSON.stringify( {
916
- port: 1000,
917
- testsPort: 2000,
918
- config: {
919
- WP_HOME: 'http://localhost:3000',
920
- },
921
- } )
922
- )
923
- );
924
- const config = await readConfig( '.wp-env.json' );
925
- // Custom port is overridden while testsPort gets the deault value.
926
- expect( config ).toMatchObject( {
927
- env: {
928
- development: {
929
- port: 1000,
930
- config: {
931
- WP_TESTS_DOMAIN: 'localhost:1000',
932
- WP_SITEURL: 'http://localhost:1000',
933
- WP_HOME: 'http://localhost:3000',
934
- },
935
- },
936
- tests: {
937
- port: 2000,
938
- config: {
939
- WP_TESTS_DOMAIN: 'localhost:2000',
940
- WP_SITEURL: 'http://localhost:2000',
941
- WP_HOME: 'http://localhost:3000',
942
- },
943
- },
944
- },
945
- } );
946
- } );
947
-
948
- it( 'should throw an error if the port number environment variable is invalid', async () => {
949
- readFile.mockImplementation( () =>
950
- Promise.resolve( JSON.stringify( {} ) )
951
- );
952
- const oldPort = process.env.WP_ENV_PORT;
953
- process.env.WP_ENV_PORT = 'hello';
954
- try {
955
- await readConfig( '.wp-env.json' );
956
- } catch ( error ) {
957
- expect( error ).toBeInstanceOf( ValidationError );
958
- expect( error.message ).toContain(
959
- 'Invalid environment variable: WP_ENV_PORT must be a number.'
960
- );
961
- }
962
- process.env.WP_ENV_PORT = oldPort;
963
- } );
964
-
965
- it( 'should throw an error if the tests port number environment variable is invalid', async () => {
966
- readFile.mockImplementation( () =>
967
- Promise.resolve( JSON.stringify( {} ) )
968
- );
969
- const oldPort = process.env.WP_ENV_TESTS_PORT;
970
- process.env.WP_ENV_TESTS_PORT = 'hello';
971
- try {
972
- await readConfig( '.wp-env.json' );
973
- } catch ( error ) {
974
- expect( error ).toBeInstanceOf( ValidationError );
975
- expect( error.message ).toContain(
976
- 'Invalid environment variable: WP_ENV_TESTS_PORT must be a number.'
977
- );
978
- }
979
- process.env.WP_ENV_TESTS_PORT = oldPort;
980
- } );
981
-
982
- it( 'should use port environment values rather than config values if both are defined', async () => {
983
- readFile.mockImplementation( () =>
984
- Promise.resolve(
985
- JSON.stringify( {
986
- port: 1000,
987
- testsPort: 2000,
988
- } )
989
- )
990
- );
991
- const oldPort = process.env.WP_ENV_PORT;
992
- const oldTestsPort = process.env.WP_ENV_TESTS_PORT;
993
- process.env.WP_ENV_PORT = 4000;
994
- process.env.WP_ENV_TESTS_PORT = 3000;
995
-
996
- const config = await readConfig( '.wp-env.json' );
997
- expect( config ).toMatchObject( {
998
- env: {
999
- development: {
1000
- port: 4000,
1001
- },
1002
- tests: {
1003
- port: 3000,
1004
- },
1005
- },
1006
- } );
1007
-
1008
- process.env.WP_ENV_PORT = oldPort;
1009
- process.env.WP_ENV_TESTS_PORT = oldTestsPort;
1010
- } );
1011
-
1012
- it( 'should use 8888 and 8889 as the default port and testsPort values if nothing else is specified', async () => {
1013
- readFile.mockImplementation( () =>
1014
- Promise.resolve( JSON.stringify( {} ) )
1015
- );
1016
-
1017
- const config = await readConfig( '.wp-env.json' );
1018
- expect( config ).toMatchObject( {
1019
- env: {
1020
- development: {
1021
- port: 8888,
1022
- },
1023
- tests: {
1024
- port: 8889,
1025
- },
1026
- },
1027
- } );
1028
- } );
1029
- } );
1030
-
1031
- describe( 'wp config values', () => {
1032
- it( 'should use default config values', async () => {
1033
- const config = await readConfig( '.wp-env.json' );
1034
-
1035
- expect( config.env.tests.config ).toMatchSnapshot();
1036
- expect( config.env.development.config ).toMatchSnapshot();
1037
- } );
1038
-
1039
- it( 'should override default config values when some are specified', async () => {
1040
- const testValue = 'new value';
1041
- readFile.mockImplementation( () =>
1042
- Promise.resolve(
1043
- JSON.stringify( {
1044
- config: {
1045
- SCRIPT_DEBUG: testValue,
1046
- },
1047
- } )
1048
- )
1049
- );
1050
-
1051
- const config = await readConfig( '.wp-env.json' );
1052
- expect( config.env.tests.config.SCRIPT_DEBUG ).toBe( testValue );
1053
- expect( config.env.development.config.SCRIPT_DEBUG ).toBe(
1054
- testValue
1055
- );
1056
- } );
1057
-
1058
- it( 'should override config values using the .override file first', async () => {
1059
- readFile.mockImplementation( ( filename ) => {
1060
- let result;
1061
- if ( filename === '.wp-env.json' ) {
1062
- result = {
1063
- config: {
1064
- SCRIPT_DEBUG: '1',
1065
- },
1066
- };
1067
- } else if ( filename === '.wp-env.override.json' ) {
1068
- result = {
1069
- config: {
1070
- SCRIPT_DEBUG: '2',
1071
- },
1072
- };
1073
- }
1074
- return Promise.resolve( JSON.stringify( result ) );
1075
- } );
1076
-
1077
- const config = await readConfig( '.wp-env.json' );
1078
- expect( config.env.tests.config.SCRIPT_DEBUG ).toBe( '2' );
1079
- expect( config.env.development.config.SCRIPT_DEBUG ).toBe( '2' );
1080
- } );
1081
-
1082
- it( 'should override config values using the environment option from the .override file after the general option', async () => {
1083
- readFile.mockImplementation( ( filename ) => {
1084
- let result;
1085
- if ( filename === '.wp-env.json' ) {
1086
- result = {
1087
- config: {
1088
- SCRIPT_DEBUG: '1',
1089
- },
1090
- env: {
1091
- tests: {
1092
- config: {
1093
- SCRIPT_DEBUG: '0',
1094
- },
1095
- },
1096
- development: {
1097
- config: {
1098
- SCRIPT_DEBUG: '0',
1099
- },
1100
- },
1101
- },
1102
- };
1103
- } else if ( filename === '.wp-env.override.json' ) {
1104
- result = {
1105
- config: {
1106
- SCRIPT_DEBUG: '2',
1107
- },
1108
- env: {
1109
- tests: {
1110
- config: {
1111
- SCRIPT_DEBUG: '3',
1112
- },
1113
- },
1114
- development: {
1115
- config: {
1116
- SCRIPT_DEBUG: '4',
1117
- },
1118
- },
1119
- },
1120
- };
1121
- }
1122
- return Promise.resolve( JSON.stringify( result ) );
1123
- } );
1124
-
1125
- const config = await readConfig( '.wp-env.json' );
1126
- expect( config.env.tests.config.SCRIPT_DEBUG ).toBe( '3' );
1127
- expect( config.env.development.config.SCRIPT_DEBUG ).toBe( '4' );
1128
- } );
1129
-
1130
- it( 'should override config values using the environment option after the general option', async () => {
1131
- readFile.mockImplementation( () =>
1132
- Promise.resolve(
1133
- JSON.stringify( {
1134
- config: {
1135
- SCRIPT_DEBUG: '1',
1136
- },
1137
- env: {
1138
- tests: {
1139
- config: {
1140
- SCRIPT_DEBUG: '2',
1141
- },
1142
- },
1143
- development: {
1144
- config: {
1145
- SCRIPT_DEBUG: '4',
1146
- },
1147
- },
1148
- },
1149
- } )
1150
- )
1151
- );
1152
-
1153
- const config = await readConfig( '.wp-env.json' );
1154
- expect( config.env.tests.config.SCRIPT_DEBUG ).toBe( '2' );
1155
- expect( config.env.development.config.SCRIPT_DEBUG ).toBe( '4' );
1156
- } );
1157
-
1158
- it( 'should merge config values without overwriting an entire section', async () => {
1159
- readFile.mockImplementation( () =>
1160
- Promise.resolve(
1161
- JSON.stringify( {
1162
- config: {
1163
- SCRIPT_DEBUG: '1',
1164
- TEST: '2',
1165
- },
1166
- env: {
1167
- tests: {
1168
- config: {
1169
- SCRIPT_DEBUG: '2',
1170
- TEST3: 'foo',
1171
- },
1172
- },
1173
- development: {
1174
- config: {
1175
- SCRIPT_DEBUG: '4',
1176
- TEST5: 5,
1177
- },
1178
- },
1179
- },
1180
- } )
1181
- )
1182
- );
1183
-
1184
- const config = await readConfig( '.wp-env.json' );
1185
- expect( config.env.tests.config ).toEqual( {
1186
- SCRIPT_DEBUG: '2',
1187
- TEST3: 'foo',
1188
- TEST: '2',
1189
- WP_DEBUG: false,
1190
- WP_ENVIRONMENT_TYPE: 'local',
1191
- WP_PHP_BINARY: 'php',
1192
- WP_TESTS_EMAIL: 'admin@example.org',
1193
- WP_TESTS_TITLE: 'Test Blog',
1194
- WP_TESTS_DOMAIN: 'localhost:8889',
1195
- WP_SITEURL: 'http://localhost:8889',
1196
- WP_HOME: 'http://localhost:8889',
1197
- } );
1198
-
1199
- expect( config.env.development.config ).toEqual( {
1200
- SCRIPT_DEBUG: '4',
1201
- TEST5: 5,
1202
- TEST: '2',
1203
- WP_DEBUG: true,
1204
- WP_ENVIRONMENT_TYPE: 'local',
1205
- WP_PHP_BINARY: 'php',
1206
- WP_TESTS_EMAIL: 'admin@example.org',
1207
- WP_TESTS_TITLE: 'Test Blog',
1208
- WP_TESTS_DOMAIN: 'localhost:8888',
1209
- WP_SITEURL: 'http://localhost:8888',
1210
- WP_HOME: 'http://localhost:8888',
1211
- } );
1212
- } );
1213
- } );
1214
- } );
1215
-
1216
- /**
1217
- * Tests that readConfig will throw errors when invalid port numbers are passed.
1218
- *
1219
- * @param {string} portName The name of the port to test ('port' or 'testsPort')
1220
- * @param {any} value A value which should throw an error.
1221
- * @param {string} envText Env text which prefixes the error.
1222
- */
1223
- async function testPortNumberValidation( portName, value, envText = '' ) {
1224
- readFile.mockImplementation( () =>
1225
- Promise.resolve( JSON.stringify( { [ portName ]: value } ) )
1226
- );
1227
- try {
1228
- await readConfig( '.wp-env.json' );
1229
- } catch ( error ) {
1230
- // Useful for debugging:
1231
- if ( ! ( error instanceof ValidationError ) ) {
1232
- throw error;
1233
- }
1234
- expect( error ).toBeInstanceOf( ValidationError );
1235
- expect( error.message ).toContain(
1236
- `Invalid .wp-env.json: "${ envText }port" must be an integer.`
1237
- );
1238
- }
1239
- jest.clearAllMocks();
1240
- }
1241
- /* eslint-enable jest/no-conditional-expect */