diff --git a/.gitignore b/.gitignore index 6bdf718f..4dc23bab 100644 --- a/.gitignore +++ b/.gitignore @@ -25,5 +25,9 @@ phoenix_test-*.tar # Temporary files, for example, from tests. /tmp/ +# Local configuration +.envrc + # Ignore assets that are produced by build tools. -/priv/static/assets/ +/priv/static/assets/* +!/priv/static/assets/app.css diff --git a/config/test.exs b/config/test.exs index 68cbde9f..bcc06013 100644 --- a/config/test.exs +++ b/config/test.exs @@ -20,3 +20,11 @@ config :esbuild, cd: Path.expand("../test/assets", __DIR__), env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} ] + +config :wallaby, + otp_app: :phoenix_test, + js_logger: nil, + chromedriver: [ + headless: System.get_env("WALLABY_HEADLESS", "t") in ["t", "true"], + binary: System.get_env("CHROME_EXECUTABLE", "") + ] diff --git a/lib/phoenix_test.ex b/lib/phoenix_test.ex index 518d38ec..68ee0bcb 100644 --- a/lib/phoenix_test.ex +++ b/lib/phoenix_test.ex @@ -213,6 +213,12 @@ defmodule PhoenixTest do LiveView or a static view. You don't need to worry about which type of page you're visiting. """ + if Code.ensure_loaded?(Wallaby) do + def visit(%Plug.Conn{assigns: %{phoenix_test_js: true}} = conn, path) do + PhoenixTest.Wallaby.build(conn, path) + end + end + def visit(conn, path) do conn |> recycle(all_headers(conn)) @@ -237,6 +243,32 @@ defmodule PhoenixTest do Enum.map(conn.req_headers, &elem(&1, 0)) end + @doc """ + Helper to run tests with the Javascript (Wallaby) driver via `@tag :js`. + + ```elixir + setup %{conn: conn} = context do + conn = if context[:js], do: with_js_driver(conn), else: conn + %{conn: conn} + end + + @tag :js + test "with wallaby", %{conn: conn} do + visit(conn, "/path") + end + + test "without wallaby", %{conn: conn} do + visit(conn, "/path") + end + ``` + + You can also use `@describetag :js` and `@moduletag :js`. + Refer to the [ExUnit docs](https://hexdocs.pm/ex_unit/1.16.3/ExUnit.Case.html#module-tags) for more details. + """ + def with_js_driver(%Plug.Conn{} = conn) do + Plug.Conn.assign(conn, :phoenix_test_js, true) + end + @doc """ Clicks a link with given text and performs the action. diff --git a/lib/phoenix_test/field.ex b/lib/phoenix_test/field.ex index 6be3ebbf..54061cf9 100644 --- a/lib/phoenix_test/field.ex +++ b/lib/phoenix_test/field.ex @@ -7,8 +7,8 @@ defmodule PhoenixTest.Field do alias PhoenixTest.Query alias PhoenixTest.Utils - @enforce_keys ~w[source_raw parsed label id name value selector]a - defstruct ~w[source_raw parsed label id name value selector]a + @enforce_keys ~w[source_raw parsed label id name value selector raw]a + defstruct ~w[source_raw parsed label id name value selector raw]a def find_input!(html, input_selectors, label, opts) do field = Query.find_by_label!(html, input_selectors, label, opts) @@ -23,13 +23,13 @@ defmodule PhoenixTest.Field do id: id, name: name, value: value, - selector: Element.build_selector(field) + selector: Element.build_selector(field), + raw: field } end def find_checkbox!(html, input_selector, label, opts) do field = Query.find_by_label!(html, input_selector, label, opts) - id = Html.attribute(field, "id") name = Html.attribute(field, "name") value = Html.attribute(field, "value") || "on" @@ -41,7 +41,8 @@ defmodule PhoenixTest.Field do id: id, name: name, value: value, - selector: Element.build_selector(field) + selector: Element.build_selector(field), + raw: field } end @@ -60,7 +61,8 @@ defmodule PhoenixTest.Field do id: id, name: name, value: value, - selector: Element.build_selector(field) + selector: Element.build_selector(field), + raw: field } end diff --git a/lib/phoenix_test/link.ex b/lib/phoenix_test/link.ex index c26e8aef..a87f513c 100644 --- a/lib/phoenix_test/link.ex +++ b/lib/phoenix_test/link.ex @@ -1,6 +1,7 @@ defmodule PhoenixTest.Link do @moduledoc false + alias PhoenixTest.Element alias PhoenixTest.Html alias PhoenixTest.Query alias PhoenixTest.Utils @@ -17,7 +18,7 @@ defmodule PhoenixTest.Link do raw: link_html, parsed: link, id: id, - selector: selector, + selector: Element.build_selector(link), text: text, href: href } diff --git a/lib/phoenix_test/wallaby.ex b/lib/phoenix_test/wallaby.ex new file mode 100644 index 00000000..22ab5285 --- /dev/null +++ b/lib/phoenix_test/wallaby.ex @@ -0,0 +1,293 @@ +if Code.ensure_loaded?(Wallaby) do + defmodule PhoenixTest.Wallaby do + @moduledoc false + + import Wallaby.Browser + + alias ExUnit.AssertionError + alias PhoenixTest.Button + alias PhoenixTest.Field + alias PhoenixTest.Html + alias PhoenixTest.Link + alias PhoenixTest.OpenBrowser + alias PhoenixTest.Query + alias PhoenixTest.Select + + require Wallaby.Browser + + @endpoint Application.compile_env(:phoenix_test, :endpoint) + + defstruct session: nil, within: :none, last_field_query: :none + + def build(_conn, path) do + {:ok, session} = Wallaby.start_session() + Wallaby.Browser.visit(session, path) + %__MODULE__{session: session} + end + + def current_path(%{session: session}) do + uri = session |> current_url() |> URI.parse() + append_query_string(uri.path, uri.query) + end + + def append_query_string(path, nil), do: path + def append_query_string(path, query), do: path <> "?" <> query + + def render_page_title(session) do + session + |> render_html() + |> Query.find("title") + |> case do + {:found, element} -> Html.text(element) + _ -> nil + end + end + + def render_html(%{session: session, within: within}) do + html = Wallaby.Browser.page_source(session) + + case within do + :none -> html + selector -> html |> Query.find!(selector) |> Html.raw() + end + end + + def click_link(session, selector, text) do + query = query(session, &Link.find!(&1, selector, text)) + # TODO Remove redundant text filter + Wallaby.Browser.click(session.session, Wallaby.Query.text(query, text)) + session + end + + def click_button(session, selector, text) do + query = query(session, &Button.find!(&1, selector, text)) + + belongs_to_form? = + session + |> render_html() + |> Button.find!(selector, text) + |> Button.belongs_to_form?() + + # TODO Remove redundant text filter + Wallaby.Browser.click(session.session, Wallaby.Query.text(query, text)) + Map.update!(session, :last_field_query, &if(belongs_to_form?, do: :none, else: &1)) + end + + def within(session, selector, fun) when is_function(fun, 1) do + session + |> Map.put(:within, selector) + |> fun.() + |> Map.put(:within, :none) + end + + def fill_in(session, input_selector, label, opts) do + {value, opts} = Keyword.pop!(opts, :with) + field = Field.find_input!(render_html(session), input_selector, label, opts) + query = query(session, &Field.find_input!(&1, input_selector, label, opts)) + + case Html.attribute(field.raw, "type") do + # Set via JS to avoid locale format issues + type when type in ~w(date datetime-local time week) -> + js = """ + el = document.querySelector('#{field.selector}'); + el.value = '#{value}'; + """ + + session.session + |> Wallaby.Browser.execute_script(js) + |> Wallaby.Browser.click(query) + + _other -> + Wallaby.Browser.clear(session.session, query) + Wallaby.Browser.fill_in(session.session, query, with: value || "") + end + + session + |> Map.put(:last_field_query, query) + |> trigger_phx_change_validations(query) + end + + def select(session, input_selector, options, opts) do + {label, opts} = Keyword.pop!(opts, :from) + query = query(session, &Select.find_select_option!(&1, input_selector, label, options, opts)) + + for option <- List.wrap(options) do + option_query = + if opts[:exact_option] do + Wallaby.Query.option(option) + else + Wallaby.Query.css("option", text: option) + end + + session.session + |> Wallaby.Browser.find(query) + |> Wallaby.Browser.set_value(option_query, :selected) + end + + Map.put(session, :last_field_query, query) + end + + def check(session, input_selector, label, opts) do + query = query(session, &Field.find_checkbox!(&1, input_selector, label, opts)) + Wallaby.Browser.set_value(session.session, query, :selected) + + Map.put(session, :last_field_query, query) + end + + def uncheck(session, input_selector, label, opts) do + query = query(session, &Field.find_checkbox!(&1, input_selector, label, opts)) + Wallaby.Browser.set_value(session.session, query, :unselected) + + Map.put(session, :last_field_query, query) + end + + def choose(session, input_selector, label, opts) do + query = query(session, &Field.find_input!(&1, input_selector, label, opts)) + Wallaby.Browser.click(session.session, query) + + Map.put(session, :last_field_query, query) + end + + def upload(session, input_selector, label, path, opts) do + # TODO Improve: wait for live upload input to be ready + Process.sleep(1000) + + query = query(session, &Field.find_input!(&1, input_selector, label, opts)) + Wallaby.Browser.attach_file(session.session, query, [{:path, Path.expand(path)}]) + + Map.put(session, :last_field_query, query) + end + + def submit(session) do + case session.last_field_query do + :none -> raise no_active_form_error() + # TODO Fix for file input ('enter' key doesn't trigger form submit) + query -> Wallaby.Browser.send_keys(session.session, query, [:enter]) + end + + Map.put(session, :last_field_query, :none) + 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.session + |> Wallaby.Browser.page_source() + |> 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 + + def unwrap(session, fun) when is_function(fun, 1) do + fun.(session.session) + session + end + + defp query(session, find_fun) do + # Use PhoenixTest.Field queries for label exact-match semantics + html = render_html(session) + field = find_fun.(html) + + within = + case session.within do + :none -> "" + selector -> selector + end + + selector = "#{within} #{field.selector}" + Wallaby.Query.css(selector) + end + + defp trigger_phx_change_validations(session, query) do + if has?(session.session, query) do + Wallaby.Browser.send_keys(session.session, query, [:tab]) + end + + session + end + + defp no_active_form_error do + %ArgumentError{ + message: "There's no active form. Fill in a form with `fill_in`, `select`, etc." + } + end + end + + defimpl PhoenixTest.Driver, for: PhoenixTest.Wallaby do + alias ExUnit.AssertionError + alias PhoenixTest.Assertions + alias PhoenixTest.Wallaby + + def render_page_title(session), do: map_errors(fn -> Wallaby.render_page_title(session) end) + def render_html(session), do: map_errors(fn -> Wallaby.render_html(session) end) + def click_link(session, selector, text), do: map_errors(fn -> Wallaby.click_link(session, selector, text) end) + def click_button(session, selector, text), do: map_errors(fn -> Wallaby.click_button(session, selector, text) end) + def within(session, selector, fun), do: map_errors(fn -> Wallaby.within(session, selector, fun) end) + + def fill_in(session, input_selector, label, opts), + do: map_errors(fn -> Wallaby.fill_in(session, input_selector, label, opts) end) + + def select(session, input_selector, option, opts), + do: map_errors(fn -> Wallaby.select(session, input_selector, option, opts) end) + + def check(session, input_selector, label, opts), + do: map_errors(fn -> Wallaby.check(session, input_selector, label, opts) end) + + def uncheck(session, input_selector, label, opts), + do: map_errors(fn -> Wallaby.uncheck(session, input_selector, label, opts) end) + + def choose(session, input_selector, label, opts), + do: map_errors(fn -> Wallaby.choose(session, input_selector, label, opts) end) + + def upload(session, input_selector, label, path, opts), + do: map_errors(fn -> Wallaby.upload(session, input_selector, label, path, opts) end) + + def submit(session), do: map_errors(fn -> Wallaby.submit(session) end) + def open_browser(session), do: map_errors(fn -> Wallaby.open_browser(session) end) + def open_browser(session, open_fun), do: map_errors(fn -> Wallaby.open_browser(session, open_fun) end) + def unwrap(session, fun), do: map_errors(fn -> Wallaby.unwrap(session, fun) end) + def current_path(session), do: map_errors(fn -> Wallaby.current_path(session) end) + + def assert_has(session, selector), do: retry(fn -> Assertions.assert_has(session, selector) end) + def assert_has(session, selector, opts), do: retry(fn -> Assertions.assert_has(session, selector, opts) end) + def refute_has(session, selector), do: retry(fn -> Assertions.refute_has(session, selector) end) + def refute_has(session, selector, opts), do: retry(fn -> Assertions.refute_has(session, selector, opts) end) + def assert_path(session, path), do: retry(fn -> Assertions.assert_path(session, path) end) + def assert_path(session, path, opts), do: retry(fn -> Assertions.assert_path(session, path, opts) end) + def refute_path(session, path), do: retry(fn -> Assertions.refute_path(session, path) end) + def refute_path(session, path, opts), do: retry(fn -> Assertions.refute_path(session, path, opts) end) + + defp map_errors(fun) do + fun.() + rescue + e -> + raise ArgumentError, Exception.message(e) + end + + defp retry(fun, timeout_ms \\ 300, interval_ms \\ 10) do + now = DateTime.to_unix(DateTime.utc_now(), :millisecond) + timeout_at = DateTime.utc_now() |> DateTime.add(timeout_ms, :millisecond) |> DateTime.to_unix(:millisecond) + retry(fun, now, timeout_at, interval_ms) + end + + defp retry(fun, now, timeout_at, _interval_ms) when now >= timeout_at do + fun.() + end + + defp retry(fun, _now, timeout_at, interval_ms) do + fun.() + rescue + AssertionError -> + Process.sleep(interval_ms) + now = DateTime.to_unix(DateTime.utc_now(), :millisecond) + retry(fun, now, timeout_at, interval_ms) + end + end +end diff --git a/mix.exs b/mix.exs index 79816100..aa13d2fe 100644 --- a/mix.exs +++ b/mix.exs @@ -51,7 +51,8 @@ defmodule PhoenixTest.MixProject do {:phoenix, "~> 1.7.10"}, {:phoenix_live_view, "~> 0.20.1"}, {:plug_cowboy, "~> 2.7", only: :test, runtime: false}, - {:styler, "~> 0.11", only: [:dev, :test], runtime: false} + {:styler, "~> 0.11", only: [:dev, :test], runtime: false}, + {:wallaby, "~> 0.30.6", runtime: false, only: :test, optional: true} ] end @@ -79,7 +80,8 @@ defmodule PhoenixTest.MixProject do [ setup: ["deps.get", "assets.setup", "assets.build"], "assets.setup": ["esbuild.install --if-missing"], - "assets.build": ["esbuild default"] + "assets.build": ["esbuild default"], + test: ["assets.build", "test"] ] end end diff --git a/mix.lock b/mix.lock index 0685833d..0e8a537d 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,6 @@ %{ "castore": {:hex, :castore, "1.0.5", "9eeebb394cc9a0f3ae56b813459f990abb0a3dedee1be6b27fdb50301930502f", [:mix], [], "hexpm", "8d7c597c3e4a64c395980882d4bca3cebb8d74197c590dc272cfd3b6a6310578"}, + "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, "cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"}, @@ -7,6 +8,9 @@ "esbuild": {:hex, :esbuild, "0.8.1", "0cbf919f0eccb136d2eeef0df49c4acf55336de864e63594adcea3814f3edf41", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "25fc876a67c13cb0a776e7b5d7974851556baeda2085296c14ab48555ea7560f"}, "ex_doc": {:hex, :ex_doc, "0.32.1", "21e40f939515373bcdc9cffe65f3b3543f05015ac6c3d01d991874129d173420", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "5142c9db521f106d61ff33250f779807ed2a88620e472ac95dc7d59c380113da"}, "floki": {:hex, :floki, "0.35.2", "87f8c75ed8654b9635b311774308b2760b47e9a579dabf2e4d5f1e1d42c39e0b", [:mix], [], "hexpm", "6b05289a8e9eac475f644f09c2e4ba7e19201fd002b89c28c1293e7bd16773d9"}, + "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, + "httpoison": {:hex, :httpoison, "2.2.1", "87b7ed6d95db0389f7df02779644171d7319d319178f6680438167d7b69b1f3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "51364e6d2f429d80e14fe4b5f8e39719cacd03eb3f9a9286e61e216feac2d2df"}, + "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, "makeup_diff": {:hex, :makeup_diff, "0.1.0", "5be352b6aa6f07fa6a236e3efd7ba689a03f28fb5d35b7a0fa0a1e4a64f6d8bb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "186bad5bb433a8afeb16b01423950e440072284a4103034ca899180343b9b4ac"}, @@ -14,8 +18,11 @@ "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, "makeup_html": {:hex, :makeup_html, "0.1.1", "c3d4abd39d5f7e925faca72ada6e9cc5c6f5fa7cd5bc0158315832656cf14d7f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "44f2a61bc5243645dd7fafeaa6cc28793cd22f3c76b861e066168f9a5b2c26a4"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, + "mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, + "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "phoenix": {:hex, :phoenix, "1.7.10", "02189140a61b2ce85bb633a9b6fd02dff705a5f1596869547aeb2b2b95edd729", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "cf784932e010fd736d656d7fead6a584a4498efefe5b8227e9f383bf15bb79d0"}, "phoenix_html": {:hex, :phoenix_html, "4.0.0", "4857ec2edaccd0934a923c2b0ba526c44a173c86b847e8db725172e9e51d11d6", [:mix], [], "hexpm", "cee794a052f243291d92fa3ccabcb4c29bb8d236f655fb03bcbdc3a8214b8d13"}, "phoenix_live_view": {:hex, :phoenix_live_view, "0.20.3", "8b6406bc0a451f295407d7acff7f234a6314be5bbe0b3f90ed82b07f50049878", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a8e4385e05618b424779f894ed2df97d3c7518b7285fcd11979077ae6226466b"}, @@ -25,8 +32,13 @@ "plug_cowboy": {:hex, :plug_cowboy, "2.7.1", "87677ffe3b765bc96a89be7960f81703223fe2e21efa42c125fcd0127dd9d6b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "02dbd5f9ab571b864ae39418db7811618506256f6d13b4a45037e5fe78dc5de3"}, "plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "styler": {:hex, :styler, "0.11.9", "2595393b94e660cd6e8b582876337cc50ff047d184ccbed42fdad2bfd5d78af5", [:mix], [], "hexpm", "8b7806ba1fdc94d0a75127c56875f91db89b75117fcc67572661010c13e1f259"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, + "tesla": {:hex, :tesla, "1.9.0", "8c22db6a826e56a087eeb8cdef56889731287f53feeb3f361dec5d4c8efb6f14", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "7c240c67e855f7e63e795bf16d6b3f5115a81d1f44b7fe4eadbf656bae0fef8a"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, + "wallaby": {:hex, :wallaby, "0.30.6", "7dc4c1213f3b52c4152581d126632bc7e06892336d3a0f582853efeeabd45a71", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}, {:httpoison, "~> 0.12 or ~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_ecto, ">= 3.0.0", [hex: :phoenix_ecto, repo: "hexpm", optional: true]}, {:web_driver_client, "~> 0.2.0", [hex: :web_driver_client, repo: "hexpm", optional: false]}], "hexpm", "50950c1d968549b54c20e16175c68c7fc0824138e2bb93feb11ef6add8eb23d4"}, + "web_driver_client": {:hex, :web_driver_client, "0.2.0", "63b76cd9eb3b0716ec5467a0f8bead73d3d9612e63f7560d21357f03ad86e31a", [:mix], [{:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:tesla, "~> 1.3", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "83cc6092bc3e74926d1c8455f0ce927d5d1d36707b74d9a65e38c084aab0350f"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock_adapter": {:hex, :websock_adapter, "0.5.5", "9dfeee8269b27e958a65b3e235b7e447769f66b5b5925385f5a569269164a210", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "4b977ba4a01918acbf77045ff88de7f6972c2a009213c515a445c48f224ffce9"}, } diff --git a/priv/static/assets/app.css b/priv/static/assets/app.css new file mode 100644 index 00000000..e69de29b diff --git a/priv/static/favicon.ico b/priv/static/favicon.ico new file mode 100644 index 00000000..e69de29b diff --git a/test/phoenix_test/assertions_test.exs b/test/phoenix_test/assertions_test.exs index c11d8025..1db57cf7 100644 --- a/test/phoenix_test/assertions_test.exs +++ b/test/phoenix_test/assertions_test.exs @@ -8,18 +8,22 @@ defmodule PhoenixTest.AssertionsTest do alias ExUnit.AssertionError alias PhoenixTest.Live - setup do - %{conn: Phoenix.ConnTest.build_conn()} + require PhoenixTest.TestHelpers + + setup context do + conn = Phoenix.ConnTest.build_conn() + conn = if context[:js], do: with_js_driver(conn), else: conn + %{conn: conn} end describe "assert_has/2" do - test "succeeds if single element is found with CSS selector", %{conn: conn} do + test_also_with_js "succeeds if single element is found with CSS selector", %{conn: conn} do conn |> visit("/page/index") |> assert_has("[data-role='title']") end - test "raises an error if the element cannot be found at all", %{conn: conn} do + test_also_with_js "raises an error if the element cannot be found at all", %{conn: conn} do conn = visit(conn, "/page/index") msg = ~r/Could not find any elements with selector "#nonexistent-id"/ @@ -29,25 +33,25 @@ defmodule PhoenixTest.AssertionsTest do end end - test "succeeds if element searched is title (Static)", %{conn: conn} do + test_also_with_js "succeeds if element searched is title (Static)", %{conn: conn} do conn |> visit("/page/index") |> assert_has("title") end - test "succeeds if element searched is title (Live)", %{conn: conn} do + test_also_with_js "succeeds if element searched is title (Live)", %{conn: conn} do conn |> visit("/live/index") |> assert_has("title") end - test "succeeds if more than one element matches selector", %{conn: conn} do + test_also_with_js "succeeds if more than one element matches selector", %{conn: conn} do conn |> visit("/page/index") |> assert_has("li") end - test "takes in input helper in assertion", %{conn: conn} do + test_also_with_js "takes in input helper in assertion", %{conn: conn} do conn |> visit("/page/index") |> assert_has(input(type: "text", label: "User Name")) @@ -55,7 +59,7 @@ defmodule PhoenixTest.AssertionsTest do end describe "assert_has/3" do - test "succeeds if single element is found with CSS selector and text (Static)", %{conn: conn} do + test_also_with_js "succeeds if single element is found with CSS selector and text (Static)", %{conn: conn} do conn |> visit("/page/index") |> assert_has("h1", text: "Main page") @@ -64,7 +68,7 @@ defmodule PhoenixTest.AssertionsTest do |> assert_has("[data-role='title']", text: "Main page") end - test "succeeds if single element is found with CSS selector and text (Live)", %{conn: conn} do + test_also_with_js "succeeds if single element is found with CSS selector and text (Live)", %{conn: conn} do conn |> visit("/live/index") |> assert_has("h1", text: "LiveView main page") @@ -73,7 +77,7 @@ defmodule PhoenixTest.AssertionsTest do |> assert_has("[data-role='title']", text: "LiveView main page") end - test "succeeds if more than one element matches selector but text narrows it down", %{ + test_also_with_js "succeeds if more than one element matches selector but text narrows it down", %{ conn: conn } do conn @@ -81,25 +85,25 @@ defmodule PhoenixTest.AssertionsTest do |> assert_has("li", text: "Aragorn") end - test "succeeds if more than one element matches selector and text", %{conn: conn} do + test_also_with_js "succeeds if more than one element matches selector and text", %{conn: conn} do conn |> visit("/page/index") |> assert_has(".multiple_links", text: "Multiple links") end - test "succeeds if text difference is only a matter of truncation", %{conn: conn} do + test_also_with_js "succeeds if text difference is only a matter of truncation", %{conn: conn} do conn |> visit("/page/index") |> assert_has(".has_extra_space", text: "Has extra space") end - test "succeeds when a non-200 status code is returned", %{conn: conn} do + test_also_with_js "succeeds when a non-200 status code is returned", %{conn: conn} do conn |> visit("/page/unauthorized") |> assert_has("h1", text: "Unauthorized") end - test "raises an error if the element cannot be found at all", %{conn: conn} do + test_also_with_js "raises an error if the element cannot be found at all", %{conn: conn} do conn = visit(conn, "/page/index") msg = ~r/Could not find any elements with selector "#nonexistent-id"/ @@ -109,7 +113,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "raises error if element cannot be found but selector matches other elements", %{ + test_also_with_js "raises error if element cannot be found but selector matches other elements", %{ conn: conn } do conn = visit(conn, "/page/index") @@ -130,25 +134,25 @@ defmodule PhoenixTest.AssertionsTest do end end - test "can be used to assert on page title (Static)", %{conn: conn} do + test_also_with_js "can be used to assert on page title (Static)", %{conn: conn} do conn |> visit("/page/index") |> assert_has("title", text: "PhoenixTest is the best!") end - test "can be used to assert on page title (Live)", %{conn: conn} do + test_also_with_js "can be used to assert on page title (Live)", %{conn: conn} do conn |> visit("/live/index") |> assert_has("title", text: "PhoenixTest is the best!") end - test "can assert title's exactness", %{conn: conn} do + test_also_with_js "can assert title's exactness", %{conn: conn} do conn |> visit("/live/index") |> assert_has("title", text: "PhoenixTest is the best!", exact: true) end - test "raises if title does not match expected value (Static)", %{conn: conn} do + test_also_with_js "raises if title does not match expected value (Static)", %{conn: conn} do msg = ignore_whitespace(""" Expected title to be "Not the title" but got "PhoenixTest is the best!" @@ -161,7 +165,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "raises if title does not match expected value (Live)", %{conn: conn} do + test_also_with_js "raises if title does not match expected value (Live)", %{conn: conn} do msg = ignore_whitespace(""" Expected title to be "Not the title" but got "PhoenixTest is the best!" @@ -174,8 +178,8 @@ defmodule PhoenixTest.AssertionsTest do end end - test "raises if title is contained but is not exactly the same as expected (with exact=true)", - %{conn: conn} do + test_also_with_js "raises if title is contained but is not exactly the same as expected (with exact=true)", + %{conn: conn} do msg = ignore_whitespace(""" Expected title to be "PhoenixTest" but got "PhoenixTest is the best!" @@ -188,7 +192,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "raises error if element cannot be found and selector matches a nested structure", %{ + test_also_with_js "raises error if element cannot be found and selector matches a nested structure", %{ conn: conn } do conn = visit(conn, "/page/index") @@ -217,7 +221,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "accepts a `count` option", %{conn: conn} do + test_also_with_js "accepts a `count` option", %{conn: conn} do conn |> visit("/page/index") |> assert_has(".multiple_links", count: 2) @@ -226,7 +230,7 @@ defmodule PhoenixTest.AssertionsTest do |> assert_has("h1", text: "Main page", count: 1) end - test "raises an error if count is more than expected count", %{conn: conn} do + test_also_with_js "raises an error if count is more than expected count", %{conn: conn} do session = visit(conn, "/page/index") msg = @@ -241,7 +245,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "raises an error if count is less than expected count", %{conn: conn} do + test_also_with_js "raises an error if count is less than expected count", %{conn: conn} do session = visit(conn, "/page/index") msg = @@ -256,14 +260,14 @@ defmodule PhoenixTest.AssertionsTest do end end - test "accepts an `exact` option to match text exactly", %{conn: conn} do + test_also_with_js "accepts an `exact` option to match text exactly", %{conn: conn} do conn |> visit("/page/index") |> assert_has("h1", text: "Main", exact: false) |> assert_has("h1", text: "Main page", exact: true) end - test "raises if `exact` text doesn't match", %{conn: conn} do + test_also_with_js "raises if `exact` text doesn't match", %{conn: conn} do msg = ignore_whitespace(""" Could not find any elements with selector "h1" and text "Main". @@ -282,13 +286,13 @@ defmodule PhoenixTest.AssertionsTest do end end - test "accepts an `at` option to assert on a specific element", %{conn: conn} do + test_also_with_js "accepts an `at` option to assert on a specific element", %{conn: conn} do conn |> visit("/page/index") |> assert_has("#multiple-items li", at: 2, text: "Legolas") end - test "raises if it cannot find element at `at` position", %{conn: conn} do + test_also_with_js "raises if it cannot find element at `at` position", %{conn: conn} do msg = ignore_whitespace(""" Could not find any elements with selector "#multiple-items li" and text "Aragorn" at position 2 @@ -303,28 +307,28 @@ defmodule PhoenixTest.AssertionsTest do end describe "refute_has/2" do - test "succeeds if no element is found with CSS selector (Static)", %{conn: conn} do + test_also_with_js "succeeds if no element is found with CSS selector (Static)", %{conn: conn} do conn |> visit("/page/index") |> refute_has("#some-invalid-id") |> refute_has("[data-role='invalid-role']") end - test "succeeds if no element is found with CSS selector (Live)", %{conn: conn} do + test_also_with_js "succeeds if no element is found with CSS selector (Live)", %{conn: conn} do conn |> visit("/live/index") |> refute_has("#some-invalid-id") |> refute_has("[data-role='invalid-role']") end - test "can refute presence of title (Static)", %{conn: conn} do + test_also_with_js "can refute presence of title (Static)", %{conn: conn} do conn |> visit("/page/index_no_layout") |> refute_has("title") |> refute_has("#something-else-to-test-pipe") end - test "accepts a `count` option", %{conn: conn} do + test_also_with_js "accepts a `count` option", %{conn: conn} do conn |> visit("/page/index") |> refute_has("h1", count: 2) @@ -333,7 +337,7 @@ defmodule PhoenixTest.AssertionsTest do |> refute_has(".multiple_links", text: "Multiple links", count: 1) end - test "raises if element is found", %{conn: conn} do + test_also_with_js "raises if element is found", %{conn: conn} do msg = ignore_whitespace(""" Expected not to find any elements with selector "h1". @@ -352,7 +356,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "raises if title is found", %{conn: conn} do + test_also_with_js "raises if title is found", %{conn: conn} do msg = ignore_whitespace(""" Expected title not to be present but found: "PhoenixTest is the best!" @@ -365,7 +369,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "raises an error if multiple elements are found", %{conn: conn} do + test_also_with_js "raises an error if multiple elements are found", %{conn: conn} do conn = visit(conn, "/page/index") msg = @@ -380,7 +384,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "raises if there is one element and count is 1", %{conn: conn} do + test_also_with_js "raises if there is one element and count is 1", %{conn: conn} do conn = visit(conn, "/page/index") msg = @@ -393,7 +397,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "raises if there are the same number of elements as refuted", %{conn: conn} do + test_also_with_js "raises if there are the same number of elements as refuted", %{conn: conn} do conn = visit(conn, "/page/index") msg = @@ -410,27 +414,27 @@ defmodule PhoenixTest.AssertionsTest do end describe "refute_has/3" do - test "can be used to refute on page title (Static)", %{conn: conn} do + test_also_with_js "can be used to refute on page title (Static)", %{conn: conn} do conn |> visit("/page/index") |> refute_has("title", text: "Not the title") |> refute_has("title", text: "Not this title either") end - test "can be used to refute on page title (Live)", %{conn: conn} do + test_also_with_js "can be used to refute on page title (Live)", %{conn: conn} do conn |> visit("/live/index") |> refute_has("title", text: "Not the title") |> refute_has("title", text: "Not this title either") end - test "can be used to refute page title's exactness", %{conn: conn} do + test_also_with_js "can be used to refute page title's exactness", %{conn: conn} do conn |> visit("/live/index") |> refute_has("title", text: "PhoenixTest is the", exact: true) end - test "raises if title matches value (Static)", %{conn: conn} do + test_also_with_js "raises if title matches value (Static)", %{conn: conn} do msg = ignore_whitespace(""" Expected title not to be "PhoenixTest is the best!" @@ -443,7 +447,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "raises if title matches value (Live)", %{conn: conn} do + test_also_with_js "raises if title matches value (Live)", %{conn: conn} do msg = ignore_whitespace(""" Expected title not to be "PhoenixTest is the best!" @@ -456,7 +460,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "succeeds if no element is found with CSS selector and text (Static)", %{conn: conn} do + test_also_with_js "succeeds if no element is found with CSS selector and text (Static)", %{conn: conn} do conn |> visit("/page/index") |> refute_has("h1", text: "Not main page") @@ -465,7 +469,7 @@ defmodule PhoenixTest.AssertionsTest do |> refute_has("#title", text: "Not main page") end - test "succeeds if no element is found with CSS selector and text (Live)", %{conn: conn} do + test_also_with_js "succeeds if no element is found with CSS selector and text (Live)", %{conn: conn} do conn |> visit("/live/index") |> refute_has("h1", text: "Not main page") @@ -474,7 +478,7 @@ defmodule PhoenixTest.AssertionsTest do |> refute_has("#title", text: "Not main page") end - test "raises an error if one element is found", %{conn: conn} do + test_also_with_js "raises an error if one element is found", %{conn: conn} do conn = visit(conn, "/page/index") msg = @@ -493,7 +497,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "raises an error if multiple elements are found", %{conn: conn} do + test_also_with_js "raises an error if multiple elements are found", %{conn: conn} do conn = visit(conn, "/page/index") msg = @@ -516,13 +520,13 @@ defmodule PhoenixTest.AssertionsTest do end end - test "accepts an `exact` option to match text exactly", %{conn: conn} do + test_also_with_js "accepts an `exact` option to match text exactly", %{conn: conn} do conn |> visit("/page/index") |> refute_has("h1", text: "Main", exact: true) end - test "raises if `exact` text makes refutation false", %{conn: conn} do + test_also_with_js "raises if `exact` text makes refutation false", %{conn: conn} do msg = ignore_whitespace(""" Expected not to find any elements with selector "h1" and text "Main". @@ -541,19 +545,19 @@ defmodule PhoenixTest.AssertionsTest do end end - test "accepts an `at` option (without text) to refute on a specific element", %{conn: conn} do + test_also_with_js "accepts an `at` option (without text) to refute on a specific element", %{conn: conn} do conn |> visit("/page/index") |> refute_has("#single-list-item li", at: 2) end - test "accepts an `at` option with text to refute on a specific element", %{conn: conn} do + test_also_with_js "accepts an `at` option with text to refute on a specific element", %{conn: conn} do conn |> visit("/page/index") |> refute_has("#multiple-items li", at: 2, text: "Aragorn") end - test "raises if it finds element at `at` position", %{conn: conn} do + test_also_with_js "raises if it finds element at `at` position", %{conn: conn} do msg = ignore_whitespace(""" Expected not to find any elements with selector "#multiple-items li" and text "Legolas" at position 2 @@ -574,25 +578,25 @@ defmodule PhoenixTest.AssertionsTest do end describe "assert_path" do - test "asserts the session's current path" do + test_also_with_js "asserts the session's current path" do session = %Live{current_path: "/page/index"} assert_path(session, "/page/index") end - test "asserts query params are the same" do + test_also_with_js "asserts query params are the same" do session = %Live{current_path: "/page/index?hello=world"} assert_path(session, "/page/index", query_params: %{"hello" => "world"}) end - test "order of query params does not matter" do + test_also_with_js "order of query params does not matter" do session = %Live{current_path: "/page/index?hello=world&foo=bar"} assert_path(session, "/page/index", query_params: %{"foo" => "bar", "hello" => "world"}) end - test "raises helpful error if path doesn't match" do + test_also_with_js "raises helpful error if path doesn't match" do msg = ignore_whitespace(""" Expected path to be "/page/not-index" but got "/page/index" @@ -605,7 +609,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "raises helpful error if path doesn't have query params" do + test_also_with_js "raises helpful error if path doesn't have query params" do msg = ignore_whitespace(""" Expected query params to be "details=true&foo=bar" but got nil @@ -618,7 +622,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "raises helpful error if query params don't match" do + test_also_with_js "raises helpful error if query params don't match" do msg = ignore_whitespace(""" Expected query params to be "goodbye=world&hi=bye" but got "hello=world&hi=bye" @@ -633,19 +637,19 @@ defmodule PhoenixTest.AssertionsTest do end describe "refute_path" do - test "refute the given path is the current path" do + test_also_with_js "refute the given path is the current path" do session = %Live{current_path: "/page/index"} refute_path(session, "/page/page_2") end - test "refutes query params are the same" do + test_also_with_js "refutes query params are the same" do session = %Live{current_path: "/page/index?hello=world"} refute_path(session, "/page/index", query_params: %{"hello" => "not-world"}) end - test "raises helpful error if path matches" do + test_also_with_js "raises helpful error if path matches" do msg = ignore_whitespace(""" Expected path not to be "/page/index" @@ -658,7 +662,7 @@ defmodule PhoenixTest.AssertionsTest do end end - test "raises helpful error if query params MATCH" do + test_also_with_js "raises helpful error if query params MATCH" do msg = ignore_whitespace(""" Expected query params not to be "hello=world&hi=bye" diff --git a/test/phoenix_test/form_test.exs b/test/phoenix_test/form_test.exs index d1a47e29..e763d0f0 100644 --- a/test/phoenix_test/form_test.exs +++ b/test/phoenix_test/form_test.exs @@ -111,7 +111,7 @@ defmodule PhoenixTest.FormTest do - diff --git a/test/phoenix_test/live_test.exs b/test/phoenix_test/live_test.exs index 3d89903a..6c5b1373 100644 --- a/test/phoenix_test/live_test.exs +++ b/test/phoenix_test/live_test.exs @@ -3,15 +3,20 @@ defmodule PhoenixTest.LiveTest do import PhoenixTest import PhoenixTest.Locators + import PhoenixTest.TestHelpers alias PhoenixTest.Driver - setup do - %{conn: Phoenix.ConnTest.build_conn()} + require PhoenixTest.TestHelpers + + setup context do + conn = Phoenix.ConnTest.build_conn() + conn = if context[:js], do: with_js_driver(conn), else: conn + %{conn: conn} end describe "render_page_title/1" do - test "renders the page title", %{conn: conn} do + test_also_with_js "renders the page title", %{conn: conn} do title = conn |> visit("/live/index") @@ -30,7 +35,7 @@ defmodule PhoenixTest.LiveTest do assert title == "Title changed!" end - test "returns nil if page title isn't found", %{conn: conn} do + test_also_with_js "returns nil if page title isn't found", %{conn: conn} do title = conn |> visit("/live/index_no_layout") @@ -41,19 +46,19 @@ defmodule PhoenixTest.LiveTest do end describe "visit/2" do - test "navigates to given LiveView page", %{conn: conn} do + test_also_with_js "navigates to given LiveView page", %{conn: conn} do conn |> visit("/live/index") |> assert_has("h1", text: "LiveView main page") end - test "follows redirects", %{conn: conn} do + test_also_with_js "follows redirects", %{conn: conn} do conn |> visit("/live/redirect_on_mount/redirect") |> assert_has("h1", text: "LiveView main page") end - test "follows push redirects (push navigate)", %{conn: conn} do + test_also_with_js "follows push redirects (push navigate)", %{conn: conn} do conn |> visit("/live/redirect_on_mount/push_navigate") |> assert_has("h1", text: "LiveView main page") @@ -77,35 +82,35 @@ defmodule PhoenixTest.LiveTest do end describe "click_link/2" do - test "follows 'navigate' links", %{conn: conn} do + test_also_with_js "follows 'navigate' links", %{conn: conn} do conn |> visit("/live/index") |> click_link("Navigate link") |> assert_has("h1", text: "LiveView page 2") end - test "follows navigation that subsequently redirect", %{conn: conn} do + test_also_with_js "follows navigation that subsequently redirect", %{conn: conn} do conn |> visit("/live/index") |> click_link("Navigate (and redirect back) link") |> assert_has("h1", text: "LiveView main page") end - test "accepts click_link with selector", %{conn: conn} do + test_also_with_js "accepts click_link with selector", %{conn: conn} do conn |> visit("/live/index") |> click_link("a", "Navigate link") |> assert_has("h1", text: "LiveView page 2") end - test "handles patches to current view", %{conn: conn} do + test_also_with_js "handles patches to current view", %{conn: conn} do conn |> visit("/live/index") |> click_link("Patch link") |> assert_has("h2", text: "LiveView main page details") end - test "handles navigation to a non-liveview", %{conn: conn} do + test_also_with_js "handles navigation to a non-liveview", %{conn: conn} do conn |> visit("/live/index") |> click_link("Navigate to non-liveview") @@ -123,24 +128,25 @@ defmodule PhoenixTest.LiveTest do end) end - test "raises error when there are multiple links with same text", %{conn: conn} do - assert_raise ArgumentError, ~r/2 of them matched the text filter/, fn -> + test_also_with_js "raises error when there are multiple links with same text", %{conn: conn} do + # TODO Improve error matching + assert_raise ArgumentError, fn -> conn |> visit("/live/index") |> click_link("Multiple links") end end - test "raises an error when link element can't be found with given text", %{conn: conn} do - assert_raise ArgumentError, ~r/elements but none matched the text filter "No link"/, fn -> + test_also_with_js "raises an error when link element can't be found with given text", %{conn: conn} do + assert_raise ArgumentError, fn -> conn |> visit("/live/index") |> click_link("No link") end end - test "raises an error when there are no links on the page", %{conn: conn} do - assert_raise ArgumentError, ~r/selector "a" did not return any element/, fn -> + test_also_with_js "raises an error when there are no links on the page", %{conn: conn} do + assert_raise ArgumentError, fn -> conn |> visit("/live/page_2") |> click_link("No link") @@ -149,7 +155,7 @@ defmodule PhoenixTest.LiveTest do end describe "click_button/2" do - test "handles a `phx-click` button", %{conn: conn} do + test_also_with_js "handles a `phx-click` button", %{conn: conn} do conn |> visit("/live/index") |> click_button("Show tab") @@ -176,7 +182,7 @@ defmodule PhoenixTest.LiveTest do refute PhoenixTest.ActiveForm.active?(session.active_form) end - test "includes name and value if specified", %{conn: conn} do + test_also_with_js "includes name and value if specified", %{conn: conn} do conn |> visit("/live/index") |> fill_in("User Name", with: "Aragorn") @@ -184,7 +190,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "user:no-phx-change-form-button: save") end - test "includes default data if form is untouched", %{conn: conn} do + test_also_with_js "includes default data if form is untouched", %{conn: conn} do conn |> visit("/live/index") |> click_button("Save Full Form") @@ -196,7 +202,7 @@ defmodule PhoenixTest.LiveTest do |> refute_has("#form-data", text: "disabled_textarea:") end - test "can click button that does not submit form after filling form", %{conn: conn} do + test_also_with_js "can click button that does not submit form after filling form", %{conn: conn} do conn |> visit("/live/index") |> fill_in("Email", with: "some@example.com") @@ -204,7 +210,7 @@ defmodule PhoenixTest.LiveTest do |> refute_has("#form-data", text: "email: some@example.com") end - test "submits owner form if button isn't nested inside form (including button data)", %{conn: conn} do + test_also_with_js "submits owner form if button isn't nested inside form (including button data)", %{conn: conn} do conn |> visit("/live/index") |> within("#owner-form", fn session -> @@ -215,7 +221,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "form-button: save-owner-form") end - test "follows form's redirect to live page", %{conn: conn} do + test_also_with_js "follows form's redirect to live page", %{conn: conn} do conn |> visit("/live/index") |> within("#redirect-form", &fill_in(&1, "Name", with: "Aragorn")) @@ -223,7 +229,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("h1", text: "LiveView page 2") end - test "follows form's redirect to static page", %{conn: conn} do + test_also_with_js "follows form's redirect to static page", %{conn: conn} do conn |> visit("/live/index") |> within("#redirect-form-to-static", &fill_in(&1, "Name", with: "Aragorn")) @@ -231,7 +237,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("h1", text: "Main page") end - test "submits regular (non phx-submit) form", %{conn: conn} do + test_also_with_js "submits regular (non phx-submit) form", %{conn: conn} do conn |> visit("/live/index") |> within("#non-liveview-form", &fill_in(&1, "Name", with: "Aragorn")) @@ -250,7 +256,7 @@ defmodule PhoenixTest.LiveTest do end end - test "raises an error when there are no buttons on page", %{conn: conn} do + test_also_with_js "raises an error when there are no buttons on page", %{conn: conn} do assert_raise ArgumentError, ~r/Could not find an element/, fn -> conn |> visit("/live/page_2") @@ -268,7 +274,7 @@ defmodule PhoenixTest.LiveTest do end end - test "raises an error if active form but can't find button", %{conn: conn} do + test_also_with_js "raises an error if active form but can't find button", %{conn: conn} do msg = ~r/Could not find an element/ assert_raise ArgumentError, msg, fn -> @@ -284,7 +290,7 @@ defmodule PhoenixTest.LiveTest do end describe "within/3" do - test "scopes assertions within selector", %{conn: conn} do + test_also_with_js "scopes assertions within selector", %{conn: conn} do conn |> visit("/live/index") |> assert_has("button", text: "Reset") @@ -293,16 +299,17 @@ defmodule PhoenixTest.LiveTest do end) end - test "scopes further form actions within a selector", %{conn: conn} do + test_also_with_js "scopes further form actions within a selector", %{conn: conn} do conn |> visit("/live/index") |> within("#email-form", fn session -> fill_in(session, "Email", with: "someone@example.com") end) - |> assert_has(input(label: "Email", value: "someone@example.com")) + |> click_button("Save Email") + |> assert_has("#form-data", text: "email: someone@example.com") end - test "raises when data is not in scoped HTML", %{conn: conn} do + test_also_with_js "raises when data is not in scoped HTML", %{conn: conn} do assert_raise ArgumentError, ~r/Could not find element with label "User Name"/, fn -> conn |> visit("/live/index") @@ -314,6 +321,7 @@ defmodule PhoenixTest.LiveTest do end describe "fill_in/4" do + # JS: Browser does not update `input.value` attribute when filling in field test "fills in a single text field based on the label", %{conn: conn} do conn |> visit("/live/index") @@ -321,7 +329,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has(input(label: "Email", value: "someone@example.com")) end - test "can fill input with `nil` to override existing value", %{conn: conn} do + test_also_with_js "can fill input with `nil` to override existing value", %{conn: conn} do conn |> visit("/live/index") |> within("#pre-rendered-data-form", fn session -> @@ -330,7 +338,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "input's value is empty") end - test "can fill-in textareas", %{conn: conn} do + test_also_with_js "can fill-in textareas", %{conn: conn} do conn |> visit("/live/index") |> fill_in("Notes", with: "Dunedain. Heir to the throne. King of Arnor and Gondor") @@ -340,7 +348,7 @@ defmodule PhoenixTest.LiveTest do ) end - test "can fill-in complex form fields", %{conn: conn} do + test_also_with_js "can fill-in complex form fields", %{conn: conn} do conn |> visit("/live/index") |> fill_in("First Name", with: "Aragorn") @@ -352,7 +360,7 @@ defmodule PhoenixTest.LiveTest do ) end - test "can fill in numbers", %{conn: conn} do + test_also_with_js "can fill in numbers", %{conn: conn} do conn |> visit("/live/index") |> fill_in("Level (number)", with: 10) @@ -360,7 +368,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "level: 10") end - test "works in 'nested' forms", %{conn: conn} do + test_also_with_js "works in 'nested' forms", %{conn: conn} do conn |> visit("/live/index") |> fill_in("User Name", with: "Aragorn") @@ -370,7 +378,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "user:role: El Jefe") end - test "can be used to submit form", %{conn: conn} do + test_also_with_js "can be used to submit form", %{conn: conn} do conn |> visit("/live/index") |> fill_in("Email", with: "someone@example.com") @@ -378,7 +386,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "email: someone@example.com") end - test "can be combined with other forms' fill_ins (without pollution)", %{conn: conn} do + test_also_with_js "can be combined with other forms' fill_ins (without pollution)", %{conn: conn} do conn |> visit("/live/index") |> fill_in("Email", with: "frodo@example.com") @@ -387,7 +395,7 @@ defmodule PhoenixTest.LiveTest do |> refute_has("#form-data", text: "email: frodo@example.com") end - test "can target a label with exact: false", %{conn: conn} do + test_also_with_js "can target a label with exact: false", %{conn: conn} do conn |> visit("/live/index") |> within("#complex-labels", fn session -> @@ -396,7 +404,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "name: Frodo") end - test "can target input with selector if multiple labels have same text", %{conn: conn} do + test_also_with_js "can target input with selector if multiple labels have same text", %{conn: conn} do conn |> visit("/live/index") |> within("#same-labels", fn session -> @@ -405,7 +413,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "book-characters: Frodo") end - test "raises an error when element can't be found with label", %{conn: conn} do + test_also_with_js "raises an error when element can't be found with label", %{conn: conn} do msg = ~r/Could not find element with label "Non-existent Email Label"./ assert_raise ArgumentError, msg, fn -> @@ -415,7 +423,7 @@ defmodule PhoenixTest.LiveTest do end end - test "raises an error when label is found but no corresponding input is found", %{conn: conn} do + test_also_with_js "raises an error when label is found but no corresponding input is found", %{conn: conn} do msg = ~r/Found label but can't find labeled element whose `id` matches/ assert_raise ArgumentError, msg, fn -> @@ -427,21 +435,21 @@ defmodule PhoenixTest.LiveTest do end describe "select/3" do - test "selects given option for a label", %{conn: conn} do + test_also_with_js "selects given option for a label", %{conn: conn} do conn |> visit("/live/index") |> select("Elf", from: "Race") |> assert_has("#full-form option[value='elf']") end - test "allows selecting option if a similar option exists", %{conn: conn} do + test_also_with_js "allows selecting option if a similar option exists", %{conn: conn} do conn |> visit("/live/index") |> select("Orc", from: "Race") |> assert_has("#full-form option[value='orc']") end - test "works in 'nested' forms", %{conn: conn} do + test_also_with_js "works in 'nested' forms", %{conn: conn} do conn |> visit("/live/index") |> select("False", from: "User Admin") @@ -449,7 +457,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "user:admin: false") end - test "can be used to submit form", %{conn: conn} do + test_also_with_js "can be used to submit form", %{conn: conn} do conn |> visit("/live/index") |> select("Elf", from: "Race") @@ -457,7 +465,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "race: elf") end - test "works for multiple select", %{conn: conn} do + test_also_with_js "handles multi select", %{conn: conn} do conn |> visit("/live/index") |> select("Elf", from: "Race") @@ -466,7 +474,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "[elf, dwarf]") end - test "works with phx-click outside of forms", %{conn: conn} do + test_also_with_js "works with phx-click outside of forms", %{conn: conn} do conn |> visit("/live/index") |> within("#not-a-form", fn session -> @@ -475,7 +483,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "selected: [dog]") end - test "works with phx-click and multi-select", %{conn: conn} do + test_also_with_js "works with phx-click and multi-select", %{conn: conn} do conn |> visit("/live/index") |> within("#not-a-form", fn session -> @@ -484,30 +492,30 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "selected: [dog, cat]") end - test "can target a label with exact: false", %{conn: conn} do + test_also_with_js "can target a label with exact: false", %{conn: conn} do conn |> visit("/live/index") |> within("#complex-labels", fn session -> - select(session, "Dog", from: "Choose a pet:", exact: false) + select(session, "Cat", from: "Choose a pet:", exact: false) end) - |> assert_has("#form-data", text: "pet: dog") + |> assert_has("#form-data", text: "pet: cat") end - test "can target an option's text with exact_option: false", %{conn: conn} do + test_also_with_js "can target an option's text with exact_option: false", %{conn: conn} do conn |> visit("/live/index") |> within("#full-form", fn session -> - select(session, "Hum", from: "Race", exact_option: false) + select(session, "Dwa", from: "Race", exact_option: false) end) |> submit() - |> assert_has("#form-data", text: "race: human") + |> assert_has("#form-data", text: "race: dwarf") end - test "can target option with selector if multiple labels have same text", %{conn: conn} do + test_also_with_js "can target option with selector if multiple labels have same text", %{conn: conn} do conn |> visit("/live/index") |> within("#same-labels", fn session -> - select(session, "#select-favorite-character", "Frodo", from: "Character") + select(session, "#select-favorite-character", "Sam", from: "Character") end) |> assert_has("#form-data", text: "favorite-character: Frodo") end @@ -519,10 +527,20 @@ defmodule PhoenixTest.LiveTest do select(session, "Dog", from: "Invalid Select Option") end end + + test_also_with_js "second call preserves values of first call for multi select (union)", %{conn: conn} do + conn + |> visit("/live/index") + |> select(["Human", "Elf"], from: "Race 2") + |> select(["Dwarf", "Orc"], from: "Race 2") + |> click_button("Save Full Form") + |> assert_has("#form-data", text: "[human, elf, dwarf, orc]") + end end describe "check/3" do - test "checks a checkbox", %{conn: conn} do + @tag :fix + test_also_with_js "checks a checkbox", %{conn: conn} do conn |> visit("/live/index") |> check("Admin") @@ -530,7 +548,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "admin: on") end - test "can check an unchecked checkbox", %{conn: conn} do + test_also_with_js "can check an unchecked checkbox", %{conn: conn} do conn |> visit("/live/index") |> uncheck("Admin") @@ -539,7 +557,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "admin: on") end - test "handle checkbox name with '?'", %{conn: conn} do + test_also_with_js "handle checkbox name with '?'", %{conn: conn} do conn |> visit("/live/index") |> check("Subscribe") @@ -547,7 +565,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "subscribe?: on") end - test "works in 'nested' forms", %{conn: conn} do + test_also_with_js "works in 'nested' forms", %{conn: conn} do conn |> visit("/live/index") |> check("Payer") @@ -555,7 +573,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "user:payer: on") end - test "works with phx-click outside a form", %{conn: conn} do + test_also_with_js "works with phx-click outside a form", %{conn: conn} do conn |> visit("/live/index") |> within("#not-a-form", fn session -> @@ -564,7 +582,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "value: second-breakfast") end - test "can target a label with exact: false", %{conn: conn} do + test_also_with_js "can target a label with exact: false", %{conn: conn} do conn |> visit("/live/index") |> within("#complex-labels", fn session -> @@ -573,7 +591,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "human: yes") end - test "can specify input selector when multiple checkboxes have same label", %{conn: conn} do + test_also_with_js "can specify input selector when multiple checkboxes have same label", %{conn: conn} do conn |> visit("/live/index") |> within("#same-labels", fn session -> @@ -592,7 +610,7 @@ defmodule PhoenixTest.LiveTest do end describe "uncheck/3" do - test "sends the default value (in hidden input)", %{conn: conn} do + test_also_with_js "sends the default value (in hidden input)", %{conn: conn} do conn |> visit("/live/index") |> uncheck("Admin") @@ -600,7 +618,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "admin: off") end - test "can uncheck a previous check/2 in the test", %{conn: conn} do + test_also_with_js "can uncheck a previous check/2 in the test", %{conn: conn} do conn |> visit("/live/index") |> check("Admin") @@ -609,7 +627,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "admin: off") end - test "works in 'nested' forms", %{conn: conn} do + test_also_with_js "works in 'nested' forms", %{conn: conn} do conn |> visit("/live/index") |> check("Payer") @@ -618,7 +636,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "user:payer: off") end - test "works with phx-click outside a form", %{conn: conn} do + test_also_with_js "works with phx-click outside a form", %{conn: conn} do conn |> visit("/live/index") |> within("#not-a-form", fn session -> @@ -629,7 +647,7 @@ defmodule PhoenixTest.LiveTest do |> refute_has("#form-data", text: "value: second-breakfast") end - test "can target a label with exact: false", %{conn: conn} do + test_also_with_js "can target a label with exact: false", %{conn: conn} do conn |> visit("/live/index") |> within("#complex-labels", fn session -> @@ -640,7 +658,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "human: no") end - test "can specify input selector when multiple checkboxes have same label", %{conn: conn} do + test_also_with_js "can specify input selector when multiple checkboxes have same label", %{conn: conn} do conn |> visit("/live/index") |> within("#same-labels", fn session -> @@ -662,7 +680,7 @@ defmodule PhoenixTest.LiveTest do end describe "choose/3" do - test "chooses an option in radio button", %{conn: conn} do + test_also_with_js "chooses an option in radio button", %{conn: conn} do conn |> visit("/live/index") |> choose("Email Choice") @@ -670,14 +688,14 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "contact: email") end - test "uses the default 'checked' if present", %{conn: conn} do + test_also_with_js "uses the default 'checked' if present", %{conn: conn} do conn |> visit("/live/index") |> click_button("Save Full Form") |> assert_has("#form-data", text: "contact: mail") end - test "works with a phx-click outside of a form", %{conn: conn} do + test_also_with_js "works with a phx-click outside of a form", %{conn: conn} do conn |> visit("/live/index") |> within("#not-a-form", fn session -> @@ -686,7 +704,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "value: huey") end - test "can target a label with exact: false", %{conn: conn} do + test_also_with_js "can target a label with exact: false", %{conn: conn} do conn |> visit("/live/index") |> within("#complex-labels", fn session -> @@ -695,7 +713,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "book-or-movie: book") end - test "can specify input selector when multiple options have same label in same form", %{conn: conn} do + test_also_with_js "can specify input selector when multiple options have same label in same form", %{conn: conn} do conn |> visit("/live/index") |> within("#same-labels", fn session -> @@ -714,7 +732,7 @@ defmodule PhoenixTest.LiveTest do end describe "upload/4" do - test "uploads an image", %{conn: conn} do + test_also_with_js "uploads an image", %{conn: conn} do conn |> visit("/live/index") |> within("#full-form", fn session -> @@ -725,7 +743,8 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "avatar: elixir.jpg") end - test "can target a label with exact: false", %{conn: conn} do + @tag :fix + test_also_with_js "can target a label with exact: false", %{conn: conn} do conn |> visit("/live/index") |> within("#complex-labels", fn session -> @@ -733,10 +752,11 @@ defmodule PhoenixTest.LiveTest do |> upload("Avatar", "test/files/elixir.jpg", exact: false) |> click_button("Save") end) - |> assert_has("#form-data", text: "avatar: elixir.jpg") + |> assert_has("#form-data", text: "complex_avatar: elixir.jpg") end - test "can specify input selector when multiple inputs have same label", %{conn: conn} do + @tag :fix + test_also_with_js "can specify input selector when multiple inputs have same label", %{conn: conn} do conn |> visit("/live/index") |> within("#same-labels", fn session -> @@ -747,7 +767,9 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "main_avatar: elixir.jpg") end - test "upload (without other form actions) does not work with submit (matches browser behavior)", %{conn: conn} do + @tag :fix + test "upload (without other form actions) does not work with submit (matches browser behavior)", + %{conn: conn} do session = conn |> visit("/live/index") @@ -762,7 +784,7 @@ defmodule PhoenixTest.LiveTest do end describe "filling out full form with field functions" do - test "populates all fields", %{conn: conn} do + test_also_with_js "populates all fields", %{conn: conn} do conn |> visit("/live/index") |> fill_in("First Name", with: "Legolas") @@ -780,7 +802,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "notes: Woodland Elf") end - test "populates all fields in nested forms", %{conn: conn} do + test_also_with_js "populates all fields in nested forms", %{conn: conn} do conn |> visit("/live/index") |> fill_in("User Name", with: "Legolas") @@ -792,7 +814,7 @@ defmodule PhoenixTest.LiveTest do end describe "submit/1" do - test "submits a pre-filled form via phx-submit", %{conn: conn} do + test_also_with_js "submits a pre-filled form via phx-submit", %{conn: conn} do conn |> visit("/live/index") |> fill_in("Email", with: "some@example.com") @@ -800,7 +822,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "email: some@example.com") end - test "includes pre-rendered data", %{conn: conn} do + test_also_with_js "includes pre-rendered data", %{conn: conn} do conn |> visit("/live/index") |> fill_in("First Name", with: "Aragorn") @@ -810,7 +832,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "contact: mail") end - test "includes the first button's name and value if present", %{conn: conn} do + test_also_with_js "includes the first button's name and value if present", %{conn: conn} do conn |> visit("/live/index") |> fill_in("First Name", with: "Aragorn") @@ -818,7 +840,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "full_form_button: save") end - test "can submit form without button", %{conn: conn} do + test_also_with_js "can submit form without button", %{conn: conn} do conn |> visit("/live/index") |> fill_in("Country of Origin", with: "Arnor") @@ -826,7 +848,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "country: Arnor") end - test "follows form's redirect to live page", %{conn: conn} do + test_also_with_js "follows form's redirect to live page", %{conn: conn} do conn |> visit("/live/index") |> within("#redirect-form", fn session -> @@ -837,7 +859,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("h1", text: "LiveView page 2") end - test "follows form's redirect to static page", %{conn: conn} do + test_also_with_js "follows form's redirect to static page", %{conn: conn} do conn |> visit("/live/index") |> within("#redirect-form-to-static", fn session -> @@ -863,7 +885,7 @@ defmodule PhoenixTest.LiveTest do end) end - test "submits regular (non phx-submit) form", %{conn: conn} do + test_also_with_js "submits regular (non phx-submit) form", %{conn: conn} do conn |> visit("/live/index") |> within("#non-liveview-form", fn session -> @@ -875,7 +897,7 @@ defmodule PhoenixTest.LiveTest do |> assert_has("#form-data", text: "button: save") end - test "raises an error if there's no active form", %{conn: conn} do + test_also_with_js "raises an error if there's no active form", %{conn: conn} do message = ~r/There's no active form. Fill in a form with `fill_in`, `select`, etc./ assert_raise ArgumentError, message, fn -> @@ -1001,21 +1023,22 @@ defmodule PhoenixTest.LiveTest do end describe "shared form helpers behavior" do - test "triggers phx-change validations", %{conn: conn} do + test_also_with_js "triggers phx-change validations", %{conn: conn} do conn |> visit("/live/index") + |> fill_in("Email", with: "email") |> fill_in("Email", with: nil) |> assert_has("#form-errors", text: "Errors present") end - test "sends _target with phx-change events", %{conn: conn} do + test_also_with_js "sends _target with phx-change events", %{conn: conn} do conn |> visit("/live/index") |> fill_in("Email", with: "frodo@example.com") |> assert_has("#form-data", text: "_target: [email]") end - test "does not trigger phx-change event if one isn't present", %{conn: conn} do + test_also_with_js "does not trigger phx-change event if one isn't present", %{conn: conn} do session = visit(conn, "/live/index") starting_html = Driver.render_html(session) @@ -1028,14 +1051,14 @@ defmodule PhoenixTest.LiveTest do assert starting_html == ending_html end - test "follows redirects on phx-change", %{conn: conn} do + test_also_with_js "follows redirects on phx-change", %{conn: conn} do conn |> visit("/live/index") |> fill_in("Email with redirect", with: "someone@example.com") |> assert_has("h1", text: "LiveView page 2") end - test "preserves correct order of active form vs form data", %{conn: conn} do + test_also_with_js "preserves correct order of active form vs form data", %{conn: conn} do conn |> visit("/live/index") |> within("#changes-hidden-input-form", fn session -> diff --git a/test/phoenix_test/static_test.exs b/test/phoenix_test/static_test.exs index 804a4218..d644d842 100644 --- a/test/phoenix_test/static_test.exs +++ b/test/phoenix_test/static_test.exs @@ -4,12 +4,16 @@ defmodule PhoenixTest.StaticTest do import PhoenixTest import PhoenixTest.TestHelpers - setup do - %{conn: Phoenix.ConnTest.build_conn()} + require PhoenixTest.TestHelpers + + setup context do + conn = Phoenix.ConnTest.build_conn() + conn = if context[:js], do: with_js_driver(conn), else: conn + %{conn: conn} end describe "render_page_title/1" do - test "renders the page title", %{conn: conn} do + test_also_with_js "renders the page title", %{conn: conn} do title = conn |> visit("/page/index") @@ -18,7 +22,7 @@ defmodule PhoenixTest.StaticTest do assert title == "PhoenixTest is the best!" end - test "renders nil if there's no page title", %{conn: conn} do + test_also_with_js "renders nil if there's no page title", %{conn: conn} do title = conn |> visit("/page/index_no_layout") @@ -29,13 +33,13 @@ defmodule PhoenixTest.StaticTest do end describe "visit/2" do - test "navigates to given static page", %{conn: conn} do + test_also_with_js "navigates to given static page", %{conn: conn} do conn |> visit("/page/index") |> assert_has("h1", text: "Main page") end - test "follows redirects", %{conn: conn} do + test_also_with_js "follows redirects", %{conn: conn} do conn |> visit("/page/redirect_to_static") |> assert_has("h1", text: "Main page") @@ -59,21 +63,21 @@ defmodule PhoenixTest.StaticTest do end describe "click_link/2" do - test "follows link's path", %{conn: conn} do + test_also_with_js "follows link's path", %{conn: conn} do conn |> visit("/page/index") |> click_link("Page 2") |> assert_has("h1", text: "Page 2") end - test "follows link that subsequently redirects", %{conn: conn} do + test_also_with_js "follows link that subsequently redirects", %{conn: conn} do conn |> visit("/page/index") |> click_link("Navigate away and redirect back") |> assert_has("h1", text: "Main page") end - test "accepts selector for link", %{conn: conn} do + test_also_with_js "accepts selector for link", %{conn: conn} do conn |> visit("/page/index") |> click_link("a", "Page 2") @@ -91,14 +95,14 @@ defmodule PhoenixTest.StaticTest do end) end - test "handles navigation to a LiveView", %{conn: conn} do + test_also_with_js "handles navigation to a LiveView", %{conn: conn} do conn |> visit("/page/index") |> click_link("To LiveView!") |> assert_has("h1", text: "LiveView main page") end - test "handles form submission via `data-method` & `data-to` attributes", %{conn: conn} do + test_also_with_js "handles form submission via `data-method` & `data-to` attributes", %{conn: conn} do conn |> visit("/page/index") |> click_link("Data-method Delete") @@ -135,24 +139,24 @@ defmodule PhoenixTest.StaticTest do end end - test "raises error when there are multiple links with same text", %{conn: conn} do - assert_raise ArgumentError, ~r/Found more than one element with selector/, fn -> + test_also_with_js "raises error when there are multiple links with same text", %{conn: conn} do + assert_raise ArgumentError, fn -> conn |> visit("/page/index") |> click_link("Multiple links") end end - test "raises an error when link element can't be found with given text", %{conn: conn} do - assert_raise ArgumentError, ~r/Could not find element with selector/, fn -> + test_also_with_js "raises an error when link element can't be found with given text", %{conn: conn} do + assert_raise ArgumentError, fn -> conn |> visit("/page/index") |> click_link("No link") end end - test "raises an error when there are no links on the page", %{conn: conn} do - assert_raise ArgumentError, ~r/Could not find element with selector/, fn -> + test_also_with_js "raises an error when there are no links on the page", %{conn: conn} do + assert_raise ArgumentError, fn -> conn |> visit("/page/page_2") |> click_link("No link") @@ -161,35 +165,35 @@ defmodule PhoenixTest.StaticTest do end describe "click_button/2" do - test "handles a button that defaults to GET", %{conn: conn} do + test_also_with_js "handles a button that defaults to GET", %{conn: conn} do conn |> visit("/page/index") |> click_button("Get record") |> assert_has("h1", text: "Record received") end - test "accepts selector for button", %{conn: conn} do + test_also_with_js "accepts selector for button", %{conn: conn} do conn |> visit("/page/index") |> click_button("button", "Get record") |> assert_has("h1", text: "Record received") end - test "handles a button clicks when button PUTs data (hidden input)", %{conn: conn} do + test_also_with_js "handles a button clicks when button PUTs data (hidden input)", %{conn: conn} do conn |> visit("/page/index") |> click_button("Mark as active") |> assert_has("h1", text: "Record updated") end - test "handles a button clicks when button DELETEs data (hidden input)", %{conn: conn} do + test_also_with_js "handles a button clicks when button DELETEs data (hidden input)", %{conn: conn} do conn |> visit("/page/index") |> click_button("Delete record") |> assert_has("h1", text: "Record deleted") end - test "can submit forms with input type submit", %{conn: conn} do + test_also_with_js "can submit forms with input type submit", %{conn: conn} do conn |> visit("/page/index") |> fill_in("Email", with: "sample@example.com") @@ -197,7 +201,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "email: sample@example.com") end - test "can handle clicking button that does not submit form after filling a form", %{conn: conn} do + test_also_with_js "can handle clicking button that does not submit form after filling a form", %{conn: conn} do conn |> visit("/page/index") |> fill_in("Email", with: "some@example.com") @@ -205,7 +209,7 @@ defmodule PhoenixTest.StaticTest do |> refute_has("#form-data", text: "email: some@example.com") end - test "submits owner form if button isn't nested inside form", %{conn: conn} do + test_also_with_js "submits owner form if button isn't nested inside form", %{conn: conn} do conn |> visit("/page/index") |> within("#owner-form", fn session -> @@ -215,14 +219,14 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "name: Aragorn") end - test "can handle redirects to a LiveView", %{conn: conn} do + test_also_with_js "can handle redirects to a LiveView", %{conn: conn} do conn |> visit("/page/index") |> click_button("Post and Redirect") |> assert_has("h1", text: "LiveView main page") end - test "handles form submission via `data-method` & `data-to` attributes", %{conn: conn} do + test_also_with_js "handles form submission via `data-method` & `data-to` attributes", %{conn: conn} do conn |> visit("/page/index") |> click_button("Data-method Delete") @@ -249,7 +253,7 @@ defmodule PhoenixTest.StaticTest do refute PhoenixTest.ActiveForm.active?(session.active_form) end - test "includes name and value if specified", %{conn: conn} do + test_also_with_js "includes name and value if specified", %{conn: conn} do conn |> visit("/page/index") |> fill_in("User Name", with: "Aragorn") @@ -257,7 +261,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "user:save-button: nested-form-save") end - test "can handle clicking button that does not submit form after fill_in", %{conn: conn} do + test_also_with_js "can handle clicking button that does not submit form after fill_in", %{conn: conn} do conn |> visit("/page/index") |> fill_in("Email", with: "some@example.com") @@ -265,7 +269,7 @@ defmodule PhoenixTest.StaticTest do |> refute_has("#form-data", text: "email: some@example.com") end - test "includes default data if form is untouched", %{conn: conn} do + test_also_with_js "includes default data if form is untouched", %{conn: conn} do conn |> visit("/page/index") |> click_button("Save Full Form") @@ -287,7 +291,7 @@ defmodule PhoenixTest.StaticTest do end end - test "raises an error when there are no buttons on page", %{conn: conn} do + test_also_with_js "raises an error when there are no buttons on page", %{conn: conn} do msg = ~r/Could not find an element with given selectors/ assert_raise ArgumentError, msg, fn -> @@ -297,7 +301,7 @@ defmodule PhoenixTest.StaticTest do end end - test "raises an error if can't find button", %{conn: conn} do + test_also_with_js "raises an error if can't find button", %{conn: conn} do msg = ~r/Could not find an element with given selectors/ assert_raise ArgumentError, msg, fn -> @@ -320,7 +324,7 @@ defmodule PhoenixTest.StaticTest do end describe "within/3" do - test "scopes assertions within selector", %{conn: conn} do + test_also_with_js "scopes assertions within selector", %{conn: conn} do conn |> visit("/page/index") |> assert_has("button", text: "Get record") @@ -329,7 +333,7 @@ defmodule PhoenixTest.StaticTest do end) end - test "scopes further form actions within a selector", %{conn: conn} do + test_also_with_js "scopes further form actions within a selector", %{conn: conn} do conn |> visit("/page/index") |> within("#email-form", fn session -> @@ -340,7 +344,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "email: someone@example.com") end - test "raises when data is not in scoped HTML", %{conn: conn} do + test_also_with_js "raises when data is not in scoped HTML", %{conn: conn} do assert_raise ArgumentError, ~r/Could not find element with label "User Name"/, fn -> conn |> visit("/page/index") @@ -352,7 +356,7 @@ defmodule PhoenixTest.StaticTest do end describe "fill_in/4" do - test "fills in a single text field based on the label", %{conn: conn} do + test_also_with_js "fills in a single text field based on the label", %{conn: conn} do conn |> visit("/page/index") |> fill_in("Email", with: "someone@example.com") @@ -360,7 +364,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "email: someone@example.com") end - test "can fill input with `nil` to override existing value", %{conn: conn} do + test_also_with_js "can fill input with `nil` to override existing value", %{conn: conn} do conn |> visit("/page/index") |> fill_in("Pre Rendered Input", with: nil) @@ -368,7 +372,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "input's value is empty") end - test "can fill-in complex form fields", %{conn: conn} do + test_also_with_js "can fill-in complex form fields", %{conn: conn} do conn |> visit("/page/index") |> fill_in("First Name", with: "Aragorn") @@ -380,7 +384,7 @@ defmodule PhoenixTest.StaticTest do ) end - test "can fill in numbers", %{conn: conn} do + test_also_with_js "can fill in numbers", %{conn: conn} do conn |> visit("/page/index") |> fill_in("Level (number)", with: 10) @@ -388,7 +392,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "level: 10") end - test "works in 'nested' forms", %{conn: conn} do + test_also_with_js "works in 'nested' forms", %{conn: conn} do conn |> visit("/page/index") |> fill_in("User Name", with: "Aragorn") @@ -399,7 +403,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "user:role: El Jefe") end - test "can be combined with other forms' fill_ins (without pollution)", %{conn: conn} do + test_also_with_js "can be combined with other forms' fill_ins (without pollution)", %{conn: conn} do conn |> visit("/page/index") |> fill_in("First Name", with: "Aragorn") @@ -409,7 +413,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "user:name: Legolas") end - test "can target a label with exact: false", %{conn: conn} do + test_also_with_js "can target a label with exact: false", %{conn: conn} do conn |> visit("/page/index") |> within("#complex-labels", fn session -> @@ -419,7 +423,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "name: Frodo") end - test "can target input with selector if multiple labels have same text", %{conn: conn} do + test_also_with_js "can target input with selector if multiple labels have same text", %{conn: conn} do conn |> visit("/page/index") |> within("#same-labels", fn session -> @@ -429,7 +433,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "book-characters: Frodo") end - test "raises an error when element can't be found with label", %{conn: conn} do + test_also_with_js "raises an error when element can't be found with label", %{conn: conn} do msg = ~r/Could not find element with label "Non-existent Email Label"./ assert_raise ArgumentError, msg, fn -> @@ -439,7 +443,7 @@ defmodule PhoenixTest.StaticTest do end end - test "raises an error when label is found but no corresponding input is found", %{conn: conn} do + test_also_with_js "raises an error when label is found but no corresponding input is found", %{conn: conn} do msg = ~r/Found label but can't find labeled element whose `id` matches/ assert_raise ArgumentError, msg, fn -> @@ -451,7 +455,7 @@ defmodule PhoenixTest.StaticTest do end describe "select/3" do - test "selects given option for a label", %{conn: conn} do + test_also_with_js "selects given option for a label", %{conn: conn} do conn |> visit("/page/index") |> select("Elf", from: "Race") @@ -459,21 +463,21 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "race: elf") end - test "picks first by default", %{conn: conn} do + test_also_with_js "picks first by default", %{conn: conn} do conn |> visit("/page/index") |> click_button("Save Full Form") |> assert_has("#form-data", text: "race: human") end - test "allows selecting option if a similar option exists", %{conn: conn} do + test_also_with_js "allows selecting option if a similar option exists", %{conn: conn} do conn |> visit("/page/index") |> select("Orc", from: "Race") |> assert_has("#full-form option[value='orc']") end - test "works in 'nested' forms", %{conn: conn} do + test_also_with_js "works in 'nested' forms", %{conn: conn} do conn |> visit("/page/index") |> select("False", from: "User Admin") @@ -481,7 +485,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "user:admin: false") end - test "handles multi select", %{conn: conn} do + test_also_with_js "handles multi select", %{conn: conn} do conn |> visit("/page/index") |> select(["Elf", "Dwarf"], from: "Race 2") @@ -489,14 +493,23 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "race_2: [elf,dwarf]") end - test "contains no data for empty multi select", %{conn: conn} do + test_also_with_js "second call preserves values of first call for multi select (union)", %{conn: conn} do + conn + |> visit("/live/index") + |> select(["Human", "Elf"], from: "Race 2") + |> select(["Dwarf", "Orc"], from: "Race 2") + |> click_button("Save Full Form") + |> assert_has("#form-data", text: "[human, elf, dwarf, orc]") + end + + test_also_with_js "contains no data for empty multi select", %{conn: conn} do conn |> visit("/page/index") |> click_button("Save Full Form") |> refute_has("#form-data", text: "race_2") end - test "can target a label with exact: false", %{conn: conn} do + test_also_with_js "can target a label with exact: false", %{conn: conn} do conn |> visit("/page/index") |> within("#complex-labels", fn session -> @@ -506,7 +519,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "pet: dog") end - test "can target an option's text with exact_option: false", %{conn: conn} do + test_also_with_js "can target an option's text with exact_option: false", %{conn: conn} do conn |> visit("/page/index") |> within("#full-form", fn session -> @@ -516,7 +529,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "race: human") end - test "can target option with selector if multiple labels have same text", %{conn: conn} do + test_also_with_js "can target option with selector if multiple labels have same text", %{conn: conn} do conn |> visit("/page/index") |> within("#same-labels", fn session -> @@ -528,7 +541,7 @@ defmodule PhoenixTest.StaticTest do end describe "check/3" do - test "checks a checkbox", %{conn: conn} do + test_also_with_js "checks a checkbox", %{conn: conn} do conn |> visit("/page/index") |> check("Admin (boolean)") @@ -536,7 +549,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "admin_boolean: true") end - test "sets checkbox value as 'on' by default", %{conn: conn} do + test_also_with_js "sets checkbox value as 'on' by default", %{conn: conn} do conn |> visit("/page/index") |> check("Admin") @@ -544,7 +557,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "admin: on") end - test "can check an unchecked checkbox", %{conn: conn} do + test_also_with_js "can check an unchecked checkbox", %{conn: conn} do conn |> visit("/page/index") |> uncheck("Admin") @@ -553,7 +566,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "admin: on") end - test "handle checkbox name with '?'", %{conn: conn} do + test_also_with_js "handle checkbox name with '?'", %{conn: conn} do conn |> visit("/page/index") |> check("Subscribe") @@ -561,7 +574,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "subscribe?: on") end - test "can target a label with exact: false", %{conn: conn} do + test_also_with_js "can target a label with exact: false", %{conn: conn} do conn |> visit("/page/index") |> within("#complex-labels", fn session -> @@ -571,7 +584,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "human: yes") end - test "can specify input selector when multiple checkboxes have same label", %{conn: conn} do + test_also_with_js "can specify input selector when multiple checkboxes have same label", %{conn: conn} do conn |> visit("/page/index") |> within("#same-labels", fn session -> @@ -583,7 +596,7 @@ defmodule PhoenixTest.StaticTest do end describe "uncheck/3" do - test "sends the default value (in hidden input)", %{conn: conn} do + test_also_with_js "sends the default value (in hidden input)", %{conn: conn} do conn |> visit("/page/index") |> uncheck("Admin") @@ -591,7 +604,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "admin: off") end - test "can uncheck a previous check/2 in the test", %{conn: conn} do + test_also_with_js "can uncheck a previous check/2 in the test", %{conn: conn} do conn |> visit("/page/index") |> check("Admin") @@ -600,7 +613,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "admin: off") end - test "can target a label with exact: false", %{conn: conn} do + test_also_with_js "can target a label with exact: false", %{conn: conn} do conn |> visit("/page/index") |> within("#complex-labels", fn session -> @@ -612,7 +625,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "human: no") end - test "can specify input selector when multiple checkboxes have same label", %{conn: conn} do + test_also_with_js "can specify input selector when multiple checkboxes have same label", %{conn: conn} do conn |> visit("/page/index") |> within("#same-labels", fn session -> @@ -627,7 +640,7 @@ defmodule PhoenixTest.StaticTest do end describe "choose/3" do - test "chooses an option in radio button", %{conn: conn} do + test_also_with_js "chooses an option in radio button", %{conn: conn} do conn |> visit("/page/index") |> choose("Email Choice") @@ -635,14 +648,14 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "contact: email") end - test "uses the default 'checked' if present", %{conn: conn} do + test_also_with_js "uses the default 'checked' if present", %{conn: conn} do conn |> visit("/page/index") |> click_button("Save Full Form") |> assert_has("#form-data", text: "contact: mail") end - test "can target a label with exact: false", %{conn: conn} do + test_also_with_js "can target a label with exact: false", %{conn: conn} do conn |> visit("/page/index") |> within("#complex-labels", fn session -> @@ -652,7 +665,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "book-or-movie: book") end - test "can specify input selector when multiple options have same label in same form", %{conn: conn} do + test_also_with_js "can specify input selector when multiple options have same label in same form", %{conn: conn} do conn |> visit("/page/index") |> within("#same-labels", fn session -> @@ -665,7 +678,7 @@ defmodule PhoenixTest.StaticTest do end describe "upload/4" do - test "uploads image", %{conn: conn} do + test_also_with_js "uploads image", %{conn: conn} do conn |> visit("/page/index") |> within("#file-upload-form", fn session -> @@ -676,6 +689,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "avatar: elixir.jpg") end + # TODO Fix multiple uploads for wallaby (don't replace existing file) test "uploads image list", %{conn: conn} do conn |> visit("/page/index") @@ -686,7 +700,8 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "avatars:[]: phoenix.jpg") end - test "uploads an image in nested forms", %{conn: conn} do + @tag :fix + test_also_with_js "uploads an image in nested forms", %{conn: conn} do conn |> visit("/page/index") |> upload("Nested Avatar", "test/files/elixir.jpg") @@ -694,7 +709,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "user:avatar: elixir.jpg") end - test "can target a label with exact: false", %{conn: conn} do + test_also_with_js "can target a label with exact: false", %{conn: conn} do conn |> visit("/page/index") |> within("#complex-labels", fn session -> @@ -705,6 +720,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "avatar: elixir.jpg") end + # TODO test_also_with_js test "can specify input selector when multiple inputs have same label", %{conn: conn} do conn |> visit("/page/index") @@ -717,7 +733,7 @@ defmodule PhoenixTest.StaticTest do end describe "filling out full form with field functions" do - test "populates all fields", %{conn: conn} do + test_also_with_js "populates all fields", %{conn: conn} do conn |> visit("/page/index") |> fill_in("First Name", with: "Legolas") @@ -735,7 +751,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "notes: Woodland Elf") end - test "populates all fields in nested forms", %{conn: conn} do + test_also_with_js "populates all fields in nested forms", %{conn: conn} do conn |> visit("/page/index") |> fill_in("User Name", with: "Legolas") @@ -747,7 +763,7 @@ defmodule PhoenixTest.StaticTest do end describe "submit/1" do - test "submits form even if no submit is present (acts as )", %{conn: conn} do + test_also_with_js "submits form even if no submit is present (acts as )", %{conn: conn} do conn |> visit("/page/index") |> within("#no-submit-button-form", fn session -> @@ -758,8 +774,8 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "name: Aragorn") end - test "includes pre-rendered data (input value, selected option, checked checkbox, checked radio button)", - %{conn: conn} do + test_also_with_js "includes pre-rendered data (input value, selected option, checked checkbox, checked radio button)", + %{conn: conn} do conn |> visit("/page/index") |> fill_in("First Name", with: "Aragorn") @@ -768,7 +784,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "race: human") end - test "includes the first button's name and value if present", %{conn: conn} do + test_also_with_js "includes the first button's name and value if present", %{conn: conn} do conn |> visit("/page/index") |> fill_in("First Name", with: "Aragorn") @@ -776,7 +792,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "full_form_button: save") end - test "can submit form without button", %{conn: conn} do + test_also_with_js "can submit form without button", %{conn: conn} do conn |> visit("/page/index") |> fill_in("Country of Origin", with: "Arnor") @@ -784,7 +800,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "country: Arnor") end - test "can handle redirects", %{conn: conn} do + test_also_with_js "can handle redirects", %{conn: conn} do conn |> visit("/page/index") |> within("#no-submit-button-and-redirect", fn session -> @@ -810,7 +826,7 @@ defmodule PhoenixTest.StaticTest do end) end - test "handles when form PUTs data through hidden input", %{conn: conn} do + test_also_with_js "handles when form PUTs data through hidden input", %{conn: conn} do conn |> visit("/page/index") |> within("#update-form", fn session -> @@ -821,7 +837,7 @@ defmodule PhoenixTest.StaticTest do |> assert_has("#form-data", text: "name: Aragorn") end - test "handles a button clicks when button DELETEs data (hidden input)", %{conn: conn} do + test_also_with_js "handles a button clicks when button DELETEs data (hidden input)", %{conn: conn} do conn |> visit("/page/index") |> click_button("Delete record") @@ -862,7 +878,7 @@ defmodule PhoenixTest.StaticTest do %{open_fun: open_fun} end - test "opens the browser ", %{conn: conn, open_fun: open_fun} do + test_also_with_js "opens the browser ", %{conn: conn, open_fun: open_fun} do conn |> visit("/page/index") |> open_browser(open_fun) @@ -897,19 +913,19 @@ defmodule PhoenixTest.StaticTest do end describe "current_path" do - test "it is set on visit", %{conn: conn} do + test_also_with_js "it is set on visit", %{conn: conn} do session = visit(conn, "/page/index") assert PhoenixTest.Driver.current_path(session) == "/page/index" end - test "it includes query string if available", %{conn: conn} do + test_also_with_js "it includes query string if available", %{conn: conn} do session = visit(conn, "/page/index?foo=bar") assert PhoenixTest.Driver.current_path(session) == "/page/index?foo=bar" end - test "it is updated on href navigation", %{conn: conn} do + test_also_with_js "it is updated on href navigation", %{conn: conn} do session = conn |> visit("/page/index") @@ -918,7 +934,7 @@ defmodule PhoenixTest.StaticTest do assert PhoenixTest.Driver.current_path(session) == "/page/page_2?foo=bar" end - test "it is updated on redirects", %{conn: conn} do + test_also_with_js "it is updated on redirects", %{conn: conn} do session = conn |> visit("/page/index") diff --git a/test/phoenix_test/wallaby_test.exs b/test/phoenix_test/wallaby_test.exs new file mode 100644 index 00000000..e081c1aa --- /dev/null +++ b/test/phoenix_test/wallaby_test.exs @@ -0,0 +1,73 @@ +defmodule PhoenixTest.WallabyTest do + use ExUnit.Case, async: true + + import PhoenixTest + + setup do + conn = with_js_driver(Phoenix.ConnTest.build_conn()) + %{conn: conn} + end + + describe "unwrap" do + test "provides an escape hatch that gives access to the underlying view", %{conn: conn} do + conn + |> visit("/live/index") + |> unwrap(fn session -> + session + |> Wallaby.Browser.clear(Wallaby.Query.text_field("Notes")) + |> Wallaby.Browser.fill_in(Wallaby.Query.text_field("Notes"), with: "Wow") + |> Wallaby.Browser.click(Wallaby.Query.button("Save Full Form")) + end) + |> assert_has("#form-data", text: "notes: Wow") + end + end + + describe "assert_has/3 title" do + test "asserts on page title", %{conn: conn} do + conn + |> visit("/live/index") + |> assert_has("title", text: "PhoenixTest is the best!") + end + + test "asserts on updated page title", %{conn: conn} do + conn + |> visit("/live/index") + |> click_button("Change page title") + |> assert_has("title", text: "Title changed!") + end + + test "refutes missing page title", %{conn: conn} do + conn + |> visit("/live/index_no_layout") + |> refute_has("title") + end + end + + describe "assert_path" do + test "it asserts on visit", %{conn: conn} do + conn + |> visit("/live/index") + |> assert_path("/live/index") + end + + test "it asserts on visit with query string", %{conn: conn} do + conn + |> visit("/live/index?foo=bar") + |> assert_path("/live/index", query_params: %{foo: "bar"}) + end + + test "it asserts on href navigation", %{conn: conn} do + conn + |> visit("/live/index") + |> click_link("Navigate to non-liveview") + |> assert_path("/page/index", query_params: %{details: true, foo: "bar"}) + end + + test "it asserts on live navigation", %{conn: conn} do + conn + |> visit("/live/index") + |> click_link("Navigate link") + |> assert_path("/live/page_2", query_params: %{details: true, foo: "bar"}) + end + end +end diff --git a/test/support/index_live.ex b/test/support/index_live.ex index 97665ad2..6572ce01 100644 --- a/test/support/index_live.ex +++ b/test/support/index_live.ex @@ -39,7 +39,7 @@ defmodule PhoenixTest.IndexLive do -
+ @@ -127,26 +127,26 @@ defmodule PhoenixTest.IndexLive do
-
+ - + - + - + - + - @@ -155,7 +155,7 @@ defmodule PhoenixTest.IndexLive do - @@ -165,24 +165,27 @@ defmodule PhoenixTest.IndexLive do
Please select your preferred contact method:
- + - +
- + - + <.live_file_input upload={@uploads.avatar} /> @@ -255,7 +258,7 @@ defmodule PhoenixTest.IndexLive do
-
+ @@ -265,7 +268,7 @@ defmodule PhoenixTest.IndexLive do Human * - + - + Do you like Erlang - +
@@ -404,7 +407,7 @@ defmodule PhoenixTest.IndexLive do
- @@ -414,6 +417,7 @@ defmodule PhoenixTest.IndexLive do
allow_upload(:avatar, accept: ~w(.jpg .jpeg)) |> allow_upload(:main_avatar, accept: ~w(.jpg .jpeg)) |> allow_upload(:backup_avatar, accept: ~w(.jpg .jpeg)) + |> allow_upload(:complex_avatar, accept: ~w(.jpg .jpeg)) } end @@ -474,6 +479,15 @@ defmodule PhoenixTest.IndexLive do {:noreply, assign(socket, :show_tab, true)} end + def handle_event("validate-form", form_data, socket) do + { + :noreply, + socket + |> assign(:form_saved, true) + |> assign(:form_data, form_data) + } + end + def handle_event("save-form", form_data, socket) do avatars = consume_uploaded_entries(socket, :avatar, fn _, %{client_name: name} -> @@ -483,10 +497,14 @@ defmodule PhoenixTest.IndexLive do main_avatars = consume_uploaded_entries(socket, :main_avatar, fn _, %{client_name: name} -> {:ok, name} end) + complex_avatars = + consume_uploaded_entries(socket, :complex_avatar, fn _, %{client_name: name} -> {:ok, name} end) + form_data = form_data |> Map.put("avatar", List.first(avatars)) |> Map.put("main_avatar", List.first(main_avatars)) + |> Map.put("complex_avatar", List.first(complex_avatars)) { :noreply, @@ -547,6 +565,12 @@ defmodule PhoenixTest.IndexLive do |> assign(:country, country) |> assign(:cities, ["Santa Cruz", "La Paz", "Cochabamba", "Other"]) |> then(&{:noreply, &1}) + + "Argentina" -> + socket + |> assign(:country, country) + |> assign(:cities, ["Buenos Aires", "Cordoba", "Other"]) + |> then(&{:noreply, &1}) end end diff --git a/test/support/page_view.ex b/test/support/page_view.ex index da79cb0d..9831ab7d 100644 --- a/test/support/page_view.ex +++ b/test/support/page_view.ex @@ -13,8 +13,8 @@ defmodule PhoenixTest.PageView do <%= assigns[:page_title] || "PhoenixTest is the best!" %> - + @@ -180,7 +180,7 @@ defmodule PhoenixTest.PageView do - @@ -200,9 +200,7 @@ defmodule PhoenixTest.PageView do - +