Frontend Tests

This page tells you which tests exist for the frontend of a Festi project and how to write them.

What you can test

The server renders the HTML: plugin templates, HUI components, DGS lists and forms, and the theme layout. Thus PHPUnit can test most of the frontend. No browser is necessary.

Part Tool State
HUI components PHPUnit, ThemeTestCase Supported. The Theme package has a test class for each component.
Theme layout and theme events PHPUnit, ThemeTestCase Supported.
Plugin templates PHPUnit Supported. Render the template and examine the HTML.
Response of a plugin method PHPUnit Supported. Examine the action and the parameters.
DGS lists and forms PHPUnit Supported. See Testing DGS.
React application of a plugin none The starter application of the Festi CLI has no test setup. Add a test runner to the application if you need one.
jimbo.js Node test runner with jsdom, ESLint Supported in the ThemeAssets repository: npm test and npm run lint.
The scripts of plugins none No test setup exists. Test them manually in the browser.

Note: Only the ThemeAssets repository has a JavaScript test runner and a JavaScript linter, for jimbo.js. A project has none for the scripts of its plugins. Keep the JavaScript of a plugin small. Put the logic on the server, where PHPUnit can test it.

The general setup of PHPUnit (the tests directory, bootstrap.php, the database) is in Unit Tests.

The examples for HUI components and themes come from the tests of the Theme package and of SmartAdmin. The examples for plugin templates and for Response show the pattern. They were not run for this page.

Test a HUI component

Festi\Theme\Tests\ThemeTestCase prepares the Core object with the theme options, a signed-in user, and a system plugin. Make the component from a plugin, render it, and examine the HTML.

<?php

namespace Festi\Theme\Tests\UI;

use Festi\Theme\Tests\Helper\TestPlugin;
use Festi\Theme\Tests\ThemeTestCase;

class ButtonHuiTest extends ThemeTestCase
{
    protected TestPlugin $plugin;

    protected function setUp(): void
    {
        parent::setUp();

        $this->plugin = $this->core->getPluginInstance(TestPlugin::class);
    }

    public function testButtonShowsItsCaptionAndType(): void
    {
        $html = $this->plugin->ui->button()
            ->setCaption('Submit')
            ->setType('submit')
            ->setCSS('btn btn-success')
            ->setID('submit-btn')
            ->fetch();

        $this->assertStringContainsString('Submit', $html);
        $this->assertStringContainsString('type="submit"', $html);
        $this->assertStringContainsString('class="btn btn-success"', $html);
        $this->assertStringContainsString('id="submit-btn"', $html);
    }
}

TestPlugin is an empty class that extends DisplayPlugin. It gives the test the property $this->plugin->ui.

For your own component, write the same test. Use a plugin class that returns your engine from createUIEngine(). See HUI guide.

Test a theme

A theme has its own tests directory. The test class extends ThemeTestCase. The constant FS_THEME_ROOT in tests/bootstrap.php gives the directory of the theme.

Example: a listener adds HTML to the header, and the page contains it.

<?php

namespace SmartAdmin\Tests\Basic;

use Festi\Theme\Plugin\DisplayPlugin;
use Festi\Theme\Tests\ThemeTestCase;
use FestiEvent;
use Theme;

class HeaderThemeTest extends ThemeTestCase
{
    public function testListenerAddsContentToTheRightSideOfTheHeader(): void
    {
        $testContent = 'testAddContentRight';

        $this->core->addEventListener(
            Theme::EVENT_THEME_HEADER_CONTENT_RIGHT,
            function (FestiEvent &$event) use ($testContent) {
                return $testContent;
            }
        );

        $displayPlugin = new DisplayPlugin();

        $result = $displayPlugin->fetchMain('');

        $this->assertStringContainsString($testContent, $result);
    }
}

fetchMain($content) renders the full page: the layout of the theme with the content in it.

Run the tests of a theme or of the Theme package:

composer install
cd tests && ../vendor/bin/phpunit

Test a plugin template

Render the template with fetch() and examine the HTML. Give the data as local variables. Then the test does not need the database.

public function testOrderRowShowsThePaidLabel(): void
{
    $plugin = $this->core->getPluginInstance('Orders');

    $html = $plugin->fetch('parts/row.phtml', [
        'order' => ['id' => 15, 'status' => 'paid'],
    ]);

    $this->assertStringContainsString('id="order-15"', $html);
    $this->assertStringContainsString('m-paid', $html);
}

To make this test simple, write the template thus:

  • The template gets its data from variables. It does not call the database.
  • A large template uses small templates (parts/row.phtml). Test the small templates.

Test the Response of a plugin method

Make a Response, call the method, and examine the action and the parameters. The test does not send the response.

public function testDoneReloadsThePageWithANotification(): void
{
    $plugin = $this->core->getPluginInstance('Orders');
    $response = new Response(Response::JSON);

    $plugin->onJsonDone($response, 15);

    $this->assertSame(Response::ACTION_RELOAD, $response->getAction());
    $this->assertContains('The order is done.', $response->notifications);
}

For a method that renders a page, examine $response->content:

$response = new Response();

$plugin->onDisplayOrder($response, 15);

$this->assertStringContainsString('Order #15', $response->content);

When the constant PHPUnit is defined (in tests/bootstrap.php), Response::send() does not print. It puts the response into $GLOBALS['outputDisplay']. Thus a test can also call code that sends the response.

Test a DGS

A DGS test calls a store action and examines the response or the storage. See Testing DGS.

For a custom field class, examine the HTML of getEditInput() and the result of isValidValue().

Test a React application

The starter application of the Festi CLI is a Vite project. It has no test script and no test runner. If the application needs tests, add a test runner (for example Vitest) to its package.json.

See ReactJS.

Manual checks in the browser

Do these checks for a change that PHPUnit cannot examine (JavaScript, CSS):

  1. Open the page. Make sure that the browser console shows no error.
  2. Open and close each dialog of the page.
  3. Submit each form one time with correct values and one time with incorrect values.
  4. Make sure that the notifications and the form errors show.
  5. Make the browser window narrow. Make sure that the page is usable on a small screen.
  6. Do the checks again with the browser cache off, to make sure that the page loads the new files.

Rules

  • Write the test name as a scenario: what the user sees or what the system does.
  • Examine the parts of the HTML that are important (text, id, CSS class, data- attribute). Do not compare the full HTML text.
  • Do not use a mock for the plugin or the theme. Use the real classes with test data.
  • Keep logic out of templates and out of JavaScript. Then PHPUnit can test it.