LibWeb/CSS: Make :heading() pseudo-class take integers not AN+B

Corresponds to 8eb3787e34
This commit is contained in:
Sam Atkins 2025-08-28 10:29:57 +01:00 committed by Jelle Raaijmakers
commit d461e96f40
Notes: github-actions[bot] 2025-08-28 10:41:31 +00:00
10 changed files with 79 additions and 161 deletions

View file

@ -899,6 +899,49 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
.languages = move(languages) }
};
}
case PseudoClassMetadata::ParameterType::LevelList: {
// https://drafts.csswg.org/selectors-5/#heading-functional-pseudo
// :heading() = :heading( <level># )
// where <level> is a <number-token> with its type flag set to "integer".
Vector<i64> levels;
auto function_token_stream = TokenStream(pseudo_function.value);
auto level_lists = parse_a_comma_separated_list_of_component_values(function_token_stream);
for (auto const& level_tokens : level_lists) {
TokenStream level_token_stream { level_tokens };
level_token_stream.discard_whitespace();
auto& maybe_integer = level_token_stream.consume_a_token();
level_token_stream.discard_whitespace();
if (!maybe_integer.is(Token::Type::Number) || !maybe_integer.token().number().is_integer()) {
ErrorReporter::the().report(InvalidPseudoClassOrElementError {
.name = MUST(String::formatted(":{}", pseudo_function.name)),
.value_string = pseudo_class_token.to_string(),
.description = "Failed to parse argument as a <level>: Not an <integer> literal."_string,
});
return ParseError::SyntaxError;
}
if (level_token_stream.has_next_token()) {
ErrorReporter::the().report(InvalidPseudoClassOrElementError {
.name = MUST(String::formatted(":{}", pseudo_function.name)),
.value_string = pseudo_class_token.to_string(),
.description = "Failed to parse argument as a <level>: Has trailing tokens."_string,
});
return ParseError::SyntaxError;
}
levels.append(maybe_integer.token().number().integer_value());
}
return Selector::SimpleSelector {
.type = Selector::SimpleSelector::Type::PseudoClass,
.value = Selector::SimpleSelector::PseudoClassSelector {
.type = pseudo_class,
.levels = move(levels),
}
};
}
case PseudoClassMetadata::ParameterType::RelativeSelectorList:
case PseudoClassMetadata::ParameterType::SelectorList: {
auto function_token_stream = TokenStream(pseudo_function.value);

View file

@ -51,7 +51,7 @@
"argument": "<relative-selector-list>"
},
"heading": {
"argument": "<an+b>#?"
"argument": "<level>#?"
},
"high-value": {
"argument": ""

View file

@ -435,7 +435,7 @@ String Selector::SimpleSelector::serialize() const
// For pseudo-classes with both a function and identifier form, see if they have arguments.
switch (pseudo_class.type) {
case PseudoClass::Heading:
return !pseudo_class.an_plus_b_patterns.is_empty();
return !pseudo_class.levels.is_empty();
case PseudoClass::Host:
return !pseudo_class.argument_selector_list.is_empty();
default:
@ -484,6 +484,10 @@ String Selector::SimpleSelector::serialize() const
// The serialization of a comma-separated list of each arguments serialization as a string, preserving relative order.
s.join(", "sv, pseudo_class.languages);
break;
case PseudoClassMetadata::ParameterType::LevelList:
// AD-HOC: not in the spec.
s.join(", "sv, pseudo_class.levels);
break;
}
s.append(')');
}

View file

@ -109,6 +109,9 @@ public:
FlyString string_value;
};
Optional<Ident> ident {};
// Used by :heading()
Vector<i64> levels {};
};
struct Name {

View file

@ -1037,7 +1037,7 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
// The :heading pseudo-class must match all h1, h2, h3, h4, h5, and h6 elements.
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-heading-functional
// The :heading(An+B#) pseudo-class must match all h1, h2, h3, h4, h5, and h6 elements that have a heading level among An+B. [CSSSYNTAX] [CSSVALUES]
// The :heading(integer#) pseudo-class must match all h1, h2, h3, h4, h5, and h6 elements that have a heading level of integer. [CSSSYNTAX] [CSSVALUES]
// NB: We combine the "is this an h* element?" and "what is it's level?" checks together here.
if (!element.is_html_element())
@ -1061,15 +1061,10 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
if (!heading_level.has_value())
return false;
if (pseudo_class.an_plus_b_patterns.is_empty())
if (pseudo_class.levels.is_empty())
return true;
for (auto const& an_plus_b_pattern : pseudo_class.an_plus_b_patterns) {
if (an_plus_b_pattern.matches(heading_level.value()))
return true;
}
return false;
return pseudo_class.levels.contains_slow(heading_level.value());
}
}

View file

@ -598,6 +598,12 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector, int in
builder.append(')');
break;
}
case CSS::PseudoClassMetadata::ParameterType::LevelList: {
builder.append('(');
builder.join(',', pseudo_class.levels);
builder.append(')');
break;
}
}
}

View file

@ -77,6 +77,7 @@ struct PseudoClassMetadata {
ForgivingRelativeSelectorList,
Ident,
LanguageRanges,
LevelList,
RelativeSelectorList,
SelectorList,
} parameter_type;
@ -183,6 +184,8 @@ PseudoClassMetadata pseudo_class_metadata(PseudoClass pseudo_class)
parameter_type = "Ident"_string;
} else if (argument_string == "<language-ranges>"sv) {
parameter_type = "LanguageRanges"_string;
} else if (argument_string == "<level>#"sv) {
parameter_type = "LevelList"_string;
} else if (argument_string == "<relative-selector-list>"sv) {
parameter_type = "RelativeSelectorList"_string;
} else if (argument_string == "<selector-list>"sv) {

View file

@ -1,8 +1,8 @@
Harness status: OK
Found 308 tests
Found 182 tests
308 Pass
182 Pass
Pass <h1 data-expect-match="1"></h1> :heading
Pass <h1 data-expect-match="1"></h1> :heading(1)
Pass <h1 data-expect-match="1"></h1> :heading(2)
@ -14,17 +14,8 @@ Pass <h1 data-expect-match="1"></h1> :heading(7)
Pass <h1 data-expect-match="1"></h1> :heading(8)
Pass <h1 data-expect-match="1"></h1> :heading(9)
Pass <h1 data-expect-match="1"></h1> :heading(0)
Pass <h1 data-expect-match="1"></h1> :heading(-1)
Pass <h1 data-expect-match="1"></h1> :heading(0, 1, 2)
Pass <h1 data-expect-match="1"></h1> :heading(6, 7)
Pass <h1 data-expect-match="1"></h1> :heading(n)
Pass <h1 data-expect-match="1"></h1> :heading(2n)
Pass <h1 data-expect-match="1"></h1> :heading(2n+1)
Pass <h1 data-expect-match="1"></h1> :heading(2n+2)
Pass <h1 data-expect-match="1"></h1> :heading(-n+3)
Pass <h1 data-expect-match="1"></h1> :heading(2n, 3n)
Pass <h1 data-expect-match="1"></h1> :heading(even)
Pass <h1 data-expect-match="1"></h1> :heading(odd)
Pass <h2 data-expect-match="2"></h2> :heading
Pass <h2 data-expect-match="2"></h2> :heading(1)
Pass <h2 data-expect-match="2"></h2> :heading(2)
@ -36,17 +27,8 @@ Pass <h2 data-expect-match="2"></h2> :heading(7)
Pass <h2 data-expect-match="2"></h2> :heading(8)
Pass <h2 data-expect-match="2"></h2> :heading(9)
Pass <h2 data-expect-match="2"></h2> :heading(0)
Pass <h2 data-expect-match="2"></h2> :heading(-1)
Pass <h2 data-expect-match="2"></h2> :heading(0, 1, 2)
Pass <h2 data-expect-match="2"></h2> :heading(6, 7)
Pass <h2 data-expect-match="2"></h2> :heading(n)
Pass <h2 data-expect-match="2"></h2> :heading(2n)
Pass <h2 data-expect-match="2"></h2> :heading(2n+1)
Pass <h2 data-expect-match="2"></h2> :heading(2n+2)
Pass <h2 data-expect-match="2"></h2> :heading(-n+3)
Pass <h2 data-expect-match="2"></h2> :heading(2n, 3n)
Pass <h2 data-expect-match="2"></h2> :heading(even)
Pass <h2 data-expect-match="2"></h2> :heading(odd)
Pass <h3 data-expect-match="3"></h3> :heading
Pass <h3 data-expect-match="3"></h3> :heading(1)
Pass <h3 data-expect-match="3"></h3> :heading(2)
@ -58,17 +40,8 @@ Pass <h3 data-expect-match="3"></h3> :heading(7)
Pass <h3 data-expect-match="3"></h3> :heading(8)
Pass <h3 data-expect-match="3"></h3> :heading(9)
Pass <h3 data-expect-match="3"></h3> :heading(0)
Pass <h3 data-expect-match="3"></h3> :heading(-1)
Pass <h3 data-expect-match="3"></h3> :heading(0, 1, 2)
Pass <h3 data-expect-match="3"></h3> :heading(6, 7)
Pass <h3 data-expect-match="3"></h3> :heading(n)
Pass <h3 data-expect-match="3"></h3> :heading(2n)
Pass <h3 data-expect-match="3"></h3> :heading(2n+1)
Pass <h3 data-expect-match="3"></h3> :heading(2n+2)
Pass <h3 data-expect-match="3"></h3> :heading(-n+3)
Pass <h3 data-expect-match="3"></h3> :heading(2n, 3n)
Pass <h3 data-expect-match="3"></h3> :heading(even)
Pass <h3 data-expect-match="3"></h3> :heading(odd)
Pass <h4 data-expect-match="4"></h4> :heading
Pass <h4 data-expect-match="4"></h4> :heading(1)
Pass <h4 data-expect-match="4"></h4> :heading(2)
@ -80,17 +53,8 @@ Pass <h4 data-expect-match="4"></h4> :heading(7)
Pass <h4 data-expect-match="4"></h4> :heading(8)
Pass <h4 data-expect-match="4"></h4> :heading(9)
Pass <h4 data-expect-match="4"></h4> :heading(0)
Pass <h4 data-expect-match="4"></h4> :heading(-1)
Pass <h4 data-expect-match="4"></h4> :heading(0, 1, 2)
Pass <h4 data-expect-match="4"></h4> :heading(6, 7)
Pass <h4 data-expect-match="4"></h4> :heading(n)
Pass <h4 data-expect-match="4"></h4> :heading(2n)
Pass <h4 data-expect-match="4"></h4> :heading(2n+1)
Pass <h4 data-expect-match="4"></h4> :heading(2n+2)
Pass <h4 data-expect-match="4"></h4> :heading(-n+3)
Pass <h4 data-expect-match="4"></h4> :heading(2n, 3n)
Pass <h4 data-expect-match="4"></h4> :heading(even)
Pass <h4 data-expect-match="4"></h4> :heading(odd)
Pass <h5 data-expect-match="5"></h5> :heading
Pass <h5 data-expect-match="5"></h5> :heading(1)
Pass <h5 data-expect-match="5"></h5> :heading(2)
@ -102,17 +66,8 @@ Pass <h5 data-expect-match="5"></h5> :heading(7)
Pass <h5 data-expect-match="5"></h5> :heading(8)
Pass <h5 data-expect-match="5"></h5> :heading(9)
Pass <h5 data-expect-match="5"></h5> :heading(0)
Pass <h5 data-expect-match="5"></h5> :heading(-1)
Pass <h5 data-expect-match="5"></h5> :heading(0, 1, 2)
Pass <h5 data-expect-match="5"></h5> :heading(6, 7)
Pass <h5 data-expect-match="5"></h5> :heading(n)
Pass <h5 data-expect-match="5"></h5> :heading(2n)
Pass <h5 data-expect-match="5"></h5> :heading(2n+1)
Pass <h5 data-expect-match="5"></h5> :heading(2n+2)
Pass <h5 data-expect-match="5"></h5> :heading(-n+3)
Pass <h5 data-expect-match="5"></h5> :heading(2n, 3n)
Pass <h5 data-expect-match="5"></h5> :heading(even)
Pass <h5 data-expect-match="5"></h5> :heading(odd)
Pass <h6 data-expect-match="6"></h6> :heading
Pass <h6 data-expect-match="6"></h6> :heading(1)
Pass <h6 data-expect-match="6"></h6> :heading(2)
@ -124,17 +79,8 @@ Pass <h6 data-expect-match="6"></h6> :heading(7)
Pass <h6 data-expect-match="6"></h6> :heading(8)
Pass <h6 data-expect-match="6"></h6> :heading(9)
Pass <h6 data-expect-match="6"></h6> :heading(0)
Pass <h6 data-expect-match="6"></h6> :heading(-1)
Pass <h6 data-expect-match="6"></h6> :heading(0, 1, 2)
Pass <h6 data-expect-match="6"></h6> :heading(6, 7)
Pass <h6 data-expect-match="6"></h6> :heading(n)
Pass <h6 data-expect-match="6"></h6> :heading(2n)
Pass <h6 data-expect-match="6"></h6> :heading(2n+1)
Pass <h6 data-expect-match="6"></h6> :heading(2n+2)
Pass <h6 data-expect-match="6"></h6> :heading(-n+3)
Pass <h6 data-expect-match="6"></h6> :heading(2n, 3n)
Pass <h6 data-expect-match="6"></h6> :heading(even)
Pass <h6 data-expect-match="6"></h6> :heading(odd)
Pass <h7 data-expect-match=""></h7> :heading
Pass <h7 data-expect-match=""></h7> :heading(1)
Pass <h7 data-expect-match=""></h7> :heading(2)
@ -146,17 +92,8 @@ Pass <h7 data-expect-match=""></h7> :heading(7)
Pass <h7 data-expect-match=""></h7> :heading(8)
Pass <h7 data-expect-match=""></h7> :heading(9)
Pass <h7 data-expect-match=""></h7> :heading(0)
Pass <h7 data-expect-match=""></h7> :heading(-1)
Pass <h7 data-expect-match=""></h7> :heading(0, 1, 2)
Pass <h7 data-expect-match=""></h7> :heading(6, 7)
Pass <h7 data-expect-match=""></h7> :heading(n)
Pass <h7 data-expect-match=""></h7> :heading(2n)
Pass <h7 data-expect-match=""></h7> :heading(2n+1)
Pass <h7 data-expect-match=""></h7> :heading(2n+2)
Pass <h7 data-expect-match=""></h7> :heading(-n+3)
Pass <h7 data-expect-match=""></h7> :heading(2n, 3n)
Pass <h7 data-expect-match=""></h7> :heading(even)
Pass <h7 data-expect-match=""></h7> :heading(odd)
Pass <h8 data-expect-match=""></h8> :heading
Pass <h8 data-expect-match=""></h8> :heading(1)
Pass <h8 data-expect-match=""></h8> :heading(2)
@ -168,17 +105,8 @@ Pass <h8 data-expect-match=""></h8> :heading(7)
Pass <h8 data-expect-match=""></h8> :heading(8)
Pass <h8 data-expect-match=""></h8> :heading(9)
Pass <h8 data-expect-match=""></h8> :heading(0)
Pass <h8 data-expect-match=""></h8> :heading(-1)
Pass <h8 data-expect-match=""></h8> :heading(0, 1, 2)
Pass <h8 data-expect-match=""></h8> :heading(6, 7)
Pass <h8 data-expect-match=""></h8> :heading(n)
Pass <h8 data-expect-match=""></h8> :heading(2n)
Pass <h8 data-expect-match=""></h8> :heading(2n+1)
Pass <h8 data-expect-match=""></h8> :heading(2n+2)
Pass <h8 data-expect-match=""></h8> :heading(-n+3)
Pass <h8 data-expect-match=""></h8> :heading(2n, 3n)
Pass <h8 data-expect-match=""></h8> :heading(even)
Pass <h8 data-expect-match=""></h8> :heading(odd)
Pass <h9 data-expect-match=""></h9> :heading
Pass <h9 data-expect-match=""></h9> :heading(1)
Pass <h9 data-expect-match=""></h9> :heading(2)
@ -190,17 +118,8 @@ Pass <h9 data-expect-match=""></h9> :heading(7)
Pass <h9 data-expect-match=""></h9> :heading(8)
Pass <h9 data-expect-match=""></h9> :heading(9)
Pass <h9 data-expect-match=""></h9> :heading(0)
Pass <h9 data-expect-match=""></h9> :heading(-1)
Pass <h9 data-expect-match=""></h9> :heading(0, 1, 2)
Pass <h9 data-expect-match=""></h9> :heading(6, 7)
Pass <h9 data-expect-match=""></h9> :heading(n)
Pass <h9 data-expect-match=""></h9> :heading(2n)
Pass <h9 data-expect-match=""></h9> :heading(2n+1)
Pass <h9 data-expect-match=""></h9> :heading(2n+2)
Pass <h9 data-expect-match=""></h9> :heading(-n+3)
Pass <h9 data-expect-match=""></h9> :heading(2n, 3n)
Pass <h9 data-expect-match=""></h9> :heading(even)
Pass <h9 data-expect-match=""></h9> :heading(odd)
Pass <h0 data-expect-match=""></h0> :heading
Pass <h0 data-expect-match=""></h0> :heading(1)
Pass <h0 data-expect-match=""></h0> :heading(2)
@ -212,17 +131,8 @@ Pass <h0 data-expect-match=""></h0> :heading(7)
Pass <h0 data-expect-match=""></h0> :heading(8)
Pass <h0 data-expect-match=""></h0> :heading(9)
Pass <h0 data-expect-match=""></h0> :heading(0)
Pass <h0 data-expect-match=""></h0> :heading(-1)
Pass <h0 data-expect-match=""></h0> :heading(0, 1, 2)
Pass <h0 data-expect-match=""></h0> :heading(6, 7)
Pass <h0 data-expect-match=""></h0> :heading(n)
Pass <h0 data-expect-match=""></h0> :heading(2n)
Pass <h0 data-expect-match=""></h0> :heading(2n+1)
Pass <h0 data-expect-match=""></h0> :heading(2n+2)
Pass <h0 data-expect-match=""></h0> :heading(-n+3)
Pass <h0 data-expect-match=""></h0> :heading(2n, 3n)
Pass <h0 data-expect-match=""></h0> :heading(even)
Pass <h0 data-expect-match=""></h0> :heading(odd)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(1)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(2)
@ -234,17 +144,8 @@ Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(7)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(8)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(9)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(0)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(-1)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(0, 1, 2)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(6, 7)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(n)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(2n)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(2n+1)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(2n+2)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(-n+3)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(2n, 3n)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(even)
Pass <h1 data-expect-match="1" aria-level="2"></h1> :heading(odd)
Pass <h1 data-expect-match="1"></h1> in section :heading
Pass <h1 data-expect-match="1"></h1> in section :heading(1)
Pass <h1 data-expect-match="1"></h1> in section :heading(2)
@ -256,17 +157,8 @@ Pass <h1 data-expect-match="1"></h1> in section :heading(7)
Pass <h1 data-expect-match="1"></h1> in section :heading(8)
Pass <h1 data-expect-match="1"></h1> in section :heading(9)
Pass <h1 data-expect-match="1"></h1> in section :heading(0)
Pass <h1 data-expect-match="1"></h1> in section :heading(-1)
Pass <h1 data-expect-match="1"></h1> in section :heading(0, 1, 2)
Pass <h1 data-expect-match="1"></h1> in section :heading(6, 7)
Pass <h1 data-expect-match="1"></h1> in section :heading(n)
Pass <h1 data-expect-match="1"></h1> in section :heading(2n)
Pass <h1 data-expect-match="1"></h1> in section :heading(2n+1)
Pass <h1 data-expect-match="1"></h1> in section :heading(2n+2)
Pass <h1 data-expect-match="1"></h1> in section :heading(-n+3)
Pass <h1 data-expect-match="1"></h1> in section :heading(2n, 3n)
Pass <h1 data-expect-match="1"></h1> in section :heading(even)
Pass <h1 data-expect-match="1"></h1> in section :heading(odd)
Pass <hgroup data-expect-match=""></hgroup> :heading
Pass <hgroup data-expect-match=""></hgroup> :heading(1)
Pass <hgroup data-expect-match=""></hgroup> :heading(2)
@ -278,17 +170,8 @@ Pass <hgroup data-expect-match=""></hgroup> :heading(7)
Pass <hgroup data-expect-match=""></hgroup> :heading(8)
Pass <hgroup data-expect-match=""></hgroup> :heading(9)
Pass <hgroup data-expect-match=""></hgroup> :heading(0)
Pass <hgroup data-expect-match=""></hgroup> :heading(-1)
Pass <hgroup data-expect-match=""></hgroup> :heading(0, 1, 2)
Pass <hgroup data-expect-match=""></hgroup> :heading(6, 7)
Pass <hgroup data-expect-match=""></hgroup> :heading(n)
Pass <hgroup data-expect-match=""></hgroup> :heading(2n)
Pass <hgroup data-expect-match=""></hgroup> :heading(2n+1)
Pass <hgroup data-expect-match=""></hgroup> :heading(2n+2)
Pass <hgroup data-expect-match=""></hgroup> :heading(-n+3)
Pass <hgroup data-expect-match=""></hgroup> :heading(2n, 3n)
Pass <hgroup data-expect-match=""></hgroup> :heading(even)
Pass <hgroup data-expect-match=""></hgroup> :heading(odd)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(1)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(2)
@ -300,14 +183,5 @@ Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(7)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(8)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(9)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(0)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(-1)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(0, 1, 2)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(6, 7)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(n)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(2n)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(2n+1)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(2n+2)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(-n+3)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(2n, 3n)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(even)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(odd)
Pass <p role="heading" aria-level="1" data-expect-match=""></p> :heading(6, 7)

View file

@ -2,8 +2,7 @@ Harness status: OK
Found 28 tests
17 Pass
11 Fail
28 Pass
Pass ":heading" should be a valid selector
Pass ":heading(2)" should be a valid selector
Pass ":heading(99999)" should be a valid selector
@ -17,17 +16,17 @@ Pass "h1:heading(2)" should be a valid selector
Pass ":heading()" should be an invalid selector
Pass ":heading(1.0)" should be an invalid selector
Pass ":heading(1.4)" should be an invalid selector
Fail ":heading(n)" should be an invalid selector
Fail ":heading(odd)" should be an invalid selector
Fail ":heading(even)" should be an invalid selector
Fail ":heading(2n)" should be an invalid selector
Fail ":heading(2n+1)" should be an invalid selector
Fail ":heading(2n+2)" should be an invalid selector
Fail ":heading(-n+3)" should be an invalid selector
Fail ":heading(2n, 3n)" should be an invalid selector
Fail ":heading(2, 3n)" should be an invalid selector
Fail ":heading(2 of .foo)" should be an invalid selector
Fail ":heading(2n of .foo)" should be an invalid selector
Pass ":heading(n)" should be an invalid selector
Pass ":heading(odd)" should be an invalid selector
Pass ":heading(even)" should be an invalid selector
Pass ":heading(2n)" should be an invalid selector
Pass ":heading(2n+1)" should be an invalid selector
Pass ":heading(2n+2)" should be an invalid selector
Pass ":heading(-n+3)" should be an invalid selector
Pass ":heading(2n, 3n)" should be an invalid selector
Pass ":heading(2, 3n)" should be an invalid selector
Pass ":heading(2 of .foo)" should be an invalid selector
Pass ":heading(2n of .foo)" should be an invalid selector
Pass ":heading(calc(1))" should be an invalid selector
Pass ":heading(max(1, 2))" should be an invalid selector
Pass ":heading(min(1, 2)" should be an invalid selector

View file

@ -33,17 +33,8 @@ const tests = [
{args: ['8'], match: []},
{args: ['9'], match: []},
{args: ['0'], match: []},
{args: ['-1'], match: []},
{args: ['0', '1', '2'], match: [1, 2]},
{args: ['6', '7'], match: [6]},
{args: ['n'], match: [1, 2, 3, 4, 5, 6]},
{args: ['2n'], match: [2, 4, 6]},
{args: ['2n+1'], match: [1, 3, 5]},
{args: ['2n+2'], match: [2, 4, 6]},
{args: ['-n+3'], match: [1, 2, 3]},
{args: ['2n', '3n'], match: [2, 3, 4, 6]},
{args: ['even'], match: [2, 4, 6]},
{args: ['odd'], match: [1, 3, 5]},
];
for (const el of els) {
const testName = el.outerHTML + (el.parentNode === document.body ? '' : ' in ' + el.parentNode.localName);