Repository: phoenixframework/phoenix
Pull request #3432 (2019-06-03 19:31:53)

mentioning the .gitignore
I hadn't noticed it in first instance, so better explicitly mention it.


Patch:
diff --git a/assets/package.json b/assets/package.json
index 731a33dcc..587aa1117 100644
--- a/assets/package.json
+++ b/assets/package.json
@@ -23,7 +23,7 @@
     "mock-socket": "^6.0.1",
     "sinon": "^1.17.6",
     "webpack": "^4.23.1",
-    "webpack-cli": "^3.1.2"
+    "webpack-cli": "^3.3.2"
   },
   "sideEffects": false,
   "scripts": {
diff --git a/guides/channels.md b/guides/channels.md
index 13c35cc31..96c1759e0 100644
--- a/guides/channels.md
+++ b/guides/channels.md
@@ -144,14 +144,12 @@ The `Phoenix.Socket.Message` module defines a struct with the following keys whi
 
 ### PubSub
 
-Typically, we don't directly use the Phoenix PubSub layer when developing Phoenix applications.
-Rather, it's used internally by Phoenix itself.
-But we may need to configure it.
-
 PubSub consists of the `Phoenix.PubSub` module and a variety of modules for different adapters and their `GenServer`s.
 These modules contain functions which are the nuts and bolts of organizing Channel communication - subscribing to topics, unsubscribing from topics, and broadcasting messages on a topic.
+PubSub is used internally by Phoenix.
+It's also useful in application development in any case where you want to notify interested processes of an event; for instance, letting all connected [live views](https://github.com/phoenixframework/phoenix_live_view) know that a new comment has been added to a post.
 
-The PubSub system also takes care of getting messages from one node to another, so that it can be sent to all subscribers across the cluster.
+The PubSub system takes care of getting messages from one node to another so that they can be sent to all subscribers across the cluster.
 By default, this is done using [Phoenix.PubSub.PG2](https://hexdocs.pm/phoenix_pubsub/Phoenix.PubSub.PG2.html), which uses native BEAM messaging.
 
 If your deployment environment does not support distributed Elixir or direct communication between servers, Phoenix also ships with a [Redis Adapter](https://hexdocs.pm/phoenix_pubsub_redis/Phoenix.PubSub.Redis.html) that uses Redis to exchange PubSub data. Please see the [Phoenix.PubSub docs](http://hexdocs.pm/phoenix_pubsub/Phoenix.PubSub.html) for more information.
@@ -262,7 +260,7 @@ Save the file and your browser should auto refresh, thanks to the Phoenix live r
 In `lib/hello_web/templates/page/index.html.eex`, we'll replace the existing code with a container to hold our chat messages, and an input field to send them:
 
 ```html
-<div id="messages"></div>
+<div id="messages" role="log" aria-live="polite"></div>
 <input id="chat-input" type="text"></input>
 ```
 
@@ -275,7 +273,7 @@ let chatInput         = document.querySelector("#chat-input")
 let messagesContainer = document.querySelector("#messages")
 
 chatInput.addEventListener("keypress", event => {
-  if(event.keyCode === 13){
+  if(event.key === 'Enter'){
     channel.push("new_msg", {body: chatInput.value})
     chatInput.value = ""
   }
@@ -297,14 +295,14 @@ let chatInput         = document.querySelector("#chat-input")
 let messagesContainer = document.querySelector("#messages")
 
 chatInput.addEventListener("keypress", event => {
-  if(event.keyCode === 13){
+  if(event.key === 'Enter'){
     channel.push("new_msg", {body: chatInput.value})
     chatInput.value = ""
   }
 })
 
 channel.on("new_msg", payload => {
-  let messageItem = document.createElement("li")
+  let messageItem = document.createElement("p")
   messageItem.innerText = `[${Date()}] ${payload.body}`
   messagesContainer.appendChild(messageItem)
 })
diff --git a/guides/contexts.md b/guides/contexts.md
index 45a72887f..facd9434b 100644
--- a/guides/contexts.md
+++ b/guides/contexts.md
@@ -1096,8 +1096,8 @@ defmodule Hello.UserRegistration do
 
   def register_user(params) do
     Multi.new()
-    |> Multi.run(:user, fn _ -> Accounts.create_user(params) end)
-    |> Multi.run(:author, fn %{user: user} ->
+    |> Multi.run(:user, fn _repo, _ -> Accounts.create_user(params) end)
+    |> Multi.run(:author, fn _repo, %{user: user} ->
       {:ok, CMS.ensure_author_exists(user)}
     end)
     |> Repo.transaction()
diff --git a/guides/deployment/heroku.md b/guides/deployment/heroku.md
index 7c97fe876..1affc42b7 100644
--- a/guides/deployment/heroku.md
+++ b/guides/deployment/heroku.md
@@ -325,8 +325,13 @@ And that's it!
 
 Heroku gives you the ability to connect to your dyno with an iex shell which allows running Elixir code such as database queries.
 
+- Modify the `web` process in your Procfile to run a named node:
+  ```
+  web: elixir --sname server -S mix phx.server
+  ```
+- Redeploy to Heroku
 - Connect to the dyno with `heroku ps:exec`
-note that if you have several applications on the same repository you will need to specify the app name or the remote name with `--app APP_NAME` or `--remote REMOTE_NAME`
+  note that if you have several applications on the same repository you will need to specify the app name or the remote name with `--app APP_NAME` or `--remote REMOTE_NAME`
 - Launch an iex session with `iex --sname console --remsh server@${HOSTNAME}`
 
 You have an iex session into your dyno!
diff --git a/guides/ecto.md b/guides/ecto.md
index 1e8bada7b..e553fed25 100644
--- a/guides/ecto.md
+++ b/guides/ecto.md
@@ -2,11 +2,10 @@
 
 Most web applications today need some form of data validation and persistence. In the Elixir ecosystem, we have Ecto to enable this. Before we jump into building database-backed web features, we're going to focus on the finer details of Ecto to give a solid base to build our web features on top of. Let's get started!
 
-Ecto currently has adapters for the following databases:
+Ecto has out of the box support for the following databases:
 
-* PostgreSQL
-* MySQL
-* MSSQL
+* PostgreSQL (via [`postgrex`](https://github.com/elixir-ecto/postgrex))
+* MySQL (via [`myxql`](https://github.com/elixir-ecto/myxql))
 
 Newly generated Phoenix projects include Ecto with the PostgreSQL adapter by default (you can pass the `--no-ecto` flag to exclude this).
 
diff --git a/guides/presence.md b/guides/presence.md
index 4569107fe..a993ac30e 100644
--- a/guides/presence.md
+++ b/guides/presence.md
@@ -64,7 +64,7 @@ end
 We also need to change our connect function to take a `user_id` from the params and assign it on the socket. In production you may want to use `Phoenix.Token` if you have real users that are authenticated.
 
 ```elixir
-def connect(params, socket) do
+def connect(params, socket, _connect_info) do
   {:ok, assign(socket, :user_id, params["user_id"])}
 end
 ```
diff --git a/guides/up_and_running.md b/guides/up_and_running.md
index 550b53222..ccc837dde 100644
--- a/guides/up_and_running.md
+++ b/guides/up_and_running.md
@@ -16,7 +16,7 @@ $ mix phx.new hello
 
 > A note about [webpack](https://webpack.js.org/) before we begin: Phoenix will use webpack for asset management by default. Webpack's dependencies are installed via the node package manager, not mix. Phoenix will prompt us to install them at the end of the `mix phx.new` task. If we say "no" at that point, and if we don't install those dependencies later with `npm install`, our application will raise errors when we try to start it, and our assets may not load properly. If we don't want to use webpack at all, we can simply pass `--no-webpack` to `mix phx.new`.
 
-> A note about [Ecto](https://hexdocs.pm/phoenix/ecto.html): Ecto allows our Phoenix application to communicate with a data store, such as PostgreSQL, MySQL, or MSSQL. If our application will not require this component we can skip this dependency by passing the `--no-ecto` flag to `mix phx.new`. This flag may also be combined with `--no-webpack` to create a skeleton application.
+> A note about [Ecto](https://hexdocs.pm/phoenix/ecto.html): Ecto allows our Phoenix application to communicate with a data store, such as PostgreSQL, MySQL, and others. If our application will not require this component we can skip this dependency by passing the `--no-ecto` flag to `mix phx.new`. This flag may also be combined with `--no-webpack` to create a skeleton application.
 
 > To learn more about `mix phx.new` you can read the [Mix Tasks Guide](phoenix_mix_tasks.html#phoenix-specific-mix-tasks).
 
@@ -32,7 +32,11 @@ mix phx.new hello
 Fetch and install dependencies? [Yn]
 ```
 
-Phoenix generates the directory structure and all the files we will need for our application. When it's done, it will ask us if we want it to install our dependencies for us. Let's say yes to that.
+Phoenix generates the directory structure and all the files we will need for our application. 
+
+> Phoenix promotes the usage of git as version control software: among the generated files we find a `.gitignore`. We can `git init` our repository, and immediately add and commit all that hasn't been marked ignored. 
+
+When it's done, it will ask us if we want it to install our dependencies for us. Let's say yes to that.
 
 ```console
 Fetch and install dependencies? [Yn] Y
diff --git a/installer/templates/phx_assets/package.json b/installer/templates/phx_assets/package.json
index e5d0f5147..015f5cd72 100644
--- a/installer/templates/phx_assets/package.json
+++ b/installer/templates/phx_assets/package.json
@@ -17,9 +17,9 @@
     "copy-webpack-plugin": "^4.5.0",
     "css-loader": "^2.1.1",
     "mini-css-extract-plugin": "^0.4.0",
-    "optimize-css-assets-webpack-plugin": "^4.0.0",
+    "optimize-css-assets-webpack-plugin": "^5.0.1",
     "terser-webpack-plugin": "^1.1.0",
     "webpack": "4.4.0",
-    "webpack-cli": "^3.1.2"
+    "webpack-cli": "^3.3.2"
   }
 }
diff --git a/installer/templates/phx_assets/webpack.config.js b/installer/templates/phx_assets/webpack.config.js
index 40b8fa684..ad2083175 100644
--- a/installer/templates/phx_assets/webpack.config.js
+++ b/installer/templates/phx_assets/webpack.config.js
@@ -13,7 +13,7 @@ module.exports = (env, options) => ({
     ]
   },
   entry: {
-    './js/app.js': ['./js/app.js'].concat(glob.sync('./vendor/**/*.js'))
+    './js/app.js': glob.sync('./vendor/**/*.js').concat(['./js/app.js'])
   },
   output: {
     filename: 'app.js',
diff --git a/lib/mix/tasks/compile.phoenix.ex b/lib/mix/tasks/compile.phoenix.ex
index f9078442e..ff7ce5240 100644
--- a/lib/mix/tasks/compile.phoenix.ex
+++ b/lib/mix/tasks/compile.phoenix.ex
@@ -11,8 +11,8 @@ defmodule Mix.Tasks.Compile.Phoenix do
     {:ok, _} = Application.ensure_all_started(:phoenix)
 
     case touch() do
-      [] -> :noop
-      _  -> :ok
+      [] -> {:noop, []}
+      _  -> {:ok, []}
     end
   end
 
diff --git a/lib/phoenix/config.ex b/lib/phoenix/config.ex
index ea983c4a5..1aff40c40 100644
--- a/lib/phoenix/config.ex
+++ b/lib/phoenix/config.ex
@@ -12,7 +12,7 @@ defmodule Phoenix.Config do
   @doc """
   Starts a Phoenix configuration handler.
   """
-  def start_link(module, config, defaults, opts \\ []) do
+  def start_link({module, config, defaults, opts}) do
     permanent = Keyword.keys(defaults)
     GenServer.start_link(__MODULE__, {module, config, permanent}, opts)
   end
diff --git a/lib/phoenix/endpoint.ex b/lib/phoenix/endpoint.ex
index aab819d40..e155a51a4 100644
--- a/lib/phoenix/endpoint.ex
+++ b/lib/phoenix/endpoint.ex
@@ -66,8 +66,8 @@ defmodule Phoenix.Endpoint do
 
   For dynamically configuring the endpoint, such as loading data
   from environment variables or configuration files, Phoenix invokes
-  the `init/2` callback on the endpoint, passing a `:supervisor`
-  atom as first argument and the endpoint configuration as second.
+  the `init/2` callback on the endpoint, passing the atom `:supervisor`
+  as the first argument and the endpoint configuration as second.
 
   All of Phoenix configuration, except the Compile-time configuration
   below can be set dynamically from the `c:init/2` callback.
diff --git a/lib/phoenix/endpoint/supervisor.ex b/lib/phoenix/endpoint/supervisor.ex
index 4bdbc9f04..60d55732f 100644
--- a/lib/phoenix/endpoint/supervisor.ex
+++ b/lib/phoenix/endpoint/supervisor.ex
@@ -67,8 +67,7 @@ defmodule Phoenix.Endpoint.Supervisor do
       server_children(mod, conf, otp_app, server?) ++
       watcher_children(mod, conf, server?)
 
-    # Supervisor.init(children, strategy: :one_for_one)
-    {:ok, {{:one_for_one, 3, 5}, children}}
+    Supervisor.init(children, strategy: :one_for_one)
   end
 
   defp pubsub_children(_mod, conf) do
@@ -89,8 +88,8 @@ defmodule Phoenix.Endpoint.Supervisor do
   end
 
   defp config_children(mod, conf, default_conf) do
-    args = [mod, conf, default_conf, [name: Module.concat(mod, "Config")]]
-    [worker(Phoenix.Config, args)]
+    args = {mod, conf, default_conf, name: Module.concat(mod, "Config")}
+    [{Phoenix.Config, args}]
   end
 
   defp server_children(mod, config, otp_app, server?) do
@@ -163,8 +162,7 @@ defmodule Phoenix.Endpoint.Supervisor do
   defp watcher_children(_mod, conf, server?) do
     if server? do
       Enum.map(conf[:watchers], fn {cmd, args} ->
-        worker(Phoenix.Endpoint.Watcher, watcher_args(cmd, args),
-               id: make_ref(), restart: :transient)
+        {Phoenix.Endpoint.Watcher, watcher_args(cmd, args)}
       end)
     else
       []
@@ -173,7 +171,7 @@ defmodule Phoenix.Endpoint.Supervisor do
 
   defp watcher_args(cmd, cmd_args) do
     {args, opts} = Enum.split_while(cmd_args, &is_binary(&1))
-    [cmd, args, opts]
+    {cmd, args, opts}
   end
 
   @doc """
diff --git a/lib/phoenix/endpoint/watcher.ex b/lib/phoenix/endpoint/watcher.ex
index d78256794..386318d01 100644
--- a/lib/phoenix/endpoint/watcher.ex
+++ b/lib/phoenix/endpoint/watcher.ex
@@ -1,10 +1,16 @@
 defmodule Phoenix.Endpoint.Watcher do
   @moduledoc false
-  use Task
-
   require Logger
 
-  def start_link(cmd, args, opts) do
+  def child_spec(args) do
+    %{
+      id: make_ref(),
+      start: {__MODULE__, :start_link, [args]},
+      restart: :transient
+    }
+  end
+
+  def start_link({cmd, args, opts}) do
     Task.start_link(__MODULE__, :watch, [to_string(cmd), args, opts])
   end
 
diff --git a/lib/phoenix/router.ex b/lib/phoenix/router.ex
index 599acea1b..914610334 100644
--- a/lib/phoenix/router.ex
+++ b/lib/phoenix/router.ex
@@ -260,11 +260,19 @@ defmodule Phoenix.Router do
   end
 
   @doc false
-  def __call__(%{private: %{phoenix_router: router, phoenix_bypass: {router, pipes}}} = conn, _match) do
-    Enum.reduce(pipes, conn, fn pipe, acc -> apply(router, pipe, [acc, []]) end)
+  def __call__(
+        %{private: %{phoenix_router: router, phoenix_bypass: {router, pipes}}} = conn,
+        {metadata, prepare, pipeline, _}
+      ) do
+    conn = prepare.(conn, metadata)
+
+    case pipes do
+      :current -> pipeline.(conn)
+      _ -> Enum.reduce(pipes, conn, fn pipe, acc -> apply(router, pipe, [acc, []]) end)
+    end
   end
-  def __call__(%{private: %{phoenix_bypass: :all}} = conn, _match) do
-    conn
+  def __call__(%{private: %{phoenix_bypass: :all}} = conn, {metadata, prepare, _, _}) do
+    prepare.(conn, metadata)
   end
   def __call__(conn, {metadata, prepare, pipeline, {plug, opts}}) do
     conn = prepare.(conn, metadata)
@@ -845,4 +853,39 @@ defmodule Phoenix.Router do
       unquote(add_route(:forward, :*, path, plug, plug_opts, router_opts))
     end
   end
+
+  @doc """
+  Returns the compile-time route info and runtime path params for a request.
+
+  A map of metadata is returned with the following keys:
+
+    * `:log` - the configured log level. For example `:debug`
+    * `:path_params` - the map of runtime path params
+    * `:pipe_through` - the list of pipelines for the route's scope, for example `[:browser]`
+    * `:plug` - the plug to dipatch the route to, for example `AppWeb.PostController`
+    * `:plug_opts` - the options to pass when calling the plug, for example: `:index`
+    * `:route` - the string route pattern, such as `"/posts/:id"`
+
+  ## Examples
+
+      iex> Phoenix.Router.route_info(AppWeb.Router, "GET", "/posts/123", "myhost")
+      %{
+        log: :debug,
+        path_params: %{"id" => "123"},
+        pipe_through: [:browser],
+        plug: AppWeb.PostController,
+        plug_opts: :show,
+        route: "/posts/:id",
+      }
+
+      iex> Phoenix.Router.route_info(MyRouter, "GET", "/not-exists", "myhost")
+      :error
+  """
+  def route_info(router, method, path, host) do
+    split_path = for segment <- String.split(path, "/"), segment != "", do: segment
+    case router.__match_route__(method, split_path, host) do
+      {%{} = metadata, _prepare, _pipeline, {_plug, _opts}} -> Map.delete(metadata, :conn)
+      :error -> :error
+    end
+  end
 end
diff --git a/lib/phoenix/token.ex b/lib/phoenix/token.ex
index 9226e9029..f89c89d33 100644
--- a/lib/phoenix/token.ex
+++ b/lib/phoenix/token.ex
@@ -69,7 +69,7 @@ defmodule Phoenix.Token do
       defmodule MyApp.UserSocket do
         use Phoenix.Socket
 
-        def connect(%{"token" => token}, socket) do
+        def connect(%{"token" => token}, socket, _connect_info) do
           case Phoenix.Token.verify(socket, "user salt", token, max_age: 86400) do
             {:ok, user_id} ->
               socket = assign(socket, :user, Repo.get!(User, user_id))
@@ -78,6 +78,8 @@ defmodule Phoenix.Token do
               :error
           end
         end
+
+        def connect(_params, _socket, _connect_info), do: :error
       end
 
   In this example, the phoenix.js client will send the token in the
diff --git a/priv/templates/phx.gen.context/schema_access.ex b/priv/templates/phx.gen.context/schema_access.ex
index f75618931..769889156 100644
--- a/priv/templates/phx.gen.context/schema_access.ex
+++ b/priv/templates/phx.gen.context/schema_access.ex
@@ -88,7 +88,7 @@
   ## Examples
 
       iex> change_<%= schema.singular %>(<%= schema.singular %>)
-      %Ecto.Changeset{source: %<%= inspect schema.alias %>{}}
+      %Ecto.Changeset{data: %<%= inspect schema.alias %>{}}
 
   """
   def change_<%= schema.singular %>(%<%= inspect schema.alias %>{} = <%= schema.singular %>) do


Test patch:
diff --git a/lib/phoenix/test/conn_test.ex b/lib/phoenix/test/conn_test.ex
index 1ad99ea70..dbc6c4e8d 100644
--- a/lib/phoenix/test/conn_test.ex
+++ b/lib/phoenix/test/conn_test.ex
@@ -501,48 +501,50 @@ defmodule Phoenix.ConnTest do
   end
 
   @doc """
-  Calls the Endpoint and bypasses Router match.
+  Calls the Endpoint and Router pipelines.
 
-  Useful for unit testing Plugs where Endpoint and/or
-  router pipeline plugs are required for proper setup.
+  Useful for unit testing Plugs where Endpoint and/or router pipeline
+  plugs are required for proper setup.
 
   Note the use of `get("/")` following `bypass_through` in the examples below.
   To execute the plug pipelines, you must issue a request against the router.
   Most often, you can simpy send a GET request against the root path, but you
   may also specify a different method or path which your pipelines may operate
-  against. If you ommit the request you may find that your tests return
-  a `flash not fetched, call fetch_flash/2` or similar error.
+  against.
 
   ## Examples
 
-  For example, imagine you are testing an authentication
-  plug in isolation, but you need to invoke the Endpoint plugs
-  and `:browser` pipeline of your Router for session and flash
-  related dependencies:
+  For example, imagine you are testing an authentication plug in
+  isolation, but you need to invoke the Endpoint plugs and router
+  Ripelines to set up session and flash related dependencies.
+  One option is to invoke an existing route that uses the proper
+  pipelines. You can do so by passing the connection and the
+  router name to `bypass_through`:
 
       conn =
         conn
-        |> bypass_through(MyAppWeb.Router, [:browser])
-        |> get("/")
+        |> bypass_through(MyAppWeb.Router)
+        |> get("/some_url")
         |> MyApp.RequireAuthentication.call([])
       assert conn.halted
 
-  Alternatively, you could invoke only the Endpoint, and Router:
+  You can also specify which pipelines you want to run:
 
       conn =
         conn
-        |> bypass_through(MyAppWeb.Router, [])
+        |> bypass_through(MyAppWeb.Router, [:browser])
         |> get("/")
         |> MyApp.RequireAuthentication.call([])
       assert conn.halted
 
-  Or only invoke the Endpoint's plugs:
+  Alternatively, you could only invoke the Endpoint's plugs:
 
       conn =
         conn
         |> bypass_through()
         |> get("/")
         |> MyApp.RequireAuthentication.call([])
+
       assert conn.halted
   """
   @spec bypass_through(Conn.t) :: Conn.t
@@ -551,12 +553,22 @@ defmodule Phoenix.ConnTest do
   end
 
   @doc """
-  Calls the Endpoint and bypasses Router match.
+  Calls the Endpoint and Router pipelines for the current route.
+
+  See `bypass_through/1`.
+  """
+  @spec bypass_through(Conn.t, module) :: Conn.t
+  def bypass_through(conn, router) do
+    Plug.Conn.put_private(conn, :phoenix_bypass, {router, :current})
+  end
+
+  @doc """
+  Calls the Endpoint and and the given Router pipelines.
 
   See `bypass_through/1`.
   """
   @spec bypass_through(Conn.t, module, atom | list) :: Conn.t
-  def bypass_through(conn, router, pipelines \\ []) do
+  def bypass_through(conn, router, pipelines) do
     Plug.Conn.put_private(conn, :phoenix_bypass, {router, List.wrap(pipelines)})
   end
 
@@ -575,18 +587,14 @@ defmodule Phoenix.ConnTest do
   def redirected_params(%Plug.Conn{} = conn) do
     router = Phoenix.Controller.router_module(conn)
     %URI{path: path, host: host} = conn |> redirected_to() |> URI.parse()
-    path_info = split_path(path)
 
-    case router.__match_route__("GET", path_info, host || conn.host) do
+    case Phoenix.Router.route_info(router, "GET", path, host || conn.host) do
       :error ->
         raise Phoenix.Router.NoRouteError, conn: conn, router: router
-      {%{path_params: path_params}, _prepare, _pipes, _dispatch} ->
+      %{path_params: path_params} ->
         Enum.into(path_params, %{}, fn {key, val} -> {String.to_atom(key), val} end)
     end
   end
-  defp split_path(path) do
-    for segment <- String.split(path, "/"), segment != "", do: segment
-  end
 
   @doc """
   Asserts an error was wrapped and sent with the given status.
diff --git a/test/mix/tasks/phx.new_test.exs b/test/mix/tasks/phx.new_test.exs
index 3f8bcf684..68e6aadd8 100644
--- a/test/mix/tasks/phx.new_test.exs
+++ b/test/mix/tasks/phx.new_test.exs
@@ -10,7 +10,7 @@ for pattern <- ["../../../installer/lib/phx_new/project.ex",
 # Define a fake live reload socket.
 defmodule Phoenix.LiveReloader.Socket do
   def child_spec(_) do
-    Supervisor.Spec.worker(Task, [fn -> :ok end], restart: :temporary)
+    Supervisor.child_spec({Task, fn -> :ok end}, [])
   end
 end
 
diff --git a/test/phoenix/code_reloader_test.exs b/test/phoenix/code_reloader_test.exs
index 41fed3343..4f3e3e53e 100644
--- a/test/phoenix/code_reloader_test.exs
+++ b/test/phoenix/code_reloader_test.exs
@@ -17,7 +17,7 @@ defmodule Phoenix.CodeReloaderTest do
   end
 
   test "compile.phoenix tasks touches files" do
-    assert Mix.Tasks.Compile.Phoenix.run([]) == :noop
+    assert Mix.Tasks.Compile.Phoenix.run([]) == {:noop, []}
   end
 
   test "reloads on every request" do
diff --git a/test/phoenix/config_test.exs b/test/phoenix/config_test.exs
index af2ff8cf8..e447010a9 100644
--- a/test/phoenix/config_test.exs
+++ b/test/phoenix/config_test.exs
@@ -15,7 +15,7 @@ defmodule Phoenix.ConfigTest do
   end
 
   test "starts an ets table as part of the module", meta do
-    {:ok, _pid} = start_link(meta.test, @all, @defaults)
+    {:ok, _pid} = start_link({meta.test, @all, @defaults, []})
     assert :ets.info(meta.test, :name) == meta.test
     assert :ets.lookup(meta.test, :parsers) == [parsers: false]
     assert :ets.lookup(meta.test, :static)  == [static: [at: "/"]]
@@ -23,7 +23,7 @@ defmodule Phoenix.ConfigTest do
   end
 
   test "can change configuration", meta do
-    {:ok, _pid} = start_link(meta.test, @all, @defaults)
+    {:ok, _pid} = start_link({meta.test, @all, @defaults, []})
 
     # Nothing changed
     config_change(meta.test, [], [])
@@ -43,7 +43,7 @@ defmodule Phoenix.ConfigTest do
   end
 
   test "can cache", meta do
-    {:ok, _pid} = start_link(meta.test, @all, @defaults)
+    {:ok, _pid} = start_link({meta.test, @all, @defaults, []})
 
     assert cache(meta.test, :__hello__, fn _ -> {:nocache, 1} end) == 1
     assert cache(meta.test, :__hello__, fn _ -> {:cache, 2} end) == 2
diff --git a/test/phoenix/endpoint/watcher_test.exs b/test/phoenix/endpoint/watcher_test.exs
index 6f6fb6232..e1a85aefb 100644
--- a/test/phoenix/endpoint/watcher_test.exs
+++ b/test/phoenix/endpoint/watcher_test.exs
@@ -6,7 +6,7 @@ defmodule Phoenix.Endpoint.WatcherTest do
 
   test "starts watching and writes to stdio" do
     assert capture_io(fn ->
-      {:ok, pid} = Watcher.start_link("echo", ["hello"], cd: File.cwd!())
+      {:ok, pid} = Watcher.start_link({"echo", ["hello"], cd: File.cwd!()})
       ref = Process.monitor(pid)
       assert_receive {:DOWN, ^ref, :process, ^pid, :normal}, 1000
     end) == "hello\n"
diff --git a/test/phoenix/integration/long_poll_channels_test.exs b/test/phoenix/integration/long_poll_channels_test.exs
index c8d4a9af4..3abf0ec3a 100644
--- a/test/phoenix/integration/long_poll_channels_test.exs
+++ b/test/phoenix/integration/long_poll_channels_test.exs
@@ -133,8 +133,8 @@ defmodule Phoenix.Integration.LongPollChannelsTest do
   end
 
   setup config do
-    for {_, pid, _, _} <- Supervisor.which_children(Phoenix.Transports.LongPoll.Supervisor) do
-      Supervisor.terminate_child(Phoenix.Transports.LongPoll.Supervisor, pid)
+    for {_, pid, _, _} <- DynamicSupervisor.which_children(Phoenix.Transports.LongPoll.Supervisor) do
+      DynamicSupervisor.terminate_child(Phoenix.Transports.LongPoll.Supervisor, pid)
     end
 
     {:ok, topic: "room:" <> to_string(config.test)}
diff --git a/test/phoenix/integration/long_poll_socket_test.exs b/test/phoenix/integration/long_poll_socket_test.exs
index f3cc39b0c..8e1aefd08 100644
--- a/test/phoenix/integration/long_poll_socket_test.exs
+++ b/test/phoenix/integration/long_poll_socket_test.exs
@@ -27,7 +27,7 @@ defmodule Phoenix.Integration.LongPollSocketTest do
 
     def child_spec(opts) do
       :value = Keyword.fetch!(opts, :custom)
-      Supervisor.Spec.worker(Task, [fn -> :ok end], restart: :transient)
+      Supervisor.child_spec({Task, fn -> :ok end}, [])
     end
 
     def connect(map) do
@@ -77,8 +77,8 @@ defmodule Phoenix.Integration.LongPollSocketTest do
   end
 
   setup do
-    for {_, pid, _, _} <- Supervisor.which_children(Phoenix.Transports.LongPoll.Supervisor) do
-      Supervisor.terminate_child(Phoenix.Transports.LongPoll.Supervisor, pid)
+    for {_, pid, _, _} <- DynamicSupervisor.which_children(Phoenix.Transports.LongPoll.Supervisor) do
+      DynamicSupervisor.terminate_child(Phoenix.Transports.LongPoll.Supervisor, pid)
     end
 
     :ok
diff --git a/test/phoenix/integration/websocket_socket_test.exs b/test/phoenix/integration/websocket_socket_test.exs
index c0fb5d558..c5fe44d4a 100644
--- a/test/phoenix/integration/websocket_socket_test.exs
+++ b/test/phoenix/integration/websocket_socket_test.exs
@@ -25,7 +25,7 @@ defmodule Phoenix.Integration.WebSocketTest do
 
     def child_spec(opts) do
       :value = Keyword.fetch!(opts, :custom)
-      Supervisor.Spec.worker(Task, [fn -> :ok end], restart: :transient)
+      Supervisor.child_spec({Task, fn -> :ok end}, [])
     end
 
     def connect(map) do
diff --git a/test/phoenix/router/routing_test.exs b/test/phoenix/router/routing_test.exs
index 2bc08aa9a..22240eb7a 100644
--- a/test/phoenix/router/routing_test.exs
+++ b/test/phoenix/router/routing_test.exs
@@ -36,7 +36,7 @@ defmodule Phoenix.Router.RoutingTest do
     get "/backups/*path", UserController, :image
     get "/static/images/icons/*image", UserController, :image
 
-    trace "/trace", UserController, :trace
+    trace("/trace", UserController, :trace)
     options "/options", UserController, :options
     connect "/connect", UserController, :connect
     match :move, "/move", UserController, :move
@@ -189,26 +189,26 @@ defmodule Phoenix.Router.RoutingTest do
 
     test "logs controller and action with (path) parameters" do
       assert capture_log(fn -> call(Router, :get, "/users/1", foo: "bar") end) =~ """
-      [debug] Processing with Phoenix.Router.RoutingTest.UserController.show/2
-        Parameters: %{"foo" => "bar", "id" => "1"}
-        Pipelines: []
-      """
+             [debug] Processing with Phoenix.Router.RoutingTest.UserController.show/2
+               Parameters: %{"foo" => "bar", "id" => "1"}
+               Pipelines: []
+             """
     end
 
     test "logs controller and action with filtered parameters" do
       assert capture_log(fn -> call(Router, :get, "/users/1", password: "bar") end) =~ """
-      [debug] Processing with Phoenix.Router.RoutingTest.UserController.show/2
-        Parameters: %{"id" => "1", "password" => "[FILTERED]"}
-        Pipelines: []
-      """
+             [debug] Processing with Phoenix.Router.RoutingTest.UserController.show/2
+               Parameters: %{"id" => "1", "password" => "[FILTERED]"}
+               Pipelines: []
+             """
     end
 
     test "logs plug with pipeline and custom level" do
       assert capture_log(fn -> call(Router, :get, "/plug") end) =~ """
-      [info]  Processing with Phoenix.Router.RoutingTest.SomePlug
-        Parameters: %{}
-        Pipelines: [:noop]
-      """
+             [info]  Processing with Phoenix.Router.RoutingTest.SomePlug
+               Parameters: %{}
+               Pipelines: [:noop]
+             """
     end
 
     test "does not log when log is set to false" do
@@ -216,4 +216,35 @@ defmodule Phoenix.Router.RoutingTest do
                "Processing with Phoenix.Router.RoutingTest.SomePlug"
     end
   end
+
+  test "route_info returns route string and path params" do
+    assert Phoenix.Router.route_info(Router, "GET", "foo/bar/baz", nil) == %{
+             log: :debug,
+             path_params: %{"path" => ["foo", "bar", "baz"]},
+             pipe_through: [],
+             plug: Phoenix.Router.RoutingTest.UserController,
+             plug_opts: :not_found,
+             route: "/*path"
+           }
+
+    assert Phoenix.Router.route_info(Router, "GET", "users/1", nil) == %{
+             log: :debug,
+             path_params: %{"id" => "1"},
+             pipe_through: [],
+             plug: Phoenix.Router.RoutingTest.UserController,
+             plug_opts: :show,
+             route: "/users/:id",
+           }
+
+    assert Phoenix.Router.route_info(Router, "GET", "/", "host") == %{
+             log: :debug,
+             path_params: %{},
+             pipe_through: [],
+             plug: Phoenix.Router.RoutingTest.UserController,
+             plug_opts: :index,
+             route: "/",
+           }
+
+    assert Phoenix.Router.route_info(Router, "POST", "/not-exists", "host") == :error
+  end
 end
diff --git a/test/phoenix/test/conn_test.exs b/test/phoenix/test/conn_test.exs
index 1ab22cfa8..c85f4f6ba 100644
--- a/test/phoenix/test/conn_test.exs
+++ b/test/phoenix/test/conn_test.exs
@@ -25,7 +25,7 @@ defmodule Phoenix.Test.ConnTest.Router do
 
   scope "/" do
     pipe_through :browser
-    get "/stat", CatchAll, :stat
+    get "/stat", CatchAll, :stat, private: %{route: :stat}
     forward "/", CatchAll
   end
 
@@ -40,6 +40,8 @@ defmodule Phoenix.Test.ConnTest do
   use Phoenix.ConnTest
   alias Phoenix.Test.ConnTest.{Router, RedirRouter}
 
+  @moduletag :capture_log
+
   defmodule ConnError do
     defexception [message: nil, plug_status: 500]
   end
@@ -188,7 +190,7 @@ defmodule Phoenix.Test.ConnTest do
         port: 111317,
         ssl_cert: <<1, 2, 3, 4>>
       }
-      conn = 
+      conn =
         build_conn()
         |> Plug.Test.put_peer_data(peer_data)
 
@@ -455,42 +457,61 @@ defmodule Phoenix.Test.ConnTest do
     conn =
       build_conn()
       |> bypass_through(Router, :browser)
-      |> get("/")
+      |> get("/stat")
 
     assert conn.assigns[:bypassed] == [:browser]
+    assert conn.private[:route] == :stat
     refute conn.assigns[:catch_all]
 
     conn =
       build_conn()
       |> bypass_through(Router, [:api])
-      |> get("/")
+      |> get("/stat")
 
     assert conn.assigns[:bypassed] == [:api]
+    assert conn.private[:route] == :stat
     refute conn.assigns[:catch_all]
 
     conn =
       build_conn()
       |> bypass_through(Router, [:browser, :api])
-      |> get("/")
+      |> get("/stat")
 
     assert conn.assigns[:bypassed] == [:browser, :api]
+    assert conn.private[:route] == :stat
     refute conn.assigns[:catch_all]
   end
 
-  test "bypass_through/2 bypasses route match" do
+  test "bypass_through/3 with empty pipeline" do
     conn =
       build_conn()
       |> bypass_through(Router, [])
-      |> get("/")
+      |> get("/stat")
+
+    refute conn.assigns[:bypassed]
+    assert conn.private[:route] == :stat
+    refute conn.assigns[:catch_all]
+  end
+
+  test "bypass_through/2 with route pipeline" do
+    conn =
+      build_conn()
+      |> bypass_through(Router)
+      |> get("/stat")
+
+    assert conn.assigns[:bypassed] == [:browser]
+    assert conn.private[:route] == :stat
     refute conn.assigns[:catch_all]
   end
 
-  test "bypass_through/1 bypasses router" do
+  test "bypass_through/1 without router" do
     conn =
       build_conn()
       |> bypass_through()
-      |> get("/")
+      |> get("/stat")
 
+    refute conn.assigns[:bypassed]
+    assert conn.private[:route] == :stat
     refute conn.assigns[:catch_all]
   end