From aeb32aed2cdc98d1a4113158be3b88ea2997df2c Mon Sep 17 00:00:00 2001 From: Greg V Date: Wed, 10 Apr 2019 22:25:10 +0300 Subject: [PATCH 1/4] Use inspect when raising unknown driver error --- lib/hound/request_utils.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hound/request_utils.ex b/lib/hound/request_utils.ex index 11726eb..8799939 100644 --- a/lib/hound/request_utils.ex +++ b/lib/hound/request_utils.ex @@ -75,7 +75,7 @@ defmodule Hound.RequestUtils do Hound.ResponseParsers.PhantomJs other_driver -> - raise "No response parser found for #{other_driver}" + raise "No response parser found for #{inspect other_driver}" end end From 7f13484a01ff3b4425286c13cf10acd6962acf62 Mon Sep 17 00:00:00 2001 From: Greg V Date: Wed, 10 Apr 2019 22:26:56 +0300 Subject: [PATCH 2/4] response_parser: support responses with only the "value" key Modern implementations like geckodriver do not send status and always send value --- lib/hound/response_parser.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/hound/response_parser.ex b/lib/hound/response_parser.ex index 3ca06d9..73edd8a 100644 --- a/lib/hound/response_parser.ex +++ b/lib/hound/response_parser.ex @@ -43,6 +43,9 @@ defmodule Hound.ResponseParser do def handle_response(_mod, "session", code, %{"sessionId" => session_id}) when code < 300 do {:ok, session_id} end + def handle_response(_mod, "session", code, %{"value" => %{"sessionId" => session_id}}) when code < 300 do + {:ok, session_id} + end def handle_response(mod, _path, _code, %{"value" => %{"message" => message} = value}) do if mod.warning?(message) do Logger.warn(message) @@ -51,7 +54,7 @@ defmodule Hound.ResponseParser do mod.handle_error(value) end end - def handle_response(_mod, _path, _code, %{"status" => 0, "value" => value}), do: value + def handle_response(_mod, _path, _code, %{"value" => value}), do: value def handle_response(_mod, _path, code, _body) when code < 400, do: :ok def handle_response(_mod, _path, _code, _body), do: :error From 19ad8b0fbe6e47b50d77e7c66d9592418df2dbc7 Mon Sep 17 00:00:00 2001 From: Greg V Date: Wed, 10 Apr 2019 22:31:34 +0300 Subject: [PATCH 3/4] Add geckodriver support (response parser, config) --- README.md | 2 +- lib/hound/connection_server.ex | 2 ++ lib/hound/internal_helpers.ex | 1 + lib/hound/request_utils.ex | 3 +++ lib/hound/response_parsers/geckodriver.ex | 9 +++++++++ 5 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 lib/hound/response_parsers/geckodriver.ex diff --git a/README.md b/README.md index 9827ec1..aad8764 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ For browser automation and writing integration tests in Elixir. * Can run __multiple browser sessions__ simultaneously. [See example](https://github.com/HashNuke/hound/blob/master/test/multiple_browser_session_test.exs). -* Supports Selenium (Firefox, Chrome), ChromeDriver and PhantomJs. +* Supports Selenium (Firefox, Chrome), ChromeDriver, geckodriver (note: [only supports a single session for now](https://github.com/mozilla/geckodriver/issues/882)) and PhantomJs. * Supports Javascript-heavy apps. Retries a few times before reporting error. diff --git a/lib/hound/connection_server.ex b/lib/hound/connection_server.ex index 8d9c2ff..b71831b 100644 --- a/lib/hound/connection_server.ex +++ b/lib/hound/connection_server.ex @@ -9,6 +9,8 @@ defmodule Hound.ConnectionServer do {9515, nil, "chrome"} "phantomjs" -> {8910, nil, "phantomjs"} + "geckodriver" -> + {4444, nil, "firefox"} _ -> # assume selenium {4444, "wd/hub/", "firefox"} end diff --git a/lib/hound/internal_helpers.ex b/lib/hound/internal_helpers.ex index f3b1194..9b36f45 100644 --- a/lib/hound/internal_helpers.ex +++ b/lib/hound/internal_helpers.ex @@ -11,6 +11,7 @@ defmodule Hound.InternalHelpers do "focus_parent_frame" ], chrome_driver: [], + geckodriver: [], selenium: [] ] diff --git a/lib/hound/request_utils.ex b/lib/hound/request_utils.ex index 8799939..78a273f 100644 --- a/lib/hound/request_utils.ex +++ b/lib/hound/request_utils.ex @@ -71,6 +71,9 @@ defmodule Hound.RequestUtils do {"chrome_driver", _} -> Hound.ResponseParsers.ChromeDriver + {"geckodriver", _} -> + Hound.ResponseParsers.GeckoDriver + {"phantomjs", _} -> Hound.ResponseParsers.PhantomJs diff --git a/lib/hound/response_parsers/geckodriver.ex b/lib/hound/response_parsers/geckodriver.ex new file mode 100644 index 0000000..742d3ec --- /dev/null +++ b/lib/hound/response_parsers/geckodriver.ex @@ -0,0 +1,9 @@ +defmodule Hound.ResponseParsers.GeckoDriver do + @moduledoc false + + use Hound.ResponseParser + + def handle_error(%{"message" => "Unable to locate element:" <> _rest}) do + {:error, :no_such_element} + end +end From 9277401c86adc4f62c14a91b42b21c4460fb9aac Mon Sep 17 00:00:00 2001 From: Greg V Date: Wed, 10 Apr 2019 22:53:43 +0300 Subject: [PATCH 4/4] innerHTML/innerText/outerHTML are properties, not attributes --- lib/hound/helpers/element.ex | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/hound/helpers/element.ex b/lib/hound/helpers/element.ex index 8dea046..467f5ed 100644 --- a/lib/hound/helpers/element.ex +++ b/lib/hound/helpers/element.ex @@ -23,18 +23,18 @@ defmodule Hound.Helpers.Element do @spec inner_html(Hound.Element.selector) :: String.t def inner_html(element) do - attribute_value(element, "innerHTML") + property_value(element, "innerHTML") end @spec inner_text(Hound.Element.selector) :: String.t def inner_text(element) do - attribute_value(element, "innerText") + property_value(element, "innerText") end - + @spec outer_html(Hound.Element.selector) :: String.t def outer_html(element) do - attribute_value(element, "outerHTML") + property_value(element, "outerHTML") end @doc """ @@ -166,6 +166,24 @@ defmodule Hound.Helpers.Element do end + @doc """ + Gets an element's property value. + + element = find_element(:name, "example") + property_value(element, "innerHTML") + + You can also pass the selector as a tuple, for the first argument + + property_value({:name, "example"}, "innerHTML") + """ + @spec property_value(Hound.Element.selector, String.t) :: String.t | :nil + def property_value(element, property_name) do + element = get_element(element) + session_id = Hound.current_session_id + make_req(:get, "session/#{session_id}/element/#{element}/property/#{property_name}") + end + + @doc """ Checks if an element has a given class.