@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
@@ -0,0 +1,149 @@
1
+ 'use strict';
2
+ /**
3
+ * Internal dependencies
4
+ */
5
+ const cli = require( '../cli' );
6
+ const env = require( '../env' );
7
+
8
+ /**
9
+ * Mocked dependencies
10
+ */
11
+ jest.spyOn( process, 'exit' ).mockImplementation( () => {} );
12
+ jest.mock( 'ora', () => () => ( {
13
+ start() {
14
+ return { text: '', succeed: jest.fn(), fail: jest.fn() };
15
+ },
16
+ } ) );
17
+ jest.mock( '../env', () => {
18
+ const actual = jest.requireActual( '../env' );
19
+ return {
20
+ start: jest.fn( Promise.resolve.bind( Promise ) ),
21
+ stop: jest.fn( Promise.resolve.bind( Promise ) ),
22
+ clean: jest.fn( Promise.resolve.bind( Promise ) ),
23
+ run: jest.fn( Promise.resolve.bind( Promise ) ),
24
+ ValidationError: actual.ValidationError,
25
+ AfterSetupError: actual.AfterSetupError,
26
+ };
27
+ } );
28
+
29
+ describe( 'env cli', () => {
30
+ beforeEach( jest.clearAllMocks );
31
+
32
+ it( 'parses start commands.', () => {
33
+ cli().parse( [ 'start' ] );
34
+ const { spinner } = env.start.mock.calls[ 0 ][ 0 ];
35
+ expect( spinner.text ).toBe( '' );
36
+ } );
37
+
38
+ it( 'parses stop commands.', () => {
39
+ cli().parse( [ 'stop' ] );
40
+ const { spinner } = env.stop.mock.calls[ 0 ][ 0 ];
41
+ expect( spinner.text ).toBe( '' );
42
+ } );
43
+
44
+ it( 'parses clean commands for the default environment.', () => {
45
+ cli().parse( [ 'clean' ] );
46
+ const { environment, spinner } = env.clean.mock.calls[ 0 ][ 0 ];
47
+ expect( environment ).toBe( 'tests' );
48
+ expect( spinner.text ).toBe( '' );
49
+ } );
50
+ it( 'parses clean commands for all environments.', () => {
51
+ cli().parse( [ 'clean', 'all' ] );
52
+ const { environment, spinner } = env.clean.mock.calls[ 0 ][ 0 ];
53
+ expect( environment ).toBe( 'all' );
54
+ expect( spinner.text ).toBe( '' );
55
+ } );
56
+ it( 'parses clean commands for the development environment.', () => {
57
+ cli().parse( [ 'clean', 'development' ] );
58
+ const { environment, spinner } = env.clean.mock.calls[ 0 ][ 0 ];
59
+ expect( environment ).toBe( 'development' );
60
+ expect( spinner.text ).toBe( '' );
61
+ } );
62
+ it( 'parses clean commands for the tests environment.', () => {
63
+ cli().parse( [ 'clean', 'tests' ] );
64
+ const { environment, spinner } = env.clean.mock.calls[ 0 ][ 0 ];
65
+ expect( environment ).toBe( 'tests' );
66
+ expect( spinner.text ).toBe( '' );
67
+ } );
68
+
69
+ it( 'parses run commands without arguments.', () => {
70
+ cli().parse( [ 'run', 'tests-wordpress', 'test' ] );
71
+ const { container, command, spinner } = env.run.mock.calls[ 0 ][ 0 ];
72
+ expect( container ).toBe( 'tests-wordpress' );
73
+ expect( command ).toStrictEqual( [ 'test' ] );
74
+ expect( spinner.text ).toBe( '' );
75
+ } );
76
+ it( 'parses run commands with variadic arguments.', () => {
77
+ cli().parse( [ 'run', 'tests-wordpress', 'test', 'test1', '--test2' ] );
78
+ const { container, command, spinner } = env.run.mock.calls[ 0 ][ 0 ];
79
+ expect( container ).toBe( 'tests-wordpress' );
80
+ expect( command ).toStrictEqual( [ 'test', 'test1', '--test2' ] );
81
+ expect( spinner.text ).toBe( '' );
82
+ } );
83
+
84
+ it( 'handles successful commands with messages.', async () => {
85
+ env.start.mockResolvedValueOnce( 'success message' );
86
+ cli().parse( [ 'start' ] );
87
+ const { spinner } = env.start.mock.calls[ 0 ][ 0 ];
88
+ await env.start.mock.results[ 0 ].value;
89
+ expect( spinner.succeed ).toHaveBeenCalledWith(
90
+ expect.stringMatching( /^success message \(in \d+s \d+ms\)$/ )
91
+ );
92
+ } );
93
+ it( 'handles successful commands with spinner text.', async () => {
94
+ env.start.mockResolvedValueOnce();
95
+ cli().parse( [ 'start' ] );
96
+ const { spinner } = env.start.mock.calls[ 0 ][ 0 ];
97
+ spinner.text = 'success spinner text';
98
+ await env.start.mock.results[ 0 ].value;
99
+ expect( spinner.succeed ).toHaveBeenCalledWith(
100
+ expect.stringMatching( /^success spinner text \(in \d+s \d+ms\)$/ )
101
+ );
102
+ } );
103
+
104
+ it( 'handles failed commands with messages.', async () => {
105
+ env.start.mockRejectedValueOnce( {
106
+ message: 'failure message',
107
+ } );
108
+ const consoleError = console.error;
109
+ console.error = jest.fn();
110
+ const processExit = process.exit;
111
+ process.exit = jest.fn();
112
+
113
+ cli().parse( [ 'start' ] );
114
+ const { spinner } = env.start.mock.calls[ 0 ][ 0 ];
115
+ await env.start.mock.results[ 0 ].value.catch( () => {} );
116
+
117
+ expect( spinner.fail ).toHaveBeenCalledWith( 'failure message' );
118
+ expect( console.error ).toHaveBeenCalled();
119
+ expect( process.exit ).toHaveBeenCalledWith( 1 );
120
+ console.error = consoleError;
121
+ process.exit = processExit;
122
+ } );
123
+ it( 'handles failed docker commands with errors.', async () => {
124
+ env.start.mockRejectedValueOnce( {
125
+ err: 'failure error',
126
+ out: 'message',
127
+ exitCode: 1,
128
+ } );
129
+ const consoleError = console.error;
130
+ console.error = jest.fn();
131
+ const processExit = process.exit;
132
+ process.exit = jest.fn();
133
+ const stderr = process.stderr.write;
134
+ process.stderr.write = jest.fn();
135
+
136
+ cli().parse( [ 'start' ] );
137
+ const { spinner } = env.start.mock.calls[ 0 ][ 0 ];
138
+ await env.start.mock.results[ 0 ].value.catch( () => {} );
139
+
140
+ expect( spinner.fail ).toHaveBeenCalledWith(
141
+ 'Error while running docker-compose command.'
142
+ );
143
+ expect( process.stderr.write ).toHaveBeenCalledWith( 'failure error' );
144
+ expect( process.exit ).toHaveBeenCalledWith( 1 );
145
+ console.error = consoleError;
146
+ process.exit = processExit;
147
+ process.stderr.write = stderr;
148
+ } );
149
+ } );
@@ -0,0 +1,66 @@
1
+ 'use strict';
2
+ /**
3
+ * External dependencies
4
+ */
5
+ const { execSync } = require( 'child_process' );
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ const {
11
+ AfterSetupError,
12
+ executeAfterSetup,
13
+ } = require( '../execute-after-setup' );
14
+
15
+ jest.mock( 'child_process', () => ( {
16
+ execSync: jest.fn(),
17
+ } ) );
18
+
19
+ describe( 'executeAfterSetup', () => {
20
+ const spinner = {
21
+ info: jest.fn(),
22
+ };
23
+
24
+ afterEach( () => {
25
+ jest.clearAllMocks();
26
+ } );
27
+
28
+ it( 'should do nothing without afterSetup option', () => {
29
+ executeAfterSetup( { afterSetup: null }, spinner );
30
+
31
+ expect( spinner.info ).not.toHaveBeenCalled();
32
+ } );
33
+
34
+ it( 'should run afterSetup option and print output without extra whitespace', () => {
35
+ execSync.mockReturnValue( 'Test \n' );
36
+
37
+ executeAfterSetup( { afterSetup: 'Test Setup' }, spinner );
38
+
39
+ expect( execSync ).toHaveBeenCalled();
40
+ expect( execSync.mock.calls[ 0 ][ 0 ] ).toEqual( 'Test Setup' );
41
+ expect( spinner.info ).toHaveBeenCalledWith( 'After Setup:\nTest' );
42
+ } );
43
+
44
+ it( 'should print nothing if afterSetup returns no output', () => {
45
+ execSync.mockReturnValue( '' );
46
+
47
+ executeAfterSetup( { afterSetup: 'Test Setup' }, spinner );
48
+
49
+ expect( execSync ).toHaveBeenCalled();
50
+ expect( execSync.mock.calls[ 0 ][ 0 ] ).toEqual( 'Test Setup' );
51
+ expect( spinner.info ).not.toHaveBeenCalled();
52
+ } );
53
+
54
+ it( 'should throw AfterSetupError when process errors', () => {
55
+ execSync.mockImplementation( ( command ) => {
56
+ expect( command ).toEqual( 'Test Setup' );
57
+ throw { stderr: 'Something bad happened.' };
58
+ } );
59
+
60
+ expect( () =>
61
+ executeAfterSetup( { afterSetup: 'Test Setup' }, spinner )
62
+ ).toThrow(
63
+ new AfterSetupError( 'After Setup:\nSomething bad happened.' )
64
+ );
65
+ } );
66
+ } );
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+ /**
3
+ * Internal dependencies
4
+ */
5
+ const md5 = require( '../md5' );
6
+
7
+ describe( 'md5', () => {
8
+ it( 'creates a hash of a string', () => {
9
+ const result = md5( 'hello' );
10
+ expect( result ).toMatchSnapshot();
11
+ } );
12
+ it( 'creates a hash of a object', () => {
13
+ const result = md5( { foo: 'xyz', test: { anotherFoo: 123 } } );
14
+ expect( result ).toMatchSnapshot();
15
+ } );
16
+ it( 'creates a hash of a number', () => {
17
+ const result = md5( 123789 );
18
+ expect( result ).toMatchSnapshot();
19
+ } );
20
+ it( 'creates a hash of null', () => {
21
+ const result = md5( null );
22
+ expect( result ).toMatchSnapshot();
23
+ } );
24
+ it( 'uses the same hash for the same values', () => {
25
+ const testObj = {
26
+ test: 1,
27
+ otherTest: {
28
+ foo: 'hello',
29
+ },
30
+ };
31
+ const result1 = md5( testObj );
32
+ const result2 = md5( { ...testObj } );
33
+ expect( result1 ).toBe( result2 );
34
+ } );
35
+ } );
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+ /**
3
+ * Internal dependencies
4
+ */
5
+ const parseXdebugMode = require( '../parse-xdebug-mode' );
6
+
7
+ describe( 'parseXdebugMode', () => {
8
+ it( 'throws an error if the passed value is neither a string nor undefined', () => {
9
+ const errorMessage = 'is not a mode recognized by Xdebug';
10
+ expect( () => parseXdebugMode( true ) ).toThrow( errorMessage );
11
+ expect( () => parseXdebugMode( false ) ).toThrow( errorMessage );
12
+ expect( () => parseXdebugMode( 1 ) ).toThrow( errorMessage );
13
+ } );
14
+
15
+ it( 'sets the Xdebug mode to "off" if no --xdebug flag is passed', () => {
16
+ const result = parseXdebugMode( undefined );
17
+ expect( result ).toEqual( 'off' );
18
+ } );
19
+
20
+ it( 'sets the Xdebug mode to "debug" if no mode is specified', () => {
21
+ const result = parseXdebugMode( '' );
22
+ expect( result ).toEqual( 'debug' );
23
+ } );
24
+
25
+ it( 'throws an error if a given mode is not recognized, including the invalid mode in the output', () => {
26
+ const fakeMode = 'fake-mode-123';
27
+ expect.assertions( 2 );
28
+ // Single mode:
29
+ expect( () => parseXdebugMode( fakeMode ) ).toThrow( fakeMode );
30
+
31
+ // Many modes:
32
+ expect( () =>
33
+ parseXdebugMode( `debug,profile,${ fakeMode }` )
34
+ ).toThrow( fakeMode );
35
+ } );
36
+
37
+ it( 'returns all modes passed', () => {
38
+ const result = parseXdebugMode( 'debug,profile,trace' );
39
+ expect( result ).toEqual( 'debug,profile,trace' );
40
+ } );
41
+ } );
package/lib/wordpress.js CHANGED
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
  /**
2
3
  * External dependencies
3
4
  */
@@ -14,7 +15,7 @@ const copyDir = util.promisify( require( 'copy-dir' ) );
14
15
 
15
16
  /**
16
17
  * @typedef {import('./config').WPConfig} WPConfig
17
- * @typedef {import('./config').WPServiceConfig} WPServiceConfig
18
+ * @typedef {import('./config').WPEnvironmentConfig} WPEnvironmentConfig
18
19
  * @typedef {import('./config').WPSource} WPSource
19
20
  * @typedef {'development'|'tests'} WPEnvironment
20
21
  * @typedef {'development'|'tests'|'all'} WPEnvironmentSelection
@@ -86,6 +87,7 @@ async function configureWordPress( environment, config, spinner ) {
86
87
  [ 'bash', '-c', setupCommands.join( ' && ' ) ],
87
88
  {
88
89
  config: config.dockerComposeConfigPath,
90
+ commandOptions: [ '--rm' ],
89
91
  log: config.debug,
90
92
  }
91
93
  );
@@ -147,30 +149,13 @@ async function setupWordPressDirectories( config ) {
147
149
  config.env.development.coreSource.path,
148
150
  config.env.development.coreSource.testsPath
149
151
  );
150
- await createUploadsDir( config.env.development.coreSource.testsPath );
151
152
  }
152
-
153
- const checkedPaths = {};
154
- for ( const { coreSource } of Object.values( config.env ) ) {
155
- if ( coreSource && ! checkedPaths[ coreSource.path ] ) {
156
- await createUploadsDir( coreSource.path );
157
- checkedPaths[ coreSource.path ] = true;
158
- }
159
- }
160
- }
161
-
162
- async function createUploadsDir( corePath ) {
163
- // Ensure the tests uploads folder is writeable for travis,
164
- // creating the folder if necessary.
165
- const uploadPath = path.join( corePath, 'wp-content/uploads' );
166
- await fs.mkdir( uploadPath, { recursive: true } );
167
- await fs.chmod( uploadPath, 0o0767 );
168
153
  }
169
154
 
170
155
  /**
171
156
  * Returns true if all given environment configs have the same core source.
172
157
  *
173
- * @param {WPServiceConfig[]} envs An array of environments to check.
158
+ * @param {WPEnvironmentConfig[]} envs An array of environments to check.
174
159
  *
175
160
  * @return {boolean} True if all the environments have the same core source.
176
161
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/env",
3
- "version": "6.0.0",
3
+ "version": "7.0.0",
4
4
  "description": "A zero-config, self contained local WordPress environment for development and testing.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -51,5 +51,5 @@
51
51
  "scripts": {
52
52
  "test": "echo \"Error: run tests from root\" && exit 1"
53
53
  },
54
- "gitHead": "6df0c62d43b8901414ccd22ffbe56eaa99d012a6"
54
+ "gitHead": "e936127e1e13881f1a940b7bd1593a9e500147f3"
55
55
  }