From e48def9f91f2bee3bc7e906a35a17e6f7ed6de63 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Fri, 30 Oct 2020 13:46:43 +0200 Subject: [PATCH 01/49] Add: ReturnPromise pass stderr to the success callback --- src/docker/util.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docker/util.js b/src/docker/util.js index 543f396..7df2629 100644 --- a/src/docker/util.js +++ b/src/docker/util.js @@ -2,7 +2,7 @@ * Return the child process as promise. * * @param {import('child_process').ChildProcessWithoutNullStreams} process - the child process that's running. - * @param {Function} callback - callback that deterred what to return when the process is successful. + * @param {([stderr]: string) => any} callback - callback that deterred what to return when the process is successful. * @returns {Promise} return what said to return form the callback */ function ReturnPromise (process, callback) { @@ -23,7 +23,7 @@ function ReturnPromise (process, callback) { return } - resolve(callback()) + resolve(callback(stderr)) }) }) } From 9ace212e2598dcd746695cbf6394936419f49849 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Fri, 30 Oct 2020 14:17:55 +0200 Subject: [PATCH 02/49] Add: RunInContainer --- src/docker/docker.js | 30 ++++++++++++++++++++++++++++++ src/docker/types.js | 6 ++++++ 2 files changed, 36 insertions(+) diff --git a/src/docker/docker.js b/src/docker/docker.js index 848fd3a..1dd2aa1 100644 --- a/src/docker/docker.js +++ b/src/docker/docker.js @@ -35,6 +35,36 @@ class Docker { }) } + /** + * Run commands in a docker container return it's output. + * + * @param {ContainerOptions} options - Docker container options. + * @returns {RunInContainerOutput} Container outputs. + */ + RunInContainer (options) { + if (!options.commands) { + throw new TypeError('options.commands must be provided to use RunInContainer.') + } + + if (!options.rm) { + throw new TypeError('options.rm must be true to use RunInContainer. (we don\'t want to leave garbage aroud)') + } + + let stdout = '' + + const args = processCreateContainerOptions(options, true, false) + + const process = spawn('docker', args) + + process.stdout.on('data', (data) => { + stdout += data + }) + + return ReturnPromise(process, (stderr) => { + return { stdout: stdout, stderr: stderr } + }) + } + /** * Create docker volume. * diff --git a/src/docker/types.js b/src/docker/types.js index fd11932..b8b0614 100644 --- a/src/docker/types.js +++ b/src/docker/types.js @@ -20,6 +20,12 @@ * @property {string} healthCommand - Command to run to check health. */ +/** @typedef RunInContainerOutput + * @type {object} + * @property {string} stdout - Container output from log stream. + * @property {string} stderr - Container output from error stream. + */ + /** * @typedef VolumeOptions * @type {object} From f9aff0f104b7a4f0454e9e701b9969e5177c0ae6 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Fri, 30 Oct 2020 14:18:22 +0200 Subject: [PATCH 03/49] Update: CreateWordpressCliContainer to use RunInContainer --- src/docker/presets/containers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docker/presets/containers.js b/src/docker/presets/containers.js index 1c87f3b..528a27f 100644 --- a/src/docker/presets/containers.js +++ b/src/docker/presets/containers.js @@ -72,7 +72,7 @@ function CreateWordpressCliContainer (wordpress, commands) { throw new TypeError('commands must be an array') } - return Docker.prototype.CreateContainer({ + return Docker.prototype.RunInContainer({ volumes: wordpress.options.volumes, image: 'wordpress:cli', network: wordpress.options.network, From 1fca7fc33bef429aa85c3c2f4876d68eb921faa2 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Fri, 30 Oct 2020 14:22:59 +0200 Subject: [PATCH 04/49] Update: docs --- src/docker/presets/containers.js | 2 +- src/workflow/sites-management.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docker/presets/containers.js b/src/docker/presets/containers.js index 528a27f..545cf7f 100644 --- a/src/docker/presets/containers.js +++ b/src/docker/presets/containers.js @@ -65,7 +65,7 @@ function CreateWordpressContainer (name, port, mysqlContainer, run = false) { * * @param {Container} wordpress - WordPress docker container based from. * @param {string[]} commands - commands to pass to the container. - * @returns {Promise} retrun promise for WordPress continer object. + * @returns {Promise} retrun promise for WordPress continer object. */ function CreateWordpressCliContainer (wordpress, commands) { if (!Array.isArray(commands)) { diff --git a/src/workflow/sites-management.js b/src/workflow/sites-management.js index c328d68..86b8913 100644 --- a/src/workflow/sites-management.js +++ b/src/workflow/sites-management.js @@ -4,7 +4,7 @@ const { CreateWordpressCliContainer } = require('../docker/presets/containers') * Run wordpress cli command to initialize the wordpress sits. * * @param {import('../docker/container')} wordpress the wordpress container to initialize. - * @returns {Promise} wordpress cli container. + * @returns {Promise} wordpress cli container. */ function InitSite (wordpress) { return CreateWordpressCliContainer(wordpress, [ From e5a2c7abca8639023de516c25d0ca6f8a78ac722 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Fri, 30 Oct 2020 23:55:51 +0200 Subject: [PATCH 05/49] Add: commands to better check conatiner health --- src/docker/docker.js | 26 ++++++++++++++++++++++++-- src/docker/types.js | 7 ++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/docker/docker.js b/src/docker/docker.js index 1dd2aa1..0b49497 100644 --- a/src/docker/docker.js +++ b/src/docker/docker.js @@ -146,8 +146,30 @@ function processCreateContainerOptions (options, run, detach) { args.push('--net', options.network) } - if (options.healthCommand) { - args.push('--health-cmd', `${options.healthCommand}`) + if (options.health) { + if (options.health.command) { + args.push('--health-cmd', options.health.command) + } + + if (options.health.interval) { + args.push('--health-interval', options.health.interval) + } + + if (options.health.retries) { + if (!Number.isInteger(options.health.retries)) { + throw new TypeError('options.health.retries must be an integer.') + } + + args.push('--health-retries', options.health.retries) + } + + if (options.health.startPeriod) { + args.push('--health-start-period', options.health.startPeriod) + } + + if (options.health.timeout) { + args.push('--health-timeout', options.health.timeout) + } } if (options.rm) { diff --git a/src/docker/types.js b/src/docker/types.js index b8b0614..7e89894 100644 --- a/src/docker/types.js +++ b/src/docker/types.js @@ -17,7 +17,12 @@ * @property {boolean} rm - remove the container after it exits. * @property {'created'|'started'|'removed'|'stopped'} status - the container status (created|started|removed|stopped) * @property {string[]} commands - commands to pass to the container. - * @property {string} healthCommand - Command to run to check health. + * @property {object} health - Object to check container health. + * @property {string} health.command - Command to run to check health. + * @property {string} health.interval - Time between running the check (ms|s|m|h) (default 30s) + * @property {number} health.retries - Consecutive failures needed to report unhealthy + * @property {string} health.startPeriod - Start period for the container to initialize before starting health-retries countdown (ms|s|m|h) (default 30s) + * @property {string} health.timeout - Maximum time to allow one check to run (ms|s|m|h) (default 30s) */ /** @typedef RunInContainerOutput From c471533a39c6a0471a04af26c944cd53978fd01e Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Fri, 30 Oct 2020 23:56:09 +0200 Subject: [PATCH 06/49] Fix: health test --- test/docker/docker.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/docker/docker.spec.js b/test/docker/docker.spec.js index 820524e..1ec159b 100644 --- a/test/docker/docker.spec.js +++ b/test/docker/docker.spec.js @@ -108,7 +108,7 @@ describe('Docker', () => { }) it('should contains container health-cmd argument', () => { - const arr = processCreateContainerOptions({ image: 'image-test', healthCommand: 'ping test' }) + const arr = processCreateContainerOptions({ image: 'image-test', health: { command: 'ping test' } }) expect(arr).toContain('ping test') expect(arr).toContain('--health-cmd') From ee22d1188e5e8ebffb39cabb29a16cd74c47d2f5 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Fri, 30 Oct 2020 23:56:29 +0200 Subject: [PATCH 07/49] Update: CreateWordpressContainer --- src/docker/presets/containers.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/docker/presets/containers.js b/src/docker/presets/containers.js index 545cf7f..070fbba 100644 --- a/src/docker/presets/containers.js +++ b/src/docker/presets/containers.js @@ -57,6 +57,12 @@ function CreateWordpressContainer (name, port, mysqlContainer, run = false) { image: 'wordpress', network: 'cywp-network', name: `cywp-${name}-wordpress`, + health: { + command: 'test -r wp-includes/version.php', + interval: '5s', + startPeriod: '3s', + retries: 30, + }, }, run) } From c2c5507dce10d22f088680931f1475c43bd9ca46 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sat, 31 Oct 2020 00:01:34 +0200 Subject: [PATCH 08/49] Update: presets to use the health checks --- src/docker/presets/containers.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/docker/presets/containers.js b/src/docker/presets/containers.js index 070fbba..4f31e18 100644 --- a/src/docker/presets/containers.js +++ b/src/docker/presets/containers.js @@ -22,7 +22,12 @@ function CreateMysqlContainer (name, port, run = false) { environmentVariables: [ { name: 'MYSQL_ROOT_PASSWORD', value: 'cywp' }, ], - healthCommand: 'mysqladmin ping --silent', // eslint-disable-line spellcheck/spell-checker + health: { + command: 'mysqladmin ping --silent', // eslint-disable-line spellcheck/spell-checker + startPeriod: '5s', + retries: 30, + interval: '1s', + }, }, run) } @@ -59,8 +64,8 @@ function CreateWordpressContainer (name, port, mysqlContainer, run = false) { name: `cywp-${name}-wordpress`, health: { command: 'test -r wp-includes/version.php', - interval: '5s', - startPeriod: '3s', + startPeriod: '1s', + interval: '1s', retries: 30, }, }, run) From e3931a5fb1356aff3bf2272e45ba0e5e9c4afe9e Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sat, 31 Oct 2020 00:09:06 +0200 Subject: [PATCH 09/49] Add: command to check container health --- src/docker/container.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/docker/container.js b/src/docker/container.js index d4fcb4e..f51c0c2 100644 --- a/src/docker/container.js +++ b/src/docker/container.js @@ -151,6 +151,20 @@ class Container { return status }) } + + /** + * Checks if the container is healthy or not. + * + * @returns {Promise} if the continer is healthy or not + */ + isHealthy () { + if (!this.options.health.command) { + throw new Error('options.health.command must be defined to use IsHealthy') + } + + return this.inspect('{{.State.Health.Status}}') + .then(status => 'healthy' === status) + } } module.exports = Container From bcaec9bf5b12951e4d4192fa02078896b3ada4bd Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sat, 31 Oct 2020 00:17:03 +0200 Subject: [PATCH 10/49] Update: setupDatabase --- src/workflow/environment.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/workflow/environment.js b/src/workflow/environment.js index c432e9a..398b467 100644 --- a/src/workflow/environment.js +++ b/src/workflow/environment.js @@ -30,9 +30,7 @@ async function setupDatabase (port) { let mysqlReady = false while (!mysqlReady) { - const status = await mysql.inspect('{{.State.Health.Status}}') - - if ('healthy' === status) { mysqlReady = true } + if (await mysql.isHealthy()) { mysqlReady = true } await sleep(1000) } From 9607e5e140f353ce307103f6b07ab431d252d004 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sat, 31 Oct 2020 00:17:52 +0200 Subject: [PATCH 11/49] Add: class to control wp theme --- src/wp-cli/theme.js | 108 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 src/wp-cli/theme.js diff --git a/src/wp-cli/theme.js b/src/wp-cli/theme.js new file mode 100644 index 0000000..0c7c266 --- /dev/null +++ b/src/wp-cli/theme.js @@ -0,0 +1,108 @@ +const Container = require('../docker/container') // eslint-disable-line no-unused-vars +const { CreateWordpressCliContainer } = require('../docker/presets/containers') + +/** + * Manage wordpress theme through wp cli. + */ +class Theme { + /** + * Constructor for the Theme object + * + * @param {Container} site - the wordpress site to work on. + */ + constructor (site) { + this.site = site + } + + /** + * + * @param {string[]} commands - + * @returns {Promise} the wordperss cli container. + */ + wpTheme (commands) { + const args = ['wp', 'theme'].concat(commands) + + return CreateWordpressCliContainer(this.site, args) + } + + /** + * Activates a theme. + * + * @param {string} theme - The theme to activate. + * @returns {Promise} the wordperss cli container. + */ + activate (theme) { + return this.wpTheme(['activate', theme]) + } + + /** + * Deletes one or more themes. + * + * @param {string|string[]|'all'} theme - One or more themes to delete, use 'all' to delete all except active theme. + * @param {boolean} force - To delete active theme use this. + * @returns {Promise} the wordperss cli container. + * + * @example ```js + * const theme = new Theme(WordPress) + * + * //delete one theme + * theme.delete('Twenty Twenty') + * + * // Delete all themes + * theme.delete('all') + * ``` + */ + delete (theme, force = false) { + if ('string' === typeof theme) { + theme = [theme] + } + + if (!Array.isArray(theme)) { + throw new TypeError('theme must be an array or a string') + } + + const args = ['delete'] + + if (force) { args.push('--force') } + + if ('all' === theme[0]) { theme = ['--all'] } + + args.push.apply(args, theme) + + return this.wpTheme(args) + } + + /** + * Get theme data. + * + * @param {string} theme - The theme to get. + * @returns {Promise} The wordpress cli container. + */ + get (theme) { + return this.wpTheme(['get', '--format=json', theme]) + .then((output) => JSON.parse(output.stdout)) + } + + install () { + } + + isActive () { + } + + isInstalled () { + } + + list () { + } + + path () { + } + + search () { + } + + status () { + } +} + +module.exports = Theme From af42ae9e87d37aee2e1f357e30e3512b26e8d712 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sat, 31 Oct 2020 00:18:09 +0200 Subject: [PATCH 12/49] Add: RunInContainerOutput type --- src/wp-cli/types.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/wp-cli/types.js diff --git a/src/wp-cli/types.js b/src/wp-cli/types.js new file mode 100644 index 0000000..a3e29b9 --- /dev/null +++ b/src/wp-cli/types.js @@ -0,0 +1,18 @@ +/** + * @typedef ThemeGetObject + * @property {string} name - The name of the theme. + * @property {string} title - the title of the theme. + * @property {string} version - version of the theme. + * @property {string} status - if the theme is active or inactive. + * @property {string} parent_theme - the name of the parent theme. + * @property {string} template_dir - path to the template directory. + * @property {string} stylesheet_dir - path to the stylesheet directory. + * @property {string} template - the name of the template file. + * @property {string} stylesheet - the name of the stylesheet file. + * @property {string} screenshot - theme screenshot. + * @property {string} description - theme description. + * @property {string} author - link of the theme author. + * @property {string[]} tags - list of the theme tags. + * @property {string} theme_root - path to theme root directory. + * @property {string} theme_root_uri - uri to theme root directory. + */ From 77eb84c0561b3279a9487fcdf94cd464f19fdbd2 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sat, 31 Oct 2020 00:19:14 +0200 Subject: [PATCH 13/49] Fix: Test --- test/docker/presets/containers.spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/docker/presets/containers.spec.js b/test/docker/presets/containers.spec.js index 4598dce..2fb8b4e 100644 --- a/test/docker/presets/containers.spec.js +++ b/test/docker/presets/containers.spec.js @@ -50,7 +50,6 @@ describe('Presets', () => { const wordpress = await CreateWordpressContainer('presets-test-cli', 4501, demoMysqlContainer) const container = await CreateWordpressCliContainer(wordpress, ['wp', '--help']) - expect(container.options.name).toBe('cywp-presets-test-cli-wordpress-cli') expect(container.options.commands).toEqual(expect.arrayContaining(['wp', '--help'])) expect(container.options.network).toBe(wordpress.options.network) }) From 21c5249470be8d76f6299ca64ca506ccae1cc4ac Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sat, 31 Oct 2020 13:52:49 +0200 Subject: [PATCH 14/49] Fix: Tests --- test/docker/presets/containers.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/docker/presets/containers.spec.js b/test/docker/presets/containers.spec.js index 2fb8b4e..f174521 100644 --- a/test/docker/presets/containers.spec.js +++ b/test/docker/presets/containers.spec.js @@ -48,10 +48,10 @@ describe('Presets', () => { it('should create WordPress', async () => { const demoMysqlContainer = new Container({ name: 'pop', exposePorts: [{ host: 123, docker: 123 }] }) const wordpress = await CreateWordpressContainer('presets-test-cli', 4501, demoMysqlContainer) - const container = await CreateWordpressCliContainer(wordpress, ['wp', '--help']) + const output = await CreateWordpressCliContainer(wordpress, ['wp', '--help']) - expect(container.options.commands).toEqual(expect.arrayContaining(['wp', '--help'])) - expect(container.options.network).toBe(wordpress.options.network) + expect(output.stdout).toBeTruthy() + expect(output.stderr).toBeDefined() }) it('should throw type error when commands is not array', async () => { From f9eaf0bc734f7d6050e6ef34cf51c3965dc0512d Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sat, 31 Oct 2020 14:43:09 +0200 Subject: [PATCH 15/49] Add: install command --- src/wp-cli/theme.js | 48 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/src/wp-cli/theme.js b/src/wp-cli/theme.js index 0c7c266..79ccd23 100644 --- a/src/wp-cli/theme.js +++ b/src/wp-cli/theme.js @@ -1,5 +1,6 @@ const Container = require('../docker/container') // eslint-disable-line no-unused-vars const { CreateWordpressCliContainer } = require('../docker/presets/containers') +require('./types') /** * Manage wordpress theme through wp cli. @@ -17,7 +18,7 @@ class Theme { /** * * @param {string[]} commands - - * @returns {Promise} the wordperss cli container. + * @returns {Promise} The output of the command */ wpTheme (commands) { const args = ['wp', 'theme'].concat(commands) @@ -29,7 +30,7 @@ class Theme { * Activates a theme. * * @param {string} theme - The theme to activate. - * @returns {Promise} the wordperss cli container. + * @returns {Promise} The output of the command */ activate (theme) { return this.wpTheme(['activate', theme]) @@ -40,7 +41,7 @@ class Theme { * * @param {string|string[]|'all'} theme - One or more themes to delete, use 'all' to delete all except active theme. * @param {boolean} force - To delete active theme use this. - * @returns {Promise} the wordperss cli container. + * @returns {Promise} The output of the command. * * @example ```js * const theme = new Theme(WordPress) @@ -61,29 +62,58 @@ class Theme { throw new TypeError('theme must be an array or a string') } - const args = ['delete'] + const deleteArgs = ['delete'] - if (force) { args.push('--force') } + if (force) { deleteArgs.push('--force') } if ('all' === theme[0]) { theme = ['--all'] } - args.push.apply(args, theme) + deleteArgs.push.apply(deleteArgs, theme) - return this.wpTheme(args) + return this.wpTheme(deleteArgs) } /** * Get theme data. * * @param {string} theme - The theme to get. - * @returns {Promise} The wordpress cli container. + * @returns {Promise} Current theme data. */ get (theme) { return this.wpTheme(['get', '--format=json', theme]) .then((output) => JSON.parse(output.stdout)) } - install () { + /** + * @param {string | Array} theme - One or more themes to install. Accepts a theme slug, the path to a local zip file, or a URL to a remote zip file. + * @param {boolean} activate - If set, the theme will be activated immediately after install. + * @param {string} version - Get that particular version from wordpress.org, instead of the stable version. + * @returns {Promise} The output of the command. + */ + install (theme, activate, version) { + if ('string' === typeof theme) { + theme = [theme] + } + + if (!Array.isArray(theme)) { + throw new TypeError('theme must be an array or a string') + } + + const installArgs = ['install', '--force'] + + if (activate) { + if (1 < theme.length) { + throw new Error('To use activate there must be at only one theme given.') + } + + installArgs.push('--activate') + } + + if (version) { installArgs.push(`--version=${version}`) } + + installArgs.push.apply(installArgs, theme) + + return this.wpTheme(installArgs) } isActive () { From 0f15b304d2a19bee4608c48dbc80cc8e1ccece50 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sat, 31 Oct 2020 14:43:24 +0200 Subject: [PATCH 16/49] Fix: flikers in wordpress continer --- src/docker/presets/containers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docker/presets/containers.js b/src/docker/presets/containers.js index 4f31e18..1e9b14e 100644 --- a/src/docker/presets/containers.js +++ b/src/docker/presets/containers.js @@ -64,7 +64,7 @@ function CreateWordpressContainer (name, port, mysqlContainer, run = false) { name: `cywp-${name}-wordpress`, health: { command: 'test -r wp-includes/version.php', - startPeriod: '1s', + startPeriod: '2s', interval: '1s', retries: 30, }, From 7af43745b37173f0eae74f746eb82aaeed65dc58 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sat, 31 Oct 2020 16:59:06 +0200 Subject: [PATCH 17/49] Fix: Flikers --- src/docker/presets/containers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docker/presets/containers.js b/src/docker/presets/containers.js index 1e9b14e..2fa5c50 100644 --- a/src/docker/presets/containers.js +++ b/src/docker/presets/containers.js @@ -64,7 +64,7 @@ function CreateWordpressContainer (name, port, mysqlContainer, run = false) { name: `cywp-${name}-wordpress`, health: { command: 'test -r wp-includes/version.php', - startPeriod: '2s', + startPeriod: '3s', interval: '1s', retries: 30, }, From 3d1f5bef6f003b8d26ee257082173c1d6debb828 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sat, 31 Oct 2020 17:11:24 +0200 Subject: [PATCH 18/49] Update: pram check to util file --- src/wp-cli/theme.js | 22 +++++----------------- src/wp-cli/util.js | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 17 deletions(-) create mode 100644 src/wp-cli/util.js diff --git a/src/wp-cli/theme.js b/src/wp-cli/theme.js index 79ccd23..45cd2c0 100644 --- a/src/wp-cli/theme.js +++ b/src/wp-cli/theme.js @@ -1,6 +1,6 @@ -const Container = require('../docker/container') // eslint-disable-line no-unused-vars -const { CreateWordpressCliContainer } = require('../docker/presets/containers') require('./types') +const { CreateWordpressCliContainer } = require('../docker/presets/containers') +const { CheckIfArrayOrString } = require('./util') /** * Manage wordpress theme through wp cli. @@ -9,7 +9,7 @@ class Theme { /** * Constructor for the Theme object * - * @param {Container} site - the wordpress site to work on. + * @param {import('../docker/container')} site - the wordpress site to work on. */ constructor (site) { this.site = site @@ -54,13 +54,7 @@ class Theme { * ``` */ delete (theme, force = false) { - if ('string' === typeof theme) { - theme = [theme] - } - - if (!Array.isArray(theme)) { - throw new TypeError('theme must be an array or a string') - } + theme = CheckIfArrayOrString(theme, 'theme') const deleteArgs = ['delete'] @@ -91,13 +85,7 @@ class Theme { * @returns {Promise} The output of the command. */ install (theme, activate, version) { - if ('string' === typeof theme) { - theme = [theme] - } - - if (!Array.isArray(theme)) { - throw new TypeError('theme must be an array or a string') - } + theme = CheckIfArrayOrString(theme, 'theme') const installArgs = ['install', '--force'] diff --git a/src/wp-cli/util.js b/src/wp-cli/util.js new file mode 100644 index 0000000..efff17d --- /dev/null +++ b/src/wp-cli/util.js @@ -0,0 +1,21 @@ + +/** + * Checks if the given value is an array or string. + * + * @param {string | Array} item - the items to be checked. + * @param {string} purpose - use to determent what to throw is the items isn't valid. + * @returns {Array} Array that contined a vlive item / items. + */ +function CheckIfArrayOrString (item, purpose) { + if ('string' === typeof item) { + item = [item] + } + + if (!Array.isArray(item)) { + throw new TypeError(`${purpose} must be an array or a string`) + } + + return item +} + +module.exports = { CheckIfArrayOrString } From 670bb883e5d95966c9cedf1ca2898247cfca83cc Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sat, 31 Oct 2020 17:19:19 +0200 Subject: [PATCH 19/49] Add: tests to util --- test/wp-cli/util.spec.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 test/wp-cli/util.spec.js diff --git a/test/wp-cli/util.spec.js b/test/wp-cli/util.spec.js new file mode 100644 index 0000000..45e0ae7 --- /dev/null +++ b/test/wp-cli/util.spec.js @@ -0,0 +1,18 @@ +const { CheckIfArrayOrString } = require('../../src/wp-cli/util') + +describe('util', () => { + describe('#CheckIfArrayOrString()', () => { + it('should return an array', () => { + expect(CheckIfArrayOrString(['item'])).toContain('item') + expect(CheckIfArrayOrString('item')).toContain('item') + }) + + it('should throw an error when item is not array or string', () => { + expect(() => CheckIfArrayOrString(1)).toThrow() + }) + + it('should throw an error with massage when item is not array or string', () => { + expect(() => CheckIfArrayOrString(1, 'fail')).toThrow('fail must be an array or a string') + }) + }) +}) From 841cbf51bf3c8b7b83528e075e263f3db8894894 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 01:03:18 +0200 Subject: [PATCH 20/49] Add: checks to install version --- src/wp-cli/theme.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/wp-cli/theme.js b/src/wp-cli/theme.js index 45cd2c0..0daa7c8 100644 --- a/src/wp-cli/theme.js +++ b/src/wp-cli/theme.js @@ -97,7 +97,13 @@ class Theme { installArgs.push('--activate') } - if (version) { installArgs.push(`--version=${version}`) } + if (version) { + if (1 < theme.length) { + throw new Error('To use version there must be at only one theme given.') + } + + installArgs.push(`--version=${version}`) + } installArgs.push.apply(installArgs, theme) From 855f5ec2a4d1fcba5277b38a0f3bef16cf9054bf Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 01:03:33 +0200 Subject: [PATCH 21/49] Add: tests for theme --- test/wp-cli/theme.spec.js | 125 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 test/wp-cli/theme.spec.js diff --git a/test/wp-cli/theme.spec.js b/test/wp-cli/theme.spec.js new file mode 100644 index 0000000..5a1b5f4 --- /dev/null +++ b/test/wp-cli/theme.spec.js @@ -0,0 +1,125 @@ +const { CreateWordpressCliContainer } = require('../../src/docker/presets/containers') +const Theme = require('../../src/wp-cli/theme') + +jest.mock('../../src/docker/presets/containers') + +describe('Theme', () => { + /** @type {Theme} */ + let theme + + beforeAll(async () => { + theme = new Theme() + }) + + describe('#wpTheme()', () => { + it('should have the arguments wp theme', () => { + theme.wpTheme([]) + + expect(CreateWordpressCliContainer) + .toHaveBeenLastCalledWith(undefined, ['wp', 'theme']) + }) + }) + + describe('#activate()', () => { + it('should have the arguments to activate theme', () => { + theme.activate('theme') + + expect(CreateWordpressCliContainer) + .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'activate', 'theme']) + }) + }) + + describe('#delete()', () => { + it('should have the arguments to delete theme', () => { + theme.delete('theme') + + expect(CreateWordpressCliContainer) + .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'delete', 'theme']) + }) + + it('should have the arguments to delete multiple themes', () => { + theme.delete(['theme1', 'theme2']) + + expect(CreateWordpressCliContainer) + .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'delete', 'theme1', 'theme2']) + }) + + it('should have the arguments to delete all themes', () => { + theme.delete('all') + + expect(CreateWordpressCliContainer) + .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'delete', '--all']) + }) + + it('should have the arguments to delete theme with force', () => { + theme.delete('theme', true) + + expect(CreateWordpressCliContainer) + .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'delete', '--force', 'theme']) + }) + + it('should throw an error massage with theme in it', () => { + expect(() => theme.delete(1)).toThrow(new TypeError('theme must be an array or a string')) + }) + }) + + describe('#get()', () => { + let originalWpTheme + beforeAll(() => { + originalWpTheme = theme.wpTheme + }) + + it('should have the arguments to get theme info', () => { + theme.wpTheme = jest.fn((commands) => Promise.resolve(commands)) + + theme.get('theme') + + expect(theme.wpTheme) + .toHaveBeenLastCalledWith(['get', '--format=json', 'theme']) + }) + + afterAll(() => { + theme.wpTheme = originalWpTheme + }) + }) + + describe('#install()', () => { + it('should have the arguments to install theme', () => { + theme.install('theme') + + expect(CreateWordpressCliContainer) + .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'install', '--force', 'theme']) + }) + + it('should have the arguments to install multiple themes', () => { + theme.install(['theme1', 'theme2']) + + expect(CreateWordpressCliContainer) + .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'install', '--force', 'theme1', 'theme2']) + }) + + it('should have the arguments to install and activate theme', () => { + theme.install('theme', true) + + expect(CreateWordpressCliContainer) + .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'install', '--force', '--activate', 'theme']) + }) + + it('should throw an error when trying to install an activate multiple themes', () => { + expect(() => theme.install(['theme1', 'theme2'], true)) + .toThrow(new Error('To use activate there must be at only one theme given.')) + }) + + it('should have the arguments to install different version theme', () => { + theme.install('theme', false, 2.2) + + expect(CreateWordpressCliContainer) + .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'install', '--force', '--version=2.2', 'theme']) + }) + + it('should throw an error when trying to install an different version multiple themes', () => { + expect(() => theme.install(['theme1', 'theme2'], false, '2.2')) + .toThrow(new Error('To use version there must be at only one theme given.')) + }) + }) +}) From 1ef4e0894196e1d718e2a07cba853f2a2546615a Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 01:06:17 +0200 Subject: [PATCH 22/49] Remove: environment Tests --- test/workflow/environment.spec.js | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100644 test/workflow/environment.spec.js diff --git a/test/workflow/environment.spec.js b/test/workflow/environment.spec.js deleted file mode 100644 index 1da584a..0000000 --- a/test/workflow/environment.spec.js +++ /dev/null @@ -1,30 +0,0 @@ -const { setupNetwork } = require('../../src/workflow/environment') -const { spawnSync } = require('child_process') - -describe('environment', () => { - describe('#setupNetwork()', () => { - let keepCywpNetwork - - beforeAll(() => { - const rmProcess = spawnSync('docker', ['network', 'rm', 'cywp-network']) - - keepCywpNetwork = true - - if (rmProcess.status) { - keepCywpNetwork = false - } - }) - - it('should setup network named cywp-network', async () => { - const network = await setupNetwork() - - expect(network.options.name).toBe('cywp-network') - }) - - afterAll(async () => { - if (!keepCywpNetwork) { - spawnSync('docker', ['network', 'rm', 'cywp-network']) - } - }) - }) -}) From 1f13e02a96ee7516e919449029a9b188c8b90dc7 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 01:56:41 +0200 Subject: [PATCH 23/49] Fix: Theme Tests --- test/wp-cli/theme.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/wp-cli/theme.spec.js b/test/wp-cli/theme.spec.js index 5a1b5f4..2b46cce 100644 --- a/test/wp-cli/theme.spec.js +++ b/test/wp-cli/theme.spec.js @@ -70,7 +70,7 @@ describe('Theme', () => { }) it('should have the arguments to get theme info', () => { - theme.wpTheme = jest.fn((commands) => Promise.resolve(commands)) + theme.wpTheme = jest.fn((commands) => Promise.resolve({ stdout: JSON.stringify(commands) })) theme.get('theme') From 3bfb24811427b49c73c25e9ec22697033c3c9133 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 02:14:59 +0200 Subject: [PATCH 24/49] Add: tests for RunInContainer --- test/docker/docker.spec.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/docker/docker.spec.js b/test/docker/docker.spec.js index 1ec159b..d829790 100644 --- a/test/docker/docker.spec.js +++ b/test/docker/docker.spec.js @@ -3,6 +3,7 @@ const { Docker, processCreateContainerOptions, ProcessCreateNetworkOption } = re const CreateContainer = Docker.prototype.CreateContainer const CreateVolume = Docker.prototype.CreateVolume const CreateNetwork = Docker.prototype.CreateNetwork +const RunInContainer = Docker.prototype.RunInContainer describe('Docker', () => { describe('#processCreateContainerOptions()', () => { @@ -247,6 +248,24 @@ describe('Docker', () => { }) }) + describe('#RunInContainer()', () => { + it('should throw error empty option.command', () => { + expect(() => RunInContainer({})).toThrow() + }) + + it('should throw error when rm is not true', () => { + expect(() => RunInContainer({ commands: ['test'] })).toThrow() + }) + + it('run tmp docker', () => { + return expect(RunInContainer({ + image: 'hello-world', + commands: ['./hello'], + rm: true, + })).resolves.toBeTruthy() + }) + }) + describe('#CreateVolume()', () => { let volumeNames From 6f4328efac3c5daf682c0ab39c53fe748e2f61b3 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 02:15:12 +0200 Subject: [PATCH 25/49] Fix: docs --- src/docker/docker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docker/docker.js b/src/docker/docker.js index 0b49497..2600f8c 100644 --- a/src/docker/docker.js +++ b/src/docker/docker.js @@ -39,7 +39,7 @@ class Docker { * Run commands in a docker container return it's output. * * @param {ContainerOptions} options - Docker container options. - * @returns {RunInContainerOutput} Container outputs. + * @returns {Promise} Container outputs. */ RunInContainer (options) { if (!options.commands) { From 29cbd8b569288ecc25e18f7d1a450bbd8a1021ca Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 02:34:55 +0200 Subject: [PATCH 26/49] Add: Checks to options.health --- src/docker/docker.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/docker/docker.js b/src/docker/docker.js index 2600f8c..41cb05c 100644 --- a/src/docker/docker.js +++ b/src/docker/docker.js @@ -147,10 +147,12 @@ function processCreateContainerOptions (options, run, detach) { } if (options.health) { - if (options.health.command) { - args.push('--health-cmd', options.health.command) + if (!options.health.command) { + throw new TypeError('options.health.command must not be defined to use options.health') } + args.push('--health-cmd', options.health.command) + if (options.health.interval) { args.push('--health-interval', options.health.interval) } From 56febf5dd9eb096e69911d8ed6a49e3eef5245a9 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 02:35:21 +0200 Subject: [PATCH 27/49] Add: tests for options.health --- test/docker/docker.spec.js | 74 +++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/test/docker/docker.spec.js b/test/docker/docker.spec.js index d829790..22ae71e 100644 --- a/test/docker/docker.spec.js +++ b/test/docker/docker.spec.js @@ -60,6 +60,19 @@ describe('Docker', () => { expect(() => { processCreateContainerOptions({ image: 'test', exposePorts: [{ error: 'error' }] }) }).toThrow() }) + it('should throw error when options.health.command is not defined', () => { + expect(() => processCreateContainerOptions({ image: 'test', health: {} })) + .toThrow('options.health.command must not be defined to use options.health') + }) + + it('should throw error when options.health.retries is not a number', () => { + expect(() => processCreateContainerOptions({ image: 'test', health: { command: ['test'], retries: 1.2 } })) + .toThrow('options.health.retries must be an integer.') + + expect(() => processCreateContainerOptions({ image: 'test', health: { command: ['test'], retries: '1' } })) + .toThrow('options.health.retries must be an integer.') + }) + it('should throw an error when commands is not array', () => { expect(() => processCreateContainerOptions({ image: 'test', commands: 'first' })).toThrow() }) @@ -166,7 +179,7 @@ describe('Docker', () => { expect(arr.indexOf('-p') < arr.lastIndexOf('-p')).toBe(true) }) - it('should contains commands for container expose ports arguments', () => { + it('should contains commands for passing commands to docker container', () => { const arr = processCreateContainerOptions({ image: 'image-test', commands: ['command1', 'command2'], @@ -175,6 +188,65 @@ describe('Docker', () => { expect(arr).toContain('command1') expect(arr).toContain('command2') }) + + it('should contains commands for checking container health', () => { + const arr = processCreateContainerOptions({ image: 'test', health: { command: 'test-command' } }) + + expect(arr).toContain('--health-cmd') + expect(arr).toContain('test-command') + }) + + it('should contains commands for checking container health checks retries', () => { + const arr = processCreateContainerOptions({ + image: 'test', + health: { + command: 'test-command', + retries: 30, + }, + }) + + expect(arr).toContain('--health-retries') + expect(arr).toContain(30) + }) + + it('should contains commands for checking container health checks interval', () => { + const arr = processCreateContainerOptions({ + image: 'test', + health: { + command: 'test-command', + interval: '2s', + }, + }) + + expect(arr).toContain('--health-interval') + expect(arr).toContain('2s') + }) + + it('should contains commands for checking container health checks start period', () => { + const arr = processCreateContainerOptions({ + image: 'test', + health: { + command: 'test-command', + startPeriod: '2s', + }, + }) + + expect(arr).toContain('--health-start-period') + expect(arr).toContain('2s') + }) + + it('should contains commands for checking container health checks timeout', () => { + const arr = processCreateContainerOptions({ + image: 'test', + health: { + command: 'test-command', + timeout: '2s', + }, + }) + + expect(arr).toContain('--health-timeout') + expect(arr).toContain('2s') + }) }) }) From 208ddc2e128bcb8736682f2ad00a533fb08ae366 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 02:53:04 +0200 Subject: [PATCH 28/49] Fix: isHealthy --- src/docker/container.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docker/container.js b/src/docker/container.js index f51c0c2..cf02865 100644 --- a/src/docker/container.js +++ b/src/docker/container.js @@ -158,7 +158,7 @@ class Container { * @returns {Promise} if the continer is healthy or not */ isHealthy () { - if (!this.options.health.command) { + if (!this.options.health) { throw new Error('options.health.command must be defined to use IsHealthy') } From 5a8f949355a24f0bb9c9a1f3d5fbaabb861e9adf Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 02:53:15 +0200 Subject: [PATCH 29/49] Add: Tests --- test/docker/container.spec.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/docker/container.spec.js b/test/docker/container.spec.js index 0fdfce2..6d4250f 100644 --- a/test/docker/container.spec.js +++ b/test/docker/container.spec.js @@ -153,6 +153,20 @@ describe('Container', () => { }) }) + describe('#isHealthy', () => { + it('should return container health status', async () => { + const container = await CreateContainer({ image: 'hello-world', health: { command: 'test -r ./hello' } }, true) + + return expect(container.isHealthy()).resolves.toBeFalsy() + }) + + it('should throw error when health.command isn\'t defined', async () => { + const container = await CreateContainer({ image: 'hello-world' }, true) + + expect(() => container.isHealthy()).toThrow('options.health.command must be defined to use IsHealthy') + }) + }) + afterAll(() => { CleanTestCreateContainer('container') }) From 8b4d91635548e5f3dd2bdc78fe1a83ab6083587b Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 03:03:21 +0200 Subject: [PATCH 30/49] Add: isActive and isInstalled --- src/wp-cli/theme.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/wp-cli/theme.js b/src/wp-cli/theme.js index 0daa7c8..5e03e66 100644 --- a/src/wp-cli/theme.js +++ b/src/wp-cli/theme.js @@ -110,10 +110,28 @@ class Theme { return this.wpTheme(installArgs) } - isActive () { + /** + * Checks if a given theme is active. + * + * @param {string} theme - The theme to check. + * @returns {Promise} Whether theme is active + */ + isActive (theme) { + return this.wpTheme(['is-active', theme]) + .then(() => true) + .catch(() => Promise.resolve(false)) } - isInstalled () { + /** + * Checks if a given theme is installed. + * + * @param {string} theme - The theme to check. + * @returns {Promise} Whether theme is installed + */ + isInstalled (theme) { + return this.wpTheme(['is-installed ', theme]) + .then(() => true) + .catch(() => Promise.resolve(false)) } list () { From d6c9c574261ebd3b323ded230df75cdd8afb3e2c Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 10:01:01 +0200 Subject: [PATCH 31/49] Add: list command --- src/wp-cli/theme.js | 20 +++++++++++++++++++- src/wp-cli/types.js | 13 +++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/wp-cli/theme.js b/src/wp-cli/theme.js index 5e03e66..63b3de4 100644 --- a/src/wp-cli/theme.js +++ b/src/wp-cli/theme.js @@ -134,7 +134,25 @@ class Theme { .catch(() => Promise.resolve(false)) } - list () { + /** + * Return list of themes installed in the wordpress site and there data. + * + * @param {ThemeListFiltersObject} [filters] - Filter results based on the value of a field. + * @returns {Promise} - List of themes installed in the wordpress site. + */ + list (filters) { + const listArgs = [ + 'list', + '--fields=name,status,update,version,update_version,update_package,update_id,title,description', + '--format=json', + ] + + for (const filtersField in filters) { + listArgs.push(`--${filtersField}=${filters[filtersField]}`) + } + + return this.wpTheme(listArgs) + .then((output) => JSON.parse(output.stdout)) } path () { diff --git a/src/wp-cli/types.js b/src/wp-cli/types.js index a3e29b9..c7bd661 100644 --- a/src/wp-cli/types.js +++ b/src/wp-cli/types.js @@ -16,3 +16,16 @@ * @property {string} theme_root - path to theme root directory. * @property {string} theme_root_uri - uri to theme root directory. */ + +/** + * @typedef ThemeListFiltersObject + * @property {string} name - Name of the theme. + * @property {'active'|'inactive'} status - If the theme is active or inactive. + * @property {'none'|'available'} update - when + * @property {string} version - current version of the theme. + * @property {string} update_version - what version the plugin can update to. + * @property {string} update_package - link to update theme. + * @property {string} update_id - the theme id. + * @property {string} title - the theme title. + * @property {string} description - The theme description. + */ From 6a1b53b20046bf5c6b6d49c6d5ce953afd7747c8 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 15:18:07 +0200 Subject: [PATCH 32/49] Add: exec to container --- src/docker/container.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/docker/container.js b/src/docker/container.js index cf02865..8d812c1 100644 --- a/src/docker/container.js +++ b/src/docker/container.js @@ -165,6 +165,26 @@ class Container { return this.inspect('{{.State.Health.Status}}') .then(status => 'healthy' === status) } + + /** + * Run commands in a running container. + * + * @param {Array} commands - Commands to run. + * @returns {Promise} Return the current container. + */ + exec (commands) { + const execArgs = ['container', 'exec'] + + execArgs.push(this.options.id) + + execArgs.push.apply(execArgs, commands) + + const inspect = spawn('docker', execArgs) + + return ReturnPromise(inspect, () => { + return this + }) + } } module.exports = Container From 46b9c6327aea3430d0f58872b38dbd221876c1bf Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 19:18:24 +0200 Subject: [PATCH 33/49] Update: Checks for exec --- src/docker/container.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/docker/container.js b/src/docker/container.js index 8d812c1..718c326 100644 --- a/src/docker/container.js +++ b/src/docker/container.js @@ -173,15 +173,19 @@ class Container { * @returns {Promise} Return the current container. */ exec (commands) { + if (!Array.isArray(commands)) { + throw new Error('commands must be an array') + } + const execArgs = ['container', 'exec'] execArgs.push(this.options.id) execArgs.push.apply(execArgs, commands) - const inspect = spawn('docker', execArgs) + const exec = spawn('docker', execArgs) - return ReturnPromise(inspect, () => { + return ReturnPromise(exec, () => { return this }) } From 8f858acc1115ab7f77cf9c06165713b75783ee6f Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 19:21:12 +0200 Subject: [PATCH 34/49] Fix: mysql health checks --- src/docker/presets/containers.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/docker/presets/containers.js b/src/docker/presets/containers.js index 2fa5c50..68d074d 100644 --- a/src/docker/presets/containers.js +++ b/src/docker/presets/containers.js @@ -23,7 +23,7 @@ function CreateMysqlContainer (name, port, run = false) { { name: 'MYSQL_ROOT_PASSWORD', value: 'cywp' }, ], health: { - command: 'mysqladmin ping --silent', // eslint-disable-line spellcheck/spell-checker + command: 'mysqladmin ping -u root -p$MYSQL_ROOT_PASSWORD | grep \'mysqld is alive\'', // eslint-disable-line spellcheck/spell-checker startPeriod: '5s', retries: 30, interval: '1s', @@ -52,6 +52,7 @@ function CreateWordpressContainer (name, port, mysqlContainer, run = false) { environmentVariables: [ { name: 'WORDPRESS_DB_HOST', value: `${mysqlContainer.options.name}:${mysqlContainer.options.exposePorts[0].host}` }, { name: 'WORDPRESS_DB_PASSWORD', value: 'cywp' }, + { name: 'WORDPRESS_DB_USER', value: 'root' }, { name: 'WORDPRESS_DB_NAME', value: `cywp-${name}-db` }, ], volumes: [ @@ -64,7 +65,7 @@ function CreateWordpressContainer (name, port, mysqlContainer, run = false) { name: `cywp-${name}-wordpress`, health: { command: 'test -r wp-includes/version.php', - startPeriod: '3s', + startPeriod: '1s', interval: '1s', retries: 30, }, From 944b9b1ae54d25d2d297ce166ad3caea0a9ad952 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 19:21:42 +0200 Subject: [PATCH 35/49] Add: function to Setup Sites --- src/workflow/environment.js | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/workflow/environment.js b/src/workflow/environment.js index 398b467..537abd3 100644 --- a/src/workflow/environment.js +++ b/src/workflow/environment.js @@ -1,5 +1,6 @@ const { Docker } = require('../docker/docker') -const { CreateMysqlContainer } = require('../docker/presets/containers') +const { CreateMysqlContainer, CreateWordpressContainer } = require('../docker/presets/containers') +const { InitSite } = require('./sites-management') const { sleep } = require('../docker/util') const docker = new Docker() @@ -20,11 +21,11 @@ function setupNetwork () { * @returns {import('../docker/container')} return when mysql database ready. * * @example - * function foo() { + * async function foo () { * const mysql = await setupDatabase(3306); * } */ -async function setupDatabase (port) { +async function SetupDatabase (port) { const mysql = await CreateMysqlContainer('main', port, true) let mysqlReady = false @@ -38,4 +39,31 @@ async function setupDatabase (port) { return mysql } -module.exports = { setupNetwork, setupDatabase } +/** + * @param {string} name - + * @param {number} port - + * @param {import('../docker/container')} mysql - + * @returns {import('../docker/container')} Wordpress Container. + */ +async function SetupSite (name, port, mysql) { + const wordpress = await CreateWordpressContainer(name, port, mysql, true) + + const dbName = wordpress.options.environmentVariables.find(env => 'WORDPRESS_DB_NAME' === env.name).value + const mysqlPassword = mysql.options.environmentVariables.find(env => 'MYSQL_ROOT_PASSWORD' === env.name).value + + await mysql.exec(['mysql', '-u', 'root', `-p${mysqlPassword}`, '-e', `CREATE DATABASE IF NOT EXISTS \`${dbName}\``]) + + let wordpressReady = false + + while (!wordpressReady) { + if (await wordpress.isHealthy()) { wordpressReady = true } + + await sleep(1000) + } + + await InitSite(wordpress) + + return wordpress +} + +module.exports = { setupNetwork, SetupDatabase, SetupSite } From 7abbae07978187f9f36db759d2d28d32a8f1ab66 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Sun, 1 Nov 2020 19:37:03 +0200 Subject: [PATCH 36/49] Add: theme.path --- src/wp-cli/theme.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/wp-cli/theme.js b/src/wp-cli/theme.js index 63b3de4..d17eb9a 100644 --- a/src/wp-cli/theme.js +++ b/src/wp-cli/theme.js @@ -155,13 +155,18 @@ class Theme { .then((output) => JSON.parse(output.stdout)) } - path () { - } + /** + * Gets the path to a theme or to the theme directory. + * + * @param {string} theme - The theme to get the path to. + * @returns {Promise} Path to a theme or to the theme directory. + */ + path (theme) { + const pathArgs = ['path'] - search () { - } + if (theme) { pathArgs.push('--dir', theme) } - status () { + return this.wpTheme(pathArgs) } } From ec235ddd192b04042735d93fdbf2e59ce35949ed Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Mon, 2 Nov 2020 15:55:44 +0200 Subject: [PATCH 37/49] Add: stdout to ReturnPromise callback --- src/docker/util.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/docker/util.js b/src/docker/util.js index 7df2629..df2685b 100644 --- a/src/docker/util.js +++ b/src/docker/util.js @@ -2,7 +2,7 @@ * Return the child process as promise. * * @param {import('child_process').ChildProcessWithoutNullStreams} process - the child process that's running. - * @param {([stderr]: string) => any} callback - callback that deterred what to return when the process is successful. + * @param {(stdout: string, stderr: string) => any} callback - callback that deterred what to return when the process is successful. * @returns {Promise} return what said to return form the callback */ function ReturnPromise (process, callback) { @@ -11,11 +11,16 @@ function ReturnPromise (process, callback) { } let stderr = '' + let stdout = '' process.stderr.on('data', (data) => { stderr += data }) + process.stdout.on('data', (data) => { + stdout += data + }) + return new Promise((resolve, reject) => { process.on('close', (code) => { if (code) { @@ -23,7 +28,7 @@ function ReturnPromise (process, callback) { return } - resolve(callback(stderr)) + resolve(callback(stdout, stderr)) }) }) } From f45ea4a84c46ab4f60be7d132d4324c8f8150ac1 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Mon, 2 Nov 2020 16:00:34 +0200 Subject: [PATCH 38/49] Update: docker to use ReturnPromise --- src/docker/docker.js | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/src/docker/docker.js b/src/docker/docker.js index 41cb05c..b8325c8 100644 --- a/src/docker/docker.js +++ b/src/docker/docker.js @@ -15,17 +15,11 @@ class Docker { * @returns {Promise} Return promise for continer object */ CreateContainer (options, run = false, detach = true) { - let stdout = '' - const args = processCreateContainerOptions(options, run, detach) const process = spawn('docker', args) - process.stdout.on('data', (data) => { - stdout += data - }) - - return ReturnPromise(process, () => { + return ReturnPromise(process, (stdout) => { options.id = stdout.replace('\n', '') options.status = run ? 'started' : 'created' @@ -50,17 +44,11 @@ class Docker { throw new TypeError('options.rm must be true to use RunInContainer. (we don\'t want to leave garbage aroud)') } - let stdout = '' - const args = processCreateContainerOptions(options, true, false) const process = spawn('docker', args) - process.stdout.on('data', (data) => { - stdout += data - }) - - return ReturnPromise(process, (stderr) => { + return ReturnPromise(process, (stdout, stderr) => { return { stdout: stdout, stderr: stderr } }) } @@ -72,15 +60,9 @@ class Docker { * @returns {Promise} return promise for volume object. */ CreateVolume (name) { - let stdout = '' - const process = spawn('docker', ['volume', 'create', name]) - process.stdout.on('data', (data) => { - stdout += data - }) - - return ReturnPromise(process, () => { + return ReturnPromise(process, (stdout) => { const options = {} options.name = stdout.replace('\n', '') options.status = 'alive' @@ -96,8 +78,6 @@ class Docker { * @returns {Promise} return promise for network object. */ CreateNetwork (options) { - let stdout = '' - if ('string' === typeof options) { options = { name: options } } @@ -106,11 +86,7 @@ class Docker { const process = spawn('docker', args) - process.stdout.on('data', (data) => { - stdout += data - }) - - return ReturnPromise(process, () => { + return ReturnPromise(process, (stdout) => { options.id = stdout.replace('\n', '') options.status = 'alive' From eea88d7aa96435b0402ac451f95d40f068b5d1fb Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Tue, 3 Nov 2020 11:34:48 +0200 Subject: [PATCH 39/49] Update ReturnPromise --- src/docker/container.js | 35 +++++++---------------------------- src/docker/network.js | 8 +------- 2 files changed, 8 insertions(+), 35 deletions(-) diff --git a/src/docker/container.js b/src/docker/container.js index 718c326..d6d07c1 100644 --- a/src/docker/container.js +++ b/src/docker/container.js @@ -81,8 +81,6 @@ class Container { */ logs (options = {}) { const logsArgs = ['container', 'logs'] - let stdout = '' - let stderr = '' if (options.since) { logsArgs.push('--since', options.since) } if (options.tail) { logsArgs.push('--tail', options.tail) } @@ -92,26 +90,12 @@ class Container { const logs = spawn('docker', logsArgs) - logs.stdout.on('data', (data) => { - stdout += data - }) - - logs.stderr.on('data', (data) => { - stderr += data - }) - - return new Promise((resolve, reject) => { - logs.on('close', (code) => { - if (code) { - reject(stderr) - return - } - resolve({ - stdout: stdout, - stderr: stderr, - container: this, - }) - }) + return ReturnPromise(logs, (stdout, stderr) => { + return { + stdout: stdout, + stderr: stderr, + container: this, + } }) } @@ -123,7 +107,6 @@ class Container { */ inspect (format) { const inspectArgs = ['container', 'inspect'] - let stdout = '' if (format) { inspectArgs.push('--format', format) } @@ -131,11 +114,7 @@ class Container { const inspect = spawn('docker', inspectArgs) - inspect.stdout.on('data', (data) => { - stdout += data - }) - - return ReturnPromise(inspect, () => { + return ReturnPromise(inspect, (stdout) => { return CleanInspect(stdout) }) } diff --git a/src/docker/network.js b/src/docker/network.js index 69cc5b4..0457b03 100644 --- a/src/docker/network.js +++ b/src/docker/network.js @@ -34,19 +34,13 @@ class Network { inspect (format) { const inspectArgs = ['network', 'inspect'] - let stdout = '' - if (format) { inspectArgs.push('--format', format) } inspectArgs.push(this.options.id) const inspect = spawn('docker', inspectArgs) - inspect.stdout.on('data', (data) => { - stdout += data - }) - - return ReturnPromise(inspect, () => { + return ReturnPromise(inspect, (stdout) => { return CleanInspect(stdout) }) } From 6d6b4e05ec2bc87b65b128fab9d47220ebf734d8 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Tue, 3 Nov 2020 13:47:04 +0200 Subject: [PATCH 40/49] Update: Using dockerContainer --- src/docker/container.js | 52 +++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/docker/container.js b/src/docker/container.js index d6d07c1..6616b13 100644 --- a/src/docker/container.js +++ b/src/docker/container.js @@ -12,15 +12,28 @@ class Container { this.options = options } + /** + * @param {string[]} args - docker container arguments + * @param {(stdout: string, stderr: string) => any} callback - callback that deterred what to return when the process is successful. + * @returns {Promise} return what said to return form the callback + */ + dockerContainer (args, callback) { + args = ['container'].concat(args) + + const container = spawn('docker', args) + + return ReturnPromise(container, callback) + } + /** * Start the container. * * @returns {Promise} Return the current container. */ start () { - const start = spawn('docker', ['container', 'start', this.options.id]) + const startArgs = ['start', this.options.id] - return ReturnPromise(start, () => { + return this.dockerContainer(startArgs, () => { this.options.status = 'started' return this }) @@ -34,16 +47,14 @@ class Container { * @returns {Promise} Return the current container. */ rm (force = false, volumes = true) { - const rmArgs = ['container', 'rm'] + const rmArgs = ['rm'] if (force) { rmArgs.push('--force') } if (volumes) { rmArgs.push('--volumes') } rmArgs.push(this.options.id) - const rm = spawn('docker', rmArgs) - - return ReturnPromise(rm, () => { + return this.dockerContainer(rmArgs, () => { this.options.status = 'removed' return this }) @@ -56,15 +67,16 @@ class Container { * @returns {Promise} Return the current container. */ stop (time = 10) { - const stopArgs = ['container', 'stop'] + const stopArgs = ['stop'] - stopArgs.push('--time', time) + if (0 > time) { + throw new Error('time must be bigger then 0') + } + stopArgs.push('--time', time) stopArgs.push(this.options.id) - const stop = spawn('docker', stopArgs) - - return ReturnPromise(stop, () => { + return this.dockerContainer(stopArgs, () => { this.options.status = 'stopped' return this }) @@ -80,7 +92,7 @@ class Container { * @returns {Promise<{stdout: string, stderr: string, container: Container}}>} Return Promise for container logs. */ logs (options = {}) { - const logsArgs = ['container', 'logs'] + const logsArgs = ['logs'] if (options.since) { logsArgs.push('--since', options.since) } if (options.tail) { logsArgs.push('--tail', options.tail) } @@ -88,9 +100,7 @@ class Container { logsArgs.push(this.options.id) - const logs = spawn('docker', logsArgs) - - return ReturnPromise(logs, (stdout, stderr) => { + return this.dockerContainer(logsArgs, (stdout, stderr) => { return { stdout: stdout, stderr: stderr, @@ -106,15 +116,13 @@ class Container { * @returns {Promise} container info. */ inspect (format) { - const inspectArgs = ['container', 'inspect'] + const inspectArgs = ['inspect'] if (format) { inspectArgs.push('--format', format) } inspectArgs.push(this.options.id) - const inspect = spawn('docker', inspectArgs) - - return ReturnPromise(inspect, (stdout) => { + return this.dockerContainer(inspectArgs, (stdout) => { return CleanInspect(stdout) }) } @@ -156,15 +164,13 @@ class Container { throw new Error('commands must be an array') } - const execArgs = ['container', 'exec'] + const execArgs = ['exec'] execArgs.push(this.options.id) execArgs.push.apply(execArgs, commands) - const exec = spawn('docker', execArgs) - - return ReturnPromise(exec, () => { + return this.dockerContainer(execArgs, () => { return this }) } From 9a7490e6e10552fbae1ca5706b714805bea3b0e8 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Tue, 3 Nov 2020 13:47:29 +0200 Subject: [PATCH 41/49] Update: docs --- test/docker/container.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/docker/container.spec.js b/test/docker/container.spec.js index 6d4250f..1a89cf9 100644 --- a/test/docker/container.spec.js +++ b/test/docker/container.spec.js @@ -153,7 +153,7 @@ describe('Container', () => { }) }) - describe('#isHealthy', () => { + describe('#isHealthy()', () => { it('should return container health status', async () => { const container = await CreateContainer({ image: 'hello-world', health: { command: 'test -r ./hello' } }, true) From 4834d41877f07f7394cc375edadba37c0c4f4876 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Tue, 3 Nov 2020 15:49:58 +0200 Subject: [PATCH 42/49] Add: Tests --- test/docker/container.spec.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/docker/container.spec.js b/test/docker/container.spec.js index 1a89cf9..3051383 100644 --- a/test/docker/container.spec.js +++ b/test/docker/container.spec.js @@ -58,6 +58,12 @@ describe('Container', () => { return expect(container.stop()).resolves.toMatchObject({ options: { status: 'stopped' } }) }) + + it('should throw error when time smaller then 0', async () => { + const container = await CreateContainer({ image: 'hello-world' }, true) + + return expect(() => container.stop(-1)).toThrow('time must be bigger then 0') + }) }) describe('#logs()', () => { @@ -167,6 +173,25 @@ describe('Container', () => { }) }) + describe('#exec()', () => { + it('should have args to execute commands in docker', async () => { + const container = await CreateContainer({ image: 'hello-world' }, true) + + container.dockerContainer = jest.fn() + + container.exec(['command']) + + expect(container.dockerContainer.mock.calls[0][0]).toEqual(['exec', container.options.id, 'command']) + expect(container.dockerContainer.mock.calls[0][1]()).toBe(container) + }) + + it('should throw error if commands in not an array', async () => { + const container = await CreateContainer({ image: 'hello-world' }, true) + + expect(() => container.exec('command')).toThrow() + }) + }) + afterAll(() => { CleanTestCreateContainer('container') }) From 1ae8fb8473c0de7ab7912e55f08fb94c82830e86 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Tue, 3 Nov 2020 21:32:59 +0200 Subject: [PATCH 43/49] Fix: isInstalled --- src/wp-cli/theme.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-cli/theme.js b/src/wp-cli/theme.js index d17eb9a..25d01ec 100644 --- a/src/wp-cli/theme.js +++ b/src/wp-cli/theme.js @@ -129,7 +129,7 @@ class Theme { * @returns {Promise} Whether theme is installed */ isInstalled (theme) { - return this.wpTheme(['is-installed ', theme]) + return this.wpTheme(['is-installed', theme]) .then(() => true) .catch(() => Promise.resolve(false)) } From b29e653e5fbbfc62ad9c2bcc86a1395369f6c4d9 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Tue, 3 Nov 2020 21:34:00 +0200 Subject: [PATCH 44/49] Fix: list --- src/wp-cli/theme.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-cli/theme.js b/src/wp-cli/theme.js index 25d01ec..eb874cd 100644 --- a/src/wp-cli/theme.js +++ b/src/wp-cli/theme.js @@ -140,7 +140,7 @@ class Theme { * @param {ThemeListFiltersObject} [filters] - Filter results based on the value of a field. * @returns {Promise} - List of themes installed in the wordpress site. */ - list (filters) { + list (filters = []) { const listArgs = [ 'list', '--fields=name,status,update,version,update_version,update_package,update_id,title,description', From 2d0529dffd911f756cfb6d4840fdea6348ba433f Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Wed, 4 Nov 2020 09:33:30 +0200 Subject: [PATCH 45/49] Add: error if filter not object --- src/wp-cli/theme.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/wp-cli/theme.js b/src/wp-cli/theme.js index eb874cd..f907e5c 100644 --- a/src/wp-cli/theme.js +++ b/src/wp-cli/theme.js @@ -140,13 +140,17 @@ class Theme { * @param {ThemeListFiltersObject} [filters] - Filter results based on the value of a field. * @returns {Promise} - List of themes installed in the wordpress site. */ - list (filters = []) { + list (filters = {}) { const listArgs = [ 'list', '--fields=name,status,update,version,update_version,update_package,update_id,title,description', '--format=json', ] + if ('object' !== typeof filters) { + throw new TypeError('filters must be an object') + } + for (const filtersField in filters) { listArgs.push(`--${filtersField}=${filters[filtersField]}`) } From 609f30cb02b0ba0dc165bf38210cf86237fc1f38 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Wed, 4 Nov 2020 09:33:39 +0200 Subject: [PATCH 46/49] Add: Tests --- test/wp-cli/theme.spec.js | 81 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/test/wp-cli/theme.spec.js b/test/wp-cli/theme.spec.js index 2b46cce..80a93ff 100644 --- a/test/wp-cli/theme.spec.js +++ b/test/wp-cli/theme.spec.js @@ -122,4 +122,85 @@ describe('Theme', () => { .toThrow(new Error('To use version there must be at only one theme given.')) }) }) + + describe('#isActive()', () => { + it('should have args to check theme activation', () => { + theme.wpTheme = jest.fn(() => Promise.resolve()) + + theme.isActive('theme') + + expect(theme.wpTheme).toHaveBeenLastCalledWith(['is-active', 'theme']) + }) + + it('should return true on resolve', () => { + theme.wpTheme = jest.fn(() => Promise.resolve()) + + return expect(theme.isActive('theme')).resolves.toBe(true) + }) + + it('should return false on reject', () => { + theme.wpTheme = jest.fn(() => Promise.reject(new Error())) + + return expect(theme.isActive('theme')).resolves.toBe(false) + }) + }) + + describe('#isInstalled()', () => { + it('should have args to check theme installation', () => { + theme.wpTheme = jest.fn(() => Promise.resolve()) + + theme.isInstalled('theme') + + expect(theme.wpTheme).toHaveBeenLastCalledWith(['is-installed', 'theme']) + }) + + it('should return true on resolve', () => { + theme.wpTheme = jest.fn(() => Promise.resolve()) + + return expect(theme.isInstalled('theme')).resolves.toBe(true) + }) + + it('should return false on reject', () => { + theme.wpTheme = jest.fn(() => Promise.reject(new Error())) + + return expect(theme.isInstalled('theme')).resolves.toBe(false) + }) + }) + + describe('#list()', () => { + it('should throw error when filters is not object', () => { + expect(() => theme.list('test')).toThrow(new TypeError('filters must be an object')) + }) + + it('should have arguments for listing themes', () => { + theme.wpTheme = jest.fn((listArgs) => Promise.resolve(JSON.stringify(listArgs))) + + theme.list() + + expect(theme.wpTheme) + .toHaveBeenLastCalledWith([ + 'list', + '--fields=name,status,update,version,update_version,update_package,update_id,title,description', + '--format=json', + ]) + }) + + it('should have arguments for listing themes with filters', () => { + theme.wpTheme = jest.fn((listArgs) => Promise.resolve(JSON.stringify(listArgs))) + + theme.list({ + name: 'test', + status: 'active', + }) + + expect(theme.wpTheme) + .toHaveBeenLastCalledWith([ + 'list', + '--fields=name,status,update,version,update_version,update_package,update_id,title,description', + '--format=json', + '--name=test', + '--status=active', + ]) + }) + }) }) From 4a2257aaf1313b820f802b27caea3a40d1d2d269 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Wed, 4 Nov 2020 10:25:59 +0200 Subject: [PATCH 47/49] Update: Theme Tests --- test/wp-cli/theme.spec.js | 60 ++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/test/wp-cli/theme.spec.js b/test/wp-cli/theme.spec.js index 80a93ff..f8c4b10 100644 --- a/test/wp-cli/theme.spec.js +++ b/test/wp-cli/theme.spec.js @@ -6,26 +6,37 @@ jest.mock('../../src/docker/presets/containers') describe('Theme', () => { /** @type {Theme} */ let theme + let originalWpTheme beforeAll(async () => { theme = new Theme() + originalWpTheme = theme.wpTheme + theme.wpTheme = jest.fn() }) describe('#wpTheme()', () => { + beforeAll(() => { + theme.wpTheme = originalWpTheme + }) + it('should have the arguments wp theme', () => { theme.wpTheme([]) expect(CreateWordpressCliContainer) .toHaveBeenLastCalledWith(undefined, ['wp', 'theme']) }) + + afterAll(() => { + theme.wpTheme = jest.fn() + }) }) describe('#activate()', () => { it('should have the arguments to activate theme', () => { theme.activate('theme') - expect(CreateWordpressCliContainer) - .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'activate', 'theme']) + expect(theme.wpTheme) + .toHaveBeenLastCalledWith(['activate', 'theme']) }) }) @@ -33,29 +44,29 @@ describe('Theme', () => { it('should have the arguments to delete theme', () => { theme.delete('theme') - expect(CreateWordpressCliContainer) - .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'delete', 'theme']) + expect(theme.wpTheme) + .toHaveBeenLastCalledWith(['delete', 'theme']) }) it('should have the arguments to delete multiple themes', () => { theme.delete(['theme1', 'theme2']) - expect(CreateWordpressCliContainer) - .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'delete', 'theme1', 'theme2']) + expect(theme.wpTheme) + .toHaveBeenLastCalledWith(['delete', 'theme1', 'theme2']) }) it('should have the arguments to delete all themes', () => { theme.delete('all') - expect(CreateWordpressCliContainer) - .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'delete', '--all']) + expect(theme.wpTheme) + .toHaveBeenLastCalledWith(['delete', '--all']) }) it('should have the arguments to delete theme with force', () => { theme.delete('theme', true) - expect(CreateWordpressCliContainer) - .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'delete', '--force', 'theme']) + expect(theme.wpTheme) + .toHaveBeenLastCalledWith(['delete', '--force', 'theme']) }) it('should throw an error massage with theme in it', () => { @@ -64,11 +75,6 @@ describe('Theme', () => { }) describe('#get()', () => { - let originalWpTheme - beforeAll(() => { - originalWpTheme = theme.wpTheme - }) - it('should have the arguments to get theme info', () => { theme.wpTheme = jest.fn((commands) => Promise.resolve({ stdout: JSON.stringify(commands) })) @@ -77,32 +83,28 @@ describe('Theme', () => { expect(theme.wpTheme) .toHaveBeenLastCalledWith(['get', '--format=json', 'theme']) }) - - afterAll(() => { - theme.wpTheme = originalWpTheme - }) }) describe('#install()', () => { it('should have the arguments to install theme', () => { theme.install('theme') - expect(CreateWordpressCliContainer) - .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'install', '--force', 'theme']) + expect(theme.wpTheme) + .toHaveBeenLastCalledWith(['install', '--force', 'theme']) }) it('should have the arguments to install multiple themes', () => { theme.install(['theme1', 'theme2']) - expect(CreateWordpressCliContainer) - .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'install', '--force', 'theme1', 'theme2']) + expect(theme.wpTheme) + .toHaveBeenLastCalledWith(['install', '--force', 'theme1', 'theme2']) }) it('should have the arguments to install and activate theme', () => { theme.install('theme', true) - expect(CreateWordpressCliContainer) - .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'install', '--force', '--activate', 'theme']) + expect(theme.wpTheme) + .toHaveBeenLastCalledWith(['install', '--force', '--activate', 'theme']) }) it('should throw an error when trying to install an activate multiple themes', () => { @@ -113,8 +115,8 @@ describe('Theme', () => { it('should have the arguments to install different version theme', () => { theme.install('theme', false, 2.2) - expect(CreateWordpressCliContainer) - .toHaveBeenLastCalledWith(undefined, ['wp', 'theme', 'install', '--force', '--version=2.2', 'theme']) + expect(theme.wpTheme) + .toHaveBeenLastCalledWith(['install', '--force', '--version=2.2', 'theme']) }) it('should throw an error when trying to install an different version multiple themes', () => { @@ -173,7 +175,7 @@ describe('Theme', () => { }) it('should have arguments for listing themes', () => { - theme.wpTheme = jest.fn((listArgs) => Promise.resolve(JSON.stringify(listArgs))) + theme.wpTheme = jest.fn(() => Promise.resolve({ stdout: JSON.stringify('data') })) theme.list() @@ -186,7 +188,7 @@ describe('Theme', () => { }) it('should have arguments for listing themes with filters', () => { - theme.wpTheme = jest.fn((listArgs) => Promise.resolve(JSON.stringify(listArgs))) + theme.wpTheme = jest.fn(() => Promise.resolve({ stdout: JSON.stringify('data') })) theme.list({ name: 'test', From d8f3fa2eff717caebefca3ea055782603b608605 Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Wed, 4 Nov 2020 10:26:13 +0200 Subject: [PATCH 48/49] Add: Tests --- test/wp-cli/theme.spec.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/wp-cli/theme.spec.js b/test/wp-cli/theme.spec.js index f8c4b10..ce73e00 100644 --- a/test/wp-cli/theme.spec.js +++ b/test/wp-cli/theme.spec.js @@ -205,4 +205,20 @@ describe('Theme', () => { ]) }) }) + + describe('#path()', () => { + it('should have arguments to get theme parent directory', () => { + theme.path() + + expect(theme.wpTheme) + .toHaveBeenLastCalledWith(['path']) + }) + + it('should have arguments to get theme directory', () => { + theme.path('theme') + + expect(theme.wpTheme) + .toHaveBeenLastCalledWith(['path', '--dir', 'theme']) + }) + }) }) From 9e7ea7eab7fae262ffc97b40f5cc1ceb603a8d2b Mon Sep 17 00:00:00 2001 From: Omri Bar-Zik Date: Wed, 4 Nov 2020 11:29:02 +0200 Subject: [PATCH 49/49] Add: docs --- src/workflow/environment.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/workflow/environment.js b/src/workflow/environment.js index 537abd3..b686602 100644 --- a/src/workflow/environment.js +++ b/src/workflow/environment.js @@ -44,6 +44,12 @@ async function SetupDatabase (port) { * @param {number} port - * @param {import('../docker/container')} mysql - * @returns {import('../docker/container')} Wordpress Container. + * + * @example + * async function foo () { + * const mysql = await setupDatabase(3306); + * const wordpress = await SetupSite('my-site', 8000, mysql) + * } */ async function SetupSite (name, port, mysql) { const wordpress = await CreateWordpressContainer(name, port, mysql, true)