Repository: phoenixframework/phoenix
Pull request #3129 (2018-10-28 08:18:24)

Remove extra newline in generated context test cases
Currently, context tests generated by running the html generator (`mix phx.gen.context Accounts User users name:string`), produce a test with an extra line with some trailing whitespace (illustrated with dots here):

        test "update_user/2 with valid data updates the user" do
          user = user_fixture()
          assert {:ok, %User{} = user} = Accounts.update_user(user, @update_attrs)

    ······
          assert user.name == "some updated name"
        end

This patch removes both newlines from the template, resulting in a generated test like this:

        test "update_user/2 with valid data updates the user" do
          user = user_fixture()
          assert {:ok, %User{} = user} = Accounts.update_user(user, @update_attrs)
          assert user.name == "some updated name"
        end




Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ba88bf71a..0d41a0bcf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,18 @@ Note the websocket/longpoll configuration given to socket/3 will only apply afte
 The old APIs for building transports are also deprecated. The good news is: adapting an existing transport to the new API is a less error prone process where you should mostly remove code.
 
 
+## 1.4.0-rc.4
+
+### Enhancements
+  * [Endpoint] Allow custom keyword pairs to be passed to the socket `:connect_info` options.
+
+## 1.4.0-rc.3 (2018-10-30)
+
+### Enhancements
+  * [phx.new] Update Ecto deps with the release of Ecto 3.0 including `phoenix_ecto` 4.0
+  * [phx.new] Import Ecto's `.formatter.exs` in new projects 
+  * [Endpoint] Log the configured url instead of raw IP when booting endpoint webserver
+
 ## 1.4.0-rc.2 (2018-10-20)
 
 ### Enhancements
diff --git a/assets/package-lock.json b/assets/package-lock.json
index c4e63ff8a..23932262a 100644
--- a/assets/package-lock.json
+++ b/assets/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "phoenix",
-  "version": "1.4.0-rc.2",
+  "version": "1.4.0-rc.3",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/assets/package.json b/assets/package.json
index 183e40ea3..d52aa75fd 100644
--- a/assets/package.json
+++ b/assets/package.json
@@ -1,6 +1,6 @@
 {
   "name": "phoenix",
-  "version": "1.4.0-rc.2",
+  "version": "1.4.0-rc.3",
   "description": "The official JavaScript client for the Phoenix web framework.",
   "license": "MIT",
   "main": "./priv/static/phoenix.js",
diff --git a/guides/ecto.md b/guides/ecto.md
index 8760f956b..77a045282 100644
--- a/guides/ecto.md
+++ b/guides/ecto.md
@@ -471,9 +471,8 @@ defmodule HelloPhoenix.MixProject do
     [
       {:phoenix, "~> 1.3.0"},
       {:phoenix_pubsub, "~> 1.0"},
-      {:phoenix_ecto, "~> 3.5"},
-      {:ecto, "~> 3.0-rc", override: true},
-      {:ecto_sql, "~> 3.0-rc", override: true},
+      {:phoenix_ecto, "~> 4.0"},
+      {:ecto_sql, "~> 3.0"},
       {:mariaex, ">= 0.0.0"},
       {:phoenix_html, "~> 2.10"},
       {:phoenix_live_reload, "~> 1.2-rc", only: :dev},
diff --git a/guides/endpoint.md b/guides/endpoint.md
index f0dab4953..b21de2599 100644
--- a/guides/endpoint.md
+++ b/guides/endpoint.md
@@ -167,21 +167,3 @@ With `force_ssl: :hsts` set the `Strict-Transport-Security` header is set with a
 This can result in unexpected behaviour if you access your application on localhost, for example `https://localhost:4000`, as from that point forward and traffic coming from localhost will be expected to be encrypted, except port 80 which will be redirected to port 443. This has the potential to disrupt traffic to any other local servers or proxies that you may be running on your computer. Other applications or proxies on localhost will refuse to work unless the traffic is encrypted.
 
 If you do inadvertently turn on HSTS for localhost you may need to reset the cache on your browser before it will accept any HTTP traffic from localhost. For Chrome you need to `Empty Cache and Hard Reload` which is available from the reload menu that appears when you click and hold the reload icon from the Developer Tools Panel. For Safari you will need to clear your cache, remove the entry from `~/Library/Cookies/HSTS.plist` (or delete that file entirely) and restart Safari. Alternately you can set the `:expires` option on `force_ssl` to `0` which should expired the entry to turn off HSTS. More information on the options for HSTS are available at [Plug.SSL](https://hexdocs.pm/plug/Plug.SSL.html).
-
-### Releasing with Exrm
-
-In order to build and run a release with Exrm, make sure you also include the `:ssl` app in `mix.exs`:
-
-```elixir
-def application do
-  [mod: {MyApp, []},
-   applications: [:phoenix, :phoenix_html, :cowboy, :logger, :gettext,
-                  :phoenix_ecto, :postgrex, :ssl]]
-end
-```
-
-Or else you might run into errors:
-
-```
-** (MatchError) no match of right hand side value: {:error, {:ssl, {'no such file or directory', 'ssl.app'}}}
-```
diff --git a/guides/introduction/installation.md b/guides/introduction/installation.md
index 30fb4d269..6a06bcf1f 100644
--- a/guides/introduction/installation.md
+++ b/guides/introduction/installation.md
@@ -45,7 +45,7 @@ Once we have Elixir and Erlang, we are ready to install the Phoenix Mix archive.
 Here's the command to install the Phoenix archive:
 
 ```console
-$ mix archive.install hex phx_new 1.4.0-rc.2
+$ mix archive.install hex phx_new 1.4.0-rc.3
 ```
 > Note: if the Phoenix archive won't install properly with this command, we can download the package from the [Phoenix archives](https://github.com/phoenixframework/archives), save it to the filesystem, and then run: `mix archive.install /path/to/local/phx_new.ez`.
 
diff --git a/installer/README.md b/installer/README.md
index 6c48bffa8..ea238babf 100644
--- a/installer/README.md
+++ b/installer/README.md
@@ -4,7 +4,7 @@ Provides `phx.new` installer as an archive.
 
 To install from hex, run:
 
-    $ mix archive.install hex phx_new 1.4.0-rc.2
+    $ mix archive.install hex phx_new 1.4.0-rc.3
 
 To build and install it locally,
 ensure any previous archive versions are removed:
diff --git a/installer/lib/mix/tasks/phx.new.ex b/installer/lib/mix/tasks/phx.new.ex
index a69b202e2..1eca2415e 100644
--- a/installer/lib/mix/tasks/phx.new.ex
+++ b/installer/lib/mix/tasks/phx.new.ex
@@ -136,25 +136,25 @@ defmodule Mix.Tasks.Phx.New do
   defp prompt_to_install_deps(%Project{} = project, generator) do
     install? = Mix.shell.yes?("\nFetch and install dependencies?")
 
+    cd_step = ["$ cd #{relative_app_path(project.project_path)}"]
+
     maybe_cd(project.project_path, fn ->
-      mix_pending =
-        install_mix(install?)
+      mix_step = install_mix(install?)
 
       compile =
-        case mix_pending do
+        case mix_step do
           [] -> Task.async(fn -> rebar_available?() && cmd("mix deps.compile") end)
           _  -> Task.async(fn -> :ok end)
         end
 
-      webpack_pending = install_webpack(install?, project)
+      webpack_step = install_webpack(install?, project)
       Task.await(compile, :infinity)
 
       if Project.webpack?(project) and !System.find_executable("npm") do
         print_webpack_info(project, generator)
       end
 
-      pending = mix_pending ++ (webpack_pending || [])
-      print_missing_commands(pending, project.project_path)
+      print_missing_steps(cd_step ++ mix_step ++ webpack_step)
 
       if Project.ecto?(project) do
         print_ecto_info(project, generator)
@@ -208,16 +208,7 @@ defmodule Mix.Tasks.Phx.New do
     """
   end
 
-  defp print_missing_commands([], path) do
-    Mix.shell.info """
-
-    We are all set! Go into your application by running:
-
-        $ cd #{relative_app_path(path)}
-    """
-  end
-  defp print_missing_commands(commands, path) do
-    steps = ["$ cd #{relative_app_path(path)}" | commands]
+  defp print_missing_steps(steps) do
     Mix.shell.info """
 
     We are almost there! The following steps are missing:
diff --git a/installer/mix.exs b/installer/mix.exs
index 4de39f2c8..759d59fc0 100644
--- a/installer/mix.exs
+++ b/installer/mix.exs
@@ -8,7 +8,7 @@ defmodule Phx.New.MixProject do
     [
       app: :phx_new,
       start_permanent: Mix.env() == :prod,
-      version: "1.4.0-rc.2",
+      version: "1.4.0-rc.3",
       elixir: "~> 1.5",
       deps: deps(),
       package: [
diff --git a/installer/templates/phx_single/README.md b/installer/templates/phx_single/README.md
index 8480b0a3d..95553d6c0 100644
--- a/installer/templates/phx_single/README.md
+++ b/installer/templates/phx_single/README.md
@@ -3,7 +3,7 @@
 To start your Phoenix server:
 
   * Install dependencies with `mix deps.get`<%= if ecto do %>
-  * Create and migrate your database with `mix ecto.create && mix ecto.migrate`<% end %><%= if webpack do %>
+  * Create and migrate your database with `mix ecto.setup`<% end %><%= if webpack do %>
   * Install Node.js dependencies with `cd assets && npm install`<% end %>
   * Start Phoenix endpoint with `mix phx.server`
 
diff --git a/installer/templates/phx_single/formatter.exs b/installer/templates/phx_single/formatter.exs
index be3b79494..1aee095fc 100644
--- a/installer/templates/phx_single/formatter.exs
+++ b/installer/templates/phx_single/formatter.exs
@@ -1,5 +1,6 @@
 [
-  import_deps: [<%= if ecto do %>:ecto, <% end %>:phoenix],
-  inputs: ["*.{ex,exs}", "{config,lib,priv,test}/**/*.{ex,exs}"]<%= if ecto do %>,
-  subdirectories: ["priv/*/migrations"]<% end %>
+  import_deps: [<%= if ecto do %>:ecto, <% end %>:phoenix],<%= if ecto do %>
+  inputs: ["*.{ex,exs}", "priv/*/seeds.exs", "{config,lib,test}/**/*.{ex,exs}"],
+  subdirectories: ["priv/*/migrations"]<% else %>
+  inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"]<% end %>
 ]
diff --git a/installer/templates/phx_single/mix.exs b/installer/templates/phx_single/mix.exs
index 469088bba..e05fce27e 100644
--- a/installer/templates/phx_single/mix.exs
+++ b/installer/templates/phx_single/mix.exs
@@ -39,10 +39,9 @@ defmodule <%= app_module %>.MixProject do
     [
       <%= phoenix_dep %>,
       {:phoenix_pubsub, "~> 1.1"},<%= if ecto do %>
-      {:phoenix_ecto, "~> 3.5"},
-      {:ecto, "~> 3.0-rc", override: true},
-      {:ecto_sql, "~> 3.0-rc", override: true},
-      {<%= inspect adapter_app %>, ">= 0.0.0-rc"},<% end %><%= if html do %>
+      {:phoenix_ecto, "~> 4.0"},
+      {:ecto_sql, "~> 3.0"},
+      {<%= inspect adapter_app %>, ">= 0.0.0"},<% end %><%= if html do %>
       {:phoenix_html, "~> 2.11"},
       {:phoenix_live_reload, "~> 1.2-rc", only: :dev},<% end %>
       {:gettext, "~> 0.11"},
diff --git a/installer/templates/phx_umbrella/apps/app_name/formatter.exs b/installer/templates/phx_umbrella/apps/app_name/formatter.exs
index 12c5d8dfc..b599203bb 100644
--- a/installer/templates/phx_umbrella/apps/app_name/formatter.exs
+++ b/installer/templates/phx_umbrella/apps/app_name/formatter.exs
@@ -1,5 +1,6 @@
 [<%= if ecto do %>
   import_deps: [:ecto],
-  subdirectories: ["priv/*/migrations"],<% end %>
-  inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"]
+  inputs: ["*.{ex,exs}", "priv/*/seeds.exs", "{config,lib,test}/**/*.{ex,exs}"],
+  subdirectories: ["priv/*/migrations"]<% else %>
+  inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"]<% end %>
 ]
diff --git a/installer/templates/phx_umbrella/apps/app_name/mix.exs b/installer/templates/phx_umbrella/apps/app_name/mix.exs
index ebbe886a5..1ae74e864 100644
--- a/installer/templates/phx_umbrella/apps/app_name/mix.exs
+++ b/installer/templates/phx_umbrella/apps/app_name/mix.exs
@@ -36,9 +36,8 @@ defmodule <%= app_module %>.MixProject do
   # Type `mix help deps` for examples and options.
   defp deps do
     [<%= if ecto do %>
-      {:<%= adapter_app %>, ">= 0.0.0-rc"},
-      {:ecto, "~> 3.0-rc", override: true},
-      {:ecto_sql, "~> 3.0-rc", override: true},
+      {:ecto_sql, "~> 3.0"},
+      {:<%= adapter_app %>, ">= 0.0.0"}
     <% end %>]
   end
 
diff --git a/installer/templates/phx_umbrella/apps/app_name_web/README.md b/installer/templates/phx_umbrella/apps/app_name_web/README.md
index ec69404ee..ba77b2ce3 100644
--- a/installer/templates/phx_umbrella/apps/app_name_web/README.md
+++ b/installer/templates/phx_umbrella/apps/app_name_web/README.md
@@ -3,7 +3,7 @@
 To start your Phoenix server:
 
   * Install dependencies with `mix deps.get`
-  * Create and migrate your database with `mix ecto.create && mix ecto.migrate`
+  * Create and migrate your database with `mix ecto.setup`
   * Install Node.js dependencies with `cd assets && npm install`
   * Start Phoenix endpoint with `mix phx.server`
 
diff --git a/installer/templates/phx_umbrella/apps/app_name_web/mix.exs b/installer/templates/phx_umbrella/apps/app_name_web/mix.exs
index 760cbd27a..77d1dc457 100644
--- a/installer/templates/phx_umbrella/apps/app_name_web/mix.exs
+++ b/installer/templates/phx_umbrella/apps/app_name_web/mix.exs
@@ -39,9 +39,7 @@ defmodule <%= web_namespace %>.MixProject do
     [
       <%= phoenix_dep %>,
       {:phoenix_pubsub, "~> 1.1"},<%= if ecto do %>
-      # TODO: temporarily requires ecto until final release
-      {:ecto, "~> 3.0-rc", override: true},
-      {:phoenix_ecto, "~> 3.5"},<% end %><%= if html do %>
+      {:phoenix_ecto, "~> 4.0"},<% end %><%= if html do %>
       {:phoenix_html, "~> 2.11"},
       {:phoenix_live_reload, "~> 1.2-rc", only: :dev},<% end %>
       {:gettext, "~> 0.11"},<%= if app_name != web_app_name do %>
diff --git a/lib/phoenix.ex b/lib/phoenix.ex
index db3f127f7..f73b6fad6 100644
--- a/lib/phoenix.ex
+++ b/lib/phoenix.ex
@@ -78,15 +78,36 @@ defmodule Phoenix do
   end
 
   defp warn_on_missing_json_library do
-    module = json_library()
-    Code.ensure_loaded?(module) || IO.warn """
-    failed to load #{inspect(module)} for Phoenix JSON encoding
-    (module #{inspect(module)} is not available).
+    configured_lib = Application.get_env(:phoenix, :json_library)
+    default_lib = json_library()
 
-    Ensure #{inspect(module)} is loaded from your deps in mix.exs, or
-    configure an existing encoder in your Mix config using:
+    cond do
+      configured_lib && not Code.ensure_loaded?(configured_lib) ->
+        warn_json configured_lib, """
+        found #{inspect(configured_lib)} in your application configuration
+        for Phoenix JSON encoding, but failed to load the library.
+        """
 
-        config :phoenix, :json_library, MyJSONLibrary
+      not Code.ensure_loaded?(default_lib) and Code.ensure_loaded?(Jason) ->
+        warn_json(Jason)
+
+      not Code.ensure_loaded?(default_lib) ->
+        warn_json(default_lib)
+
+      true -> :ok
+    end
+  end
+
+  defp warn_json(lib, preabmle \\ nil) do
+    IO.warn """
+    #{preabmle || "failed to load #{inspect(lib)} for Phoenix JSON encoding"}
+    (module #{inspect(lib)} is not available).
+
+    Ensure #{inspect(lib)} exists in your deps in mix.exs,
+    and you have configured Phoenix to use it for JSON encoding by
+    verifying the following exists in your config/config.exs:
+
+        config :phoenix, :json_library, #{inspect(lib)}
 
     """
   end
diff --git a/lib/phoenix/channel/server.ex b/lib/phoenix/channel/server.ex
index 7d4ce3d9d..6a8928a55 100644
--- a/lib/phoenix/channel/server.ex
+++ b/lib/phoenix/channel/server.ex
@@ -33,7 +33,7 @@ defmodule Phoenix.Channel.Server do
           channel: channel,
           join_ref: join_ref,
           assigns: Map.merge(socket.assigns, assigns),
-          private: channel.__socket__(:private)
+          private: Map.merge(channel.__socket__(:private), socket.private)
       }
 
     instrument = %{params: payload, socket: socket}
diff --git a/lib/phoenix/endpoint.ex b/lib/phoenix/endpoint.ex
index 310f922f8..5322a9542 100644
--- a/lib/phoenix/endpoint.ex
+++ b/lib/phoenix/endpoint.ex
@@ -869,6 +869,9 @@ defmodule Phoenix.Endpoint do
         * `:x_headers` - all request headers that have an "x-" prefix
         * `:uri` - a `%URI{}` with information from the conn
 
+      Arbitrary keywords may also appear following the above valid keys, which
+      is useful for passing custom connection information to the socket.
+
       For example:
 
           socket "/socket", AppWeb.UserSocket,
@@ -876,6 +879,14 @@ defmodule Phoenix.Endpoint do
               connect_info: [:peer_data, :x_headers, :uri]
             ]
 
+      With arbitrary keywords:
+
+          socket "/socket", AppWeb.UserSocket,
+            websocket: [
+              connect_info: [:uri, signing_salt: "NZIguRPO"]
+            ]
+
+
   ## Websocket configuration
 
   The following configuration applies only to `:websocket`.
diff --git a/lib/phoenix/endpoint/cowboy2_handler.ex b/lib/phoenix/endpoint/cowboy2_handler.ex
index 3e091d73b..32a1c5490 100644
--- a/lib/phoenix/endpoint/cowboy2_handler.ex
+++ b/lib/phoenix/endpoint/cowboy2_handler.ex
@@ -25,7 +25,7 @@ defmodule Phoenix.Endpoint.Cowboy2Handler do
               {:cowboy_websocket, req, [handler | state], cowboy_opts}
 
             {:error, %{adapter: {@connection, req}}} ->
-              {:error, req}
+              {:ok, req, {handler, opts}}
           end
 
         {:plug, handler, opts} ->
diff --git a/lib/phoenix/socket/transport.ex b/lib/phoenix/socket/transport.ex
index f2219d661..6f00b24c0 100644
--- a/lib/phoenix/socket/transport.ex
+++ b/lib/phoenix/socket/transport.ex
@@ -415,8 +415,10 @@ defmodule Phoenix.Socket.Transport do
         :uri ->
           {:uri, fetch_uri(conn)}
 
+        {key, val} -> {key, val}
+
         _ ->
-          raise ArgumentError, ":connect_info keys are expected to be one of :peer_data, :x_headers, or :uri, got: #{inspect(key)}"
+          raise ArgumentError, ":connect_info keys are expected to be one of :peer_data, :x_headers, or :uri, optionally followed by custom keyword pairs, got: #{inspect(key)}"
       end
     end
   end
diff --git a/mix.exs b/mix.exs
index d8fc36507..967f06a10 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,7 +1,7 @@
 defmodule Phoenix.MixProject do
   use Mix.Project
 
-  @version "1.4.0-rc.2"
+  @version "1.4.0-rc.3"
 
   def project do
     [
diff --git a/package.json b/package.json
index 0cdf237c3..fd5bcd5bf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "phoenix",
-  "version": "1.4.0-rc.2",
+  "version": "1.4.0-rc.3",
   "description": "The official JavaScript client for the Phoenix web framework.",
   "license": "MIT",
   "main": "./priv/static/phoenix.js",


Test patch:
diff --git a/installer/test/phx_new_test.exs b/installer/test/phx_new_test.exs
index bc8b9beb1..680f41c1d 100644
--- a/installer/test/phx_new_test.exs
+++ b/installer/test/phx_new_test.exs
@@ -24,7 +24,13 @@ defmodule Mix.Tasks.Phx.NewTest do
       Mix.Tasks.Phx.New.run([@app_name])
 
       assert_file "phx_blog/README.md"
-      assert_file "phx_blog/.formatter.exs", ~r/import_deps: \[:ecto, :phoenix\]/
+
+      assert_file "phx_blog/.formatter.exs", fn file ->
+        assert file =~ "import_deps: [:ecto, :phoenix]"
+        assert file =~ "inputs: [\"*.{ex,exs}\", \"priv/*/seeds.exs\", \"{config,lib,test}/**/*.{ex,exs}\"]"
+        assert file =~ "subdirectories: [\"priv/*/migrations\"]"
+      end
+
       assert_file "phx_blog/mix.exs", fn file ->
         assert file =~ "app: :phx_blog"
         refute file =~ "deps_path: \"../../deps\""
@@ -173,6 +179,12 @@ defmodule Mix.Tasks.Phx.NewTest do
       config = ~r/config :phx_blog, PhxBlog.Repo,/
       refute File.exists?("phx_blog/lib/phx_blog/repo.ex")
 
+      assert_file "phx_blog/.formatter.exs", fn file ->
+        assert file =~ "import_deps: [:phoenix]"
+        assert file =~ "inputs: [\"*.{ex,exs}\", \"{config,lib,test}/**/*.{ex,exs}\"]"
+        refute file =~ "subdirectories:"
+      end
+
       assert_file "phx_blog/mix.exs", &refute(&1 =~ ~r":phoenix_ecto")
 
       assert_file "phx_blog/config/config.exs", fn file ->
diff --git a/installer/test/phx_new_umbrella_test.exs b/installer/test/phx_new_umbrella_test.exs
index 269f9dde8..99dba6b6f 100644
--- a/installer/test/phx_new_umbrella_test.exs
+++ b/installer/test/phx_new_umbrella_test.exs
@@ -76,6 +76,18 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
         assert file =~ ":inet6"
       end
 
+      assert_file app_path(@app, ".formatter.exs"), fn file ->
+        assert file =~ "import_deps: [:ecto]"
+        assert file =~ "inputs: [\"*.{ex,exs}\", \"priv/*/seeds.exs\", \"{config,lib,test}/**/*.{ex,exs}\"]"
+        assert file =~ "subdirectories: [\"priv/*/migrations\"]"
+      end
+
+      assert_file web_path(@app, ".formatter.exs"), fn file ->
+        assert file =~ "inputs: [\"*.{ex,exs}\", \"{config,lib,test}/**/*.{ex,exs}\"]"
+        refute file =~ "import_deps: [:ecto]"
+        refute file =~ "subdirectories:"
+      end
+
       assert_file app_path(@app, "lib/#{@app}/application.ex"), ~r/defmodule PhxUmb.Application do/
       assert_file app_path(@app, "lib/#{@app}/application.ex"), ~r/PhxUmb.Repo/
       assert_file app_path(@app, "lib/#{@app}.ex"), ~r/defmodule PhxUmb do/
diff --git a/installer/test/phx_new_web_test.exs b/installer/test/phx_new_web_test.exs
index a8166241c..a126b66c0 100644
--- a/installer/test/phx_new_web_test.exs
+++ b/installer/test/phx_new_web_test.exs
@@ -37,6 +37,10 @@ defmodule Mix.Tasks.Phx.New.WebTest do
         assert file =~ "generators: [context_app: false]"
       end
 
+      assert_file "#{@app_name}/mix.exs", fn file ->
+        assert file =~ "{:jason, \"~> 1.0\"}"
+      end
+
       # Install dependencies?
       assert_received {:mix_shell, :yes?, ["\nFetch and install dependencies?"]}
 
diff --git a/lib/phoenix/test/conn_test.ex b/lib/phoenix/test/conn_test.ex
index 2cce7bf99..416030031 100644
--- a/lib/phoenix/test/conn_test.ex
+++ b/lib/phoenix/test/conn_test.ex
@@ -434,7 +434,7 @@ defmodule Phoenix.ConnTest do
       assert redirected_to(conn, 301) =~ "/foo/bar"
       assert redirected_to(conn, :moved_permanently) =~ "/foo/bar"
   """
-  @spec redirected_to(Conn.t, status :: non_neg_integer) :: Conn.t
+  @spec redirected_to(Conn.t, status :: non_neg_integer) :: String.t
   def redirected_to(conn, status \\ 302)
 
   def redirected_to(%Conn{state: :unset}, _status) do
diff --git a/priv/templates/phx.gen.context/test_cases.exs b/priv/templates/phx.gen.context/test_cases.exs
index e83b1252f..92f73323f 100644
--- a/priv/templates/phx.gen.context/test_cases.exs
+++ b/priv/templates/phx.gen.context/test_cases.exs
@@ -36,9 +36,7 @@
 
     test "update_<%= schema.singular %>/2 with valid data updates the <%= schema.singular %>" do
       <%= schema.singular %> = <%= schema.singular %>_fixture()
-      assert {:ok, %<%= inspect schema.alias %>{} = <%= schema.singular %>} = <%= inspect context.alias %>.update_<%= schema.singular %>(<%= schema.singular %>, @update_attrs)
-
-      <%= for {field, value} <- schema.params.update do %>
+      assert {:ok, %<%= inspect schema.alias %>{} = <%= schema.singular %>} = <%= inspect context.alias %>.update_<%= schema.singular %>(<%= schema.singular %>, @update_attrs)<%= for {field, value} <- schema.params.update do %>
       assert <%= schema.singular %>.<%= field %> == <%= Mix.Phoenix.Schema.value(schema, field, value) %><% end %>
     end
 
diff --git a/test/phoenix/integration/websocket_channels_test.exs b/test/phoenix/integration/websocket_channels_test.exs
index 863df89b7..7811eb271 100644
--- a/test/phoenix/integration/websocket_channels_test.exs
+++ b/test/phoenix/integration/websocket_channels_test.exs
@@ -130,6 +130,14 @@ defmodule Phoenix.Integration.WebSocketChannelsTest do
         timeout: 200,
         connect_info: [:x_headers, :peer_data, :uri]
       ]
+
+    socket "/ws/connect_info_custom", UserSocketConnectInfo,
+      websocket: [
+        check_origin: ["//example.com"],
+        timeout: 200,
+        connect_info: [:x_headers, :peer_data, :uri, signing_salt: "salt"]
+      ]
+
   end
 
   setup_all do
@@ -245,6 +253,21 @@ defmodule Phoenix.Integration.WebSocketChannelsTest do
                                       "port" => 80}}}}
       end
 
+      test "transport custom keywords are extracted to the socket connect_info" do
+        {:ok, sock} =
+          WebsocketClient.start_link(
+            self(),
+            "ws://127.0.0.1:#{@port}/ws/connect_info_custom/websocket?vsn=#{@vsn}",
+            @serializer
+          )
+        WebsocketClient.join(sock, lobby(), %{})
+
+        assert_receive %Message{
+          event: "joined",
+          payload: %{"connect_info" => %{"signing_salt" => "salt"}}
+        }
+      end
+
       test "logs user socket connect when enabled" do
         log = capture_log(fn ->
           {:ok, _} = WebsocketClient.start_link(self(), "#{@vsn_path}&logging=enabled", @serializer)