Packages
wallaby
0.30.2
0.31.0
0.30.12
0.30.11
0.30.10
0.30.9
0.30.8
0.30.7
0.30.6
0.30.5
0.30.4
0.30.3
0.30.2
0.30.1
0.30.0
0.29.1
0.29.0
0.28.1
0.28.0
0.27.0
0.26.2
0.26.1
0.26.0
0.25.1
0.25.0
0.24.1
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.2
0.19.1
0.19.0
0.18.1
0.18.0
0.17.0
0.16.1
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.1
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.1
0.3.0
0.2.0
0.1.0
0.0.1
Concurrent feature tests for elixir
Current section
Files
Jump to
Current section
Files
lib/wallaby/query/error_message.ex
defmodule Wallaby.Query.ErrorMessage do
@moduledoc false
alias Wallaby.Query
@doc """
Compose an error message based on the error method and query information
"""
@spec message(Query.t(), any()) :: String.t()
def message(%Query{} = query, :not_found) do
"Expected to find #{found_error_message(query)}"
end
def message(%Query{} = query, :found) do
"Expected not to find #{found_error_message(query)}"
end
def message(%{method: method, selector: selector}, :label_with_no_for) do
"""
The text '#{selector}' matched a label but the label has no 'for'
attribute and can't be used to find the correct #{method(method)}.
You can fix this by including the `for="YOUR_INPUT_ID"` attribute on the
appropriate label.
"""
end
def message(%{method: method, selector: selector}, {:label_does_not_find_field, for_text, 0}) do
"""
The text '#{selector}' matched a label but the label's 'for' attribute
doesn't match the id of any #{method(method)}.
Make sure that id on your #{method(method)} is `id="#{for_text}"`.
"""
end
def message(%{selector: selector}, {:label_does_not_find_field, for_text, 1}) do
"""
The text '#{selector}' matched a label but the label's 'for' attribute
matches one element `id="#{for_text}"` of a different type than requested.
Make sure you are using the right query function (e.g. `checkbox` vs `radio_button`).
"""
end
def message(%{selector: selector}, {:label_does_not_find_field, for_text, count}) do
"""
The text '#{selector}' matched a label but the label's 'for' attribute
matches #{count} elements with `id="#{for_text}"`.
Make sure that ids are unique as mandated by HTML spec.
"""
end
def message(%{selector: selector}, :button_with_bad_type) do
"""
The text '#{selector}' matched a button but the button has an invalid 'type' attribute.
You can fix this by including `type="[submit|reset|button|image]"` on the appropriate button.
"""
end
def message(_, :cannot_set_text_with_invisible_elements) do
"""
Cannot set the `text` filter when `visible` is set to `false`.
Text is based on visible text on the page. This is a limitation of webdriver.
Since the element isn't visible the text isn't visible. Because of that I
can't apply both filters correctly.
"""
end
def message(_, {:invalid_at_number, at_number}) do
# The query is invalid. the 'at' number requested is negative or not a number
"""
The given at index #{inspect(at_number)} is not a non-negative number or :all.
"""
end
def message(_, :min_max) do
"""
The query is invalid. Cannot set the minimum greater than the maximum.
"""
end
def message(%{method: method, selector: selector}, :invalid_selector) do
"""
The #{method} '#{selector}' is not a valid query.
"""
end
def message(_, :unexpected_alert) do
"""
There was an unexpected alert.
"""
end
def help(elements) do
"""
If you expect to find the selector #{times(length(elements))} then you
should include the `count: #{length(elements)}` option in your finder.
"""
end
defp found_error_message(query) do
"""
#{expected_count(query)} #{visibility_and_selection(query)} #{method(query)} #{selector(query)}#{with_index(Query.at_number(query))}, but #{result_adverb(query)}#{result_count(query.result)} #{visibility_and_selection(query)} #{short_method(query.method, Enum.count(query.result))} #{result_expectation(query.result)}.
"""
end
@doc """
Extracts the selector from the query
"""
@spec selector(Query.t()) :: String.t()
def selector(%Query{selector: {name, value}}) do
"'#{name}' with value '#{value}'"
end
def selector(%Query{selector: selector, conditions: conditions}) do
text = with_text(conditions[:text])
"'#{selector}'#{text}"
end
defp with_text(nil), do: ""
defp with_text(text), do: " and contained the text '#{text}'"
defp with_index(:all), do: nil
defp with_index(at), do: " and return element at index #{at}"
@doc """
Extracts the selector method from the selector and converts it into a human
readable format
"""
@spec method(Query.t()) :: String.t()
@spec method({atom(), boolean()}) :: String.t()
def method(%Query{} = query) do
method(query.method, Query.count(query) != 1)
end
def method(_), do: "element"
def method(:css, true), do: "elements that matched the css"
def method(:css, false), do: "element that matched the css"
def method(:select, true), do: "selects"
def method(:select, false), do: "select"
def method(:option, true), do: "option fields"
def method(:option, false), do: "option"
def method(:fillable_field, true), do: "text inputs or textareas"
def method(:fillable_field, false), do: "text input or textarea"
def method(:checkbox, true), do: "checkboxes"
def method(:checkbox, false), do: "checkbox"
def method(:radio_button, true), do: "radio buttons"
def method(:radio_button, false), do: "radio button"
def method(:link, true), do: "links"
def method(:link, false), do: "link"
def method(:xpath, true), do: "elements that matched the xpath"
def method(:xpath, false), do: "element that matched the xpath"
def method(:button, true), do: "buttons"
def method(:button, false), do: "button"
def method(:file_field, true), do: "file fields"
def method(:file_field, false), do: "file field"
def method(:text, true), do: "elements with the text"
def method(:text, false), do: "element with the text"
def method(:attribute, true), do: "elements with the attribute"
def method(:attribute, false), do: "element with the attribute"
def short_method(:css, count) when count > 1, do: "elements"
def short_method(:css, count) when count == 0, do: "elements"
def short_method(:css, _), do: "element"
def short_method(:xpath, count) when count == 1, do: "element"
def short_method(:xpath, _), do: "elements"
def short_method(method, count), do: method(method, count != 1)
@doc """
Generates failure conditions based on query conditions.
"""
@spec conditions(Keyword.t()) :: list(String.t())
def conditions(opts) do
opts
|> Keyword.delete(:visible)
|> Keyword.delete(:count)
|> Enum.map(&condition/1)
|> Enum.reject(&(&1 == nil))
end
@doc """
Converts a condition into a human readable failure message.
"""
@spec condition({atom(), String.t()}) :: String.t() | nil
def condition({:text, text}) when is_binary(text) do
"text: '#{text}'"
end
def condition(_), do: nil
@spec visibility_and_selection(Query.t()) :: String.t()
defp visibility_and_selection(query) do
case Query.selected?(query) do
true -> "#{visibility(query)}, selected"
false -> "#{visibility(query)}, unselected"
:any -> visibility(query)
end
end
@doc """
Converts the visibility attribute into a human readable form.
"""
@spec visibility(Query.t()) :: String.t()
def visibility(query) do
case Query.visible?(query) do
true -> "visible"
false -> "invisible"
:any -> "visible or invisible"
end
end
defp result_adverb(query) do
conditions = query.conditions
min =
conditions[:count] || conditions[:minimum] ||
(conditions[:at] != :all && conditions[:at] + 1)
if min && min > Enum.count(query.result) && query.result != [], do: "only "
end
defp result_count(result), do: "#{Enum.count(result)}"
defp times(1), do: "1 time"
defp times(count), do: "#{count} times"
defp expected_count(query) do
conditions = query.conditions
count = Query.count(query)
cond do
count ->
"#{count}"
conditions[:minimum] && Enum.count(query.result) < conditions[:minimum] ->
"at least #{conditions[:minimum]}"
conditions[:maximum] && Enum.count(query.result) > conditions[:maximum] ->
"no more than #{conditions[:maximum]}"
true ->
"some"
end
end
def result_expectation(result) when length(result) == 1, do: "was found"
def result_expectation(_), do: "were found"
end