Skip to content

Add unit testing infrastructure (PHPUnit + Jest) excluded from build artifact - #1885

Open
subodhr258 wants to merge 16 commits into
developfrom
feature/unit-testing-setup
Open

Add unit testing infrastructure (PHPUnit + Jest) excluded from build artifact#1885
subodhr258 wants to merge 16 commits into
developfrom
feature/unit-testing-setup

Conversation

@subodhr258

Copy link
Copy Markdown
Collaborator

Summary

Establishes a complete unit-testing infrastructure for the GoDAM plugin. None exists today. Includes PHPUnit (two tiers), Jest, scripts, CI, Codecov, build-artifact exclusion, and three smoke tests proving the harnesses work.

The implementation follows the plan committed at docs/superpowers/plans/2026-05-22-unit-testing-setup.md. Per-section coverage rollout (REST, blocks, transcoder, etc.) is intentionally out of scope for this PR — that lands in follow-ups.

What's in this PR

Test harness

  • PHPUnit (PHP 9.6) wired through composer.json with Brain Monkey + Mockery (newly added) for Tier-1 tests that don't boot WordPress, and wp-phpunit + Yoast PHPUnit-Polyfills (already present) for Tier-2 integration tests.
  • Two separate configs: phpunit-unit.xml.dist (fast, no WP) and phpunit-integration.xml.dist (boots WP via wp-phpunit).
  • Base test classes under namespace GoDAM\Tests\ (PSR-4 via autoload-dev).
  • bin/install-wp-tests.sh (canonical wp-cli scaffold script) for CI + local integration runs.
  • Jest via @wordpress/jest-preset-default (already in devDeps) + newly-added @testing-library/{react,jest-dom,user-event}.
  • jest.config.js extending the WP preset with our coverage scope and ignore patterns.
  • New npm/composer scripts: test, test:js, test:js:watch, test:js:coverage, test:php, test:php:unit, test:php:integration, plus matching composer scripts.

Three smoke tests (one per runner)

  • tests/phpunit/unit/Harness/HarnessTest.php — proves Brain Monkey loads + can fake WP functions.
  • tests/phpunit/integration/Plugin/PluginBootTest.php — proves wp-phpunit boots WP and our plugin loads (asserts `RTGODAM_VERSION` is defined).
  • tests/js/harness.test.js — proves `@testing-library/{react,jest-dom}` work.

Build artifact protection

  • Tests excluded from the release zip via .distignore (respected by both `10up/action-wordpress-plugin-deploy` and `rtcamp/action-deploy-wordpress`).
  • Belt-and-braces `.gitattributes` with `export-ignore` for `git archive` consumers.
  • New step in `release_on_tag.yml` greps the staged tree and fails the release if `tests/`, phpunit configs, `jest.config.js`, `docs/`, `.codecov.yml`, or `.github/` leak in. Also confirms `vendor/` is retained (needed for `woocommerce/action-scheduler`).

CI

  • New `.github/workflows/tests_on_pull_request.yml` with three jobs:
    • `php-unit` matrix: PHP 7.4 / 8.1 / 8.4
    • `php-integration` matrix: 7.4×6.5, 8.1×latest, 8.4×latest (with MySQL 8.0 service container)
    • `js-unit` (Node from `.nvmrc`)
  • All three upload coverage to Codecov with per-flag tagging.
  • `fail_ci_if_error: false` on every Codecov step (informational stance).

Codecov

  • `.codecov.yml` with `informational: true` and `carryforward: true` for the first ~60 days so the gate doesn't block PRs while baseline coverage is being seeded.

Docs

  • New "Running Tests" section in `CONTRIBUTING.md` with all canonical commands.
  • The full implementation plan committed at `docs/superpowers/plans/2026-05-22-unit-testing-setup.md` (excluded from build).

Things worth knowing during review

  • vendor/ is intentionally retained in the release artifact. `woocommerce/action-scheduler` is a runtime require. `composer install --no-dev` already runs before deploy, so dev tooling (PHPUnit, Brain Monkey, PHPCS) is stripped from the production vendor.
  • `@testing-library/react ^14` is the correct pairing for React 18 — `^15` requires React 19.
  • `.eslintrc` gained two small, scoped overrides for test files only (`jest/unbound-method: off`, `jest/prefer-expect-assertions: off`, `@wordpress/dependency-group: off`). Reason: the rules either require TypeScript parser services (not configured here) or don't apply to test files. Scope is the test-files `files` block only, never global.
  • `phpcs.xml` gained two scoped exclusions for `tests/*`: `WordPress.Files.FileName.NotHyphenatedLowercase` (PSR-4 class filenames are PascalCase by convention) and `WordPress.Security.EscapeOutput.OutputNotEscaped` (one informational echo in the integration bootstrap).
  • Branch protection should be enabled only after ~2 weeks of consistent green CI to avoid blocking on early flake.
  • Per-section coverage rollout (REST controllers, blocks, transcoder, form integrations) is deferred to follow-up PRs — each section in the plan ships independently.

Test plan

  • `composer test:unit` passes locally (2 tests, 2 assertions)
  • `npm run test:js` passes locally (2 tests)
  • `bash -n bin/install-wp-tests.sh` passes
  • `php -l` clean on all new PHP files
  • YAML in both new/edited workflows parses
  • Build artifact simulation (`rsync --exclude-from=.distignore`) leaves `tests/`, `phpunit*.xml.dist`, `jest.config.js`, `docs/`, `.codecov.yml`, `.github/`, `.gitattributes` excluded while retaining `vendor/`
  • CI `tests_on_pull_request.yml` jobs all green on this PR
  • PHPCS workflow still green
  • Plugin Check workflow still green
  • (Post-merge) First tag push after merge succeeds — the new verify step in `release_on_tag.yml` runs

🤖 Generated with Claude Code — see CLAUDE attribution on each commit.

subodhr258 and others added 16 commits May 22, 2026 15:41
Adds brain/monkey + mockery/mockery to composer require-dev for
Tier-1 (no-WP) PHP unit tests. Adds @testing-library/react +
jest-dom + user-event to package.json devDeps for Jest-based block
tests. Wires a PSR-4 autoload-dev mapping so GoDAM\Tests\
namespaces resolve.

Co-Authored-By: Claude <noreply@anthropic.com>
Adds tests/phpunit/{unit,integration} dirs, tests/js placeholder,
two bootstraps (unit = Brain Monkey only; integration = wp-phpunit),
and two abstract base TestCase classes namespaced under GoDAM\Tests.

Also adds phpcs.xml exclusions for test-file naming and output
escaping rules that don't apply to the test harness.

Co-Authored-By: Claude <noreply@anthropic.com>
Two separate configs — phpunit-unit.xml.dist for fast Tier-1 tests
that don't boot WordPress, and phpunit-integration.xml.dist for the
wp-phpunit-driven suite. Coverage scopes differ accordingly.

Co-Authored-By: Claude <noreply@anthropic.com>
Canonical wp-cli plugin-tests scaffold script. Downloads WordPress
core, the wp-phpunit test suite, and creates the test DB. Used by
CI and by local devs before running the integration suite.

Co-Authored-By: Claude <noreply@anthropic.com>
Extends @wordpress/jest-preset-default. Ignores node_modules,
vendor, and assets/build. Loads @testing-library/jest-dom matchers
via tests/js/setup.js. Coverage scope limited to assets/src.

Also disables jest/unbound-method ESLint rule in the test override
(requires TypeScript parser services which this project does not use).

Co-Authored-By: Claude <noreply@anthropic.com>
Adds test:js, test:js:watch, test:js:coverage, test:php and tier-
specific test:php:unit / test:php:integration npm scripts plus the
corresponding composer scripts (test, test:unit, test:integration,
test:coverage).

Co-Authored-By: Claude <noreply@anthropic.com>
Updates .distignore so 10up/action-wordpress-plugin-deploy and
rtcamp/action-deploy-wordpress strip the test suite from the
WordPress.org zip. Adds .gitattributes export-ignore for git archive
consumers. Adds .phpunit.result.cache and coverage/ to .gitignore.

Co-Authored-By: Claude <noreply@anthropic.com>
Confirms Brain Monkey loads via composer autoload and that a faked
WP function returns the stubbed value. Establishes the harness
works before we start covering real plugin code.

Co-Authored-By: Claude <noreply@anthropic.com>
Confirms wp-phpunit loads WordPress core and that the GoDAM plugin
loads under the muplugins_loaded filter (asserting the plugin's
main version constant becomes defined). Not run locally — requires
the WP test environment from bin/install-wp-tests.sh; CI runs it.

Co-Authored-By: Claude <noreply@anthropic.com>
Renders a div and a disabled button to confirm @testing-library/react
and @testing-library/jest-dom matchers wire correctly via the
project's jest.config.js + tests/js/setup.js.

Co-Authored-By: Claude <noreply@anthropic.com>
Adds tests_on_pull_request workflow with three jobs:
- php-unit (matrix: PHP 7.4, 8.1, 8.4)
- php-integration (matrix: php × wp pairs covering 7.4/6.5,
  8.1/latest, 8.4/latest with a MySQL service)
- js-unit (Node from .nvmrc)
All jobs upload coverage to Codecov with per-flag tagging.

Co-Authored-By: Claude <noreply@anthropic.com>
Project + patch coverage gates are informational for the first 60
days while baseline coverage stabilises. Carry-forward enabled per
flag so a job that didn't run on a PR doesn't drop the score.

Co-Authored-By: Claude <noreply@anthropic.com>
Documents the two PHP test tiers (unit via Brain Monkey, integration
via wp-phpunit) and the Jest setup. Includes the canonical commands
plus a pointer to the architectural plan.

Co-Authored-By: Claude <noreply@anthropic.com>
Stages the release tree with the same rsync exclusion logic that
10up/action-wordpress-plugin-deploy uses and fails the workflow if
tests/, phpunit configs, jest.config.js, docs/, .codecov.yml, or
.github/ are present after the .distignore filter. Also confirms
vendor/ (needed for action-scheduler) survives the filter.

Co-Authored-By: Claude <noreply@anthropic.com>
Captures the architectural decisions and per-section coverage
targets that drove this PR. Lives under docs/ which is excluded
from the build artifact via .distignore. Referenced from
CONTRIBUTING.md "Running Tests" section.

Co-Authored-By: Claude <noreply@anthropic.com>
…fact

Caught by the final-review reviewer: the artifact-verification step
added in 23591b6 greps for .codecov.yml and would have failed the
first release after merge. .gitattributes is added for hygiene —
it's a dev-only file with no runtime purpose.

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 22, 2026 10:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Establishes a full unit-testing setup for the GoDAM WordPress plugin across PHP (fast unit + WP integration tiers) and JS (Jest + Testing Library), adds CI to run the suites on PRs with Codecov reporting, and ensures test/dev-only files are excluded from release artifacts.

Changes:

  • Add PHPUnit tier-1 (Brain Monkey) + tier-2 (wp-phpunit) harnesses, configs, and smoke tests.
  • Add Jest configuration + smoke test, plus npm/composer scripts to run tests and generate coverage.
  • Add CI workflows (PR tests, release artifact verification) and packaging exclusions (.distignore + export-ignore).

Reviewed changes

Copilot reviewed 21 out of 29 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/phpunit/unit/Harness/HarnessTest.php Adds a PHPUnit unit smoke test using Brain Monkey.
tests/phpunit/unit/.gitkeep Keeps empty unit test directory tracked.
tests/phpunit/TestCase.php Base PHPUnit test case that boots/tears down Brain Monkey per test.
tests/phpunit/IntegrationTestCase.php Base WP integration test case extending WP_UnitTestCase.
tests/phpunit/integration/Plugin/PluginBootTest.php Integration smoke test verifying WP + plugin boot.
tests/phpunit/integration/.gitkeep Keeps empty integration test directory tracked.
tests/phpunit/bootstrap-unit.php Unit test bootstrap (autoload + minimal WP constants).
tests/phpunit/bootstrap-integration.php Integration bootstrap that loads wp-phpunit and the plugin.
tests/js/setup.js Loads Testing Library jest-dom matchers for Jest.
tests/js/harness.test.js Jest smoke test proving Testing Library setup works.
tests/js/.gitkeep Keeps empty JS test directory tracked.
phpunit-unit.xml.dist PHPUnit config for fast unit tier and coverage include set.
phpunit-integration.xml.dist PHPUnit config for WP integration tier and coverage include set.
phpcs.xml Adds scoped PHPCS exclusions for tests.
package.json Adds Testing Library dev deps and test scripts.
package-lock.json Locks new JS testing dependencies.
jest.config.js Jest config extending WP preset and scoping coverage paths.
docs/superpowers/plans/2026-05-22-unit-testing-setup.md Captures the implementation plan and rationale (excluded from builds).
CONTRIBUTING.md Documents how to run PHP and JS tests locally.
composer.lock Locks new PHP dev dependencies (Brain Monkey + Mockery).
composer.json Adds PHP dev deps, autoload-dev, and composer test scripts.
bin/install-wp-tests.sh Adds WP test suite installer script for integration tests.
.gitignore Ignores PHPUnit cache and coverage outputs.
.github/workflows/tests_on_pull_request.yml Runs PHP unit/integration + JS unit tests in CI with coverage uploads.
.github/workflows/release_on_tag.yml Verifies excluded files don’t leak into the staged release artifact.
.gitattributes Adds export-ignore rules for git archive consumers.
.eslintrc Adds test-only ESLint overrides for Jest/test files.
.distignore Excludes tests/config/docs/CI files from release zips.
.codecov.yml Sets Codecov flags and informational coverage statuses.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions

Copy link
Copy Markdown

🔍 WordPress Plugin Check Report

❌ Status: Failed

📊 Report

🎯 Total Issues ❌ Errors ⚠️ Warnings
15 1 14

❌ Errors (1)

📁 readme.txt (1 error)
📍 Line 🔖 Check 💬 Message
0 outdated_tested_upto_header Tested up to: 6.9 < 7.0. The "Tested up to" value in your plugin is not set to the current version of WordPress. This means your plugin will not show up in searches, as we require plugins to be compatible and documented as tested up to the most recent version of WordPress.

⚠️ Warnings (14)

📁 readme.txt (2 warnings)
📍 Line 🔖 Check 💬 Message
0 mismatched_plugin_name Plugin name "GoDAM - Organize WordPress Media Library & File Manager with Unlimited Folders for Images, Videos & more" is different from the name declared in plugin header "GoDAM".
0 trademarked_term The plugin name includes a restricted term. Your chosen plugin name - "GoDAM - Organize WordPress Media Library & File Manager with Unlimited Folders for Images, Videos & more" - contains the restricted term "wordpress" which cannot be used at all in your plugin name.
📁 composer.json (1 warning)
📍 Line 🔖 Check 💬 Message
0 missing_composer_json_file The "/vendor" directory using composer exists, but "composer.json" file is missing.
📁 assets/build/css/main.css (1 warning)
📍 Line 🔖 Check 💬 Message
0 EnqueuedStylesScope This style is being loaded in all contexts.
📁 assets/src/libs/analytics.min.js (5 warnings)
📍 Line 🔖 Check 💬 Message
0 EnqueuedScriptsScope This script is being loaded in all frontend contexts.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880 (with handle analytics-library) is loaded in the footer. Consider a defer or async script loading strategy instead.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880/2026/05/22/hello-world/ (with handle analytics-library) is loaded in the footer. Consider a defer or async script loading strategy instead.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880/sample-page/ (with handle analytics-library) is loaded in the footer. Consider a defer or async script loading strategy instead.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880/demo-attachment-post/ (with handle analytics-library) is loaded in the footer. Consider a defer or async script loading strategy instead.
📁 assets/build/js/main.min.js (5 warnings)
📍 Line 🔖 Check 💬 Message
0 EnqueuedScriptsScope This script is being loaded in all frontend contexts.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880 (with handle rtgodam-script) is loaded in the footer. Consider a defer or async script loading strategy instead.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880/2026/05/22/hello-world/ (with handle rtgodam-script) is loaded in the footer. Consider a defer or async script loading strategy instead.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880/sample-page/ (with handle rtgodam-script) is loaded in the footer. Consider a defer or async script loading strategy instead.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880/demo-attachment-post/ (with handle rtgodam-script) is loaded in the footer. Consider a defer or async script loading strategy instead.

🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants