This commit is contained in:
nuno maduro
2026-05-01 20:02:46 +01:00
parent 45b1d4ce20
commit bed5e5b54a
39 changed files with 4 additions and 426 deletions

View File

@ -3,8 +3,10 @@
declare(strict_types=1); declare(strict_types=1);
use Pest\Contracts\Restarter;
use Pest\Kernel; use Pest\Kernel;
use Pest\Panic; use Pest\Panic;
use Pest\Support\Container;
use Pest\TestCaseFilters\GitDirtyTestCaseFilter; use Pest\TestCaseFilters\GitDirtyTestCaseFilter;
use Pest\TestCaseMethodFilters\AssigneeTestCaseFilter; use Pest\TestCaseMethodFilters\AssigneeTestCaseFilter;
use Pest\TestCaseMethodFilters\IssueTestCaseFilter; use Pest\TestCaseMethodFilters\IssueTestCaseFilter;
@ -193,17 +195,11 @@ use Symfony\Component\Console\Output\ConsoleOutput;
try { try {
$kernel = Kernel::boot($testSuite, $input, $output); $kernel = Kernel::boot($testSuite, $input, $output);
// Restarters re-exec the PHP process when conditions warrant it $container = Container::getInstance();
// (XdebugRestarter drops Xdebug on TIA replay; PcovRestarter pins
// `pcov.directory` to the project root). Runs here, after
// Kernel::boot has loaded `tests/Pest.php` (so
// `pest()->tia()->always()` is visible) and before any plugin's
// `handleArguments` runs (so a re-exec replays cleanly).
$container = \Pest\Support\Container::getInstance();
foreach (Kernel::RESTARTERS as $restarterClass) { foreach (Kernel::RESTARTERS as $restarterClass) {
$restarter = $container->get($restarterClass); $restarter = $container->get($restarterClass);
assert($restarter instanceof \Pest\Contracts\Restarter); assert($restarter instanceof Restarter);
$restarter->maybeRestart($rootPath, $originalArguments); $restarter->maybeRestart($rootPath, $originalArguments);
} }

View File

@ -12,13 +12,6 @@ use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
/** /**
* Raised when fetching the team-shared TIA baseline hits an error
* that's actionable rather than transient — missing `gh`, broken
* auth, scope/perms misconfiguration, or a CI publish that produced
* an unreadable artifact. Silently falling through to a full record
* would paper over the bug and waste minutes; better to stop, tell
* the user what to fix, and offer the `--fresh` escape hatch.
*
* @internal * @internal
*/ */
final class BaselineFetchFailed extends RuntimeException implements ExceptionInterface, Panicable, RenderlessEditor, RenderlessTrace final class BaselineFetchFailed extends RuntimeException implements ExceptionInterface, Panicable, RenderlessEditor, RenderlessTrace

View File

@ -31,11 +31,6 @@ use Symfony\Component\Process\Process;
use Throwable; use Throwable;
/** /**
* Test Impact Analysis plugin — record/replay, parallel-aware.
*
* Must be registered before `Parallel` — Parallel exits on `--parallel`,
* so later plugins never execute.
*
* @internal * @internal
*/ */
final class Tia implements AddsOutput, HandlesArguments, Terminable final class Tia implements AddsOutput, HandlesArguments, Terminable

View File

@ -5,12 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia; namespace Pest\Plugins\Tia;
/** /**
* Captures PHP files that were included while a test was running.
*
* Coverage drivers miss declaration-only files (classes, enums, interfaces,
* traits) and files loaded before the coverage window opens. Diffing
* `get_included_files()` gives TIA an explicit edge for those autoloaded files.
*
* @internal * @internal
*/ */
final readonly class AutoloadEdges final readonly class AutoloadEdges

View File

@ -13,13 +13,6 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
/** /**
* Downloads a team-shared TIA baseline from GitHub workflow artifacts so new contributors and
* fresh CI workspaces start in replay mode. Artifacts are used instead of releases because they
* produce no tag (no push cascade), support tunable retention, and can only be published by CI.
*
* Fingerprint validation happens in `Tia::handleParent` after the blobs land; a mismatched
* environment falls through to the normal record path.
*
* @internal * @internal
*/ */
final readonly class BaselineSync final readonly class BaselineSync

View File

@ -5,27 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia; namespace Pest\Plugins\Tia;
/** /**
* Laravel-only collaborator: during record mode, attributes every
* rendered Blade view to the currently-running test.
*
* Why this exists: the coverage driver only sees compiled view files
* under `storage/framework/views/<hash>.php`, not the `.blade.php`
* source. Without a dedicated hook TIA has no edges for blade files,
* so it leans on the Laravel WatchDefault's broad "any .blade.php
* change → every feature test" fallback. Safe but noisy — editing a
* single partial re-runs the whole suite.
*
* With this armed at record time, each test's edge set grows to
* include the precise `.blade.php` files it rendered (directly or
* through `@include`, layouts, components, Livewire, Inertia root
* views — anything that goes through Laravel's view factory fires
* `View::composer('*')`). Replay then invalidates exactly the tests
* that rendered the changed template.
*
* Implementation note: everything Laravel-touching goes through
* string class names, `class_exists`, and `method_exists` so Pest
* core doesn't pull `illuminate/container` into its `require`.
*
* @internal * @internal
*/ */
final class BladeEdges final class BladeEdges

View File

@ -10,17 +10,6 @@ use Pest\Support\Container;
use Pest\TestSuite; use Pest\TestSuite;
/** /**
* Plugin-level container registrations for TIA. Runs as part of Kernel's
* bootstrapper chain so Tia's own service graph is set up without Kernel
* having to know about any of its internals.
*
* Most Tia services (`Recorder`, `CoverageCollector`, `WatchPatterns`,
* `ResultCollector`, `BaselineSync`) are auto-buildable — Pest's container
* resolves them lazily via constructor reflection. The only service that
* requires an explicit binding is the `State` contract, because the
* filesystem implementation needs a root-directory string that reflection
* can't infer.
*
* @internal * @internal
*/ */
final readonly class Bootstrapper implements BootstrapperContract final readonly class Bootstrapper implements BootstrapperContract

View File

@ -7,19 +7,6 @@ namespace Pest\Plugins\Tia;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
/** /**
* Detects files that changed between the last recorded TIA run and the
* current working tree.
*
* Strategy:
* 1. If we have a `recordedAtSha`, `git diff <sha>..HEAD` captures committed
* changes on top of the recording point.
* 2. `git status --short` captures unstaged + staged + untracked changes on
* top of that.
*
* We return relative paths to the project root. Deletions are included so the
* caller can decide whether to invalidate: a deleted source file may still
* appear in the graph and should mark its dependents as affected.
*
* @internal * @internal
*/ */
final readonly class ChangedFiles final readonly class ChangedFiles

View File

@ -7,19 +7,6 @@ namespace Pest\Plugins\Tia;
use Pest\Support\Container; use Pest\Support\Container;
/** /**
* User-facing TIA configuration, returned by `pest()->tia()`.
*
* Usage in `tests/Pest.php`:
*
* pest()->tia()->watch([
* 'resources/js/**\/*.tsx' => 'tests/Browser',
* 'public/build/**\/*' => 'tests/Browser',
* ]);
*
* Patterns are merged with the built-in defaults (config, routes, views,
* frontend assets, migrations). Duplicate glob keys overwrite the default
* mapping so users can redirect a pattern to a narrower directory.
*
* @internal * @internal
*/ */
final class Configuration final class Configuration

View File

@ -5,24 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia; namespace Pest\Plugins\Tia;
/** /**
* Per-file hashing that ignores changes which can't alter behaviour —
* comments and whitespace for PHP, `{{-- … --}}` comments and whitespace
* runs for Blade templates. Every other file type falls back to a plain
* xxh128 of the raw bytes.
*
* Why it matters: TIA's file diff signals drive which tests re-run. A
* one-line comment tweak on a migration is a behavioural no-op, but the
* raw-bytes hash still differs, so every test that talks to the DB would
* currently re-execute. Normalising to the parsed-token / compiled-shape
* keeps the drift signal honest: edits that can't change runtime
* behaviour don't invalidate the replay cache.
*
* Important: this hash is stored in the graph's last-run tree, so any
* format change here must be paired with a `Fingerprint::SCHEMA_VERSION`
* bump — otherwise stale hashes from older graphs would be compared
* against normalised hashes from the new code and everything would
* appear changed.
*
* @internal * @internal
*/ */
final class ContentHash final class ContentHash

View File

@ -5,12 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia\Contracts; namespace Pest\Plugins\Tia\Contracts;
/** /**
* Storage contract for TIA's persistent state (graph, baselines, affected
* set, worker partials, coverage snapshots). Modelled as a flat key/value
* store of raw byte blobs so implementations can sit on top of whatever
* backend fits — a directory, a shared cache, a remote object store — and
* TIA's logic stays identical.
*
* @internal * @internal
*/ */
interface State interface State

View File

@ -9,16 +9,6 @@ use ReflectionClass;
use Throwable; use Throwable;
/** /**
* Extracts per-test file coverage from PHPUnit's shared `CodeCoverage`
* instance. Used when TIA piggybacks on `--coverage` instead of starting
* its own driver session — both share the same PCOV / Xdebug state, so
* running two recorders in parallel would corrupt each other's data.
*
* PHPUnit tags every coverage sample with the current test's id
* (`$test->valueObjectForEvents()->id()`, e.g. `Foo\BarTest::baz`). The
* per-file / per-line coverage map therefore already carries everything
* we need to rebuild TIA edges at the end of the run.
*
* @internal * @internal
*/ */
final class CoverageCollector final class CoverageCollector

View File

@ -11,33 +11,6 @@ use SebastianBergmann\CodeCoverage\CodeCoverage;
use Throwable; use Throwable;
/** /**
* Merges the current run's PHPUnit coverage into a cached full-suite
* snapshot so `--tia --coverage` can produce a complete report after
* executing only the affected tests.
*
* Invoked from `Pest\Support\Coverage::report()` right before the coverage
* file is consumed. A marker dropped by the `Tia` plugin gates the
* behaviour — plain `--coverage` runs (no `--tia`) leave the marker absent
* and therefore keep their existing semantics.
*
* Algorithm
* ---------
* The PHPUnit coverage PHP file unserialises to a `CodeCoverage` object.
* Its `ProcessedCodeCoverageData` stores, per source file, per line, the
* list of test IDs that covered that line. We:
*
* 1. Load the cached snapshot from `State` (serialised bytes).
* 2. Strip every test id that re-ran this time from the cached map —
* the tests that ran now are the ones whose attribution is fresh.
* 3. Merge the current run into the stripped cached snapshot via
* `CodeCoverage::merge()`.
* 4. Write the merged result back to the report path (so Pest's report
* generator sees the full suite) and back into `State` (for the
* next invocation).
*
* If no cache exists yet (first `--tia --coverage` run on this machine)
* we serialise the current object and save it — nothing to merge yet.
*
* @internal * @internal
*/ */
final class CoverageMerger final class CoverageMerger

View File

@ -7,14 +7,6 @@ namespace Pest\Plugins\Tia;
use Pest\Plugins\Tia\Contracts\State; use Pest\Plugins\Tia\Contracts\State;
/** /**
* Filesystem-backed implementation of the TIA `State` contract. Each key
* maps verbatim to a file name under `$rootDir`, so existing `.temp/*.json`
* layouts are preserved exactly.
*
* The root directory is created lazily on first write — callers don't have
* to pre-provision it, and reads against a missing directory simply return
* `null` rather than throwing.
*
* @internal * @internal
*/ */
final readonly class FileState implements State final readonly class FileState implements State

View File

@ -5,15 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia; namespace Pest\Plugins\Tia;
/** /**
* Two-bucket fingerprint for TIA staleness detection.
*
* - **structural**: inputs whose drift means graph *edges* may be wrong → full rebuild.
* `tests/TestCase.php` and `tests/Pest.php` are intentionally absent; they're covered by
* `Recorder::linkAncestorFiles` and the watch pattern, giving precise per-test invalidation.
* - **environmental**: runtime inputs (PHP version, extensions, env files) whose drift means
* edges are still valid but cached results may not reproduce → drop results and re-run.
* Pest's own version is absent; `composer.lock` moves whenever Pest is upgraded.
*
* @internal * @internal
*/ */
final readonly class Fingerprint final readonly class Fingerprint

View File

@ -11,9 +11,6 @@ use PHPUnit\Framework\TestStatus\TestStatus;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
/** /**
* Dependency graph: test file → set<source file>. Skips unchanged tests on replay.
* Source files are indexed by numeric id to keep the on-disk JSON compact.
*
* @internal * @internal
*/ */
final class Graph final class Graph

View File

@ -5,28 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia; namespace Pest\Plugins\Tia;
/** /**
* Inertia-aware collaborator: during record mode, attributes every
* Inertia component the test server-side renders to the currently-
* running test file.
*
* Why this exists: a change to `resources/js/Pages/Users/Show.vue`
* should only invalidate tests that actually rendered `Users/Show`.
* The Laravel `WatchDefaults\Inertia` glob is a broad fallback — fine
* for brand-new pages, but noisy once the graph has real data. With
* this armed, each test's recorded edge set grows to include the
* component names it returned through `Inertia::render()`, and
* subsequent replay intersects page-file changes against that set.
*
* Mechanism: listen for `Illuminate\Foundation\Http\Events\RequestHandled`
* on Laravel's event dispatcher. Inertia responses are identifiable by
* either an `X-Inertia` header (XHR / JSON shape) or a `data-page`
* attribute on the root `<div id="app">` (full HTML shape). Both carry
* the component name in a structured payload we can parse cheaply.
*
* Same dep-free handshake as `BladeEdges` / `TableTracker`: string
* class lookup + method-capability probes so Pest's `require` stays
* Laravel-free.
*
* @internal * @internal
*/ */
final class InertiaEdges final class InertiaEdges

View File

@ -5,29 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia; namespace Pest\Plugins\Tia;
/** /**
* Fallback parser for ES module imports under `resources/js/`.
*
* Used only when the Node helper (`bin/pest-tia-vite-deps.mjs`) is
* unavailable — typically when Node isn't on `PATH` or the user's
* `vite.config.*` can't be loaded. Pure PHP, so it degrades
* gracefully on locked-down environments but cannot match the
* full-fidelity Vite resolver.
*
* Known limits (intentional — preserving correctness over precision):
* - Only `@/` and `~/` aliases recognised (both resolve to
* `resources/js/`, the community default). Custom aliases from
* `vite.config.ts` are ignored; anything we can't resolve is
* simply skipped and falls through to the watch-pattern safety
* net.
* - Dynamic imports with variable expressions
* (`import(`./${name}`.vue)`) can't be resolved; the literal
* prefix is ignored and the caller over-runs. Safe.
* - Vue SFC `<script>` blocks parsed whole; imports inside
* `<template>` blocks (rare but legal) are not scanned.
*
* Output shape mirrors the Node helper: project-relative source path
* → sorted list of component names of pages that depend on it.
*
* @internal * @internal
*/ */
final class JsImportParser final class JsImportParser

View File

@ -8,35 +8,6 @@ use Symfony\Component\Process\ExecutableFinder;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
/** /**
* Builds a reverse dependency map for the project's JS sources under
* `resources/js/**` — for every source file, the list of Inertia page
* components that transitively import it.
*
* Backed by a Node helper (`bin/pest-tia-vite-deps.mjs`) that boots a
* headless Vite server in middleware mode, walks Vite's own module
* graph for each page entry, and outputs JSON. Uses the project's real
* `vite.config.*`, so aliases, plugins, and SFC transformers produce
* the exact graph Vite itself would use.
*
* Two latency mitigations:
*
* 1. **Content-hash cache** keyed by every file under `resources/js/`
* (path + size + mtime) plus the bytes of `vite.config.*` and the
* pages-directory casing. When inputs are unchanged, the 13s+ Node
* bootstrap is skipped entirely and the previous result is reused.
*
* 2. **Background warmer** — `warmInBackground()` is called at suite
* start. It computes the fingerprint, checks the cache, and only
* spawns Node if a refresh is needed. The subprocess runs in
* parallel with the test suite. By the time `build()` is called at
* flush time, the result is usually already on stdout — `wait()`
* returns instantly. If tests finish faster than Vite boots,
* `build()` simply pays the remainder, never the full bootstrap.
*
* Callers invoke `build()` at record time; results are persisted into
* the graph so replay never re-runs the resolver. On stale-map detection
* the callers decide whether to rebuild.
*
* @internal * @internal
*/ */
final class JsModuleGraph final class JsModuleGraph

View File

@ -8,9 +8,6 @@ use Pest\TestSuite;
use ReflectionClass; use ReflectionClass;
/** /**
* Captures per-test file coverage. Singleton because PCOV/Xdebug have a single global state
* shared across the `Prepared` and `Finished` subscribers.
*
* @internal * @internal
*/ */
final class Recorder final class Recorder

View File

@ -5,10 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia; namespace Pest\Plugins\Tia;
/** /**
* Collects per-test status + message during the run so the graph can persist
* them for faithful replay. PHPUnit's own result cache discards messages
* during serialisation — this collector retains them.
*
* @internal * @internal
*/ */
final class ResultCollector final class ResultCollector

View File

@ -5,22 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia; namespace Pest\Plugins\Tia;
/** /**
* Scopes coverage collection to project source — the directories
* declared in `phpunit.xml`'s `<source>` config plus any other
* top-level project directories that aren't on a hard-coded noise
* list (vendor, caches, IDE/git metadata).
*
* Used by `Recorder` as the per-test filter passed to
* `\pcov\collect(\pcov\inclusive, …)` — pcov tracks every file PHP
* loads, but we only ask for coverage on files inside the project
* source scope, so anything outside (vendor / caches / etc.) is
* dropped before any line counts come back.
*
* Falls back to "every top-level project dir minus the noise list"
* when no `phpunit.xml` / `phpunit.xml.dist` is present or it has no
* `<source>` block — Pest projects without explicit phpunit config
* still get sensible scoping.
*
* @internal * @internal
*/ */
final class SourceScope final class SourceScope

View File

@ -5,29 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia; namespace Pest\Plugins\Tia;
/** /**
* Resolves TIA's on-disk state directory.
*
* Default location: `$HOME/.pest/tia/<project-key>/`. Keeping state in the
* user's home directory (rather than under `vendor/pestphp/pest/`) means:
*
* - `composer install` / path-repo reinstalls don't wipe the graph.
* - The state lives outside the project tree, so there is nothing for
* users to gitignore or accidentally commit.
* - Multiple worktrees of the same repo share one cache naturally.
*
* The project key is derived from the git origin URL when available — a
* CI workflow running on `github.com/org/repo` and a developer's clone
* of the same remote both compute the *same* key, which is what lets the
* CI-uploaded baseline line up with the dev-side reader. When the project
* is not in git, the key falls back to a hash of the absolute path so
* unrelated projects on the same machine stay isolated.
*
* When no home directory is resolvable (`HOME` / `USERPROFILE` both
* unset — the tests-tia sandboxes strip these deliberately, and some
* locked-down CI environments do the same), state falls back to
* `<projectRoot>/.pest/tia/`. That path is project-local but still
* survives composer installs, so the degradation is graceful.
*
* @internal * @internal
*/ */
final class Storage final class Storage

View File

@ -5,30 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia; namespace Pest\Plugins\Tia;
/** /**
* Extracts table names from SQL statements and migration PHP sources.
*
* Two callers, two methods:
*
* - `fromSql()` runs against query strings Laravel's `DB::listen`
* hands us at record time. We only look at DML (`SELECT`, `INSERT`,
* `UPDATE`, `DELETE`) because DDL emitted by `RefreshDatabase` in
* `setUp()` is noise — we don't want every test to end up linked
* to every migration's `CREATE TABLE`.
* - `fromMigrationSource()` reads a migration file on disk at
* replay time and pulls table names out of `Schema::` calls.
* Used in two places:
* 1. For every migration file reported as changed — what
* tables does the current version of this file touch?
* 2. For brand-new migration files that weren't in the graph
* yet, so we never had a chance to observe their DDL.
*
* Regex isn't a parser. CTEs, subqueries, and raw `DB::statement()`
* that reference tables only inside exotic syntax can slip through.
* The direction of that error is under-attribution (a table the test
* genuinely touches but we missed), so the safety net is to keep the
* broad `database/migrations/**` watch pattern as a last resort for
* files that produce an empty extraction.
*
* @internal * @internal
*/ */
final class TableExtractor final class TableExtractor

View File

@ -5,25 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia; namespace Pest\Plugins\Tia;
/** /**
* Laravel-only collaborator: during record mode, attributes every SQL
* table the test body queries to the currently-running test.
*
* Why this exists: the coverage graph can tell us which PHP files a
* test touched but cannot distinguish "this test depends on the
* `users` table" from "this test depends on `questions`". That
* distinction is the whole point of surgical migration invalidation —
* a column rename in `create_questions_table.php` should only re-run
* tests whose body actually queried `questions`.
*
* Mechanism: install a listener on Laravel's event dispatcher that
* subscribes to `Illuminate\Database\Events\QueryExecuted`. Each
* query string is piped through `TableExtractor::fromSql()`; DDL is
* filtered at extraction time so migrations running in `setUp` don't
* attribute every table to every test.
*
* Same dep-free handshake as `BladeEdges`: string class lookup +
* method-capability probes so Pest's `require` stays Laravel-free.
*
* @internal * @internal
*/ */
final class TableTracker final class TableTracker

View File

@ -10,11 +10,6 @@ use Pest\Factories\TestCaseFactory;
use Pest\TestSuite; use Pest\TestSuite;
/** /**
* Watch patterns for frontend assets that affect browser tests.
*
* Uses `BrowserTestIdentifier` from pest-plugin-browser to auto-discover tests
* using `visit()`. Also keeps the `tests/Browser` convention when present.
*
* @internal * @internal
*/ */
final readonly class Browser implements WatchDefault final readonly class Browser implements WatchDefault

View File

@ -7,12 +7,6 @@ namespace Pest\Plugins\Tia\WatchDefaults;
use Composer\InstalledVersions; use Composer\InstalledVersions;
/** /**
* Watch patterns for Inertia.js projects (Laravel or otherwise).
*
* Inertia bridges PHP controllers with JS/TS page components. A change to
* a React / Vue / Svelte page can break assertions in browser tests or
* Inertia-specific feature tests.
*
* @internal * @internal
*/ */
final readonly class Inertia implements WatchDefault final readonly class Inertia implements WatchDefault

View File

@ -7,14 +7,6 @@ namespace Pest\Plugins\Tia\WatchDefaults;
use Composer\InstalledVersions; use Composer\InstalledVersions;
/** /**
* Watch patterns for Laravel projects.
*
* Laravel boots the entire application inside `setUp()` (before PHPUnit's
* `Prepared` event where TIA's coverage window opens). That means PHP files
* loaded during boot — config, routes, service providers, migrations — are
* invisible to the coverage driver. Watch patterns are the only way to
* track them.
*
* @internal * @internal
*/ */
final readonly class Laravel implements WatchDefault final readonly class Laravel implements WatchDefault

View File

@ -7,12 +7,6 @@ namespace Pest\Plugins\Tia\WatchDefaults;
use Composer\InstalledVersions; use Composer\InstalledVersions;
/** /**
* Watch patterns for projects using Livewire.
*
* Livewire components pair a PHP class with a Blade view. A view change can
* break rendering or assertions in feature / browser tests even though the
* PHP side is untouched.
*
* @internal * @internal
*/ */
final readonly class Livewire implements WatchDefault final readonly class Livewire implements WatchDefault

View File

@ -5,8 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia\WatchDefaults; namespace Pest\Plugins\Tia\WatchDefaults;
/** /**
* Baseline watch patterns for any PHP project.
*
* @internal * @internal
*/ */
final readonly class Php implements WatchDefault final readonly class Php implements WatchDefault

View File

@ -7,8 +7,6 @@ namespace Pest\Plugins\Tia\WatchDefaults;
use Composer\InstalledVersions; use Composer\InstalledVersions;
/** /**
* Watch patterns for Symfony projects.
*
* @internal * @internal
*/ */
final readonly class Symfony implements WatchDefault final readonly class Symfony implements WatchDefault

View File

@ -5,13 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia\WatchDefaults; namespace Pest\Plugins\Tia\WatchDefaults;
/** /**
* A set of file-watch patterns that apply when a particular framework,
* library or project layout is detected.
*
* Each implementation probes for the presence of the tool it covers
* (`applicable`) and returns glob → test-directory mappings (`defaults`)
* that are merged into `WatchPatterns`.
*
* @internal * @internal
*/ */
interface WatchDefault interface WatchDefault

View File

@ -8,18 +8,6 @@ use Pest\Plugins\Tia\WatchDefaults\WatchDefault;
use Pest\TestSuite; use Pest\TestSuite;
/** /**
* Maps non-PHP file globs to the tests they should invalidate.
*
* Coverage drivers only see `.php` files. Frontend assets, config files,
* Blade templates, routes and environment files are invisible to the graph.
* Watch patterns bridge the gap: when a changed file matches a glob, every
* test under the associated directory (or the exact associated test file) is
* marked as affected.
*
* Defaults are assembled dynamically from the `WatchDefaults/` registry —
* each implementation probes the current project and contributes patterns
* when applicable. Users extend via `pest()->tia()->watch(…)`.
*
* @internal * @internal
*/ */
final class WatchPatterns final class WatchPatterns

View File

@ -8,25 +8,6 @@ use Pest\Contracts\Restarter;
use Pest\Plugins\Tia; use Pest\Plugins\Tia;
/** /**
* Re-execs the PHP process with `pcov.directory` pinned to the project
* root so pcov never instruments anything outside it (vendor, system
* includes, etc.).
*
* pcov reads `pcov.directory` once, on the first file it instruments —
* setting it via `ini_set()` from inside the test runner is too late
* for files already compiled by Composer's autoloader. Restarting with
* `-dpcov.directory=<root>` means *every* file pcov sees is filtered
* correctly.
*
* Only fires when ALL of these hold:
* 1. The pcov extension is loaded.
* 2. TIA is enabled for this run (see {@see Tia::isEnabledForRun()} —
* either `--tia` on the CLI or `pest()->tia()->always()`); plain
* `pest` runs are unaffected.
* 3. The current `pcov.directory` differs from the project root.
* 4. We are not already the restarted process — guarded by an env
* sentinel so a single round-trip is enough.
*
* @internal * @internal
*/ */
final class PcovRestarter implements Restarter final class PcovRestarter implements Restarter

View File

@ -10,10 +10,6 @@ use PHPUnit\Event\Test\Finished;
use PHPUnit\Event\Test\FinishedSubscriber; use PHPUnit\Event\Test\FinishedSubscriber;
/** /**
* Fires last for each test, after the outcome subscribers. Records the exact
* assertion count so replay can emit the same `addToAssertionCount()` instead
* of a hardcoded value.
*
* @internal * @internal
*/ */
final readonly class EnsureTiaAssertionsAreRecordedOnFinished implements FinishedSubscriber final readonly class EnsureTiaAssertionsAreRecordedOnFinished implements FinishedSubscriber

View File

@ -9,9 +9,6 @@ use PHPUnit\Event\Test\Finished;
use PHPUnit\Event\Test\FinishedSubscriber; use PHPUnit\Event\Test\FinishedSubscriber;
/** /**
* Stops PCOV collection after each test and merges the covered files into the
* TIA recorder's aggregate map. No-op unless the recorder is active.
*
* @internal * @internal
*/ */
final readonly class EnsureTiaCoverageIsFlushed implements FinishedSubscriber final readonly class EnsureTiaCoverageIsFlushed implements FinishedSubscriber

View File

@ -10,10 +10,6 @@ use PHPUnit\Event\Test\Prepared;
use PHPUnit\Event\Test\PreparedSubscriber; use PHPUnit\Event\Test\PreparedSubscriber;
/** /**
* Starts PCOV collection before each test. Pest tests start from
* `Testable::setUp()` so Laravel boot is covered; this subscriber remains the
* fallback for PHPUnit-style tests and is idempotent for Pest tests.
*
* @internal * @internal
*/ */
final readonly class EnsureTiaCoverageIsRecorded implements PreparedSubscriber final readonly class EnsureTiaCoverageIsRecorded implements PreparedSubscriber

View File

@ -10,14 +10,6 @@ use PHPUnit\Event\Test\Prepared;
use PHPUnit\Event\Test\PreparedSubscriber; use PHPUnit\Event\Test\PreparedSubscriber;
/** /**
* Starts a per-test recording window on Prepared. Sibling subscribers
* (`EnsureTia*`) close it with the outcome and the assertion count so the
* graph can persist everything needed for faithful replay.
*
* Why one subscriber per event: PHPUnit's `TypeMap::map()` picks only the
* first subscriber interface it finds on a class, so one class cannot fan
* out to multiple events — each event needs its own subscriber class.
*
* @internal * @internal
*/ */
final readonly class EnsureTiaResultsAreCollected implements PreparedSubscriber final readonly class EnsureTiaResultsAreCollected implements PreparedSubscriber

View File

@ -8,11 +8,6 @@ use Pest\Contracts\TestCaseFilter;
use Pest\Plugins\Tia\Graph; use Pest\Plugins\Tia\Graph;
/** /**
* Accepts a test file only if it is in the TIA-computed affected set.
*
* Falls back to accepting when the graph has no record of the file (new tests
* must always run) or when the file is outside the project root.
*
* @internal * @internal
*/ */
final readonly class TiaTestCaseFilter implements TestCaseFilter final readonly class TiaTestCaseFilter implements TestCaseFilter