Author: Mason Wright
Email:
[email protected]
Date: Sun, 6 Jul 2025 20:33:11 -0600
tests/css_selector.cc
b966b2a517365074e5c381dbdea05b3221dc0198
Testing hook
Clone
-
.gitignore
-
Makefile
-
config
-
include/catch_amalgamated.cpp
-
include/catch_amalgamated.hpp
-
include/grim.h
-
include/parser.h
-
index.html
-
main.cc
A
notes
-
src/adapter.cc
-
src/events.cc
M
src/grim.cc
-
src/parser.cc
-
style.css
-
tests/css_selector.cc
-
tests/html_node.cc
-
tests/html_parser.cc
Commits
fc556c26a73a47fe5a718a1cad4eead8c1873174
6dfb96c72f966734322c80cd77576867f462dc77
3482d6efe8f342715f9a6e190ee7e8b2d59ddf7f
0bff1901284184c86da44aa48971c856d6d4fae3
b966b2a517365074e5c381dbdea05b3221dc0198
e840f1eeb0ae26af69e1ae146ea9938e28e9f1af
e4e05418a640eaed08cd1ec7cd8644eb1dbcca50
4e01ba8ad2c3361fa4be3d896288020948b58b5e
aae562ac1350480e4889aabb35899f776c5b59e9
6c3ae0e31eb0893f20e3872117f92cc6b9a942af
350e7d88bb2feb9db00c6e032cc6623f215b7adf
95e6c70d23e99ffcf70e5bbe12503496e5d8f232
e188783659b9bc3b9993a647e93ed110e7f41db6
5e4c38ff3c212cdd9881427ef3f8c2706539a190
e50ea9e1356a74af18fdd171337ef9dc931e1f4e
8f2e83556d12aaebe8e8597ea6923804b0eb7a43
1627c585128af263181053ab2cf1a4cdcd14ee21
def3513f75b325464ad88a33c741c4ca80572b77
a21501590980a905fa9b902897d700a42a08b7f0
56074a6bfe4498d092f3a227297c8c20e2bb962c
d9cf1485b7ae0614130494f0e73237921323b9a1
80f04b134ae32ad8a9d526007b33dd02f6600f05
23d6c65f9368d3c622a55a3068a6b2f1efa0c8d4
09c195df02536b6a796bd648fce9669397b96109
f2b5c8202fbc904e2ed78260e3fdbd55164799d2
4bfba076120f389994fc46a98e8b7a2622314400
e36ac5417e10ee9b9f94f340e1ccf28afc5705ea
d00dc89a86dd7e2fcfd4618bc3a1c8cfba9e3c3d
d9eef16adaf292f3748db5fb5aa98463de10d712
18ff2ec1bfc1cf9fcd17c1acb05c3b41f8f0ed83
9e7fd2980d723437ea621b78d395fa72ca3f4922
Diff
#include
#include "../include/grim.h" #include "../include/parser.h" #include
#include
#include
void checkSelectorParts(std::string selector, std::vector
> expected) { INFO("========================="); INFO("Selector: "+selector+"\n"); std::vector
> css = parseSelectorParts(selector); REQUIRE(css.size() == expected.size()); std::string note = ""; for (size_t a = 0; a < css.size(); a++) { std::string str = "{"; for (size_t b = 0; b < css[a].size(); b++) { str += "\""+css[a][b]+"\","; } str += "},"; note += str; } INFO("PARSED: "+note); for (size_t a = 0; a < css.size(); a++) { REQUIRE(css[a].size() == expected[a].size()); for (size_t b = 0; b < css[a].size(); b++) { REQUIRE(css[a][b] == expected[a][b]); } } } TEST_CASE( "CSS Selection", "[css]" ) { SECTION("parseSelectorParts can parse a simple CSS selector") { // This function only parses the last part of the selector checkSelectorParts("div > h1", {{"div", ">", "h1"}}); BENCHMARK("div > h1") { return parseSelectorParts("div > h1"); }; } SECTION("parseSelectorParts can parse a simple CSS selector with a psuedo class selector") { checkSelectorParts("div > h1:checked", {{"div", ">", "h1","[checked]"}}); BENCHMARK("div > h1:checked") { return parseSelectorParts("div > h1:checked"); }; } SECTION("parseSelectorParts can parse a attribute selector") { checkSelectorParts("a[href=\"https://www.example.com/\"]", {{"a", "[href=\"https://www.example.com/\"]"}}); BENCHMARK("a[href=\"https://www.example.com/\"]") { return parseSelectorParts("a[href=\"https://www.example.com/\"]"); }; } SECTION("parseSelectorParts can parse has()") { checkSelectorParts("h1:has(+ h2)", {{"h1",":has(+ h2)"}}); BENCHMARK("h1:has(+ h2)") { return parseSelectorParts("h1:has(+ h2)"); }; } SECTION("parseSelectorParts can parse a complex where() selector") { checkSelectorParts(":where(ol, ul, menu:unsupported)", {{":where(ol, ul, menu:unsupported)"}}); BENCHMARK(":where(ol, ul, menu:unsupported)") { return parseSelectorParts(":where(ol, ul, menu:unsupported)"); }; } SECTION("parseSelectorParts can parse multiple simple selectors"){ checkSelectorParts("input:required, div#id + h1.c1.c2, input[type='password'].class", {{"input", "[required]"},{"div", "#id", "+", "h1", ".c1", ".c2"},{"input", "[type=\"password\"]", ".class"}}); BENCHMARK("input:required, div#id + h1.c1.c2, input[type='password'].class") { return parseSelectorParts("input:required, div#id + h1.c1.c2, input[type='password'].class"); }; } SECTION("parseSelectorParts can parse a selector with a decendent combinator") { checkSelectorParts("div h1", {{"div", " ", "h1"}}); BENCHMARK("div h1") { return parseSelectorParts("div h1"); }; } SECTION("parseSelectorParts can parse a selector with a decendent combinator") { checkSelectorParts("div:has(h1):checked", {{"div", ":has(h1)", "[checked]"}}); BENCHMARK("div h1") { return parseSelectorParts("div h1"); }; } SECTION("parseSelectorParts can parse a selector with only a id") { checkSelectorParts("#id", {{"#id"}}); BENCHMARK("#id") { return parseSelectorParts("#id"); }; } SECTION("parseSelectorParts can parse a selector with multiple selectors") { checkSelectorParts("h1, h1 ~p, :last-child", {{"h1"},{"h1","~","p"}, {"[last-child]"}}); BENCHMARK("h1, h1 ~p, :last-child") { return parseSelectorParts("h1, h1 ~p, :last-child"); }; } SECTION("parseSelectorParts can parse a relative selector without another reference") { checkSelectorParts("+ p", {{"+", "p"}}); } } TEST_CASE("testSelector", "[CSS]") { std::string html = "" "" "" "
This is a simple test
" "
" "" "" "
Header
" "
This is some text for a simple test
" "
" "
" "" ""; std::stringstream ss(html); std::unique_ptr
document = parseStream(ss); SECTION("testSelector can match a element given it's tagName") { auto html = document->children[0].get(); REQUIRE(testSelector(html, parseSelectorParts("html"))); REQUIRE_FALSE(testSelector(html, parseSelectorParts("h1"))); } SECTION("testSelector can match a element by it's attributes only") { auto link = document->children[0]->children[0]->children[1].get(); REQUIRE(testSelector(link, parseSelectorParts("[rel=\"stylesheet\"]"))); } SECTION("testSelector can match a element by it's attributes and tagName") { auto link = document->children[0]->children[0]->children[1].get(); REQUIRE(testSelector(link, parseSelectorParts("link[rel=\"stylesheet\"]"))); REQUIRE_FALSE(testSelector(link, parseSelectorParts("title[rel=\"stylesheet\"]"))); } SECTION("testSelector can match a element by it's id only") { auto h1 = document->children[0]->children[1]->children[0].get(); REQUIRE(testSelector(h1, parseSelectorParts("#h1Tag"))); } SECTION("testSelector can match a element by it's id and tagName") { auto h1 = document->children[0]->children[1]->children[0].get(); REQUIRE(testSelector(h1, parseSelectorParts("h1#h1Tag"))); } SECTION("testSelector can match a element by it's class and not matach on a non class") { auto p = document->children[0]->children[1]->children[1].get(); REQUIRE(testSelector(p, parseSelectorParts(".class1"))); REQUIRE_FALSE(testSelector(p, parseSelectorParts(".class3"))); } SECTION("testSelector can match a child combinator") { auto h1 = document->children[0]->children[1]->children[0].get(); REQUIRE(testSelector(h1, parseSelectorParts("body > h1"))); REQUIRE_FALSE(testSelector(h1, parseSelectorParts("html > h1"))); } SECTION("testSelector can match a next sibling combinator") { auto p = document->children[0]->children[1]->children[1].get(); REQUIRE(testSelector(p, parseSelectorParts("h1 + p"))); REQUIRE_FALSE(testSelector(p, parseSelectorParts("h1 + div"))); } SECTION("testSelector can match a descendant combinator") { auto p = document->children[0]->children[1]->children[1].get(); REQUIRE(testSelector(p, parseSelectorParts("body p"))); REQUIRE_FALSE(testSelector(p, parseSelectorParts("h1 p"))); } SECTION("testSelector can match a subsequent-sibling combinator") { auto p = document->children[0]->children[1]->children[1].get(); REQUIRE(testSelector(p, parseSelectorParts("h1 ~ p"))); REQUIRE_FALSE(testSelector(p, parseSelectorParts("div ~ p"))); } SECTION("testSelector can match a element with a pseudo class") { auto input = document->children[0]->children[1]->children[3].get(); REQUIRE(testSelector(input, parseSelectorParts("input:required"))); REQUIRE(testSelector(input, parseSelectorParts(":checked"))); REQUIRE_FALSE(testSelector(input, parseSelectorParts("input:enabled"))); } SECTION("testSelector can match a element with first-child") { auto h1 = document->children[0]->children[1]->children[0].get(); REQUIRE(testSelector(h1, parseSelectorParts(":first-child"))); } SECTION("testSelector can match a element with last-child") { auto input = document->children[0]->children[1]->children[3].get(); REQUIRE(testSelector(input, parseSelectorParts(":last-child"))); } SECTION("testSelector can match multiple selectors") { auto input = document->children[0]->children[1]->children[3].get(); REQUIRE(testSelector(input, parseSelectorParts("h1, h1 ~p, :last-child"))); REQUIRE_FALSE(testSelector(input, parseSelectorParts("h1, h1 ~p, :first-child"))); BENCHMARK("Multiselector and parse") { return testSelector(input, parseSelectorParts("h1, h1 ~p, :last-child")); }; auto parts = parseSelectorParts("h1, h1 ~p, :last-child"); BENCHMARK("Multiselector pre-parse") { return testSelector(input, parts); }; } SECTION("testSelector can match :is() selectors") { auto input = document->children[0]->children[1]->children[3].get(); REQUIRE(testSelector(input, parseSelectorParts(":is(h1, input, div)"))); REQUIRE_FALSE(testSelector(input, parseSelectorParts(":is(h1, html, div)"))); } SECTION("testSelector can match :where() selectors") { auto input = document->children[0]->children[1]->children[3].get(); REQUIRE(testSelector(input, parseSelectorParts(":where(h1, input, div)"))); REQUIRE_FALSE(testSelector(input, parseSelectorParts(":where(h1, html, div)"))); } SECTION("testSelector can match :not() selectors") { auto input = document->children[0]->children[1]->children[3].get(); REQUIRE(testSelector(input, parseSelectorParts(":not(h1, html, div)"))); REQUIRE_FALSE(testSelector(input, parseSelectorParts(":not(h1, input, div)"))); } SECTION("testSelector can match nested :not(:is()) selectors") { auto input = document->children[0]->children[1]->children[3].get(); REQUIRE(testSelector(input, parseSelectorParts(":not(:is(h1, html, div))"))); REQUIRE_FALSE(testSelector(input, parseSelectorParts(":not(:is(h1, input, div))"))); BENCHMARK("is not test") { return testSelector(input, parseSelectorParts(":not(:is(h1, html, div))")); }; } SECTION("testSelector can match a :has() selector") { auto h1 = document->children[0]->children[1]->children[0].get(); REQUIRE(testSelector(h1, parseSelectorParts("h1:has(+ p)"))); REQUIRE_FALSE(testSelector(h1, parseSelectorParts("h1:has(+ p:required)"))); } // make for: // pseudo elements // * // pseudo selectors(:has, :where etc) // nth-child // nth-* }