blade(''); expect($html) ->toContain('x-persist="material-toast"') ->toContain('x-data="materialSnackbar"') ->toContain('bg-inverse-surface') ->toContain('justify-center'); }); it('draws no state icon, and reaches 48px from its 40px controls', function () { $html = (string) $this->blade(''); // M3 tells you to avoid an icon in a snackbar; the type is left to pick the announcement role. expect(substr_count($html, 'toBe(1) ->and($html)->not->toContain('text-inverse-success') ->not->toContain('opacity-80') ->toContain('data-toast-action class="state-layer focus-ring touch-target') ->toContain('state-layer focus-ring touch-target inline-flex size-10'); }); it('grows to M3\'s two-line height, and wraps the action below on a compact window', function () { $html = (string) $this->blade(''); // SnackbarTokens: 48dp for one line, 68dp for two — a description is that second line. expect($html) ->toContain('x-bind:class="current.description ? \'min-h-17\' : \'min-h-12\'"') ->toContain('flex-wrap') // M3's "two lines with longer action": the action leaves the text's line below `medium`. ->toContain('x-bind:class="current.description && current.action ? \'max-medium:basis-full\' : \'\'"') ->toContain('touch-target ms-auto h-10'); }); it('can sit at the start', function () { expect((string) $this->blade(''))->toContain('justify-start'); }); it('marks the snackbar and its action for tests and styling', function () { $html = (string) $this->blade(''); expect($html) ->toMatch('/]*\bdata-toast\b/') ->toMatch('/]*\bdata-toast-action\b[^>]*x-on:click="act\(\)"/'); }); it('keeps the live region in the page, polite, around the snackbar that comes and goes', function () { $html = (string) $this->blade(''); expect($html) ->toMatch('/]*x-data="materialSnackbar"[^>]*aria-live="polite"/') ->toContain('aria-atomic="true"') ->toContain("x-bind:role=\"current && (current.type === 'error' || current.type === 'warning') ? 'alert' : 'status'\"") ->and(substr_count($html, 'aria-live'))->toBe(1); });