Bug 1892346 [wpt PR 45792] - Support :user-valid/:user-invalid on multifield inputs, a=testonly

Automatic update from web-platform-tests
Support :user-valid/:user-invalid on multifield inputs

This patch makes :user-valid and :user-invalid start working on inputs
with type date, datetime-local, and time.

I matched the webkit behavior for when to start matching in response to
keyboard input, and added a separate tentative test for that behavior.

Fixed: 328674226
Change-Id: If3c394e43043a0b3d27eac22d0671c6b45b82bc6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5463293
Reviewed-by: Di Zhang <dizhangg@chromium.org>
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1294002}

--

wpt-commits: 6a7c427b1e2f8c5542e4515ae7d73afaa6e0f236
wpt-pr: 45792
This commit is contained in:
Joey Arhar 2024-04-30 16:06:29 +00:00 коммит произвёл moz-wptsync-bot
Родитель d52a52dc4a
Коммит 3286fe8859
3 изменённых файлов: 226 добавлений и 0 удалений

Просмотреть файл

@ -29,6 +29,7 @@
<input placeholder="Required field" required id="required-input"><br>
<textarea placeholder="Required field" required id="required-textarea"></textarea><br>
<input type="checkbox" required id="required-checkbox"><br>
<input type="date" required id="required-date"><br>
<input type="submit" id="submit-button">
<input type="reset" id="reset-button">
</form>
@ -80,12 +81,14 @@ promise_test(async () => {
const requiredInput = document.querySelector("#required-input");
const requiredTextarea = document.querySelector("#required-textarea");
const requiredCheckbox = document.querySelector("#required-checkbox");
const requiredDate = document.querySelector("#required-date");
const submitButton = document.querySelector("#submit-button");
const resetButton = document.querySelector("#reset-button");
assert_false(requiredInput.validity.valid);
assert_false(requiredTextarea.validity.valid);
assert_false(requiredCheckbox.validity.valid);
assert_false(requiredDate.validity.valid);
// The selector can't match because no interaction has happened.
assert_false(requiredInput.matches(":user-valid"), "Initially does not match :user-valid");
assert_false(requiredInput.matches(":user-invalid"), "Initially does not match :user-invalid");
@ -96,6 +99,9 @@ promise_test(async () => {
assert_false(requiredCheckbox.matches(":user-valid"), "Initially does not match :user-valid");
assert_false(requiredCheckbox.matches(":user-invalid"), "Initially does not match :user-invalid");
assert_false(requiredDate.matches(":user-valid"), "Initially does not match :user-valid");
assert_false(requiredDate.matches(":user-invalid"), "Initially does not match :user-invalid");
submitButton.click();
assert_true(requiredInput.matches(":user-invalid"), "Submitted the form, input is validated");
@ -107,6 +113,9 @@ promise_test(async () => {
assert_true(requiredCheckbox.matches(":user-invalid"), "Submitted the form, checkbox is validated");
assert_false(requiredCheckbox.matches(":user-valid"), "Submitted the form, checkbox is validated");
assert_true(requiredDate.matches(":user-invalid"), "Submitted the form, date input is validated");
assert_false(requiredDate.matches(":user-valid"), "Submitted the form, date input is validated");
resetButton.click();
assert_false(requiredInput.matches(":user-valid"), "Reset the form, user-interacted flag is reset");
@ -118,6 +127,9 @@ promise_test(async () => {
assert_false(requiredCheckbox.matches(":user-valid"), "Reset the form, user-interacted flag is reset");
assert_false(requiredCheckbox.matches(":user-invalid"), "Reset the form, user-interacted flag is reset");
assert_false(requiredDate.matches(":user-valid"), "Reset the form, user-interacted flag is reset");
assert_false(requiredDate.matches(":user-invalid"), "Reset the form, user-interacted flag is reset");
// Test programmatic form submission with constraint validation.
form.requestSubmit();
@ -129,6 +141,9 @@ promise_test(async () => {
assert_true(requiredCheckbox.matches(":user-invalid"), "Called form.requestSubmit(), checkbox is validated");
assert_false(requiredCheckbox.matches(":user-valid"), "Called form.requestSubmit(), checkbox is validated");
assert_true(requiredDate.matches(":user-invalid"), "Called form.requestSubmit(), date input is validated");
assert_false(requiredDate.matches(":user-valid"), "Called form.requestSubmit(), date input is validated");
}, ":user-invalid selector properly interacts with submit & reset buttons");
// historical: https://github.com/w3c/csswg-drafts/issues/1329
@ -193,4 +208,38 @@ promise_test(async () => {
assert_true(checkbox.matches(':user-invalid'),
'Checkbox should match :user-invalid after clicking twice.');
}, 'A required checkbox should match :user-invalid if the user unchecks it and blurs.');
promise_test(async () => {
const date = document.getElementById('required-date');
const resetButton = document.getElementById('reset-button');
resetButton.click();
assert_false(date.matches(':user-invalid'),
'date input should not match :user-invalid at the start of the test.');
assert_equals(date.value, '',
'date input should not have a value at the start of the test.');
date.value = '2024-04-15';
assert_false(date.matches(':user-invalid'),
'date should not match :user-invalid after programatically changing value.');
date.value = '';
assert_false(date.matches(':user-invalid'),
'date should not match :user-invalid after programatically changing value.');
const tabKey = '\uE004';
const backspace = '\uE003';
date.focus();
// Press tab twice at the end to make sure that focus has left the input.
await test_driver.send_keys(date, `1${tabKey}1${tabKey}1234${tabKey}${tabKey}`);
assert_not_equals(document.activeElement, date,
'Pressing tab twice after typing in the date should have blurred the input.');
assert_equals(date.value, '1234-01-01',
'Date input value should match the testdriver input.');
date.focus();
await test_driver.send_keys(date, backspace);
assert_equals(date.value, '',
'Date input value should be cleared when deleting one of the sub-values.');
assert_true(date.matches(':user-invalid'),
'Date input should match :user-invalid after typing in an invalid value.');
}, 'A required date should match :user-invalid if the user unchecks it and blurs.');
</script>

Просмотреть файл

@ -0,0 +1,133 @@
<!DOCTYPE html>
<link rel=author href="mailto:jarhar@chromium.org">
<link rel="help" href="https://drafts.csswg.org/selectors/#user-pseudos">
<link rel="help" href="https://html.spec.whatwg.org/#selector-user-invalid">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<!-- This test asserts specifics of keyboard behavior in multifield inputs,
like type=date and type=time, in ways that are not specified. -->
<form>
<input id=date type=date required>
<input id=time type=time required>
<input id=datetime-local type=datetime-local required>
</form>
<script>
const tabKey = '\uE004';
const backspace = '\uE003';
promise_test(async () => {
const date = document.getElementById('date');
assert_false(date.matches(':user-valid'),
'Date input should not match :user-valid at the start of the test.');
assert_false(date.matches(':user-invalid'),
'Date input should not match :user-invalid at the start of the test.');
assert_equals(date.value, '',
'Date input should not have a value at the start of the test.');
date.focus();
await test_driver.send_keys(date, `1${tabKey}`);
assert_equals(date.value, '',
'Date input value should not be set after partially inputting the date.');
assert_false(date.matches(':user-valid'),
'Date input should not match :user-valid after partially inputting the date.');
assert_false(date.matches(':user-invalid'),
'Date input should not match :user-invalid after partially inputting the date.');
await test_driver.send_keys(date, `1${tabKey}1234${tabKey}`);
assert_equals(date.value, '1234-01-01',
'Date input value should match the testdriver input.');
assert_true(date.matches(':user-valid'),
'Date input should match :user-valid after typing in a complete value.');
assert_false(date.matches(':user-invalid'),
'Date input should not match :user-invalid after typing in a complete value.');
date.blur();
date.focus();
await test_driver.send_keys(date, backspace);
assert_equals(date.value, '',
'Date input value should be cleared when deleting one of the sub-values.');
assert_false(date.matches(':user-valid'),
'Date input should not match :user-valid after typing in an invalid value.');
assert_true(date.matches(':user-invalid'),
'Date input should match :user-invalid after typing in an invalid value.');
}, '<input type=date> keyboard behavior for :user-valid/:user-invalid.');
promise_test(async () => {
const time = document.getElementById('time');
assert_false(time.matches(':user-valid'),
'Time input should not match :user-valid at the start of the test.');
assert_false(time.matches(':user-invalid'),
'Time input should not match :user-invalid at the start of the test.');
assert_equals(time.value, '',
'Time input should not have a value at the start of the test.');
time.focus();
await test_driver.send_keys(time, `1${tabKey}`);
assert_equals(time.value, '',
'Time input value should not be set after partially inputting the time.');
assert_false(time.matches(':user-valid'),
'Time input should not match :user-valid after partially inputting the time.');
assert_false(time.matches(':user-invalid'),
'Time input should not match :user-invalid after partially inputting the time.');
await test_driver.send_keys(time, `2${tabKey}a${tabKey}`);
assert_equals(time.value, '01:02',
'Time input value should match the testdriver input.');
assert_true(time.matches(':user-valid'),
'Time input should match :user-valid after typing in a complete value.');
assert_false(time.matches(':user-invalid'),
'Time input should not match :user-invalid after typing in a complete value.');
time.blur();
time.focus();
await test_driver.send_keys(time, backspace);
assert_equals(time.value, '',
'Time input value should be cleared when deleting one of the sub-values.');
assert_false(time.matches(':user-valid'),
'Time input should not match :user-valid after typing in an invalid value.');
assert_true(time.matches(':user-invalid'),
'Time input should match :user-invalid after typing in an invalid value.');
}, '<input type=time> keyboard behavior for :user-valid/:user-invalid.');
promise_test(async () => {
const dateTimeLocal = document.getElementById('datetime-local');
assert_false(dateTimeLocal.matches(':user-valid'),
'Datetime input should not match :user-valid at the start of the test.');
assert_false(dateTimeLocal.matches(':user-invalid'),
'Datetime input should not match :user-invalid at the start of the test.');
assert_equals(dateTimeLocal.value, '',
'Datetime input should not have a value at the start of the test.');
dateTimeLocal.focus();
await test_driver.send_keys(dateTimeLocal, `1${tabKey}`);
assert_equals(dateTimeLocal.value, '',
'Datetime input value should not be set after partially inputting the dateTimeLocal.');
assert_false(dateTimeLocal.matches(':user-valid'),
'Datetime input should not match :user-valid after partially inputting the dateTimeLocal.');
assert_false(dateTimeLocal.matches(':user-invalid'),
'Datetime input should not match :user-invalid after partially inputting the dateTimeLocal.');
await test_driver.send_keys(dateTimeLocal, `1${tabKey}1234${tabKey}1${tabKey}2${tabKey}a${tabKey}`);
assert_equals(dateTimeLocal.value, '1234-01-01T01:02',
'Datetime input value should match the testdriver input.');
assert_true(dateTimeLocal.matches(':user-valid'),
'Datetime input should match :user-valid after typing in a complete value.');
assert_false(dateTimeLocal.matches(':user-invalid'),
'Datetime input should not match :user-invalid after typing in a complete value.');
dateTimeLocal.blur();
dateTimeLocal.focus();
await test_driver.send_keys(dateTimeLocal, backspace);
assert_equals(dateTimeLocal.value, '',
'Datetime input value should be cleared when deleting one of the sub-values.');
assert_false(dateTimeLocal.matches(':user-valid'),
'Datetime input should not match :user-valid after typing in an invalid value.');
assert_true(dateTimeLocal.matches(':user-invalid'),
'Datetime input should match :user-invalid after typing in an invalid value.');
}, '<input type=datetime-local> keyboard behavior for :user-valid/:user-invalid.');
</script>

Просмотреть файл

@ -29,6 +29,7 @@
<input placeholder="Optional field" id="optional-input"><br>
<textarea placeholder="Optional field" id="optional-textarea"></textarea><br>
<input type="checkbox" id="optional-checkbox"><br>
<input type="date" id="optional-date"><br>
<input required placeholder="Required field"> <!-- Prevent the form from navigating with this invalid input -->
<input type="submit" id="submit-button">
<input type="reset" id="reset-button">
@ -77,12 +78,14 @@ promise_test(async () => {
const optionalInput = document.querySelector("#optional-input");
const optionalTextarea = document.querySelector("#optional-textarea");
const optionalCheckbox = document.querySelector("#optional-checkbox");
const optionalDate = document.querySelector("#optional-date");
const submitButton = document.querySelector("#submit-button");
const resetButton = document.querySelector("#reset-button");
assert_true(optionalInput.validity.valid);
assert_true(optionalTextarea.validity.valid);
assert_true(optionalCheckbox.validity.valid);
assert_true(optionalDate.validity.valid);
// The selector can't match because no interaction has happened.
assert_false(optionalInput.matches(":user-valid"), "Initially does not match :user-valid");
assert_false(optionalInput.matches(":user-invalid"), "Initially does not match :user-invalid");
@ -93,6 +96,9 @@ promise_test(async () => {
assert_false(optionalCheckbox.matches(":user-valid"), "Initially does not match :user-valid");
assert_false(optionalCheckbox.matches(":user-invalid"), "Initially does not match :user-invalid");
assert_false(optionalDate.matches(":user-valid"), "Initially does not match :user-valid");
assert_false(optionalDate.matches(":user-invalid"), "Initially does not match :user-invalid");
submitButton.click();
assert_true(optionalInput.matches(":user-valid"), "Submitted the form, input is validated");
@ -104,6 +110,9 @@ promise_test(async () => {
assert_true(optionalCheckbox.matches(":user-valid"), "Submitted the form, checkbox is validated");
assert_false(optionalCheckbox.matches(":user-invalid"), "Submitted the form, checkbox is validated");
assert_true(optionalDate.matches(":user-valid"), "Submitted the form, date is validated");
assert_false(optionalDate.matches(":user-invalid"), "Submitted the form, date is validated");
resetButton.click();
assert_false(optionalInput.matches(":user-valid"), "Reset the form, user-interacted flag is reset");
@ -115,6 +124,9 @@ promise_test(async () => {
assert_false(optionalCheckbox.matches(":user-valid"), "Reset the form, user-interacted flag is reset");
assert_false(optionalCheckbox.matches(":user-invalid"), "Reset the form, user-interacted flag is reset");
assert_false(optionalDate.matches(":user-valid"), "Reset the form, user-interacted flag is reset");
assert_false(optionalDate.matches(":user-invalid"), "Reset the form, user-interacted flag is reset");
// Test programmatic form submission with constraint validation.
form.requestSubmit();
@ -126,6 +138,9 @@ promise_test(async () => {
assert_true(optionalCheckbox.matches(":user-valid"), "Called form.requestSubmit(), checkbox is validated");
assert_false(optionalCheckbox.matches(":user-invalid"), "Called form.requestSubmit(), checkbox is validated");
assert_true(optionalDate.matches(":user-valid"), "Called form.requestSubmit(), date is validated");
assert_false(optionalDate.matches(":user-invalid"), "Called form.requestSubmit(), date is validated");
}, ":user-valid selector properly interacts with submit & reset buttons");
promise_test(async () => {
@ -151,4 +166,33 @@ promise_test(async () => {
assert_true(checkbox.matches(':user-valid'),
'Checkbox should match :user-valid after clicking once.');
}, 'Checkboxes should match :user-valid after the user clicks on it.');
promise_test(async () => {
const date = document.getElementById('optional-date');
const resetButton = document.getElementById('reset-button');
resetButton.click();
assert_false(date.matches(':user-valid'),
'Date input should not match :user-valid at the start of the test.');
assert_equals(date.value, '',
'Date input should not have a value at the start of the test.');
date.value = '2024-04-15';
assert_false(date.matches(':user-valid'),
'Date input should not match :user-valid after programatically changing value.');
date.value = '';
assert_false(date.matches(':user-valid'),
'Date input should not match :user-valid after programatically changing value.');
const tabKey = '\uE004';
date.focus();
// Press tab twice at the end to make sure that focus has left the input.
await test_driver.send_keys(date, `1${tabKey}1${tabKey}1234${tabKey}${tabKey}`);
assert_not_equals(document.activeElement, date,
'Pressing tab twice after typing in the date should have blurred the input.');
assert_equals(date.value, '1234-01-01',
'Date input value should match the testdriver input.');
assert_true(date.matches(':user-valid'),
'Date input should match :user-valid after typing in a value.');
}, 'Date inputs should match :user-valid after the user types a value into it.');
</script>