Skip to content

Latest commit

 

History

History
865 lines (644 loc) · 16.6 KB

index.mdx

File metadata and controls

865 lines (644 loc) · 16.6 KB

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>

The Modern WordPress Plugin Development Toolset

(Part 1)

Josh Pollock | Plugin Machine


About Me

Josh Pollock | @josh412 | JoshPress.net

  • PHP & JavaScript Engineer
  • Working on Plugin Machine
  • WordPress core contributor
  • ex: 10up, Ninja Forms, Caldera Forms, Pods.

Who This Is For

  • Plugin developers

  • Anyone who writes PHP and JavaScript for WordPress

  • View Slides


What We Will Cover

  • What WordPress is made of

  • What we use to make WordPress

  • View Slides

Create speaker notes with the Notes component

Slides And Code


The WordPress Stack

  • Client (Browser)
    • JavaScript/ CSS/ HTML
  • Server
    • PHP
  • Database
    • MySQL

React In WordPress

  • Used in block editor
  • Can be used for:
    • Admin pages
    • Front-end
    • Headless builds

Why Developer Tooling For React?

  • React is generally written in JSX, not JavaScript.
  • Optimize for browser.
  • Automated testing

Developer Tooling

  • IDE
  • Local Development Environment
  • Dependency Management
  • Automated Testing
  • Compiling and Optimizing JS(X) and (SCSS)
  • Code Quality
  • CI/ CD

IDE

Integrated Developer Environment

  • Code editor plus other development tools.

IDE

Some Good Ones


Local Development

Why

  • Isolation
    • Don't want to break live site
  • Common setup for all developers on a project
    • Automated configuration
    • Consitency

Local Development

Solutions


Local Development

My Recommendation: Docker Compose

  • Runs anywhere
    • Mac/ Windows/ Linux
    • Github Actions
    • vsCode dev containers
    • In the cloud
  • Config as code
  • Infinetly extendable

Docker Compose

Basic

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

Docker Compose

With WPCLI And Tests

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: {}

Dependency Management

  • For PHP: Composer
  • For JavaScript: npm or yarn

Composer

  • Add packages to your plugin or site
  • Run scripts
  • PHP autoloader

Composer

Install

  • Install Composer
  • For Windows, download installer.
  • For Mac/ Linux, follow latest instructions
    • sudo mv composer.phar /usr/local/bin/composer

Composer

Basic composer.json

{
    "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"
    }
}

Composer

composer.json with Autoloader

{
    "autoload": {
        "psr-4": {
            "AllOfTheThings\\": "./php"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "AllOfTheThings\\Tests\\": "./tests"
        }
    }
}

npm (or Yarn)

Why

  • 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 (or Yarn)

Install

  • npm is installed with node

package.json

Basic

{
    "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"
    }
}

Automated Tests

function add($one, $two){
  return $one + $two;
}

class SampleTest extends \PHPUnit\Framework\TestCase
{

    public function testAdd()
    {
        $this->assertEquals(
          4, add(2,2)
        );
    }

Automated Tests

  • Guide development.
  • Prevent regression errors.
  • Makes clear how the code is supposed to work.
  • Debugging errors.
  • Profiling performance.
  • Surfacing accessibility issues

phpunit

  • PHP test runner and assertions.
  • Can be used with or without WordPress test suite.

Automated Tests

composer.json for phpunit

{
    "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"
    }
}

Automated Tests

Example Unit Test

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')
        );
    }

Automated Tests

Example Unit Test With Mock

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());
    }

Automated Tests

Example Integration Test Without Mock

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);
    }


Automated Tests

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.

Compiling JavaScript

Why

  • Allows for alternative syntaxes
  • Optimize JavaScript
  • Optimize CSS

JSX

  • React's template syntax
  • Not required or blocks
    • Worth using for sure.
  • HTML and JavaScript together

JSX

//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>

JSX

//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 ])
    ]);
}

Testing JavaScript


Testing JavaScript

package.json With Tests

{
    "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"
    }
}

Testing JavaScript

Example

//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);
    });

});

Code Quality Analysis

  • Tests check code by running it in various ways.
  • Code quality analysis parses the code and detetchs likely issues.

Code Quality Analysis

  • 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.

Code Quality Analysis


CI/CD

What

  • CI: Continuous integration.
    • Using automtion to continously test, analysis and merge changes to code.
  • CD: Continous deployment.
    • Using automation to continously deploy code.

CI/CD

Services


CI/ Code

Example Github Action

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

Live Demo Time


Thank You

Josh Pollock | @josh412 | JoshPress.net