@wordpress/env 5.16.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 +189 -120
  2. package/lib/build-docker-compose-config.js +67 -96
  3. package/lib/cache.js +1 -0
  4. package/lib/cli.js +23 -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 +79 -15
  11. package/lib/commands/start.js +32 -10
  12. package/lib/commands/stop.js +1 -0
  13. package/lib/config/add-or-replace-port.js +41 -0
  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 +81 -0
  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 +103 -40
  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 +5 -33
  51. package/package.json +2 -2
  52. package/lib/config/config.js +0 -357
  53. package/lib/config/test/__snapshots__/config.js.snap +0 -83
  54. package/lib/config/test/config.js +0 -1203
@@ -0,0 +1,295 @@
1
+ 'use strict';
2
+ /**
3
+ * Internal dependencies
4
+ */
5
+ const { ValidationError } = require( '..' );
6
+ const postProcessConfig = require( '../post-process-config' );
7
+
8
+ describe( 'postProcessConfig', () => {
9
+ afterEach( () => {
10
+ jest.clearAllMocks();
11
+ } );
12
+
13
+ it( 'should merge relevant root options into environment options', () => {
14
+ const processed = postProcessConfig( {
15
+ port: 123,
16
+ testsPort: 456,
17
+ coreSource: {
18
+ type: 'test',
19
+ },
20
+ config: {
21
+ TESTS_ROOT: 'root',
22
+ },
23
+ pluginSources: [
24
+ {
25
+ type: 'root-plugin',
26
+ },
27
+ ],
28
+ themeSources: [
29
+ {
30
+ type: 'root-theme',
31
+ },
32
+ ],
33
+ mappings: {
34
+ 'root-mapping': {
35
+ type: 'root-mapping',
36
+ },
37
+ },
38
+ env: {
39
+ development: {
40
+ coreSource: {
41
+ type: 'test',
42
+ },
43
+ config: {
44
+ TEST_ENV: 'development',
45
+ },
46
+ pluginSources: [
47
+ {
48
+ type: 'development-plugin',
49
+ },
50
+ ],
51
+ themeSources: [
52
+ {
53
+ type: 'development-theme',
54
+ },
55
+ ],
56
+ mappings: {
57
+ 'development-mapping': {
58
+ type: 'development-mapping',
59
+ },
60
+ },
61
+ },
62
+ tests: {
63
+ coreSource: {
64
+ type: 'test',
65
+ },
66
+ config: {
67
+ TEST_ENV: 'tests',
68
+ },
69
+ },
70
+ },
71
+ } );
72
+
73
+ expect( processed ).toEqual( {
74
+ port: 123,
75
+ testsPort: 456,
76
+ coreSource: {
77
+ type: 'test',
78
+ },
79
+ config: {
80
+ TESTS_ROOT: 'root',
81
+ },
82
+ pluginSources: [
83
+ {
84
+ type: 'root-plugin',
85
+ },
86
+ ],
87
+ themeSources: [
88
+ {
89
+ type: 'root-theme',
90
+ },
91
+ ],
92
+ mappings: {
93
+ 'root-mapping': {
94
+ type: 'root-mapping',
95
+ },
96
+ },
97
+ env: {
98
+ development: {
99
+ port: 123,
100
+ coreSource: {
101
+ type: 'test',
102
+ },
103
+ config: {
104
+ TESTS_ROOT: 'root',
105
+ TEST_ENV: 'development',
106
+ },
107
+ pluginSources: [
108
+ {
109
+ type: 'development-plugin',
110
+ },
111
+ ],
112
+ themeSources: [
113
+ {
114
+ type: 'development-theme',
115
+ },
116
+ ],
117
+ mappings: {
118
+ 'root-mapping': {
119
+ type: 'root-mapping',
120
+ },
121
+ 'development-mapping': {
122
+ type: 'development-mapping',
123
+ },
124
+ },
125
+ },
126
+ tests: {
127
+ port: 456,
128
+ coreSource: {
129
+ type: 'test',
130
+ },
131
+ config: {
132
+ TESTS_ROOT: 'root',
133
+ TEST_ENV: 'tests',
134
+ },
135
+ pluginSources: [
136
+ {
137
+ type: 'root-plugin',
138
+ },
139
+ ],
140
+ themeSources: [
141
+ {
142
+ type: 'root-theme',
143
+ },
144
+ ],
145
+ mappings: {
146
+ 'root-mapping': {
147
+ type: 'root-mapping',
148
+ },
149
+ },
150
+ },
151
+ },
152
+ } );
153
+ } );
154
+
155
+ it( 'should not merge some root options into environment options', () => {
156
+ const processed = postProcessConfig( {
157
+ port: 8888,
158
+ testsPort: 8889,
159
+ afterSetup: 'test',
160
+ env: {
161
+ development: {},
162
+ tests: {},
163
+ },
164
+ } );
165
+
166
+ expect( processed ).toEqual( {
167
+ port: 8888,
168
+ testsPort: 8889,
169
+ afterSetup: 'test',
170
+ env: {
171
+ development: {
172
+ port: 8888,
173
+ },
174
+ tests: {
175
+ port: 8889,
176
+ },
177
+ },
178
+ } );
179
+ } );
180
+
181
+ describe( 'appendPortToWPConfigs', () => {
182
+ it( 'should add port to certain environment config options', () => {
183
+ const processed = postProcessConfig( {
184
+ port: 123,
185
+ config: {
186
+ WP_TESTS_DOMAIN: 'localhost',
187
+ WP_SITEURL: 'localhost',
188
+ WP_HOME: 'localhost',
189
+ },
190
+ env: {
191
+ development: {
192
+ port: 123,
193
+ },
194
+ tests: {
195
+ port: 456,
196
+ },
197
+ },
198
+ } );
199
+
200
+ expect( processed ).toEqual( {
201
+ // Since the root-level config shouldn't apply to an environment,
202
+ // we shouldn't add the port to the config options for it.
203
+ port: 123,
204
+ config: {
205
+ WP_TESTS_DOMAIN: 'localhost',
206
+ WP_SITEURL: 'localhost',
207
+ WP_HOME: 'localhost',
208
+ },
209
+ env: {
210
+ development: {
211
+ port: 123,
212
+ config: {
213
+ WP_TESTS_DOMAIN: 'localhost:123',
214
+ WP_SITEURL: 'localhost:123',
215
+ WP_HOME: 'localhost:123',
216
+ },
217
+ },
218
+ tests: {
219
+ port: 456,
220
+ config: {
221
+ WP_TESTS_DOMAIN: 'localhost:456',
222
+ WP_SITEURL: 'localhost:456',
223
+ WP_HOME: 'localhost:456',
224
+ },
225
+ },
226
+ },
227
+ } );
228
+ } );
229
+
230
+ it( 'should not overwrite port in WP_HOME', () => {
231
+ const processed = postProcessConfig( {
232
+ env: {
233
+ development: {
234
+ port: 123,
235
+ config: {
236
+ WP_TESTS_DOMAIN: 'localhost:777',
237
+ WP_SITEURL: 'localhost:777',
238
+ WP_HOME: 'localhost:777',
239
+ },
240
+ },
241
+ tests: {
242
+ port: 456,
243
+ config: {
244
+ WP_TESTS_DOMAIN: 'localhost:777',
245
+ WP_SITEURL: 'localhost:777',
246
+ WP_HOME: 'localhost:777',
247
+ },
248
+ },
249
+ },
250
+ } );
251
+
252
+ expect( processed ).toEqual( {
253
+ env: {
254
+ development: {
255
+ port: 123,
256
+ config: {
257
+ WP_TESTS_DOMAIN: 'localhost:123',
258
+ WP_SITEURL: 'localhost:123',
259
+ WP_HOME: 'localhost:777',
260
+ },
261
+ },
262
+ tests: {
263
+ port: 456,
264
+ config: {
265
+ WP_TESTS_DOMAIN: 'localhost:456',
266
+ WP_SITEURL: 'localhost:456',
267
+ WP_HOME: 'localhost:777',
268
+ },
269
+ },
270
+ },
271
+ } );
272
+ } );
273
+ } );
274
+
275
+ describe( 'validatePortUniqueness', () => {
276
+ it( 'should fail when two environments have the same port', () => {
277
+ expect( () => {
278
+ postProcessConfig( {
279
+ env: {
280
+ development: {
281
+ port: 123,
282
+ },
283
+ tests: {
284
+ port: 123,
285
+ },
286
+ },
287
+ } );
288
+ } ).toThrow(
289
+ new ValidationError(
290
+ 'The "development" and "tests" environments may not have the same port.'
291
+ )
292
+ );
293
+ } );
294
+ } );
295
+ } );
@@ -1,3 +1,5 @@
1
+ 'use strict';
2
+ /* eslint-disable jest/no-conditional-expect */
1
3
  /**
2
4
  * External dependencies
3
5
  */
@@ -6,7 +8,8 @@ const { readFile } = require( 'fs' ).promises;
6
8
  /**
7
9
  * Internal dependencies
8
10
  */
9
- const readConfigFile = require( '../read-raw-config-file' );
11
+ const readRawConfigFile = require( '../read-raw-config-file' );
12
+ const { ValidationError } = require( '../validate-config' );
10
13
 
11
14
  jest.mock( 'fs', () => ( {
12
15
  promises: {
@@ -15,76 +18,29 @@ jest.mock( 'fs', () => ( {
15
18
  } ) );
16
19
 
17
20
  describe( 'readRawConfigFile', () => {
18
- beforeEach( () => {
21
+ afterEach( () => {
19
22
  jest.clearAllMocks();
20
23
  } );
21
24
 
22
25
  it( 'returns null if it cannot find a file', async () => {
23
- readFile.mockImplementation( () =>
24
- Promise.reject( { code: 'ENOENT' } )
25
- );
26
- const result = await readConfigFile( 'wp-env', '/.wp-env.json' );
26
+ readFile.mockRejectedValue( { code: 'ENOENT' } );
27
+
28
+ const result = await readRawConfigFile( '/.wp-env.json' );
27
29
  expect( result ).toBe( null );
28
30
  } );
29
31
 
30
- it( 'converts testPort into tests.port', async () => {
31
- readFile.mockImplementation( () =>
32
- Promise.resolve( JSON.stringify( { testsPort: 100 } ) )
33
- );
34
- const result = await readConfigFile( 'wp-env', '/.wp-env.json' );
35
- expect( result ).toEqual( {
36
- env: {
37
- tests: {
38
- port: 100,
39
- },
40
- },
41
- } );
42
- } );
32
+ it( 'rejects when read file fails', async () => {
33
+ readFile.mockRejectedValue( { message: 'Test' } );
43
34
 
44
- it( 'does not overwrite other test config values', async () => {
45
- readFile.mockImplementation( () =>
46
- Promise.resolve(
47
- JSON.stringify( {
48
- testsPort: 100,
49
- env: {
50
- tests: {
51
- something: 'test',
52
- },
53
- },
54
- } )
55
- )
56
- );
57
- const result = await readConfigFile( 'wp-env', '/.wp-env.json' );
58
- expect( result ).toEqual( {
59
- env: {
60
- tests: {
61
- port: 100,
62
- something: 'test',
63
- },
64
- },
65
- } );
66
- } );
35
+ expect.assertions( 1 );
67
36
 
68
- it( 'uses tests.port if both tests.port and testsPort exist', async () => {
69
- readFile.mockImplementation( () =>
70
- Promise.resolve(
71
- JSON.stringify( {
72
- testsPort: 100,
73
- env: {
74
- tests: {
75
- port: 200,
76
- },
77
- },
78
- } )
79
- )
80
- );
81
- const result = await readConfigFile( 'wp-env', '/.wp-env.json' );
82
- expect( result ).toEqual( {
83
- env: {
84
- tests: {
85
- port: 200,
86
- },
87
- },
88
- } );
37
+ try {
38
+ await readRawConfigFile( '/.wp-env.json' );
39
+ } catch ( error ) {
40
+ expect( error ).toEqual(
41
+ new ValidationError( 'Could not read .wp-env.json: Test' )
42
+ );
43
+ }
89
44
  } );
90
45
  } );
46
+ /* eslint-enable jest/no-conditional-expect */
@@ -0,0 +1,305 @@
1
+ 'use strict';
2
+ /**
3
+ * Internal dependencies
4
+ */
5
+ const {
6
+ ValidationError,
7
+ checkString,
8
+ checkPort,
9
+ checkStringArray,
10
+ checkObjectWithValues,
11
+ checkVersion,
12
+ checkValidURL,
13
+ } = require( '../validate-config' );
14
+
15
+ describe( 'validate-config', () => {
16
+ describe( 'checkString', () => {
17
+ it( 'throws when not a string', () => {
18
+ expect( () => checkString( 'test.json', 'test', 1234 ) ).toThrow(
19
+ new ValidationError(
20
+ 'Invalid test.json: "test" must be a string.'
21
+ )
22
+ );
23
+ } );
24
+
25
+ it( 'passes for string', () => {
26
+ expect( () =>
27
+ checkString( 'test.json', 'test', 'test' )
28
+ ).not.toThrow();
29
+ } );
30
+ } );
31
+
32
+ describe( 'checkPort', () => {
33
+ it( 'throws when not a number', () => {
34
+ expect( () => checkPort( 'test.json', 'test', 'test' ) ).toThrow(
35
+ new ValidationError(
36
+ 'Invalid test.json: "test" must be an integer.'
37
+ )
38
+ );
39
+
40
+ expect( () =>
41
+ checkPort( 'test.json', 'test', { test: 'test' } )
42
+ ).toThrow(
43
+ new ValidationError(
44
+ 'Invalid test.json: "test" must be an integer.'
45
+ )
46
+ );
47
+ } );
48
+
49
+ it( 'throws when port out of range', () => {
50
+ expect( () => checkPort( 'test.json', 'test', -1 ) ).toThrow(
51
+ new ValidationError(
52
+ 'Invalid test.json: "test" must be a valid port.'
53
+ )
54
+ );
55
+
56
+ expect( () => checkPort( 'test.json', 'test', 99999999 ) ).toThrow(
57
+ new ValidationError(
58
+ 'Invalid test.json: "test" must be a valid port.'
59
+ )
60
+ );
61
+ } );
62
+
63
+ it( 'passes for valid port', () => {
64
+ expect( () =>
65
+ checkPort( 'test.json', 'test', 8888 )
66
+ ).not.toThrow();
67
+ } );
68
+ } );
69
+
70
+ describe( 'checkStringArray', () => {
71
+ it( 'throws when not an array', () => {
72
+ expect( () =>
73
+ checkStringArray( 'test.json', 'test', 'test' )
74
+ ).toThrow(
75
+ new ValidationError(
76
+ 'Invalid test.json: "test" must be an array.'
77
+ )
78
+ );
79
+
80
+ expect( () =>
81
+ checkStringArray( 'test.json', 'test', { test: 'test' } )
82
+ ).toThrow(
83
+ new ValidationError(
84
+ 'Invalid test.json: "test" must be an array.'
85
+ )
86
+ );
87
+ } );
88
+
89
+ it( 'throws when array contains non-strings', () => {
90
+ expect( () =>
91
+ checkStringArray( 'test.json', 'test', [ 12 ] )
92
+ ).toThrow(
93
+ new ValidationError(
94
+ 'Invalid test.json: "test" must be an array of strings.'
95
+ )
96
+ );
97
+
98
+ expect( () =>
99
+ checkStringArray( 'test.json', 'test', [ 'test', 12 ] )
100
+ ).toThrow(
101
+ new ValidationError(
102
+ 'Invalid test.json: "test" must be an array of strings.'
103
+ )
104
+ );
105
+ } );
106
+
107
+ it( 'passes for string arrays', () => {
108
+ expect( () =>
109
+ checkStringArray( 'test.json', 'test', [] )
110
+ ).not.toThrow();
111
+ expect( () =>
112
+ checkStringArray( 'test.json', 'test', [ 'test' ] )
113
+ ).not.toThrow();
114
+ } );
115
+ } );
116
+
117
+ describe( 'checkObjectWithValues', () => {
118
+ it( 'throws when not an object', () => {
119
+ expect( () =>
120
+ checkObjectWithValues( 'test.json', 'test', 'test', [] )
121
+ ).toThrow(
122
+ new ValidationError(
123
+ 'Invalid test.json: "test" must be an object.'
124
+ )
125
+ );
126
+
127
+ expect( () =>
128
+ checkObjectWithValues( 'test.json', 'test', [ 'test' ], [] )
129
+ ).toThrow(
130
+ new ValidationError(
131
+ 'Invalid test.json: "test" must be an object.'
132
+ )
133
+ );
134
+ } );
135
+
136
+ it( 'throws when no allowed types are given', () => {
137
+ expect( () =>
138
+ checkObjectWithValues(
139
+ 'test.json',
140
+ 'test',
141
+ { test: 'test' },
142
+ []
143
+ )
144
+ ).toThrow(
145
+ new ValidationError(
146
+ 'Invalid test.json: "test.test" must be a .'
147
+ )
148
+ );
149
+ } );
150
+
151
+ it( 'throws when type is not allowed', () => {
152
+ expect( () =>
153
+ checkObjectWithValues( 'test.json', 'test', { test: 'test' }, [
154
+ 'number',
155
+ ] )
156
+ ).toThrow(
157
+ new ValidationError(
158
+ 'Invalid test.json: "test.test" must be a number.'
159
+ )
160
+ );
161
+
162
+ expect( () =>
163
+ checkObjectWithValues( 'test.json', 'test', { test: 1 }, [
164
+ 'string',
165
+ ] )
166
+ ).toThrow(
167
+ new ValidationError(
168
+ 'Invalid test.json: "test.test" must be a string.'
169
+ )
170
+ );
171
+
172
+ expect( () =>
173
+ checkObjectWithValues(
174
+ 'test.json',
175
+ 'test',
176
+ { test: [ 'test' ] },
177
+ [ 'object', 'string' ]
178
+ )
179
+ ).toThrow(
180
+ new ValidationError(
181
+ 'Invalid test.json: "test.test" must be a object or string.'
182
+ )
183
+ );
184
+ } );
185
+
186
+ it( 'passes when type is allowed', () => {
187
+ expect( () =>
188
+ checkObjectWithValues( 'test.json', 'test', { test: 'test' }, [
189
+ 'string',
190
+ ] )
191
+ ).not.toThrow();
192
+ expect( () =>
193
+ checkObjectWithValues( 'test.json', 'test', { test: 1 }, [
194
+ 'number',
195
+ ] )
196
+ ).not.toThrow();
197
+ expect( () =>
198
+ checkObjectWithValues(
199
+ 'test.json',
200
+ 'test',
201
+ { test: { nested: 'test' } },
202
+ [ 'object' ]
203
+ )
204
+ ).not.toThrow();
205
+ expect( () =>
206
+ checkObjectWithValues(
207
+ 'test.json',
208
+ 'test',
209
+ { test: [ 'test' ] },
210
+ [ 'array' ]
211
+ )
212
+ ).not.toThrow();
213
+ } );
214
+ } );
215
+
216
+ describe( 'checkVersion', () => {
217
+ it( 'throws for invalid input', () => {
218
+ expect( () => checkVersion( 'test.json', 'test', 'test' ) ).toThrow(
219
+ new ValidationError(
220
+ 'Invalid test.json: "test" must be a string of the format "X", "X.X", or "X.X.X".'
221
+ )
222
+ );
223
+
224
+ expect( () => checkVersion( 'test.json', 'test', 123 ) ).toThrow(
225
+ new ValidationError(
226
+ 'Invalid test.json: "test" must be a string.'
227
+ )
228
+ );
229
+ } );
230
+
231
+ it( 'passes for different version formats', () => {
232
+ expect( () =>
233
+ checkVersion( 'test.json', 'test', '1' )
234
+ ).not.toThrow();
235
+ expect( () =>
236
+ checkVersion( 'test.json', 'test', '1.1' )
237
+ ).not.toThrow();
238
+ expect( () =>
239
+ checkVersion( 'test.json', 'test', '1.1.1' )
240
+ ).not.toThrow();
241
+ expect( () =>
242
+ checkVersion( 'test.json', 'test', '15.7.2' )
243
+ ).not.toThrow();
244
+ expect( () =>
245
+ checkVersion( 'test.json', 'test', '26634543' )
246
+ ).not.toThrow();
247
+ } );
248
+ } );
249
+
250
+ describe( 'checkValidURL', () => {
251
+ it( 'throws for invaid URLs', () => {
252
+ expect( () =>
253
+ checkValidURL( 'test.json', 'test', 'localhost' )
254
+ ).toThrow(
255
+ new ValidationError(
256
+ 'Invalid test.json: "test" must be a valid URL.'
257
+ )
258
+ );
259
+
260
+ expect( () => checkValidURL( 'test.json', 'test', '' ) ).toThrow(
261
+ new ValidationError(
262
+ 'Invalid test.json: "test" must be a valid URL.'
263
+ )
264
+ );
265
+
266
+ expect( () => checkValidURL( 'test.json', 'test', 123 ) ).toThrow(
267
+ new ValidationError(
268
+ 'Invalid test.json: "test" must be a valid URL.'
269
+ )
270
+ );
271
+ } );
272
+
273
+ it( 'passes for valid URLs', () => {
274
+ expect( () =>
275
+ checkValidURL( 'test.json', 'test', 'http://test.com' )
276
+ ).not.toThrow();
277
+ expect( () =>
278
+ checkValidURL( 'test.json', 'test', 'https://test.com' )
279
+ ).not.toThrow();
280
+ expect( () =>
281
+ checkValidURL( 'test.json', 'test', 'http://test' )
282
+ ).not.toThrow();
283
+ expect( () =>
284
+ checkValidURL(
285
+ 'test.json',
286
+ 'test',
287
+ 'http://test/test?test=test'
288
+ )
289
+ ).not.toThrow();
290
+ expect( () =>
291
+ checkValidURL( 'test.json', 'test', 'http://test.co.uk' )
292
+ ).not.toThrow();
293
+ expect( () =>
294
+ checkValidURL( 'test.json', 'test', 'https://test.co.uk:8888' )
295
+ ).not.toThrow();
296
+ expect( () =>
297
+ checkValidURL(
298
+ 'test.json',
299
+ 'test',
300
+ 'http://test.co.uk:8888/test?test=test#test'
301
+ )
302
+ ).not.toThrow();
303
+ } );
304
+ } );
305
+ } );