diff --git a/.gitignore b/.gitignore index c0522c6..2e3704b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ pom.xml.asc .hgignore .hg/ .DS_Store +*~ gh-pages node_modules @@ -34,3 +35,4 @@ build.xml /fiddle/ /.cljdoc-preview /.vscode + diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 3533da2..730542c 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -12,12 +12,18 @@ A release with an intentional breaking changes is marked with: * https://github.com/clj-commons/etaoin/issues/566[#566]: Recognize `:driver-log-level` for Edge * https://github.com/clj-commons/etaoin/issues/563[#563]: Support `"debug"` `:driver-log-level` for Safari * https://github.com/clj-commons/etaoin/issues/517[#517]: Properly cleanup after failed webdriver launch +* https://github.com/clj-commons/etaoin/issues/604[#604]: Add support for shadow DOM +(https://github.com/dgr[@dgr]) +* https://github.com/clj-commons/etaoin/issues/603[#603]: Add :fn/index as alias for :index in map syntax +(https://github.com/dgr[@dgr]) * 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) * docs ** https://github.com/clj-commons/etaoin/issues/534[#534]: better describe `etaoin.api/select` and its alternatives ** https://github.com/clj-commons/etaoin/issues/536[#536]: user guide examples are now all os agnostic and CI tested via test-doc-blocks on all supported OSes +** https://github.com/clj-commons/etaoin/issues/602[#602]: Document all `:fn/*` query pseudo-functions in a definitive list +(https://github.com/dgr[@dgr]) == v1.0.40 diff --git a/README.adoc b/README.adoc index 47ce353..11b7ff7 100644 --- a/README.adoc +++ b/README.adoc @@ -102,6 +102,7 @@ Can be `alpha`, `beta`, `rc1`, etc. * https://github.com/verma[Uday Verma] * https://github.com/mjmeintjes[Matt Meintjes] * https://github.com/tupini07[Andrea Tupini] +* https://github.com/dgr[Dave Roberts] === Current Maintainers diff --git a/doc/01-user-guide.adoc b/doc/01-user-guide.adoc index 120b229..fd9c126 100644 --- a/doc/01-user-guide.adoc +++ b/doc/01-user-guide.adoc @@ -564,12 +564,35 @@ The rules are: * A `:tag` key represents a tag's name. Defaults to `*`. -* An `:index` key expands into the trailing XPath `[x]` clause. -Useful when you need to select a third row from a table, for example. * Any non-special key represents an attribute and its value. * `:fn/` is a prefix followed by a supported query function. -Examples: +There are several query functions of the form `:fn/*`. +Each query function takes a parameter which is the value associated with the query function keyword in the map. + +* `:fn/index`: Takes an positive integer parameter. + This expands into a trailing XPath `[x]` clause and is useful when you need to select a specific row in a table, for example. +* `:fn/text`: Takes a string parameter. Matches if the element has the exact text specified. +* `:fn/has-text`: Takes a string parameter. + Matches if the element includes the specified text. +* `:fn/has-string`: Takes a string parameter. + Matches if the element string contains the specified string. + The difference between `:fn/has-text` and `:fn/has-string` is the difference between the XPath `text()` and `string()` functions (`text()` is the text within a given element and `string()` is the text of all descendant elements concatenated together in document order). + Generally, if you're targeting an element at the top of the hierarchy, you probably want `:fn/has-string`, and if you're targeting a single element at the bottom of the hierarchy, you probably want to use `:fn/has-text`. +* `:fn/has-class`: Takes a string parameter. + Matches if the element's `class` attribute includes the string. Unlike using a `:class` key in the map, `:fn/has-class` can match single classes, whereas `:class` is an exact match of the whole class string. +* `:fn/has-classes`: Takes a vector of strings parameter. + Matches if the element's `class` attribute includes _all_ of the specified class strings. +* `:fn/link`: Takes a string parameter. + Matches if the element's `href` attribute contains the specified string. +* `:fn/enabled`: Takes a boolean (`true` or `false`) parameter. + If the parameter is `true`, matches if the element is enabled. + If the parameter is `false`, matches if the element is disabled. +* `:fn/disabled`: Takes a boolean (`true` or `false`) parameter. + If the parameter is `true`, matches if the element is disabled. + If the parameter is `true`, matches if the element is enabled. + +Here are some examples of the map syntax: * find the first `div` tag + @@ -585,7 +608,7 @@ Examples: + [source,clojure] ---- -(= (e/query driver {:tag :div :index 1}) +(= (e/query driver {:tag :div :fn/index 1}) ;; equivalent via xpath: (e/query driver ".//div[1]")) ;; => true @@ -622,7 +645,7 @@ Examples: + [source,clojure] ---- -(e/get-element-text driver {:fn/has-text "blarg" :index 3}) +(e/get-element-text driver {:fn/has-text "blarg" :fn/index 3}) ;; => "blarg in a p" ;; equivalent in xpath: @@ -742,11 +765,11 @@ Maybe you want to click on the second link within: ---- -You can use the `:index` like so: +You can use the `:fn/index` like so: [source,clojure] ---- -(e/click driver [{:tag :li :class :search-result :index 2} {:tag :a}]) +(e/click driver [{:tag :li :class :search-result :fn/index 2} {:tag :a}]) ;; check click tracker from our sample page: (e/get-element-text driver :clicked) ;; => "b" @@ -822,6 +845,125 @@ 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. +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 in the User Guide sample HTML that has a bit of shadow DOM in it. + +[source,html] +---- +I'm not in the shadow DOM +
In main page paragraph
diff --git a/env/test/resources/static/test.html b/env/test/resources/static/test.html index 1e4473e..837d1a8 100644 --- a/env/test/resources/static/test.html +++ b/env/test/resources/static/test.html @@ -208,6 +208,16 @@