blade(<<<'BLADE' BLADE); preg_match('/anchor-name: (--material-menu-([a-z0-9]+))/', $html, $anchor); expect($anchor)->not->toBeEmpty() ->and($html) ->toContain('x-data="materialMenu"') ->toContain('') ->toContain('popover="auto"') ->toContain('role="menu"') ->toContain('aria-label="Share actions"') ->toContain("id=\"material-menu-{$anchor[2]}\"") ->toContain("position-anchor: {$anchor[1]}") ->toContain('bg-surface-container-low') ->toContain('[position-area:bottom_span-right]'); }); it('opens at the position asked for, in the vibrant colours on request', function () { $html = (string) $this->blade(''); expect($html)->toContain('[position-area:top_span-left]')->toContain('bg-tertiary-container text-on-tertiary-container'); }); it('draws an item as a menuitem button', function () { $html = (string) $this->blade(''); expect($html) ->toContain('role="menuitem"') ->toContain('tabindex="-1"') ->toContain('type="button"') ->toContain('wire:click="download"') ->toContain('Download') ->toContain('⌘D') ->toContain('As a ZIP') ->toContain('size-5 text-on-surface-variant'); }); it('makes a selectable item a menuitemcheckbox', function () { expect((string) $this->blade('')) ->toContain('role="menuitemcheckbox"') ->toContain('aria-checked="true"') ->toContain('bg-tertiary-container') ->toContain('data-keep-open') ->and((string) $this->blade('')) ->toContain('aria-checked="false"'); }); it('marks the page an item leads to, and carries a badge', function () { $html = (string) $this->blade(''); expect($html) ->toContain('role="menuitem"') ->toContain('aria-current="page"') ->not->toContain('aria-checked') ->toContain('bg-secondary-container text-on-secondary-container') ->toContain('>3<') ->and((string) $this->blade('')) ->not->toContain('aria-current') ->not->toContain('secondary-container'); }); it('links an item, and keeps a disabled one out of reach', function () { $this->blade('') ->assertSee('href="/settings"', false) ->assertSee('wire:navigate', false); $this->blade('') ->assertSee('aria-disabled="true"', false) ->assertSee('pointer-events-none', false); }); it('separates and labels groups', function () { $this->blade('')->assertSee('role="separator"', false); $this->blade('') ->assertSee('role="group" aria-label="Sort by"', false); }); it('adds icon-class to the leading icon, over its own colour but not over disabled', function () { $leading = fn (string $html): string => preg_match('/]*class="([^"]*)"/', $html, $icon) ? $icon[1] : ''; expect($leading((string) $this->blade(''))) ->toBe('shrink-0 size-5 [:where(&)]:text-on-surface-variant text-sport-run') ->and($leading((string) $this->blade(''))) ->toBe('shrink-0 size-5 [:where(&)]:text-on-tertiary-container text-sport-run') ->and($leading((string) $this->blade(''))) ->toBe('shrink-0 size-5 text-sport-run text-on-surface/38!') ->and($leading((string) $this->blade(''))) ->toBe('shrink-0 size-5 text-on-surface-variant') ->and((string) $this->blade('')) ->not->toContain('text-sport-run'); });