etaoin icon indicating copy to clipboard operation
etaoin copied to clipboard

Consider Supporting Selenium Grid WebDriver

Open dpassen opened this issue 4 years ago • 10 comments

Using [etaoin "0.4.6"] and seleniarm/standalone-chromium:4.1.2-20220227 docker image,

(let [q {:tag :input :id "email"}]
    (etaoin/wait-exists driver q)
    [(etaoin/query driver q)
     (etaoin/query-all driver q)])

yields

[nil ["427cdc53-73a7-4a55-a98a-a6a05a650805"]]

This default multimethod does not find the value correctly, but the Safari and Firefox multimethods do.

dpassen avatar Mar 03 '22 16:03 dpassen

When issuing a raw execute:

(let [q {:tag :input :id "email"}
        [loc term] (etaoin.query/expand driver q)]
    (-> (etaoin.api/execute
         {:driver driver
          :method :post
          :path   [:session (:session driver) :element]
          :data   {:using loc :value term}})))

the return is:

{:state "success",
 :sessionId nil,
 :class "org.openqa.selenium.remote.Response",
 :value
 {:element-6066-11e4-a52e-4f735466cecf
  "a3b83ecf-961e-41a9-9fb4-2f8e8213b362"},
 :status 0}

That's why the (-> ... first second) works. Any ideas?

dpassen avatar Mar 07 '22 20:03 dpassen

Hiya @dpassen, thanks for raising this issue.

I've not tried Selenium Grid docker images yet, but to get started, I'll do a sanity test on macOS hitting regular old Chrome.

(require '[etaoin.api :as e])

(def driver (e/chrome))

;; status gives us some idea of what I'm running here:
(e/get-status driver)
;; => {:build
;;     {:version
;;      "103.0.5060.53 (a1711811edd74ff1cf2150f36ffa3b0dae40b17f-refs/branch-heads/5060@{#853})"},
;;     :message "ChromeDriver ready for new sessions.",
;;     :os {:arch "x86_64", :name "Mac OS X", :version "10.15.7"},
;;     :ready true}

;; I'll navigate to a random page a test page on the InterWebs (so we can be on the page, :-))
(e/go driver "https://testpages.herokuapp.com/styled/basic-html-form-test.html")

;; And I'll adapt your query so it will find a single input on that page:
(let [q {:tag :input :name "username"}]
  (e/wait-exists driver q)
  [(e/query driver q)
   (e/query-all driver q)])

;; I'll also try your raw execute:
(require '[etaoin.query :as eq])

(let [q {:tag :input :name "username"}
      [loc term] (eq/expand driver q)]
    (-> (etaoin.api/execute
         {:driver driver
          :method :post
          :path   [:session (:session driver) :element]
          :data   {:using loc :value term}})))
;; => {:sessionId "3c9c305bfe7b6fa595bacada3d3cfedc",
;;     :status 0,
;;     :value {:ELEMENT "0.7985155027546353-1"}}

So does Selenium Grid translate/normalize W3C WebDriver requests? The :class "org.openqa.selenium.remote.Response" in your example raw response seems to suggest it is does at least add some data.

lread avatar Jun 22 '22 19:06 lread

I'll learn-as-I-go with docker images.

Here's me launching a current seleniarm/standalone-chrome image:

docker run -d -p 4444:4444 -p 7900:7900 --shm-size="2g" seleniarm/standalone-chromium:4.2.2-20220620

And from the REPL

(require '[etaoin.api :as e])

;; fake out a driver to check the status
(e/get-status {:webdriver-url "http://localhost:4444"})
;; => {:ready true,
;;     :message "Selenium Grid ready.",
;;     :nodes
;;     [{:id "cb4a51db-715b-49f8-8cb4-9a72859526ab",
;;       :uri "http://172.17.0.2:4444",
;;       :maxSessions 1,
;;       :osInfo {:arch "amd64", :name "Linux", :version "5.10.104-linuxkit"},
;;       :heartbeatPeriod 60000,
;;       :availability "UP",
;;       :version "4.2.2 (revision 683ccb65d6)",
;;       :slots
;;       [{:id
;;         {:hostId "cb4a51db-715b-49f8-8cb4-9a72859526ab",
;;          :id "9a395f09-e0e6-402b-9dbc-4d587c871d36"},
;;         :lastStarted "1970-01-01T00:00:00Z",
;;         :session nil,
;;         :stereotype
;;         {:browserName "chrome",
;;          :browserVersion "102.0",
;;          :platformName "LINUX"}}]}]}

;; Ok that's certainly a selenium grid generated response

;; Now I'll try to create a session on the grid.
;; I was getting timeouts until I added in the :capabilities you see 
;; (a selenium grid docs example made me think this might help)
(def driver (e/chrome {:webdriver-url "http://localhost:4444"
                       :capabilities {:browserName "chrome"
                                      :browserVersion "102.0"
                                      :platformName "LINUX"}}))

;; let's see what status returns now
(e/get-status driver)
;; => {:ready false,
;;     :message "Selenium Grid not ready.",
;;     :nodes
;;     [{:id "85df67b0-e738-4c4b-b07a-1c5587ce1934",
;;       :uri "http://172.17.0.2:4444",
;;       :maxSessions 1,
;;       :osInfo {:arch "amd64", :name "Linux", :version "5.10.104-linuxkit"},
;;       :heartbeatPeriod 60000,
;;       :availability "UP",
;;       :version "4.2.2 (revision 683ccb65d6)",
;;       :slots
;;       [{:id
;;         {:hostId "85df67b0-e738-4c4b-b07a-1c5587ce1934",
;;          :id "e22d24ce-6e0b-4bd7-99b1-b9853fca0bea"},
;;         :lastStarted "2022-06-22T20:08:44.408660Z",
;;         :session
;;         {:capabilities
;;          {:browserName "chrome",
;;           :acceptInsecureCerts false,
;;           :goog:chromeOptions {:debuggerAddress "localhost:38761"},
;;           :se:cdpVersion "102.0.5005.115",
;;           :networkConnectionEnabled false,
;;           :webauthn:extension:largeBlob true,
;;           :unhandledPromptBehavior "dismiss and notify",
;;           :pageLoadStrategy "normal",
;;           :setWindowRect true,
;;           :se:cdp
;;           "ws://172.17.0.2:4444/session/693254bf31e96a52e2d77fb5d48396d2/se/cdp",
;;           :webauthn:virtualAuthenticators true,
;;           :proxy {},
;;           :chrome
;;           {:chromedriverVersion
;;            "102.0.5005.115 (174dbe6e33bc81994fceb71d751be201d0b4803d-refs/branch-heads/5005_109@{#3})",
;;            :userDataDir "/tmp/.org.chromium.Chromium.f0C2DS"},
;;           :browserVersion "102.0.5005.115",
;;           :timeouts {:implicit 0, :pageLoad 300000, :script 30000},
;;           :platformName "LINUX",
;;           :webauthn:extension:credBlob true,
;;           :strictFileInteractability false},
;;          :sessionId "693254bf31e96a52e2d77fb5d48396d2",
;;          :start "2022-06-22T20:08:44.408660Z",
;;          :stereotype
;;          {:browserName "chrome", :browserVersion "102.0", :platformName "LINUX"},
;;          :uri "http://172.17.0.2:4444"},
;;         :stereotype
;;         {:browserName "chrome",
;;          :browserVersion "102.0",
;;          :platformName "LINUX"}}]}]}

;; interesting!

;; Ok let's repeat our experiments
(e/go driver "https://testpages.herokuapp.com/styled/basic-html-form-test.html")
;; When I open http://localhost:7900 in my browser I can see that the go worked

(let [q {:tag :input :name "username"}]
  (e/wait-exists driver q)
  [(e/query driver q)
   (e/query-all driver q)])
;; => [nil ["258562f2-4ef4-4484-b364-baa137ff3906"]]
;; ^ matches your experience

(require '[etaoin.query :as eq])

(let [q {:tag :input :name "username"}
      [loc term] (eq/expand driver q)]
    (-> (etaoin.api/execute
         {:driver driver
          :method :post
          :path   [:session (:session driver) :element]
          :data   {:using loc :value term}})))
;; => {:state "success",
;;     :sessionId nil,
;;     :class "org.openqa.selenium.remote.Response",
;;     :value
;;     {:element-6066-11e4-a52e-4f735466cecf "258562f2-4ef4-4484-b364-baa137ff3906"},
;;     :status 0}

;; ^ also matches your experience

So to me, without understanding more, it seems that Selenium Grid is not a simple pass-through to chromedriver. It does stuff.

Etaoin currently assumes a connection to chromedriver (or any other driver) is not abstracted in any way. I'm guessing that to support Selenium Grid, we'd have to add in a feature to understand that Etaoin is talking to a Selenium Grid W3C WebDriver HTTP server.

Which seems reasonable if there is enough interest from the community in doing so.

lread avatar Jun 22 '22 20:06 lread

Thanks for the research! Above and beyond

dpassen avatar Jun 22 '22 20:06 dpassen

Thanks for the issue, I might not have learned about Selenium Grid otherwise!

lread avatar Jun 22 '22 20:06 lread

I realised this feature for me, but did not tested all functions of Etaion. Result success, but need some modifies. My fork with this feature is and example

ekochetkov avatar Aug 11 '22 13:08 ekochetkov

Cool @ekochetkov, thanks for sharing. (Note your example is giving me a 404).

So does Selenium Grid WebDriver abstract specific WebDriver implementations to the W3C spec?

lread avatar Aug 11 '22 13:08 lread

Cool @ekochetkov, thanks for sharing. (Note your example is giving me a 404).

Fix

So does Selenium Grid WebDriver abstract specific WebDriver implementations to the W3C spec?

On the Webdriver page of selenium development documentation exists reference to W3C Recommendation

ekochetkov avatar Aug 11 '22 19:08 ekochetkov

Sooo... my understanding is as Etaoin was developed it adapted to the various WebDriver implementations which did not always follow the W3C spec correctly.

(Some of the adaptations might no longer be necessary with current WebDriver implementations that might be more conformant. Dunno.)

I assume the Selenium Grid abstraction, since it is written by the Selenium team who drive the WebDriver spec, probably abstracts the WebDriver implementations to conform to the spec.

lread avatar Feb 03 '23 17:02 lread

Of interest perhaps: api to get status of grid

lread avatar Feb 04 '23 17:02 lread