Press once whatever a second press would open over or undo
tests / feature (8.4) (push) Successful in 1m50s
tests / feature (8.5) (push) Successful in 1m54s
tests / browser (chrome, chromium) (push) Successful in 7m52s
tests / browser (firefox, firefox) (push) Successful in 11m56s
tests / browser (safari, webkit) (push) Successful in 12m35s

The browser plugin runs every call on a page again when its first
attempt takes over a second, and on the runner a press can. For most
presses that costs nothing. For two kinds it breaks the test: a press
that opens a dialog, a sheet, a full-screen view or a modal rail over
its own trigger, whose second press can never land, and a press that
changes state a second one would change again — a menu trigger, a
toggle, a chip, a range picker's day, a paging key, a Save. The bottom
sheet with preset heights timed out on WebKit that way after the date
picker had on Firefox.

Every such press in the browser suite now goes through pressOnce(), 253
of them across sixteen files, not only the ones the runner happened to
catch; focus moves inside an open view, Escape, links and plain "set"
actions keep the retry, which is harmless for them.

Two samples that were still racing the machine:

- The standard side sheet's exit is caught half-way with its motion
  stretched, as every other mid-exit sample is, and fullSpeed() takes
  the stretch off again before the test times a reopen against the real
  exit. Under load on Linux WebKit it had failed two runs in five; it
  passes ten in ten.
- The switch-and-checkbox row measures its widths once, so it now waits
  for the resize to land and the brand face to load before it does.

Browser 300 passed on Firefox and WebKitGTK in a Linux container held to
two busy cores, and on Chrome, Firefox and WebKit on macOS. Feature 1159
passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
surtic86
2026-09-18 16:15:25 +02:00
co-authored by Claude Opus 5
parent ad230e0f48
commit ebdc2ef2e1
17 changed files with 1170 additions and 578 deletions
+65 -31
View File
@@ -103,11 +103,14 @@ it('floats the label while the field has focus or holds a value', function () {
});
it('clears a field and tells Livewire', function () {
fieldProbe()
$page = fieldProbe()
->type('#name-field', 'Holiday')
->assertSeeIn('#name', 'Holiday')
->click('[data-md-field-clear]')
->assertScript("document.querySelector('#name-field').value === ''")
->assertSeeIn('#name', 'Holiday');
// The clear button vanishes once its field is empty: pressOnce(), tests/Pest.php.
pressOnce($page)->click('[data-md-field-clear]');
$page->assertScript("document.querySelector('#name-field').value === ''")
->assertScript("document.querySelector('#name').textContent === ''")
->assertScript("document.activeElement.id === 'name-field'");
});
@@ -123,13 +126,18 @@ it('copies a field\'s value and says so', function () {
});
it('shows and hides a password', function () {
fieldProbe()
->type('#password-field', 'secret')
->click('[data-md-field-reveal]')
->assertScript("document.querySelector('#password-field').type === 'text'")
->assertAttribute('[data-md-field-reveal]', 'aria-label', 'Hide password')
->click('[data-md-field-reveal]')
->assertScript("document.querySelector('#password-field').type === 'password'");
$page = fieldProbe()
->type('#password-field', 'secret');
// The reveal button toggles on every press: pressOnce(), tests/Pest.php.
pressOnce($page)->click('[data-md-field-reveal]');
$page->assertScript("document.querySelector('#password-field').type === 'text'")
->assertAttribute('[data-md-field-reveal]', 'aria-label', 'Hide password');
pressOnce($page)->click('[data-md-field-reveal]');
$page->assertScript("document.querySelector('#password-field').type === 'password'");
});
it('shows the server\'s error in place of the hint', function () {
@@ -162,8 +170,10 @@ it('opens the customizable select picker as M3\'s menu, where the browser suppor
$page->assertScript("getComputedStyle({$select}).appearance === 'base-select'");
$page->click('#hours-field')
->assertScript("{$select}.matches(':open')")
// Opens the customizable select's list over the field that triggers it: pressOnce(), tests/Pest.php.
pressOnce($page)->click('#hours-field');
$page->assertScript("{$select}.matches(':open')")
// M3's menu row height (menu-item.css's 48px), not the browser's own tiny option row.
->assertScript("parseFloat(getComputedStyle({$option}).minBlockSize) === 48");
});
@@ -250,30 +260,42 @@ it('keeps a partly ticked checkbox in step with the server', function () {
$page = fieldProbe()->assertScript("! {$all}.indeterminate");
$page->click('label[for="file-a"]')
->assertSeeIn('#files', 'a')
// A checkbox toggles on every press: pressOnce(), tests/Pest.php.
pressOnce($page)->click('label[for="file-a"]');
$page->assertSeeIn('#files', 'a')
->assertScript("{$all}.hasAttribute('data-md-indeterminate') && {$all}.indeterminate");
$page->click('label[for="file-b"]')
->click('label[for="file-c"]')
->assertSeeIn('#files', 'a,b,c')
pressOnce($page)->click('label[for="file-b"]')
->click('label[for="file-c"]');
$page->assertSeeIn('#files', 'a,b,c')
->assertScript("! {$all}.hasAttribute('data-md-indeterminate') && ! {$all}.indeterminate");
});
it('moves between radio buttons with the arrow keys', function () {
fieldProbe()
->keys('input[type="radio"][value="anyone"]', 'ArrowDown')
->assertSeeIn('#audience', 'team')
$page = fieldProbe();
// A two-option radio group: a second press of the same arrow cycles back: pressOnce(),
// tests/Pest.php.
pressOnce($page)->keys('input[type="radio"][value="anyone"]', 'ArrowDown');
$page->assertSeeIn('#audience', 'team')
->assertScript("document.querySelector('input[type=\"radio\"][value=\"team\"]').checked");
});
it('turns a switch on from the keyboard and from its label', function () {
$page = fieldProbe()
->keys('#public', 'Space')
->assertSeeIn('#public-state', 'true');
$page = fieldProbe();
$page->click('label[for="public"]')
->assertSeeIn('#public-state', 'false')
// Space toggles a switch on every press: pressOnce(), tests/Pest.php.
pressOnce($page)->keys('#public', 'Space');
$page->assertSeeIn('#public-state', 'true');
// A switch's label toggles it, same as the switch itself: pressOnce(), tests/Pest.php.
pressOnce($page)->click('label[for="public"]');
$page->assertSeeIn('#public-state', 'false')
->assertAttribute('#public', 'role', 'switch');
});
@@ -394,13 +416,19 @@ it('keeps a focused field\'s focus edge under the pointer, as M3 layers focus ov
// A customizable select reads as focused while its menu is open, though focus is then on an
// option in the top layer: in primary under the pointer, and in error when the field is.
if ($page->script("CSS.supports('appearance', 'base-select')") === true) {
$page->click('#select-field')->assertScript("document.getElementById('select-field').matches(':open')");
// Opens the customizable select's list over the field that triggers it: pressOnce(),
// tests/Pest.php.
pressOnce($page)->click('#select-field');
$page->assertScript("document.getElementById('select-field').matches(':open')");
expect($page->script(settledEdge('select-field')))->toBe('2px '.$role('primary'));
$page->keys('#select-field', 'Escape')->assertScript("! document.getElementById('select-field').matches(':open')");
$page->click('#invalid-select-field')->assertScript("document.getElementById('invalid-select-field').matches(':open')");
pressOnce($page)->click('#invalid-select-field');
$page->assertScript("document.getElementById('invalid-select-field').matches(':open')");
expect($page->script(settledEdge('invalid-select-field')))->toBe('2px '.$role('error'));
}
@@ -513,8 +541,10 @@ it('draws the switch\'s handle at SwitchTokens\' sizes and centres, off and on',
$page = fieldProbe()->assertScript("{$handle} === '52,32,16,16,16'");
$page->keys('#public', 'Space')
->assertSeeIn('#public-state', 'true')
// Space toggles a switch on every press: pressOnce(), tests/Pest.php.
pressOnce($page)->keys('#public', 'Space');
$page->assertSeeIn('#public-state', 'true')
->wait(0.7)
->assertScript("{$handle} === '52,32,24,36,16'");
});
@@ -560,7 +590,11 @@ it('keeps a switch\'s track and a checkbox\'s box whole beside long text in a ro
})()";
// A phone's window: the row is 329px across, less than the text beside each control would take.
$page = ready(visit('/selection-row-probe')->resize(393, 800), alpine: false, livewire: false);
$page = ready(visit('/selection-row-probe')->resize(393, 800), alpine: false, livewire: false)
// Measured once, so measured only when the layout is final: the resize has landed and the
// brand face has replaced its fallback, either of which moves every width below.
->assertScript("window.innerWidth === 393 && document.fonts.status === 'loaded'")
->assertScript(settled('document.documentElement', subtree: true));
expect($page->script($control('row-bare-switch', '[data-md-switch]', '[data-md-toggle]')))->toBe([52, true, true])
->and($page->script($control('row-labelled-switch', '[data-md-switch]', '[data-md-toggle]')))->toBe([52, true, true])