Comprehensive testing guide for the Tour Operator WordPress plugin.
- Overview
- Test Types
- Running Tests
- PHPUnit Tests
- Jest Tests
- Playwright E2E Tests
- YAML Linting
- Continuous Integration
The Tour Operator plugin uses a multi-layered testing strategy:
- PHPUnit - Unit tests for PHP backend logic
- Jest - Unit tests for JavaScript/React components
- Playwright - End-to-end browser tests
- yamllint - Workflow and configuration validation
Located in tests/php/, these tests verify:
- Custom Post Types registration
- Taxonomy registration
- Block Pattern registration
- Custom Fields logic (Bindings)
- WordPress hooks and filters
Located in tests/js/, these tests verify:
- JavaScript utility functions
- Conditional block registration logic
- React component behavior
- Block editor extensions
Located in tests/e2e/, these tests verify:
- Plugin activation and admin menu presence
- Post type creation workflows
- Block editor integration
- Taxonomy management interfaces
Validates GitHub Actions workflows and YAML configuration files for syntax and best practices.
# Run all tests
npm test
# Run comprehensive test suite
npm run test:all# Run all PHPUnit tests
npm run test:php:all
# Run specific test file
npm run test:php:registration
# Run with PHPUnit directly
./vendor/bin/phpunit
# Run specific test class
./vendor/bin/phpunit tests/php/TestRegistration.php
# Run specific test method
./vendor/bin/phpunit --filter test_post_types_registered# Run all Jest tests
npm run test:unit
# Watch mode (re-runs on file changes)
npm run test:unit:watch
# With coverage report
npm run test:unit:coverage
# Run specific test file
npm run test:unit -- conditional-registration.test.js# Run all E2E tests (headless)
npm run test:e2e
# Run with UI (interactive)
npm run test:e2e:ui
# Debug mode (step through tests)
npm run test:e2e:debug
# Run specific test file
npm run test:e2e:plugin# Lint all YAML files
npm run lint:yaml
# Lint only GitHub workflows
npm run lint:yaml:workflows
# Direct yamllint usage
yamllint .
yamllint .github/workflows/PHPUnit configuration is in phpunit.xml at the project root.
<?php
/**
* Example test file
*/
class TestExample extends Tour_Operator_Test_Case {
public function test_something() {
$this->assertTrue(true);
}
}The base class Tour_Operator_Test_Case provides:
create_tour($args)- Create test tour postscreate_accommodation($args)- Create test accommodation postscreate_destination($args)- Create test destination postscreate_tour_with_relations($args)- Create tour with linked postsassertPostExists($id, $type, $status)- Verify post existenceassertPostMetaEquals($id, $key, $value)- Check post metaassertHookRegistered($hook, $function)- Verify WordPress hooks
- Create file in
tests/php/ - Extend
Tour_Operator_Test_Case - Prefix test methods with
test_ - Use descriptive test names
Example:
<?php
class TestMyFeature extends Tour_Operator_Test_Case {
public function test_feature_works_correctly() {
$tour_id = $this->create_tour(['post_title' => 'Test Tour']);
$this->assertPostExists($tour_id, 'tour', 'publish');
$this->assertGreaterThan(0, $tour_id);
}
}Jest configuration is handled by @wordpress/scripts in package.json.
import { myFunction } from '../src/js/my-module';
describe('My Module', () => {
it('should do something', () => {
expect(myFunction()).toBe(true);
});
});- Create file with
.test.jssuffix intests/js/ - Import functions to test
- Use
describefor test suites - Use
itortestfor individual tests
Example:
import { createConditionalRegistration } from '../../src/js/conditional-block-registration';
describe('Conditional Registration', () => {
it('should export function', () => {
expect(typeof createConditionalRegistration).toBe('function');
});
});Playwright E2E tests require:
- WordPress test environment
- Playwright browsers installed
- Admin credentials configured
Playwright configuration should be in playwright.config.js (create if needed).
import { test, expect } from '@wordpress/e2e-test-utils-playwright';
test.describe('Feature Test', () => {
test('should do something', async ({ admin, page }) => {
await admin.visitAdminPage('/');
// Test assertions
});
});- Create file with
.spec.jssuffix intests/e2e/ - Import from
@wordpress/e2e-test-utils-playwright - Use
test.describefor test groups - Use
testfor individual tests - Use async/await for page interactions
Example:
import { test, expect } from '@wordpress/e2e-test-utils-playwright';
test('should create new tour', async ({ admin, page }) => {
await admin.visitAdminPage('post-new.php?post_type=tour');
const titleField = page.getByRole('textbox', { name: /add title/i });
await expect(titleField).toBeVisible();
});Install yamllint (requires Python):
# macOS
brew install yamllint
# Linux (Ubuntu/Debian)
sudo apt-get install yamllint
# Using pip
pip install yamllintYAML linting rules are in .yamllint at the project root.
# Lint all YAML
yamllint .
# Lint specific directory
yamllint .github/workflows/
# Lint specific file
yamllint .github/workflows/ci.yml- Line too long: Break long lines or adjust
.yamllintconfig - Truthy values: Use explicit
true/falseinstead ofyes/no - Indentation: Use 2 spaces consistently
- Trailing spaces: Remove whitespace at line ends
All tests run automatically on:
- Push to
develop,main,2.*-trunkbranches - Pull requests
- Manual workflow dispatch
ci.yml- Main CI pipeline (PHPUnit, Jest, Linting)yaml-lint.yml- YAML validationpush-deploy.yml- Deployment pipeline
Run the same checks as CI locally:
# All linting
npm run lint:all
# All tests
npm run test:all- Write tests before fixing bugs (TDD)
- Keep tests focused and isolated
- Use descriptive test names
- Clean up test data in
tearDown/afterEach - Mock external dependencies
- Extend
Tour_Operator_Test_Casefor plugin-specific utilities - Use data providers for parameterized tests
- Group related tests in one class
- Test both success and failure cases
- Mock WordPress globals in
setup.js - Use
jest.fn()for spies and mocks - Test component rendering and interactions
- Verify prop types and default values
- Use built-in WordPress utilities (
admin,editor) - Wait for elements with
waitForLoadState,waitForSelector - Use semantic selectors (
getByRole,getByLabel) - Group related tests in
describeblocks - Clean up test data after each test
WordPress test library not found:
# Set WP_TESTS_DIR environment variable
export WP_TESTS_DIR=/tmp/wordpress-tests-lib
# Or run setup script
bash bin/install-wp-tests.sh wordpress_test root '' localhost latestDatabase connection errors:
- Verify MySQL is running
- Check credentials in test bootstrap
- Ensure test database exists
Module not found:
# Clear Jest cache
npm run test:unit -- --clearCache
# Reinstall dependencies
rm -rf node_modules package-lock.json
npm installBrowsers not installed:
npx playwright installWordPress environment not accessible:
- Verify WordPress is running
- Check
playwright.config.jsbase URL - Ensure admin credentials are configured
yamllint not found:
# Install yamllint
pip install yamllint
# Or use brew (macOS)
brew install yamllint- PHPUnit Documentation
- Jest Documentation
- Playwright Documentation
- WordPress Handbook: Plugin Tests
- GitHub Actions Documentation
When adding new features:
- Write tests first (TDD approach)
- Ensure all tests pass locally
- Add test documentation if needed
- Submit PR with test coverage
For test-related issues, see CONTRIBUTING.md.