# Bylaw.HTML

Validate rendered HTML strings before assertions finish, so invalid HTML
patterns are easier to catch and harder to ship.

Use `bylaw_html` to enforce application-specific HTML invariants, keep rendered
markup accessible and semantically correct, and codify conventions around links,
buttons, images, and other browser-facing behavior. Callers choose checks
explicitly and pass them to `Bylaw.HTML.validate_html/2`.

## Installation

Add `:bylaw_html` to test dependencies:

```elixir
def deps do
  [
    {:bylaw_html, "~> 0.1.0-alpha.2", only: :test}
  ]
end
```

## Usage

Choose the checks you want to enforce and pass them explicitly to
`Bylaw.HTML.validate_html/2` from your tests:

```elixir
defmodule MyAppWeb.PageHTMLTest do
  use MyAppWeb.ConnCase, async: true

  @html_checks [
    Bylaw.HTML.Check.RequireLinkHref,
    Bylaw.HTML.Check.PreferButtonForAction,
    Bylaw.HTML.Check.PreferLinkForNavigation,
    Bylaw.HTML.Check.RequireImageAlt,
    Bylaw.HTML.Check.RequireButtonType,
    Bylaw.HTML.Check.RequireInputAutocomplete,
    Bylaw.HTML.Check.NoInlineStyle
  ]

  test "home page satisfies Bylaw HTML checks", %{conn: conn} do
    conn = get(conn, ~p"/")
    html = html_response(conn, 200)

    assert :ok = Bylaw.HTML.validate_html(html, @html_checks)
  end
end
```

For LiveView and component tests, pass the rendered string from the test helper:

```elixir
html = render(view)
assert :ok = Bylaw.HTML.validate_html(html, @html_checks)
```

```elixir
html = render_component(&MyAppWeb.Button.button/1, label: "Save")
assert :ok = Bylaw.HTML.validate_html(html, @html_checks)
```

If you want assertion helpers, write a small downstream wrapper around
`Bylaw.HTML.validate_html/2` in your own test support, such as `ConnCase` or
LiveView test helpers.

For example, in `test/support/html_assertions.ex`:

```elixir
defmodule MyAppWeb.HTMLAssertions do
  import ExUnit.Assertions

  @html_checks [
    Bylaw.HTML.Check.RequireLinkHref,
    Bylaw.HTML.Check.PreferButtonForAction,
    Bylaw.HTML.Check.PreferLinkForNavigation,
    Bylaw.HTML.Check.RequireImageAlt,
    Bylaw.HTML.Check.RequireButtonType,
    Bylaw.HTML.Check.RequireInputAutocomplete,
    Bylaw.HTML.Check.NoInlineStyle
  ]

  def assert_valid_html(html) when is_binary(html) do
    case Bylaw.HTML.validate_html(html, @html_checks) do
      :ok ->
        html

      {:error, issues} ->
        flunk(format_html_issues(issues))
    end
  end

  defp format_html_issues(issues) do
    issues
    |> Enum.map_join("\n\n", fn issue ->
      [issue.message, issue.snippet]
      |> Enum.reject(&is_nil/1)
      |> Enum.join("\n")
    end)
  end
end
```

Then import it from your case template:

```elixir
defmodule MyAppWeb.ConnCase do
  use ExUnit.CaseTemplate

  using do
    quote do
      import MyAppWeb.HTMLAssertions
    end
  end
end
```

And use it in tests:

```elixir
html =
  conn
  |> get(~p"/")
  |> html_response(200)

assert_valid_html(html)
```

`bylaw_html` does not read application config or register checks globally.
