diff --git a/lib/phoenix_test.ex b/lib/phoenix_test.ex index c86d7897..18fe2b8a 100644 --- a/lib/phoenix_test.ex +++ b/lib/phoenix_test.ex @@ -171,6 +171,12 @@ defmodule PhoenixTest do """ defdelegate submit_form(session, selector, data), to: Driver + @doc """ + Open the default browser to display current HTML of `session`. + """ + defdelegate open_browser(session), to: Driver + defdelegate open_browser(session, open_fun), to: Driver + @doc """ Assert helper to ensure an element with given CSS selector and `text` is present. diff --git a/lib/phoenix_test/driver.ex b/lib/phoenix_test/driver.ex index 0d6c1558..b41a17b6 100644 --- a/lib/phoenix_test/driver.ex +++ b/lib/phoenix_test/driver.ex @@ -8,4 +8,6 @@ defprotocol PhoenixTest.Driver do def click_button(session, selector, text) def fill_form(session, selector, form_data) def submit_form(session, selector, form_data) + def open_browser(session) + def open_browser(session, open_fun) end diff --git a/lib/phoenix_test/live.ex b/lib/phoenix_test/live.ex index 912989a9..f415aab9 100644 --- a/lib/phoenix_test/live.ex +++ b/lib/phoenix_test/live.ex @@ -156,6 +156,11 @@ defimpl PhoenixTest.Driver, for: PhoenixTest.Live do phx_change != nil && phx_change != "" end + def open_browser(%{view: view} = session, open_fun \\ &Phoenix.LiveViewTest.open_browser/1) do + open_fun.(view) + session + end + defp maybe_redirect({:error, {:redirect, %{to: path}}}, session) do PhoenixTest.visit(session.conn, path) end diff --git a/lib/phoenix_test/open_browser.ex b/lib/phoenix_test/open_browser.ex new file mode 100644 index 00000000..afcfce37 --- /dev/null +++ b/lib/phoenix_test/open_browser.ex @@ -0,0 +1,76 @@ +defmodule PhoenixTest.OpenBrowser do + @moduledoc false + + # This module contains private functionality ported over from + # `Phoenix.LiveViewTest` to make `open_browser` work with `Static` tests. + + @doc """ + Fully qualifies static assets paths so the browser can find them. + """ + def prefix_static_paths(node, endpoint) do + static_path = static_path(endpoint) + + case node do + # Remove script tags + {"script", _, _} -> nil + # Skip prefixing src attributes on anchor tags + {"a", _, _} = link -> link + {el, attrs, children} -> {el, maybe_prefix_static_path(attrs, static_path), children} + el -> el + end + end + + defp static_path(endpoint) do + static_url = endpoint.config(:static_url) || [] + priv_dir = :otp_app |> endpoint.config() |> Application.app_dir("priv") + + if Keyword.get(static_url, :path) do + priv_dir + else + Path.join(priv_dir, "static") + end + end + + defp maybe_prefix_static_path(attrs, nil), do: attrs + + defp maybe_prefix_static_path(attrs, static_path) do + Enum.map(attrs, fn + {"src", path} -> {"src", prefix_static_path(path, static_path)} + {"href", path} -> {"href", prefix_static_path(path, static_path)} + attr -> attr + end) + end + + defp prefix_static_path(<<"//" <> _::binary>> = url, _prefix), do: url + + defp prefix_static_path(<<"/" <> _::binary>> = path, prefix) do + "file://#{Path.join([prefix, path])}" + end + + defp prefix_static_path(url, _), do: url + + @doc """ + System agnostic function to open the default browser with the given `path`. + + This is ripped verbatim from `Phoenix.LiveViewTest`. + """ + def open_with_system_cmd(path) do + {cmd, args} = + case :os.type() do + {:win32, _} -> + {"cmd", ["/c", "start", path]} + + {:unix, :darwin} -> + {"open", [path]} + + {:unix, _} -> + if System.find_executable("cmd.exe") do + {"cmd.exe", ["/c", "start", path]} + else + {"xdg-open", [path]} + end + end + + System.cmd(cmd, args) + end +end diff --git a/lib/phoenix_test/static.ex b/lib/phoenix_test/static.ex index b21dd891..ef884ffc 100644 --- a/lib/phoenix_test/static.ex +++ b/lib/phoenix_test/static.ex @@ -27,6 +27,7 @@ defimpl PhoenixTest.Driver, for: PhoenixTest.Static do alias PhoenixTest.Html alias PhoenixTest.Query + alias PhoenixTest.OpenBrowser def render_page_title(session) do session @@ -177,6 +178,22 @@ defimpl PhoenixTest.Driver, for: PhoenixTest.Static do |> maybe_redirect(session) end + def open_browser(session, open_fun \\ &OpenBrowser.open_with_system_cmd/1) do + path = Path.join([System.tmp_dir!(), "phx-test#{System.unique_integer([:monotonic])}.html"]) + + html = + session.conn.resp_body + |> Floki.parse_document!() + |> Floki.traverse_and_update(&OpenBrowser.prefix_static_paths(&1, @endpoint)) + |> Floki.raw_html() + + File.write!(path, html) + + open_fun.(path) + + session + end + defp maybe_redirect(conn, session) do case conn do %{status: 302} -> diff --git a/test/phoenix_test/live_test.exs b/test/phoenix_test/live_test.exs index b4101068..8d94fc85 100644 --- a/test/phoenix_test/live_test.exs +++ b/test/phoenix_test/live_test.exs @@ -333,4 +333,21 @@ defmodule PhoenixTest.LiveTest do end end end + + describe "open_browser" do + setup do + open_fun = fn view -> + assert %Phoenix.LiveViewTest.View{} = view + end + + %{open_fun: open_fun} + end + + test "opens the browser", %{conn: conn, open_fun: open_fun} do + conn + |> visit("/live/index") + |> open_browser(open_fun) + |> assert_has("h1", "LiveView main page") + end + end end diff --git a/test/phoenix_test/static_test.exs b/test/phoenix_test/static_test.exs index 214748d0..b9c9cd29 100644 --- a/test/phoenix_test/static_test.exs +++ b/test/phoenix_test/static_test.exs @@ -337,4 +337,35 @@ defmodule PhoenixTest.StaticTest do end end end + + describe "open_browser" do + setup do + open_fun = fn path -> + assert content = File.read!(path) + + assert content =~ + ~r[] + + assert content =~ "" + assert content =~ "body { font-size: 12px; }" + + assert content =~ ~r/