diff --git a/tests/Browser/BarsTest.php b/tests/Browser/BarsTest.php
index db577655..1a08f147 100644
--- a/tests/Browser/BarsTest.php
+++ b/tests/Browser/BarsTest.php
@@ -9,6 +9,17 @@ class BarsProbe extends Component
{
public string $tab = 'files';
+ public string $deferredTab = 'files';
+
+ public int $deferredUpdates = 0;
+
+ // Only runs while the server processes a request that carries $deferredTab along — never on
+ // the click alone, since a plain wire:model (no `.live`) only changes it locally until then.
+ public function updatedDeferredTab(): void
+ {
+ $this->deferredUpdates++;
+ }
+
public function render(): string
{
return <<<'BLADE'
@@ -27,6 +38,13 @@ class BarsProbe extends Component
Two downloads.
+
+
+
+ Deferred files.
+ Deferred people.
+
+
@@ -36,7 +54,7 @@ class BarsProbe extends Component
-
+
@@ -89,6 +107,30 @@ it('moves between tabs with the arrow keys, skipping disabled ones, and tells Li
->assertSeeIn('#tab', 'activity');
});
+it('sends a request per click with wire:model.live, but nothing until then with plain wire:model', function () {
+ // updatedDeferredTab() on the probe only runs while the server processes a request that
+ // carries $deferredTab with it — never from the click alone — so its counter (not a browser
+ // network hook, which a plain property commit does not reliably fire) tells whether a click
+ // reached the server at all.
+ $page = barsProbe();
+
+ // Plain wire:model is entangled locally: the click switches the panel and its aria-selected at
+ // once, but the property itself only reaches the server on the next request from anything else.
+ $page->click('#deferred-people')
+ ->assertScript("document.querySelector('#deferred-people').getAttribute('aria-selected') === 'true'")
+ ->assertSee('Deferred people.')
+ ->assertSeeIn('#deferred-tab', 'files')
+ ->assertSeeIn('#deferred-updates', '0');
+
+ // wire:model.live sends its own request, which carries the deferred property above along with
+ // it — the one request updates both.
+ $page->click('#share-people')
+ ->assertSeeIn('#tab', 'people')
+ ->assertSeeIn('#deferred-tab', 'people')
+ ->assertSeeIn('#deferred-updates', '1')
+ ->assertNoJavaScriptErrors();
+});
+
it('collapses a medium app bar into its row as the page scrolls, and fades its small title in', function () {
$headline = "getComputedStyle(document.querySelector('#bar [data-md-app-bar-row] [data-md-app-bar-headline]')).opacity";
@@ -146,6 +188,15 @@ it('turns section tabs into a picker on a phone', function () {
->assertScript("! document.querySelector('[data-md-section-nav-picker] [role=\"menuitem\"][href=\"#security\"]').hasAttribute('aria-current')");
});
+it('shares the row width equally between the section nav\'s items, whatever their label', function () {
+ $widths = "[...document.querySelectorAll('[data-md-section-nav] [data-md-tabs-bar] > li')].map((li) => Math.round(li.getBoundingClientRect().width))";
+
+ barsProbe()
+ ->assertScript("{$widths}.length === 3")
+ ->assertScript("new Set({$widths}).size === 1")
+ ->assertNoJavaScriptErrors();
+});
+
class AppBarOverflowProbe extends Component
{
public int $stars = 0;
@@ -217,7 +268,7 @@ it('shows the app bar overflow at exactly 599 and 600px', function () {
it('moves the keyboard through an app bar\'s overflow menu, and closes it once the width no longer shows it', function () {
$page = appBarOverflowProbe()->resize(599, 800);
- $page->click('#bar [data-md-app-bar-overflow="compact"] button')
+ $page->click('#bar [data-md-app-bar-overflow="compact"] [data-md-menu-trigger] button')
->assertScript("document.querySelector('#bar [data-md-app-bar-overflow=\"compact\"] [data-md-menu-popover]').matches(':popover-open')")
->keys(':focus', 'ArrowDown')
->assertScript("document.activeElement.textContent.includes('Star')")
@@ -226,9 +277,12 @@ it('moves the keyboard through an app bar\'s overflow menu, and closes it once t
->keys(':focus', 'Escape')
->assertScript("! document.querySelector('#bar [data-md-app-bar-overflow=\"compact\"] [data-md-menu-popover]').matches(':popover-open')");
+ // Past the reopen guard of the Escape above (menu.js), so the click below is on its own.
+ $page->wait(0.3);
+
// Reopen it, then resize across 600px: the compact menu's trigger is no longer shown, so the
// menu it opened closes rather than staying open anchored to nothing.
- $page->click('#bar [data-md-app-bar-overflow="compact"] button')
+ $page->click('#bar [data-md-app-bar-overflow="compact"] [data-md-menu-trigger] button')
->assertScript("document.querySelector('#bar [data-md-app-bar-overflow=\"compact\"] [data-md-menu-popover]').matches(':popover-open')")
->resize(900, 800)
->assertScript("! document.querySelector('#bar [data-md-app-bar-overflow=\"compact\"] [data-md-menu-popover]').matches(':popover-open')")
@@ -242,7 +296,7 @@ it('runs a wire:click action from either the icon button or its overflow menu it
->assertSeeIn('#stars', '1');
$page->resize(599, 800)
- ->click('#bar [data-md-app-bar-overflow="compact"] button')
+ ->click('#bar [data-md-app-bar-overflow="compact"] [data-md-menu-trigger] button')
->click('[role="menuitem"]:has-text("Star")')
->assertSeeIn('#stars', '2');
});