Skip to content

Commit

Permalink
Improved test coverage (clj-commons#665)
Browse files Browse the repository at this point in the history
* Add tests for use-xpath and use-css

* Add tests for when-<browser> and when-not-<browser> macros

* Add tests for when-headless and when-not-headless

* Test vector forms of fill-multi and fill-human-multi

* Add test for submit

* Add test for with-css

* Update clj-kondo config to ignore Slingshot test symbols

* Improve fill-multi/fill-human-multi exception testing

Add slingshot to api-test.clj dependencies

* Rename test-find-element and test-find-elements-more

to test-query and test-query-all, to better reflect what they are
really testing.

* Move test-multiple-elements to be part of test-query-all

This better reflects what it is doing.

* Add test of query id by keyword

* Rework test-query to provide more extensive test coverage

* Add test querying for empty vector

* Add tests searching for missing elements

* Add more test ideas for later exploration

* Update CHANGELOG with note about improved test coverage

* Fix assertions in test-switch-default-locator
  • Loading branch information
dgr authored Sep 15, 2024
1 parent ea9910e commit c37f4df
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 31 deletions.
4 changes: 3 additions & 1 deletion .clj-kondo/config.edn
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@
etaoin.impl.util/with-tmp-file etaoin.impl.util/with-tmp-file}}
:linters {:deprecated-var
{:exclude {etaoin.api/child {:namespaces [etaoin.api-test]}
etaoin.api/children {:namespaces [etaoin.api-test]}}}}}
etaoin.api/children {:namespaces [etaoin.api-test]}}}
:unresolved-symbol
{:exclude [(clojure.test/is [thrown+? thrown+-with-msg?])]}}}
2 changes: 2 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ A release with an intentional breaking changes is marked with:
** {issue}663[#663]: `query` throws a more accurate exception with a more accurate error message when provided with an empty query vector. ({person}dgr[@dgr])
* Docs
** {issue}656[#656]: Correctly describe behavior when query's parameter is a string. The User Guide and `query` doc strings say that a string passed to `query` is interpreted as an XPath expression. In fact, `query` interprets this as either XPath or CSS depending on the setting of the driver's `:locator` parameter, which can be changed. ({person}dgr[@dgr])
* Quality
** {issue}640[#650]: Significantly improved test coverage. ({person}dgr[@dgr])

== v1.1.41 [minor breaking] - 2024-08-14 [[v1.1.41]]

Expand Down
21 changes: 19 additions & 2 deletions env/test/resources/static/test.html
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ <h3>Cookies section</h3>
<h3>Element property section</h3>
<div type="text" id="element-props" value="value text"><div>Inner HTML</div></div>

<h3>Find element</h3>
<div class="target">target-1</div>
<h3 id="find-element">Find element</h3>
<div id="find-element-by-id" class="target">target-1</div>
<div class="foo">
<div class="target">target-2</div>
</div>
Expand All @@ -181,6 +181,23 @@ <h3>Find element</h3>
</div>
</div>
</div>
<div strangeattribute="foo">DIV with strange attribute</div>
<h3>:fn/* tests</h3>
<div id="multiple-classes" class="class1, class2">multiple classes</div>
<div>Test Indexing</div>
<ol id="ordered-list" class="ol-class1, ol-class2">
<li class="list">ordered 1</li>
<li class="list">ordered 2</li>
<li class="list">ordered 3</li>
</ol>
<a href="https://www.github.com/">Link to GitHub</a>
<div id="enabled-disabled">Enabled/Disabled elements<br>
<input id="checkbox-1" type="checkbox" />Checkbox 1 - Enabled<br>
<input id="checkbox-2" type="checkbox" disabled />Checkbox 2 - Disabled<br>
<input id="checkbox-3" type="checkbox" />Checkbox 3 - Enabled<br>
</div>



<h3>Find multiple elements</h3>
<div id="find-elements">
Expand Down
237 changes: 209 additions & 28 deletions test/etaoin/api_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
[etaoin.impl.client :as client]
[etaoin.keys :as k]
[etaoin.test-report :as test-report]
[slingshot.slingshot :refer [try+]])
[slingshot.slingshot :refer [try+]]
[slingshot.test])
(:import [java.net ServerSocket]))

(defn numeric? [val]
Expand Down Expand Up @@ -128,6 +129,33 @@
report-browsers
test-server)

(deftest test-browser-conditionals
(testing "Chrome conditionals"
(e/when-chrome *driver*
(is (e/driver? *driver* :chrome)))
(e/when-not-chrome *driver*
(is (not (e/driver? *driver* :chrome)))))
(testing "Firefox conditionals"
(e/when-firefox *driver*
(is (e/driver? *driver* :firefox)))
(e/when-not-firefox *driver*
(is (not (e/driver? *driver* :firefox)))))
(testing "Safari conditionals"
(e/when-safari *driver*
(is (e/driver? *driver* :safari)))
(e/when-not-safari *driver*
(is (not (e/driver? *driver* :safari)))))
(testing "Edge conditionals"
(e/when-edge *driver*
(is (e/driver? *driver* :edge)))
(e/when-not-edge *driver*
(is (not (e/driver? *driver* :edge)))))
(testing "Headless conditionals"
(e/when-headless *driver*
(is (e/headless? *driver*)))
(e/when-not-headless *driver*
(is (not (e/headless? *driver*))))))

(deftest test-navigation
(is (= (test-server-url "test.html") (e/get-url *driver*)) "initial page")
(e/go *driver* (test-server-url "test2.html"))
Expand Down Expand Up @@ -158,27 +186,78 @@
(is (e/selected? *driver* :vehicle2))
(is (e/selected? *driver* :vehicle3)))

(deftest test-submit
(doto *driver*
(e/fill-multi {:simple-input 1
:simple-password 2
:simple-textarea 3})
(e/submit :simple-input)
;; Both Safari and Firefox need a slight delay here before the URL
;; corresponding to the submitted form is valid. Chrome and Edge
;; seem to do OK without. As of Aug 28, 2024.
(e/when-safari (e/wait 3))
(e/when-firefox (e/wait 3))
(-> e/get-url
(str/ends-with? "?login=1&password=2&message=3")
is)))

(deftest test-input
(testing "fill multiple inputs"
;; Test with map form
(doto *driver*
(e/fill-multi {:simple-input 1
:simple-password 2
:simple-textarea 3})
(e/click :simple-submit)
(e/when-safari (e/wait 3))
(-> e/get-url
(str/ends-with? "?login=1&password=2&message=3")
is))
;; Test with vector form
(doto *driver*
(e/fill-multi [:simple-input 1
:simple-password 2
:simple-textarea 3])
(e/click :simple-submit)
(e/when-safari (e/wait 3))
(-> e/get-url
(str/ends-with? "?login=1&password=2&message=3")
is)))
(testing "fill-multi bad inputs"
(is (thrown+? [:type :etaoin/argument]
(e/fill-multi *driver* #{:set :is :not :allowed})))
(is (thrown+? [:type :etaoin/argument]
(e/fill-multi *driver* '(:list :is :not :allowed))))
(is (thrown+? [:type :etaoin/argument]
(e/fill-multi *driver* [:vector :with :odd :length :is :not :allowed]))))
(testing "fill human multiple inputs"
(doto *driver*
;; Test with map form
(e/fill-human-multi {:simple-input "login"
:simple-password "123"
:simple-textarea "text"})
(e/click :simple-submit)
(e/when-safari (e/wait 3))
(-> e/get-url
(str/ends-with? "?login=login&password=123&message=text")
is))
(doto *driver*
;; Test with vector form
(e/fill-human-multi [:simple-input "login"
:simple-password "123"
:simple-textarea "text"])
(e/click :simple-submit)
(e/when-safari (e/wait 3))
(-> e/get-url
(str/ends-with? "?login=login&password=123&message=text")
is)))
(testing "fill-human-multi bad inputs"
(is (thrown+? [:type :etaoin/argument]
(e/fill-multi *driver* #{:set :is :not :allowed})))
(is (thrown+? [:type :etaoin/argument]
(e/fill-multi *driver* '(:list :is :not :allowed))))
(is (thrown+? [:type :etaoin/argument]
(e/fill-multi *driver* [:vector :with :odd :length :is :not :allowed]))))
(testing "fill multiple vars"
(doto *driver*
(e/fill :simple-input 1 "test" 2 \space \A)
Expand Down Expand Up @@ -600,7 +679,7 @@
(is (= init-handle (e/get-window-handle *driver*)) "wrapped around to original window")))

(deftest test-maximize
(when-not (e/headless? *driver*) ;; skip for headless
(e/when-not-headless *driver* ;; skip for headless
(e/set-window-position *driver* 2 2)
(let [orig-rect (e/get-window-rect *driver*)
target-rect (-> orig-rect
Expand Down Expand Up @@ -749,22 +828,118 @@
(e/set-hash "goodbye")
(-> e/get-url (str/ends-with? "/test.html#goodbye") is))))

(deftest test-find-element
(let [text (e/get-element-text *driver* {:class :target})]
(is (= text "target-1")))
(let [text (e/get-element-text *driver* [{:class :foo}
{:class :target}])]
(is (= text "target-2")))
(e/with-xpath *driver*
(let [text (e/get-element-text *driver* ".//div[@class='target'][1]")]
(is (= text "target-1"))))
(let [text (e/get-element-text *driver* {:css ".target"})]
(is (= text "target-1")))
(let [q [{:css ".bar"} ".//div[@class='inside']" {:tag :span}]
text (e/get-element-text *driver* q)]
(is (= text "target-3"))))

(deftest test-find-elements-more
(deftest test-query
(testing "finding an element by id keyword"
(let [el (e/query *driver* :find-element-by-id)]
(is (= "target-1" (e/get-element-text-el *driver* el)))))
(testing "XPath and CSS string syntax"
(e/with-xpath *driver*
(let [el (e/query *driver* ".//div[@class='target'][1]")]
(is (= "target-1" (e/get-element-text-el *driver* el)))))
(e/with-css *driver*
(let [el (e/query *driver* ".bar .deep .inside span") ]
(is (= "target-3" (e/get-element-text-el *driver* el))))))
(testing "XPath and CSS map syntax"
(let [el (e/query *driver* {:xpath ".//*[@class='target']"})]
(is (= "target-1" (e/get-element-text-el *driver* el))))
(let [el (e/query *driver* {:css ".target"})]
(is (= "target-1" (e/get-element-text-el *driver* el)))))
(testing "map syntax"
;; 1. tags
(testing "tags"
(let [el (e/query *driver* {:tag :h3 :id :find-element})]
(is (= "Find element" (e/get-element-text-el *driver* el)))))
;; 2. class
(testing "class"
(let [el (e/query *driver* {:class :target})]
(is (= "target-1" (e/get-element-text-el *driver* el)))))
;; 3. random attributes
(testing "random attributes"
(let [el (e/query *driver* {:strangeattribute :foo})]
(is (= "DIV with strange attribute" (e/get-element-text-el *driver* el)))))
;; 4. :fn/*
(testing ":fn/* functions"
;; :index and :fn/index
(let [el (e/query *driver* {:class :list :index 3})] ; deprecated syntax
(is (= "ordered 3" (e/get-element-text-el *driver* el))))
(let [el (e/query *driver* {:class :list :fn/index 3})] ; new syntax
(is (= "ordered 3" (e/get-element-text-el *driver* el))))
;; :fn/text
(let [el (e/query *driver* {:fn/text "multiple classes"})]
(is (= "multiple-classes" (e/get-element-attr-el *driver* el "id"))))
;; :fn/has-text
(let [el (e/query *driver* {:fn/has-text "ple cla"})] ; pick out the middle
(is (= "multiple-classes" (e/get-element-attr-el *driver* el "id"))))
;; :fn/has-string
(let [el (e/query *driver* {:tag :ol :fn/has-string "ordered 3"})]
(is (= "ordered-list" (e/get-element-attr-el *driver* el "id"))))
;; :fn/has-class
(let [el (e/query *driver* {:fn/has-class "ol-class1"})]
(is (= "ordered-list" (e/get-element-attr-el *driver* el "id"))))
;; :fn/has-classes
;; verify that order doesn't matter
(let [elx (e/query *driver* {:fn/has-classes [:ol-class1 :ol-class2]})
ely (e/query *driver* {:fn/has-classes [:ol-class2 :ol-class1]})
elz (e/query *driver* :ordered-list)]
(is (= elx ely elz)))
;; :fn/link
(let [el (e/query *driver* {:fn/link "https://www.github.com/"})]
(is (= "Link to GitHub" (e/get-element-text-el *driver* el))))
;; :fn/enabled
(let [el (e/query *driver* [:enabled-disabled {:type :checkbox :fn/enabled true}])]
(is (= "checkbox-1" (e/get-element-attr-el *driver* el "id"))))
(let [el (e/query *driver* [:enabled-disabled {:type :checkbox :fn/enabled false}])]
(is (= "checkbox-2" (e/get-element-attr-el *driver* el "id"))))
(let [el (e/query *driver* [:enabled-disabled {:type :checkbox :fn/enabled true :fn/index 2}])]
(is (= "checkbox-3" (e/get-element-attr-el *driver* el "id"))))
;; :fn/disabled
(let [el (e/query *driver* [:enabled-disabled {:type :checkbox :fn/disabled false}])]
(is (= "checkbox-1" (e/get-element-attr-el *driver* el "id"))))
(let [el (e/query *driver* [:enabled-disabled {:type :checkbox :fn/disabled true}])]
(is (= "checkbox-2" (e/get-element-attr-el *driver* el "id"))))
(let [el (e/query *driver* [:enabled-disabled {:type :checkbox :fn/disabled false :fn/index 2}])]
(is (= "checkbox-3" (e/get-element-attr-el *driver* el "id"))))))
(testing "vector syntax"
;; TODO: should check vectors with length 1, 2, and 3.
(e/with-xpath *driver* ; force XPath because we use a string
(let [el (e/query *driver* [{:css ".bar"} ".//div[@class='inside']" {:tag :span}])]
(is (= "target-3" (e/get-element-text-el *driver* el)))))
(let [el (e/query *driver* [{:class :foo} {:class :target}])]
(is (= "target-2" (e/get-element-text-el *driver* el)))))
(testing "variable arguments syntax"
;; Same as vector syntax but just provided as separate arguments to `query`
(e/with-xpath *driver*
(let [el (e/query *driver* {:css ".bar"} ".//div[@class='inside']" {:tag :span})]
(is (= "target-3" (e/get-element-text-el *driver* el)))))
(let [el (e/query *driver* {:class :foo} {:class :target})]
(is (= "target-2" (e/get-element-text-el *driver* el)))))
(testing "negative test cases"
;; TODO:
;; 1. searching for nothing
(testing "zero-length vector queries"
;; 1. pass a vector of length 0 to query
(is (thrown+? [:type :etaoin/argument] (e/query *driver* []))))
;; 2. searching for an element that can't be found
(testing "querying for missing elements"
;; 2a. searching for a missing element with missing ID
(is (thrown+? [:type :etaoin/http-error] (e/query *driver* :missing-element)))
(is (thrown+? [:type :etaoin/http-error] (e/query *driver* [:missing-element])))
;; 2a. element not found in middle of a vector query
(is (thrown+? [:type :etaoin/http-error] (e/query *driver* [{:css ".bar"}
:missing-element
{:tag :span}])))
;; 2b. element not found at the end of a vector query
(is (thrown+? [:type :etaoin/http-error] (e/query *driver* [{:css ".bar"}
{:tag :div :class :inside}
:missing-element]))))
;; 3. malformed XPath
;; 4. malformed CSS
;; 5. query isn't a string, map, or vector. Perhaps a list and set.
;; 6. bad :fn/... keywords
;; 7. vector queries with vector elements (vectors in vectors)
))

(deftest test-query-all
(testing "simple case"
(let [q {:class :find-elements-target}
elements (e/query-all *driver* q)]
Expand All @@ -777,7 +952,22 @@
texts (for [el elements]
(e/get-element-text-el *driver* el))]
(is (= (count elements) 2))
(is (= texts ["1" "2"])))))
(is (= texts ["1" "2"]))))
(testing "returning multiple elements via XPath"
(let [q {:xpath ".//div[@id='operate-multiple-elements']//*"}
elements (e/query-all *driver* q)
tag-names (for [el elements]
(str/lower-case (e/get-element-tag-el *driver* el)))]
(is (= (vec tag-names)
["div" "b" "p" "span"])))))

(deftest test-switch-default-locator
(testing "xpath locator"
(let [driver (e/use-xpath *driver*)]
(is (= "target-1" (e/get-element-text driver ".//*[@class='target']")))))
(testing "css locator"
(let [driver (e/use-css *driver*)]
(is (= "target-1" (e/get-element-text driver ".target"))))))

(deftest test-fn-index
(testing ":fn/index"
Expand All @@ -786,15 +976,6 @@
(e/get-element-text-el *driver*)))]
(is (= items ["One" "Two" "Three" "Four" "Five"])))))

(deftest test-multiple-elements
(testing "tag names"
(let [q {:xpath ".//div[@id='operate-multiple-elements']//*"}
elements (e/query-all *driver* q)
tag-names (for [el elements]
(str/lower-case (e/get-element-tag-el *driver* el)))]
(is (= (vec tag-names)
["div" "b" "p" "span"])))))

(deftest test-query-tree
(let [url (test-server-url "test2.html")
_ (e/go *driver* url)
Expand Down

0 comments on commit c37f4df

Please sign in to comment.