Skip to content

Commit c37f4df

Browse files
authored
Improved test coverage (#665)
* 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
1 parent ea9910e commit c37f4df

File tree

4 files changed

+233
-31
lines changed

4 files changed

+233
-31
lines changed

.clj-kondo/config.edn

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@
1111
etaoin.impl.util/with-tmp-file etaoin.impl.util/with-tmp-file}}
1212
:linters {:deprecated-var
1313
{:exclude {etaoin.api/child {:namespaces [etaoin.api-test]}
14-
etaoin.api/children {:namespaces [etaoin.api-test]}}}}}
14+
etaoin.api/children {:namespaces [etaoin.api-test]}}}
15+
:unresolved-symbol
16+
{:exclude [(clojure.test/is [thrown+? thrown+-with-msg?])]}}}

CHANGELOG.adoc

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ A release with an intentional breaking changes is marked with:
3434
** {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])
3535
* Docs
3636
** {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])
37+
* Quality
38+
** {issue}640[#650]: Significantly improved test coverage. ({person}dgr[@dgr])
3739

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

env/test/resources/static/test.html

+19-2
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ <h3>Cookies section</h3>
169169
<h3>Element property section</h3>
170170
<div type="text" id="element-props" value="value text"><div>Inner HTML</div></div>
171171

172-
<h3>Find element</h3>
173-
<div class="target">target-1</div>
172+
<h3 id="find-element">Find element</h3>
173+
<div id="find-element-by-id" class="target">target-1</div>
174174
<div class="foo">
175175
<div class="target">target-2</div>
176176
</div>
@@ -181,6 +181,23 @@ <h3>Find element</h3>
181181
</div>
182182
</div>
183183
</div>
184+
<div strangeattribute="foo">DIV with strange attribute</div>
185+
<h3>:fn/* tests</h3>
186+
<div id="multiple-classes" class="class1, class2">multiple classes</div>
187+
<div>Test Indexing</div>
188+
<ol id="ordered-list" class="ol-class1, ol-class2">
189+
<li class="list">ordered 1</li>
190+
<li class="list">ordered 2</li>
191+
<li class="list">ordered 3</li>
192+
</ol>
193+
<a href="https://www.github.com/">Link to GitHub</a>
194+
<div id="enabled-disabled">Enabled/Disabled elements<br>
195+
<input id="checkbox-1" type="checkbox" />Checkbox 1 - Enabled<br>
196+
<input id="checkbox-2" type="checkbox" disabled />Checkbox 2 - Disabled<br>
197+
<input id="checkbox-3" type="checkbox" />Checkbox 3 - Enabled<br>
198+
</div>
199+
200+
184201

185202
<h3>Find multiple elements</h3>
186203
<div id="find-elements">

test/etaoin/api_test.clj

+209-28
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
[etaoin.impl.client :as client]
1414
[etaoin.keys :as k]
1515
[etaoin.test-report :as test-report]
16-
[slingshot.slingshot :refer [try+]])
16+
[slingshot.slingshot :refer [try+]]
17+
[slingshot.test])
1718
(:import [java.net ServerSocket]))
1819

1920
(defn numeric? [val]
@@ -128,6 +129,33 @@
128129
report-browsers
129130
test-server)
130131

132+
(deftest test-browser-conditionals
133+
(testing "Chrome conditionals"
134+
(e/when-chrome *driver*
135+
(is (e/driver? *driver* :chrome)))
136+
(e/when-not-chrome *driver*
137+
(is (not (e/driver? *driver* :chrome)))))
138+
(testing "Firefox conditionals"
139+
(e/when-firefox *driver*
140+
(is (e/driver? *driver* :firefox)))
141+
(e/when-not-firefox *driver*
142+
(is (not (e/driver? *driver* :firefox)))))
143+
(testing "Safari conditionals"
144+
(e/when-safari *driver*
145+
(is (e/driver? *driver* :safari)))
146+
(e/when-not-safari *driver*
147+
(is (not (e/driver? *driver* :safari)))))
148+
(testing "Edge conditionals"
149+
(e/when-edge *driver*
150+
(is (e/driver? *driver* :edge)))
151+
(e/when-not-edge *driver*
152+
(is (not (e/driver? *driver* :edge)))))
153+
(testing "Headless conditionals"
154+
(e/when-headless *driver*
155+
(is (e/headless? *driver*)))
156+
(e/when-not-headless *driver*
157+
(is (not (e/headless? *driver*))))))
158+
131159
(deftest test-navigation
132160
(is (= (test-server-url "test.html") (e/get-url *driver*)) "initial page")
133161
(e/go *driver* (test-server-url "test2.html"))
@@ -158,27 +186,78 @@
158186
(is (e/selected? *driver* :vehicle2))
159187
(is (e/selected? *driver* :vehicle3)))
160188

189+
(deftest test-submit
190+
(doto *driver*
191+
(e/fill-multi {:simple-input 1
192+
:simple-password 2
193+
:simple-textarea 3})
194+
(e/submit :simple-input)
195+
;; Both Safari and Firefox need a slight delay here before the URL
196+
;; corresponding to the submitted form is valid. Chrome and Edge
197+
;; seem to do OK without. As of Aug 28, 2024.
198+
(e/when-safari (e/wait 3))
199+
(e/when-firefox (e/wait 3))
200+
(-> e/get-url
201+
(str/ends-with? "?login=1&password=2&message=3")
202+
is)))
203+
161204
(deftest test-input
162205
(testing "fill multiple inputs"
206+
;; Test with map form
163207
(doto *driver*
164208
(e/fill-multi {:simple-input 1
165209
:simple-password 2
166210
:simple-textarea 3})
167211
(e/click :simple-submit)
168212
(e/when-safari (e/wait 3))
213+
(-> e/get-url
214+
(str/ends-with? "?login=1&password=2&message=3")
215+
is))
216+
;; Test with vector form
217+
(doto *driver*
218+
(e/fill-multi [:simple-input 1
219+
:simple-password 2
220+
:simple-textarea 3])
221+
(e/click :simple-submit)
222+
(e/when-safari (e/wait 3))
169223
(-> e/get-url
170224
(str/ends-with? "?login=1&password=2&message=3")
171225
is)))
226+
(testing "fill-multi bad inputs"
227+
(is (thrown+? [:type :etaoin/argument]
228+
(e/fill-multi *driver* #{:set :is :not :allowed})))
229+
(is (thrown+? [:type :etaoin/argument]
230+
(e/fill-multi *driver* '(:list :is :not :allowed))))
231+
(is (thrown+? [:type :etaoin/argument]
232+
(e/fill-multi *driver* [:vector :with :odd :length :is :not :allowed]))))
172233
(testing "fill human multiple inputs"
173234
(doto *driver*
235+
;; Test with map form
174236
(e/fill-human-multi {:simple-input "login"
175237
:simple-password "123"
176238
:simple-textarea "text"})
177239
(e/click :simple-submit)
178240
(e/when-safari (e/wait 3))
241+
(-> e/get-url
242+
(str/ends-with? "?login=login&password=123&message=text")
243+
is))
244+
(doto *driver*
245+
;; Test with vector form
246+
(e/fill-human-multi [:simple-input "login"
247+
:simple-password "123"
248+
:simple-textarea "text"])
249+
(e/click :simple-submit)
250+
(e/when-safari (e/wait 3))
179251
(-> e/get-url
180252
(str/ends-with? "?login=login&password=123&message=text")
181253
is)))
254+
(testing "fill-human-multi bad inputs"
255+
(is (thrown+? [:type :etaoin/argument]
256+
(e/fill-multi *driver* #{:set :is :not :allowed})))
257+
(is (thrown+? [:type :etaoin/argument]
258+
(e/fill-multi *driver* '(:list :is :not :allowed))))
259+
(is (thrown+? [:type :etaoin/argument]
260+
(e/fill-multi *driver* [:vector :with :odd :length :is :not :allowed]))))
182261
(testing "fill multiple vars"
183262
(doto *driver*
184263
(e/fill :simple-input 1 "test" 2 \space \A)
@@ -600,7 +679,7 @@
600679
(is (= init-handle (e/get-window-handle *driver*)) "wrapped around to original window")))
601680

602681
(deftest test-maximize
603-
(when-not (e/headless? *driver*) ;; skip for headless
682+
(e/when-not-headless *driver* ;; skip for headless
604683
(e/set-window-position *driver* 2 2)
605684
(let [orig-rect (e/get-window-rect *driver*)
606685
target-rect (-> orig-rect
@@ -749,22 +828,118 @@
749828
(e/set-hash "goodbye")
750829
(-> e/get-url (str/ends-with? "/test.html#goodbye") is))))
751830

752-
(deftest test-find-element
753-
(let [text (e/get-element-text *driver* {:class :target})]
754-
(is (= text "target-1")))
755-
(let [text (e/get-element-text *driver* [{:class :foo}
756-
{:class :target}])]
757-
(is (= text "target-2")))
758-
(e/with-xpath *driver*
759-
(let [text (e/get-element-text *driver* ".//div[@class='target'][1]")]
760-
(is (= text "target-1"))))
761-
(let [text (e/get-element-text *driver* {:css ".target"})]
762-
(is (= text "target-1")))
763-
(let [q [{:css ".bar"} ".//div[@class='inside']" {:tag :span}]
764-
text (e/get-element-text *driver* q)]
765-
(is (= text "target-3"))))
766-
767-
(deftest test-find-elements-more
831+
(deftest test-query
832+
(testing "finding an element by id keyword"
833+
(let [el (e/query *driver* :find-element-by-id)]
834+
(is (= "target-1" (e/get-element-text-el *driver* el)))))
835+
(testing "XPath and CSS string syntax"
836+
(e/with-xpath *driver*
837+
(let [el (e/query *driver* ".//div[@class='target'][1]")]
838+
(is (= "target-1" (e/get-element-text-el *driver* el)))))
839+
(e/with-css *driver*
840+
(let [el (e/query *driver* ".bar .deep .inside span") ]
841+
(is (= "target-3" (e/get-element-text-el *driver* el))))))
842+
(testing "XPath and CSS map syntax"
843+
(let [el (e/query *driver* {:xpath ".//*[@class='target']"})]
844+
(is (= "target-1" (e/get-element-text-el *driver* el))))
845+
(let [el (e/query *driver* {:css ".target"})]
846+
(is (= "target-1" (e/get-element-text-el *driver* el)))))
847+
(testing "map syntax"
848+
;; 1. tags
849+
(testing "tags"
850+
(let [el (e/query *driver* {:tag :h3 :id :find-element})]
851+
(is (= "Find element" (e/get-element-text-el *driver* el)))))
852+
;; 2. class
853+
(testing "class"
854+
(let [el (e/query *driver* {:class :target})]
855+
(is (= "target-1" (e/get-element-text-el *driver* el)))))
856+
;; 3. random attributes
857+
(testing "random attributes"
858+
(let [el (e/query *driver* {:strangeattribute :foo})]
859+
(is (= "DIV with strange attribute" (e/get-element-text-el *driver* el)))))
860+
;; 4. :fn/*
861+
(testing ":fn/* functions"
862+
;; :index and :fn/index
863+
(let [el (e/query *driver* {:class :list :index 3})] ; deprecated syntax
864+
(is (= "ordered 3" (e/get-element-text-el *driver* el))))
865+
(let [el (e/query *driver* {:class :list :fn/index 3})] ; new syntax
866+
(is (= "ordered 3" (e/get-element-text-el *driver* el))))
867+
;; :fn/text
868+
(let [el (e/query *driver* {:fn/text "multiple classes"})]
869+
(is (= "multiple-classes" (e/get-element-attr-el *driver* el "id"))))
870+
;; :fn/has-text
871+
(let [el (e/query *driver* {:fn/has-text "ple cla"})] ; pick out the middle
872+
(is (= "multiple-classes" (e/get-element-attr-el *driver* el "id"))))
873+
;; :fn/has-string
874+
(let [el (e/query *driver* {:tag :ol :fn/has-string "ordered 3"})]
875+
(is (= "ordered-list" (e/get-element-attr-el *driver* el "id"))))
876+
;; :fn/has-class
877+
(let [el (e/query *driver* {:fn/has-class "ol-class1"})]
878+
(is (= "ordered-list" (e/get-element-attr-el *driver* el "id"))))
879+
;; :fn/has-classes
880+
;; verify that order doesn't matter
881+
(let [elx (e/query *driver* {:fn/has-classes [:ol-class1 :ol-class2]})
882+
ely (e/query *driver* {:fn/has-classes [:ol-class2 :ol-class1]})
883+
elz (e/query *driver* :ordered-list)]
884+
(is (= elx ely elz)))
885+
;; :fn/link
886+
(let [el (e/query *driver* {:fn/link "https://www.github.com/"})]
887+
(is (= "Link to GitHub" (e/get-element-text-el *driver* el))))
888+
;; :fn/enabled
889+
(let [el (e/query *driver* [:enabled-disabled {:type :checkbox :fn/enabled true}])]
890+
(is (= "checkbox-1" (e/get-element-attr-el *driver* el "id"))))
891+
(let [el (e/query *driver* [:enabled-disabled {:type :checkbox :fn/enabled false}])]
892+
(is (= "checkbox-2" (e/get-element-attr-el *driver* el "id"))))
893+
(let [el (e/query *driver* [:enabled-disabled {:type :checkbox :fn/enabled true :fn/index 2}])]
894+
(is (= "checkbox-3" (e/get-element-attr-el *driver* el "id"))))
895+
;; :fn/disabled
896+
(let [el (e/query *driver* [:enabled-disabled {:type :checkbox :fn/disabled false}])]
897+
(is (= "checkbox-1" (e/get-element-attr-el *driver* el "id"))))
898+
(let [el (e/query *driver* [:enabled-disabled {:type :checkbox :fn/disabled true}])]
899+
(is (= "checkbox-2" (e/get-element-attr-el *driver* el "id"))))
900+
(let [el (e/query *driver* [:enabled-disabled {:type :checkbox :fn/disabled false :fn/index 2}])]
901+
(is (= "checkbox-3" (e/get-element-attr-el *driver* el "id"))))))
902+
(testing "vector syntax"
903+
;; TODO: should check vectors with length 1, 2, and 3.
904+
(e/with-xpath *driver* ; force XPath because we use a string
905+
(let [el (e/query *driver* [{:css ".bar"} ".//div[@class='inside']" {:tag :span}])]
906+
(is (= "target-3" (e/get-element-text-el *driver* el)))))
907+
(let [el (e/query *driver* [{:class :foo} {:class :target}])]
908+
(is (= "target-2" (e/get-element-text-el *driver* el)))))
909+
(testing "variable arguments syntax"
910+
;; Same as vector syntax but just provided as separate arguments to `query`
911+
(e/with-xpath *driver*
912+
(let [el (e/query *driver* {:css ".bar"} ".//div[@class='inside']" {:tag :span})]
913+
(is (= "target-3" (e/get-element-text-el *driver* el)))))
914+
(let [el (e/query *driver* {:class :foo} {:class :target})]
915+
(is (= "target-2" (e/get-element-text-el *driver* el)))))
916+
(testing "negative test cases"
917+
;; TODO:
918+
;; 1. searching for nothing
919+
(testing "zero-length vector queries"
920+
;; 1. pass a vector of length 0 to query
921+
(is (thrown+? [:type :etaoin/argument] (e/query *driver* []))))
922+
;; 2. searching for an element that can't be found
923+
(testing "querying for missing elements"
924+
;; 2a. searching for a missing element with missing ID
925+
(is (thrown+? [:type :etaoin/http-error] (e/query *driver* :missing-element)))
926+
(is (thrown+? [:type :etaoin/http-error] (e/query *driver* [:missing-element])))
927+
;; 2a. element not found in middle of a vector query
928+
(is (thrown+? [:type :etaoin/http-error] (e/query *driver* [{:css ".bar"}
929+
:missing-element
930+
{:tag :span}])))
931+
;; 2b. element not found at the end of a vector query
932+
(is (thrown+? [:type :etaoin/http-error] (e/query *driver* [{:css ".bar"}
933+
{:tag :div :class :inside}
934+
:missing-element]))))
935+
;; 3. malformed XPath
936+
;; 4. malformed CSS
937+
;; 5. query isn't a string, map, or vector. Perhaps a list and set.
938+
;; 6. bad :fn/... keywords
939+
;; 7. vector queries with vector elements (vectors in vectors)
940+
))
941+
942+
(deftest test-query-all
768943
(testing "simple case"
769944
(let [q {:class :find-elements-target}
770945
elements (e/query-all *driver* q)]
@@ -777,7 +952,22 @@
777952
texts (for [el elements]
778953
(e/get-element-text-el *driver* el))]
779954
(is (= (count elements) 2))
780-
(is (= texts ["1" "2"])))))
955+
(is (= texts ["1" "2"]))))
956+
(testing "returning multiple elements via XPath"
957+
(let [q {:xpath ".//div[@id='operate-multiple-elements']//*"}
958+
elements (e/query-all *driver* q)
959+
tag-names (for [el elements]
960+
(str/lower-case (e/get-element-tag-el *driver* el)))]
961+
(is (= (vec tag-names)
962+
["div" "b" "p" "span"])))))
963+
964+
(deftest test-switch-default-locator
965+
(testing "xpath locator"
966+
(let [driver (e/use-xpath *driver*)]
967+
(is (= "target-1" (e/get-element-text driver ".//*[@class='target']")))))
968+
(testing "css locator"
969+
(let [driver (e/use-css *driver*)]
970+
(is (= "target-1" (e/get-element-text driver ".target"))))))
781971

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

789-
(deftest test-multiple-elements
790-
(testing "tag names"
791-
(let [q {:xpath ".//div[@id='operate-multiple-elements']//*"}
792-
elements (e/query-all *driver* q)
793-
tag-names (for [el elements]
794-
(str/lower-case (e/get-element-tag-el *driver* el)))]
795-
(is (= (vec tag-names)
796-
["div" "b" "p" "span"])))))
797-
798979
(deftest test-query-tree
799980
(let [url (test-server-url "test2.html")
800981
_ (e/go *driver* url)

0 commit comments

Comments
 (0)