@wordpress/env
Version:
A zero-config, self contained local WordPress environment for development and testing.
328 lines (297 loc) • 10.2 kB
JavaScript
'use strict';
/**
* Internal dependencies
*/
const buildDockerComposeConfig = require( '../runtime/docker/build-docker-compose-config' );
const {
wordpressDockerFileContents,
getLoopbackPortConfig,
} = require( '../runtime/docker/docker-config' );
const getHostUser = require( '../runtime/docker/get-host-user' );
// The basic config keys which build docker compose config requires.
const CONFIG = {
mappings: {},
pluginSources: [],
themeSources: [],
port: 8888,
configDirectoryPath: '/path/to/config',
};
jest.mock( '../runtime/docker/get-host-user', () => jest.fn() );
getHostUser.mockImplementation( () => {
return {
name: 'test',
uid: 1,
gid: 2,
fullUser: '1:2',
};
} );
describe( 'buildDockerComposeConfig', () => {
it( 'should map directories before individual sources', () => {
const envConfig = {
...CONFIG,
mappings: {
'wp-content/plugins': {
path: '/path/to/wp-plugins',
},
},
pluginSources: [
{ path: '/path/to/local/plugin', basename: 'test-name' },
],
};
const dockerConfig = buildDockerComposeConfig( {
workDirectoryPath: '/path',
env: { development: envConfig, tests: envConfig },
} );
const { volumes } = dockerConfig.services.wordpress;
expect( volumes ).toEqual( [
'wordpress:/var/www/html', // WordPress root.
'/path/WordPress-PHPUnit/tests/phpunit:/wordpress-phpunit', // WordPress test library,
'user-home:/home/test',
'/path/to/wp-plugins:/var/www/html/wp-content/plugins', // Mapped plugins root.
'/path/to/local/plugin:/var/www/html/wp-content/plugins/test-name', // Mapped plugin.
] );
} );
it( 'should add all specified sources to tests, dev, and cli services', () => {
const envConfig = {
...CONFIG,
mappings: {
'wp-content/plugins': {
path: '/path/to/wp-plugins',
},
},
pluginSources: [
{ path: '/path/to/local/plugin', basename: 'test-name' },
],
themeSources: [
{ path: '/path/to/local/theme', basename: 'test-theme' },
],
};
const dockerConfig = buildDockerComposeConfig( {
workDirectoryPath: '/path',
env: { development: envConfig, tests: envConfig },
} );
const devVolumes = dockerConfig.services.wordpress.volumes;
const cliVolumes = dockerConfig.services.cli.volumes;
expect( devVolumes ).toEqual( cliVolumes );
const testsVolumes = dockerConfig.services[ 'tests-wordpress' ].volumes;
const testsCliVolumes = dockerConfig.services[ 'tests-cli' ].volumes;
expect( testsVolumes ).toEqual( testsCliVolumes );
let localSources = [
'/path/to/wp-plugins:/var/www/html/wp-content/plugins',
'/path/WordPress-PHPUnit/tests/phpunit:/wordpress-phpunit',
'user-home:/home/test',
'/path/to/local/plugin:/var/www/html/wp-content/plugins/test-name',
'/path/to/local/theme:/var/www/html/wp-content/themes/test-theme',
];
expect( devVolumes ).toEqual( expect.arrayContaining( localSources ) );
localSources = [
'/path/to/wp-plugins:/var/www/html/wp-content/plugins',
'/path/tests-WordPress-PHPUnit/tests/phpunit:/wordpress-phpunit',
'tests-user-home:/home/test',
'/path/to/local/plugin:/var/www/html/wp-content/plugins/test-name',
'/path/to/local/theme:/var/www/html/wp-content/themes/test-theme',
];
expect( testsVolumes ).toEqual(
expect.arrayContaining( localSources )
);
} );
it( 'should create "wordpress" and "tests-wordpress" volumes if they are needed by containers', () => {
// CONFIG has no coreSource entry, so there are no core sources on the
// local filesystem, so a volume should be created to contain core
// sources.
const dockerConfig = buildDockerComposeConfig( {
workDirectoryPath: '/path',
env: { development: CONFIG, tests: CONFIG },
} );
expect( dockerConfig.volumes.wordpress ).not.toBe( undefined );
expect( dockerConfig.volumes[ 'tests-wordpress' ] ).not.toBe(
undefined
);
} );
it( 'should NOT create "wordpress" and "tests-wordpress" volumes if they are not needed by containers', () => {
const envConfig = {
...CONFIG,
coreSource: {
path: '/some/random/path',
local: true,
},
};
const dockerConfig = buildDockerComposeConfig( {
workDirectoryPath: '/path',
env: { development: envConfig, tests: envConfig },
} );
expect( dockerConfig.volumes.wordpress ).toBe( undefined );
expect( dockerConfig.volumes[ 'tests-wordpress' ] ).toBe( undefined );
} );
it( 'should add healthcheck to mysql services', () => {
const config = buildDockerComposeConfig( {
workDirectoryPath: '/some/path',
env: {
development: {
port: 8888,
mysqlPort: 3306,
coreSource: null,
pluginSources: [],
themeSources: [],
mappings: {},
},
tests: {
port: 8889,
mysqlPort: 3307,
coreSource: null,
pluginSources: [],
themeSources: [],
mappings: {},
},
},
} );
expect( config.services.mysql.healthcheck ).toBeDefined();
expect( config.services.mysql.healthcheck.test ).toEqual( [
'CMD',
'healthcheck.sh',
'--connect',
'--innodb_initialized',
] );
expect( config.services.mysql.healthcheck.interval ).toBe( '5s' );
expect( config.services.mysql.healthcheck.timeout ).toBe( '10s' );
expect( config.services.mysql.healthcheck.retries ).toBe( 12 );
expect( config.services.mysql.healthcheck.start_period ).toBe( '60s' );
// Verify MARIADB_AUTO_UPGRADE is set for existing installations
expect( config.services.mysql.environment.MARIADB_AUTO_UPGRADE ).toBe(
'1'
);
expect( config.services[ 'tests-mysql' ].healthcheck ).toBeDefined();
expect( config.services[ 'tests-mysql' ].healthcheck.test ).toEqual( [
'CMD',
'healthcheck.sh',
'--connect',
'--innodb_initialized',
] );
expect(
config.services[ 'tests-mysql' ].environment.MARIADB_AUTO_UPGRADE
).toBe( '1' );
} );
it( 'should use service_healthy condition for WordPress depends_on', () => {
const config = buildDockerComposeConfig( {
workDirectoryPath: '/some/path',
env: {
development: {
port: 8888,
mysqlPort: 3306,
coreSource: null,
pluginSources: [],
themeSources: [],
mappings: {},
},
tests: {
port: 8889,
mysqlPort: 3307,
coreSource: null,
pluginSources: [],
themeSources: [],
mappings: {},
},
},
} );
expect( config.services.wordpress.depends_on ).toEqual( {
mysql: { condition: 'service_healthy' },
} );
expect( config.services[ 'tests-wordpress' ].depends_on ).toEqual( {
'tests-mysql': { condition: 'service_healthy' },
} );
} );
describe( 'testsEnvironment', () => {
it( 'should omit tests services when testsEnvironment is false', () => {
const dockerConfig = buildDockerComposeConfig( {
testsEnvironment: false,
workDirectoryPath: '/path',
env: {
development: CONFIG,
tests: CONFIG,
},
} );
// Development services should exist.
expect( dockerConfig.services.mysql ).toBeDefined();
expect( dockerConfig.services.wordpress ).toBeDefined();
expect( dockerConfig.services.cli ).toBeDefined();
expect( dockerConfig.services.phpmyadmin ).toBeDefined();
// Tests services should not exist.
expect( dockerConfig.services[ 'tests-mysql' ] ).toBeUndefined();
expect(
dockerConfig.services[ 'tests-wordpress' ]
).toBeUndefined();
expect( dockerConfig.services[ 'tests-cli' ] ).toBeUndefined();
expect(
dockerConfig.services[ 'tests-phpmyadmin' ]
).toBeUndefined();
} );
it( 'should omit tests volumes when testsEnvironment is false', () => {
const dockerConfig = buildDockerComposeConfig( {
testsEnvironment: false,
workDirectoryPath: '/path',
env: {
development: CONFIG,
tests: CONFIG,
},
} );
// Development volumes should exist.
expect( dockerConfig.volumes.wordpress ).toBeDefined();
expect( dockerConfig.volumes.mysql ).toBeDefined();
expect( dockerConfig.volumes[ 'user-home' ] ).toBeDefined();
// Tests volumes should not exist.
expect( dockerConfig.volumes[ 'tests-wordpress' ] ).toBeUndefined();
expect( dockerConfig.volumes[ 'mysql-test' ] ).toBeUndefined();
expect( dockerConfig.volumes[ 'tests-user-home' ] ).toBeUndefined();
} );
it( 'should include tests services by default', () => {
const dockerConfig = buildDockerComposeConfig( {
workDirectoryPath: '/path',
env: {
development: CONFIG,
tests: CONFIG,
},
} );
expect( dockerConfig.services[ 'tests-mysql' ] ).toBeDefined();
expect( dockerConfig.services[ 'tests-wordpress' ] ).toBeDefined();
expect( dockerConfig.services[ 'tests-cli' ] ).toBeDefined();
} );
} );
} );
describe( 'getLoopbackPortConfig', () => {
it( 'returns Apache Listen and VirtualHost edits for a non-default port using an anchored sed pattern', () => {
const out = getLoopbackPortConfig( 8888 );
expect( out ).toContain( 'Listen 8888' );
expect( out ).toContain( '/etc/apache2/ports.conf' );
expect( out ).toContain( '<VirtualHost *:80 *:8888>' );
expect( out ).toContain(
'/etc/apache2/sites-enabled/000-default.conf'
);
// Guard against a future "simplification" to a greedy s/80/.../g
// that would also match e.g. unrelated "80" substrings.
expect( out ).not.toMatch( /s\|80\|/ );
expect( out ).toMatch( /s\|<VirtualHost \\\*:80>\|/ );
} );
it( 'returns an empty string for port 80 (Apache already listens there)', () => {
expect( getLoopbackPortConfig( 80 ) ).toBe( '' );
} );
it( 'returns an empty string for port 443 (wp-env does not configure SSL)', () => {
expect( getLoopbackPortConfig( 443 ) ).toBe( '' );
} );
} );
describe( 'wordpressDockerFileContents', () => {
it( 'injects the resolved per-environment port into the generated Dockerfile', () => {
const config = {
xdebug: 'off',
spx: 'off',
env: {
development: { port: 8888, phpVersion: null },
tests: { port: 8889, phpVersion: null },
},
};
const dockerfile = wordpressDockerFileContents( 'tests', config );
// Proves getLoopbackPortConfig is wired in AND that each environment
// uses its own port (not the development port).
expect( dockerfile ).toContain( 'Listen 8889' );
expect( dockerfile ).not.toContain( 'Listen 8888' );
} );
} );