diff --git a/src/Testing/DesignGuard.php b/src/Testing/DesignGuard.php index fb55d098..089508c4 100644 --- a/src/Testing/DesignGuard.php +++ b/src/Testing/DesignGuard.php @@ -3,9 +3,11 @@ namespace NoNameWeb\LivewireMaterial\Testing; use Illuminate\Support\Str; +use Illuminate\View\Compilers\ComponentTagCompiler; use NoNameWeb\LivewireMaterial\Support\Layout; use NoNameWeb\LivewireMaterial\Support\Stylesheets; use NoNameWeb\LivewireMaterial\Support\SvgFile; +use RuntimeException; use SplFileInfo; use Symfony\Component\Finder\Finder; @@ -1227,15 +1229,31 @@ class DesignGuard } /** - * Whether the application defines its own component of this name — an anonymous - * `resources/views/components/.blade.php`, found the way Blade itself falls back to it - * (the base view finder's `components.`, which `resource_path('views/components')` - * feeds by default), or an `App\View\Components` class. Either wins over this package's - * `` in Blade's own resolution order. + * Whether the application defines its own component of this name, found the way Blade itself + * looks before it reaches this package's anonymous path: an alias registered with + * `Blade::component()`, a class under the application's `View\\Components` namespace (its own + * root namespace, as Blade guesses it), or an anonymous + * `resources/views/components/.blade.php` (the base view finder's `components.`). */ protected function shadowedByApplication(string $name): bool { - return view()->exists('components.'.$name) || class_exists('App\\View\\Components\\'.Str::studly($name)); + if (view()->exists('components.'.$name)) { + return true; + } + + $blade = app('blade.compiler'); + $compiler = new ComponentTagCompiler($blade->getClassComponentAliases(), $blade->getClassComponentNamespaces(), $blade); + + try { + $class = $compiler->guessClassName($name); + } catch (RuntimeException) { + return false; // No application namespace to guess a class in. + } + + return isset($blade->getClassComponentAliases()[$name]) + || $compiler->findClassByComponent($name) !== null + || class_exists($class) + || class_exists($class.'\\'.Str::afterLast($class, '\\')); } /** diff --git a/tests/Feature/DesignGuardTest.php b/tests/Feature/DesignGuardTest.php index 04e6007e..0062554b 100644 --- a/tests/Feature/DesignGuardTest.php +++ b/tests/Feature/DesignGuardTest.php @@ -486,8 +486,12 @@ it('reports a package tag the application shadows with its own anonymous compone }); it('reports a package tag the application shadows with its own component class', function () { - if (! class_exists('App\View\Components\Card', false)) { - eval('namespace App\View\Components; class Card {}'); + // Under a namespace of its own, which Blade's class guess reads from the application, so the + // class cannot shadow `` for any later test in this process. + (fn () => $this->namespace = 'DesignGuardShadow\\')->call(app()); + + if (! class_exists('DesignGuardShadow\View\Components\Card', false)) { + eval('namespace DesignGuardShadow\View\Components; class Card {}'); } $violations = fixtureRelative(DesignGuard::scan(realpath(GUARD_FIXTURES.'/stylesheets/views/shadow-class.blade.php')) @@ -539,8 +543,11 @@ it('never reads a Tailwind-shaped word out of running text', function () { expect($violations)->toBe([]); }); -it('passes the package\'s own views', function () { - $violations = DesignGuard::scan([__DIR__.'/../../resources/views', __DIR__.'/../../resources/js', __DIR__.'/../../src'])->violations(); +it('passes the package\'s own views, showcase and Workbench, every stylesheet imported through all.css', function () { + $root = realpath(__DIR__.'/../..'); + $paths = [$root.'/resources/views', $root.'/resources/js', $root.'/src', $root.'/workbench/resources']; - expect($violations)->toBe([]); + expect(DesignGuard::scan($paths)->missingStylesheets($root.'/resources/css/all.css')->violations())->toBe([]) + // …and the stylesheet check is not vacuous there: the showcase's plain tags do need imports. + ->and(DesignGuard::scan($root.'/resources/views/showcase')->missingStylesheets(realpath(GUARD_FIXTURES.'/stylesheets/foundation-only.css'))->violations())->not->toBe([]); });