Skip to content

Commit

Permalink
Driver defaults :path renamed to :path-driver
Browse files Browse the repository at this point in the history
Also:
- no longer add nil driver map values
- add `defaults-global` for defaults that apply to all driver types
- add some more tests around created driver map
- localized handling of most driver option defaults to one spot

Closes clj-commons#471
  • Loading branch information
lread committed Jul 1, 2022
1 parent 24d52a8 commit 379e47f
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 60 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ The symbol `num-.` is technically an invalid Clojure symbol and can confuse tool
A grep.app for `num-.` found Etaoin itself as the only user of this var.
If your code uses `etaoin.keys/num-.`, you'll need to rename it to `etaoin.keys/num-dot`.

* https://github.com/clj-commons/etaoin/issues/471[#471]: `etaoin.api/defaults` keyword `:path` renamed to `:path-driver` to match keyword used in driver options.

* https://github.com/clj-commons/etaoin/issues/430[#430]: Declare the public API.
We made what we think is a good guess at what the public Etaoin API is.
The following namespaces are now considered internal and subject to change:
Expand Down
2 changes: 1 addition & 1 deletion doc/01-user-guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1993,7 +1993,7 @@ If `:port` is found to already in use when creating a new local WebDriver proce
See also <<connecting-existing>>.

Example: `:port 9997`
a| Varies by vendor:
a| Random port when lanching local WebDriver process, else varies by vendor:

* chrome `9515`
* firefox `4444`
Expand Down
96 changes: 49 additions & 47 deletions src/etaoin/api.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
This is a rich API:
**WebDriver**
- [[with-driver]] [[boot-driver]] [[defaults]] [[when-not-drivers]]
- [[with-driver]] [[boot-driver]] [[defaults]] [[defaults-global]] [[when-not-drivers]]
- [[with-chrome]] [[with-chrome-headless]] [[chrome]] [[chrome-headless]] [[chrome?]] [[when-chrome]] [[when-not-chrome]]
- [[with-edge]] [[with-edge-headless]] [[edge]] [[edge-headless]] [[when-edge]] [[when-not-edge]]
- [[with-firefox]] [[with-firefox-headless]] [[firefox]] [[firefox-headless]] [[firefox?]] [[when-firefox]] [[when-not-firefox]]
Expand Down Expand Up @@ -163,22 +163,27 @@
;;
;; WebDriver defaults
;;
(def ^:no-doc default-locator "xpath")
(def ^:no-doc locator-xpath "xpath")
(def ^:no-doc locator-css "css selector")

(def ^{:doc "WebDriver defaults"} defaults
(def ^{:doc "WebDriver global option defaults"} defaults-global
{:log-level :all
:locator default-locator})

(def ^{:doc "WebDriver driver type specific option defaults.
Note that for locally launched WebDriver processes the default port is a random free port."}
defaults
{:firefox {:port 4444
:path "geckodriver"}
:path-driver "geckodriver"}
:chrome {:port 9515
:path "chromedriver"}
:path-driver "chromedriver"}
:phantom {:port 8910
:path "phantomjs"}
:path-driver "phantomjs"}
:safari {:port 4445
:path "safaridriver"}
:path-driver "safaridriver"}
:edge {:port 17556
:path "msedgedriver"}})

(def ^:no-doc default-locator "xpath")
(def ^:no-doc locator-xpath "xpath")
(def ^:no-doc locator-css "css selector")
:path-driver "msedgedriver"}})

;;
;; utils
Expand Down Expand Up @@ -3348,7 +3353,6 @@
[host port]
(format "http://%s:%s" host port))


(defn- -create-driver
"Creates a new driver instance.
Expand All @@ -3369,31 +3373,23 @@
-- `:host` is a string with either IP or hostname. Use it if the
server is run not locally but somethere in your network.
-- `:port` is an integer value what HTTP port to use. It is taken
from the `defaults` global map if is not passed. If there is no
port in that map, a random-generated port is used.
-- `:port` is an integer value what HTTP port to use.
-- `:webdriver-url` is a URL to a web-driver service. This URL is
generally provided by web-driver service providers. When specified the
`:host` and `:port` parameters are ignored.
-- `:locator` is a string determs what algorithm to use by default
when finding elements on a page. `default-locator` variable is used
if not passed."
when finding elements on a page."
[type & [{:keys [port host webdriver-url locator]}]]
(let [port (or port
(if host
(get-in defaults [type :port])
(util/get-free-port)))
host (or host "127.0.0.1")
(let [host (or host "127.0.0.1")
url (make-url host port)
locator (or locator default-locator)
driver {:type type
:host host
:port port
:url url
:webdriver-url webdriver-url
:locator locator}]
driver (util/assoc-some {:type type
:host host
:port port
:url url ;; NOTE: not great that this is also used on input to indicate default browser url
:locator locator}
:webdriver-url webdriver-url)]
(if webdriver-url
(log/debugf "Created driver: %s %s" (name type) (util/strip-url-creds webdriver-url))
(log/debugf "Created driver: %s %s:%s" (name type) host port))
Expand All @@ -3420,15 +3416,14 @@
- `opts` is an optional map with the following possible parameters:
-- `:path-driver` is a string path to the driver's binary file. When
not passed, it is taken from defaults.
-- `:path-driver` is a string path to the driver's binary file.
-- `:path-browser` is a string path to the browser's binary
file. When not passed, the driver discovers it by its own.
-- `:log-level` a keyword to set browser's log level. Used when fetching
browser's logs. Possible values are: `:off`, `:debug`, `:warn`, `:info`,
`:error`, `:all`. When not passed, `:all` is set.
`:error`, `:all`.
-- `:driver-log-level` a keyword to set driver's log level.
The value is a string. Possible values are:
Expand Down Expand Up @@ -3456,16 +3451,13 @@
path-browser
driver-log-level]}]]

(let [{:keys [type port host]} driver
(let [{:keys [port host]} driver

_ (when (util/connectable? host port)
(throw (ex-info
(format "Port %d already in use" port)
{:port port})))

log-level (or log-level :all)
path-driver (or path-driver (get-in defaults [type :path]))

driver (cond-> driver
true (drv/set-browser-log-level log-level)
true (drv/set-path path-driver)
Expand All @@ -3480,7 +3472,7 @@
process (proc/run proc-args {:log-stdout log-stdout
:log-stderr log-stderr
:env env})]
(assoc driver :env env :process process)))
(util/assoc-some driver :env env :process process)))

(defn- -connect-driver
"Connects to a running Webdriver server.
Expand Down Expand Up @@ -3534,7 +3526,7 @@
See https://www.w3.org/TR/webdriver/#capabilities"
[driver & [{:keys [webdriver-url
url
url ;; NOTE: somewhat confusing because we also set a webdriver :url in returned driver
size
args
prefs
Expand All @@ -3548,7 +3540,6 @@
(when (not webdriver-url)
(wait-running driver))
(let [type (:type driver)
caps (get-in defaults [type :capabilities])
proxy (proxy-env proxy)
[with height] size
driver (cond-> driver
Expand All @@ -3561,9 +3552,12 @@
prefs (drv/set-prefs prefs)
profile (drv/set-profile profile)
user-agent (drv/set-user-agent user-agent)
:else
:always
;; NOTE: defaults overriding specific capabilities potentially set by above seems suspect
;; but... maybe... not worth worrying about?
(->
(drv/set-capabilities caps)
(drv/set-capabilities (:capabilities defaults-global))
(drv/set-capabilities (get-in defaults [type :capabilities]))
(drv/set-capabilities capabilities)
(drv/set-capabilities desired-capabilities)))
caps (:capabilities driver)
Expand All @@ -3576,7 +3570,6 @@
Closes the current session that is stored in the driver if it still exists.
Removes the session from `driver`."
[driver]

(try (delete-session driver)
(catch Exception e
(when (not (= 404 (:status (ex-data e))))
Expand All @@ -3599,15 +3592,24 @@
- launches a WebDriver process (or connects to an existing running process if `:host` is specified)
- creates a session for driver
Defaults taken from [[defaults-global]] then [[defaults]] for `type`:
`:port` - if `:host` not specified, port is randomly generated for local WebDriver process
`:capabilities` - are deep merged as part of connect logic.
`opts` map is optionally, see [Driver Options](/doc/01-user-guide.adoc#driver-options)."
([type]
(boot-driver type {}))
([type {:keys [host webdriver-url] :as opts}]
(cond-> type
true (-create-driver opts)
(and (not host)
(not webdriver-url)) (-run-driver opts)
true (-connect-driver opts))))
(let [default-opts (cond-> (merge (dissoc defaults-global :capabilities)
(dissoc (type defaults) :capabilities))
;; if host, we are launching webdriver, default port is random
(not host) (assoc :port (util/get-free-port)))
opts (merge default-opts opts)]
(cond-> type
:always (-create-driver opts)
(and (not host)
(not webdriver-url)) (-run-driver opts)
:always (-connect-driver opts)))))

(defn quit
"Have `driver` close the current session, then, if Etaoin launched it, kill the WebDriver process."
Expand Down
5 changes: 3 additions & 2 deletions src/etaoin/impl/driver.clj
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,9 @@

(defn set-capabilities
[driver caps]
(update driver :capabilities deep-merge caps))

(if caps
(update driver :capabilities deep-merge caps)
driver))

(defn set-load-strategy
[driver strategy]
Expand Down
10 changes: 10 additions & 0 deletions src/etaoin/impl/util.clj
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,13 @@
(.getPort u)
(.getFile u)
(.getRef u)))))

(defn assoc-some
"Associates a key with a value in a map, if and only if the value is
not nil. From medley."
([m k v]
(if (nil? v) m (assoc m k v)))
([m k v & kvs]
(reduce (fn [m [k v]] (assoc-some m k v))
(assoc-some m k v)
(partition 2 kvs))))
76 changes: 66 additions & 10 deletions test/etaoin/unit/unit_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,89 @@
[etaoin.ide.flow :as ide]
[etaoin.ide.impl.spec :as spec]
[etaoin.impl.proc :as proc]
[etaoin.impl.util :as util]
[etaoin.test-report]))

(deftest test-firefox-driver-args
(deftest test-driver-options
(with-redefs
[etaoin.impl.proc/run (fn [_ _])
[etaoin.impl.proc/run (fn [_ _] {:some :process})
e/wait-running identity
e/create-session (fn [_ _] "session-key")
proc/kill identity
e/delete-session identity]
(testing "Session"
e/delete-session identity
util/get-free-port (constantly 12345)]
(testing "defaults"
(e/with-firefox driver
(is (= "session-key"
(:session driver)))))
(testing "No custom args"
(is (= {:args ["geckodriver" "--port" 12345]
:capabilities {:loggingPrefs {:browser "ALL"}}
:host "127.0.0.1"
:locator "xpath"
:port 12345
:process {:some :process}
:session "session-key"
:type :firefox
:url "http://127.0.0.1:12345"} driver))))
(testing "port"
(e/with-firefox {:port 1234} driver
(is (= ["geckodriver" "--port" 1234]
(:args driver)))))
(testing "Default `--marionette-port` is assigned when `:profile` is specified"
(testing "when host, default to port for driver, process options are not relevant"
(e/with-firefox {:host "somehost"} driver
(is (= {:host "somehost"
:locator "xpath"
:port 4444
:session "session-key"
:type :firefox
:url "http://somehost:4444"} driver))))
(testing "default `--marionette-port` is assigned when `:profile` is specified"
(e/with-firefox {:port 1234 :profile "/tmp/firefox-profile/1"} driver
(is (= ["geckodriver" "--port" 1234 "--marionette-port" 2828]
(:args driver)))))
(testing "Custom `--marionette-port` is assigned when `:profile` is specified"
(testing "custom `--marionette-port` is assigned when `:profile` is specified"
(e/with-firefox {:port 1234
:profile "/tmp/firefox-profile/1"
:args-driver ["--marionette-port" 2821]} driver
(is (= ["geckodriver" "--port" 1234 "--marionette-port" 2821]
(:args driver)))))))
(:args driver)))))
(testing "capabilities are deep merged"
(with-redefs
[e/defaults-global (assoc e/defaults-global :capabilities {:default-firefox :val1
:some {:deeper {:thing0 0
:thing1 1
:thing2 2
:thing3 3}}})
e/defaults (assoc-in e/defaults [:firefox :capabilities] {:default-global :val2
:some {:deeper {:thing0 10
:thing3 30
:thing4 40}}})]
(e/with-firefox {:capabilities {:specified :val2
:some {:deeper {:thing1 100
:thing3 300
:thing5 500}}}
:desired-capabilities {:specified-desired :val3
:some {:deeper {:thing3 3000
:thing6 6000}}}} driver
(is (= {:args ["geckodriver" "--port" 12345],
:capabilities
{:default-firefox :val1
:default-global :val2
:loggingPrefs {:browser "ALL"},
:some {:deeper {:thing0 10
:thing1 100
:thing2 2
:thing3 3000
:thing4 40
:thing5 500
:thing6 6000}}
:specified :val2
:specified-desired :val3}
:host "127.0.0.1"
:locator "xpath"
:port 12345
:process {:some :process}
:session "session-key"
:type :firefox
:url "http://127.0.0.1:12345"} driver)))))))

(deftest test-chrome-profile
(fs/with-temp-dir [chrome-dir {:prefix "chrome-dir"}]
Expand Down

0 comments on commit 379e47f

Please sign in to comment.