Files
livewire-material/tests/Feature/Components/DataTest.php
T
Andreas Reinhold / reiniandClaude Sonnet 5 6897306e18 Prove no Tailwind utility class remains in resources/ or tests/
StylesheetsTest.php's new test runs DesignGuard's own family table (check
(i)) over resources/views, resources/js, src, tests/Browser and
tests/Feature — the guard turned on the package's own source, so the two
never drift apart. tests/Feature/DesignGuardTest.php and
tests/Fixtures/design-guard/ carry Tailwind on purpose and are left out.

The scan found Tailwind-shaped class strings used as caller classes in 21
test files (a component test proving a caller's class lands on the root or
a caller's size wins, mostly), left over from before Tailwind cleared the
whole stack: replaced with neutral application-style names (`app-hero-icon`)
or, where one already carries the right meaning, an `md-*` class
(`md-text-end`, `md-ink-warning`). BreakpointsTest.php's own heredoc probe —
which needs a genuine Tailwind-shaped breakpoint prefix to prove its
detection works — now builds that one string from a placeholder at runtime,
so its own source stays clean for this same scan.

Plan step 42 (Phase F), Part A.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 09:32:36 +02:00

124 lines
7.2 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\Paginator;
use Livewire\Component;
use Livewire\Livewire;
use Livewire\WithPagination;
use NoNameWeb\LivewireMaterial\Tests\Support\ComponentStylesheet;
it('marks a table for its styles and takes a size', function () {
expect((string) $this->blade('<x-table class="app-data-table"><tbody><tr><td>a</td></tr></tbody></x-table>'))
->toContain('<table data-md-table data-md-size="sm" class="app-data-table">')
->and((string) $this->blade('<x-table size="xs" />'))->toContain('data-md-size="xs"')
->and((string) $this->blade('<x-table size="huge" />'))->toContain('data-md-size="sm"');
});
it('tightens a table only when the caller asks for it', function () {
expect((string) $this->blade('<x-table />'))->not->toContain('data-md-dense')
->and((string) $this->blade('<x-table dense />'))->toContain('data-md-dense');
});
it('draws 52px rows, 36px dense ones, from its stylesheet in the components layer', function () {
$css = (string) file_get_contents(__DIR__.'/../../../resources/css/components/table.css');
expect($css)->toContain('@layer material.components')
->toMatch('/\\[data-md-table\\] \\{\\s*--cell-x: 12px;\\s*--cell-y: var\\(--md-sys-measurement-space200\\);/')
->toMatch('/\\[data-md-table\\]\\[data-md-dense\\] \\{\\s*--cell-y: var\\(--md-sys-measurement-space100\\);/')
->toContain('[data-md-table] :where(tbody tr[aria-selected=\'true\'])')
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/table.css';");
});
it('sorts by its column, ascending first and then flipping', function () {
expect((string) $this->blade('<x-sort-header column="size" :sort-by="[\'column\' => \'name\', \'direction\' => \'asc\']">Size</x-sort-header>'))
->not->toContain('aria-sort')
->toContain('wire:click="$set(\'sortBy\', {&quot;column&quot;:&quot;size&quot;,&quot;direction&quot;:&quot;asc&quot;})"')
->and((string) $this->blade('<x-sort-header column="size" model="order" :sort-by="[\'column\' => \'size\', \'direction\' => \'asc\']" class="md-text-end">Size</x-sort-header>'))
->toContain('aria-sort="ascending"')
->toMatch('/<th data-md-sort-header\\s+data-md-active aria-sort="ascending"\\s+class="md-text-end">/')
->toContain('$set(\'order\', {&quot;column&quot;:&quot;size&quot;,&quot;direction&quot;:&quot;desc&quot;})')
->and((string) $this->blade('<x-sort-header column="size" :sort-by="[\'column\' => \'size\', \'direction\' => \'desc\']">Size</x-sort-header>'))
->toContain('aria-sort="descending"')
->toContain('&quot;direction&quot;:&quot;asc&quot;');
});
it('gives the sort button and every page control a 48px target, from the foundation\'s classes', function () {
$pages = new LengthAwarePaginator(range(1, 10), 95, 10, 3, ['path' => '/shares']);
expect((string) $this->blade('<x-sort-header column="size" :sort-by="[]">Size</x-sort-header>'))
->toMatch('/<button(?=[^>]*data-md-sort-header-button)(?=[^>]*class="md-focus-ring md-touch-target")[^>]*>/')
->toContain('--md-icon-size: 16px')
->not->toContain('data-md-active')
->and((string) $pages->links())
->toContain('<a href="/shares?page=4" class="md-state-layer md-focus-ring md-touch-target" data-md-pagination-step data-md-pagination-page aria-label="Go to page 4">')
->and(ComponentStylesheet::read('pagination')->declarations('[data-md-pagination-step]'))->toMatchArray([
'width' => 'var(--md-sys-measurement-space500)',
'height' => 'var(--md-sys-measurement-space500)',
])
->and(ComponentStylesheet::read('pagination')->has(':is(a, button)[data-md-pagination-step]::after'))->toBeFalse()
->and(ComponentStylesheet::read('pagination')->has(':is(a, button)[data-md-pagination-step]:focus-visible'))->toBeFalse();
});
it('draws Laravel\'s paginators in M3', function () {
$pages = new LengthAwarePaginator(range(1, 10), 95, 10, 3, ['path' => '/shares']);
expect((string) $pages->links())
->toContain('data-md-pagination')
->toContain('<span data-md-pagination-compact>Page 3 of 10</span>')
->toContain('<span data-md-pagination-range>2130 of 95</span>')
// The current page is a plain span, not a live step: it carries no interaction class.
->toContain('<span aria-current="page" data-md-pagination-step data-md-pagination-page>3</span>')
->toContain('href="/shares?page=2" rel="prev"')
->toContain('aria-label="Go to page 4"')
->toContain('data-md-mirror-rtl')
->not->toContain('text-gray')
// A live step (a link or a button) carries only the foundation's interaction classes.
->not->toMatch('/class="(?!md-state-layer md-focus-ring md-touch-target")/');
expect((string) (new Paginator(range(1, 11), 10, 2, ['path' => '/shares']))->links())
->toContain('data-md-pagination')
->toContain('href="/shares?page=1"')
->toContain('href="/shares?page=3"')
->toContain('Previous');
});
it('draws Livewire\'s paginators in M3, wired to its page actions', function () {
Livewire::component('paging-probe', new class extends Component
{
use WithPagination;
public function render(): string
{
return <<<'BLADE'
<div>
{{ (new \Illuminate\Pagination\LengthAwarePaginator(range(1, 10), 95, 10, $this->getPage(), ['path' => '/'])) ->links() }}
</div>
BLADE;
}
});
expect(Livewire::test('paging-probe')->html())
->toContain('data-md-pagination')
->toContain('data-md-pagination-page')
->toContain('wire:click="gotoPage(2, \'page\')"')
->toContain('wire:click="nextPage(\'page\')"')
->not->toContain('text-gray')
// A live step (a link or a button) carries only the foundation's interaction classes.
->not->toMatch('/class="(?!md-state-layer md-focus-ring md-touch-target")/');
});
it('draws the sort header from its stylesheet, its arrow hidden until the column sorts or is hovered', function () {
expect((string) file_get_contents(__DIR__.'/../../../resources/css/components/sort-header.css'))
->toContain('@layer material.components')
->toMatch('/\\[data-md-sort-header\\]:not\\(\\[data-md-active\\]\\) \\[data-md-sort-header-arrow\\] \\{\\s*opacity: 0;/')
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/sort-header.css';");
});
it('draws the paginators from their stylesheet, trading the numbers for "Page n of m" below 600px', function () {
expect((string) file_get_contents(__DIR__.'/../../../resources/css/components/pagination.css'))
->toContain('@layer material.components')
->toMatch('/@media \\(width < 600px\\) \\{\\s*\\[data-md-pagination-page\\],\\s*\\[data-md-pagination-range\\] \\{\\s*display: none;/')
->toMatch('/@media \\(width >= 600px\\) \\{\\s*\\[data-md-pagination-compact\\] \\{\\s*display: none;/')
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/pagination.css';");
});