Skip to content

Commit

Permalink
Make print limits customizable
Browse files Browse the repository at this point in the history
  • Loading branch information
angelikatyborska committed Jun 2, 2024
1 parent e8e0701 commit 35d3561
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 70 deletions.
31 changes: 28 additions & 3 deletions lib/a11y_audit/assertions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,24 @@ defmodule A11yAudit.Assertions do
alias A11yAudit.Formatter
alias A11yAudit.Results

@spec assert_no_violations(Results.t()) :: nil | no_return
def assert_no_violations(results) do
@type opts ::
[
violations_print_limit: integer | :infinity,
nodes_per_violation_print_limit: integer | :infinity,
node_html_print_limit: integer | :infinity
]

@doc """
## Options
- `:violations_print_limit` - The maximum number of violations that will be printed in the error when the assertion fails. Can be an integer or `:infinity`. Defaults to `:infinity`.
- `:nodes_per_violation_print_limit` - The maximum number of nodes that will be printed for each violation when the assertion fails. Can be an integer or `:infinity`. Defaults to 5.
- `:node_html_print_limit` - The maximum length of the HTML snippet that will be printed for each node when the assertion fails. Can be an integer or `:infinity`. Defaults to 100.
"""
@spec assert_no_violations(Results.t(), opts()) :: nil | no_return
def assert_no_violations(results, opts \\ []) do
opts = Keyword.merge(default_opts(), opts)

violations_count = Enum.count(results.violations)

if violations_count > 0 do
Expand All @@ -14,10 +30,19 @@ defmodule A11yAudit.Assertions do
error_message =
"Expected page to have no accessibility violations, but got #{Enum.count(results.violations)} #{noun}.\n\n"

error_message = error_message <> Formatter.format_results(results)
error_message = error_message <> Formatter.format_results(results, opts)

raise ExUnit.AssertionError,
message: error_message
end
end

@doc false
def default_opts() do
[
violations_print_limit: :infinity,
nodes_per_violation_print_limit: 5,
node_html_print_limit: 100
]
end
end
57 changes: 29 additions & 28 deletions lib/a11y_audit/formatter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,31 @@ defmodule A11yAudit.Formatter do

import IO.ANSI, only: [magenta: 0, yellow: 0, red: 0, white: 0, reset: 0, reverse: 0]

alias A11yAudit.Assertions
alias A11yAudit.Results
alias A11yAudit.Results.Violation
alias A11yAudit.Formatter.PrintLimit

@doc false
@spec format_results(Results.t()) :: String.t()
def format_results(results) do
# TODO: make customizable
max_violations_per_result = 5
total_violations = Enum.count(results.violations)
@spec format_results(Results.t(), Assertions.opts()) :: String.t()
def format_results(results, opts) do
violations_print_limit = Keyword.fetch!(opts, :violations_print_limit)

sorted_violations = Enum.sort_by(results.violations, &violation_impact_sort/1)

{truncated_violations, omitted_violations_count} =
PrintLimit.truncate_list(sorted_violations, violations_print_limit)

formatted_results =
results.violations
|> Enum.sort_by(&violation_impact_sort/1)
|> Enum.take(max_violations_per_result)
|> Enum.map(&format_violation/1)
truncated_violations
|> Enum.map(&format_violation(&1, opts))
|> Enum.join("")

if max_violations_per_result >= total_violations do
if omitted_violations_count == 0 do
formatted_results
else
truncated_violations = total_violations - max_violations_per_result
noun = if truncated_violations === 1, do: "violation", else: "violations"
formatted_results <> "... and #{truncated_violations} more #{noun}.\n"
noun = if omitted_violations_count === 1, do: "violation", else: "violations"
formatted_results <> "... and #{omitted_violations_count} more #{noun}.\n"
end
end

Expand All @@ -40,8 +42,8 @@ defmodule A11yAudit.Formatter do
end

@doc false
@spec format_violation(Violation.t()) :: String.t()
def format_violation(violation) do
@spec format_violation(Violation.t(), Assertions.opts()) :: String.t()
def format_violation(violation, opts) do
%Violation{
id: _id,
description: _description,
Expand All @@ -57,7 +59,7 @@ defmodule A11yAudit.Formatter do
left_padding = "#{with_color("┃", color)} "

nodes_with_padding =
format_nodes(nodes) |> Enum.map(fn line -> left_pad_line(line, left_padding) end)
format_nodes(nodes, opts) |> Enum.map(fn line -> left_pad_line(line, left_padding) end)

node_count = Enum.count(nodes)

Expand Down Expand Up @@ -86,22 +88,22 @@ defmodule A11yAudit.Formatter do
end

@doc false
def format_nodes(nodes) do
# TODO: make customizable
max_nodes_per_result = 5
# TODO: make customizable
max_node_html_length = 100
def format_nodes(nodes, opts) do
nodes_per_violation_print_limit = Keyword.fetch!(opts, :nodes_per_violation_print_limit)
node_html_print_limit = Keyword.fetch!(opts, :node_html_print_limit)

total_nodes = Enum.count(nodes)

{truncated_nodes, omitted_nodes_count} =
PrintLimit.truncate_list(nodes, nodes_per_violation_print_limit)

formatted_nodes =
nodes
|> Enum.take(max_nodes_per_result)
truncated_nodes
|> Enum.with_index()
|> Enum.flat_map(fn {node, index} ->
index_string = "#{index + 1}. "

html = trim_string(String.replace(node.html, "\n", "\\n"), max_node_html_length)
html = trim_string(String.replace(node.html, "\n", "\\n"), node_html_print_limit)

[
"#{index_string}#{html}\n"
Expand All @@ -126,12 +128,11 @@ defmodule A11yAudit.Formatter do
end)

formatted_nodes =
if max_nodes_per_result >= total_nodes do
if omitted_nodes_count == 0 do
formatted_nodes
else
truncated_nodes = total_nodes - max_nodes_per_result
noun = if truncated_nodes == 1, do: "node", else: "nodes"
formatted_nodes ++ ["... and #{truncated_nodes} more #{noun}.\n"]
noun = if omitted_nodes_count == 1, do: "node", else: "nodes"
formatted_nodes ++ ["... and #{omitted_nodes_count} more #{noun}.\n"]
end

summary_string =
Expand Down
13 changes: 13 additions & 0 deletions lib/a11y_audit/formatter/print_limit.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
defmodule A11yAudit.Formatter.PrintLimit do
@moduledoc false

def truncate_list(list, limit) do
if limit == :infinity do
{list, 0}
else
truncated_list = Enum.take(list, limit)
omitted_element_count = Enum.count(list) - Enum.count(truncated_list)
{truncated_list, omitted_element_count}
end
end
end
6 changes: 3 additions & 3 deletions lib/a11y_audit/hound.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ defmodule A11yAudit.Hound do
alias A11yAudit.Results
alias A11yAudit.Assertions

@spec assert_no_violations() :: nil | no_return
def assert_no_violations() do
@spec assert_no_violations(Assertions.opts()) :: nil | no_return
def assert_no_violations(opts \\ []) do
if Code.ensure_loaded?(Hound) do
Hound.Helpers.ScriptExecution.execute_script(JS.axe_core())
axe_result = Hound.Helpers.ScriptExecution.execute_script(JS.await_audit_results())
audit_results = Results.from_json(axe_result)
Assertions.assert_no_violations(audit_results)
Assertions.assert_no_violations(audit_results, opts)
else
Logger.error("""
Could not find dependency :hound.
Expand Down
7 changes: 4 additions & 3 deletions lib/a11y_audit/wallaby.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ defmodule A11yAudit.Wallaby do
alias A11yAudit.Results
alias A11yAudit.Assertions

@spec assert_no_violations(Wallaby.Browser.parent()) :: Wallaby.Browser.parent() | no_return()
def assert_no_violations(session) do
@spec assert_no_violations(Wallaby.Browser.parent(), Assertions.opts()) ::
Wallaby.Browser.parent() | no_return()
def assert_no_violations(session, opts \\ []) do
if Code.ensure_loaded?(Wallaby) do
session
|> Wallaby.Browser.execute_script(JS.axe_core())
|> Wallaby.Browser.execute_script(JS.await_audit_results(), [], fn axe_result ->
audit_results = Results.from_json(axe_result)
Assertions.assert_no_violations(audit_results)
Assertions.assert_no_violations(audit_results, opts)
end)
else
Logger.error("""
Expand Down
47 changes: 47 additions & 0 deletions test/a11y_audit/assertions_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule A11yAudit.AssertionsTest do

alias A11yAudit.Results
alias A11yAudit.Results.Violation
alias A11yAudit.Results.Node
alias A11yAudit.Assertions

describe "assert_no_violations" do
Expand Down Expand Up @@ -86,5 +87,51 @@ defmodule A11yAudit.AssertionsTest do
"""
end
end

test "accepts opts" do
try do
Assertions.assert_no_violations(
%Results{
url: "url",
test_engine: "3",
violations: [
%Violation{
id: "id 1",
description: "description 1",
help: "help 1",
help_url: "help_url 1",
impact: :moderate,
nodes: []
},
%Violation{
id: "id 2",
description: "description 2",
help: "help 2",
help_url: "help_url 2",
impact: :serious,
nodes: [%Node{html: "<h1>Hello</h1>"}]
}
]
},
violations_print_limit: 1,
node_html_print_limit: 3
)

raise "this line should not be reached"
rescue
error in [ExUnit.AssertionError] ->
assert error.message ==
"""
Expected page to have no accessibility violations, but got 2 violations.
\e[31m\e[7m serious \e[0m help 2
\e[31m┃\e[0m Learn more: help_url 2
\e[31m┃\e[0m\n\e[31m┃\e[0m There is 1 node with this violation:
\e[31m┃\e[0m\n\e[31m┃\e[0m 1. <h1...
... and 1 more violation.
"""
end
end
end
end
Loading

0 comments on commit 35d3561

Please sign in to comment.