From f779aee549edf8152b862f3175faa18a378725af Mon Sep 17 00:00:00 2001 From: Matthew Erhard Date: Mon, 3 Oct 2022 22:30:44 -0400 Subject: [PATCH] Allow setting of optional cookie attributes (#711) Add a `set_cookie/4` function to permit the setting of optional cookie attributes per the [WebDriver specification](https://www.w3.org/TR/webdriver1/#cookies). --- .../cases/browser/cookies_test.exs | 38 +++++++++++++++++- lib/wallaby/browser.ex | 4 +- lib/wallaby/chrome.ex | 5 ++- lib/wallaby/driver.ex | 2 + lib/wallaby/exceptions.ex | 2 +- lib/wallaby/selenium.ex | 4 +- lib/wallaby/webdriver_client.ex | 20 ++++++++-- .../selenium/webdriver_client_test.exs | 40 +++++++++++++++++++ 8 files changed, 105 insertions(+), 10 deletions(-) diff --git a/integration_test/cases/browser/cookies_test.exs b/integration_test/cases/browser/cookies_test.exs index eef856ed..0cf6ce6b 100644 --- a/integration_test/cases/browser/cookies_test.exs +++ b/integration_test/cases/browser/cookies_test.exs @@ -20,12 +20,15 @@ defmodule Wallaby.Integration.Browser.CookiesTest do session |> visit("/") |> Browser.set_cookie("api_token", "abc123") - |> visit("/") + |> visit("/index.html") |> Browser.cookies() |> hd() assert cookie["name"] == "api_token" assert cookie["value"] == "abc123" + assert cookie["path"] == "/" + assert cookie["secure"] == false + assert cookie["httpOnly"] == false end test "without visiting a page first throws an error", %{session: session} do @@ -35,4 +38,37 @@ defmodule Wallaby.Integration.Browser.CookiesTest do end end end + + describe "set_cookie/4" do + test "sets a cookie in the browser", %{session: session} do + expiry = DateTime.utc_now() |> DateTime.to_unix() |> Kernel.+(1000) + + cookie = + session + |> visit("/") + |> Browser.set_cookie("api_token", "abc123", + path: "/index.html", + secure: true, + httpOnly: true, + expiry: expiry + ) + |> visit("/index.html") + |> Browser.cookies() + |> hd() + + assert cookie["name"] == "api_token" + assert cookie["value"] == "abc123" + assert cookie["path"] == "/index.html" + assert cookie["secure"] == true + assert cookie["httpOnly"] == true + assert cookie["expiry"] == expiry + end + + test "without visiting a page first throws an error", %{session: session} do + assert_raise CookieError, fn -> + session + |> Browser.set_cookie("other_cookie", "test", secure: true, httpOnly: true) + end + end + end end diff --git a/lib/wallaby/browser.ex b/lib/wallaby/browser.ex index 3e282a78..385cf82c 100644 --- a/lib/wallaby/browser.ex +++ b/lib/wallaby/browser.ex @@ -1281,12 +1281,12 @@ defmodule Wallaby.Browser do cookies_list end - def set_cookie(%Session{driver: driver} = session, key, value) do + def set_cookie(%Session{driver: driver} = session, key, value, attributes \\ []) do if blank_page?(session) do raise CookieError end - case driver.set_cookie(session, key, value) do + case driver.set_cookie(session, key, value, attributes) do {:ok, _list} -> session diff --git a/lib/wallaby/chrome.ex b/lib/wallaby/chrome.ex index fe1f5eec..82049d7b 100644 --- a/lib/wallaby/chrome.ex +++ b/lib/wallaby/chrome.ex @@ -447,8 +447,11 @@ defmodule Wallaby.Chrome do def page_title(session), do: delegate(:page_title, session) @doc false def page_source(session), do: delegate(:page_source, session) + @doc false - def set_cookie(session, key, value), do: delegate(:set_cookie, session, [key, value]) + def set_cookie(session, key, value, attributes \\ []), + do: delegate(:set_cookie, session, [key, value, attributes]) + @doc false def visit(session, url), do: delegate(:visit, session, [url]) @doc false diff --git a/lib/wallaby/driver.ex b/lib/wallaby/driver.ex index 6373cf3f..79630fc3 100644 --- a/lib/wallaby/driver.ex +++ b/lib/wallaby/driver.ex @@ -115,6 +115,8 @@ defmodule Wallaby.Driver do Invoked to set a cookie on a session """ @callback set_cookie(Session.t(), String.t(), String.t()) :: {:ok, any} | {:error, reason} + @callback set_cookie(Session.t(), String.t(), String.t(), keyword()) :: + {:ok, any} | {:error, reason} @doc """ Invoked to set the size of the currently focused window. diff --git a/lib/wallaby/exceptions.ex b/lib/wallaby/exceptions.ex index 4c9914ae..2b0f557d 100644 --- a/lib/wallaby/exceptions.ex +++ b/lib/wallaby/exceptions.ex @@ -72,7 +72,7 @@ defmodule Wallaby.CookieError do You're most likely seeing this error because you're trying to set a cookie before you have visited a page. You can fix this issue by calling `visit/1` - before you call `set_cookie/3`. + before you call `set_cookie/3` or `set_cookie/4`. """ %__MODULE__{message: msg} diff --git a/lib/wallaby/selenium.ex b/lib/wallaby/selenium.ex index e5184c2f..2469c880 100644 --- a/lib/wallaby/selenium.ex +++ b/lib/wallaby/selenium.ex @@ -223,8 +223,8 @@ defmodule Wallaby.Selenium do end @doc false - def set_cookie(%Session{} = session, key, value) do - WebdriverClient.set_cookie(session, key, value) + def set_cookie(%Session{} = session, key, value, attributes \\ []) do + WebdriverClient.set_cookie(session, key, value, attributes) end @doc false diff --git a/lib/wallaby/webdriver_client.ex b/lib/wallaby/webdriver_client.ex index 242e100e..92adf7d6 100644 --- a/lib/wallaby/webdriver_client.ex +++ b/lib/wallaby/webdriver_client.ex @@ -415,12 +415,26 @@ defmodule Wallaby.WebdriverClient do @doc """ Sets a cookie for the session. + + It accepts a list of optional cookie attributes per the + [WebDriver specification](https://www.w3.org/TR/webdriver1/#cookies): + + * path (string) + * domain (string) + * secure (boolean) + * httpOnly (boolean) + * expiry (integer; number of seconds since Unix Epoch) """ @spec set_cookie(Session.t(), String.t(), String.t()) :: {:ok, []} - def set_cookie(session, key, value) do - params = %{cookie: %{name: key, value: value}} + @spec set_cookie(Session.t(), String.t(), String.t(), keyword()) :: {:ok, []} + def set_cookie(session, key, value, attributes \\ []) do + cookie_params = + attributes + |> Enum.into(%{}) + |> Map.take(~w[path domain secure httpOnly expiry]a) + |> Map.merge(%{name: key, value: value}) - with {:ok, resp} <- request(:post, "#{session.url}/cookie", params) do + with {:ok, resp} <- request(:post, "#{session.url}/cookie", %{cookie: cookie_params}) do Map.fetch(resp, "value") end end diff --git a/test/wallaby/selenium/webdriver_client_test.exs b/test/wallaby/selenium/webdriver_client_test.exs index 19c59312..ac8214fe 100644 --- a/test/wallaby/selenium/webdriver_client_test.exs +++ b/test/wallaby/selenium/webdriver_client_test.exs @@ -623,6 +623,46 @@ defmodule Wallaby.WebdriverClientTest do end end + describe "set_cookie/4" do + test "sends the correct request to the server", %{bypass: bypass} do + session = build_session_for_bypass(bypass) + key = "tester" + value = "McTestington" + expiry = DateTime.utc_now() |> DateTime.to_unix() |> Kernel.+(1000) + + Bypass.expect(bypass, "POST", "/session/#{session.id}/cookie", fn conn -> + conn = parse_body(conn) + + assert conn.body_params == %{ + "cookie" => %{ + "name" => key, + "value" => value, + "path" => "/index.html", + "domain" => "example.com", + "secure" => true, + "httpOnly" => true, + "expiry" => expiry + } + } + + send_json_resp(conn, 200, %{ + "sessionId" => session.id, + "status" => 0, + "value" => [] + }) + end) + + assert {:ok, []} = + Client.set_cookie(session, key, value, + path: "/index.html", + domain: "example.com", + secure: true, + httpOnly: true, + expiry: expiry + ) + end + end + describe "set_window_size/3" do test "sends the correct request to the server", %{bypass: bypass} do session = build_session_for_bypass(bypass)