From 16136c3ceeffc08cc32e3776f73b42e28a2ae5a4 Mon Sep 17 00:00:00 2001 From: Adam Jackman Date: Wed, 3 Aug 2016 12:53:43 -0400 Subject: [PATCH 1/2] First major set of updates: Remove list of sessions per process, remove session name concept to simplify and only use IDs, Expose active sessions --- lib/hound.ex | 2 +- lib/hound/helpers/session.ex | 61 ++++++----------- lib/hound/session_server.ex | 93 +++++++------------------- mix.lock | 18 ++--- test/multiple_browser_session_test.exs | 22 +++--- 5 files changed, 66 insertions(+), 130 deletions(-) diff --git a/lib/hound.ex b/lib/hound.ex index 535535c..657b937 100644 --- a/lib/hound.ex +++ b/lib/hound.ex @@ -26,7 +26,7 @@ defmodule Hound do @doc "See `Hound.Helpers.Session.end_session/1`" defdelegate end_session, to: Hound.Helpers.Session - defdelegate end_session(pid), to: Hound.Helpers.Session + defdelegate end_session(session_id), to: Hound.Helpers.Session @doc false defdelegate current_session_id, to: Hound.Helpers.Session diff --git a/lib/hound/helpers/session.ex b/lib/hound/helpers/session.ex index 037b837..40edebc 100644 --- a/lib/hound/helpers/session.ex +++ b/lib/hound/helpers/session.ex @@ -2,48 +2,28 @@ defmodule Hound.Helpers.Session do @moduledoc "Session helpers" @doc """ - Switches to another session. - - When you need more than one browser session, use this function switch to another session. - If the session doesn't exist it a new one will be created for you. + Switches to another session when you need more than one browser session. All further commands will then run in the session you switched to. - - # Pass any name to the session to refer to it later. - change_session_to("random-session") - - The name can be an atom or a string. The default session created is called `:default`. - """ - def change_session_to(session_name, opts \\ []) do - Hound.SessionServer.change_current_session_for_pid(self, session_name, opts) - end - - - @doc """ - When running multiple browser sessions, calling this function will switch to the default browser session. - - change_to_default_session - - # is the same as calling - change_session_to(:default) + Exits if session does not exist. """ - def change_to_default_session do - change_session_to(:default) + def change_session_to(session_id) do + Hound.SessionServer.change_current_session_for_pid(self, session_id) end @doc """ Execute commands in a seperate browser session. - in_browser_session "another_user", fn -> + perform_in_session "another_user", fn -> navigate_to "http://example.com" click({:id, "announcement"}) end """ - def in_browser_session(session_name, func) do - previous_session_name = current_session_name - change_session_to(session_name) + def perform_in_session(session_id, func) do + previous_session_id = current_session_id + change_session_to(session_id) result = apply(func, []) - change_session_to(previous_session_name) + change_session_to(previous_session_id) result end @@ -94,19 +74,23 @@ defmodule Hound.Helpers.Session do * `:driver` - The additional capabilities to be passed directly to the webdriver. """ def start_session(opts \\ []) do - Hound.SessionServer.session_for_pid(self, opts) + Hound.SessionServer.start_session_for_pid(self, opts) end @doc """ - Ends a Hound session that is associated with a pid. - - If you have multiple sessions, all of those sessions are killed. + Ends a session that is associated with a session_id. """ - def end_session(pid \\ self()) do - Hound.SessionServer.destroy_sessions_for_pid(pid) + def end_session(session_id) do + Hound.Session.destroy_session(session_id) end + @doc """ + Ends the current session for the process + """ + def end_session() do + Hound.SessionServer.destroy_session(self) + end @doc false def current_session_id do @@ -115,11 +99,4 @@ defmodule Hound.Helpers.Session do end - @doc false - def current_session_name do - Hound.SessionServer.current_session_name(self) || - raise "could not find a session for process #{inspect self}" - - - end end diff --git a/lib/hound/session_server.ex b/lib/hound/session_server.ex index 2de7a38..80198bc 100644 --- a/lib/hound/session_server.ex +++ b/lib/hound/session_server.ex @@ -8,98 +8,51 @@ defmodule Hound.SessionServer do GenServer.start_link(__MODULE__, %{}, name: @name) end - - def session_for_pid(pid, opts) do - current_session_id(pid) || - change_current_session_for_pid(pid, :default, opts) + def start_session_for_pid(pid, opts) do + GenServer.call(@name, {:start_session, pid, opts}, 60000) end - def current_session_id(pid) do - case :ets.lookup(@name, pid) do - [{^pid, _ref, session_id, _all_sessions}] -> session_id - [] -> nil - end - end - - def current_session_name(pid) do - case :ets.lookup(@name, pid) do - [{^pid, _ref, session_id, all_sessions}] -> - Enum.find_value all_sessions, fn - {name, id} when id == session_id -> name - _ -> nil - end - [] -> nil - end + GenServer.call(@name, {:current_session, pid}, 60000) end - - def change_current_session_for_pid(pid, session_name, opts) do - GenServer.call(@name, {:change_session, pid, session_name, opts}, 60000) - end - - - def all_sessions_for_pid(pid) do - case :ets.lookup(@name, pid) do - [{^pid, _ref, _session_id, all_sessions}] -> all_sessions - [] -> %{} - end + def change_current_session_for_pid(pid, session_id) do + GenServer.call(@name, {:change_session, pid, session_id}, 60000) end - - def destroy_sessions_for_pid(pid) do - GenServer.call(@name, {:destroy_sessions, pid}, 60000) + def destroy_session_for_pid(pid) do + GenServer.cast(@name, {:destroy_session, pid}, 60000) end ## Callbacks def init(state) do - :ets.new(@name, [:set, :named_table, :protected, read_concurrency: true]) {:ok, state} end - - def handle_call({:change_session, pid, session_name, opts}, _from, state) do + def handle_call({:start_session, pid, opts}, _from, state) do {:ok, driver_info} = Hound.driver_info - - {ref, sessions} = - case :ets.lookup(@name, pid) do - [{^pid, ref, _session_id, sessions}] -> - {ref, sessions} - [] -> - {Process.monitor(pid), %{}} - end - - {session_id, sessions} = - case Map.fetch(sessions, session_name) do - {:ok, session_id} -> - {session_id, sessions} - :error -> - {:ok, session_id} = Hound.Session.create_session(driver_info[:browser], opts) - {session_id, Map.put(sessions, session_name, session_id)} - end - - :ets.insert(@name, {pid, ref, session_id, sessions}) - {:reply, session_id, Map.put(state, ref, pid)} + {:ok, session_id} = Hound.Session.create_session(driver_info[:browser], opts) + state = Map.put(state, pid, session_id) + {:reply, session_id, state} end - def handle_call({:destroy_sessions, pid}, _from, state) do - destroy_sessions(pid) - {:reply, :ok, state} + def handle_call({:current_session, pid}, _from, state) do + state[pid] end - def handle_info({:DOWN, ref, _, _, _}, state) do - if pid = state[ref] do - destroy_sessions(pid) + def handle_call({:change_session, pid, session_id}, _from, state) do + if (!Enum.member?(Hound.Session.active_sessions(), session_id)) do + raise "Error: Session id does not exist" end - {:noreply, state} + state = Map.put(state, pid, session_id) + {:reply, session_id, state} end - defp destroy_sessions(pid) do - sessions = all_sessions_for_pid(pid) - :ets.delete(@name, pid) - Enum.each sessions, fn({_session_name, session_id})-> - Hound.Session.destroy_session(session_id) - end + def handle_cast({:destroy_session, pid}, _from, state) do + Hound.Session.destroy_session(state[pid]) + state = Map.put(state, pid, []) + {:noreply, state} end + end diff --git a/mix.lock b/mix.lock index 22cc65f..c2e5c9c 100644 --- a/mix.lock +++ b/mix.lock @@ -1,9 +1,9 @@ -%{"certifi": {:hex, :certifi, "0.4.0", "a7966efb868b179023618d29a407548f70c52466bf1849b9e8ebd0e34b7ea11f", [:rebar3], []}, - "earmark": {:hex, :earmark, "0.2.1", "ba6d26ceb16106d069b289df66751734802777a3cbb6787026dd800ffeb850f3", [:mix], []}, - "ex_doc": {:hex, :ex_doc, "0.11.3", "bb16cb3f4135d880ce25279dc19a9d70802bc4f4942f0c3de9e4862517ae3ace", [:mix], [{:earmark, "~> 0.1.17 or ~> 0.2", [hex: :earmark, optional: true]}]}, - "hackney": {:hex, :hackney, "1.6.1", "ddd22d42db2b50e6a155439c8811b8f6df61a4395de10509714ad2751c6da817", [:rebar3], [{:certifi, "0.4.0", [hex: :certifi, optional: false]}, {:idna, "1.2.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.0", [hex: :ssl_verify_fun, optional: false]}]}, - "idna": {:hex, :idna, "1.2.0", "ac62ee99da068f43c50dc69acf700e03a62a348360126260e87f2b54eced86b2", [:rebar3], []}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, - "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, - "poison": {:hex, :poison, "1.5.2", "560bdfb7449e3ddd23a096929fb9fc2122f709bcc758b2d5d5a5c7d0ea848910", [:mix], []}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.0", "edee20847c42e379bf91261db474ffbe373f8acb56e9079acb6038d4e0bf414f", [:rebar, :make], []}} +%{"certifi": {:hex, :certifi, "0.4.0"}, + "earmark": {:hex, :earmark, "0.2.1"}, + "ex_doc": {:hex, :ex_doc, "0.11.3"}, + "hackney": {:hex, :hackney, "1.6.1"}, + "idna": {:hex, :idna, "1.2.0"}, + "metrics": {:hex, :metrics, "1.0.1"}, + "mimerl": {:hex, :mimerl, "1.0.2"}, + "poison": {:hex, :poison, "1.5.2"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.0"}} diff --git a/test/multiple_browser_session_test.exs b/test/multiple_browser_session_test.exs index d528b11..257a358 100644 --- a/test/multiple_browser_session_test.exs +++ b/test/multiple_browser_session_test.exs @@ -5,6 +5,7 @@ defmodule MultipleBrowserSessionTest do hound_session test "should be able to run multiple sessions" do + first_session = Hound.start_session url1 = "http://localhost:9090/page1.html" url2 = "http://localhost:9090/page2.html" @@ -12,14 +13,14 @@ defmodule MultipleBrowserSessionTest do navigate_to(url1) # Change to another session - change_session_to :another_session + Hound.start_session # Navigate to a url in the second session navigate_to(url2) # Then assert url assert url2 == current_url - # Now go back to the default session - change_to_default_session + # Now go back to the original session + change_session_to first_session # Assert if the url is the one we visited assert url1 == current_url end @@ -33,7 +34,8 @@ defmodule MultipleBrowserSessionTest do navigate_to(url1) # In another session... - in_browser_session :another_session, fn-> + another_session = Hound.start_session + perform_in_session another_session, fn-> navigate_to(url2) assert url2 == current_url end @@ -43,6 +45,7 @@ defmodule MultipleBrowserSessionTest do end test "should preserve session after using in_browser_session" do + default = Hound.start_session url1 = "http://localhost:9090/page1.html" url2 = "http://localhost:9090/page2.html" url3 = "http://localhost:9090/page3.html" @@ -51,11 +54,13 @@ defmodule MultipleBrowserSessionTest do navigate_to(url1) # Change to a second session and navigate to url2 - change_session_to :session_a + session_a = Hound.start_session + change_session_to session_a navigate_to(url2) # In a third session... - in_browser_session :session_b, fn -> + session_b = Hound.start_session + perform_in_session session_b, fn -> navigate_to(url3) assert url3 == current_url end @@ -64,7 +69,7 @@ defmodule MultipleBrowserSessionTest do assert url2 == current_url # Switch back to the default session - change_session_to :default + change_session_to default # Assert the current url is the one we visited in the default session assert url1 == current_url @@ -74,8 +79,9 @@ defmodule MultipleBrowserSessionTest do url1 = "http://localhost:9090/page1.html" # In another session, navigate to url1 and return the current url + another_session = Hound.start_session result = - in_browser_session :another_session, fn -> + perform_in_session another_session, fn -> navigate_to(url1) current_url end From 0dd80573ede94bdef58ce70da618175c04100fc2 Mon Sep 17 00:00:00 2001 From: Adam Jackman Date: Thu, 4 Aug 2016 23:01:37 -0400 Subject: [PATCH 2/2] Bugfixes --- lib/hound/helpers/session.ex | 8 +++++++- lib/hound/session_server.ex | 11 ++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/hound/helpers/session.ex b/lib/hound/helpers/session.ex index 40edebc..f442821 100644 --- a/lib/hound/helpers/session.ex +++ b/lib/hound/helpers/session.ex @@ -89,7 +89,7 @@ defmodule Hound.Helpers.Session do Ends the current session for the process """ def end_session() do - Hound.SessionServer.destroy_session(self) + Hound.SessionServer.destroy_session_for_pid(self) end @doc false @@ -98,5 +98,11 @@ defmodule Hound.Helpers.Session do raise "could not find a session for process #{inspect self}" end + @doc """ + Get list of active sessions + """ + def active_sessions() do + Hound.Session.active_sessions() + end end diff --git a/lib/hound/session_server.ex b/lib/hound/session_server.ex index 80198bc..01f524a 100644 --- a/lib/hound/session_server.ex +++ b/lib/hound/session_server.ex @@ -21,9 +21,10 @@ defmodule Hound.SessionServer do end def destroy_session_for_pid(pid) do - GenServer.cast(@name, {:destroy_session, pid}, 60000) + GenServer.call(@name, {:destroy_session, pid}, 60000) end + ## Callbacks def init(state) do @@ -38,21 +39,21 @@ defmodule Hound.SessionServer do end def handle_call({:current_session, pid}, _from, state) do - state[pid] + {:reply, state[pid], state} end def handle_call({:change_session, pid, session_id}, _from, state) do - if (!Enum.member?(Hound.Session.active_sessions(), session_id)) do + if (!Enum.any?(Hound.Session.active_sessions(), fn(session) -> session["id"] == session_id end)) do raise "Error: Session id does not exist" end state = Map.put(state, pid, session_id) {:reply, session_id, state} end - def handle_cast({:destroy_session, pid}, _from, state) do + def handle_call({:destroy_session, pid}, _from, state) do Hound.Session.destroy_session(state[pid]) state = Map.put(state, pid, []) - {:noreply, state} + {:reply, :ok, state} end end