import { Head, Notes,Image } from 'mdx-deck' import { code,future, highlight, } from '@mdx-deck/themes' export const theme = { ...future, ...highlight,...code,...{ colors: { background:'rgb(1, 116, 162)' }, h1: { textTransform: 'uppercase', letterSpacing: '0.1em' }, styles: { h1: { textAlign: 'center', }, a: { color: 'white' } } } }
<title> The Modern WordPress Plugin Development Toolset (Part 1) </title>Josh Pollock | Plugin Machine
Josh Pollock | @josh412 | JoshPress.net
- PHP & JavaScript Engineer
- Working on Plugin Machine
- WordPress core contributor
- ex: 10up, Ninja Forms, Caldera Forms, Pods.
-
Plugin developers
-
Anyone who writes PHP and JavaScript for WordPress
-
What WordPress is made of
-
What we use to make WordPress
- Client (Browser)
- JavaScript/ CSS/ HTML
- Server
- PHP
- Database
- MySQL
- Used in block editor
- Can be used for:
- Admin pages
- Front-end
- Headless builds
- React is generally written in JSX, not JavaScript.
- Optimize for browser.
- Automated testing
- IDE
- Local Development Environment
- Dependency Management
- Automated Testing
- Compiling and Optimizing JS(X) and (SCSS)
- Code Quality
- CI/ CD
- Code editor plus other development tools.
- Isolation
- Don't want to break live site
- Common setup for all developers on a project
- Automated configuration
- Consitency
- Docker
- WordPress-specific GUI Apps
- Virtual Machine
- Runs anywhere
- Mac/ Windows/ Linux
- Github Actions
- vsCode dev containers
- In the cloud
- Config as code
- Infinetly extendable
version: "3.9"
services:
wordpress:
depends_on:
- wpdb
image: wordpress:latest
volumes:
- wordpress_data:/var/www/html
- ./:/var/www/html/wp-content/plugins/everything-all-of-the-time
ports:
- "6100:80"
restart: always
environment:
WORDPRESS_DB_HOST: wpdb:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
wpdb:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: wordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
version: "3.9"
services:
wordpress:
depends_on:
- wpdb
image: wordpress:latest
volumes:
- wordpress_data:/var/www/html
- ./:/var/www/html/wp-content/plugins/everything-all-of-the-time
ports:
- "6100:80"
restart: always
environment:
WORDPRESS_DB_HOST: wpdb:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
wpdb:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: wordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wpcli:
image: wordpress:cli
volumes:
- wordpress_data:/var/www/html
- ./:/var/www/html/wp-content/plugins/everything-all-of-the-time
- ./db:/var/www/html/db
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
ABSPATH: /usr/src/wordpress/
phpunit:
command:
- bash
depends_on:
- testwpdb
environment:
DATABASE_PASSWORD: examplepass
DATABASE_HOST: testwpdb
image: futureys/phpunit-wordpress-plugin
stdin_open: true
tty: true
volumes:
- ./:/plugin
testwpdb:
environment:
MYSQL_ROOT_PASSWORD: examplepass
image: mysql:5.7
volumes:
db_data: {}
wordpress_data: {}
- For PHP: Composer
- For JavaScript: npm or yarn
- Add packages to your plugin or site
- Use dependencies from packagist
- Install plugins or themes using wpackagist
- Run scripts
- PHP autoloader
- Install Composer
- For Windows, download installer.
- For Mac/ Linux, follow latest instructions
sudo mv composer.phar /usr/local/bin/composer
{
"name": "imaginary-machines/everything-all-of-the-time",
"description": "Example of Plugin Machine generated plugin with many features on.",
"type": "wordpress-plugin",
"require": {
"php": "^7.2|^8.0"
}
}
{
"autoload": {
"psr-4": {
"AllOfTheThings\\": "./php"
}
},
"autoload-dev": {
"psr-4": {
"AllOfTheThings\\Tests\\": "./tests"
}
}
}
- Add additional functionality
- For example: component library, API client, utility functions, etc.
- Compile code so it can run in the browser and optimize it
- Babel, webpack, etc.
- Run scripts
- Configure Packages
- npm is installed with node
- Install Node
- Install yarn:
npm install -g yarn
{
"name": "@imaginary-machines/everything-all-of-the-time",
"private": true,
"version": "0.1.0",
"license": "GPL-2.0-or-later",
"main": "build/index.js",
"scripts": {
"build": "wp-scripts build",
"lint:css": "wp-scripts lint-style",
"lint:js": "wp-scripts lint-js",
"start": "wp-scripts start"
},
"devDependencies": {
"@wordpress/scripts": "^16"
}
}
function add($one, $two){
return $one + $two;
}
class SampleTest extends \PHPUnit\Framework\TestCase
{
public function testAdd()
{
$this->assertEquals(
4, add(2,2)
);
}
- Guide development.
- Prevent regression errors.
- Makes clear how the code is supposed to work.
- Debugging errors.
- Profiling performance.
- Surfacing accessibility issues
- PHP test runner and assertions.
- Can be used with or without WordPress test suite.
{
"require-dev": {
"phpunit/phpunit": "^7.0",
"yoast/phpunit-polyfills": "^0.1.0",
"mockery/mockery": "1.2",
"brain/monkey": "2.*",
"squizlabs/php_codesniffer": "^3.6"
},
"scripts": {
"test:unit": "phpunit --config=phpunit-unit.xml",
"test:wordpress": "phpunit --config=phpunit-integration.xml"
}
}
class Url {
protected $baseUrl;
public function __construct($baseUrl)
public function getUrl($endpoint){
return sprintf("%s/%s",$this->baseUrl,$endpoint);
}
}
class UrlTest extends \PHPUnit\Framework\TestCase
{
public function testGetUrl()
{
$url = new Url('https://hiroy.club');
$this->assertEquals(
'https://hiroy.club/api',
$url->getUrl('/api')
);
}
class UrlTest extends \PHPUnit\Framework\TestCase
{
/**
* Test that we can mock WordPress functions
*
* @see https://giuseppe-mazzapica.gitbook.io/brain-monkey/functions-testing-tools/functions-when#justreturn
*/
public function testMockWordPressFunction(){
//A fake wp_insert_post() that always returns 1
Functions\when('wp_insert_post' )->justReturn(1);
$this->assertIsNumeric(
wp_insert_post([
'post_title' => 'Succulents',
'post_content' => 'lithops and echeveria'
])
);
$this->assertSame(1,wp_insert_post());
}
class SomethingTest extends \PHPUnit\Framework\TestCase
{
public function testInsertPost(){
$post_id = wp_insert_post([
'post_title' => 'Succulents',
'post_content' => 'lithops and echeveria'
]);
$this->assertIsNumeric($post_id);
}
function add($one, $two){
return $one + $two;
}
expect(add(2,2)).toBe(4);
- Compiles
- WordPress-safe.
- Works with
wp_register_script/style()
- Lints
- Runs tests
- Uses and configures Jest.
- Allows for alternative syntaxes
- Optimize JavaScript
- Optimize CSS
- React's template syntax
- Not required or blocks
- Worth using for sure.
- HTML and JavaScript together
//React component with JSX
function Alert({message}){
return (
<div>
<p className="alert">
{message}</p>
</div>
);
}
<Alert message="Hi Roy" />
Renders as:
<div>
<p class="alert">
Hi Roy
</p>
</div>
//React component with JSX
function Alert({message}){
return (
<div>
<p className="alert">
{message}</p>
</div>
);
}
Is the same as:
//React component without JSX
function Alert({message}){
return React.createElement( 'div', {},[
React.createElement( 'p', {
className: 'alert'
}, [ message ])
]);
}
- Use Jest, via WordPress Scripts
- Generally need a React testing library in adittion to Jest.
- I recommend @testing-library/react
- Slides For My JavaScript Testing Talk
{
"name": "@imaginary-machines/everything-all-of-the-time",
"private": true,
"version": "0.1.0",
"license": "GPL-2.0-or-later",
"main": "build/index.js",
"scripts": {
"test": "yarn test:unit",
"test:unit": "wp-scripts test-unit-js",
"build": "wp-scripts build",
"test:ci": "wp-scripts test-unit-js --ci",
"format:js": "wp-scripts format-js",
"lint:css": "wp-scripts lint-style",
"lint:js": "wp-scripts lint-js",
"start": "wp-scripts start"
},
"devDependencies": {
"@babel/core": "^7",
"@testing-library/react": "^12",
"@wordpress/scripts": "^16",
},
"dependencies": {
"@wordpress/block-editor": "^6",
"@wordpress/blocks": "^9",
"@wordpress/components": "^14",
"@wordpress/element": "^3",
"@wordpress/i18n": "^4"
}
}
//Import React
import React from 'react';
//Import test renderer
import { render, fireEvent, cleanup } from '@testing-library/react';
//Import component to test
import { Editor } from './Edit';
describe("Editor componet", () => {
afterEach(cleanup);
it('matches snapshot when selected', () => {
const onChange = jest.fn();
const { container } = render(<Editor
onChange={onChange}
value={'Tacos'}
isSelected="true"
/>);
expect(container).toMatchSnapshot();
});
it("Calls the onchange function", () => {
const onChange = jest.fn();
const { getByDisplayValue } = render(<Editor
onChange={onChange}
value={'Salad'}
isSelected="false"
/>);
fireEvent.change(getByDisplayValue("Salad"), {
target: { value: "New Value" }
});
expect(onChange).toHaveBeenCalledTimes(1);
});
});
- Tests check code by running it in various ways.
- Code quality analysis parses the code and detetchs likely issues.
- Linters
- Does code follow coding standards?
- Are bad smelling patterns used?
- Is the code using deprecated functions?
- Static Analysis
- Are variable types correct?
- Predicts compile-time errors.
- Linters
- Static Analysis
- CI: Continuous integration.
- Using automtion to continously test, analysis and merge changes to code.
- CD: Continous deployment.
- Using automation to continously deploy code.
name: PHP Unit Tests
on: push
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: [ 7.2, 7.3, 7.4 ]
steps:
- uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
# Install composer with cache
## See https://github.com/marketplace/actions/setup-php-action#cache-composer-dependencies
- name: Get composer cache directory
id: composercache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache dependencies
uses: actions/cache@v2
with:
path: ${{ steps.composercache.outputs.dir }}
key: php-${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: composer install --prefer-dist
# Run unit tests
- name : Unit Tests
run: composer test:unit
Josh Pollock | @josh412 | JoshPress.net