Skip to content

Commit 1881bba

Browse files
committed
test: Adopt CSS Selector parsing tests from WPT
1 parent 8a29466 commit 1881bba

1 file changed

Lines changed: 184 additions & 0 deletions

File tree

src/wpt.spec.ts

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/**
2+
* @fileoverview CSS Selector parsing tests from WPT
3+
* @see https://github.com/web-platform-tests/wpt/tree/0bb883967c888261a8372923fd61eb5ad14305b2/css/selectors/parsing
4+
* @license BSD-3-Clause (https://github.com/web-platform-tests/wpt/blob/master/LICENSE.md)
5+
*/
6+
7+
import { parse, stringify } from ".";
8+
9+
function test_valid_selector(
10+
selector: string,
11+
serialized: string | string[] = selector
12+
) {
13+
const result = stringify(parse(selector));
14+
if (Array.isArray(serialized)) {
15+
// Should be a part of the array
16+
expect(serialized).toContain(result);
17+
} else {
18+
expect(result).toStrictEqual(serialized);
19+
}
20+
}
21+
22+
function test_invalid_selector(selector: string) {
23+
expect(() => parse(selector)).toThrow(Error);
24+
}
25+
26+
describe("Web Platform Tests", () => {
27+
it("Attribute selectors", () => {
28+
// Attribute presence and value selectors
29+
test_valid_selector("[att]");
30+
test_valid_selector("[att=val]", '[att="val"]');
31+
test_valid_selector("[att~=val]", '[att~="val"]');
32+
test_valid_selector("[att|=val]", '[att|="val"]');
33+
test_valid_selector("h1[title]");
34+
test_valid_selector("span[class='example']", 'span[class="example"]');
35+
test_valid_selector("a[hreflang=fr]", 'a[hreflang="fr"]');
36+
test_valid_selector("a[hreflang|='en']", 'a[hreflang|="en"]');
37+
38+
// Substring matching attribute selectors
39+
test_valid_selector("[att^=val]", '[att^="val"]');
40+
test_valid_selector("[att$=val]", '[att$="val"]');
41+
test_valid_selector("[att*=val]", '[att*="val"]');
42+
test_valid_selector('object[type^="image/"]');
43+
test_valid_selector('a[href$=".html"]');
44+
test_valid_selector('p[title*="hello"]');
45+
46+
// From Attribute selectors and namespaces examples in spec:
47+
test_valid_selector("[*|att]");
48+
test_valid_selector("[|att]", "[att]");
49+
});
50+
51+
it("Child combinators", () => {
52+
test_valid_selector("body > p");
53+
test_valid_selector("div ol>li p", "div ol > li p");
54+
});
55+
56+
it("Class selectors", () => {
57+
test_valid_selector("*.pastoral", ["*.pastoral", ".pastoral"]);
58+
test_valid_selector(".pastoral", ["*.pastoral", ".pastoral"]);
59+
test_valid_selector("h1.pastoral");
60+
test_valid_selector("p.pastoral.marine");
61+
});
62+
63+
it("Descendant combinator", () => {
64+
test_valid_selector("h1 em");
65+
test_valid_selector("div * p");
66+
test_valid_selector("div p *[href]", ["div p *[href]", "div p [href]"]);
67+
});
68+
69+
it(":focus-visible pseudo-class", () => {
70+
test_valid_selector(":focus-visible");
71+
test_valid_selector("a:focus-visible");
72+
test_valid_selector(":focus:not(:focus-visible)");
73+
});
74+
75+
it("The relational pseudo-class", () => {
76+
test_valid_selector(":has(a)");
77+
test_valid_selector(":has(#a)");
78+
test_valid_selector(":has(.a)");
79+
test_valid_selector(":has([a])");
80+
test_valid_selector(':has([a="b"])');
81+
test_valid_selector(':has([a|="b"])');
82+
test_valid_selector(":has(:hover)");
83+
test_valid_selector("*:has(.a)", ["*:has(.a)", ":has(.a)"]);
84+
test_valid_selector(".a:has(.b)");
85+
test_valid_selector(".a:has(> .b)");
86+
test_valid_selector(".a:has(~ .b)");
87+
test_valid_selector(".a:has(+ .b)");
88+
test_valid_selector(".a:has(.b) .c");
89+
test_valid_selector(".a .b:has(.c)");
90+
test_valid_selector(".a .b:has(.c .d)");
91+
test_valid_selector(".a .b:has(.c .d) .e");
92+
test_valid_selector(".a:has(.b:has(.c))");
93+
test_valid_selector(".a:has(.b:is(.c .d))");
94+
test_valid_selector(".a:has(.b:is(.c:has(.d) .e))");
95+
test_valid_selector(".a:is(.b:has(.c) .d)");
96+
test_valid_selector(".a:not(:has(.b))");
97+
test_valid_selector(".a:has(:not(.b))");
98+
test_valid_selector(".a:has(.b):has(.c)");
99+
test_valid_selector("*|*:has(*)", ":has(*)");
100+
test_valid_selector(":has(*|*)");
101+
test_invalid_selector(".a:has()");
102+
});
103+
104+
it("ID selectors", () => {
105+
test_valid_selector("h1#chapter1");
106+
test_valid_selector("#chapter1");
107+
test_valid_selector("*#z98y", ["*#z98y", "#z98y"]);
108+
});
109+
110+
it("The Matches-Any Pseudo-class: ':is()'", () => {
111+
test_valid_selector(
112+
":is(ul,ol,.list) > [hidden]",
113+
":is(ul, ol, .list) > [hidden]"
114+
);
115+
test_valid_selector(":is(:hover,:focus)", ":is(:hover, :focus)");
116+
test_valid_selector("a:is(:not(:hover))");
117+
118+
test_valid_selector(":is(#a)");
119+
test_valid_selector(".a.b ~ :is(.c.d ~ .e.f)");
120+
test_valid_selector(".a.b ~ .c.d:is(span.e + .f, .g.h > .i.j .k)");
121+
});
122+
123+
it("The negation pseudo-class", () => {
124+
test_valid_selector("button:not([disabled])");
125+
test_valid_selector("*:not(foo)", ["*:not(foo)", ":not(foo)"]);
126+
test_valid_selector(":not(:link):not(:visited)");
127+
test_valid_selector("*|*:not(*)", ":not(*)");
128+
test_valid_selector(":not(:hover)");
129+
test_valid_selector(":not(*|*)");
130+
test_valid_selector("foo:not(bar)");
131+
test_valid_selector(":not(:not(foo))");
132+
test_valid_selector(":not(.a .b)");
133+
test_valid_selector(":not(.a + .b)");
134+
test_valid_selector(":not(.a .b ~ c)");
135+
test_valid_selector(":not(span.a, div.b)");
136+
test_valid_selector(":not(.a .b ~ c, .d .e)");
137+
test_valid_selector(":not(:host)");
138+
test_valid_selector(":not(:host(.a))");
139+
test_valid_selector(":host(:not(.a))");
140+
test_valid_selector(":not(:host(:not(.a)))");
141+
test_valid_selector(
142+
":not([disabled][selected])",
143+
":not([disabled][selected])"
144+
);
145+
test_valid_selector(
146+
":not([disabled],[selected])",
147+
":not([disabled], [selected])"
148+
);
149+
150+
test_invalid_selector(":not()");
151+
test_invalid_selector(":not(:not())");
152+
});
153+
154+
it("Sibling combinators", () => {
155+
test_valid_selector("math + p");
156+
test_valid_selector("h1.opener + h2");
157+
test_valid_selector("h1 ~ pre");
158+
});
159+
160+
it("Universal selector", () => {
161+
test_valid_selector("*");
162+
test_valid_selector("div :first-child", [
163+
"div *:first-child",
164+
"div :first-child",
165+
]);
166+
test_valid_selector("div *:first-child", [
167+
"div *:first-child",
168+
"div :first-child",
169+
]);
170+
});
171+
172+
it("The Specificity-adjustment Pseudo-class: ':where()'", () => {
173+
test_valid_selector(
174+
":where(ul,ol,.list) > [hidden]",
175+
":where(ul, ol, .list) > [hidden]"
176+
);
177+
test_valid_selector(":where(:hover,:focus)", ":where(:hover, :focus)");
178+
test_valid_selector("a:where(:not(:hover))");
179+
180+
test_valid_selector(":where(#a)");
181+
test_valid_selector(".a.b ~ :where(.c.d ~ .e.f)");
182+
test_valid_selector(".a.b ~ .c.d:where(span.e + .f, .g.h > .i.j .k)");
183+
});
184+
});

0 commit comments

Comments
 (0)