mirror of
https://github.com/pestphp/pest.git
synced 2026-06-05 10:52:14 +02:00
wip
This commit is contained in:
@ -367,6 +367,15 @@ YAML;
|
|||||||
return $m[1];
|
return $m[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSH URL form: ssh://[user@]github.com[:port]/org/repo(.git).
|
||||||
|
// Some teams configure this explicitly to pin the SSH port; the
|
||||||
|
// colon-separated form above doesn't match. Mirrors the parser
|
||||||
|
// in `Storage::originIdentity` so the same remote produces the
|
||||||
|
// same project key for both storage and remote-fetch.
|
||||||
|
if (preg_match('#^ssh://(?:[^@/]+@)?github\.com(?::\d+)?/([\w.-]+/[\w.-]+?)(?:\.git)?/?$#i', $url, $m) === 1) {
|
||||||
|
return $m[1];
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ namespace Pest\Plugins\Tia;
|
|||||||
|
|
||||||
use Pest\Support\Container;
|
use Pest\Support\Container;
|
||||||
use PHPUnit\Framework\TestStatus\TestStatus;
|
use PHPUnit\Framework\TestStatus\TestStatus;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File-level Test Impact Analysis graph.
|
* File-level Test Impact Analysis graph.
|
||||||
@ -319,7 +320,21 @@ final class Graph
|
|||||||
if ($newJsFiles !== []) {
|
if ($newJsFiles !== []) {
|
||||||
$freshMap = JsModuleGraph::buildStrict($this->projectRoot);
|
$freshMap = JsModuleGraph::buildStrict($this->projectRoot);
|
||||||
|
|
||||||
if ($freshMap !== null) {
|
if ($freshMap === null) {
|
||||||
|
// Vite resolver was unavailable (Node missing, cold-start
|
||||||
|
// timeout, vite.config refused to load). Falling back to
|
||||||
|
// the broad watch pattern is the correct call, but
|
||||||
|
// doing so silently can make a slow replay feel
|
||||||
|
// inexplicable — surface a single line so the user
|
||||||
|
// knows precision was downgraded for these files.
|
||||||
|
$output = Container::getInstance()->get(OutputInterface::class);
|
||||||
|
if ($output instanceof OutputInterface) {
|
||||||
|
$output->writeln(sprintf(
|
||||||
|
' <fg=yellow>TIA</> Vite resolver unavailable — falling back to watch pattern for %d new JS file(s).',
|
||||||
|
count($newJsFiles),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
foreach ($newJsFiles as $rel) {
|
foreach ($newJsFiles as $rel) {
|
||||||
$pages = $freshMap[$rel] ?? [];
|
$pages = $freshMap[$rel] ?? [];
|
||||||
|
|
||||||
|
|||||||
@ -100,21 +100,19 @@ final class Recorder
|
|||||||
if (function_exists('pcov\\start')) {
|
if (function_exists('pcov\\start')) {
|
||||||
$this->driver = 'pcov';
|
$this->driver = 'pcov';
|
||||||
$this->driverAvailable = true;
|
$this->driverAvailable = true;
|
||||||
} elseif (function_exists('xdebug_start_code_coverage')) {
|
} elseif (function_exists('xdebug_start_code_coverage') && function_exists('xdebug_info')) {
|
||||||
// Xdebug is loaded. Probe whether coverage mode is active by
|
// Xdebug 3+ exposes the active mode set via `xdebug_info`,
|
||||||
// attempting a start — it emits E_WARNING when the mode is off.
|
// so we can ask directly instead of probing with a
|
||||||
// We capture the warning via a temporary error handler.
|
// start/stop pair. The probe approach used to emit
|
||||||
$probeOk = true;
|
// E_WARNING when coverage mode was off; with monitoring
|
||||||
set_error_handler(static function () use (&$probeOk): bool {
|
// agents (Sentry, Bugsnag) hooked into the error
|
||||||
$probeOk = false;
|
// handler stack that warning could be reported as a
|
||||||
|
// real error. `xdebug_info('mode')` is silent and
|
||||||
|
// returns the active modes as a list, so a presence
|
||||||
|
// check is enough.
|
||||||
|
$modes = \xdebug_info('mode');
|
||||||
|
|
||||||
return true;
|
if (is_array($modes) && in_array('coverage', $modes, true)) {
|
||||||
});
|
|
||||||
\xdebug_start_code_coverage();
|
|
||||||
restore_error_handler();
|
|
||||||
|
|
||||||
if ($probeOk) {
|
|
||||||
\xdebug_stop_code_coverage(false);
|
|
||||||
$this->driver = 'xdebug';
|
$this->driver = 'xdebug';
|
||||||
$this->driverAvailable = true;
|
$this->driverAvailable = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -105,21 +105,27 @@ final class TableExtractor
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return list<string> Table names referenced by `Schema::` calls
|
* @return list<string> Table names referenced by `Schema::` calls,
|
||||||
* OR raw DDL statements in the given migration
|
* raw DDL, or DML inside the given migration
|
||||||
* file contents. Empty when nothing matches —
|
* file contents. Empty when nothing matches —
|
||||||
* callers treat that as "fall back to the
|
* callers treat that as "fall back to the
|
||||||
* broad watch pattern".
|
* broad watch pattern".
|
||||||
*
|
*
|
||||||
* Two passes:
|
* Three passes:
|
||||||
* 1. `Schema::create|table|drop|dropIfExists|dropColumn[s]|rename`
|
* 1. `Schema::create|table|drop|dropIfExists|dropColumn[s]|rename`
|
||||||
* captures the conventional Laravel migration shape.
|
* captures the conventional Laravel migration shape.
|
||||||
* 2. Raw DDL fallback: scans for `CREATE / ALTER / DROP /
|
* 2. Raw DDL fallback: scans for `CREATE / ALTER / DROP /
|
||||||
* TRUNCATE / RENAME TABLE <name>` patterns inside string
|
* TRUNCATE / RENAME TABLE <name>` patterns inside string
|
||||||
* literals (i.e. `DB::statement('CREATE TABLE …')`,
|
* literals (i.e. `DB::statement('CREATE TABLE …')`,
|
||||||
* `DB::unprepared('ALTER TABLE …')`). False positives possible
|
* `DB::unprepared('ALTER TABLE …')`).
|
||||||
* if the same syntax appears in a comment or unrelated string,
|
* 3. DML inside migration bodies — `INSERT INTO`, `UPDATE … SET`,
|
||||||
* but over-attribution is correctness-safe.
|
* `DELETE FROM`, and Laravel's fluent `DB::table('foo')`.
|
||||||
|
* Catches the seeded-lookup-table case where a migration
|
||||||
|
* populates rows that tests later read.
|
||||||
|
*
|
||||||
|
* False positives possible when the same syntax appears in a
|
||||||
|
* comment or unrelated string, but over-attribution is
|
||||||
|
* correctness-safe.
|
||||||
*/
|
*/
|
||||||
public static function fromMigrationSource(string $php): array
|
public static function fromMigrationSource(string $php): array
|
||||||
{
|
{
|
||||||
@ -157,6 +163,33 @@ final class TableExtractor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pass 3: DML inside migration bodies. Migrations that seed
|
||||||
|
// lookup tables via `DB::statement('INSERT INTO roles …')`,
|
||||||
|
// `DB::table('statuses')->insert(…)`, `UPDATE foo SET …`, or
|
||||||
|
// `DELETE FROM bar` are common in Laravel. Without picking
|
||||||
|
// these up, an edit to the seed payload would route through
|
||||||
|
// only the schema'd tables and silently skip every test that
|
||||||
|
// reads from the populated table. Fluent-builder calls
|
||||||
|
// (`DB::table('x')`) and raw SQL strings are both covered.
|
||||||
|
$dmlPatterns = [
|
||||||
|
'/INSERT\s+(?:IGNORE\s+)?INTO\s+["`\[]?(\w+)["`\]]?/i',
|
||||||
|
'/UPDATE\s+["`\[]?(\w+)["`\]]?\s+SET\b/i',
|
||||||
|
'/DELETE\s+FROM\s+["`\[]?(\w+)["`\]]?/i',
|
||||||
|
'/DB::table\(\s*[\'"]([^\'"]+)[\'"]\s*\)/',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($dmlPatterns as $pattern) {
|
||||||
|
if (preg_match_all($pattern, $php, $matches) === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach ($matches[1] as $name) {
|
||||||
|
$lower = strtolower($name);
|
||||||
|
if (! self::isSchemaMeta($lower)) {
|
||||||
|
$tables[$lower] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$out = array_keys($tables);
|
$out = array_keys($tables);
|
||||||
sort($out);
|
sort($out);
|
||||||
|
|
||||||
|
|||||||
@ -43,12 +43,18 @@ final readonly class Inertia implements WatchDefault
|
|||||||
'resources/js/Pages/**/*.tsx' => [$browserDir],
|
'resources/js/Pages/**/*.tsx' => [$browserDir],
|
||||||
'resources/js/Pages/**/*.jsx' => [$browserDir],
|
'resources/js/Pages/**/*.jsx' => [$browserDir],
|
||||||
'resources/js/Pages/**/*.svelte' => [$browserDir],
|
'resources/js/Pages/**/*.svelte' => [$browserDir],
|
||||||
|
'resources/js/Pages/**/*.ts' => [$browserDir],
|
||||||
|
'resources/js/Pages/**/*.js' => [$browserDir],
|
||||||
|
|
||||||
// Shared layouts / components consumed by pages.
|
// Shared layouts / components consumed by pages.
|
||||||
'resources/js/Layouts/**/*.vue' => [$browserDir],
|
'resources/js/Layouts/**/*.vue' => [$browserDir],
|
||||||
'resources/js/Layouts/**/*.tsx' => [$browserDir],
|
'resources/js/Layouts/**/*.tsx' => [$browserDir],
|
||||||
|
'resources/js/Layouts/**/*.ts' => [$browserDir],
|
||||||
|
'resources/js/Layouts/**/*.js' => [$browserDir],
|
||||||
'resources/js/Components/**/*.vue' => [$browserDir],
|
'resources/js/Components/**/*.vue' => [$browserDir],
|
||||||
'resources/js/Components/**/*.tsx' => [$browserDir],
|
'resources/js/Components/**/*.tsx' => [$browserDir],
|
||||||
|
'resources/js/Components/**/*.ts' => [$browserDir],
|
||||||
|
'resources/js/Components/**/*.js' => [$browserDir],
|
||||||
|
|
||||||
// SSR entry point.
|
// SSR entry point.
|
||||||
'resources/js/ssr.js' => [$browserDir],
|
'resources/js/ssr.js' => [$browserDir],
|
||||||
|
|||||||
Reference in New Issue
Block a user