Skip to content

Commit

Permalink
Add support for wait/until functions
Browse files Browse the repository at this point in the history
- Make it possible to wait for some event to happen before acting on it
- Much more efficient than using wait/sleep
  • Loading branch information
agilecreativity committed Jul 31, 2017
1 parent acb9b0b commit 2447334
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 85 deletions.
51 changes: 30 additions & 21 deletions examples/lmgtfy
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,43 @@
(set-env! :dependencies '[[webica "3.4.0-clj0"]])

(require
'[clojure.string :as string]
'[webica.core :as w] ;; must always be first
'[webica.by :as by]
'[webica.web-driver :as driver]
'[webica.firefox-driver :as firefox]
'[webica.web-element :as element]
'[webica.web-driver-wait :as wait]
'[webica.remote-web-driver :as browser])
'[clojure.string :as string]
'[webica.core :as w] ;; must always be first
'[webica.by :as by]
'[webica.web-driver :as driver]
'[webica.firefox-driver :as firefox]
'[webica.chrome-driver :as chrome]
'[webica.web-element :as element]
'[webica.web-driver-wait :as wait]
'[webica.expected-conditions :as ec]
'[webica.remote-web-driver :as browser])

(defn lmgtfy [search]
(browser/get "http://www.google.com")
(let [q (browser/find-element (by/name "q"))]
(let [wdriver (wait/init-web-driver-wait (driver/get-instance))
q (browser/find-element (by/name "q"))]
(element/send-keys q search)
(element/submit q)
(wait/until (wait/instance 10)
(wait/condition
(fn [driver]
(string/starts-with?
(string/lower-case (driver/get-title driver))
(string/lower-case search)))))
(println "Was that so hard?")
(w/sleep 10)))
(wait/until wdriver (ec/title-contains search))
(println (str "current URL of your search : " (driver/get-current-url)))
#_
;; TODO: use assert to confirm the result e.g. if the search is "Clojure Conj 2017"
;; the assert would be something like
(assert (= "https://www.google.com/search?site=&source=hp&q=Clojure+Conj+2017&oq=&gs_l="
(driver/get-current-url)))
;; Note: wait for a bit to let the user see the result
(w/sleep 10)
(println "Was that so hard?")))

(defn -main
"Webica example of 'Let me Google that for you'"
[& args]
(let [search (if (empty? args) "Clojure Conj 2016"
(let [search (if (empty? args) "Clojure Conj 2017"
(apply str (interpose " " args)))]
(firefox/start-firefox)
(lmgtfy search)
(browser/quit)))
(let [chrome-binary (chrome/get-chromedriver-binary "/usr/lib/chromium/chromedriver")]
(chrome/start-chrome chrome-binary
;; "headless" ;; uncomment this out to run headless
"--window-size=1024,1080"
"--disable-gpu")
(lmgtfy search)
(browser/quit))))
91 changes: 69 additions & 22 deletions generate/webica/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
(ns webica.core
"Clojure wrapper for Selenium Webdriver
NOTE: load this namespace first when using webica."
NOTE: load this namespace first when using webica."
(:require [clojure.string :as string]
[clojure.pprint :as pp :refer [pprint]]
[environ.core :refer [env]]
Expand All @@ -18,12 +18,13 @@ NOTE: load this namespace first when using webica."
Reflector]
[java.lang.reflect
InvocationTargetException ParameterizedType Modifier Type]
[java.util.concurrent.TimeUnit]
[com.google.common.collect
ImmutableList ImmutableMap]
[org.openqa.selenium
By Keys WebDriver WebElement]
[org.openqa.selenium.chrome
ChromeDriver ChromeDriverService]
ChromeDriver ChromeDriverService ChromeOptions]
[org.openqa.selenium.firefox
FirefoxDriver]
[org.openqa.selenium.interactions
Expand Down Expand Up @@ -115,6 +116,30 @@ NOTE: load this namespace first when using webica."
(.addArguments chrome-options flags)
chrome-options))
(def driver-prop \"webdriver.chrome.driver\")
(defn ^:private validate-chromedriver
[driver-binary]
(if-not (fs/exists? driver-binary)
(throw
(RuntimeException.
(str \"ERROR: chromedriver executable not found:\" driver-binary))))
driver-binary)
(defn get-chromedriver-binary
[driver-binary]
(let [default-exe (System/getProperty driver-prop)
chromedriver-system \"/usr/lib/chromium/chromedriver\"
chromedriver-local \"/usr/local/bin/chromedriver\"
chromedriver (or driver-binary
(if (and (not (empty? default-exe))
(fs/exists? default-exe))
default-exe)
(if (fs/exists? chromedriver-system)
chromedriver-system)
chromedriver-local)]
(validate-chromedriver chromedriver)))
(defn start-chrome [chromedriver & args]
\"The default chrome options with the web-driver initialization.
Expand All @@ -132,25 +157,11 @@ NOTE: load this namespace first when using webica."
\\\"--disable-gpu\\\" ;; mandatory argument for Chrome/Chromium 59.0
\\\"--window-size=1920,1080\\\") ;; optional but good to have
For complete options please see https://goo.gl/DcUcrj\"
(let [driver-prop \"webdriver.chrome.driver\"
default-exe (System/getProperty driver-prop)
chromedriver-system \"/usr/lib/chromium/chromedriver\"
chromedriver-local \"/usr/local/bin/chromedriver\"
chromedriver (or chromedriver
(if (and (not (empty? default-exe))
(fs/exists? default-exe))
default-exe)
(if (fs/exists? chromedriver-system)
chromedriver-system)
chromedriver-local)]
(if-not (fs/exists? chromedriver)
(throw
(RuntimeException.
(str \"ERROR: chromedriver executable not found:\" chromedriver))))
(System/setProperty driver-prop chromedriver)
(if args
(instance (init-chrome-options args))
(instance))))")
(System/setProperty driver-prop (get-chromedriver-binary chromedriver))
(if args
(instance (init-chrome-options args))
(instance)))")

(def #^{:added "clj0"}
firefox-driver-extra
Expand Down Expand Up @@ -217,7 +228,42 @@ NOTE: load this namespace first when using webica."
(string/lower-case (driver/get-title driver))
'cheese!'))))\"
[apply-fn]
(Condition. apply-fn))")
(Condition. apply-fn))
(def ^:dynamic *default-timeout* 15000) ;; msec
(defn init-web-driver-wait
\"Create the WebDriverWait from existing WebDriver object using a given timeout (in msec).\"
([driver]
(init-web-driver-wait driver *default-timeout*))
([driver timeout]
;; Set implicitly for a given timeout
(let [timeout-in-secs (/ timeout 1000)]
(.implicitlyWait
(-> driver .manage .timeouts)
timeout-in-secs java.util.concurrent.TimeUnit/SECONDS)
;; Then return the WebDriverWait instance
(org.openqa.selenium.support.ui.WebDriverWait. driver timeout-in-secs))))
(defn until
\"Wait until a given condition is met or raise exception if the condition can't be met.
Sample usage:
;; a) For the most control try
(until wdriver (ec/title-contains \\\"some-text\\\"))
;; b) For simple condition with one argument :
(until ec/title-is \\\"Google Search - Clojure Conj 2016\\\")
;; c) For condition that needed require `by` locator e.g. by/xpath, by/css-selector, etc:
(until ec/presence-of-element-located by/xpath \\\"some-id\\\")\"
([wdriver expected-fn]
(.until wdriver expected-fn))
([wdriver expected-fn arg]
(.until wdriver (expected-fn arg)))
([wdriver expected-fn by-fn arg]
(.until wdriver (expected-fn (by-fn arg)))))")

(def #^{:added "clj0"}
web-driver-wait-coercions
Expand All @@ -237,6 +283,7 @@ on how to generate the Clojure source code."
:require '[[me.raynes.fs :as fs]]
:extra chrome-driver-extra}
ChromeDriverService {}
ChromeOptions {}
FirefoxDriver {:exclude '[get]
:clear '[quit kill]
:require '[[me.raynes.fs :as fs]]
Expand Down
48 changes: 29 additions & 19 deletions src/webica/chrome_driver.clj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,30 @@
(.addArguments chrome-options flags)
chrome-options))

(def driver-prop "webdriver.chrome.driver")

(defn ^:private validate-chromedriver
[driver-binary]
(if-not (fs/exists? driver-binary)
(throw
(RuntimeException.
(str "ERROR: chromedriver executable not found:" driver-binary))))
driver-binary)

(defn get-chromedriver-binary
[driver-binary]
(let [default-exe (System/getProperty driver-prop)
chromedriver-system "/usr/lib/chromium/chromedriver"
chromedriver-local "/usr/local/bin/chromedriver"
chromedriver (or driver-binary
(if (and (not (empty? default-exe))
(fs/exists? default-exe))
default-exe)
(if (fs/exists? chromedriver-system)
chromedriver-system)
chromedriver-local)]
(validate-chromedriver chromedriver)))

(defn start-chrome [chromedriver & args]
"The default chrome options with the web-driver initialization.
Expand All @@ -41,22 +65,8 @@
\"--disable-gpu\" ;; mandatory argument for Chrome/Chromium 59.0
\"--window-size=1920,1080\") ;; optional but good to have
For complete options please see https://goo.gl/DcUcrj"
(let [driver-prop "webdriver.chrome.driver"
default-exe (System/getProperty driver-prop)
chromedriver-system "/usr/lib/chromium/chromedriver"
chromedriver-local "/usr/local/bin/chromedriver"
chromedriver (or chromedriver
(if (and (not (empty? default-exe))
(fs/exists? default-exe))
default-exe)
(if (fs/exists? chromedriver-system)
chromedriver-system)
chromedriver-local)]
(if-not (fs/exists? chromedriver)
(throw
(RuntimeException.
(str "ERROR: chromedriver executable not found:" chromedriver))))
(System/setProperty driver-prop chromedriver)
(if args
(instance (init-chrome-options args))
(instance))))

(System/setProperty driver-prop (get-chromedriver-binary chromedriver))
(if args
(instance (init-chrome-options args))
(instance)))
9 changes: 9 additions & 0 deletions src/webica/chrome_options.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(ns webica.chrome-options
"Clojure binding for Selenium class ChromeOptions"
(:require [clojure.core :as clj]
[webica.core :as w])
(:import [org.openqa.selenium.chrome
ChromeOptions]))

(w/intern-java ChromeOptions *ns*)

Loading

0 comments on commit 2447334

Please sign in to comment.