Developing a WordPress plugin from scratch with Codeception and wp-browser.
Gattiny
The plugin name is a brilliant idea of an italian colleague mixing the Italian translation of the word “kittens” (“gattini”) with a touch of Englishness replacing the trailing “i” with an “y”.
The purpose of this plugin will be to resize animated GIF images during upload preserving animations and creating different sized versions for each applicable image size.
As any idea of mine it could crash and burn; it provides, though, an excellent playing ground for test-driven development and I simply cannot let the chance slip.
Setting up for testing
Starting from scratch I’ve created the plugin folder in my repositories folder and initialized git
:
cd ~/Repos
mkdir gattiny
git init
As customary in my development flow I’m symbolically linking the plugin folder in the WordPress installation folder:
ln -s ~/Repos/gattiny ~/Sites/wp/wp-content/plugins/
After that I init Composer answering the following questions and ending up with this file:
composer init
I’m then putting in place the plugin main file, nothing but WordPress required comment block so far, to allow WordPress to pick it up as an available plugin.
How does it work now?
Before I write any code I’m going to test current WordPress handling and support of animated GIFs to look for any shortcomings.
WordPress will create a version of the uploaded images only if the Imagick or gd are installed and preferes the former.
Since WordPress itself will not provide support for image resizing on upload without the extensions installed, I’m assuming the plugin should work when the Imagick extension is activated.
The current state of support provided by WordPress to animated GIFs is not to modify the original image but to create resized static versions of it when scaling; any version of the uploaded GIF created by WordPress will hence be a resized, though not animated, version of the first image in the GIF stack.
Asserting the obvioius
The series is about test-driven development and as such I will try to write applicable tests before I write any code.
My usual pick for a first test is an acceptance one, but this specific case requires some thinking: what am I going to test first?
- when uploading a GIF animated image a resized version of it should be created in the WordPress uploads folder
- when uploading a GIF animated image each resized version should still be an animated image
looking at the wording of the two sentences above it’s easy to spot how the tests, to assert their conditions, must know and check on the functionality implementation details. This is a white-box approach to the problem that deviates from the realm of acceptance testing.
My first test will then be a functional one.
Bootstrapping the tests
Once again the quickest way to get up and running with the tests is to use wp-browser and its interactive bootstrap command:
composer require --dev lucatume/wp-browser
wpcept bootstrap --interactive
Note: prepend
vendor/bin
to your path to avoid having to type./vendor/bin/wpcept
every time.
Functional tests, as well as acceptance tests, require a starting database fixture to be set up; that database fixture will be:
- a clean and empty WordPress installation
- the Gattiny plugin activated
- no other plugin or theme active on the site (save for the default theme)
To set up the database fixture I’m using wp-cli from the local WordPress installation folder (/home/luca/Sites/wp
in my case):
wp site empty --yes
wp plugin deactivate $(wp plugin list --status=active --field=name)
wp plugin activate gattiny
wp db export ~/Repos/gattiny/tests/_data/dump.sql
Since the functional
suite has been configured during the interactive bootstrap process, I’m now able to run the empty suite successfully using wpcept run functional
[caption id=“attachment_3387” align=“aligncenter” width=“717”] Gattiny functional testing - dry run[/caption]
Time to write the first test.
Scaffolding
When writing functional tests I usually use Codeception cest
format to re-use commong _before
and _after
statements in each test method:
wpcept generate:cest functional FormatCreation
After some tweaking, this is the first version of the FormatCreationCest.php
file; it’s doing nothing but asserting the obvious here:
<?php
class FormatCreationCest {
protected $gif = 'images/kitten-animated.gif';
protected $jpg = 'images/kitten-image.jpg';
protected $uploads;
protected $sizes = [
'thumbnail' => '150x150',
'medium' => '300x169',
'medium_large' => '768x432',
'large' => '1024x576',
'full' => ''
];
public function _before( FunctionalTester $I ) {
$config = \Codeception\Configuration::config();
$this->uploads = $config['folders']['uploads'] . '/' . date( 'Y/m' );
$I->useTheme( 'empty' );
}
public function _after( FunctionalTester $I ) {
$I->deleteDir( $this->uploads );
}
/**
* It should create a version of the gif for each size
* @test
*/
public function create_a_version_of_the_gif_for_each_size( FunctionalTester $I ) {
$I->loginAsAdmin();
$I->amOnAdminPage( 'media-new.php' );
$I->attachFile( 'input[name="async-upload"]', $this->gif );
$I->click( 'input[name="html-upload"]' );
$I->seeResponseCodeIs( 200 );
$I->amInPath( $this->uploads );
foreach ( $this->sizes as $key => $size ) {
$suffix = '' !== $size ? '-' . $size : '';
$I->seeFileFound( basename( $this->gif, '.gif' ) . $suffix . '.gif' );
}
}
}
The test code requires some explanation to understand what is happening.
I’m using the WPBrowser
module in place of the WordPress
one to submit the file upload requests successfully; I’ve changed the functional
suite configuration to use it along with the Filesystem
module and the WPDb
one:
# Codeception Test Suite Configuration
# Suite for WordPress functional tests.
# Emulate web requests and make the WordPress application process them.
class_name: FunctionalTester
modules:
enabled:
- \Helper\Functional
- Filesystem
- WPDb
- WPBrowser
config:
WPDb:
dsn: 'mysql:host=localhost;dbname=wp'
user: root
password: root
dump: tests/_data/dump.sql
populate: true
cleanup: true
url: 'http://wp.dev'
tablePrefix: wp_
WPBrowser:
url: 'http://wp.dev'
adminUsername: admin
adminPassword: admin
adminPath: /wp-admin
In the _before
method I’m setting the theme to empty
; the empty
theme lives in the _data/empty-theme
folder and is, as the name implies, nothing but an empty theme.
The purpose here is to use a theme I control and that’s not modifying WordPress default image sizes in numbers and quality in any way for the tests.
Using the Copier extension the theme will be copied into the WordPress installation themes
folder before the tests and removed afterward.
actor: Tester
paths:
tests: tests
log: tests/_output
data: tests/_data
helpers: tests/_support
settings:
bootstrap: _bootstrap.php
colors: true
memory_limit: 1024M
extensions:
enabled:
- tad\WPBrowser\Extension\Copier
config:
tad\WPBrowser\Extension\Copier:
files:
tests/_data/empty-theme: /home/luca/Sites/wp/wp-content/themes/empty-theme
folders:
uploads: /home/luca/Sites/wp/wp-content/uploads
Lastly I’m providing the absolute path to the WordPress local installation root uploads folder in the configuration under the folders
key; this is an handy trick offered by Codeception configuration that allows me to access that information using a service locator in my tests as I’m doing in the lines:
$config = \Codeception\Configuration::config();
$this->uploads = $config['folders']['uploads'] . '/' . date( 'Y/m' );
Running the only test results in a success:
[caption id=“attachment_3388” align=“aligncenter” width=“874”] Gattiny functional testing - first green[/caption]
Next
The code I’ve shown here is on GitHub tagged post-01
for anyone to see.
Next step will be writing the second functional test and get down to work.