Giter VIP home page Giter VIP logo

etaoin's People

Contributors

adamfrey avatar ageneau avatar alanmarazzi avatar alehatsman avatar anthonygalea avatar borkdude avatar codefarmer avatar daveyarwood avatar dgr avatar fancygits avatar github-actions[bot] avatar igrishaev avatar jkrasnay avatar jwkoelewijn avatar kidd avatar lread avatar mgerlach-klick avatar miner avatar nberger avatar nebesnytihohod avatar nenadalm avatar piotr-yuxuan avatar reefersleep avatar rickmoynihan avatar rogererens avatar scottlowe avatar seancorfield avatar tslocke avatar uunnamed avatar verma avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

etaoin's Issues

Move some settings from run-driver to connect-driver

Сейчас в run-driver мы процессим некоторые опции, которые долнжны быть в connect-driver. В частности это все, что касается capabilities. Другими словами, в run-driver должно остаться только то, что относится к самому драйверу. В connect-driver передать то, что касается создания новой сессии. В том числе исправить докстринги к обеим функциям.

Postmortem wrapper

Add a wrapper that dumps a screenshot and HTML file in case of an uncaught exception, smth like:

(with-postmortem {:html "foo/bar.html" :screenshot "foo/image.png"}
   (click driver {:id :non-existing}))

The result is:

  • foo/bar.html and foo/image.png are created;
  • exception is raised

global config

provide a module with common settings possibly overridden with system vars

Cannot initialize driver

Attempting to follow along with the instructions provided in the README, I ran into an error initializing a driver instance:

sample.core> (use 'etaoin.api)
nil
sample.core> (require '[etaoin.keys :as k])
nil
sample.core> (def driver (firefox))
                    java.io.IOException: error=2, No such file or directory
                    java.io.IOException: Cannot run program "geckodriver": error=2, No such file or directory
clojure.lang.Compiler$CompilerException: java.io.IOException: Cannot run program "geckodriver": error=2, No such file or directory, compiling:(form-init2904624762632379775.clj:47:21)

I expect that I'm walking a well-traveled road over here. Please point out what I'm doing wrong if its something obvious.

extend xpath statements

try to improve xpath clauses to support text() = ..., contains and some related features.

add initial profile support

When creating a new driver instance, a map of options should support :profile field that points either on profile directory (Chrome) or a profile name (Firefox).

Technically, it should be done with passing additional arguments for browser process.

Chrome:
https://sites.google.com/a/chromium.org/chromedriver/capabilities
user-data-dir=/path/to/your/custom/profile

Example:

(def driver (chrome {:capabilities {:chromeOptions {:args ["user-data-dir=/path/to/your/custom/profile"]}}}))

Firefox:
https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile

  1. Create a new profile for testing, e.g. firefox -CreateProfile JoelUser;
  2. When running a firefox driver, specify -P flag:
(def driver (ff {:capabilities {:moz:firefoxOptions {:args ["-P" "JoelUser"]}}}))

There should be a multimethid in drv module that fills those flags properly depending on a browser type.

decrease wait time

Время ожидания default-timeout сейчас 20, предлагаю понизить до 7 секунд, потому что в случае ошибки приходится долго ждать. Проверить, есть ли в readme упоминия числа 20, и если да, то поменять.

Capabilities are overwritten by merge

I was surprised by the behavior of the way etaoin merges default and user-passed capabilities.

I tried running

(e/headless {:desired-capabilities
                      {:chromeOptions
                       {:args ["--proxy-server=localhost:12345"]}}}

and to my surprise a non-headless chrome window opened up.

Looking at the Etaoin source I found the answer. In the default options table, etaoin has these options for :headless

(def defaults
  {...
   :headless {:port 9515
                     :path "chromedriver"
                     :capabilities {:chromeOptions {:args ["--headless"]}}}})

But then in connect-driver user supplied options are merged with default options:

(deep-merge cap-default cap) 

and the implementation of deep-merge just takes the last value it sees for a nested key:

(defn deep-merge
  [& vals]
  (if (every? map? vals)
    (apply merge-with deep-merge vals)
    (last vals)))

Does it make sense to change the implementation of deep-merge to concatenate sequential values? Like:

(deep-merge {:chromeOptions {:args ["arg1"]}} {:chromeOptions {:args ["arg2"]}}) => {:chromeOptions {:args ["arg1" "arg2"]}}

I can make a PR if you think this is a good idea.

Tests failing locally

An empty Firefox browser window opens and then the test fails immediately.

Firefox version: 54.0.1
OS version: macOS Sierra 10.12.5


Testing etaoin.api-test
DEBUG etaoin.client: firefox 127.0.0.1:4444   POST session {:desiredCapabilities {}}
DEBUG etaoin.client: firefox 127.0.0.1:4444   POST session//url {:url "file:/Users/Jrock/Projects/etaoin/resources/html/test.html"}
DEBUG etaoin.client: firefox 127.0.0.1:4444 DELETE session/ 
Exception in thread "main" clojure.lang.ExceptionInfo: throw+: {:response {:value {:error "unknown command", :message "DELETE /session/ did not match a known command", :stacktrace "stack backtrace:\n   0:        0x103fe3444 - backtrace::backtrace::trace::h9f8fad52a1a8acf1\n   1:        0x103fe37fe - backtrace::capture::Backtrace::new::hbc1a12654c8fdba8\n   2:        0x103f0df4d - webdriver::error::WebDriverError::new::h10bc8992bd1e5ce5\n   3:        0x103eb690d - _$LT$webdriver..httpapi..WebDriverHttpApi$LT$U$GT$$GT$::decode_request::h59625e2271927f5c\n   4:        0x103ef545e - _$LT$webdriver..server..HttpHandler$LT$U$GT$$u20$as$u20$hyper..server..Handler$GT$::handle::h75bcf4a70da4d3d8\n   5:        0x103e35b11 - _$LT$hyper..server..Worker$LT$H$GT$$GT$::keep_alive_loop::h6cffe26f2f997d81\n   6:        0x103e36665 - _$LT$hyper..server..Worker$LT$H$GT$$GT$::handle_connection::ha65f143767ef2701\n   7:        0x103ec24c1 - hyper::server::handle::_$u7b$$u7b$closure$u7d$$u7d$::h021c4837b0330036\n   8:        0x103ec2a5e - hyper::server::listener::spawn_with::_$u7b$$u7b$closure$u7d$$u7d$::h922fff03b485de70\n   9:        0x103f030a7 - _$LT$std..panic..AssertUnwindSafe$LT$F$GT$$u20$as$u20$core..ops..FnOnce$LT$$LP$$RP$$GT$$GT$::call_once::hb095877d82356444\n  10:        0x103e47c86 - std::panicking::try::do_call::h7a395bca958e623c\n  11:        0x10447554a - __rust_maybe_catch_panic\n  12:        0x103e475bc - std::panicking::try::h55d8916b3f7a3515\n  13:        0x103e44e52 - std::panic::catch_unwind::hf1717d8b89ed66e7\n  14:        0x103e46ffc - std::thread::Builder::spawn::_$u7b$$u7b$closure$u7d$$u7d$::hd9c5bf9811bb3b73\n  15:        0x103ea1a4c - _$LT$F$u20$as$u20$alloc..boxed..FnBox$LT$A$GT$$GT$::call_box::h0c4aa35835be38e8\n  16:        0x104471714 - std::sys::imp::thread::Thread::new::thread_start::hae18fa578e305215\n  17:     0x7fffb9b5693a - _pthread_body\n  18:     0x7fffb9b56886 - _pthread_start"}}, :path "session/", :payload nil, :method :delete, :type :etaoin/http-error, :port 4444, :host "127.0.0.1", :status 404, :driver {:args ["geckodriver" "--port" 4444], :desired-capabilities nil, :process #object[java.lang.UNIXProcess 0x5b2ade46 "java.lang.UNIXProcess@5b2ade46"], :locator "xpath", :type :firefox, :env {}, :port 4444, :host "127.0.0.1", :url "http://127.0.0.1:4444", :session nil}} {:response {:value {:error "unknown command", :message "DELETE /session/ did not match a known command", :stacktrace "stack backtrace:\n   0:        0x103fe3444 - backtrace::backtrace::trace::h9f8fad52a1a8acf1\n   1:        0x103fe37fe - backtrace::capture::Backtrace::new::hbc1a12654c8fdba8\n   2:        0x103f0df4d - webdriver::error::WebDriverError::new::h10bc8992bd1e5ce5\n   3:        0x103eb690d - _$LT$webdriver..httpapi..WebDriverHttpApi$LT$U$GT$$GT$::decode_request::h59625e2271927f5c\n   4:        0x103ef545e - _$LT$webdriver..server..HttpHandler$LT$U$GT$$u20$as$u20$hyper..server..Handler$GT$::handle::h75bcf4a70da4d3d8\n   5:        0x103e35b11 - _$LT$hyper..server..Worker$LT$H$GT$$GT$::keep_alive_loop::h6cffe26f2f997d81\n   6:        0x103e36665 - _$LT$hyper..server..Worker$LT$H$GT$$GT$::handle_connection::ha65f143767ef2701\n   7:        0x103ec24c1 - hyper::server::handle::_$u7b$$u7b$closure$u7d$$u7d$::h021c4837b0330036\n   8:        0x103ec2a5e - hyper::server::listener::spawn_with::_$u7b$$u7b$closure$u7d$$u7d$::h922fff03b485de70\n   9:        0x103f030a7 - _$LT$std..panic..AssertUnwindSafe$LT$F$GT$$u20$as$u20$core..ops..FnOnce$LT$$LP$$RP$$GT$$GT$::call_once::hb095877d82356444\n  10:        0x103e47c86 - std::panicking::try::do_call::h7a395bca958e623c\n  11:        0x10447554a - __rust_maybe_catch_panic\n  12:        0x103e475bc - std::panicking::try::h55d8916b3f7a3515\n  13:        0x103e44e52 - std::panic::catch_unwind::hf1717d8b89ed66e7\n  14:        0x103e46ffc - std::thread::Builder::spawn::_$u7b$$u7b$closure$u7d$$u7d$::hd9c5bf9811bb3b73\n  15:        0x103ea1a4c - _$LT$F$u20$as$u20$alloc..boxed..FnBox$LT$A$GT$$GT$::call_box::h0c4aa35835be38e8\n  16:        0x104471714 - std::sys::imp::thread::Thread::new::thread_start::hae18fa578e305215\n  17:     0x7fffb9b5693a - _pthread_body\n  18:     0x7fffb9b56886 - _pthread_start"}}, :path "session/", :payload nil, :method :delete, :type :etaoin/http-error, :port 4444, :host "127.0.0.1", :status 404, :driver {:args ["geckodriver" "--port" 4444], :desired-capabilities nil, :process #object[java.lang.UNIXProcess 0x5b2ade46 "java.lang.UNIXProcess@5b2ade46"], :locator "xpath", :type :firefox, :env {}, :port 4444, :host "127.0.0.1", :url "http://127.0.0.1:4444", :session nil}}, compiling:(/private/var/folders/yp/46_n05bs7k18jnxtkvjfzpk40000gn/T/form-init1325635718044467402.clj:1:125)
	at clojure.lang.Compiler.load(Compiler.java:7391)
	at clojure.lang.Compiler.loadFile(Compiler.java:7317)
	at clojure.main$load_script.invokeStatic(main.clj:275)
	at clojure.main$init_opt.invokeStatic(main.clj:277)
	at clojure.main$init_opt.invoke(main.clj:277)
	at clojure.main$initialize.invokeStatic(main.clj:308)
	at clojure.main$null_opt.invokeStatic(main.clj:342)
	at clojure.main$null_opt.invoke(main.clj:339)
	at clojure.main$main.invokeStatic(main.clj:421)
	at clojure.main$main.doInvoke(main.clj:384)
	at clojure.lang.RestFn.invoke(RestFn.java:421)
	at clojure.lang.Var.invoke(Var.java:383)
	at clojure.lang.AFn.applyToHelper(AFn.java:156)
	at clojure.lang.Var.applyTo(Var.java:700)
	at clojure.main.main(main.java:37)
Caused by: clojure.lang.ExceptionInfo: throw+: {:response {:value {:error "unknown command", :message "DELETE /session/ did not match a known command", :stacktrace "stack backtrace:\n   0:        0x103fe3444 - backtrace::backtrace::trace::h9f8fad52a1a8acf1\n   1:        0x103fe37fe - backtrace::capture::Backtrace::new::hbc1a12654c8fdba8\n   2:        0x103f0df4d - webdriver::error::WebDriverError::new::h10bc8992bd1e5ce5\n   3:        0x103eb690d - _$LT$webdriver..httpapi..WebDriverHttpApi$LT$U$GT$$GT$::decode_request::h59625e2271927f5c\n   4:        0x103ef545e - _$LT$webdriver..server..HttpHandler$LT$U$GT$$u20$as$u20$hyper..server..Handler$GT$::handle::h75bcf4a70da4d3d8\n   5:        0x103e35b11 - _$LT$hyper..server..Worker$LT$H$GT$$GT$::keep_alive_loop::h6cffe26f2f997d81\n   6:        0x103e36665 - _$LT$hyper..server..Worker$LT$H$GT$$GT$::handle_connection::ha65f143767ef2701\n   7:        0x103ec24c1 - hyper::server::handle::_$u7b$$u7b$closure$u7d$$u7d$::h021c4837b0330036\n   8:        0x103ec2a5e - hyper::server::listener::spawn_with::_$u7b$$u7b$closure$u7d$$u7d$::h922fff03b485de70\n   9:        0x103f030a7 - _$LT$std..panic..AssertUnwindSafe$LT$F$GT$$u20$as$u20$core..ops..FnOnce$LT$$LP$$RP$$GT$$GT$::call_once::hb095877d82356444\n  10:        0x103e47c86 - std::panicking::try::do_call::h7a395bca958e623c\n  11:        0x10447554a - __rust_maybe_catch_panic\n  12:        0x103e475bc - std::panicking::try::h55d8916b3f7a3515\n  13:        0x103e44e52 - std::panic::catch_unwind::hf1717d8b89ed66e7\n  14:        0x103e46ffc - std::thread::Builder::spawn::_$u7b$$u7b$closure$u7d$$u7d$::hd9c5bf9811bb3b73\n  15:        0x103ea1a4c - _$LT$F$u20$as$u20$alloc..boxed..FnBox$LT$A$GT$$GT$::call_box::h0c4aa35835be38e8\n  16:        0x104471714 - std::sys::imp::thread::Thread::new::thread_start::hae18fa578e305215\n  17:     0x7fffb9b5693a - _pthread_body\n  18:     0x7fffb9b56886 - _pthread_start"}}, :path "session/", :payload nil, :method :delete, :type :etaoin/http-error, :port 4444, :host "127.0.0.1", :status 404, :driver {:args ["geckodriver" "--port" 4444], :desired-capabilities nil, :process #object[java.lang.UNIXProcess 0x5b2ade46 "java.lang.UNIXProcess@5b2ade46"], :locator "xpath", :type :firefox, :env {}, :port 4444, :host "127.0.0.1", :url "http://127.0.0.1:4444", :session nil}} {:response {:value {:error "unknown command", :message "DELETE /session/ did not match a known command", :stacktrace "stack backtrace:\n   0:        0x103fe3444 - backtrace::backtrace::trace::h9f8fad52a1a8acf1\n   1:        0x103fe37fe - backtrace::capture::Backtrace::new::hbc1a12654c8fdba8\n   2:        0x103f0df4d - webdriver::error::WebDriverError::new::h10bc8992bd1e5ce5\n   3:        0x103eb690d - _$LT$webdriver..httpapi..WebDriverHttpApi$LT$U$GT$$GT$::decode_request::h59625e2271927f5c\n   4:        0x103ef545e - _$LT$webdriver..server..HttpHandler$LT$U$GT$$u20$as$u20$hyper..server..Handler$GT$::handle::h75bcf4a70da4d3d8\n   5:        0x103e35b11 - _$LT$hyper..server..Worker$LT$H$GT$$GT$::keep_alive_loop::h6cffe26f2f997d81\n   6:        0x103e36665 - _$LT$hyper..server..Worker$LT$H$GT$$GT$::handle_connection::ha65f143767ef2701\n   7:        0x103ec24c1 - hyper::server::handle::_$u7b$$u7b$closure$u7d$$u7d$::h021c4837b0330036\n   8:        0x103ec2a5e - hyper::server::listener::spawn_with::_$u7b$$u7b$closure$u7d$$u7d$::h922fff03b485de70\n   9:        0x103f030a7 - _$LT$std..panic..AssertUnwindSafe$LT$F$GT$$u20$as$u20$core..ops..FnOnce$LT$$LP$$RP$$GT$$GT$::call_once::hb095877d82356444\n  10:        0x103e47c86 - std::panicking::try::do_call::h7a395bca958e623c\n  11:        0x10447554a - __rust_maybe_catch_panic\n  12:        0x103e475bc - std::panicking::try::h55d8916b3f7a3515\n  13:        0x103e44e52 - std::panic::catch_unwind::hf1717d8b89ed66e7\n  14:        0x103e46ffc - std::thread::Builder::spawn::_$u7b$$u7b$closure$u7d$$u7d$::hd9c5bf9811bb3b73\n  15:        0x103ea1a4c - _$LT$F$u20$as$u20$alloc..boxed..FnBox$LT$A$GT$$GT$::call_box::h0c4aa35835be38e8\n  16:        0x104471714 - std::sys::imp::thread::Thread::new::thread_start::hae18fa578e305215\n  17:     0x7fffb9b5693a - _pthread_body\n  18:     0x7fffb9b56886 - _pthread_start"}}, :path "session/", :payload nil, :method :delete, :type :etaoin/http-error, :port 4444, :host "127.0.0.1", :status 404, :driver {:args ["geckodriver" "--port" 4444], :desired-capabilities nil, :process #object[java.lang.UNIXProcess 0x5b2ade46 "java.lang.UNIXProcess@5b2ade46"], :locator "xpath", :type :firefox, :env {}, :port 4444, :host "127.0.0.1", :url "http://127.0.0.1:4444", :session nil}}
	at slingshot.support$stack_trace.invoke(support.clj:201)
	at etaoin.client$call.invokeStatic(client.clj:97)
	at etaoin.client$call.invoke(client.clj:64)
	at etaoin.api$delete_session.invokeStatic(api.clj:150)
	at etaoin.api$delete_session.invoke(api.clj:148)
	at etaoin.api$disconnect_driver.invokeStatic(api.clj:1954)
	at etaoin.api$disconnect_driver.invoke(api.clj:1948)
	at etaoin.api$quit.invokeStatic(api.clj:1988)
	at etaoin.api$quit.invoke(api.clj:1984)
	at etaoin.api_test$fixture_browsers$fn__7159.invoke(api_test.clj:26)
	at etaoin.api_test$fixture_browsers.invokeStatic(api_test.clj:26)
	at etaoin.api_test$fixture_browsers.invoke(api_test.clj:23)
	at clojure.test$compose_fixtures$fn__7977$fn__7978.invoke(test.clj:693)
	at clojure.test$default_fixture.invokeStatic(test.clj:686)
	at clojure.test$default_fixture.invoke(test.clj:682)
	at clojure.test$compose_fixtures$fn__7977.invoke(test.clj:693)
	at clojure.test$test_vars$fn__8005.invoke(test.clj:734)
	at clojure.test$default_fixture.invokeStatic(test.clj:686)
	at clojure.test$default_fixture.invoke(test.clj:682)
	at clojure.test$test_vars.invokeStatic(test.clj:730)
	at clojure.test$test_all_vars.invokeStatic(test.clj:736)
	at clojure.test$test_ns.invokeStatic(test.clj:757)
	at clojure.test$test_ns.invoke(test.clj:742)
	at clojure.core$map$fn__4785.invoke(core.clj:2646)
	at clojure.lang.LazySeq.sval(LazySeq.java:40)
	at clojure.lang.LazySeq.seq(LazySeq.java:49)
	at clojure.lang.Cons.next(Cons.java:39)
	at clojure.lang.RT.boundedLength(RT.java:1749)
	at clojure.lang.RestFn.applyTo(RestFn.java:130)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.test$run_tests.invokeStatic(test.clj:767)
	at clojure.test$run_tests.doInvoke(test.clj:767)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:646)
	at clojure.core$apply.invoke(core.clj:641)
	at user$eval3200$fn__3263$fn__3294.invoke(form-init1325635718044467402.clj:1)
	at user$eval3200$fn__3263$fn__3264.invoke(form-init1325635718044467402.clj:1)
	at user$eval3200$fn__3263.invoke(form-init1325635718044467402.clj:1)
	at user$eval3200.invokeStatic(form-init1325635718044467402.clj:1)
	at user$eval3200.invoke(form-init1325635718044467402.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:6927)
	at clojure.lang.Compiler.eval(Compiler.java:6917)
	at clojure.lang.Compiler.load(Compiler.java:7379)
	... 14 more
Tests failed.

add window size argument

that might help

(defn prepend [seq x]
  (cons x seq))

(defn make-url
  "Makes an Webdriver URL from a host and port."
  [host port]
  (format "http://%s:%s" host port))

(defn set-path
  [driver path]
  (update driver :args prepend path))

(defn set-args
  [driver args]
  (update driver :args concat args))

(defmulti set-port
  "Updates driver's map with the given port added to the args.

  Note: it takes not an atom but pure map to be used with swap!.
  Our further goal is to reduce atom usage everywhere it is possible."
  {:arglists '([driver port])}
  (fn [driver & _] (:type driver)))

(defmethods set-port
  [:firefox :safari]
  [driver port]
  (set-args driver ["--port" port]))

(defmethods set-port
  [:chrome :headless]
  [driver port]
  (set-args driver [(str "--port=" port)]))

(defmethod set-port
  :phantom
  [driver port]
  (set-args driver ["--webdriver" port]))

Update API Docs

The API docs that are hosted at http://grishaev.me are out of date. Notably they don't have entries for any of the wait functions like wait-visible, etc.

Filling input seems to be broken with :firefox

Environment: MacOS 10.13.1, geckodriver 0.19.1.
Reproduced with Firefox 56.0.2 (stable), Firefox 57.0 (Beta), Firefox Nightly 58.0a1 (2017-11-12).

I can't reproduce this bug with :chrome and :phantom.

Steps to reproduce:

  1. Use :firefox driver.
  2. Try to fill the input:
(e/fill d {:id :username} "foobar")

Expected: no errors.

Actual:

ERROR in (my-test) (support.clj:201)
Uncaught exception, not in assertion.
expected: nil
  actual: clojure.lang.ExceptionInfo: throw+: {:response {:value {:error "invalid argument", :message "Missing 'text' parameter", :stacktrace "stack backtrace:\n   0:        0x101c90804 - backtrace::backtrace::trace::hf4fb9cb16c5cd3d5\n   1:        0x101c90bdf - backtrace::capture::Backtrace::new::h109fc438e1b250fa\n   2:        0x1019c824c - webdriver::error::WebDriverError::new::h55f28d494fb52985\n   3:        0x1019bb1a2 - _$LT$webdriver..command..SendKeysParameters$u20$as$u20$webdriver..command..Parameters$GT$::from_json::hcec9d3aa53ea79d9\n   4:        0x1018dd477 - _$LT$webdriver..command..WebDriverMessage$LT$U$GT$$GT$::from_http::hf39003ef8bb4fef9\n   5:        0x1018e0179 - _$LT$webdriver..httpapi..WebDriverHttpApi$LT$U$GT$$GT$::decode_request::h22eca87d078a6dc4\n   6:        0x10192ac07 - _$LT$webdriver..server..HttpHandler$LT$U$GT$$u20$as$u20$hyper..server..Handler$GT$::handle::h2b535e3cc76d3f78\n   7:        0x101844078 - _$LT$hyper..server..Worker$LT$H$GT$$GT$::keep_alive_loop::h45f7f92b91973f2b\n   8:        0x101844f21 - _$LT$hyper..server..Worker$LT$H$GT$$GT$::handle_connection::h5ee65c07c0fed43f\n   9:        0x1018fa1d7 - hyper::server::handle::_$u7b$$u7b$closure$u7d$$u7d$::h3fe31a4cf8d7cf04\n  10:        0x1018fa75d - hyper::server::listener::spawn_with::_$u7b$$u7b$closure$u7d$$u7d$::hf33d4e4ab2936cf9\n  11:        0x1018479f7 - std::sys_common::backtrace::__rust_begin_short_backtrace::h024931f7d16d4d84\n  12:        0x101858cbd - std::thread::Builder::spawn::_$u7b$$u7b$closure$u7d$$u7d$::_$u7b$$u7b$closure$u7d$$u7d$::h4f89971b92f1c7a8\n  13:        0x101808ca7 - _$LT$std..panic..AssertUnwindSafe$LT$F$GT$$u20$as$u20$core..ops..function..FnOnce$LT$$LP$$RP$$GT$$GT$::call_once::h0f3b12cd61063ffe\n  14:        0x10185994f - std::panicking::try::do_call::he35b3e04abf19639\n  15:        0x101f9cc8c - __rust_maybe_catch_panic\n  16:        0x1018592fc - std::panicking::try::h6891a1b8bc2b2efc\n  17:        0x1018566d2 - std::panic::catch_unwind::hf8158f85675cb440\n  18:        0x1018586cc - std::thread::Builder::spawn::_$u7b$$u7b$closure$u7d$$u7d$::h7ca9b1c3c82280dd\n  19:        0x1018c5da1 - _$LT$F$u20$as$u20$alloc..boxed..FnBox$LT$A$GT$$GT$::call_box::h5a6b04902633fc56\n  20:        0x101f98e7b - std::sys::imp::thread::Thread::new::thread_start::h65773f24735ca5ff\n  21:     0x7fff5d50e6c0 - _pthread_body\n  22:     0x7fff5d50e56c - _pthread_start"}}, :path "session/bb454ccc-15ec-c646-89dd-c2958435f8ce/element/7533efe0-0cd1-6e49-a3f2-5cbbeaabf963/value", :payload {:value ["f" "o" "o" "b" "a" "r"]}, :method :post, :type :etaoin/http-error, :port 29983, :host "127.0.0.1", :status 400, :driver {:args ("geckodriver" "--port" 29983), :capabilities {:loggingPrefs {:browser "INFO"}}, :process #object[java.lang.UNIXProcess 0x77c66ac2 "java.lang.UNIXProcess@77c66ac2"], :locator "xpath", :type :firefox, :env nil, :port 29983, :host "127.0.0.1", :url "http://127.0.0.1:29983", :session "bb454ccc-15ec-c646-89dd-c2958435f8ce"}}
 at slingshot.support$stack_trace.invoke (support.clj:201)
    etaoin.client$call.invokeStatic (client.clj:97)
    etaoin.client$call.invoke (client.clj:64)
    etaoin.api$fill_el.invokeStatic (api.clj:1822)
    etaoin.api$fill_el.doInvoke (api.clj:1819)
    clojure.lang.RestFn.invoke (RestFn.java:445)
    clojure.lang.AFn.applyToHelper (AFn.java:160)
    clojure.lang.RestFn.applyTo (RestFn.java:132)
    clojure.core$apply.invokeStatic (core.clj:663)
    clojure.core$apply.invoke (core.clj:652)
    etaoin.api$fill.invokeStatic (api.clj:1835)
    etaoin.api$fill.doInvoke (api.clj:1826)
    clojure.lang.RestFn.invoke (RestFn.java:445)
    e2e.auth$fn__12204$fn__12205.invoke (auth.clj:25)
...

selenium in docker, can not create session

Hi,

I would like to use etaoin 0.1.6 with a selenium in docker:
docker run -p 4444:4444 selenium/standalone-firefox:3.4.0-chromium

I am trying to connect the following way:

(require '[etaoin.api :exclude [wait] :refer :all])
(def driver (connect-driver (create-driver :firefox {:port 4444})))

Here an exception is thrown since the create-session function calls to http://localhost:4444/sessions (page does not exist). I think the correct url would be http://localhost:4444/wd/hub/session.

Driver start hangs if port is in use

If the port used for communication with the webdriver is already in use by another process, starting a driver blocks forever.

Steps to reproduce

Use [etaoin "0.1.5"], which uses 5555 as the default port for chromedriver

Start a server process listening on port 5555. One way to do this is to start Clojure with the Java command-line option:

"-Dclojure.server.repl={:port 5555 :accept clojure.core.server/repl}"

Alternatively, Netcat can run a server like this:

nc -kl 127.0.0.1 5555

At the Clojure REPL, run:

(require '[etaoin.api :as e])
(def c (e/chrome))

The REPL blocks and does not return.

Notes

Running Netcat, I can see the HTTP request to start the webdriver session:

$ nc -kl 127.0.0.1 5555
POST /session HTTP/1.1
Connection: close
content-type: application/json
accept: application/json
accept-encoding: gzip, deflate
Content-Length: 26
Host: 127.0.0.1:5555
User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_92)

{"desiredCapabilities":{}}

If I kill the Netcat process, org.apache.http.NoHttpResponseException is thrown in the REPL. Otherwise, the REPL just hangs forever.

Possible resolutions

Adding a timeout on the HTTP response would at least provide some indication of a problem, although it would not necessarily help with diagnosis.

For greater robustness, it would be ideal to find an unused port.

Add driver version support

Когда-то у меня была мысль сделать поддержку разных версий драйверов в библиотеке, но я не осилил. Может, получится сейчас? Но сначала нужно подумать, нужно ли нам это вообще.

Идея в следующем: есть наша библотека и драйвер X. Со временем драйвер обновляется, что что-то перестает работать. Мы исправляем код под новый драйвер и выпускаем релиз. Тому, кто пожаловался, говорим -- скачайте новый брайвер и обновите библиотеку.

Так нормально, но было бы круто, если ли бы библиотка помнила прошлые реализации. Например, до версии драйвера 1.3 было так, а позже стало эдак. Тогда библиотеке не обящательно нужны свежие драйверы.

Для этого нужно, чтобы при старте мы определяли версию драйвера, например, 1.25. Это можно сделать, вызвав драйвер с ключом --version. Далее мы ханим версию словаре.

Method Names!

In Clojure you usually use '!' at the end of impure function names that change the state of an object. In this case, many of the functions change the state of a driver. Eg. (go! driver url) instead of (go driver url)
Your project is very similar to something I've been working on. May switch to this after further research. But I thought I'd mention the naming thing regarding driver mutating functions.

You are probably much more experienced in Clojure than me so correct me if I'm wrong.

headless fix

headless should be not a separated driver but just a predicate since more and more drivers support headless mode.

wrap elements when executing js

(js-execute d "return arguments[0].scrollIntoView()" {:ELEMENT :d054b533-3e07-6248-b030-b8c237853d8e :element-6066-11e4-a52e-4f735466cecf :d054b533-3e07-6248-b030-b8c237853d8e})

How to upload file using etaoin?

Hi @igrishaev

I was trying out this library ( thanks for this pure clojure solution btw 👍 ) and to evaluate it, I wanted to translate this tutorial which relies on selenium

(ns ui-test.file-upload
  (:use etaoin.api)
  (:require [clojure.test :as test]
            [etaoin.keys :as k]))

(def driver (chrome)) ;; here, a Firefox window should appear

;; let's perform a quick Wiki session
(go driver "http://demo.guru99.com/selenium/upload/")



(click driver {:id "terms" })



(click driver {:id "uploadfile_0" })



(fill {:id "uploadfile_0" } "")

(fill driver {:id "uploadfile_0"} )

I'm new to browser automation and clojure, could you help me out a bit please :)

Cljs-repl

How can I piggieback onto a driver instance and get a Cljs REPL connected to it?

Turn a string to a vector manually

`keys` is supposed to be a string, which is then converted to a vector ?


[12:01] 
That is what caused the bug with an older version of cheshire, but a newer version of cheshire can encode a vector of java.lang.Chars


igrishaev [12:02 PM] 
@borkdude I know it looks strange, but still, webdriver requires an array of 1-symbol strings.


[12:03] 
I see now. Probably, there might be `(mapv str some-sting)`


borkdude [12:03 PM] 
hmm, but it’s first encoded to json right? aaah ok


igrishaev [12:03 PM] 
instead of just vec


borkdude [12:03 PM] 
I see what happens now: `(cheshire.core/generate-string (vec "foo")) "[\"f\",\"o\",\"o\"]"`


[12:04] 
didn’t know webdriver required this, but now I understand. sorry for the noise


igrishaev [12:04 PM] 
webriver takes something like `{"text": ["h", "e", "l", "l", "o"]}`


[12:04] 
no worries, that’s a good point to prevent an error (edited)


[12:04] 
I’ll create an issue for that (edited)

fix todo when passing env

Функции для запуска драйвера принимают словарь переменных :env для процесса драйвера. Проблема в том, что при явной передаче переменных не учитываются текущие переменные. Поэтому надо не просто передавать их в ProcessBuilder, а сначала взять текущие через System/getenv и дополнить результат теми, что передали в :env, и только потом передать в билдер.

Можно взять способ как в книжке: прочитать переменные, перевести их в кложурную мапу, потом сделать merge. Кложурные мапы имплементят интерфейс Map, поэтому их можно передать как есть. Но ключи обязательно должны быть строками (и значения тоже). Кейворды в ключах перевести через name, а значения через str.

Убрать todo возле env в коде. Добавить в readme.

Interacting with select controls

What's the officially recommended way of doing this? I'd like to select a particular value from a HTML select element. As first try I could do this with first clicking the select and the clicking the option, however, my controls are not working the same way when it happens with human interaction (seemingly some javascript events are not fired properly?!)

Wiki?

Create a wiki; move code samples from readme there. Create troubleshooting section.

Pass homepage to Chrome

Chrome capabilities spec: https://sites.google.com/a/chromium.org/chromedriver/capabilities
List of Chrome arguments: http://peter.sh/experiments/chromium-command-line-switches

What I've tried for far:

(def cap 
  {:chromeOptions
   {:binary "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary"
    :args ["homepage=http://google.com" "--incognito"]}})

;; or just simply

(def cap {:chromeOptions {:args ["--homepage=gmail.com"]}})

and then

(def driver (chrome {:desired-capabilities cap}))

it does not have any effect on default URL.

One interesting fact, it seems that chromedriver accepts only single arguments, for example:

(def cap {:chromeOptions {:args ["--incognito"]}})

really opens chrome in incognito mode.

The task is to investigate why key-value pairs separated with = are ignored.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.