WPBrowser and WP-CLI integration 01

Trying to tap into the power of WP-CLI in WordPress tests.

The CLI for WordPress

WP-Cli is an amazing command line utility to manage WordPress.
Someone willing to trade WordPress administration UI for the speed and flexibility of a CLI tool can perform basic operations like bulk plugin or theme updates:

wp plugin update $(wp plugin list --update=available --field=name)
wp theme update $(wp theme list --update=available --field=name)

Activate and deactivate plugins and themes

wp plugin activate hello
wp plugin deactivate hello
wp theme activate twentysixteen
wp theme deactivate twentysixteen

and much much more.
Under the hood the script, once pointed to the root folder of a WordPress installation, will find its way around the WordPress installation to fetch the access credentials needed to connect to the database and perform operations.
I tried to think how this tool could help in testing WordPress using wp-browser.

A test example

Starting from the end I’ve tried to imagine a test where a “wp-cli-like” tool could prove useful and it took me little time.
Say I’m writing a functional test using the WordPress functional module provided by wp-browser and I want to test a plugin activation routine.
Specifically I want to test that some defaults will be set when the plugin is first activated; I’ve already put in place integration tests using the WPLoader module and while I know that the activation routine will work on an integration level I’d like to test it on a functional one.
I will start with a cept test

<?php
$I = new FunctionalTester($scenario);

$I->am('the admin user');
$I->wantTo('activate the plugin');
$I->lookForwardTo('see some default options set');

$option = 'acme_option';
$defaults = [
    'foo' => 'bar',
    'baz' => 23
];

// our starting database dump for this test has no plugin active
// let's make sure that the option is not set 
$I->doNotSeeOptionInDatabase(['option_name' => $option]);

// now let's activate the plugin
$I->cli('plugin activate acme');

// let's make sure the option is now in place
$I->seeOptionInDatabase(['option_name' => $option, 'option_value' => $defaults]);

A plugin activation might imply costly one-time operations that cannot be “mocked” by simply overriding the active_plugins option and requires someone capable to activate the plugin to do so; the long, cli-access-less version of the same test is this:

<?php
$I = new FunctionalTester($scenario);

$I->am('the admin user');
$I->wantTo('activate the plugin');
$I->lookForwardTo('see some default options set');

$option = 'acme_option';
$defaults = [
    'foo' => 'bar',
    'baz' => 23
];


// our starting database dump for this test has no plugin active
// let's make sure that the option is not set 
$I->doNotSeeOptionInDatabase(['option_name' => $option]);

// now let's activate the plugin
$I->loginAsAdmin();
$I->amOnAdminPage('/plugins.php');
$I->click('tr.inactive[data-slug="acme" .activate a]');

// let's make sure the option is now in place
$I->seeOptionInDatabase(['option_name' => $option, 'option_value' => $defaults]);

The difference is not that much in such a small test but the second one has got an inherent weakness and overhead: what I want to test is not my ability to access the admin area or activate a plugin but if the plugin activation will behave as expected in the context of a full blown website.
Many plugins will then put in place redirects and side effects on activation that could make a test like the second one fail for the wrong reasons like not finding a link or the test user not being where I assume it should be.
To iterate say I want to test the same scenario again but taking another plugin into account:

$I = new FunctionalTester($scenario);

$I->am('the admin user');
$I->wantTo('activate the plugin when plugin another-plugin is already installed and active');
$I->lookForwardTo('see some default options set taking another-plugin plugin presence into account');

$option = 'acme_option';
$defaults = [
    'foo' => 'bar',
    // for some reason if another-plugin is active `baz` should be 13 and not 23
    'baz' => 23,
];

// our starting database dump for this test has no plugin active
// let's make sure that the option is not set 
$I->doNotSeeOptionInDatabase(['option_name' => $option]);

// now let's activate the plugin
$I->cli('plugin activate another-plugin');
$I->cli('plugin activate acme');

// let's make sure the option is now in place and that
$I->seeOptionInDatabase(['option_name' => $option, 'option_value' => $defaults]);

While I control the code of the acme plugin I do not control the code of another-plugin and the CLI based access isolates my test from redirects and manhandling another-plugin could have put in place.

Status

I’m working on the module now and while the integration per se is not that difficult to put in place the potential benefits are immense.