# Testing Guide
**Status**: Active/Authoritative
**Last Updated**: April 5, 2025
**Scope**: Testing the HVAC Community Events plugin
This guide covers setting up the test environment and running tests for the HVAC Community Events plugin using our unified test suite within the Docker environment.
## Test Environment Requirements
### Prerequisites
- Docker and Docker Compose
- Git
- Node.js and npm (for E2E tests)
- Composer
### Required WordPress Plugins
The test environment requires specific plugins with their minimum versions. These plugins are included in the database/files restored by the `setup-from-backup.sh` script and should **not** be updated manually during testing unless specifically testing updates:
1. The Events Calendar (6.10.2 or higher)
2. The Events Calendar Pro (7.4.2 or higher)
3. Event Tickets (5.19.3 or higher)
4. Event Tickets Plus (6.2.0 or higher)
5. The Events Calendar: Community Events (latest version)
- Note: Use only 'the-events-calendar-community-events' plugin, not the legacy 'community-events' plugin
6. Spectra Pro (2.0.0 or higher)
7. Premium Starter Templates (4.4.14 or higher)
8. Essential Blocks (5.3.2 or higher)
**Important:**
- Do not update plugins as part of standard testing.
- Plugin updates should be tested separately in a dedicated environment or branch.
## Test Environment Setup
The test environment relies heavily on the Docker setup defined in `docker-compose.yml` and configuration files mounted via volumes.
### 1. Initial Environment Setup
Ensure the main development environment is set up correctly using the backup restoration script:
```bash
# Run from wordpress-dev directory
./bin/setup-from-backup.sh
# Verify basic setup (optional but recommended)
./bin/verify-simple.sh
```
### 2. Install Dependencies (Composer & NPM)
Install PHP and Node.js dependencies:
```bash
# Run from wordpress-dev directory
composer install
npm install
```
This installs necessary libraries (PHPUnit, Playwright, etc.) into the `./vendor` and `node_modules` directories, which are mounted into the container. Ensure `@playwright/test` is listed in `package.json` devDependencies. Ensure `composer.json` includes an `autoload-dev` section for the `tests/` directory.
### 3. Configure PHP Memory Limit
# Run from wordpress-dev directory
composer install
```
This installs necessary libraries into the `./vendor` directory, which is mounted into the container.
### 3. Configure PHP Memory Limit
The default PHP memory limit might be too low. Ensure it's increased:
- Edit the host file: `wordpress-dev/php.ini/custom.ini`
- Set `memory_limit = 512M` (or higher if needed).
- **Crucially, restart the Docker containers** after modifying this file:
```bash
# Run from wordpress-dev directory
docker-compose down && docker-compose up -d
```
### 4. Configure WP-CLI
WP-CLI is required for some test setup steps and verification. It's made available via a volume mount.
- Ensure `wp-cli.phar` exists in `wordpress-dev/bin/`. If not, download it:
```bash
# Run from wordpress-dev directory
curl -o bin/wp-cli.phar https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x bin/wp-cli.phar
```
- Ensure the following volume mount exists in `docker-compose.yml` under the `wordpress` service:
```yaml
volumes:
# ... other volumes ...
- ./bin/wp-cli.phar:/usr/local/bin/wp
```
- **Restart containers** if `docker-compose.yml` was modified:
```bash
# Run from wordpress-dev directory
docker-compose down && docker-compose up -d
```
- When running WP-CLI commands inside the container, use the `--allow-root` flag:
```bash
docker-compose exec wordpress wp plugin list --allow-root
```
### 5. Configure PHPUnit Test Environment
The PHPUnit environment requires several configuration files to work correctly with WordPress within Docker:
- **`wordpress-dev/phpunit.xml.dist`**:
- Defines test suites (e.g., `unit`, `integration`).
- Specifies `tests/bootstrap.php` as the bootstrap file.
- Sets `backupGlobals="false"` and `processIsolation="false"` globally to prevent issues with Closures and test environment state.
- **Important:** Should *not* contain `` definitions for WordPress constants (like `ABSPATH`, `DB_*`, `WP_TESTS_DIR`), as these are handled by the bootstrap process and config files.
- **`wordpress-dev/wp-tests-config.php`** (Host file):
- This file *must* exist on the host.
- It defines the **test database** credentials (`DB_NAME`, `DB_USER`, `DB_HOST`). `DB_HOST` must be `db`.
- `DB_PASSWORD` should use `getenv('DEV_DB_ROOT_PASSWORD')` to read the root password from the `.env` file (sourced by the test runner script).
- It **must** define `define( 'ABSPATH', '/var/www/html/' );` because the test installation script needs it.
- It should *not* define `WP_TESTS_CONFIG_FILE_PATH`.
- This file is mounted into the container at `/var/www/html/wp-tests-config.php` via `docker-compose.yml`. Ensure the mount exists:
```yaml
volumes:
# ... other volumes ...
- ./wp-tests-config.php:/var/www/html/wp-tests-config.php:cached
```
*(Note: `:cached` flag might cause sync issues on some systems; remove if problems persist)*.
- **`wordpress-dev/tests/bootstrap.php`** (Host file):
- Locates the `wp-phpunit` library installed by Composer in the `vendor` directory.
- Defines `define( 'WP_TESTS_CONFIG_FILE_PATH', '/var/www/html/wp-tests-config.php' );` (using absolute path).
- Defines `define( 'WP_TESTS_RUNNING', true );` to signal to `wp-config.php` to skip its DB definitions.
- Includes the Composer autoloader (`vendor/autoload.php`).
- Loads the plugin being tested (`hvac-community-events`) using `tests_add_filter('muplugins_loaded', '_manually_load_hvac_plugin')`. (Note: Manual loading of other dependencies like TEC via `plugins_loaded` hook should remain commented out unless proven necessary).
- Includes the main `wp-phpunit` test library bootstrap: `require $_tests_dir . '/includes/bootstrap.php';`.
- **`wordpress-dev/wordpress/wp-config.php`** (Host file, mounted into container):
- The main database definitions (`DB_NAME`, `DB_USER`, `DB_PASSWORD`, `DB_HOST`, `DB_CHARSET`, `DB_COLLATE`) **must** be wrapped in `if ( ! defined( 'WP_TESTS_RUNNING' ) ) { ... }` to prevent conflicts with `wp-tests-config.php` during tests.
### 6. Verify Test Database
Ensure the test database (`wordpress_test`) exists and the user (`root`) has access.
```bash
# Run from wordpress-dev directory
# Create DB if it doesn't exist (uses password from .env)
docker-compose exec db mysql -uroot -p"${DEV_DB_ROOT_PASSWORD}" -e "CREATE DATABASE IF NOT EXISTS wordpress_test;"
# Grant privileges (optional, root usually has them)
# docker-compose exec db mysql -uroot -p"${DEV_DB_ROOT_PASSWORD}" -e "GRANT ALL PRIVILEGES ON wordpress_test.* TO 'root'@'%';"
```
**Important:** Before running tests, ensure dependencies are installed (`composer install`, `npm install`) and the environment is prepared using `./bin/setup-test-env.sh`.
### 7. Activate Plugin Under Test
Ensure the `hvac-community-events` plugin is active for integration and E2E tests:
```bash
# Run from wordpress-dev directory
docker-compose exec wordpress wp plugin activate hvac-community-events --allow-root
```
## Running Tests
Use the `run-tests.sh` script located in the `wordpress-dev/bin` directory.
```bash
# Run all test suites (Unit, Integration, E2E)
./bin/run-tests.sh
# Run only Unit tests
./bin/run-tests.sh --unit
# Run only Integration tests
./bin/run-tests.sh --integration
# Run only E2E tests
./bin/run-tests.sh --e2e
# Run specific E2E test suite (e.g., login tests tagged with @login)
./bin/run-tests.sh --login
# Run with debug output
./bin/run-tests.sh --debug
```
**Note:** The script is configured to exit immediately if Unit or Integration tests fail.
## Test Types
### Unit Tests
- **Location:** `wordpress-dev/tests/unit/`
- **Note:** `Test_HVAC_Profile_Integration` is currently skipped (`@group skip`) due to unresolved environment conflicts related to serialization/initialization within `wp-phpunit`. See Troubleshooting section.
- **Purpose:** Test individual PHP classes and functions in isolation.
- **Environment:** Minimal dependencies, does not require a fully bootstrapped WordPress environment for most tests (though uses `WP_UnitTestCase` which provides some WP functions).
- **Execution:** Fast.
### Integration Tests
- **Location:** `wordpress-dev/tests/integration/`
- **Purpose:** Test functionality that requires interaction with WordPress core, the database, or other plugins (e.g., hooks, filters, options, user roles, CPTs).
- **Environment:** Requires the WordPress test environment loaded via `wp-phpunit`.
- **Execution:** Slower than unit tests.
### E2E (End-to-End) Tests
- **Location:** `wordpress-dev/tests/e2e/`
- **Framework:** Playwright
- **Purpose:** Simulate real user interactions in a browser, testing complete user flows from the frontend.
- **Environment:** Requires the full Docker environment (WordPress, Nginx, DB) to be running and accessible via `http://localhost:8080`.
- **Execution:** Slowest test type.
## Writing Tests
Refer to PHPUnit and Playwright documentation for detailed syntax.
### Best Practices
- One test class per code class/component/feature.
- Use descriptive test method names (e.g., `test_login_redirects_trainer_to_dashboard`).
- Test both expected outcomes (happy path) and error conditions (negative path).
- Make tests independent; the outcome of one test should not affect another.
- Clean up any created data (posts, users, options) in `tearDown()` methods or use WP test factories.
## Troubleshooting Common Issues
1. **PHPUnit: `wp-tests-config.php is missing!`**
- Verify `wordpress-dev/wp-tests-config.php` exists on the host.
- Verify the volume mount `- ./wp-tests-config.php:/var/www/html/wp-tests-config.php` exists in `docker-compose.yml` for the `wordpress` service.
- Verify `define( 'WP_TESTS_CONFIG_FILE_PATH', ABSPATH . 'wp-tests-config.php' );` is present and correct in `tests/bootstrap.php`.
- Restart Docker containers (`docker-compose down && docker-compose up -d`).
2. **PHPUnit: `Constant ... already defined` Warnings**
- Ensure `` definitions are *removed* from `phpunit.xml.dist`.
- Ensure the `if ( ! defined( 'WP_TESTS_RUNNING' ) )` check correctly wraps DB definitions in `wordpress/wp-config.php`.
- Ensure `define( 'WP_TESTS_RUNNING', true );` exists in `tests/bootstrap.php` *before* the main test bootstrap is required.
3. **PHPUnit: `Undefined constant "ABSPATH"` Fatal Error**
- Ensure `define( 'ABSPATH', '/var/www/html/' );` exists in `wp-tests-config.php`.
- Ensure `wp-tests-config.php` is correctly mounted and loaded (see point 1).
- Ensure the main `wp-phpunit/includes/bootstrap.php` loads `wp-tests-config.php` *before* scripts needing `ABSPATH` (like `install.php` or potentially `mock-mailer.php`) are called. *(Self-correction: We fixed mock-mailer by defining ABSPATH early in our bootstrap, but install.php needs it from wp-tests-config)*.
4. **PHPUnit: `Access denied for user 'root'@'...' (using password: NO)`**
- Ensure `DB_PASSWORD` is correctly defined (hardcoded) in `wp-tests-config.php`.
- Ensure the `if ( ! defined( 'WP_TESTS_RUNNING' ) )` check correctly wraps DB definitions in `wordpress/wp-config.php`.
- Ensure `wp-tests-config.php` is correctly mounted and loaded (see point 1).
- Verify the test database (`wordpress_test`) exists and the user (`root`) has privileges.
5. **PHPUnit: `Could not find tests_dir/includes/functions.php`**
- Ensure `composer install` was run successfully in `wordpress-dev`.
- Verify the path calculation for `$_vendor_dir` in `tests/bootstrap.php` is correct (`$_vendor_dir = ABSPATH . 'vendor/wp-phpunit/wp-phpunit';`).
- Check permissions on the host `vendor` directory if issues persist.
- Try removing the `:cached` flag from volume mounts in `docker-compose.yml` if sync issues are suspected. Restart containers after changes.
6. **PHPUnit: `Declaration of ... must be compatible with ...(): void`**
- Add the `: void` return type hint to the `setUp()` and `tearDown()` methods in your test class.
7. **WP-CLI: `executable file not found in $PATH`**
- Ensure `wp-cli.phar` is downloaded to `wordpress-dev/bin/`.
- Ensure the volume mount `- ./bin/wp-cli.phar:/usr/local/bin/wp` exists in `docker-compose.yml`.
- Ensure the host file `bin/wp-cli.phar` has execute permissions (`chmod +x`).
- Restart Docker containers (`docker-compose down && docker-compose up -d`).
8. **WP-CLI/PHPUnit: `Allowed memory size exhausted`**
- Increase `memory_limit` in `wordpress-dev/php.ini/custom.ini` (e.g., `512M`).
- Ensure the volume mount `- ./php.ini/custom.ini:/usr/local/etc/php/conf.d/custom.ini` is correct in `docker-compose.yml`.
- Restart Docker containers (`docker-compose down && docker-compose up -d`).
9. **E2E: Timeout waiting for element / 404 errors**
11. **PHPUnit: `Serialization of 'Closure' is not allowed`**
- **Cause:** This error occurs when PHPUnit tries to serialize the test environment state (e.g., for process isolation or global state backup) and encounters a Closure (anonymous function), which PHP cannot serialize. This often happens with mocking libraries (like Brain Monkey) or potentially within the `wp-phpunit` bootstrap itself.
- **Solution:**
- Ensure `backupGlobals="false"` and `processIsolation="false"` are set in the main `` tag in `phpunit.xml.dist`.
- If using mocking libraries like Brain Monkey, ensure `Monkey\setUp();` and `Monkey\tearDown();` are called in your test class's `setUp()` and `tearDown()` methods.
- If the error persists, it might indicate a deeper conflict within the `wp-phpunit` bootstrap or WordPress core hooks introducing unserializable Closures. Investigate hooks added during bootstrap or test setup.
- Verify the plugin under test (`hvac-community-events`) is active (`docker-compose exec wordpress wp plugin is-active hvac-community-events --allow-root`). Activate if needed.
- Verify the URL/slug used in `page.goto()` in the Playwright test matches the actual page slug in WordPress containing the shortcode or element.
- Verify the element selector used in the test matches the actual element ID/class/attribute in the rendered HTML.
12. **PHPUnit: `Class ... not found` or `No tests executed!`**
- **Cause 1 (Missing Autoload):** Composer's autoloader isn't configured to find your test classes. Ensure `composer.json` has an `autoload-dev` section mapping your test namespace (e.g., `"Tests\\": "tests/"`) and run `composer dump-autoload`.
- **Cause 2 (Namespace Conflict):** The test class file is missing a `namespace` declaration, or it's using a namespace (e.g., `namespace Tests\Integration;`) that conflicts with the `wp-phpunit` environment's test discovery. The `wp-phpunit` setup seems to work more reliably with test classes in the global namespace.
- **Cause 3 (Filename Mismatch):** PHPUnit might sometimes struggle if the filename doesn't match the class name convention (e.g., `TestClass.php` for `class TestClass`).
- **Solution:**
- Ensure `composer.json` has the correct `autoload-dev` PSR-4 mapping and run `composer dump-autoload`.
- For integration tests using `WP_UnitTestCase`, define the test class in the **global namespace** (remove any `namespace ...;` line) and ensure it extends `WP_UnitTestCase` (no leading backslash needed).
- Ensure the test filename matches the class name (e.g., `TestMyFeature.php` for `class TestMyFeature`).
- Verify the class name and method names are correct (case-sensitive, methods start with `test`).
- Check for subtle PHP syntax errors in the test file.
- Check container logs (`docker-compose logs wordpress`, `docker-compose logs nginx`) for PHP or web server errors preventing the page from rendering.
13. **E2E: 404 Error for Specific JS File (e.g., `php-date-formatter.js`)**
- **Cause:** The specific JavaScript file required by a third-party plugin (like The Events Calendar: Community Events) is missing from the plugin's installed files within the Docker container, even after syncing from production.
- **Diagnosis:** Use `docker-compose exec -T wordpress find /path/to/plugin/ -name 'filename.js'` to confirm the file is missing.
- **Solution/Workaround:**
- Verify the expected plugin version is installed.
- Attempt a clean reinstall of the specific third-party plugin.
- If the file is consistently missing, modify the E2E test to skip steps that depend on the missing script's functionality (e.g., avoid interacting with a date picker or rich text editor initialized by that script).
10. **General Docker Issues:**
- Restart containers: `docker-compose down && docker-compose up -d`
- Prune system: `docker system prune -a` (Use with caution, removes unused images/volumes/networks)
- Check Docker Desktop/Engine resource allocation (Memory, CPU).
*Last Updated: March 28, 2025*