| <!doctype html> |
| <title>Support for the :user-invalid pseudo-class</title> |
| <link rel="author" title="Tim Nguyen" href="https://github.com/nt1m"> |
| <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> |
| |
| <style> |
| :is(input:not([type=submit], [type=reset]), textarea) { |
| border: 2px solid black; |
| } |
| |
| :is(input:not([type=submit], [type=reset]), textarea):user-valid { |
| border-color: green; |
| } |
| |
| :is(input:not([type=submit], [type=reset]), textarea):user-invalid { |
| border-color: red; |
| } |
| </style> |
| |
| <input id="initially-invalid" type="email" value="foo"> |
| |
| <p>Test form interactions (reset / submit):</p> |
| <form id="form"> |
| <input placeholder="Required field" required id="required-input"><br> |
| <textarea placeholder="Required field" required id="required-textarea"></textarea><br> |
| <input type="submit" id="submit-button"> |
| <input type="reset" id="reset-button"> |
| </form> |
| |
| <script> |
| promise_test(async () => { |
| const input = document.querySelector("#initially-invalid"); |
| assert_false(input.validity.valid, "Should be invalid"); |
| // The selector can't match because no interaction has happened. |
| assert_false(input.matches(':user-invalid')); |
| |
| assert_false(input.matches(":user-valid"), "Initially does not match :user-valid"); |
| assert_false(input.matches(":user-invalid"), "Initially does not match :user-invalid"); |
| |
| await test_driver.click(input); |
| input.blur(); |
| |
| assert_false(input.matches(":user-valid"), "No change happened, still does not match :user-valid"); |
| assert_false(input.matches(":user-invalid"), "No change happened, still does not match :user-invalid"); |
| |
| await test_driver.click(input); |
| await test_driver.send_keys(input, "not an email"); |
| input.blur(); |
| |
| assert_true(input.matches(":user-invalid"), "Typed an invalid email, :user-invalid now matches"); |
| assert_false(input.matches(":user-valid"), "Typed an invalid email, :user-valid does not match"); |
| |
| input.value = ""; |
| await test_driver.click(input); |
| await test_driver.send_keys(input, "test@example.com"); |
| input.blur(); |
| |
| assert_true(input.matches(":user-valid"), "Put a valid email, :user-valid now matches"); |
| assert_false(input.matches(":user-invalid"), "Put an valid email, :user-invalid no longer matches"); |
| }, ':user-invalid selector should respond to user action'); |
| |
| promise_test(async () => { |
| const form = document.querySelector("#form"); |
| const requiredInput = document.querySelector("#required-input"); |
| const requiredTextarea = document.querySelector("#required-textarea"); |
| const submitButton = document.querySelector("#submit-button"); |
| const resetButton = document.querySelector("#reset-button"); |
| |
| assert_false(requiredInput.validity.valid); |
| assert_false(requiredTextarea.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"); |
| |
| assert_false(requiredTextarea.matches(":user-valid"), "Initially does not match :user-valid"); |
| assert_false(requiredTextarea.matches(":user-invalid"), "Initially does not match :user-invalid"); |
| |
| submitButton.click(); |
| |
| assert_true(requiredInput.matches(":user-invalid"), "Submitted the form, input is validated"); |
| assert_false(requiredInput.matches(":user-valid"), "Submitted the form, input is validated"); |
| |
| assert_true(requiredTextarea.matches(":user-invalid"), "Submitted the form, textarea is validated"); |
| assert_false(requiredTextarea.matches(":user-valid"), "Submitted the form, textarea is validated"); |
| |
| resetButton.click(); |
| |
| assert_false(requiredInput.matches(":user-valid"), "Reset the form, user-interacted flag is reset"); |
| assert_false(requiredInput.matches(":user-invalid"), "Reset the form, user-interacted flag is reset"); |
| |
| assert_false(requiredTextarea.matches(":user-valid"), "Reset the form, user-interacted flag is reset"); |
| assert_false(requiredTextarea.matches(":user-invalid"), "Reset the form, user-interacted flag is reset"); |
| |
| // Test programmatic form submission with constraint validation. |
| form.requestSubmit(); |
| |
| assert_true(requiredInput.matches(":user-invalid"), "Called form.requestSubmit(), input is validated"); |
| assert_false(requiredInput.matches(":user-valid"), "Called form.requestSubmit(), input is validated"); |
| |
| assert_true(requiredTextarea.matches(":user-invalid"), "Called form.requestSubmit(), textarea is validated"); |
| assert_false(requiredTextarea.matches(":user-valid"), "Called form.requestSubmit(), textarea is validated"); |
| }, ":user-invalid selector properly interacts with submit & reset buttons"); |
| |
| // historical: https://github.com/w3c/csswg-drafts/issues/1329 |
| test(() => { |
| const input = document.querySelector('input'); |
| // matches() will throw if the selector isn't suppported |
| assert_throws_dom("SyntaxError", () => input.matches(':user-error')); |
| }, ':user-error selector should not be supported'); |
| </script> |