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 7import { parse, stringify } from "."; 8 9function 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 22function test_invalid_selector(selector: string) { 23 expect(() => parse(selector)).toThrow(Error); 24} 25 26describe("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}); 185