mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Add contain matcher improvements to have matcher
This commit is contained in:
parent
edf8ae36df
commit
20caed9262
1 changed files with 87 additions and 40 deletions
|
@ -4,7 +4,14 @@ module Spectator::Matchers
|
|||
# Matcher that tests whether a value, such as a `String` or `Array`, matches one or more values.
|
||||
# For a `String`, the `includes?` method is used.
|
||||
# Otherwise, it expects an `Enumerable` and iterates over each item until === is true.
|
||||
struct HaveMatcher(ExpectedType) < ValueMatcher(ExpectedType)
|
||||
struct HaveMatcher(ExpectedType) < Matcher
|
||||
# Expected value and label.
|
||||
private getter expected
|
||||
|
||||
# Creates the matcher with an expected value.
|
||||
def initialize(@expected : TestValue(ExpectedType))
|
||||
end
|
||||
|
||||
# Short text about the matcher's purpose.
|
||||
# This explains what condition satisfies the matcher.
|
||||
# The description is used when the one-liner syntax is used.
|
||||
|
@ -12,62 +19,102 @@ module Spectator::Matchers
|
|||
"includes #{expected.label}"
|
||||
end
|
||||
|
||||
# Checks whether the matcher is satisifed with the expression given to it.
|
||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
||||
# Entrypoint for the matcher, forwards to the correct method for string or enumerable.
|
||||
def match(actual : TestExpression(T)) : MatchData forall T
|
||||
if (value = actual.value).is_a?(String)
|
||||
match_string?(value)
|
||||
match_string(value, actual.label)
|
||||
else
|
||||
match_enumerable?(value)
|
||||
match_enumerable(value, actual.label)
|
||||
end
|
||||
end
|
||||
|
||||
# Actually performs the test against the expression.
|
||||
private def match_enumerable(actual_value, actual_label)
|
||||
array = actual_value.to_a
|
||||
missing = expected.value.reject do |item|
|
||||
array.any? do |element|
|
||||
item === element
|
||||
end
|
||||
end
|
||||
|
||||
if missing.empty?
|
||||
# Contents are present.
|
||||
SuccessfulMatchData.new(description)
|
||||
else
|
||||
# Content is missing.
|
||||
FailedMatchData.new(description, "#{actual_label} does not include #{expected.label}",
|
||||
expected: expected.value.inspect,
|
||||
actual: actual_value.inspect,
|
||||
missing: missing.inspect,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Checks if a `String` matches the expected values.
|
||||
# The `includes?` method is used for this check.
|
||||
private def match_string?(value)
|
||||
expected.value.all? do |item|
|
||||
value.includes?(item)
|
||||
private def match_string(actual_value, actual_label)
|
||||
missing = expected.value.reject do |item|
|
||||
actual_value.includes?(item)
|
||||
end
|
||||
|
||||
if missing.empty?
|
||||
SuccessfulMatchData.new(description)
|
||||
else
|
||||
FailedMatchData.new(description, "#{actual_label} does not include #{expected.label}",
|
||||
expected: expected.value.inspect,
|
||||
actual: actual_value.inspect,
|
||||
missing: missing.inspect,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Checks if an `Enumerable` matches the expected values.
|
||||
# The `===` operator is used on every item.
|
||||
private def match_enumerable?(value)
|
||||
array = value.to_a
|
||||
expected.value.all? do |item|
|
||||
# Performs the test against the expression, but inverted.
|
||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
||||
if (value = actual.value).is_a?(String)
|
||||
negated_match_string(value, actual.label)
|
||||
else
|
||||
negated_match_enumerable(value, actual.label)
|
||||
end
|
||||
end
|
||||
|
||||
# Actually performs the negated test against the expression.
|
||||
private def negated_match_enumerable(actual_value, actual_label)
|
||||
array = actual_value.to_a
|
||||
satisfied = expected.value.any? do |item|
|
||||
array.any? do |element|
|
||||
item === element
|
||||
end
|
||||
end
|
||||
|
||||
if satisfied
|
||||
# Contents are present.
|
||||
FailedMatchData.new(description, "#{actual_label} includes #{expected.label}",
|
||||
expected: "Not #{expected.value.inspect}",
|
||||
actual: actual_value.inspect
|
||||
)
|
||||
else
|
||||
# Content is missing.
|
||||
SuccessfulMatchData.new(description)
|
||||
end
|
||||
end
|
||||
|
||||
# Message displayed when the matcher isn't satisifed.
|
||||
#
|
||||
# This is only called when `#match?` returns false.
|
||||
#
|
||||
# The message should typically only contain the test expression labels.
|
||||
# Actual values should be returned by `#values`.
|
||||
private def failure_message(actual) : String
|
||||
"#{actual.label} does not include #{expected.label}"
|
||||
end
|
||||
# Checks if a `String` doesn't match the expected values.
|
||||
# The `includes?` method is used for this check.
|
||||
private def negated_match_string(actual_value, actual_label)
|
||||
satisfied = expected.value.any? do |item|
|
||||
actual_value.includes?(item)
|
||||
end
|
||||
|
||||
# Message displayed when the matcher isn't satisifed and is negated.
|
||||
# This is essentially what would satisfy the matcher if it wasn't negated.
|
||||
#
|
||||
# This is only called when `#does_not_match?` returns false.
|
||||
#
|
||||
# The message should typically only contain the test expression labels.
|
||||
# Actual values should be returned by `#values`.
|
||||
private def failure_message_when_negated(actual) : String
|
||||
"#{actual.label} includes #{expected.label}"
|
||||
end
|
||||
|
||||
# Additional information about the match failure.
|
||||
# The return value is a NamedTuple with Strings for each value.
|
||||
private def values(actual)
|
||||
{
|
||||
subset: expected.value.inspect,
|
||||
superset: actual.value.inspect,
|
||||
}
|
||||
if satisfied
|
||||
SuccessfulMatchData.new(description)
|
||||
else
|
||||
FailedMatchData.new(description, "#{actual_label} does not include #{expected.label}",
|
||||
expected: expected.value.inspect,
|
||||
actual: actual_value.inspect,
|
||||
missing: missing.inspect,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue