Skip to content

v3.0.0

Choose a tag to compare

@hotmeteor hotmeteor released this 28 Apr 14:41
· 6 commits to master since this release
e9890f9

v3 is a significant release that raises the platform floor, adds a suite of developer-experience tools, and fixes several validation edge cases.


Breaking Changes

Minimum requirements

  • PHP 8.3+ required (was 8.1+)
  • Laravel 12+ required (was 10+)

New config key
A new error_format key is required in config/spectator.php. If you published the config in v2, add:

'error_format' => env('SPECTATOR_ERROR_FORMAT', 'text'),

No behaviour change unless you set SPECTATOR_ERROR_FORMAT=json.

Internal type changes (extenders only)
If you extend Spectator's validator classes, note that:

  • 'read' / 'write' strings in AbstractValidator are now ValidationMode::Read / ValidationMode::Write enum cases
  • Format::TEXT_GREEN style class constants are now Format::TextGreen string-backed enum cases

These are internal details — if you only use the public facade and TestResponse assertions, no action is required.

See UPGRADE.md for the full list.


New Features

Artisan Commands

Four new artisan commands provide visibility into your spec and its relationship to your application.

spectator:validate — lint a spec file before tests run:

php artisan spectator:validate --spec=Api.v1.yml
php artisan spectator:validate --spec=Api.v1.yml --format=json

spectator:coverage — list every operation defined in the spec:

php artisan spectator:coverage --spec=Api.v1.yml

spectator:routes — cross-reference spec operations against your registered Laravel routes:

php artisan spectator:routes --spec=Api.v1.yml

Outputs matched, unimplemented, and undocumented routes at a glance.

spectator:stubs — generate skeleton test classes from a spec, ready to fill in:

php artisan spectator:stubs --spec=Api.v1.yml --output=tests/Contract

Operations are grouped by tag (falling back to path segment), producing one test class per group with one markTestIncomplete method per operation.

All commands support --format=json for machine-readable output.

PHPUnit Coverage Extension

SpectatorExtension is a PHPUnit 11 extension that tracks which spec operations are exercised across the full test run and prints a summary table when the suite finishes. Configure a minimum threshold to fail CI when coverage drops:

<extensions>
    <bootstrap class="Spectator\Coverage\SpectatorExtension">
        <parameter name="min_coverage" value="80"/>
    </bootstrap>
</extensions>

JSON Error Format

Validation errors can now be emitted as structured JSON instead of ANSI-coloured text — useful for CI log parsers, LLM-driven workflows, and anything that processes test output programmatically:

SPECTATOR_ERROR_FORMAT=json

Or toggle per test:

Spectator::useJsonErrors();
Spectator::useTextErrors();

A failed assertion with JSON errors produces:

{ "errors": ["The data (null) must match the type: string"] }

Fluent Path Prefix API

Spectator::withPathPrefix('v1');

As an alternative to setting SPECTATOR_PATH_PREFIX in .env.

AI & CI Integration

The combination of spectator:validate --format=json, SPECTATOR_ERROR_FORMAT=json, and SpectatorExtension makes Spectator a natural fit for AI-driven development workflows — structured outputs at every stage mean errors and coverage gaps can be piped directly into LLM toolchains.


Bug Fixes

  • Nullable inside additionalProperties not migrated — OpenAPI 3.0 nullable: true on dictionary value schemas was silently skipped during the 3.0 to 3.1 nullable migration. Values like null were incorrectly rejected. (#215)

  • Comma-separated values in header parametersexplode: false with type: array on in: header parameters now correctly splits the comma-separated string into an array before validation, matching the existing behaviour for query parameters. (inspired by #213)

  • TypeError with empty request body on allOf schemas — sending an empty body against a required allOf request body no longer throws a TypeError. The error is now a clean "Request body required!" validation failure. (#216)

  • Nullable properties inside allOfnullable: true on properties nested within allOf arms is correctly migrated to 3.1-style type: [T, null]. (#212)


Internals

  • PHP enums replace string and class constants throughout (ValidationMode, Format, Source)
  • First-class callables, readonly properties, and match expressions used where appropriate
  • PHPStan level 6 enforced in CI with zero errors
  • Test suite expanded from ~180 to 248 test cases
  • GitHub Actions matrix covers PHP 8.3/8.4 x Laravel 12/13
  • All dependency version constraints loosened on the upper bound so future Laravel versions work without a constraint update

Full Changelog

v2.5.2...v3.0.0