diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 081dd799..2005eedc 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -19,7 +19,8 @@ A release with an intentional breaking changes is marked with: // (adjust these in publish.clj as you see fit) == Unreleased [minor breaking] -* Technically breaking +* Minor breaking +** {issue}615[#615]: Etaoin now requires a minimum of JDK11 and Clojure 1.10 ** {issue}613[#612]: Remove all support for long obsolete and long untested PhantomJS ({lread}) ** {issue}467[#467]: Move to W3C WebDriver spec. diff --git a/deps.edn b/deps.edn index 271c43aa..f1ee9c4e 100644 --- a/deps.edn +++ b/deps.edn @@ -1,9 +1,8 @@ {:paths ["src" "resources"] - :deps {org.clojure/clojure {:mvn/version "1.9.0"} ;; min clojure version + :deps {org.clojure/clojure {:mvn/version "1.10.3"} ;; min clojure version babashka/fs {:mvn/version "0.5.21"} babashka/process {:mvn/version "0.5.22"} - clj-http/clj-http {:mvn/version "3.13.0"} ;; for jvm use - org.clj-commons/clj-http-lite {:mvn/version "1.0.13"} ;; for babashka use + org.babashka/http-client {:mvn/version "0.4.20"} slingshot/slingshot {:mvn/version "0.12.2"} cheshire/cheshire {:mvn/version "5.13.0"} org.clojure/tools.cli {:mvn/version "1.1.230"} diff --git a/doc/01-user-guide.adoc b/doc/01-user-guide.adoc index 950592ff..ad18ca17 100644 --- a/doc/01-user-guide.adoc +++ b/doc/01-user-guide.adoc @@ -82,7 +82,7 @@ There are two steps to installation: ==== For Clojure Users -Etaoin supports Clojure v1.9 and above. +Etaoin supports Clojure v1.10 and above on JDK11 and above. Add the following into the `:dependencies` vector in your `project.clj` file: diff --git a/script/test_matrix.clj b/script/test_matrix.clj index 6a0dca94..2ee986a8 100644 --- a/script/test_matrix.clj +++ b/script/test_matrix.clj @@ -41,11 +41,8 @@ :desc (str "test-doc " os " jdk" jdk-version)} ) (defn- github-actions-matrix [] - (let [os-jdks {"ubuntu" ["8" "11" "17" "21"] - ;; macOS on GitHub Actions is now arm-based and does not include jdk8 - "macos" ["11" "17" "21"] - "windows" ["8" "11" "17" "21"]} - oses (keys os-jdks) + (let [jdks ["11" "17" "21"] + oses ["ubuntu" "macos" "windows"] ide-browsers ["chrome" "firefox"] api-browsers ["chrome" "firefox" "edge" "safari"] platforms ["jvm" "bb"] @@ -67,14 +64,14 @@ (test-def (merge default-opts {:os os :id "api" :platform platform :browser browser}))) ;; for jdk coverage we don't need to run across all oses and browsers (for [id ["unit" "ide" "api"] - jdk-version (get os-jdks "ubuntu") + jdk-version jdks :when (not= jdk-version (:jdk-version default-opts))] (test-def {:jdk-version jdk-version :os "ubuntu" :id id :platform "jvm" :browser (when (not= "unit" id) "firefox")})) (for [os oses] (test-doc (merge default-opts {:os os}))) - (for [jdk-version (get os-jdks "ubuntu") + (for [jdk-version jdks :when (not= jdk-version (:jdk-version default-opts))] (test-doc {:jdk-version jdk-version :os "ubuntu"}))) (sort-by :desc natural-compare/natural-compare) diff --git a/src/etaoin/api.clj b/src/etaoin/api.clj index 0a12a122..6829d762 100644 --- a/src/etaoin/api.clj +++ b/src/etaoin/api.clj @@ -159,7 +159,7 @@ [etaoin.query :as query] [slingshot.slingshot :refer [throw+ try+]]) (:import - java.text.SimpleDateFormat + (java.text SimpleDateFormat) (java.util Base64 Date))) (set! *warn-on-reflection* true) @@ -3554,12 +3554,11 @@ (let [[opts bind & body] (if (symbol? (second args)) args (cons nil args))] - `(client/with-pool {} - (let [~bind (boot-driver ~type ~opts)] - (try - ~@body - (finally - (quit ~bind))))))) + `(let [~bind (boot-driver ~type ~opts)] + (try + ~@body + (finally + (quit ~bind)))))) (defmacro ^:no-doc with-headless-driver {:arglists '([type opts? bind & body])} diff --git a/src/etaoin/impl/client.cljc b/src/etaoin/impl/client.clj similarity index 75% rename from src/etaoin/impl/client.cljc rename to src/etaoin/impl/client.clj index 31b6eb7a..8a1fbc4d 100644 --- a/src/etaoin/impl/client.cljc +++ b/src/etaoin/impl/client.clj @@ -1,12 +1,11 @@ (ns ^:no-doc etaoin.impl.client (:require + [babashka.http-client :as client] [cheshire.core :as json] [clojure.string :as str] [clojure.tools.logging :as log] [etaoin.impl.proc :as proc] [etaoin.impl.util :as util] - #?(:bb [clj-http.lite.client :as client] - :clj [clj-http.client :as client]) [slingshot.slingshot :refer [throw+]])) (set! *warn-on-reflection* true) @@ -29,21 +28,12 @@ (def timeout (read-timeout)) -(def default-api-params - #?(:bb - {:accept :json - :content-type :json - :socket-timeout (* 1000 timeout) - :conn-timeout (* 1000 timeout) - :debug false} - :clj - {:as :json - :accept :json - :content-type :json - :socket-timeout (* 1000 timeout) - :conn-timeout (* 1000 timeout) - :debug false})) - +(def client (delay (client/client + {:request {:headers {:accept "application/json" + :content-type "application/json" + :accept-encoding ["gzip" "deflate"]} + :timeout (* 1000 timeout)} ;; request timeout + :connect-timeout (* 1000 timeout)}))) ;; ;; helpers ;; @@ -58,10 +48,6 @@ (defn- get-url-path [items] (str/join "/" (map url-item-str items))) -(defmacro with-pool [opt & body] - `(client/with-connection-pool ~opt - ~@body)) - (defn- parse-json [body] (let [body* (str/replace body #"Invalid Command Method -" "")] (try @@ -84,7 +70,6 @@ ;; if, by chance, something goes wrong while trying to realize process liveness (assoc driver :process-liveness-ex ex)))) - (defn http-request "an isolated http-request to support mocking" [params] @@ -101,15 +86,13 @@ url (if webdriver-url (format "%s/%s" webdriver-url path) (format "http://%s:%s/%s" host port path)) - params (cond-> (merge - default-api-params - {:url url - :method method - :throw-exceptions false}) + params (cond-> {:uri url + :method method + :client @client + :throw false} (= :post method) - #?(:bb (assoc :body (.getBytes (json/generate-string (or payload {})) - "UTF-8")) - :clj (assoc :form-params (or payload {})))) + (assoc :body (.getBytes (json/generate-string (or payload {})) + "UTF-8"))) _ (log/debugf "%s %s %6s %s %s" (name driver-type) (if webdriver-url @@ -131,8 +114,7 @@ {:exception ex}))] (if (:exception resp) (throw+ @error (:exception resp)) - (let [body #?(:bb (some-> resp :body parse-json) - :clj (:body resp)) + (let [body (some-> resp :body parse-json) error (delay (assoc @error :type :etaoin/http-error :status (:status resp) @@ -146,3 +128,11 @@ :else body))))) + +(comment + (http-request {:method :head + :uri "https://clojure.org" + :client @client}) + + + ) diff --git a/test/etaoin/api_test.clj b/test/etaoin/api_test.clj index 8c96945e..67a9f5af 100644 --- a/test/etaoin/api_test.clj +++ b/test/etaoin/api_test.clj @@ -110,7 +110,7 @@ (let [deadline (+ (System/currentTimeMillis) 15000) test-url (test-server-url "test.html") ] (loop [] - (let [resp (try (client/http-request {:method :get :url test-url}) + (let [resp (try (client/http-request {:method :get :uri test-url}) (catch Throwable _ :not-ready))] (when (= :not-ready resp) (if (< (System/currentTimeMillis) deadline) diff --git a/test/etaoin/unit/proc_test.clj b/test/etaoin/unit/proc_test.clj index 11789a43..62f4c8e1 100644 --- a/test/etaoin/unit/proc_test.clj +++ b/test/etaoin/unit/proc_test.clj @@ -1,28 +1,35 @@ (ns etaoin.unit.proc-test (:require - [clojure.java.shell :as shell] [clojure.string :as str] [clojure.test :refer [deftest is]] [etaoin.api :as e] [etaoin.impl.client :as client] [etaoin.impl.proc :as proc] - [etaoin.test-report])) + [etaoin.impl.util :as util] + [etaoin.test-report]) + (:import (java.lang ProcessHandle))) + +(defn all-processes [] + (for [^ProcessHandle p (-> (ProcessHandle/allProcesses) .iterator iterator-seq) + :when (some-> p .info .command .isPresent) + :let [info (.info p) + command (-> info .command .get) + arguments (when (-> info .arguments .isPresent) + (->> info .arguments .get (into []))) + start-instant (-> info .startInstant .get)]] + {:pid (.pid p) + :is-alive (.isAlive p) + :start-instant start-instant + :handle p + :command command + :arguments arguments})) (defn get-count-driver-instances [drivername] - (if proc/windows? - (let [instance-report (-> (shell/sh "powershell" "-command" (format "(Get-Process %s -ErrorAction SilentlyContinue).Path" drivername)) - :out - str/split-lines)] - (->> instance-report - (remove #(str/includes? % "\\scoop\\shims\\")) ;; for the scoop users, exclude the shim process - (filter #(str/includes? % drivername)) - count)) - (->> (shell/sh "sh" "-c" "ps aux") - :out - str/split-lines - (filter #(str/includes? % drivername)) - count))) + (->> (all-processes) + (remove #(str/includes? (:command %) "\\scoop\\shims\\")) ;; exclude windows scoop shims + (filter #(str/includes? (:command %) drivername)) + count)) (defn get-count-chromedriver-instances [] (get-count-driver-instances "chromedriver")) @@ -31,7 +38,7 @@ (get-count-driver-instances "geckodriver")) (deftest test-process-forking-port-specified-is-in-use - (let [port 9997 + (let [port (util/get-free-port) process (proc/run ["chromedriver" (format "--port=%d" port)])] (try (e/wait-running {:port port :host "localhost"}) @@ -44,7 +51,7 @@ (proc/kill process))))) (deftest test-process-forking-port-not-specified-so-random-port-is-picked - (let [port 9998 + (let [port (util/get-free-port) process (proc/run ["chromedriver" (format "--port=%d" port)])] (try (e/wait-running {:port port :host "localhost"}) @@ -58,7 +65,7 @@ (proc/kill process))))) (deftest test-process-forking-connect-existing-webdriver-host - (let [port 9999 + (let [port (util/get-free-port) process (proc/run ["chromedriver" (format "--port=%d" port)])] (try (e/wait-running {:port port :host "localhost"}) @@ -70,7 +77,7 @@ (proc/kill process))))) (deftest test-process-forking-connect-existing-webdriver-url - (let [port 9999 + (let [port (util/get-free-port) process (proc/run ["chromedriver" (format "--port=%d" port)])] (try (e/wait-running {:port port :host "localhost"}) @@ -112,9 +119,9 @@ (deftest http-exception-after-create-proc-now-dead (let [orig-http-request client/http-request] - (with-redefs [client/http-request (fn [{:keys [method url] :as params}] + (with-redefs [client/http-request (fn [{:keys [method uri] :as params}] ;; allow create session through, fail on everything else - (if (and (= :post method) (str/ends-with? url "/session")) + (if (and (= :post method) (str/ends-with? uri "/session")) (orig-http-request params) (throw (ex-info "read timeout" {}))))] (let [ex (try @@ -135,9 +142,9 @@ (deftest http-error-after-create-proc-now-dead ;; unlikely, we know we just talked to the driver because it returned an http error, but for completeness (let [orig-http-request client/http-request] - (with-redefs [client/http-request (fn [{:keys [method url] :as params}] + (with-redefs [client/http-request (fn [{:keys [method uri] :as params}] ;; allow create session through, fail on everything else - (if (and (= :post method) (str/ends-with? url "/session")) + (if (and (= :post method) (str/ends-with? uri "/session")) (orig-http-request params) {:status 418}))] (let [ex (try @@ -157,7 +164,7 @@ (is (= 0 (get-count-firefoxdriver-instances))))))) (deftest test-cleanup-connect-existing-on-create-error - (let [port 9999 + (let [port (util/get-free-port) process (proc/run ["chromedriver" (format "--port=%d" port)])] (try (e/wait-running {:port port :host "localhost"})