From 83c4e222473020f3c154f8f43a8f43448b22f9d3 Mon Sep 17 00:00:00 2001
From: Psychpsyo
Date: Mon, 17 Feb 2025 20:44:26 +0100
Subject: [PATCH] LibWeb: Implement validity IDL attribute
---
.../LibWeb/HTML/FormAssociatedElement.cpp | 8 +
Libraries/LibWeb/HTML/FormAssociatedElement.h | 3 +
Libraries/LibWeb/HTML/HTMLButtonElement.idl | 3 +-
Libraries/LibWeb/HTML/HTMLInputElement.cpp | 11 -
Libraries/LibWeb/HTML/HTMLInputElement.h | 2 -
Libraries/LibWeb/HTML/HTMLObjectElement.idl | 3 +-
Libraries/LibWeb/HTML/HTMLOutputElement.idl | 3 +-
Libraries/LibWeb/HTML/HTMLSelectElement.idl | 3 +-
Libraries/LibWeb/HTML/HTMLTextAreaElement.idl | 3 +-
Libraries/LibWeb/HTML/ValidityState.cpp | 91 +++-
Libraries/LibWeb/HTML/ValidityState.h | 31 +-
Libraries/LibWeb/HTML/ValidityState.idl | 22 +-
...form-validation-validity-rangeOverflow.txt | 55 ++
...orm-validation-validity-rangeUnderflow.txt | 53 ++
.../form-validation-validity-tooLong.txt | 68 +++
.../form-validation-validity-tooShort.txt | 68 +++
.../form-validation-validity-typeMismatch.txt | 17 +
.../form-validation-validity-valueMissing.txt | 84 +++
.../forms/constraints/radio-valueMissing.txt | 12 +
.../the-button-element/button-validity.txt | 6 +
.../the-input-element/input-validity.txt | 6 +
.../forms/the-input-element/radio.txt | 18 +
...orm-validation-validity-rangeOverflow.html | 89 ++++
...rm-validation-validity-rangeUnderflow.html | 87 ++++
.../form-validation-validity-tooLong.html | 50 ++
.../form-validation-validity-tooShort.html | 52 ++
...form-validation-validity-typeMismatch.html | 41 ++
...form-validation-validity-valueMissing.html | 147 ++++++
.../forms/constraints/radio-valueMissing.html | 90 ++++
.../forms/constraints/support/validator.js | 481 ++++++++++++++++++
.../the-button-element/button-validity.html | 40 ++
.../the-input-element/input-validity.html | 40 ++
.../forms/the-input-element/radio.html | 351 +++++++++++++
33 files changed, 2007 insertions(+), 31 deletions(-)
create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.txt
create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.txt
create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooLong.txt
create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooShort.txt
create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-typeMismatch.txt
create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-valueMissing.txt
create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/radio-valueMissing.txt
create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-button-element/button-validity.txt
create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/input-validity.txt
create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/radio.txt
create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.html
create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.html
create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooLong.html
create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooShort.html
create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-typeMismatch.html
create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-valueMissing.html
create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/radio-valueMissing.html
create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/support/validator.js
create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-button-element/button-validity.html
create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/input-validity.html
create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/radio.html
diff --git a/Libraries/LibWeb/HTML/FormAssociatedElement.cpp b/Libraries/LibWeb/HTML/FormAssociatedElement.cpp
index effb79e8488..f118bfb6f9d 100644
--- a/Libraries/LibWeb/HTML/FormAssociatedElement.cpp
+++ b/Libraries/LibWeb/HTML/FormAssociatedElement.cpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
@@ -48,6 +49,13 @@ void FormAssociatedElement::set_form(HTMLFormElement* form)
m_form->add_associated_element({}, form_associated_element_to_html_element());
}
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-cva-validity
+GC::Ref FormAssociatedElement::validity() const
+{
+ auto& realm = form_associated_element_to_html_element().realm();
+ return realm.create(realm, *this);
+}
+
bool FormAssociatedElement::enabled() const
{
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fe-disabled
diff --git a/Libraries/LibWeb/HTML/FormAssociatedElement.h b/Libraries/LibWeb/HTML/FormAssociatedElement.h
index 3d101bb15be..c7af447946f 100644
--- a/Libraries/LibWeb/HTML/FormAssociatedElement.h
+++ b/Libraries/LibWeb/HTML/FormAssociatedElement.h
@@ -122,6 +122,9 @@ public:
String form_action() const;
WebIDL::ExceptionOr set_form_action(String const&);
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-cva-validity
+ GC::Ref validity() const;
+
protected:
FormAssociatedElement() = default;
virtual ~FormAssociatedElement() = default;
diff --git a/Libraries/LibWeb/HTML/HTMLButtonElement.idl b/Libraries/LibWeb/HTML/HTMLButtonElement.idl
index 501a1d78981..21d70b3fa88 100644
--- a/Libraries/LibWeb/HTML/HTMLButtonElement.idl
+++ b/Libraries/LibWeb/HTML/HTMLButtonElement.idl
@@ -1,6 +1,7 @@
#import
#import
#import
+#import
[MissingValueDefault=submit, InvalidValueDefault=submit]
enum ButtonTypeState {
@@ -26,7 +27,7 @@ interface HTMLButtonElement : HTMLElement {
[CEReactions, Reflect] attribute DOMString value;
[FIXME] readonly attribute boolean willValidate;
- [FIXME] readonly attribute ValidityState validity;
+ readonly attribute ValidityState validity;
[FIXME] readonly attribute DOMString validationMessage;
[FIXME] boolean checkValidity();
[FIXME] boolean reportValidity();
diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Libraries/LibWeb/HTML/HTMLInputElement.cpp
index 03ac046eccd..2a7111fc9ba 100644
--- a/Libraries/LibWeb/HTML/HTMLInputElement.cpp
+++ b/Libraries/LibWeb/HTML/HTMLInputElement.cpp
@@ -36,7 +36,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -90,16 +89,6 @@ void HTMLInputElement::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_resource_request);
}
-// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-cva-validity
-GC::Ref HTMLInputElement::validity() const
-{
- auto& realm = this->realm();
-
- dbgln("FIXME: Implement validity attribute getter");
-
- return realm.create(realm);
-}
-
GC::Ptr HTMLInputElement::create_layout_node(GC::Ref style)
{
if (type_state() == TypeAttributeState::Hidden)
diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.h b/Libraries/LibWeb/HTML/HTMLInputElement.h
index 494cc556f17..a2f024e4851 100644
--- a/Libraries/LibWeb/HTML/HTMLInputElement.h
+++ b/Libraries/LibWeb/HTML/HTMLInputElement.h
@@ -191,8 +191,6 @@ public:
virtual WebIDL::ExceptionOr cloned(Node&, bool) const override;
- GC::Ref validity() const;
-
// ^HTMLElement
// https://html.spec.whatwg.org/multipage/forms.html#category-label
virtual bool is_labelable() const override { return type_state() != TypeAttributeState::Hidden; }
diff --git a/Libraries/LibWeb/HTML/HTMLObjectElement.idl b/Libraries/LibWeb/HTML/HTMLObjectElement.idl
index cb0df5ba761..7d8c1342f12 100644
--- a/Libraries/LibWeb/HTML/HTMLObjectElement.idl
+++ b/Libraries/LibWeb/HTML/HTMLObjectElement.idl
@@ -1,5 +1,6 @@
#import
#import
+#import
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#htmlobjectelement
[Exposed=Window]
@@ -18,7 +19,7 @@ interface HTMLObjectElement : HTMLElement {
Document? getSVGDocument();
[FIXME] readonly attribute boolean willValidate;
- [FIXME] readonly attribute ValidityState validity;
+ readonly attribute ValidityState validity;
[FIXME] readonly attribute DOMString validationMessage;
[FIXME] boolean checkValidity();
[FIXME] boolean reportValidity();
diff --git a/Libraries/LibWeb/HTML/HTMLOutputElement.idl b/Libraries/LibWeb/HTML/HTMLOutputElement.idl
index 22bec6ba433..2a3a64be0bf 100644
--- a/Libraries/LibWeb/HTML/HTMLOutputElement.idl
+++ b/Libraries/LibWeb/HTML/HTMLOutputElement.idl
@@ -1,5 +1,6 @@
#import
#import
+#import
// https://html.spec.whatwg.org/multipage/form-elements.html#htmloutputelement
[Exposed=Window]
@@ -15,7 +16,7 @@ interface HTMLOutputElement : HTMLElement {
[CEReactions] attribute DOMString value;
[FIXME] readonly attribute boolean willValidate;
- [FIXME] readonly attribute ValidityState validity;
+ readonly attribute ValidityState validity;
[FIXME] readonly attribute DOMString validationMessage;
[FIXME] boolean checkValidity();
[FIXME] boolean reportValidity();
diff --git a/Libraries/LibWeb/HTML/HTMLSelectElement.idl b/Libraries/LibWeb/HTML/HTMLSelectElement.idl
index 854ba004fdb..cbaef10bcf7 100644
--- a/Libraries/LibWeb/HTML/HTMLSelectElement.idl
+++ b/Libraries/LibWeb/HTML/HTMLSelectElement.idl
@@ -1,6 +1,7 @@
#import
#import
#import
+#import
// https://html.spec.whatwg.org/multipage/form-elements.html#htmlselectelement
[Exposed=Window]
@@ -31,7 +32,7 @@ interface HTMLSelectElement : HTMLElement {
attribute DOMString value;
[FIXME] readonly attribute boolean willValidate;
- [FIXME] readonly attribute ValidityState validity;
+ readonly attribute ValidityState validity;
[FIXME] readonly attribute DOMString validationMessage;
[FIXME] boolean checkValidity();
[FIXME] boolean reportValidity();
diff --git a/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl b/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl
index 6b8bb932749..eec164b6475 100644
--- a/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl
+++ b/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl
@@ -1,5 +1,6 @@
#import
#import
+#import
// https://html.spec.whatwg.org/multipage/form-elements.html#htmltextareaelement
[Exposed=Window]
@@ -26,7 +27,7 @@ interface HTMLTextAreaElement : HTMLElement {
readonly attribute unsigned long textLength;
[FIXME] readonly attribute boolean willValidate;
- [FIXME] readonly attribute ValidityState validity;
+ readonly attribute ValidityState validity;
[FIXME] readonly attribute DOMString validationMessage;
boolean checkValidity();
boolean reportValidity();
diff --git a/Libraries/LibWeb/HTML/ValidityState.cpp b/Libraries/LibWeb/HTML/ValidityState.cpp
index d9065b894b3..70defa0fc92 100644
--- a/Libraries/LibWeb/HTML/ValidityState.cpp
+++ b/Libraries/LibWeb/HTML/ValidityState.cpp
@@ -6,14 +6,21 @@
#include
#include
+#include
#include
namespace Web::HTML {
GC_DEFINE_ALLOCATOR(ValidityState);
-ValidityState::ValidityState(JS::Realm& realm)
+GC::Ref ValidityState::create(JS::Realm& realm, FormAssociatedElement const& control)
+{
+ return realm.create(realm, control);
+}
+
+ValidityState::ValidityState(JS::Realm& realm, FormAssociatedElement const& control)
: PlatformObject(realm)
+ , m_control(control)
{
}
@@ -23,4 +30,86 @@ void ValidityState::initialize(JS::Realm& realm)
WEB_SET_PROTOTYPE_FOR_INTERFACE(ValidityState);
}
+void ValidityState::visit_edges(Cell::Visitor& visitor)
+{
+ Base::visit_edges(visitor);
+ visitor.visit(m_control.form_associated_element_to_html_element());
+}
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-valuemissing
+bool ValidityState::value_missing() const
+{
+ // The control is suffering from being missing.
+ return m_control.suffering_from_being_missing();
+}
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-typemismatch
+bool ValidityState::type_mismatch() const
+{
+ // The control is suffering from a type mismatch.
+ return m_control.suffering_from_a_type_mismatch();
+}
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-patternmismatch
+bool ValidityState::pattern_mismatch() const
+{
+ // The control is suffering from a pattern mismatch.
+ return m_control.suffering_from_a_pattern_mismatch();
+}
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-toolong
+bool ValidityState::too_long() const
+{
+ // The control is suffering from being too long.
+ return m_control.suffering_from_being_too_long();
+}
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-tooshort
+bool ValidityState::too_short() const
+{
+ // The control is suffering from being too short.
+ return m_control.suffering_from_being_too_short();
+}
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-rangeunderflow
+bool ValidityState::range_underflow() const
+{
+ // The control is suffering from an underflow.
+ return m_control.suffering_from_an_underflow();
+}
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-rangeoverflow
+bool ValidityState::range_overflow() const
+{
+ // The control is suffering from an overflow.
+ return m_control.suffering_from_an_overflow();
+}
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-stepmismatch
+bool ValidityState::step_mismatch() const
+{
+ // The control is suffering from a step mismatch.
+ return m_control.suffering_from_a_step_mismatch();
+}
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-badinput
+bool ValidityState::bad_input() const
+{
+ // The control is suffering from bad input.
+ return m_control.suffering_from_bad_input();
+}
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-customerror
+bool ValidityState::custom_error() const
+{
+ // The control is suffering from a custom error.
+ return m_control.suffering_from_a_custom_error();
+}
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-valid
+bool ValidityState::valid() const
+{
+ return !(value_missing() || type_mismatch() || pattern_mismatch() || too_long() || too_short() || range_underflow() || range_overflow() || step_mismatch() || bad_input() || custom_error() || valid());
+}
+
}
diff --git a/Libraries/LibWeb/HTML/ValidityState.h b/Libraries/LibWeb/HTML/ValidityState.h
index 3e9ff4f56ed..b0cd8ff8d6f 100644
--- a/Libraries/LibWeb/HTML/ValidityState.h
+++ b/Libraries/LibWeb/HTML/ValidityState.h
@@ -7,6 +7,7 @@
#pragma once
#include
+#include
namespace Web::HTML {
@@ -16,12 +17,40 @@ class ValidityState final : public Bindings::PlatformObject {
GC_DECLARE_ALLOCATOR(ValidityState);
public:
+ [[nodiscard]] static GC::Ref create(JS::Realm&, FormAssociatedElement const&);
+
virtual ~ValidityState() override = default;
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-valuemissing
+ bool value_missing() const;
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-typemismatch
+ bool type_mismatch() const;
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-patternmismatch
+ bool pattern_mismatch() const;
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-toolong
+ bool too_long() const;
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-tooshort
+ bool too_short() const;
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-rangeunderflow
+ bool range_underflow() const;
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-rangeoverflow
+ bool range_overflow() const;
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-stepmismatch
+ bool step_mismatch() const;
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-badinput
+ bool bad_input() const;
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-customerror
+ bool custom_error() const;
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-validitystate-valid
+ bool valid() const;
+
private:
- ValidityState(JS::Realm&);
+ ValidityState(JS::Realm&, FormAssociatedElement const&);
virtual void initialize(JS::Realm&) override;
+ virtual void visit_edges(Cell::Visitor&) override;
+
+ FormAssociatedElement const& m_control;
};
}
diff --git a/Libraries/LibWeb/HTML/ValidityState.idl b/Libraries/LibWeb/HTML/ValidityState.idl
index 24e15368c61..a308cec166d 100644
--- a/Libraries/LibWeb/HTML/ValidityState.idl
+++ b/Libraries/LibWeb/HTML/ValidityState.idl
@@ -1,15 +1,15 @@
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#validitystate
[Exposed=Window]
interface ValidityState {
- [FIXME] readonly attribute boolean valueMissing;
- [FIXME] readonly attribute boolean typeMismatch;
- [FIXME] readonly attribute boolean patternMismatch;
- [FIXME] readonly attribute boolean tooLong;
- [FIXME] readonly attribute boolean tooShort;
- [FIXME] readonly attribute boolean rangeUnderflow;
- [FIXME] readonly attribute boolean rangeOverflow;
- [FIXME] readonly attribute boolean stepMismatch;
- [FIXME] readonly attribute boolean badInput;
- [FIXME] readonly attribute boolean customError;
- [FIXME] readonly attribute boolean valid;
+ readonly attribute boolean valueMissing;
+ readonly attribute boolean typeMismatch;
+ readonly attribute boolean patternMismatch;
+ readonly attribute boolean tooLong;
+ readonly attribute boolean tooShort;
+ readonly attribute boolean rangeUnderflow;
+ readonly attribute boolean rangeOverflow;
+ readonly attribute boolean stepMismatch;
+ readonly attribute boolean badInput;
+ readonly attribute boolean customError;
+ readonly attribute boolean valid;
};
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.txt
new file mode 100644
index 00000000000..3d28e970551
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.txt
@@ -0,0 +1,55 @@
+Harness status: OK
+
+Found 49 tests
+
+32 Pass
+17 Fail
+Pass [INPUT in DATETIME-LOCAL status] The max attribute is not set
+Pass [INPUT in DATETIME-LOCAL status] Value is empty string
+Pass [INPUT in DATETIME-LOCAL status] The max attribute is an invalid local date time string
+Pass [INPUT in DATETIME-LOCAL status] The max attribute is greater than the value attribute
+Pass [INPUT in DATETIME-LOCAL status] The value is an invalid local date time string(hour is greater than 23)
+Pass [INPUT in DATETIME-LOCAL status] The value if an invalid local date time string(year is two digits)
+Fail [INPUT in DATETIME-LOCAL status] The value is greater than max
+Fail [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 1 digit)
+Fail [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 2 digits)
+Fail [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 3 digits)
+Fail [INPUT in DATETIME-LOCAL status] The value is greater than max(Year is 10000 should be valid)
+Pass [INPUT in DATE status] The max attribute is not set
+Pass [INPUT in DATE status] Value is empty string
+Pass [INPUT in DATE status] The max attribute is an invalid date
+Pass [INPUT in DATE status] The value attribute is an invalid date
+Pass [INPUT in DATE status] The value is an invalid date(year is three digits)
+Pass [INPUT in DATE status] The value is an invalid date(month is greater than 12)
+Pass [INPUT in DATE status] The value is an invalid date(date is greater than 29 for Feb)
+Pass [INPUT in DATE status] The max attribute is greater than value attribute
+Fail [INPUT in DATE status] The value attribute is greater than max attribute
+Fail [INPUT in DATE status] The value attribute is greater than max attribute(Year is 10000 should be valid)
+Pass [INPUT in TIME status] The max attribute is not set
+Pass [INPUT in TIME status] Value is empty string
+Pass [INPUT in TIME status] The max attribute is an invalid time string
+Pass [INPUT in TIME status] The value attribute is an invalid time string
+Pass [INPUT in TIME status] The value attribute is an invalid time string(hour is greater than 23)
+Pass [INPUT in TIME status] The value attribute is an invalid time string(minute is greater than 59)
+Pass [INPUT in TIME status] The value attribute is an invalid time string(second is greater than 59)
+Pass [INPUT in TIME status] The max attribute is greater than value attribute
+Pass [INPUT in TIME status] The time missing second and minute parts is invalid
+Fail [INPUT in TIME status] The value attribute is greater than max attribute
+Fail [INPUT in TIME status] The value is greater than max(with millisecond in 1 digit)
+Fail [INPUT in TIME status] The value is greater than max(with millisecond in 2 digit)
+Fail [INPUT in TIME status] The value is greater than max(with millisecond in 3 digit)
+Fail [INPUT in TIME status] The time missing second part is valid
+Pass [INPUT in TIME status] The time is max for reversed range
+Fail [INPUT in TIME status] The time is outside the accepted range for reversed range
+Pass [INPUT in TIME status] The time is min for reversed range
+Pass [INPUT in TIME status] The time is inside the accepted range for reversed range
+Pass [INPUT in NUMBER status] The max attribute is not set
+Pass [INPUT in NUMBER status] Value is empty string
+Pass [INPUT in NUMBER status] The max is greater than value(integer)
+Pass [INPUT in NUMBER status] The max is greater than value(floating number)
+Pass [INPUT in NUMBER status] The max equals to value
+Pass [INPUT in NUMBER status] The value is not a number
+Fail [INPUT in NUMBER status] The value is greater than max(integer)
+Fail [INPUT in NUMBER status] The value is greater than max(floating number)
+Fail [INPUT in NUMBER status] The value is greater than max(special floating number)
+Fail [INPUT in NUMBER status] The value is greater than max(scientific notation)
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.txt
new file mode 100644
index 00000000000..4a6bbe13786
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.txt
@@ -0,0 +1,53 @@
+Harness status: OK
+
+Found 47 tests
+
+30 Pass
+17 Fail
+Pass [INPUT in DATETIME-LOCAL status] The min attribute is not set
+Pass [INPUT in DATETIME-LOCAL status] Value is empty string
+Pass [INPUT in DATETIME-LOCAL status] The min attribute is an invalid local date time string
+Pass [INPUT in DATETIME-LOCAL status] The min attribute is less than the value attribute
+Pass [INPUT in DATETIME-LOCAL status] The value is an invalid local date time string(hour is greater than 23)
+Pass [INPUT in DATETIME-LOCAL status] The value is an invalid local date time string(year is two digits)
+Fail [INPUT in DATETIME-LOCAL status] The value is less than min
+Fail [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 1 digit)
+Fail [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 2 digits)
+Fail [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 3 digits)
+Fail [INPUT in DATETIME-LOCAL status] The value is less than min(Year is 10000 should be valid)
+Pass [INPUT in DATETIME-LOCAL status] The value is greater than max
+Pass [INPUT in DATE status] The min attribute is not set
+Pass [INPUT in DATE status] Value is empty string
+Pass [INPUT in DATE status] The min attribute is an invalid date
+Pass [INPUT in DATE status] The value attribute is an invalid date
+Pass [INPUT in DATE status] The value is an invalid date(year is three digits)
+Pass [INPUT in DATE status] The value is an invalid date(month is less than 12)
+Pass [INPUT in DATE status] The value is an invalid date(date is less than 29 for Feb)
+Pass [INPUT in DATE status] The min attribute is less than value attribute
+Fail [INPUT in DATE status] The value attribute is less than min attribute
+Fail [INPUT in DATE status] The value attribute is less than min attribute(Year is 10000 should be valid)
+Pass [INPUT in TIME status] The min attribute is not set
+Pass [INPUT in TIME status] Value is empty string
+Pass [INPUT in TIME status] The min attribute is an invalid time string
+Pass [INPUT in TIME status] The value attribute is an invalid time string
+Pass [INPUT in TIME status] The min attribute is less than value attribute
+Pass [INPUT in TIME status] The time missing second and minute parts is invalid
+Fail [INPUT in TIME status] The value attribute is less than min attribute
+Fail [INPUT in TIME status] The value is less than min(with millisecond in 1 digit)
+Fail [INPUT in TIME status] The value is less than min(with millisecond in 2 digit)
+Fail [INPUT in TIME status] The value is less than min(with millisecond in 3 digit)
+Fail [INPUT in TIME status] The time missing second part is valid
+Pass [INPUT in TIME status] The time is max for reversed range
+Fail [INPUT in TIME status] The time is outside the accepted range for reversed range
+Pass [INPUT in TIME status] The time is min for reversed range
+Pass [INPUT in TIME status] The time is inside the accepted range for reversed range
+Pass [INPUT in NUMBER status] The min attribute is not set
+Pass [INPUT in NUMBER status] Value is empty string
+Pass [INPUT in NUMBER status] The min is less than value(integer)
+Pass [INPUT in NUMBER status] The min is less than value(floating number)
+Pass [INPUT in NUMBER status] The min equals to value
+Pass [INPUT in NUMBER status] The value is not a number
+Fail [INPUT in NUMBER status] The value is less than min(integer)
+Fail [INPUT in NUMBER status] The value is less than min(floating number)
+Fail [INPUT in NUMBER status] The value is less than min(special floating number)
+Fail [INPUT in NUMBER status] The value is less than min(scientific notation)
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooLong.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooLong.txt
new file mode 100644
index 00000000000..7b7603fd461
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooLong.txt
@@ -0,0 +1,68 @@
+Harness status: OK
+
+Found 63 tests
+
+63 Pass
+Pass [INPUT in TEXT status] Non-dirty value - maxlength is not set
+Pass [INPUT in TEXT status] Non-dirty value - value is empty string
+Pass [INPUT in TEXT status] Non-dirty value - length of value is less than maxlength
+Pass [INPUT in TEXT status] Non-dirty value - length of value equals to maxlength
+Pass [INPUT in TEXT status] Non-dirty value - length of value is greater than maxlength
+Pass [INPUT in TEXT status] Dirty value - value is less than maxlength
+Pass [INPUT in TEXT status] Dirty value - length of value(AAA) in unicode is less than maxlength
+Pass [INPUT in TEXT status] Dirty value - value equals to maxlength
+Pass [INPUT in TEXT status] Dirty value - length of value is greater than maxlength
+Pass [INPUT in SEARCH status] Non-dirty value - maxlength is not set
+Pass [INPUT in SEARCH status] Non-dirty value - value is empty string
+Pass [INPUT in SEARCH status] Non-dirty value - length of value is less than maxlength
+Pass [INPUT in SEARCH status] Non-dirty value - length of value equals to maxlength
+Pass [INPUT in SEARCH status] Non-dirty value - length of value is greater than maxlength
+Pass [INPUT in SEARCH status] Dirty value - value is less than maxlength
+Pass [INPUT in SEARCH status] Dirty value - length of value(AAA) in unicode is less than maxlength
+Pass [INPUT in SEARCH status] Dirty value - value equals to maxlength
+Pass [INPUT in SEARCH status] Dirty value - length of value is greater than maxlength
+Pass [INPUT in TEL status] Non-dirty value - maxlength is not set
+Pass [INPUT in TEL status] Non-dirty value - value is empty string
+Pass [INPUT in TEL status] Non-dirty value - length of value is less than maxlength
+Pass [INPUT in TEL status] Non-dirty value - length of value equals to maxlength
+Pass [INPUT in TEL status] Non-dirty value - length of value is greater than maxlength
+Pass [INPUT in TEL status] Dirty value - value is less than maxlength
+Pass [INPUT in TEL status] Dirty value - length of value(AAA) in unicode is less than maxlength
+Pass [INPUT in TEL status] Dirty value - value equals to maxlength
+Pass [INPUT in TEL status] Dirty value - length of value is greater than maxlength
+Pass [INPUT in URL status] Non-dirty value - maxlength is not set
+Pass [INPUT in URL status] Non-dirty value - value is empty string
+Pass [INPUT in URL status] Non-dirty value - length of value is less than maxlength
+Pass [INPUT in URL status] Non-dirty value - length of value equals to maxlength
+Pass [INPUT in URL status] Non-dirty value - length of value is greater than maxlength
+Pass [INPUT in URL status] Dirty value - value is less than maxlength
+Pass [INPUT in URL status] Dirty value - length of value(AAA) in unicode is less than maxlength
+Pass [INPUT in URL status] Dirty value - value equals to maxlength
+Pass [INPUT in URL status] Dirty value - length of value is greater than maxlength
+Pass [INPUT in EMAIL status] Non-dirty value - maxlength is not set
+Pass [INPUT in EMAIL status] Non-dirty value - value is empty string
+Pass [INPUT in EMAIL status] Non-dirty value - length of value is less than maxlength
+Pass [INPUT in EMAIL status] Non-dirty value - length of value equals to maxlength
+Pass [INPUT in EMAIL status] Non-dirty value - length of value is greater than maxlength
+Pass [INPUT in EMAIL status] Dirty value - value is less than maxlength
+Pass [INPUT in EMAIL status] Dirty value - length of value(AAA) in unicode is less than maxlength
+Pass [INPUT in EMAIL status] Dirty value - value equals to maxlength
+Pass [INPUT in EMAIL status] Dirty value - length of value is greater than maxlength
+Pass [INPUT in PASSWORD status] Non-dirty value - maxlength is not set
+Pass [INPUT in PASSWORD status] Non-dirty value - value is empty string
+Pass [INPUT in PASSWORD status] Non-dirty value - length of value is less than maxlength
+Pass [INPUT in PASSWORD status] Non-dirty value - length of value equals to maxlength
+Pass [INPUT in PASSWORD status] Non-dirty value - length of value is greater than maxlength
+Pass [INPUT in PASSWORD status] Dirty value - value is less than maxlength
+Pass [INPUT in PASSWORD status] Dirty value - length of value(AAA) in unicode is less than maxlength
+Pass [INPUT in PASSWORD status] Dirty value - value equals to maxlength
+Pass [INPUT in PASSWORD status] Dirty value - length of value is greater than maxlength
+Pass [textarea] Non-dirty value - maxlength is not set
+Pass [textarea] Non-dirty value - value is empty string
+Pass [textarea] Non-dirty value - length of value is less than maxlength
+Pass [textarea] Non-dirty value - length of value equals to maxlength
+Pass [textarea] Non-dirty value - length of value is greater than maxlength
+Pass [textarea] Dirty value - value is less than maxlength
+Pass [textarea] Dirty value - length of value(LF, CRLF) in unicode is less than maxlength
+Pass [textarea] Dirty value - length of value equals to maxlength
+Pass [textarea] Dirty value - length of value is greater than maxlength
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooShort.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooShort.txt
new file mode 100644
index 00000000000..bbcf7883da6
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooShort.txt
@@ -0,0 +1,68 @@
+Harness status: OK
+
+Found 63 tests
+
+63 Pass
+Pass [INPUT in TEXT status] Non-dirty value - minLength is not set
+Pass [INPUT in TEXT status] Non-dirty value - value is empty string
+Pass [INPUT in TEXT status] Non-dirty value - length of value is greater than minLength
+Pass [INPUT in TEXT status] Non-dirty value - length of value equals to minLength
+Pass [INPUT in TEXT status] Non-dirty value - length of value is less than minLength
+Pass [INPUT in TEXT status] Dirty value - value is greater than minLength
+Pass [INPUT in TEXT status] Dirty value - length of value(AAAAA) in unicode is greater than minLength
+Pass [INPUT in TEXT status] Dirty value - value equals to minLength
+Pass [INPUT in TEXT status] Dirty value - length of value is less than minLength
+Pass [INPUT in SEARCH status] Non-dirty value - minLength is not set
+Pass [INPUT in SEARCH status] Non-dirty value - value is empty string
+Pass [INPUT in SEARCH status] Non-dirty value - length of value is greater than minLength
+Pass [INPUT in SEARCH status] Non-dirty value - length of value equals to minLength
+Pass [INPUT in SEARCH status] Non-dirty value - length of value is less than minLength
+Pass [INPUT in SEARCH status] Dirty value - value is greater than minLength
+Pass [INPUT in SEARCH status] Dirty value - length of value(AAAAA) in unicode is greater than minLength
+Pass [INPUT in SEARCH status] Dirty value - value equals to minLength
+Pass [INPUT in SEARCH status] Dirty value - length of value is less than minLength
+Pass [INPUT in TEL status] Non-dirty value - minLength is not set
+Pass [INPUT in TEL status] Non-dirty value - value is empty string
+Pass [INPUT in TEL status] Non-dirty value - length of value is greater than minLength
+Pass [INPUT in TEL status] Non-dirty value - length of value equals to minLength
+Pass [INPUT in TEL status] Non-dirty value - length of value is less than minLength
+Pass [INPUT in TEL status] Dirty value - value is greater than minLength
+Pass [INPUT in TEL status] Dirty value - length of value(AAAAA) in unicode is greater than minLength
+Pass [INPUT in TEL status] Dirty value - value equals to minLength
+Pass [INPUT in TEL status] Dirty value - length of value is less than minLength
+Pass [INPUT in URL status] Non-dirty value - minLength is not set
+Pass [INPUT in URL status] Non-dirty value - value is empty string
+Pass [INPUT in URL status] Non-dirty value - length of value is greater than minLength
+Pass [INPUT in URL status] Non-dirty value - length of value equals to minLength
+Pass [INPUT in URL status] Non-dirty value - length of value is less than minLength
+Pass [INPUT in URL status] Dirty value - value is greater than minLength
+Pass [INPUT in URL status] Dirty value - length of value(AAAAA) in unicode is greater than minLength
+Pass [INPUT in URL status] Dirty value - value equals to minLength
+Pass [INPUT in URL status] Dirty value - length of value is less than minLength
+Pass [INPUT in EMAIL status] Non-dirty value - minLength is not set
+Pass [INPUT in EMAIL status] Non-dirty value - value is empty string
+Pass [INPUT in EMAIL status] Non-dirty value - length of value is greater than minLength
+Pass [INPUT in EMAIL status] Non-dirty value - length of value equals to minLength
+Pass [INPUT in EMAIL status] Non-dirty value - length of value is less than minLength
+Pass [INPUT in EMAIL status] Dirty value - value is greater than minLength
+Pass [INPUT in EMAIL status] Dirty value - length of value(AAAAA) in unicode is greater than minLength
+Pass [INPUT in EMAIL status] Dirty value - value equals to minLength
+Pass [INPUT in EMAIL status] Dirty value - length of value is less than minLength
+Pass [INPUT in PASSWORD status] Non-dirty value - minLength is not set
+Pass [INPUT in PASSWORD status] Non-dirty value - value is empty string
+Pass [INPUT in PASSWORD status] Non-dirty value - length of value is greater than minLength
+Pass [INPUT in PASSWORD status] Non-dirty value - length of value equals to minLength
+Pass [INPUT in PASSWORD status] Non-dirty value - length of value is less than minLength
+Pass [INPUT in PASSWORD status] Dirty value - value is greater than minLength
+Pass [INPUT in PASSWORD status] Dirty value - length of value(AAAAA) in unicode is greater than minLength
+Pass [INPUT in PASSWORD status] Dirty value - value equals to minLength
+Pass [INPUT in PASSWORD status] Dirty value - length of value is less than minLength
+Pass [textarea] Non-dirty value - minLength is no set
+Pass [textarea] Non-dirty value - value is empty string
+Pass [textarea] Non-dirty value - length of value is greater than minLength
+Pass [textarea] Non-dirty value - length of value equals to minLength
+Pass [textarea] Non-dirty value - length of length of value is greater than minLength
+Pass [textarea] Dirty value - value is less than minLength
+Pass [textarea] Dirty value - length of value(LF, CRLF) in unicode is less than minLength
+Pass [textarea] Dirty value - length of value equals to minLength
+Pass [textarea] Dirty value - length of value is greater than minLength
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-typeMismatch.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-typeMismatch.txt
new file mode 100644
index 00000000000..0768bad0669
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-typeMismatch.txt
@@ -0,0 +1,17 @@
+Harness status: OK
+
+Found 11 tests
+
+7 Pass
+4 Fail
+Pass [INPUT in EMAIL status] The value is empty
+Pass [INPUT in EMAIL status] The value is a valid email address
+Pass [INPUT in EMAIL status] The value is a valid email address with some white spaces.
+Fail [INPUT in EMAIL status] The value is not an email address
+Fail [INPUT in EMAIL status] The value contains multiple email addresses
+Pass [INPUT in EMAIL status] The value is valid email addresses
+Fail [INPUT in EMAIL status] The value contains invalid separator
+Pass [INPUT in URL status] The value is empty
+Pass [INPUT in URL status] The value is a valid url
+Pass [INPUT in URL status] The value is a valid url with some white spaces.
+Fail [INPUT in URL status] The value is not an url
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-valueMissing.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-valueMissing.txt
new file mode 100644
index 00000000000..d0c79a4af5a
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-valueMissing.txt
@@ -0,0 +1,84 @@
+Harness status: OK
+
+Found 78 tests
+
+44 Pass
+34 Fail
+Pass [INPUT in TEXT status] The required attribute is not set
+Pass [INPUT in TEXT status] The value is not empty and required is true
+Fail [INPUT in TEXT status] The value is empty and required is true
+Pass [INPUT in SEARCH status] The required attribute is not set
+Pass [INPUT in SEARCH status] The value is not empty and required is true
+Fail [INPUT in SEARCH status] The value is empty and required is true
+Pass [INPUT in TEL status] The required attribute is not set
+Pass [INPUT in TEL status] The value is not empty and required is true
+Fail [INPUT in TEL status] The value is empty and required is true
+Pass [INPUT in URL status] The required attribute is not set
+Pass [INPUT in URL status] The value is not empty and required is true
+Fail [INPUT in URL status] The value is empty and required is true
+Pass [INPUT in EMAIL status] The required attribute is not set
+Pass [INPUT in EMAIL status] The value is not empty and required is true
+Fail [INPUT in EMAIL status] The value is empty and required is true
+Pass [INPUT in PASSWORD status] The required attribute is not set
+Pass [INPUT in PASSWORD status] The value is not empty and required is true
+Fail [INPUT in PASSWORD status] The value is empty and required is true
+Pass [INPUT in DATETIME-LOCAL status] The required attribute is not set
+Pass [INPUT in DATETIME-LOCAL status] Valid local date and time string(2000-12-10T12:00:00)
+Pass [INPUT in DATETIME-LOCAL status] Valid local date and time string(2000-12-10 12:00)
+Pass [INPUT in DATETIME-LOCAL status] Valid local date and time string(1979-10-14T12:00:00.001)
+Fail [INPUT in DATETIME-LOCAL status] The value attribute is a number(1234567)
+Fail [INPUT in DATETIME-LOCAL status] The value attribute is a Date object
+Fail [INPUT in DATETIME-LOCAL status] Invalid local date and time string(1979-10-99 99:99)
+Pass [INPUT in DATETIME-LOCAL status] Valid local date and time string(1979-10-14 12:00:00)
+Fail [INPUT in DATETIME-LOCAL status] Invalid local date and time string(2001-12-21 12:00)-two white space
+Fail [INPUT in DATETIME-LOCAL status] the value attribute is a string(abc)
+Fail [INPUT in DATETIME-LOCAL status] The value attribute is empty string
+Pass [INPUT in DATE status] The required attribute is not set
+Pass [INPUT in DATE status] Valid date string(2000-12-10)
+Pass [INPUT in DATE status] Valid date string(9999-01-01)
+Fail [INPUT in DATE status] The value attribute is a number(1234567)
+Fail [INPUT in DATE status] The value attribute is a Date object
+Fail [INPUT in DATE status] Invalid date string(9999-99-99)
+Fail [INPUT in DATE status] Invalid date string(37-01-01)
+Fail [INPUT in DATE status] Invalid date string(2000/01/01)
+Fail [INPUT in DATE status] The value attribute is empty string
+Pass [INPUT in TIME status] The required attribute is not set
+Pass [INPUT in TIME status] Validtime string(12:00:00)
+Pass [INPUT in TIME status] Validtime string(12:00)
+Pass [INPUT in TIME status] Valid time string(12:00:60.001)
+Pass [INPUT in TIME status] Valid time string(12:00:60.01)
+Pass [INPUT in TIME status] Valid time string(12:00:60.1)
+Fail [INPUT in TIME status] The value attribute is a number(1234567)
+Fail [INPUT in TIME status] The value attribute is a time object
+Fail [INPUT in TIME status] Invalid time string(25:00:00)
+Fail [INPUT in TIME status] Invalid time string(12:60:00)
+Fail [INPUT in TIME status] Invalid time string(12:00:60)
+Fail [INPUT in TIME status] Invalid time string(12:00:00:001)
+Fail [INPUT in TIME status] The value attribute is empty string
+Pass [INPUT in NUMBER status] The required attribute is not set
+Pass [INPUT in NUMBER status] Value is an integer with a leading symbol '+'
+Pass [INPUT in NUMBER status] Value is a number with a '-' symbol
+Pass [INPUT in NUMBER status] Value is a number in scientific notation form(e is in lowercase)
+Pass [INPUT in NUMBER status] Value is a number in scientific notation form(E is in uppercase)
+Pass [INPUT in NUMBER status] Value is -0
+Fail [INPUT in NUMBER status] Value is a number with some white spaces
+Fail [INPUT in NUMBER status] Value is Math.pow(2, 1024)
+Fail [INPUT in NUMBER status] Value is Math.pow(-2, 1024)
+Fail [INPUT in NUMBER status] Value is a string that cannot be converted to a number
+Fail [INPUT in NUMBER status] The value attribute is empty string
+Pass [INPUT in CHECKBOX status] The required attribute is not set
+Pass [INPUT in CHECKBOX status] The checked attribute is true
+Pass [INPUT in CHECKBOX status] The checked attribute is false
+Pass [INPUT in RADIO status] The required attribute is not set
+Pass [INPUT in RADIO status] The checked attribute is true
+Fail [INPUT in RADIO status] The checked attribute is false
+Pass [INPUT in RADIO status] The checked attribute is false and the name attribute is empty
+Pass [INPUT in FILE status] The required attribute is not set
+Fail [INPUT in FILE status] The Files attribute is null
+Pass [select] The required attribute is not set
+Pass [select] Selected the option with value equals to 1
+Pass [select] Selected the option with value equals to empty
+Pass [textarea] The required attribute is not set
+Pass [textarea] The value is not empty
+Fail [textarea] The value is empty
+Fail validationMessage should return empty string when willValidate is false and valueMissing is true
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/radio-valueMissing.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/radio-valueMissing.txt
new file mode 100644
index 00000000000..fc2b600dd08
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/radio-valueMissing.txt
@@ -0,0 +1,12 @@
+Harness status: OK
+
+Found 6 tests
+
+4 Pass
+2 Fail
+Pass The required attribute is not set
+Fail One of the radios is required, but none checked
+Pass One of the radios is required and checked
+Pass One of the radios is required and another one is checked
+Fail One of the radios is required and disabled, but none checked
+Pass One of the radios is required, checked and disabled
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-button-element/button-validity.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-button-element/button-validity.txt
new file mode 100644
index 00000000000..ef3cc8b460e
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-button-element/button-validity.txt
@@ -0,0 +1,6 @@
+Harness status: OK
+
+Found 1 tests
+
+1 Pass
+Pass Forms
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/input-validity.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/input-validity.txt
new file mode 100644
index 00000000000..ef3cc8b460e
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/input-validity.txt
@@ -0,0 +1,6 @@
+Harness status: OK
+
+Found 1 tests
+
+1 Pass
+Pass Forms
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/radio.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/radio.txt
new file mode 100644
index 00000000000..0bbc41ea756
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/radio.txt
@@ -0,0 +1,18 @@
+Harness status: OK
+
+Found 12 tests
+
+11 Pass
+1 Fail
+Pass click on mutable radio fires click event, then input event, then change event
+Pass click on non-mutable radio doesn't fire the input event
+Pass click on non-mutable radio doesn't fire the change event
+Pass canceled activation steps on unchecked radio
+Pass only one control of a radio button group can have its checkedness set to true
+Pass radio inputs with non-ASCII name attributes belong to the same radio button group
+Pass changing the name of a radio input element and setting its checkedness to true makes all the other elements' checkedness in the same radio button group be set to false
+Pass moving radio input element out of or into a form should still work as expected
+Fail Radio buttons in an orphan tree should make a group
+Pass Radio buttons in different groups (because they have different form owners or no form owner) do not affect each other's checkedness
+Pass Radio buttons in different groups (because they are not in the same tree) do not affect each other's checkedness
+Pass Radio buttons in different groups (because they have different name attribute values, or no name attribute) do not affect each other's checkedness
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.html
new file mode 100644
index 00000000000..f2737180d08
--- /dev/null
+++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.html
@@ -0,0 +1,89 @@
+
+
+The constraint validation API Test: element.validity.rangeOverflow
+
+
+
+
+
+
+
+
diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.html
new file mode 100644
index 00000000000..df9ce3932c5
--- /dev/null
+++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.html
@@ -0,0 +1,87 @@
+
+
+The constraint validation API Test: element.validity.rangeUnderflow
+
+
+
+
+
+
+
+
diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooLong.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooLong.html
new file mode 100644
index 00000000000..2def8467f0d
--- /dev/null
+++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooLong.html
@@ -0,0 +1,50 @@
+
+
+The constraint validation API Test: element.validity.tooLong
+
+
+
+
+
+
+
+
diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooShort.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooShort.html
new file mode 100644
index 00000000000..16f74b3dfb0
--- /dev/null
+++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-tooShort.html
@@ -0,0 +1,52 @@
+
+
+The constraint validation API Test: element.validity.tooShort
+
+
+
+
+
+
+
+
diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-typeMismatch.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-typeMismatch.html
new file mode 100644
index 00000000000..c4d063c0968
--- /dev/null
+++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-typeMismatch.html
@@ -0,0 +1,41 @@
+
+
+The constraint validation API Test: element.validity.typeMismatch
+
+
+
+
+
+
+
+
diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-valueMissing.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-valueMissing.html
new file mode 100644
index 00000000000..72a5eaa416e
--- /dev/null
+++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-validity-valueMissing.html
@@ -0,0 +1,147 @@
+
+
+The constraint validation API Test: element.validity.valueMissing
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/radio-valueMissing.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/radio-valueMissing.html
new file mode 100644
index 00000000000..0bbfada1db6
--- /dev/null
+++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/radio-valueMissing.html
@@ -0,0 +1,90 @@
+
+
+
+ valueMissing property on the input[type=radio] element
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/support/validator.js b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/support/validator.js
new file mode 100644
index 00000000000..aa43b3a2f6a
--- /dev/null
+++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/support/validator.js
@@ -0,0 +1,481 @@
+var validator = {
+
+ test_tooLong: function(ctl, data) {
+ var self = this;
+ test(function() {
+ self.pre_check(ctl, 'tooLong');
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.tooLong,
+ 'The validity.tooLong should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.tooLong,
+ 'The validity.tooLong should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_tooShort: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "tooShort");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.tooShort,
+ 'The validity.tooShort should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.tooShort,
+ 'The validity.tooShort should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_patternMismatch: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "patternMismatch");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.patternMismatch,
+ 'The validity.patternMismatch should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.patternMismatch,
+ 'The validity.patternMismatch should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_valueMissing: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "valueMissing");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.valueMissing,
+ 'The validity.valueMissing should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.valueMissing,
+ 'The validity.valueMissing should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_typeMismatch: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "typeMismatch");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.typeMismatch,
+ 'The validity.typeMismatch should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.typeMismatch,
+ 'The validity.typeMismatch should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_rangeOverflow: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "rangeOverflow");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.rangeOverflow,
+ 'The validity.rangeOverflow should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.rangeOverflow,
+ 'The validity.rangeOverflow should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_rangeUnderflow: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "rangeUnderflow");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.rangeUnderflow,
+ 'The validity.rangeUnderflow should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.rangeUnderflow,
+ 'The validity.rangeUnderflow should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_stepMismatch: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "stepMismatch");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.stepMismatch,
+ 'The validity.stepMismatch should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.stepMismatch,
+ 'The validity.stepMismatch should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_badInput: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "badInput");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.badInput,
+ 'The validity.badInput should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.badInput,
+ 'The validity.badInput should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_customError: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "customError");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected) {
+ assert_true(
+ ctl.validity.customError,
+ 'The validity.customError attribute should be true' + condStr);
+ // validationMessage returns the empty string if ctl is barred from
+ // constraint validation, which happens if ctl is disabled or readOnly.
+ if (ctl.disabled || ctl.readOnly) {
+ assert_equals(
+ ctl.validationMessage, '',
+ 'The validationMessage attribute must be empty' + condStr);
+ } else {
+ assert_equals(
+ ctl.validationMessage, data.conditions.message,
+ 'The validationMessage attribute should be \'' +
+ data.conditions.message + '\'' + condStr);
+ }
+ } else {
+ assert_false(
+ ctl.validity.customError,
+ 'The validity.customError attribute should be false' + condStr);
+ assert_equals(
+ ctl.validationMessage, '',
+ 'The validationMessage attribute must be empty' + condStr);
+ }
+ });
+ }, data.name);
+ },
+
+ test_isValid: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.valid,
+ 'The validity.valid should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.valid,
+ 'The validity.valid should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_willValidate: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "willValidate");
+ self.set_conditions(ctl, data.conditions);
+ if (data.ancestor) {
+ var dl = document.createElement("datalist");
+ dl.appendChild(ctl);
+ }
+
+ if (data.expected)
+ assert_true(ctl.willValidate, "The willValidate attribute should be true.");
+ else
+ assert_false(ctl.willValidate, "The willValidate attribute should be false.");
+ }, data.name);
+ },
+
+ test_checkValidity: function(ctl, data) {
+ var self = this;
+ test(function () {
+ var eventFired = false;
+ self.pre_check(ctl, "checkValidity");
+ self.set_conditions(ctl, data.conditions);
+ if (data.dirty)
+ self.set_dirty(ctl);
+
+ on_event(ctl, "invalid", function(e){
+ assert_equals(e.type, "invalid", "The invalid event should be fired.");
+ eventFired = true;
+ });
+
+ if (data.expected) {
+ assert_true(ctl.checkValidity(), "The checkValidity method should be true.");
+ assert_false(eventFired, "The invalid event should not be fired.");
+ } else {
+ assert_false(ctl.checkValidity(), "The checkValidity method should be false.");
+ assert_true(eventFired, "The invalid event should be fired.");
+ }
+ }, data.name);
+
+ test(function () {
+ var fm = document.createElement("form");
+ var ctl2 = ctl.cloneNode(true);
+
+ self.pre_check(ctl, "checkValidity");
+ self.set_conditions(ctl2, data.conditions);
+ fm.appendChild(ctl2);
+ document.body.appendChild(fm);
+ if (data.dirty)
+ self.set_dirty(ctl2);
+
+ var result = fm.checkValidity();
+ document.body.removeChild(fm);
+
+ if (data.expected)
+ assert_true(result, "The checkValidity method of the element's form owner should return true.");
+ else
+ assert_false(result, "The checkValidity method of the element's form owner should return false.");
+ }, data.name + " (in a form)");
+ },
+
+ test_reportValidity: function(ctl, data) {
+ var self = this;
+ test(function () {
+ var eventFired = false;
+
+ self.pre_check(ctl, "reportValidity");
+ self.set_conditions(ctl, data.conditions);
+ if (data.dirty)
+ self.set_dirty(ctl);
+
+ on_event(ctl, "invalid", function(e){
+ assert_equals(e.type, "invalid", "The invalid event should be fired.");
+ eventFired = true;
+ });
+
+ if (data.expected) {
+ assert_true(ctl.reportValidity(), "The reportValidity method should be true.");
+ assert_false(eventFired, "The invalid event should not be fired.");
+ } else {
+ assert_false(ctl.reportValidity(), "The reportValidity method should be false.");
+ assert_true(eventFired, "The invalid event should be fired.");
+ }
+ }, data.name);
+
+ test(function () {
+ var fm = document.createElement("form");
+ var ctl2 = ctl.cloneNode(true);
+
+ self.pre_check(ctl, "reportValidity");
+ self.set_conditions(ctl2, data.conditions);
+ fm.appendChild(ctl2);
+ document.body.appendChild(fm);
+ if (data.dirty)
+ self.set_dirty(ctl2);
+
+ var result = fm.reportValidity();
+ document.body.removeChild(fm);
+
+ if (data.expected)
+ assert_true(result, "The reportValidity method of the element's form owner should return true.");
+ else
+ assert_false(result, "The reportValidity method of the element's form owner should return false.");
+ }, data.name + " (in a form)");
+ },
+
+ set_conditions: function(ctl, obj) {
+ [
+ "checked",
+ "disabled",
+ "max",
+ "maxlength",
+ "min",
+ "minlength",
+ "multiple",
+ "pattern",
+ "readonly",
+ "required",
+ "selected",
+ "step",
+ "value"
+ ].forEach(function(item) {
+ ctl.removeAttribute(item);
+ });
+ for (var attr in obj) {
+ if (attr === "message")
+ ctl.setCustomValidity(obj[attr]);
+ else if (attr === "checked" || obj[attr] || obj[attr] === "")
+ ctl[attr] = obj[attr];
+ }
+ },
+
+ set_dirty: function(ctl) {
+ ctl.focus();
+ var old_value = ctl.value;
+ ctl.value = "a";
+ ctl.value = old_value;
+ },
+
+ pre_check: function(ctl, item) {
+ switch (item) {
+ case "willValidate":
+ assert_true(item in ctl, "The " + item + " attribute doesn't exist.");
+ break;
+ case "checkValidity":
+ case "reportValidity":
+ assert_true(item in ctl, "The " + item + " method doesn't exist.");
+ break;
+ case "tooLong":
+ case "tooShort":
+ case "patternMismatch":
+ case "typeMismatch":
+ case "stepMismatch":
+ case "rangeOverflow":
+ case "rangeUnderflow":
+ case "valueMissing":
+ case "badInput":
+ case "valid":
+ assert_true("validity" in ctl, "The validity attribute doesn't exist.");
+ assert_true(item in ctl.validity, "The " + item + " attribute doesn't exist.");
+ break;
+ case "customError":
+ assert_true("validity" in ctl, "The validity attribute doesn't exist.");
+ assert_true("setCustomValidity" in ctl, "The validity attribute doesn't exist.");
+ assert_true("validationMessage" in ctl, "The validity attribute doesn't exist.");
+ assert_true(item in ctl.validity, "The " + item + " attribute doesn't exist.");
+ break;
+ }
+ },
+
+ iterate_over: function(ctl, data) {
+ // Iterate over normal, disabled, readonly, and both (if applicable).
+ var ctlNormal = ctl.cloneNode(true);
+ this.set_conditions(ctlNormal, data.conditions);
+ if (data.dirty)
+ this.set_dirty(ctlNormal);
+
+ var ctlDisabled = ctl.cloneNode(true);
+ this.set_conditions(ctlDisabled, data.conditions);
+ if (data.dirty)
+ this.set_dirty(ctlDisabled);
+ ctlDisabled.disabled = true;
+
+ var expectedImmutable =
+ data.expectedImmutable !== undefined ? data.expectedImmutable : data.expected;
+
+ var variants = [
+ {ctl: ctlNormal, expected: data.expected, condStr: '.'},
+ {ctl: ctlDisabled, expected: expectedImmutable, condStr: ', when control is disabled.'},
+ ];
+
+ if ('readOnly' in ctl) {
+ var ctlReadonly = ctl.cloneNode(true);
+ this.set_conditions(ctlReadonly, data.conditions);
+ if (data.dirty)
+ this.set_dirty(ctlReadonly);
+ ctlReadonly.readOnly = true;
+
+ var ctlBoth = ctl.cloneNode(true);
+ this.set_conditions(ctlBoth, data.conditions);
+ if (data.dirty)
+ this.set_dirty(ctlBoth);
+ ctlBoth.disabled = true;
+ ctlBoth.readOnly = true;
+
+ variants.push({
+ ctl: ctlReadonly,
+ expected: expectedImmutable,
+ condStr: ', when control is readonly.'
+ });
+
+ variants.push({
+ ctl: ctlBoth,
+ expected: expectedImmutable,
+ condStr: ', when control is disabled & readonly.'
+ });
+ }
+
+ return variants;
+ },
+
+ run_test: function(testee, method) {
+ var testMethod = "test_" + method;
+ if (typeof this[testMethod] !== "function") {
+ return false;
+ }
+
+ var ele = null,
+ prefix = "";
+
+ for (var i = 0; i < testee.length; i++) {
+ if (testee[i].types.length > 0) {
+ for (var typ in testee[i].types) {
+ ele = document.createElement(testee[i].tag);
+ document.body.appendChild(ele);
+ try {
+ ele.type = testee[i].types[typ];
+ } catch (e) {
+ //Do nothing, avoid the runtime error breaking the test
+ }
+
+ prefix = "[" + testee[i].tag.toUpperCase() + " in " + testee[i].types[typ].toUpperCase() + " status] ";
+
+ for (var j = 0; j < testee[i].testData.length; j++) {
+ testee[i].testData[j].name = testee[i].testData[j].name.replace(/\[.*\]\s/g, prefix);
+ this[testMethod](ele, testee[i].testData[j]);
+ }
+ }
+ } else {
+ ele = document.createElement(testee[i].tag);
+ document.body.appendChild(ele);
+ prefix = "[" + testee[i].tag + "] ";
+
+ if (testElements[i].tag === "select") {
+ ele.add(new Option('test1', '')); // Placeholder
+ ele.add(new Option("test2", 1));
+ }
+
+ for (var item in testee[i].testData) {
+ testee[i].testData[item].name = testee[i].testData[item].name.replace("[target]", prefix);
+ this[testMethod](ele, testee[i].testData[item]);
+ }
+ }
+ }
+ }
+}
diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-button-element/button-validity.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-button-element/button-validity.html
new file mode 100644
index 00000000000..3492f0444b5
--- /dev/null
+++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-button-element/button-validity.html
@@ -0,0 +1,40 @@
+
+
+
+ Forms
+
+
+
+
+