user-valid.html (9699B)
1 <!doctype html> 2 <title>Support for the :user-valid pseudo-class</title> 3 <link rel="author" title="Tim Nguyen" href="https://github.com/nt1m"> 4 <link rel="help" href="https://drafts.csswg.org/selectors/#user-pseudos"> 5 <link rel="help" href="https://html.spec.whatwg.org/#selector-user-valid"> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 <script src="/resources/testdriver.js"></script> 9 <script src="/resources/testdriver-vendor.js"></script> 10 11 <style> 12 :is(input:not([type=submit], [type=reset]), textarea) { 13 border: 2px solid black; 14 } 15 16 :is(input:not([type=submit], [type=reset]), textarea):user-valid { 17 border-color: green; 18 } 19 20 :is(input:not([type=submit], [type=reset]), textarea):user-invalid { 21 border-color: red; 22 } 23 </style> 24 25 <input id="initially-valid" type="email"> 26 27 <p>Test form interactions (reset / submit):</p> 28 <form id="form"> 29 <input placeholder="Optional field" id="optional-input"><br> 30 <textarea placeholder="Optional field" id="optional-textarea"></textarea><br> 31 <input type="checkbox" id="optional-checkbox"><br> 32 <input type="date" id="optional-date"><br> 33 <input required placeholder="Required field"> <!-- Prevent the form from navigating with this invalid input --> 34 <input type="submit" id="submit-button"> 35 <input type="reset" id="reset-button"> 36 </form> 37 38 <script> 39 promise_test(async () => { 40 const input = document.querySelector("#initially-valid"); 41 assert_true(input.validity.valid); 42 // The selector can't match because no interaction has happened. 43 assert_false(input.matches(":user-valid"), "Initially does not match :user-valid"); 44 assert_false(input.matches(":user-invalid"), "Initially does not match :user-invalid"); 45 46 await test_driver.click(input); 47 input.blur(); 48 49 assert_false(input.matches(":user-valid"), "No change happened, still does not match :user-valid"); 50 assert_false(input.matches(":user-invalid"), "No change happened, still does not match :user-invalid"); 51 52 input.value = "test@example.com"; 53 54 assert_false(input.matches(":user-valid"), "Programatically set value, :user-valid should not match"); 55 assert_false(input.matches(":user-invalid"), "Programatically set value, :user-invalid should not match"); 56 57 input.value = ""; 58 59 assert_false(input.matches(":user-valid"), "Programatically cleared value, :user-valid should not match"); 60 assert_false(input.matches(":user-invalid"), "Programatically cleared value, :user-invalid should not match"); 61 62 await test_driver.click(input); 63 await test_driver.send_keys(input, "test@example.com"); 64 input.blur(); 65 66 assert_true(input.matches(":user-valid"), "Typed a valid email, :user-valid now matches"); 67 assert_false(input.matches(":user-invalid"), "Typed a valid email, :user-invalid does not match"); 68 69 input.required = true; 70 input.value = ""; 71 72 assert_false(input.matches(":user-valid"), "Cleared required input, :user-valid no longer matches"); 73 assert_true(input.matches(":user-invalid"), "Cleared required input, :user-invalid now matches"); 74 }, ":user-valid selector should respond to user action"); 75 76 promise_test(async () => { 77 const form = document.querySelector("#form"); 78 const optionalInput = document.querySelector("#optional-input"); 79 const optionalTextarea = document.querySelector("#optional-textarea"); 80 const optionalCheckbox = document.querySelector("#optional-checkbox"); 81 const optionalDate = document.querySelector("#optional-date"); 82 const submitButton = document.querySelector("#submit-button"); 83 const resetButton = document.querySelector("#reset-button"); 84 85 assert_true(optionalInput.validity.valid); 86 assert_true(optionalTextarea.validity.valid); 87 assert_true(optionalCheckbox.validity.valid); 88 assert_true(optionalDate.validity.valid); 89 // The selector can't match because no interaction has happened. 90 assert_false(optionalInput.matches(":user-valid"), "Initially does not match :user-valid"); 91 assert_false(optionalInput.matches(":user-invalid"), "Initially does not match :user-invalid"); 92 93 assert_false(optionalTextarea.matches(":user-valid"), "Initially does not match :user-valid"); 94 assert_false(optionalTextarea.matches(":user-invalid"), "Initially does not match :user-invalid"); 95 96 assert_false(optionalCheckbox.matches(":user-valid"), "Initially does not match :user-valid"); 97 assert_false(optionalCheckbox.matches(":user-invalid"), "Initially does not match :user-invalid"); 98 99 assert_false(optionalDate.matches(":user-valid"), "Initially does not match :user-valid"); 100 assert_false(optionalDate.matches(":user-invalid"), "Initially does not match :user-invalid"); 101 102 submitButton.click(); 103 104 assert_true(optionalInput.matches(":user-valid"), "Submitted the form, input is validated"); 105 assert_false(optionalInput.matches(":user-invalid"), "Submitted the form, input is validated"); 106 107 assert_true(optionalTextarea.matches(":user-valid"), "Submitted the form, textarea is validated"); 108 assert_false(optionalTextarea.matches(":user-invalid"), "Submitted the form, textarea is validated"); 109 110 assert_true(optionalCheckbox.matches(":user-valid"), "Submitted the form, checkbox is validated"); 111 assert_false(optionalCheckbox.matches(":user-invalid"), "Submitted the form, checkbox is validated"); 112 113 assert_true(optionalDate.matches(":user-valid"), "Submitted the form, date is validated"); 114 assert_false(optionalDate.matches(":user-invalid"), "Submitted the form, date is validated"); 115 116 resetButton.click(); 117 118 assert_false(optionalInput.matches(":user-valid"), "Reset the form, user-interacted flag is reset"); 119 assert_false(optionalInput.matches(":user-invalid"), "Reset the form, user-interacted flag is reset"); 120 121 assert_false(optionalTextarea.matches(":user-valid"), "Reset the form, user-interacted flag is reset"); 122 assert_false(optionalTextarea.matches(":user-invalid"), "Reset the form, user-interacted flag is reset"); 123 124 assert_false(optionalCheckbox.matches(":user-valid"), "Reset the form, user-interacted flag is reset"); 125 assert_false(optionalCheckbox.matches(":user-invalid"), "Reset the form, user-interacted flag is reset"); 126 127 assert_false(optionalDate.matches(":user-valid"), "Reset the form, user-interacted flag is reset"); 128 assert_false(optionalDate.matches(":user-invalid"), "Reset the form, user-interacted flag is reset"); 129 130 // Test programmatic form submission with constraint validation. 131 form.requestSubmit(); 132 133 assert_true(optionalInput.matches(":user-valid"), "Called form.requestSubmit(), input is validated"); 134 assert_false(optionalInput.matches(":user-invalid"), "Called form.requestSubmit(), input is validated"); 135 136 assert_true(optionalTextarea.matches(":user-valid"), "Called form.requestSubmit(), textarea is validated"); 137 assert_false(optionalTextarea.matches(":user-invalid"), "Called form.requestSubmit(), textarea is validated"); 138 139 assert_true(optionalCheckbox.matches(":user-valid"), "Called form.requestSubmit(), checkbox is validated"); 140 assert_false(optionalCheckbox.matches(":user-invalid"), "Called form.requestSubmit(), checkbox is validated"); 141 142 assert_true(optionalDate.matches(":user-valid"), "Called form.requestSubmit(), date is validated"); 143 assert_false(optionalDate.matches(":user-invalid"), "Called form.requestSubmit(), date is validated"); 144 }, ":user-valid selector properly interacts with submit & reset buttons"); 145 146 promise_test(async () => { 147 const checkbox = document.getElementById('optional-checkbox'); 148 149 const resetButton = document.getElementById('reset-button'); 150 resetButton.click(); 151 assert_false(checkbox.matches(':user-valid'), 152 'Checkbox should not match :user-valid at the start of the test.'); 153 assert_false(checkbox.checked, 154 'Checkbox should not be checked at the start of the test.'); 155 156 checkbox.checked = true; 157 assert_false(checkbox.matches(':user-valid'), 158 'Checkbox should not match :user-valid after programatically changing value.'); 159 checkbox.checked = false; 160 assert_false(checkbox.matches(':user-valid'), 161 'Checkbox should not match :user-valid after programatically changing value.'); 162 163 await test_driver.click(checkbox); 164 assert_true(checkbox.checked, 'Checkbox should be checked after clicking once.'); 165 166 assert_true(checkbox.matches(':user-valid'), 167 'Checkbox should match :user-valid after clicking once.'); 168 }, 'Checkboxes should match :user-valid after the user clicks on it.'); 169 170 promise_test(async () => { 171 const date = document.getElementById('optional-date'); 172 173 const resetButton = document.getElementById('reset-button'); 174 resetButton.click(); 175 assert_false(date.matches(':user-valid'), 176 'Date input should not match :user-valid at the start of the test.'); 177 assert_equals(date.value, '', 178 'Date input should not have a value at the start of the test.'); 179 180 date.value = '2024-04-15'; 181 assert_false(date.matches(':user-valid'), 182 'Date input should not match :user-valid after programatically changing value.'); 183 date.value = ''; 184 assert_false(date.matches(':user-valid'), 185 'Date input should not match :user-valid after programatically changing value.'); 186 187 const tabKey = '\uE004'; 188 date.focus(); 189 // Press tab twice at the end to make sure that focus has left the input. 190 await test_driver.send_keys(date, `1${tabKey}1${tabKey}1234${tabKey}${tabKey}`); 191 assert_not_equals(document.activeElement, date, 192 'Pressing tab twice after typing in the date should have blurred the input.'); 193 assert_equals(date.value, '1234-01-01', 194 'Date input value should match the testdriver input.'); 195 assert_true(date.matches(':user-valid'), 196 'Date input should match :user-valid after typing in a value.'); 197 }, 'Date inputs should match :user-valid after the user types a value into it.'); 198 </script>