From 00af1093a7930beb382274142e5fd305a3dc4fe0 Mon Sep 17 00:00:00 2001 From: Dave Roberts Date: Sat, 13 Jul 2024 22:26:52 -0500 Subject: [PATCH 1/8] dev: Implement shadow DOM functions (#604) --- doc/01-user-guide.adoc | 116 +++++++++++++++++++++++++++ env/test/resources/static/test.html | 9 +++ src/etaoin/api.clj | 117 ++++++++++++++++++++++++++++ test/etaoin/api_test.clj | 39 ++++++++++ 4 files changed, 281 insertions(+) diff --git a/doc/01-user-guide.adoc b/doc/01-user-guide.adoc index 4dc35970..84df10af 100644 --- a/doc/01-user-guide.adoc +++ b/doc/01-user-guide.adoc @@ -845,6 +845,122 @@ The following query will find a vector of `div` tags, then return a set of all ` ;; => ("a1" "a2" "a3" "a4" "a5" "a6" "a7" "a8" "a9") ---- +=== Querying the Shadow DOM + +The shadow DOM provides a way to attach another DOM tree to a specified element in the normal DOM and have the internals of that tree hidden from JavaScript and CSS on the same page. +When the browser renders the DOM, the elements from the shadow DOM appear at the location where the tree is rooted in the normal DOM. +This provides a level of encapsulation, allowing "components" in the shadow DOM to be styled differently than the rest of the page and preventing conflicts between the normal page CSS and the component CSS. +The shadow DOM is also hidden from normal Web Driver queries (`query`) and thus requires a separate set of API calls to query it. For more details about the shadow DOM, see this article at https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM#shadow_dom_and_custom_elements[Mozilla Developer Network (MDN)]. + +There are a few terms that are important to understand when dealing with the Shadow DOM. +The "shadow root host" is the element in the standard DOM to which a shadow root is attached as a property. +The "shadow root" is the top of the shadow DOM tree rooted at the shadow root host. + +The following examples use this HTML fragment that has a bit of shadow DOM in it. + +[source,html] +---- +I'm not in the shadow DOM +
+ +
+---- + +Everthing in the `template` element is part of the shadow DOM. +The `div` with the `id` of `shadow-root-host` is, as the ID suggests, the shadow root host element. + +Given this HTML, you can run a standard `query` to find the shadow root host and then use `get-element-property-el` to return to the `"shadowRoot"` property. + +[source,clojure] +---- +(e/query driver {:id "shadow-root-host"}) +;; => "78344155-7a53-46fb-a46e-e864210e501d" + +(e/get-element-property-el driver (e/query driver {:id "shadow-root-host"}) "shadowRoot") +;; => {:shadow-6066-11e4-a52e-4f735466cecf +;; => "ac5ab914-7f93-427f-a0bf-f7e91098fd37"} + +(e/get-element-property driver {:id "shadow-root-host"} "shadowRoot") +;; => {:shadow-6066-11e4-a52e-4f735466cecf +;; => "ac5ab914-7f93-427f-a0bf-f7e91098fd37"} +---- + +If you go this route, you're going to have to pick apart the return +value. +The element-id of the shadow root is the string value of the first map key. + +You can get the shadow root element ID more directly using Etaoin's `get-element-shadow-root` API. +The query parameter looks for an element in the standard DOM and returns its shadow root property. + +[source,clojure] +---- +(e/get-element-shadow-root driver {:id "shadow-root-host"}) +;; => "ac5ab914-7f93-427f-a0bf-f7e91098fd37" +---- + +If you already have the shadow root host element, you can return its corresponding shadow root element ID using `get-element-shadow-root-el`. + +[source,clojure] +---- +(def host (e/query driver {:id "shadow-root-host"})) +;; => #'user/host +(e/get-element-shadow-root-el driver host) +;; => "ac5ab914-7f93-427f-a0bf-f7e91098fd37" +---- + +You can test whether an element is a shadow root host using `has-shadow-root?` and `has-shadow-root-el?`. + +[source,clojure] +---- +(e/has-shadow-root? driver {:id "shadow-root-host"}) +;; => true +(e/has-shadow-root-el? driver host) +;; => true +(e/has-shadow-root? driver {:id "not-in-shadow"}) +;; => false +---- + +Now that you know how to retrieve the shadow root, you can query elements in the shadow DOM using `query-shadow-root`, `query-all-shadow-root`, `query-shadow-root-el`, and `query-all-shadow-root-el`. + +For `query-shadow-root` and `query-all-shadow-root`, the `q` parameter specifies a query of the _normal_ DOM to find the shadow root host. +If the host is identified, the `shadow-q` parameter is a query that is executed within the shadow DOM rooted at the shadow root host. + +The `query-shadow-root-el` and `query-all-shadow-root-el` allow you to specify the shadow root host element directly, rather than querying for it. + +[source,clojure] +---- +(e/query-shadow-root driver {:id "shadow-root-host"} {:css "#in-shadow"}) +;; => "30fca382-6d4a-4f8a-9534-db76a1ed7cba" +(e/get-element-text-el driver "30fca382-6d4a-4f8a-9534-db76a1ed7cba") +;; => "I'm in the shadow DOM" + +(->> (e/query-all-shadow-root driver {:id "shadow-root-host"} {:css "span"}) + (map #(e/get-element-text-el driver %))) +;; => ("I'm in the shadow DOM" "I'm also in the shadow DOM") + +(def root (e/get-element-shadow-root-el driver host)) +;; => #'user/root +(e/get-element-text-el driver (e/query-shadow-root-el driver root {:css "#in-shadow"})) +;; => "I'm in the shadow DOM" + +(->> (e/query-all-shadow-root-el driver root {:css "span"}) + (map #(e/get-element-text-el driver %))) +;; => ("I'm in the shadow DOM" "I'm also in the shadow DOM") +---- + +[NOTE] +==== +In the previous shadow root queries, you should note that we used CSS selectors for the `shadow-q` argument in each case. +This was done because current browsers do not support XPath, which is what the Etaoin map syntax is typically translated into under the hood. +While it is expected that browsers will support XPath queries of the shadow DOM in the future, it is unclear when this support might appear. +For now, use CSS. + +For more information, see the https://wpt.fyi/results/webdriver/tests/classic/find_element_from_shadow_root/find.py?label=experimental&label=master&aligned[Web Platforms Test Dashobard]. +==== + === Interacting with Queried Elements To interact with elements found via a `query` or `query-all` function call you have to pass the query result to either `click-el` or `fill-el` (note the `-el` suffix): diff --git a/env/test/resources/static/test.html b/env/test/resources/static/test.html index 1e4473e5..9071cef8 100644 --- a/env/test/resources/static/test.html +++ b/env/test/resources/static/test.html @@ -238,6 +238,15 @@

Find text with quote

+

Shadow DOM

+ I'm not in the shadow DOM +
+ +
+

Document end

diff --git a/src/etaoin/api.clj b/src/etaoin/api.clj index dc9d79cc..80d5baf0 100644 --- a/src/etaoin/api.clj +++ b/src/etaoin/api.clj @@ -1611,6 +1611,112 @@ [driver q] (get-element-property driver q :value)) +(defn get-element-shadow-root-el + "Returns the shadow root for the specified element or `nil` if the + element does not have a shadow root." + [driver el] + (-> (get-element-property-el driver el "shadowRoot") + first + second)) + +(defn get-element-shadow-root + "Returns the shadow root for the first element matching the query, or + `nil` if the element does not have a shadow root." + [driver q] + (get-element-shadow-root-el driver (query driver q))) + +;;; +;;; Shadow root queries +;;; + +(defmulti ^:private find-element-from-shadow-root* dispatch-driver) + +(defmethods find-element-from-shadow-root* + [:firefox :safari] + [driver shadow-root-el locator term] + {:pre [(some? shadow-root-el)]} + (-> (execute {:driver driver + :method :post + :path [:session (:session driver) :shadow shadow-root-el :element] + :data {:using locator :value term}}) + :value + first + second)) + +(defmethod find-element-from-shadow-root* :default + [driver shadow-root-el locator term] + {:pre [(some? shadow-root-el)]} + (-> (execute {:driver driver + :method :post + :path [:session (:session driver) :shadow shadow-root-el :element] + :data {:using locator :value term}}) + :value + :ELEMENT)) + +(defmulti ^:private find-elements-from-shadow-root* dispatch-driver) + +(defmethod find-elements-from-shadow-root* :default + [driver shadow-root-el locator term] + {:pre [(some? shadow-root-el)]} + (->> (execute {:driver driver + :method :post + :path [:session (:session driver) :shadow shadow-root-el :elements] + :data {:using locator :value term}}) + :value + (mapv (comp second first)))) + +(defn query-shadow-root-el + "Queries the shadow DOM rooted at `shadow-root-el`, looking for the + first element specified by `shadow-q`. + + The `shadow-q` parameter is similar to the `q` parameter of + the [[query]] function, but some drivers may limit it to specific + formats (e.g., CSS). See [this page](https://wpt.fyi/results/webdriver/tests/classic/find_element_from_shadow_root/find.py?label=experimental&label=master&aligned) on the Web Platform + Tests website for more information on specific browser support." + [driver shadow-root-el shadow-q] + (let [[loc term] (query/expand driver shadow-q)] + (find-element-from-shadow-root* driver shadow-root-el loc term))) + +(defn query-all-shadow-root-el + "Queries the shadow DOM rooted at `shadow-root-el`, looking for all + elements specified by `shadow-q`. + + The `shadow-q` parameter is similar to the `q` parameter of + the [[query]] function, but some drivers may limit it to specific + formats (e.g., CSS). See [this page](https://wpt.fyi/results/webdriver/tests/classic/find_element_from_shadow_root/find.py?label=experimental&label=master&aligned) on the Web Platform + Tests website for more information on specific browser support." + [driver shadow-root-el shadow-q] + (let [[loc term] (query/expand driver shadow-q)] + (find-elements-from-shadow-root* driver shadow-root-el loc term))) + +(defn query-shadow-root + "First, conducts a standard search (as if by `query`) for an element + with a shadow root. Then, from that shadow root element, conducts a + search of the shadow DOM for the first element matching `shadow-q`. + + For details on `q`, see [[query]]. + + The `shadow-q` parameter is similar to the `q` parameter of + the [[query]] function, but some drivers may limit it to specific + formats (e.g., CSS). See [this page](https://wpt.fyi/results/webdriver/tests/classic/find_element_from_shadow_root/find.py?label=experimental&label=master&aligned) on the Web Platform + Tests website for more information on specific browser support." + [driver q shadow-q] + (query-shadow-root-el driver (get-element-shadow-root driver q) shadow-q)) + +(defn query-all-shadow-root + "First, conducts a standard search (as if by `query`) for an element + with a shadow root. Then, from that shadow root element, conducts a + search of the shadow DOM for all elements matching `shadow-q`. + + For details on `q`, see [[query]]. + + The `shadow-q` parameter is similar to the `q` parameter of + the [[query]] function, but some drivers may limit it to specific + formats (e.g., CSS). See [this page](https://wpt.fyi/results/webdriver/tests/classic/find_element_from_shadow_root/find.py?label=experimental&label=master&aligned) on the Web Platform + Tests website for more information on specific browser support." + [driver q shadow-q] + (query-all-shadow-root-el driver (get-element-shadow-root driver q) shadow-q)) + ;; ;; cookies ;; @@ -2431,6 +2537,17 @@ :arglists '([driver])} has-no-alert? (complement has-alert?)) +(defn has-shadow-root-el? + "Returns `true` if the specified element has a shadow root or `false` otherwise." + [driver el] + (boolean (get-element-shadow-root-el driver el))) + +(defn has-shadow-root? + "Returns `true` if the first element matching the query has a shadow + root or `false` otherwise." + [driver q] + (boolean (get-element-shadow-root driver q))) + ;; ;; wait functions ;; diff --git a/test/etaoin/api_test.clj b/test/etaoin/api_test.clj index abade557..802cd937 100644 --- a/test/etaoin/api_test.clj +++ b/test/etaoin/api_test.clj @@ -799,6 +799,45 @@ (e/wait 1) (is (str/ends-with? (e/get-url *driver*) "?login=1&password=2&message=3")))))) +(deftest test-shadow-dom + (testing "basic functional sanity" + ;; Validate that the test DOM is as we would expect + (is (e/has-text? *driver* {:id "not-in-shadow"} "I'm not in the shadow DOM")) + (is (not (e/has-text? *driver* {:id "in-shadow"} "I'm in the shadow DOM")))) + (testing "getting the shadow root for an element" + (is (some? (e/get-element-shadow-root *driver* {:id "shadow-root-host"}))) + (is (some? (e/get-element-shadow-root-el *driver* + (e/query *driver* {:id "shadow-root-host"}))))) + (testing "whether an element has a shadow root" + (is (e/has-shadow-root? *driver* {:id "shadow-root-host"})) + (is (e/has-shadow-root-el? *driver* (e/query *driver* {:id "shadow-root-host"})))) + (let [shadow-root (e/get-element-shadow-root *driver* {:id "shadow-root-host"})] + (testing "querying the shadow root element for a single element" + (is (= "I'm in the shadow DOM" + (->> (e/query-shadow-root-el *driver* + shadow-root + {:css "#in-shadow"}) + (e/get-element-text-el *driver*)))) + (is (= "I'm also in the shadow DOM" + (->> (e/query-shadow-root-el *driver* + shadow-root + {:css "#also-in-shadow"}) + (e/get-element-text-el *driver*))))) + (testing "querying the shadow root element for multiple elements" + (is (= ["I'm in the shadow DOM" "I'm also in the shadow DOM"] + (->> (e/query-all-shadow-root-el *driver* + shadow-root + {:css "span"}) + (mapv #(e/get-element-text-el *driver* %))))))) + (testing "querying the shadow root element" + (is (= "I'm in the shadow DOM" + (->> (e/query-shadow-root *driver* {:id "shadow-root-host"} {:css "#in-shadow"}) + (e/get-element-text-el *driver*))))) + (testing "querying the shadow root element for multiple elements" + (is (= ["I'm in the shadow DOM" "I'm also in the shadow DOM"] + (->> (e/query-all-shadow-root *driver* {:id "shadow-root-host"} {:css "span"}) + (mapv #(e/get-element-text-el *driver* %))))))) + (comment ;; start test server (def test-server (p/process {:out :inherit :err :inherit} "bb test-server --port" 9993)) From 2b950451cb4ba6a837b637635d5be5afe94fbc69 Mon Sep 17 00:00:00 2001 From: Dave Roberts Date: Sat, 13 Jul 2024 22:32:01 -0500 Subject: [PATCH 2/8] Add #604 to CHANGELOG.adoc --- CHANGELOG.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 5fe6b7f3..5985b21f 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -10,6 +10,7 @@ A release with an intentional breaking changes is marked with: * https://github.com/clj-commons/etaoin/pull/552[#552]: Add support for wide characters to input fill functions (https://github.com/tupini07[@tupini07]) * https://github.com/clj-commons/etaoin/issues/566[#566]: Recognize `:driver-log-level` for Edge +* https://github.com/clj-commons/etaoin/issues/604[#604]: Add support for shadow DOM * bump all deps to current versions * tests ** https://github.com/clj-commons/etaoin/issues/572[#572]: stop using chrome `--no-sandbox` option, it has become problematic on Windows (and we did not need it anyway) From ba93bd40bec0b6bee0c4296e45a7d70a0b96eb6e Mon Sep 17 00:00:00 2001 From: Dave Roberts Date: Sun, 14 Jul 2024 13:24:14 -0500 Subject: [PATCH 3/8] docs: skip code tests for shadow root examples --- doc/01-user-guide.adoc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/01-user-guide.adoc b/doc/01-user-guide.adoc index 84df10af..004e1edf 100644 --- a/doc/01-user-guide.adoc +++ b/doc/01-user-guide.adoc @@ -858,6 +858,7 @@ The "shadow root" is the top of the shadow DOM tree rooted at the shadow root ho The following examples use this HTML fragment that has a bit of shadow DOM in it. +//:test-doc-blocks/skip [source,html] ---- I'm not in the shadow DOM @@ -874,6 +875,8 @@ The `div` with the `id` of `shadow-root-host` is, as the ID suggests, the shadow Given this HTML, you can run a standard `query` to find the shadow root host and then use `get-element-property-el` to return to the `"shadowRoot"` property. +// This has unique element ID's that are specific to the execution run, so skip tests. +//:test-doc-blocks/skip [source,clojure] ---- (e/query driver {:id "shadow-root-host"}) @@ -895,6 +898,8 @@ The element-id of the shadow root is the string value of the first map key. You can get the shadow root element ID more directly using Etaoin's `get-element-shadow-root` API. The query parameter looks for an element in the standard DOM and returns its shadow root property. +// This has unique element ID's that are specific to the execution run, so skip tests. +//:test-doc-blocks/skip [source,clojure] ---- (e/get-element-shadow-root driver {:id "shadow-root-host"}) @@ -903,6 +908,8 @@ The query parameter looks for an element in the standard DOM and returns its sha If you already have the shadow root host element, you can return its corresponding shadow root element ID using `get-element-shadow-root-el`. +// This has unique element ID's that are specific to the execution run, so skip tests. +//:test-doc-blocks/skip [source,clojure] ---- (def host (e/query driver {:id "shadow-root-host"})) @@ -913,6 +920,7 @@ If you already have the shadow root host element, you can return its correspondi You can test whether an element is a shadow root host using `has-shadow-root?` and `has-shadow-root-el?`. +//:test-doc-blocks/skip [source,clojure] ---- (e/has-shadow-root? driver {:id "shadow-root-host"}) @@ -930,6 +938,8 @@ If the host is identified, the `shadow-q` parameter is a query that is executed The `query-shadow-root-el` and `query-all-shadow-root-el` allow you to specify the shadow root host element directly, rather than querying for it. +// This has unique element ID's that are specific to the execution run, so skip tests. +//:test-doc-blocks/skip [source,clojure] ---- (e/query-shadow-root driver {:id "shadow-root-host"} {:css "#in-shadow"}) From 285de94c6e5c8d9782e75e9d823d0a2639597105 Mon Sep 17 00:00:00 2001 From: Dave Roberts Date: Mon, 15 Jul 2024 12:37:55 -0500 Subject: [PATCH 4/8] Link from shadow root doc strings to note in User Guide --- doc/01-user-guide.adoc | 2 ++ src/etaoin/api.clj | 12 ++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/doc/01-user-guide.adoc b/doc/01-user-guide.adoc index 004e1edf..8ece80f9 100644 --- a/doc/01-user-guide.adoc +++ b/doc/01-user-guide.adoc @@ -845,6 +845,7 @@ The following query will find a vector of `div` tags, then return a set of all ` ;; => ("a1" "a2" "a3" "a4" "a5" "a6" "a7" "a8" "a9") ---- +[#shadow-dom] === Querying the Shadow DOM The shadow DOM provides a way to attach another DOM tree to a specified element in the normal DOM and have the internals of that tree hidden from JavaScript and CSS on the same page. @@ -961,6 +962,7 @@ The `query-shadow-root-el` and `query-all-shadow-root-el` allow you to specify t ;; => ("I'm in the shadow DOM" "I'm also in the shadow DOM") ---- +[#shadow-root-browser-limitations] [NOTE] ==== In the previous shadow root queries, you should note that we used CSS selectors for the `shadow-q` argument in each case. diff --git a/src/etaoin/api.clj b/src/etaoin/api.clj index 80d5baf0..a1bdba3e 100644 --- a/src/etaoin/api.clj +++ b/src/etaoin/api.clj @@ -1671,8 +1671,7 @@ The `shadow-q` parameter is similar to the `q` parameter of the [[query]] function, but some drivers may limit it to specific - formats (e.g., CSS). See [this page](https://wpt.fyi/results/webdriver/tests/classic/find_element_from_shadow_root/find.py?label=experimental&label=master&aligned) on the Web Platform - Tests website for more information on specific browser support." + formats (e.g., CSS). See [this note](/doc/01-user-guide.adoc#shadow-root-browser-limitations] for more information." [driver shadow-root-el shadow-q] (let [[loc term] (query/expand driver shadow-q)] (find-element-from-shadow-root* driver shadow-root-el loc term))) @@ -1683,8 +1682,7 @@ The `shadow-q` parameter is similar to the `q` parameter of the [[query]] function, but some drivers may limit it to specific - formats (e.g., CSS). See [this page](https://wpt.fyi/results/webdriver/tests/classic/find_element_from_shadow_root/find.py?label=experimental&label=master&aligned) on the Web Platform - Tests website for more information on specific browser support." + formats (e.g., CSS). See [this note](/doc/01-user-guide.adoc#shadow-root-browser-limitations] for more information." [driver shadow-root-el shadow-q] (let [[loc term] (query/expand driver shadow-q)] (find-elements-from-shadow-root* driver shadow-root-el loc term))) @@ -1698,8 +1696,7 @@ The `shadow-q` parameter is similar to the `q` parameter of the [[query]] function, but some drivers may limit it to specific - formats (e.g., CSS). See [this page](https://wpt.fyi/results/webdriver/tests/classic/find_element_from_shadow_root/find.py?label=experimental&label=master&aligned) on the Web Platform - Tests website for more information on specific browser support." + formats (e.g., CSS). See [this note](/doc/01-user-guide.adoc#shadow-root-browser-limitations] for more information." [driver q shadow-q] (query-shadow-root-el driver (get-element-shadow-root driver q) shadow-q)) @@ -1712,8 +1709,7 @@ The `shadow-q` parameter is similar to the `q` parameter of the [[query]] function, but some drivers may limit it to specific - formats (e.g., CSS). See [this page](https://wpt.fyi/results/webdriver/tests/classic/find_element_from_shadow_root/find.py?label=experimental&label=master&aligned) on the Web Platform - Tests website for more information on specific browser support." + formats (e.g., CSS). See [this note](/doc/01-user-guide.adoc#shadow-root-browser-limitations] for more information." [driver q shadow-q] (query-all-shadow-root-el driver (get-element-shadow-root driver q) shadow-q)) From af7d317a681ca68008510db9249c639622abc41b Mon Sep 17 00:00:00 2001 From: Dave Roberts Date: Mon, 15 Jul 2024 13:43:57 -0500 Subject: [PATCH 5/8] doc: Update shadow root section for test-doc-blocks --- doc/01-user-guide.adoc | 51 ++++++++++++++++---------------------- doc/user-guide-sample.html | 9 +++++++ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/doc/01-user-guide.adoc b/doc/01-user-guide.adoc index 8ece80f9..0dedcb8d 100644 --- a/doc/01-user-guide.adoc +++ b/doc/01-user-guide.adoc @@ -857,9 +857,8 @@ There are a few terms that are important to understand when dealing with the Sha The "shadow root host" is the element in the standard DOM to which a shadow root is attached as a property. The "shadow root" is the top of the shadow DOM tree rooted at the shadow root host. -The following examples use this HTML fragment that has a bit of shadow DOM in it. +The following examples use this HTML fragment in the User Guide sample HTML that has a bit of shadow DOM in it. -//:test-doc-blocks/skip [source,html] ---- I'm not in the shadow DOM @@ -875,53 +874,49 @@ Everthing in the `template` element is part of the shadow DOM. The `div` with the `id` of `shadow-root-host` is, as the ID suggests, the shadow root host element. Given this HTML, you can run a standard `query` to find the shadow root host and then use `get-element-property-el` to return to the `"shadowRoot"` property. +Note that the element IDs returned in the following examples will be unique to the specific Etaoin driver and driver session and you will not see the same IDs. -// This has unique element ID's that are specific to the execution run, so skip tests. -//:test-doc-blocks/skip [source,clojure] ---- (e/query driver {:id "shadow-root-host"}) -;; => "78344155-7a53-46fb-a46e-e864210e501d" +;; an element ID similar to (but not the same as) +;; "78344155-7a53-46fb-a46e-e864210e501d" (e/get-element-property-el driver (e/query driver {:id "shadow-root-host"}) "shadowRoot") -;; => {:shadow-6066-11e4-a52e-4f735466cecf -;; => "ac5ab914-7f93-427f-a0bf-f7e91098fd37"} +;; something similar to +;; {:shadow-6066-11e4-a52e-4f735466cecf "ac5ab914-7f93-427f-a0bf-f7e91098fd37"} (e/get-element-property driver {:id "shadow-root-host"} "shadowRoot") -;; => {:shadow-6066-11e4-a52e-4f735466cecf -;; => "ac5ab914-7f93-427f-a0bf-f7e91098fd37"} +;; something similar to +;; {:shadow-6066-11e4-a52e-4f735466cecf "ac5ab914-7f93-427f-a0bf-f7e91098fd37"} ---- If you go this route, you're going to have to pick apart the return -value. +values. The element-id of the shadow root is the string value of the first map key. You can get the shadow root element ID more directly using Etaoin's `get-element-shadow-root` API. -The query parameter looks for an element in the standard DOM and returns its shadow root property. +The query parameter looks for a matching element in the standard DOM and returns its shadow root property. -// This has unique element ID's that are specific to the execution run, so skip tests. -//:test-doc-blocks/skip [source,clojure] ---- (e/get-element-shadow-root driver {:id "shadow-root-host"}) -;; => "ac5ab914-7f93-427f-a0bf-f7e91098fd37" +;; something similar to +;; "ac5ab914-7f93-427f-a0bf-f7e91098fd37" ---- If you already have the shadow root host element, you can return its corresponding shadow root element ID using `get-element-shadow-root-el`. -// This has unique element ID's that are specific to the execution run, so skip tests. -//:test-doc-blocks/skip [source,clojure] ---- (def host (e/query driver {:id "shadow-root-host"})) -;; => #'user/host (e/get-element-shadow-root-el driver host) -;; => "ac5ab914-7f93-427f-a0bf-f7e91098fd37" +;; something similar to +;; "ac5ab914-7f93-427f-a0bf-f7e91098fd37" ---- You can test whether an element is a shadow root host using `has-shadow-root?` and `has-shadow-root-el?`. -//:test-doc-blocks/skip [source,clojure] ---- (e/has-shadow-root? driver {:id "shadow-root-host"}) @@ -939,27 +934,23 @@ If the host is identified, the `shadow-q` parameter is a query that is executed The `query-shadow-root-el` and `query-all-shadow-root-el` allow you to specify the shadow root host element directly, rather than querying for it. -// This has unique element ID's that are specific to the execution run, so skip tests. -//:test-doc-blocks/skip [source,clojure] ---- -(e/query-shadow-root driver {:id "shadow-root-host"} {:css "#in-shadow"}) -;; => "30fca382-6d4a-4f8a-9534-db76a1ed7cba" -(e/get-element-text-el driver "30fca382-6d4a-4f8a-9534-db76a1ed7cba") +(def in-shadow (e/query-shadow-root driver {:id "shadow-root-host"} {:css "#in-shadow"})) +(e/get-element-text-el driver in-shadow) ;; => "I'm in the shadow DOM" (->> (e/query-all-shadow-root driver {:id "shadow-root-host"} {:css "span"}) (map #(e/get-element-text-el driver %))) ;; => ("I'm in the shadow DOM" "I'm also in the shadow DOM") -(def root (e/get-element-shadow-root-el driver host)) -;; => #'user/root -(e/get-element-text-el driver (e/query-shadow-root-el driver root {:css "#in-shadow"})) +(def shadow-root (e/get-element-shadow-root-el driver host)) +(e/get-element-text-el driver (e/query-shadow-root-el driver shadow-root {:css "#in-shadow"})) ;; => "I'm in the shadow DOM" - -(->> (e/query-all-shadow-root-el driver root {:css "span"}) + +(->> (e/query-all-shadow-root-el driver shadow-root {:css "span"}) (map #(e/get-element-text-el driver %))) -;; => ("I'm in the shadow DOM" "I'm also in the shadow DOM") +;; > ("I'm in the shadow DOM" "I'm also in the shadow DOM") ---- [#shadow-root-browser-limitations] diff --git a/doc/user-guide-sample.html b/doc/user-guide-sample.html index 42c88019..b5f030b5 100644 --- a/doc/user-guide-sample.html +++ b/doc/user-guide-sample.html @@ -140,6 +140,15 @@

Query Tree Example

+

Shadow DOM Example

+ I'm not in the shadow DOM +
+ +
+

Frames

In main page paragraph

From 8ce772cc08aaceed7c8597d7c7c4c3df8645127f Mon Sep 17 00:00:00 2001 From: Dave Roberts Date: Tue, 16 Jul 2024 07:40:16 -0500 Subject: [PATCH 6/8] Fix linking between doc strings and User Guide --- src/etaoin/api.clj | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/etaoin/api.clj b/src/etaoin/api.clj index a1bdba3e..29705353 100644 --- a/src/etaoin/api.clj +++ b/src/etaoin/api.clj @@ -1621,7 +1621,9 @@ (defn get-element-shadow-root "Returns the shadow root for the first element matching the query, or - `nil` if the element does not have a shadow root." + `nil` if the element does not have a shadow root. + + See [[query]] for more details on `q`." [driver q] (get-element-shadow-root-el driver (query driver q))) @@ -1671,7 +1673,7 @@ The `shadow-q` parameter is similar to the `q` parameter of the [[query]] function, but some drivers may limit it to specific - formats (e.g., CSS). See [this note](/doc/01-user-guide.adoc#shadow-root-browser-limitations] for more information." + formats (e.g., CSS). See [this note](/doc/01-user-guide.adoc#shadow-root-browser-limitations) for more information." [driver shadow-root-el shadow-q] (let [[loc term] (query/expand driver shadow-q)] (find-element-from-shadow-root* driver shadow-root-el loc term))) @@ -1682,13 +1684,13 @@ The `shadow-q` parameter is similar to the `q` parameter of the [[query]] function, but some drivers may limit it to specific - formats (e.g., CSS). See [this note](/doc/01-user-guide.adoc#shadow-root-browser-limitations] for more information." + formats (e.g., CSS). See [this note](/doc/01-user-guide.adoc#shadow-root-browser-limitations) for more information." [driver shadow-root-el shadow-q] (let [[loc term] (query/expand driver shadow-q)] (find-elements-from-shadow-root* driver shadow-root-el loc term))) (defn query-shadow-root - "First, conducts a standard search (as if by `query`) for an element + "First, conducts a standard search (as if by [[query]]) for an element with a shadow root. Then, from that shadow root element, conducts a search of the shadow DOM for the first element matching `shadow-q`. @@ -1696,12 +1698,12 @@ The `shadow-q` parameter is similar to the `q` parameter of the [[query]] function, but some drivers may limit it to specific - formats (e.g., CSS). See [this note](/doc/01-user-guide.adoc#shadow-root-browser-limitations] for more information." + formats (e.g., CSS). See [this note](/doc/01-user-guide.adoc#shadow-root-browser-limitations) for more information." [driver q shadow-q] (query-shadow-root-el driver (get-element-shadow-root driver q) shadow-q)) (defn query-all-shadow-root - "First, conducts a standard search (as if by `query`) for an element + "First, conducts a standard search (as if by [[query]]) for an element with a shadow root. Then, from that shadow root element, conducts a search of the shadow DOM for all elements matching `shadow-q`. @@ -1709,7 +1711,7 @@ The `shadow-q` parameter is similar to the `q` parameter of the [[query]] function, but some drivers may limit it to specific - formats (e.g., CSS). See [this note](/doc/01-user-guide.adoc#shadow-root-browser-limitations] for more information." + formats (e.g., CSS). See [this note](/doc/01-user-guide.adoc#shadow-root-browser-limitations) for more information." [driver q shadow-q] (query-all-shadow-root-el driver (get-element-shadow-root driver q) shadow-q)) From cc7df60515b93802a493b237fcc5c820a3024915 Mon Sep 17 00:00:00 2001 From: Dave Roberts Date: Tue, 16 Jul 2024 07:41:08 -0500 Subject: [PATCH 7/8] Link shadow root fns into api namespace doc string --- src/etaoin/api.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/etaoin/api.clj b/src/etaoin/api.clj index 29705353..102bd4a2 100644 --- a/src/etaoin/api.clj +++ b/src/etaoin/api.clj @@ -25,6 +25,8 @@ **Querying/Selecting DOM Elements** - [[query]] [[query-all]] [[query-tree]] + - [[query-shadow-root]] [[query-shadow-root-el]] [[query-all-shadow-root]] [[query-all-shadow-root-el]] + - [[has-shadow-root?]] [[has-shadow-root-el?]] - [[exists?]] [[absent?]] - [[displayed?]] [[displayed-el?]] [[enabled?]] [[enabled-el?]] [[disabled?]] [[invisible?]] [[visible?]] - [[child]] [[children]] @@ -33,6 +35,7 @@ - [[get-element-property]] [[get-element-property-el]] [[get-element-properties]] - [[has-class?]] [[has-class-el?]] [[has-no-class?]] - [[get-element-css]] [[get-element-css-el]] [[get-element-csss]] + - [[get-element-shadow-root]] [[get-element-shadow-root-el]] - [[get-element-text]] [[get-element-text-el]] [[has-text?]] - [[get-element-inner-html]] [[get-element-inner-html-el]] - [[get-element-value]] [[get-element-value-el]] From 88067de4eb323c266183e8d79e0c16d5faae8dfb Mon Sep 17 00:00:00 2001 From: Dave Roberts Date: Tue, 16 Jul 2024 07:41:49 -0500 Subject: [PATCH 8/8] Fix indentation for shadow root examples in User Guide --- doc/01-user-guide.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/01-user-guide.adoc b/doc/01-user-guide.adoc index 0dedcb8d..539f356c 100644 --- a/doc/01-user-guide.adoc +++ b/doc/01-user-guide.adoc @@ -941,7 +941,7 @@ The `query-shadow-root-el` and `query-all-shadow-root-el` allow you to specify t ;; => "I'm in the shadow DOM" (->> (e/query-all-shadow-root driver {:id "shadow-root-host"} {:css "span"}) - (map #(e/get-element-text-el driver %))) + (map #(e/get-element-text-el driver %))) ;; => ("I'm in the shadow DOM" "I'm also in the shadow DOM") (def shadow-root (e/get-element-shadow-root-el driver host)) @@ -949,7 +949,7 @@ The `query-shadow-root-el` and `query-all-shadow-root-el` allow you to specify t ;; => "I'm in the shadow DOM" (->> (e/query-all-shadow-root-el driver shadow-root {:css "span"}) - (map #(e/get-element-text-el driver %))) + (map #(e/get-element-text-el driver %))) ;; > ("I'm in the shadow DOM" "I'm also in the shadow DOM") ----