# `Stampede.LiveView`
[🔗](https://github.com/mandar1045/stampede/blob/v0.2.0/lib/stampede/live_view.ex#L1)

A LiveView client for load tests.

    {:ok, view} = Stampede.LiveView.connect("http://localhost:4000/counter")
    {:ok, _reply} = Stampede.LiveView.click(view, "inc")
    {:ok, _reply} = Stampede.LiveView.submit(view, "save", %{"user" => %{"name" => "jo"}})
    {:ok, view} = Stampede.LiveView.navigate(view, "/posts")

`connect/2` does what a browser does. It fetches the page over HTTP, reads
the CSRF token and the LiveView's signed session from it, opens the
LiveView socket with the cookies the page set, and joins the LiveView.

The connection is a `Stampede.Channels` socket, available as `view.socket`,
so it sends heartbeats and is closed at the end of an iteration like any
other. `view.url` is the URL of the LiveView the view is on.

## Navigation

`patch/3` and `navigate/3` move between pages like `<.link patch>` and
`<.link navigate>` do, without loading the page again when LiveView would
not. When an event makes the LiveView navigate on its own, it returns
`{:error, {:live_redirect, %{"to" => to}}}`, or `{:error, {:redirect,
%{"to" => to}}}` for a full redirect, as `Phoenix.LiveViewTest` does.
Follow either one with `navigate/3`.

## Metrics

Inside a run, a view records:

  * `"live.page"` and `"live.page.errors"` - fetching the page
  * `"live.join"` and `"live.join.errors"` - joining the LiveView
  * `"live.event"` and `"live.event.errors"` - events and their replies
  * `"live.patch"` and `"live.patch.errors"` - patches
  * `"live.navigate"` and `"live.navigate.errors"` - navigations over the
    same socket. A navigation that needs a new page load records that
    load instead.

along with the connection metrics listed in `Stampede.Channels`.

# `t`

```elixir
@type t() :: %Stampede.LiveView{
  opts: term(),
  params: term(),
  root: term(),
  socket: Stampede.Channels.socket(),
  topic: String.t(),
  url: String.t()
}
```

# `await`

```elixir
@spec await(t(), timeout() | keyword()) :: {:ok, map()} | {:error, term()}
```

Waits for the LiveView to send an update of its own, such as one caused by
`handle_info/2`, and returns its diff.

Takes a timeout in milliseconds, or the same options as
`Stampede.Channels.await/4`, with `:match` getting each diff.

# `change`

```elixir
@spec change(t(), String.t(), map(), keyword()) ::
  {:ok, map() | nil} | {:error, term()}
```

Sends a form change, like a form with `phx-change={event}` would.

`params` are the form's fields, nested the way `handle_event/3` should
get them, such as `%{"user" => %{"name" => "jo"}}`. Takes the same
options and returns the same as `click/4`.

# `click`

```elixir
@spec click(t(), String.t(), map(), keyword()) ::
  {:ok, map() | nil} | {:error, term()}
```

Sends a click, like an element with `phx-click={event}` would.

`value` is what `handle_event/3` gets as its params. Returns `{:ok, reply}`
once the LiveView has handled it, where `reply` is the map given to
`{:reply, map, socket}`, or `nil` when `handle_event/3` returned
`{:noreply, socket}`. Returns `{:error, :channel_crashed}` if the LiveView
crashes while handling it.

## Options

  * `:target` - the component to send the event to, like `phx-target`
    does. It is the number LiveView renders for `phx-target={@myself}`;
    components are numbered from 1, in the order they first render.
  * `:timeout` - how long to wait for the reply, in milliseconds. Defaults
    to `5_000`.

# `close`

```elixir
@spec close(t()) :: :ok
```

Closes the connection.

# `connect`

```elixir
@spec connect(String.t(), keyword()) :: {:ok, t()} | {:error, term()}
```

Fetches the page at `url` and connects to the LiveView it renders.

## Options

  * `:headers` - extra headers for both the page and the socket, such as a
    `cookie` for pages behind a login
  * `:max_redirects` - how many redirects to follow while loading the page,
    keeping the cookies they set. Defaults to `5`.
  * `:socket_path` - where the LiveView socket is mounted. Defaults to
    `"/live"`.
  * `:timeout` - how long to wait for each page request and for the join,
    in milliseconds. Defaults to `5_000`.
  * `:transport_opts` - options for `:gen_tcp` or `:ssl`

Returns `{:error, {:http_status, status}}` when the page does not load or
redirects too many times, `{:error, :no_live_view}` when it has no
LiveView, and `{:error, reply}` when the LiveView refuses the join, for
example to redirect.

# `navigate`

```elixir
@spec navigate(t(), String.t(), keyword()) :: {:ok, t()} | {:error, term()}
```

Navigates to the LiveView at `to`, like `<.link navigate={to}>` would.

`to` is a path or a full URL. When the new LiveView is in the same
`live_session`, it joins over the same socket. Otherwise the page is
loaded again with `connect/2`, as a browser does. Returns `{:ok, view}`
for the new LiveView.

Takes a `:timeout` option, in milliseconds, that defaults to `5_000`.

# `patch`

```elixir
@spec patch(t(), String.t(), keyword()) :: {:ok, t()} | {:error, term()}
```

Patches the LiveView to `to`, like `<.link patch={to}>` would.

`to` is a path, such as `"/posts?page=2"`, or a full URL. The LiveView's
`handle_params/3` runs, and `{:ok, view}` returns the view at its new URL.
When `to` belongs to another LiveView, it navigates there with
`navigate/3` instead, as a browser does.

Takes a `:timeout` option, in milliseconds, that defaults to `5_000`.

# `submit`

```elixir
@spec submit(t(), String.t(), map(), keyword()) ::
  {:ok, map() | nil} | {:error, term()}
```

Submits a form, like a form with `phx-submit={event}` would.

Takes the same arguments and returns the same as `change/4`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
