diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index bb68681c18f0f..602530c541d4f 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -2,8 +2,10 @@ ## Unreleased -### Breaking Changes +### Breaking Change +- Use test environment's `WP_SITEURL` instead of `WP_TESTS_DOMAIN` as the WordPress URL. +- Automatically add the environment's port to `WP_TESTS_DOMAIN`. - `run` command now has a `--env-cwd` option to set the working directory in the container for the command to execute from. ## 5.16.0 (2023-04-12) diff --git a/packages/env/lib/commands/start.js b/packages/env/lib/commands/start.js index 7ffc333d23bda..a217d5492d2b4 100644 --- a/packages/env/lib/commands/start.js +++ b/packages/env/lib/commands/start.js @@ -193,7 +193,7 @@ module.exports = async function start( { spinner, debug, update, xdebug } ) { } const siteUrl = config.env.development.config.WP_SITEURL; - const e2eSiteUrl = `http://${ config.env.tests.config.WP_TESTS_DOMAIN }:${ config.env.tests.port }/`; + const testsSiteUrl = config.env.tests.config.WP_SITEURL; const { out: mySQLAddress } = await dockerCompose.port( 'mysql', @@ -213,7 +213,7 @@ module.exports = async function start( { spinner, debug, update, xdebug } ) { .concat( siteUrl ? ` at ${ siteUrl }` : '.' ) .concat( '\n' ) .concat( 'WordPress test site started' ) - .concat( e2eSiteUrl ? ` at ${ e2eSiteUrl }` : '.' ) + .concat( testsSiteUrl ? ` at ${ testsSiteUrl }` : '.' ) .concat( '\n' ) .concat( `MySQL is listening on port ${ mySQLPort }` ) .concat( diff --git a/packages/env/lib/config/add-or-replace-port.js b/packages/env/lib/config/add-or-replace-port.js new file mode 100644 index 0000000000000..8d891869238a3 --- /dev/null +++ b/packages/env/lib/config/add-or-replace-port.js @@ -0,0 +1,32 @@ +/** + * Internal dependencies + */ +const { ValidationError } = require( './validate-config' ); + +/** + * Adds or replaces the port to the given domain or URI. + * + * @param {string} input The domain or URI to operate on. + * @param {string} port The port to append. + * @param {boolean} [replace] Indicates whether or not the port should be replaced if one is already present. Defaults to true. + * @return {string} The string with the port added or replaced. + */ +module.exports = function addOrReplacePort( input, port, replace = true ) { + // This matches both domains and URIs with an optional port and anything + // that remains after. We can use this to build an output string that + // adds or replaces the port without making any other changes to the input. + const matches = input.match( + /^((?:.+:\/\/)?[a-z0-9.\-]+)(?::([0-9]+))?(.*)$/i + ); + if ( ! matches ) { + throw new ValidationError( `Invalid domain or uri: ${ input }.` ); + } + + // When a port is already present we will do nothing if the caller doesn't want it to be replaced. + if ( matches[ 2 ] !== undefined && ! replace ) { + return input; + } + + // Place the port in the correct location in the input. + return matches[ 1 ] + ':' + port + matches[ 3 ]; +}; diff --git a/packages/env/lib/config/config.js b/packages/env/lib/config/config.js index afb845ed0b16e..4018830f853a0 100644 --- a/packages/env/lib/config/config.js +++ b/packages/env/lib/config/config.js @@ -14,6 +14,7 @@ const { validateConfig, ValidationError } = require( './validate-config' ); const readRawConfigFile = require( './read-raw-config-file' ); const parseConfig = require( './parse-config' ); const { includeTestsPath, parseSourceString } = parseConfig; +const addOrReplacePort = require( './add-or-replace-port' ); const md5 = require( '../md5' ); /** @@ -272,30 +273,25 @@ function withOverrides( config ) { config.env.tests.phpVersion = process.env.WP_ENV_PHP_VERSION || config.env.tests.phpVersion; - const updateEnvUrl = ( configKey ) => { - [ 'development', 'tests' ].forEach( ( envKey ) => { - try { - const baseUrl = new URL( - config.env[ envKey ].config[ configKey ] - ); - - // Don't overwrite the port of WP_HOME when set. - if ( ! ( configKey === 'WP_HOME' && !! baseUrl.port ) ) { - baseUrl.port = config.env[ envKey ].port; - } + // Some of our configuration options need to have the port added to them. + const addConfigPort = ( configKey ) => { + // Don't replace the port if one is set in WP_HOME. + const replace = configKey !== 'WP_HOME'; - config.env[ envKey ].config[ configKey ] = baseUrl.toString(); - } catch ( error ) { - throw new ValidationError( - `Invalid .wp-env.json: config.${ configKey } must be a valid URL.` - ); - } - } ); + config.env.development.config[ configKey ] = addOrReplacePort( + config.env.development.config[ configKey ], + config.env.development.port, + replace + ); + config.env.tests.config[ configKey ] = addOrReplacePort( + config.env.tests.config[ configKey ], + config.env.tests.port, + replace + ); }; - - // Update wp config options to include the correct port number in the URL. - updateEnvUrl( 'WP_SITEURL' ); - updateEnvUrl( 'WP_HOME' ); + addConfigPort( 'WP_TESTS_DOMAIN' ); + addConfigPort( 'WP_SITEURL' ); + addConfigPort( 'WP_HOME' ); return config; } diff --git a/packages/env/lib/config/test/__snapshots__/config.js.snap b/packages/env/lib/config/test/__snapshots__/config.js.snap index aeb08118ef6e4..c7bcdbd94d7ff 100644 --- a/packages/env/lib/config/test/__snapshots__/config.js.snap +++ b/packages/env/lib/config/test/__snapshots__/config.js.snap @@ -14,10 +14,10 @@ exports[`readConfig config file should match snapshot 1`] = ` "TEST_VAL3": false, "WP_DEBUG": true, "WP_ENVIRONMENT_TYPE": "local", - "WP_HOME": "http://localhost:2000/", + "WP_HOME": "http://localhost:2000", "WP_PHP_BINARY": "php", - "WP_SITEURL": "http://localhost:2000/", - "WP_TESTS_DOMAIN": "localhost", + "WP_SITEURL": "http://localhost:2000", + "WP_TESTS_DOMAIN": "localhost:2000", "WP_TESTS_EMAIL": "admin@example.org", "WP_TESTS_TITLE": "Test Blog", }, @@ -36,10 +36,10 @@ exports[`readConfig config file should match snapshot 1`] = ` "TEST_VAL3": false, "WP_DEBUG": false, "WP_ENVIRONMENT_TYPE": "local", - "WP_HOME": "http://localhost:1000/", + "WP_HOME": "http://localhost:1000", "WP_PHP_BINARY": "php", - "WP_SITEURL": "http://localhost:1000/", - "WP_TESTS_DOMAIN": "localhost", + "WP_SITEURL": "http://localhost:1000", + "WP_TESTS_DOMAIN": "localhost:1000", "WP_TESTS_EMAIL": "admin@example.org", "WP_TESTS_TITLE": "Test Blog", }, @@ -59,10 +59,10 @@ exports[`readConfig wp config values should use default config values 1`] = ` "SCRIPT_DEBUG": false, "WP_DEBUG": false, "WP_ENVIRONMENT_TYPE": "local", - "WP_HOME": "http://localhost:8889/", + "WP_HOME": "http://localhost:8889", "WP_PHP_BINARY": "php", - "WP_SITEURL": "http://localhost:8889/", - "WP_TESTS_DOMAIN": "localhost", + "WP_SITEURL": "http://localhost:8889", + "WP_TESTS_DOMAIN": "localhost:8889", "WP_TESTS_EMAIL": "admin@example.org", "WP_TESTS_TITLE": "Test Blog", } @@ -73,10 +73,10 @@ exports[`readConfig wp config values should use default config values 2`] = ` "SCRIPT_DEBUG": true, "WP_DEBUG": true, "WP_ENVIRONMENT_TYPE": "local", - "WP_HOME": "http://localhost:8888/", + "WP_HOME": "http://localhost:8888", "WP_PHP_BINARY": "php", - "WP_SITEURL": "http://localhost:8888/", - "WP_TESTS_DOMAIN": "localhost", + "WP_SITEURL": "http://localhost:8888", + "WP_TESTS_DOMAIN": "localhost:8888", "WP_TESTS_EMAIL": "admin@example.org", "WP_TESTS_TITLE": "Test Blog", } diff --git a/packages/env/lib/config/test/add-or-replace-port.js b/packages/env/lib/config/test/add-or-replace-port.js new file mode 100644 index 0000000000000..f5186e1db9373 --- /dev/null +++ b/packages/env/lib/config/test/add-or-replace-port.js @@ -0,0 +1,53 @@ +/** + * Internal dependencies + */ +const addOrReplacePort = require( '../add-or-replace-port.js' ); + +describe( 'addOrReplacePort', () => { + beforeEach( () => { + jest.clearAllMocks(); + } ); + + it( 'should add or replace port with various inputs', () => { + const testMap = [ + // Addition + { in: 'test', expect: 'test:101' }, + { in: 'test/test?test#test', expect: 'test:101/test?test#test' }, + { in: 'http://test.com', expect: 'http://test.com:101' }, + { + in: 'http://test.com/test?test#test', + expect: 'http://test.com:101/test?test#test', + }, + { in: 'ssh://test.com', expect: 'ssh://test.com:101' }, + { in: 'test.com', expect: 'test.com:101' }, + + // Replacement + { in: 'test:99', expect: 'test:101' }, + { in: 'test:99/test?test#test', expect: 'test:101/test?test#test' }, + { in: 'http://test.com:99', expect: 'http://test.com:101' }, + { + in: 'http://test.com:99/test?test#test', + expect: 'http://test.com:101/test?test#test', + }, + { in: 'ssh://test.com:99', expect: 'ssh://test.com:101' }, + { in: 'test.com:99', expect: 'test.com:101' }, + ]; + + for ( const test of testMap ) { + const result = addOrReplacePort( test.in, '101' ); + expect( result ).toEqual( test.expect ); + } + } ); + + it( 'should do nothing if port is present but replacement is not requested', () => { + const testMap = [ + { in: 'test', expect: 'test:103' }, + { in: 'test:99', expect: 'test:99' }, + ]; + + for ( const test of testMap ) { + const result = addOrReplacePort( test.in, '103', false ); + expect( result ).toEqual( test.expect ); + } + } ); +} ); diff --git a/packages/env/lib/config/test/config.js b/packages/env/lib/config/test/config.js index f94ecd4c09956..7552af2239a78 100644 --- a/packages/env/lib/config/test/config.js +++ b/packages/env/lib/config/test/config.js @@ -70,6 +70,44 @@ describe( 'readConfig', () => { } } ); + it( 'should throw a validation error if WP_SITEURL is not a valid URL', async () => { + readFile.mockImplementation( () => + Promise.resolve( + JSON.stringify( { + config: { + WP_SITEURL: 'test', + }, + } ) + ) + ); + expect.assertions( 2 ); + try { + await readConfig( '.wp-env.json' ); + } catch ( error ) { + expect( error ).toBeInstanceOf( ValidationError ); + expect( error.message ).toContain( 'must be a valid URL' ); + } + } ); + + it( 'should throw a validation error if WP_HOME is not a valid URL', async () => { + readFile.mockImplementation( () => + Promise.resolve( + JSON.stringify( { + config: { + WP_SITEURL: 'test', + }, + } ) + ) + ); + expect.assertions( 2 ); + try { + await readConfig( '.wp-env.json' ); + } catch ( error ) { + expect( error ).toBeInstanceOf( ValidationError ); + expect( error.message ).toContain( 'must be a valid URL' ); + } + } ); + it( 'should infer a core config when ran from a core directory', async () => { readFile.mockImplementation( () => Promise.reject( { code: 'ENOENT' } ) @@ -854,17 +892,17 @@ describe( 'readConfig', () => { development: { port: 1000, config: { - WP_TESTS_DOMAIN: 'localhost', - WP_SITEURL: 'http://localhost:1000/', - WP_HOME: 'http://localhost:1000/', + WP_TESTS_DOMAIN: 'localhost:1000', + WP_SITEURL: 'http://localhost:1000', + WP_HOME: 'http://localhost:1000', }, }, tests: { port: 2000, config: { - WP_TESTS_DOMAIN: 'localhost', - WP_SITEURL: 'http://localhost:2000/', - WP_HOME: 'http://localhost:2000/', + WP_TESTS_DOMAIN: 'localhost:2000', + WP_SITEURL: 'http://localhost:2000', + WP_HOME: 'http://localhost:2000', }, }, }, @@ -878,7 +916,7 @@ describe( 'readConfig', () => { port: 1000, testsPort: 2000, config: { - WP_HOME: 'http://localhost:3000/', + WP_HOME: 'http://localhost:3000', }, } ) ) @@ -890,17 +928,17 @@ describe( 'readConfig', () => { development: { port: 1000, config: { - WP_TESTS_DOMAIN: 'localhost', - WP_SITEURL: 'http://localhost:1000/', - WP_HOME: 'http://localhost:3000/', + WP_TESTS_DOMAIN: 'localhost:1000', + WP_SITEURL: 'http://localhost:1000', + WP_HOME: 'http://localhost:3000', }, }, tests: { port: 2000, config: { - WP_TESTS_DOMAIN: 'localhost', - WP_SITEURL: 'http://localhost:2000/', - WP_HOME: 'http://localhost:3000/', + WP_TESTS_DOMAIN: 'localhost:2000', + WP_SITEURL: 'http://localhost:2000', + WP_HOME: 'http://localhost:3000', }, }, }, @@ -1153,9 +1191,9 @@ describe( 'readConfig', () => { WP_PHP_BINARY: 'php', WP_TESTS_EMAIL: 'admin@example.org', WP_TESTS_TITLE: 'Test Blog', - WP_TESTS_DOMAIN: 'localhost', - WP_SITEURL: 'http://localhost:8889/', - WP_HOME: 'http://localhost:8889/', + WP_TESTS_DOMAIN: 'localhost:8889', + WP_SITEURL: 'http://localhost:8889', + WP_HOME: 'http://localhost:8889', } ); expect( config.env.development.config ).toEqual( { @@ -1167,9 +1205,9 @@ describe( 'readConfig', () => { WP_PHP_BINARY: 'php', WP_TESTS_EMAIL: 'admin@example.org', WP_TESTS_TITLE: 'Test Blog', - WP_TESTS_DOMAIN: 'localhost', - WP_SITEURL: 'http://localhost:8888/', - WP_HOME: 'http://localhost:8888/', + WP_TESTS_DOMAIN: 'localhost:8888', + WP_SITEURL: 'http://localhost:8888', + WP_HOME: 'http://localhost:8888', } ); } ); } ); diff --git a/packages/env/lib/config/validate-config.js b/packages/env/lib/config/validate-config.js index 1ecee11010f11..74a5fb79b5deb 100644 --- a/packages/env/lib/config/validate-config.js +++ b/packages/env/lib/config/validate-config.js @@ -83,9 +83,33 @@ function validateConfig( config, envLocation ) { ); } + checkValidURL( envPrefix, config.config, 'WP_SITEURL' ); + checkValidURL( envPrefix, config.config, 'WP_HOME' ); + return config; } +/** + * Validates the input and throws if it isn't a valid URL. + * + * @param {string} envPrefix The environment we're validating. + * @param {Object} config The configuration object we're looking at. + * @param {string} configKey The configuration key we're validating. + */ +function checkValidURL( envPrefix, config, configKey ) { + if ( config[ configKey ] === undefined ) { + return; + } + + try { + new URL( config[ configKey ] ); + } catch { + throw new ValidationError( + `Invalid .wp-env.json: "${ envPrefix }config.${ configKey }" must be a valid URL.` + ); + } +} + module.exports = { validateConfig, ValidationError, diff --git a/packages/env/lib/wordpress.js b/packages/env/lib/wordpress.js index 0d8f571cce71c..fc8f6db567077 100644 --- a/packages/env/lib/wordpress.js +++ b/packages/env/lib/wordpress.js @@ -44,20 +44,7 @@ async function checkDatabaseConnection( { dockerComposeConfigPath, debug } ) { * @param {Object} spinner A CLI spinner which indicates progress. */ async function configureWordPress( environment, config, spinner ) { - const url = ( () => { - const port = config.env[ environment ].port; - const domain = - environment === 'tests' - ? config.env.tests.config.WP_TESTS_DOMAIN - : config.env.development.config.WP_SITEURL; - if ( port === 80 ) { - return domain; - } - - return `${ domain }:${ port }`; - } )(); - - const installCommand = `wp core install --url="${ url }" --title="${ config.name }" --admin_user=admin --admin_password=password --admin_email=wordpress@example.com --skip-email`; + const installCommand = `wp core install --url="${ config.env[ environment ].config.WP_SITEURL }" --title="${ config.name }" --admin_user=admin --admin_password=password --admin_email=wordpress@example.com --skip-email`; // -eo pipefail exits the command as soon as anything fails in bash. const setupCommands = [ 'set -eo pipefail', installCommand ]; diff --git a/phpunit/blocks/render-block-file-test.php b/phpunit/blocks/render-block-file-test.php index 7ffbb41103875..7fdeb60a707a9 100644 --- a/phpunit/blocks/render-block-file-test.php +++ b/phpunit/blocks/render-block-file-test.php @@ -18,14 +18,14 @@ class Tests_Blocks_Render_File extends WP_UnitTestCase { */ public function test_render_block_core_file() { $attributes = array( - 'href' => 'http://localhost:8889/wp-content/uploads/2021/04/yolo.pdf', + 'href' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/2021/04/yolo.pdf', 'fileId' => 'wp-block-file--media-_clientId_0', - 'textLinkHref' => 'http://localhost:8889/wp-content/uploads/2021/04/yolo.pdf', + 'textLinkHref' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/2021/04/yolo.pdf', 'showDownloadButton' => true, 'displayPreview' => true, 'previewHeight' => 370, ); - $content = '
'; + $content = ''; $new_content = gutenberg_render_block_core_file( $attributes, $content ); $this->assertStringContainsString( 'aria-label="Embed of yolo."', $new_content ); @@ -36,14 +36,14 @@ public function test_render_block_core_file() { */ public function test_render_block_core_file_custom_filename() { $attributes = array( - 'href' => 'http://localhost:8889/wp-content/uploads/2021/04/yolo.pdf', + 'href' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/2021/04/yolo.pdf', 'fileId' => 'wp-block-file--media-_clientId_0', - 'textLinkHref' => 'http://localhost:8889/wp-content/uploads/2021/04/yolo.pdf', + 'textLinkHref' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/2021/04/yolo.pdf', 'showDownloadButton' => true, 'displayPreview' => true, 'previewHeight' => 370, ); - $content = ''; + $content = ''; $new_content = gutenberg_render_block_core_file( $attributes, $content ); $this->assertStringContainsString( 'aria-label="Embed of custom filename."', $new_content ); @@ -54,14 +54,14 @@ public function test_render_block_core_file_custom_filename() { */ public function test_render_block_core_file_empty_filename() { $attributes = array( - 'href' => 'http://localhost:8889/wp-content/uploads/2021/04/yolo.pdf', + 'href' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/2021/04/yolo.pdf', 'fileId' => 'wp-block-file--media-_clientId_0', - 'textLinkHref' => 'http://localhost:8889/wp-content/uploads/2021/04/yolo.pdf', + 'textLinkHref' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/2021/04/yolo.pdf', 'showDownloadButton' => true, 'displayPreview' => true, 'previewHeight' => 370, ); - $content = ''; + $content = ''; $new_content = gutenberg_render_block_core_file( $attributes, $content ); $this->assertStringContainsString( 'aria-label="PDF embed"', $new_content ); diff --git a/phpunit/blocks/render-block-navigation-test.php b/phpunit/blocks/render-block-navigation-test.php index 211a20be3b994..ca4bbb6c34278 100644 --- a/phpunit/blocks/render-block-navigation-test.php +++ b/phpunit/blocks/render-block-navigation-test.php @@ -17,7 +17,7 @@ class Render_Block_Navigation_Test extends WP_UnitTestCase { */ public function test_block_core_navigation_get_post_ids_from_block() { $parsed_blocks = parse_blocks( - '' + '' ); $parsed_block = $parsed_blocks[0]; $context = array(); @@ -33,13 +33,13 @@ public function test_block_core_navigation_get_post_ids_from_block() { public function test_block_core_navigation_get_post_ids_from_block_nested() { $parsed_blocks = parse_blocks( ' - - - - - - - + + + + + + + ' @@ -56,7 +56,7 @@ public function test_block_core_navigation_get_post_ids_from_block_nested() { * @covers ::gutenberg_block_core_navigation_from_block_get_post_ids */ public function test_block_core_navigation_get_post_ids_from_block_with_submenu() { - $parsed_blocks = parse_blocks( '\n\n' ); + $parsed_blocks = parse_blocks( '\n\n' ); $parsed_block = $parsed_blocks[0]; $context = array(); $block = new WP_Block( $parsed_block, $context ); diff --git a/phpunit/class-block-library-navigation-link-test.php b/phpunit/class-block-library-navigation-link-test.php index 0ff603bca3ef6..b0a5b989a9ae8 100644 --- a/phpunit/class-block-library-navigation-link-test.php +++ b/phpunit/class-block-library-navigation-link-test.php @@ -111,9 +111,10 @@ public function tear_down() { public function test_returns_link_when_post_is_published() { $page_id = self::$page->ID; + $url = 'http://' . WP_TESTS_DOMAIN; $parsed_blocks = parse_blocks( - "" + "" ); $this->assertEquals( 1, count( $parsed_blocks ) ); @@ -133,9 +134,10 @@ public function test_returns_link_when_post_is_published() { public function test_returns_empty_when_label_is_missing() { $page_id = self::$page->ID; + $url = 'http://' . WP_TESTS_DOMAIN; $parsed_blocks = parse_blocks( - "" + "" ); $this->assertEquals( 1, count( $parsed_blocks ) ); @@ -152,9 +154,10 @@ public function test_returns_empty_when_label_is_missing() { public function test_returns_empty_when_draft() { $page_id = self::$draft->ID; + $url = 'http://' . WP_TESTS_DOMAIN; $parsed_blocks = parse_blocks( - "" + "" ); $this->assertEquals( 1, count( $parsed_blocks ) ); @@ -172,9 +175,10 @@ public function test_returns_empty_when_draft() { public function test_returns_link_for_category() { $category_id = self::$category->term_id; + $url = 'http://' . WP_TESTS_DOMAIN; $parsed_blocks = parse_blocks( - "" + "" ); $this->assertEquals( 1, count( $parsed_blocks ) ); @@ -247,9 +251,10 @@ public function test_returns_link_for_decoded_link() { public function test_returns_empty_when_custom_post_type_draft() { $page_id = self::$custom_draft->ID; + $url = 'http://' . WP_TESTS_DOMAIN; $parsed_blocks = parse_blocks( - "" + "" ); $this->assertEquals( 1, count( $parsed_blocks ) ); @@ -267,9 +272,10 @@ public function test_returns_empty_when_custom_post_type_draft() { public function test_returns_link_when_custom_post_is_published() { $page_id = self::$custom_post->ID; + $url = 'http://' . WP_TESTS_DOMAIN; $parsed_blocks = parse_blocks( - "" + "" ); $this->assertEquals( 1, count( $parsed_blocks ) );