Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions config/test.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import Config

config :phoenix_test, :endpoint, PhoenixTest.WebApp.Endpoint

config :phoenix_test, PhoenixTest.WebApp.Endpoint,
server: true,
http: [port: 4000],
Expand All @@ -13,6 +11,13 @@ config :phoenix_test, PhoenixTest.WebApp.Endpoint,
layout: false
]

config :phoenix_test, PhoenixTest.AnotherWebApp.Endpoint,
server: true,
http: [port: 4001],
live_view: [signing_salt: "112345678212345678312345678412"],
secret_key_base: String.duplicate("57689", 50),
pubsub_server: PhoenixTest.PubSub

config :logger, level: :error

config :esbuild,
Expand Down
37 changes: 27 additions & 10 deletions lib/phoenix_test.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,6 @@ defmodule PhoenixTest do
end
```

### Configuration

In `config/test.exs` specify the endpoint to be used for routing requests:

```elixir
config :phoenix_test, :endpoint, MyAppWeb.Endpoint
```

### Getting `PhoenixTest` helpers

`PhoenixTest` helpers can be included via `import PhoenixTest`.
Expand All @@ -65,7 +57,7 @@ defmodule PhoenixTest do
### With `ConnCase`

If you plan to use `ConnCase` solely for `PhoenixTest`, then you can import
the helpers there:
the helpers there and update the `setup` block to set the endpoint on the conn:

```elixir
using do
Expand All @@ -77,6 +69,12 @@ defmodule PhoenixTest do
# doing other setup for ConnCase
end
end

setup tags do
# existing ConnCase setup...

{:ok, conn: Phoenix.ConnTest.build_conn() |> PhoenixTest.put_endpoint(MyAppWeb.Endpoint)}
end
```

### Adding a `FeatureCase`
Expand All @@ -96,14 +94,16 @@ defmodule PhoenixTest do
import MyAppWeb.FeatureCase

import PhoenixTest

@endpoint MyAppWeb.Endpoint
end
end

setup tags do
pid = Ecto.Adapters.SQL.Sandbox.start_owner!(MyApp.Repo, shared: not tags[:async])
on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end)

{:ok, conn: Phoenix.ConnTest.build_conn()}
{:ok, conn: Phoenix.ConnTest.build_conn() |> PhoenixTest.put_endpoint(MyAppWeb.Endpoint)}
end
end
```
Expand Down Expand Up @@ -203,6 +203,23 @@ defmodule PhoenixTest do
alias PhoenixTest.ConnHandler
alias PhoenixTest.Driver

@doc """
Sets the endpoint on the conn so PhoenixTest knows which endpoint to use.

Call this in your test setup with your app's endpoint module.

## Examples

```elixir
setup tags do
{:ok, conn: Phoenix.ConnTest.build_conn() |> PhoenixTest.put_endpoint(MyAppWeb.Endpoint)}
end
```
"""
def put_endpoint(conn, endpoint) do
Plug.Conn.put_private(conn, :phoenix_endpoint, endpoint)
end

@doc """
Entrypoint to create a session.

Expand Down
15 changes: 6 additions & 9 deletions lib/phoenix_test/conn_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ defmodule PhoenixTest.ConnHandler do
@moduledoc false
import Phoenix.ConnTest

@endpoint Application.compile_env!(:phoenix_test, :endpoint)
alias PhoenixTest.EndpointHelpers

def visit(conn, path) do
conn
|> get(path)
|> Phoenix.ConnTest.dispatch(EndpointHelpers.endpoint_from!(conn), :get, path, nil)
|> visit()
end

Expand Down Expand Up @@ -35,7 +35,9 @@ defmodule PhoenixTest.ConnHandler do
defp append_query_string(path, query), do: path <> "?" <> query

def recycle_all_headers(conn) do
recycle(conn, all_headers(conn))
conn
|> recycle(all_headers(conn))
|> EndpointHelpers.copy_endpoint(conn)
end

defp all_headers(conn) do
Expand All @@ -50,12 +52,7 @@ defmodule PhoenixTest.ConnHandler do

@plug_adapters_test_conn_default_host "www.example.com"
defp local_path?(conn) do
conn.host == @plug_adapters_test_conn_default_host or conn.host == endpoint_host()
end

defp endpoint_host do
endpoint_at_runtime_to_avoid_warning = Application.get_env(:phoenix_test, :endpoint)
endpoint_at_runtime_to_avoid_warning.host()
conn.host == @plug_adapters_test_conn_default_host or conn.host == EndpointHelpers.endpoint_from!(conn).host()
end

defp route_exists?(conn) do
Expand Down
57 changes: 57 additions & 0 deletions lib/phoenix_test/endpoint_helpers.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
defmodule PhoenixTest.EndpointHelpers do
@moduledoc false

# This module replaces Phoenix test macros that require `@endpoint` to be set
# as a compile-time module attribute. Instead, the endpoint is read at runtime
# from `conn.private[:phoenix_endpoint]`, set via `PhoenixTest.put_endpoint/2`.

def endpoint_from!(conn) do
conn.private[:phoenix_endpoint] ||
raise ArgumentError, """
No endpoint set on conn. Use PhoenixTest.put_endpoint/2 in your test setup:

conn = Phoenix.ConnTest.build_conn() |> PhoenixTest.put_endpoint(MyAppWeb.Endpoint)
"""
end

def endpoint_from(conn) do
conn.private[:phoenix_endpoint]
end

def copy_endpoint(dest_conn, source_conn) do
Plug.Conn.put_private(dest_conn, :phoenix_endpoint, endpoint_from(source_conn))
end

# Replaces: live(conn)
# The `live/1` macro's binary-path branch calls `get(conn, path)` which
# requires `@endpoint` at compile time, even when path is nil.
def live(conn) do
Phoenix.LiveViewTest.__live__(conn, nil, [])
end
Comment on lines +25 to +30
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of my concerns with handling multiple endpoint configs is that we have to shadow these "internal" LiveViewTest functions.

I'm worried about that we'll either miss something or experience drift from how LiveViewTest tests things.

In this particular case, I think the get(conn, path) is important for trying to emulate the real behavior LiveView has -- first we have a static connection which then gets upgraded.

2 questions for you:

  1. Do you know if there's a way we can emulate the get request without having to include the compile time endpoint?
  2. More broadly, do you know of a good strategy for us to keep "in line" with any changes that come in the future to the private LiveViewTest functions? I'm open to ideas.


# Replaces: follow_redirect(result, conn) for :live_redirect
def follow_live_redirect(conn, opts) do
endpoint = endpoint_from!(conn)
{conn, to} = Phoenix.LiveViewTest.__follow_redirect__(conn, endpoint, nil, opts)

conn
|> Phoenix.ConnTest.dispatch(endpoint, :get, to, nil)
|> Phoenix.LiveViewTest.__live__(to, [])
end

# Replaces: follow_redirect(result, conn) for :redirect
def follow_redirect(conn, opts) do
endpoint = endpoint_from!(conn)
{conn, to} = Phoenix.LiveViewTest.__follow_redirect__(conn, endpoint, nil, opts)
Phoenix.ConnTest.dispatch(conn, endpoint, :get, to, nil)
end

# Replaces: file_input(view, form_selector, name, entries)
# The `file_input/4` macro calls `Phoenix.ChannelTest.connect/2` which
# requires `@endpoint` at compile time.
def file_input(view, conn, form_selector, name, entries) do
endpoint = endpoint_from!(conn)
builder = fn -> Phoenix.ChannelTest.__connect__(endpoint, Phoenix.LiveView.Socket, %{}, []) end
Phoenix.LiveViewTest.__file_input__(view, form_selector, name, entries, builder)
end
end
25 changes: 16 additions & 9 deletions lib/phoenix_test/live.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
defmodule PhoenixTest.Live do
@moduledoc false
import Phoenix.ConnTest
import Phoenix.LiveViewTest
import PhoenixTest.SessionHelpers, only: [scope_selector: 2]

Expand All @@ -11,6 +10,7 @@ defmodule PhoenixTest.Live do
alias PhoenixTest.Element.Field
alias PhoenixTest.Element.Form
alias PhoenixTest.Element.Select
alias PhoenixTest.EndpointHelpers
alias PhoenixTest.FileUpload
alias PhoenixTest.FormData
alias PhoenixTest.FormPayload
Expand All @@ -22,8 +22,6 @@ defmodule PhoenixTest.Live do
alias PhoenixTest.Query
alias PhoenixTest.SessionHelpers

@endpoint Application.compile_env!(:phoenix_test, :endpoint)

defstruct view: nil,
watcher: nil,
conn: nil,
Expand All @@ -33,7 +31,7 @@ defmodule PhoenixTest.Live do
current_operation: nil

def build(conn) do
{:ok, view, _html} = live(conn)
{:ok, view, _html} = EndpointHelpers.live(conn)
current_path = ConnHandler.build_current_path(conn)
{:ok, watcher} = start_watcher(view)
%__MODULE__{view: view, watcher: watcher, conn: conn, current_path: current_path}
Expand Down Expand Up @@ -335,7 +333,7 @@ defmodule PhoenixTest.Live do

upload_progress_result =
session.view
|> file_input(form.selector, live_upload_name, [entry])
|> EndpointHelpers.file_input(session.conn, form.selector, live_upload_name, [entry])
|> render_upload(file_name)
|> maybe_throw_upload_errors(session, file_name, live_upload_name)

Expand Down Expand Up @@ -557,11 +555,20 @@ defmodule PhoenixTest.Live do

defp maybe_redirect({:error, {kind, %{to: path}}} = result, session) when kind in [:redirect, :live_redirect] do
session = %{session | current_path: path}
conn = session.conn

result
|> follow_redirect(ConnHandler.recycle_all_headers(conn))
|> maybe_redirect(session)
case result do
{:error, {:live_redirect, opts}} ->
session.conn
|> ConnHandler.recycle_all_headers()
|> EndpointHelpers.follow_live_redirect(opts)
|> maybe_redirect(session)

{:error, {:redirect, opts}} ->
session.conn
|> ConnHandler.recycle_all_headers()
|> EndpointHelpers.follow_redirect(opts)
|> ConnHandler.visit()
end
end

defp maybe_redirect({:ok, view, _}, session) do
Expand Down
7 changes: 3 additions & 4 deletions lib/phoenix_test/static.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defmodule PhoenixTest.Static do
alias PhoenixTest.Element.Form
alias PhoenixTest.Element.Link
alias PhoenixTest.Element.Select
alias PhoenixTest.EndpointHelpers
alias PhoenixTest.FileUpload
alias PhoenixTest.FormData
alias PhoenixTest.FormPayload
Expand All @@ -20,8 +21,6 @@ defmodule PhoenixTest.Static do
alias PhoenixTest.Operation
alias PhoenixTest.Query

@endpoint Application.compile_env!(:phoenix_test, :endpoint)

defstruct conn: nil, active_form: ActiveForm.new(), within: :none, current_path: "", current_operation: nil

def build(conn) do
Expand Down Expand Up @@ -244,7 +243,7 @@ defmodule PhoenixTest.Static do
html =
session.conn.resp_body
|> Html.parse_document()
|> Html.postwalk(&OpenBrowser.prefix_static_paths(&1, @endpoint))
|> Html.postwalk(&OpenBrowser.prefix_static_paths(&1, EndpointHelpers.endpoint_from!(session.conn)))
|> Html.raw()

File.write!(path, html)
Expand Down Expand Up @@ -288,7 +287,7 @@ defmodule PhoenixTest.Static do

conn
|> ConnHandler.recycle_all_headers()
|> dispatch(@endpoint, form.method, form.action, payload)
|> dispatch(EndpointHelpers.endpoint_from!(session.conn), form.method, form.action, payload)
|> maybe_redirect(session)
end

Expand Down
2 changes: 1 addition & 1 deletion test/phoenix_test/assertions_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule PhoenixTest.AssertionsTest do
alias PhoenixTest.Live

setup do
%{conn: Phoenix.ConnTest.build_conn()}
%{conn: PhoenixTest.put_endpoint(Phoenix.ConnTest.build_conn(), PhoenixTest.WebApp.Endpoint)}
end

describe "assert_has/2" do
Expand Down
2 changes: 1 addition & 1 deletion test/phoenix_test/conn_handler_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule PhoenixTest.ConnHandlerTest do
alias PhoenixTest.ConnHandler

setup do
%{conn: Phoenix.ConnTest.build_conn()}
%{conn: PhoenixTest.put_endpoint(Phoenix.ConnTest.build_conn(), PhoenixTest.WebApp.Endpoint)}
end

describe "visit/2" do
Expand Down
2 changes: 1 addition & 1 deletion test/phoenix_test/live_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ defmodule PhoenixTest.LiveTest do
alias PhoenixTest.Html

setup do
%{conn: Phoenix.ConnTest.build_conn()}
%{conn: PhoenixTest.put_endpoint(Phoenix.ConnTest.build_conn(), PhoenixTest.WebApp.Endpoint)}
end

describe "render_page_title/1" do
Expand Down
2 changes: 1 addition & 1 deletion test/phoenix_test/live_view_timeout_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ defmodule PhoenixTest.LiveViewTimeoutTest do
setup do
{:ok, view_pid} = start_supervised(DummyLiveView)
view = %{pid: view_pid}
conn = Phoenix.ConnTest.build_conn()
conn = PhoenixTest.put_endpoint(Phoenix.ConnTest.build_conn(), PhoenixTest.WebApp.Endpoint)
{:ok, watcher} = start_supervised({LiveViewWatcher, %{caller: self(), view: view}})
session = %Live{conn: conn, view: view, watcher: watcher}

Expand Down
4 changes: 2 additions & 2 deletions test/phoenix_test/static_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule PhoenixTest.StaticTest do
import PhoenixTest.TestHelpers

setup do
%{conn: Phoenix.ConnTest.build_conn()}
%{conn: PhoenixTest.put_endpoint(Phoenix.ConnTest.build_conn(), PhoenixTest.WebApp.Endpoint)}
end

describe "render_page_title/1" do
Expand Down Expand Up @@ -859,7 +859,7 @@ defmodule PhoenixTest.StaticTest do
describe "unwrap" do
require Phoenix.ConnTest

@endpoint Application.compile_env(:phoenix_test, :endpoint)
@endpoint PhoenixTest.WebApp.Endpoint

test "provides an escape hatch that gives access to the underlying conn", %{conn: conn} do
conn
Expand Down
15 changes: 14 additions & 1 deletion test/phoenix_test_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule PhoenixTestTest do
import PhoenixTest

setup do
%{conn: Phoenix.ConnTest.build_conn()}
%{conn: PhoenixTest.put_endpoint(Phoenix.ConnTest.build_conn(), PhoenixTest.WebApp.Endpoint)}
end

describe "select/3" do
Expand Down Expand Up @@ -32,4 +32,17 @@ defmodule PhoenixTestTest do
assert message =~ "select/4 with :from is deprecated"
end
end

describe "put_endpoint/2" do
test "can visit a page on a different endpoint", %{conn: conn} do
Phoenix.ConnTest.build_conn()
|> PhoenixTest.put_endpoint(PhoenixTest.AnotherWebApp.Endpoint)
|> visit("/page")
|> assert_has("h1", text: "AnotherWebApp page")

conn
|> visit("/live/index")
|> assert_has("h1", text: "LiveView main page")
end
end
end
Loading