# `AssertHTML`
[🔗](https://github.com/Kr00lIX/assert_html/blob/v0.2.3/lib/assert_html.ex#L1)

AssertHTML provides ExUnit assert helpers for testing rendered HTML using CSS selectors.

## Usage in Phoenix Controller and Integration Test

Given the `html_response(conn, 200)` returns:

```html
<!DOCTYPE html>
<html>
  <head>
    <title>PAGE TITLE</title>
  </head>
  <body>
    <a href="/signup">Sign up</a>
    <a href="/help">Help</a>
  </body>
</html>
```

An example controller test could be:

```elixir
defmodule YourAppWeb.PageControllerTest do
  use YourAppWeb.ConnCase, async: true

  test "should get index", %{conn: conn} do
    resp_conn = conn
    |> get(Routes.page_path(conn, :index))

    html_response(conn, 200)
    # Asserts that the page title is "PAGE TITLE"
    |> assert_html("title", "PAGE TITLE")
    # Asserts that the page title is "PAGE TITLE" and there is only one title element
    |> assert_html("title", count: 1, text: "PAGE TITLE")
    # Asserts that the page title matches "PAGE" and there is only one title element
    |> assert_html("title", count: 1, match: "PAGE")
    # Asserts that the page has one link with href value "/signup"
    |> assert_html("a[href='/signup']", count: 1)
    # Asserts that the page has at least one link
    |> assert_html("a", min: 1)
    # Asserts that the page has at most two links
    |> assert_html("a", max: 2)
    # Asserts that the page contains no forms
    |> refute_html("form")
  end
end
```

### Selector Checks
`assert_html(html, ".css .selector .exsits")` - Asserts that an element exists in the selector path.
`refute_html(html, ".css .selector")` - Asserts that an element does not exist in the selector path.

### Check Attributes
Supports meta attributes:

* `:text` – The exact text within the element
* `:match` - A value that the element's text should contain.

# `attribute_name`

```elixir
@type attribute_name() :: atom() | binary()
```

HTML element attribute name

# `attributes`

```elixir
@type attributes() :: [{attribute_name(), value()}]
```

HTML element attributes

# `context`

```elixir
@type context() :: {matcher(), html()}
```

# `css_selector`

```elixir
@type css_selector() :: String.t()
```

CSS selector

## Supported selectors

| Pattern         | Description                  |
|-----------------|------------------------------|
| *               | any element                  |
| E               | an element of type `E`       |
| E[foo]          | an `E` element with a "foo" attribute |
| E[foo="bar"]    | an E element whose "foo" attribute value is exactly equal to "bar" |
| E[foo~="bar"]   | an E element whose "foo" attribute value is a list of whitespace-separated values, one of which is exactly equal to "bar" |
| E[foo^="bar"]   | an E element whose "foo" attribute value begins exactly with the string "bar" |
| E[foo$="bar"]   | an E element whose "foo" attribute value ends exactly with the string "bar" |
| E[foo*="bar"]   | an E element whose "foo" attribute value contains the substring "bar" |
| E[foo\|="en"]    | an E element whose "foo" attribute has a hyphen-separated list of values beginning (from the left) with "en" |
| E:nth-child(n)  | an E element, the n-th child of its parent |
| E:first-child   | an E element, first child of its parent |
| E:last-child   | an E element, last child of its parent |
| E:nth-of-type(n)  | an E element, the n-th child of its type among its siblings |
| E:first-of-type   | an E element, first child of its type among its siblings |
| E:last-of-type   | an E element, last child of its type among its siblings |
| E.warning       | an E element whose class is "warning" |
| E#myid          | an `E` element with ID equal to "myid" |
| E:not(s)        | an E element that does not match simple selector s |
| E F             | an F element descendant of an E element |
| E > F           | an F element child of an E element |
| E + F           | an F element immediately preceded by an E element |
| E ~ F           | an F element preceded by an E element |

# `html`

```elixir
@type html() :: String.t()
```

HTML response

# `value`

```elixir
@type value() :: nil | String.t() | Regex.t()
```

Checking value
- if nil should not exist

# `assert_html`

```elixir
@spec assert_html(html(), Regex.t()) :: html() | no_return()
@spec assert_html(html(), block_fn()) :: html() | no_return()
@spec assert_html(html(), css_selector()) :: html() | no_return()
@spec assert_html(html(), attributes()) :: html() | no_return()
```

Asserts an attributes in HTML element

## Asserting Attributes
- `:text` – Asserts the exact text within an HTML element.
- `:match` - Asserts that the HTML element's text contains a specific value.

```elixir
iex> html = ~S{<div class="foo bar"></div><div class="zoo bar"></div>}
...> assert_html(html, ".zoo", class: "bar zoo")
~S{<div class="foo bar"></div><div class="zoo bar"></div>}

# Check if `id` attribute does not exist
iex> assert_html(~S{<div>text</div>}, id: nil)
"<div>text</div>"
```

#### Examples for :text

Asserts the exact text within an HTML element.

    iex> html = ~S{<h1 class="title">Header</h1>}
    ...> assert_html(html, text: "Header")
    ~S{<h1 class="title">Header</h1>}

    iex> html = ~S{<div class="container">   <h1 class="title">Header</h1>   </div>}
    ...> assert_html(html, ".title", text: "Header")
    ~S{<div class="container">   <h1 class="title">Header</h1>   </div>}

    iex> html = ~S{<h1 class="title">Header</h1>}
    ...> try do
    ...>   assert_html(html, text: "HEADER")
    ...> rescue
    ...>   e in ExUnit.AssertionError -> e
    ...> end
    %ExUnit.AssertionError{
      left: "HEADER",
      right: "Header",
      message: "Comparison `text` attribute failed.\n\n\t<h1 class=\"title\">Header</h1>.\n"
    }

    iex> html = ~S{<div class="foo">Some &amp; text</div>}
    ...> assert_html(html, text: "Some & text")
    ~S{<div class="foo">Some &amp; text</div>}

## Selector

`assert_html(html, "css selector")`

```elixir
    iex> html = ~S{<p><div class="foo"><h1>Header</h1></div></p>}
    ...> assert_html(html, "p .foo h1")
    ~S{<p><div class="foo"><h1>Header</h1></div></p>}

    iex> html = ~S{<p><div class="foo"><h1>Header</h1></div></p>}
    ...> assert_html(html, "h1")
    ~S{<p><div class="foo"><h1>Header</h1></div></p>}
```

## Match elements in HTML

Asserts that the HTML contains a specific element.

```elixir
    assert_html(html, ~r{<p>Hello</p>})
    assert_html(html, match: ~r{<p>Hello</p>})
    assert_html(html, match: "<p>Hello</p>")
```

### Asserts a text element in HTML

    iex> html = ~S{<p><div class="foo"><h1>Header</h1></div></p>}
    ...> assert_html(html, "p .foo h1", text: "Header")
    ~S{<p><div class="foo"><h1>Header</h1></div></p>}

### Examples

    iex> html = ~S{<div class="container">   <h1 class="title">Hello World</h1>   </div>}
    ...> assert_html(html, "h1", "Hello World") == html
    true

    iex> html = ~S{<div class="container">   <h1 class="title">Hello World</h1>   </div>}
    ...> assert_html(html, ".title", ~r{World})
    ~S{<div class="container">   <h1 class="title">Hello World</h1>   </div>}

## assert elements in selector
    assert_html(html, ".container table", ~r{<p>Hello</p>})

# `assert_html`

```elixir
@spec assert_html(html(), Regex.t(), block_fn()) :: html() | no_return()
@spec assert_html(html(), attributes(), block_fn()) :: html() | no_return()
@spec assert_html(html(), css_selector(), block_fn()) :: html() | no_return()
```

# `assert_html`

```elixir
@spec assert_html(html(), css_selector(), value(), block_fn() | nil) ::
  html() | no_return()
@spec assert_html(html(), css_selector(), attributes(), block_fn() | nil) ::
  html() | no_return()
```

# `refute_html`

```elixir
@spec refute_html(html(), Regex.t()) :: html() | no_return()
@spec refute_html(html(), css_selector()) :: html() | no_return()
@spec refute_html(html(), attributes()) :: html() | no_return()
```

Opposite method for assert_html

See more (t:refute_html/2)

# `refute_html`

```elixir
@spec refute_html(html(), Regex.t(), block_fn()) :: html() | no_return()
@spec refute_html(html(), attributes(), block_fn()) :: html() | no_return()
@spec refute_html(html(), css_selector(), block_fn()) :: html() | no_return()
```

# `refute_html`

```elixir
@spec refute_html(html(), css_selector(), value(), block_fn() | nil) ::
  html() | no_return()
@spec refute_html(html(), css_selector(), attributes(), block_fn() | nil) ::
  html() | no_return()
```

---

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