# ESpecPhoenix
##### ESpec helpers and matchers for Phoenix web framework.
Read about ESpec [here](https://github.com/antonmi/espec).
There are examples in [app/spec](https://github.com/antonmi/espec_phoenix/tree/master/app/spec).
## Contents
- [Installation](#installation)
## Installation
Add `espec_phoenix` to dependencies in the `mix.exs` file:
```elixir
def deps do
...
{:espec_phoenix, "~> 0.1.2", only: :test, app: false},
#{:espec, github: "antonmi/espec_phoenix", only: :test, app: false}, to get the latest version
...
end
```
```sh
mix deps.get
```
Set `preferred_cli_env` for `espec` in the `mix.exs` file:
```elixir
def project do
...
preferred_cli_env: [espec: :test],
...
end
```
Run:
```sh
MIX_ENV=test mix espec.init
```
The task creates `spec/spec_helper.exs` and `spec/example_spec.exs`.
Run:
```sh
MIX_ENV=test mix espec_phoenix.init
```
The task creates `phoenix_helper.exs`, `espec_phoenix_extend.ex`, and basic specs folders and places simple examples there.
`phoenix_helper.exs` has Phoenix related configurations.
Replace `App.Repo` with your repo module.
You must require this helper in your `spec_helper.exs`.
Also you need restart `Ecto` transaction before each example. So `spec_helper.exs` should look like:
```elixir
#require phoenix_helper.exs
Code.require_file("spec/phoenix_helper.exs")
ESpec.start
ESpec.configure fn(config) ->
config.before fn ->
#restart transactions
Ecto.Adapters.SQL.restart_test_transaction(YourApplication.Repo, [])
end
config.finally fn(__) ->
end
end
```
The `espec_phoenix_extend.ex` file contains `ESpec.Phoenix.Extend` module.
Use this module to import or alias additional modules to your specs.
## Model specs
### Example
```elixir
defmodule App.UserSpec do
use ESpec.Phoenix, model: App.User
alias App.User
let :valid_attrs, do: %{age: 42, name: "some content"}
let :invalid_attrs, do: %{}
context "valid changeset" do
subject do: User.changeset(%User{}, valid_attrs)
it do: should be_valid
end
end
```
#### Changeset helpers
```elixir
expect(changeset).to be_valid
... have_errors(:name)
... have_errors([:name, :surname])
... have_errors(name: "can't be blank", surname: "can't be blank")
```
## Controller specs
There is the `action/2` helper function wich call controller functions directy.
### Example
```elixir
defmodule App.UserControllerSpec do
use ESpec.Phoenix, controller: App.UserController
alias App.User
describe "show" do
let :user, do: %User{id: 1, age: 25, name: "Jim"}
before do
allow(Repo).to accept(:get, fn
User, 1 -> user
User, id -> passthrough([id])
end)
end
subject do: action(:show, %{"id" => 1})
it do: should be_successfull
it do: should render_template("show.html")
it do: should have_in_assigns(user: user)
end
end
```
#### Conn helpers
##### Check status
```elixir
expect(res_conn).to be_successfull #or be_success
... be_redirection #be_redirect
... be_not_found #be_missing
... be_server_error #be_error
... have_http_status(code)
... redirect_to(user_path(conn, :index))
```
##### Check template and view
```elixir
expect(res_conn).to render_template("index.html")
... use_view(App.UserView)
```
##### Check assigns
```elixir
expect(res_conn).to have_in_assigns(:users)
... have_in_assigns([:users, :options])
... have_in_assigns(users: users, options: options)
```
##### Check flash
```elixir
expect(res_conn).to have_in_flash(:info)
... have_in_flash(info: "User created successfully.")
```
## View specs
There is the `render/2` helper function available in the view specs.
### Example
```elixir
defmodule App.UserViewsSpec do
use ESpec.Phoenix, view: App.UserView
alias App.User
describe "show" do
let :user, do: %User{id: 1, age: 25, name: "Jim"}
subject do: render("show.html", user: user)
it do: should have_text("Show user")
it do: should have_text_in("ul li", user.name)
it do: should have_text_in("ul li", user.age)
it do: should have_attribute_in("a", href: user_path(conn, :index))
end
end
```
ESpec.Phoenix uses [Floki](https://github.com/philss/floki) to parse html.
There are some mathers for html string or for `conn` structure.
#### Content helpers
##### Check presence of plain text
```elixir
expect(html).to have_text("some text") #String.contains?(html, "some text")
... have_content("some text")
```
##### Check presence of some selector
```elixir
expect(html).to have_selector("input #user_name") #Floki.find(html, "input #user_name")
```
##### Check text in the selector
```elixir
expect(html).to have_text_in("label", "Name")
```
##### Check attributes in the selector
```elixir
expect(html).to have_attributes_in("form", action: "/users", method: "post")
```
## Requests specs
Requests specs tests request/response cycles from end to end using a black box approach.
Functions for corresponding http methods are imported from `Phoenix.ConnTest`.
Both 'Conn helpers' and 'Content helpers' available.
### Example
```elixir
defmodule App.UserRequestsSpec do
use ESpec.Phoenix, request: App.Endpoint
alias App.User
describe "list user" do
before do
user1 = %User{name: "Bill", age: 25} |> Repo.insert
user2 = %User{name: "Jonh", age: 26} |> Repo.insert
{:ok, user1: user1, user2: user2}
end
subject! do: get(conn(), user_path(conn(), :index))
it do: should be_successfull
it "checks content" do
should have_content __.user1.name
should have_content __.user2.name
end
end
end
```