MobiusNimble/Source/NimbleFirstMatchers.swift (94 lines of code) (raw):

// Copyright Spotify AB. // SPDX-License-Identifier: Apache-2.0 import Foundation import MobiusCore import MobiusTest import Nimble /// Function produces an `AssertFirst` function to be used with the `InitSpec` /// /// - Parameter predicates: Nimble `Predicate` that verifies a first. Can be produced through `FirstMatchers` /// - Returns: An `AssertFirst` function to be used with the `InitSpec` public func assertThatFirst<Model, Effect>( _ predicates: Nimble.Predicate<First<Model, Effect>>... ) -> AssertFirst<Model, Effect> { return { (result: First<Model, Effect>) in predicates.forEach({ predicate in expect(result).to(predicate) }) } } let nextBeingNilNotAllowed = "have a non-nil First. Got <nil>" let unexpectedNilParameterPredicateResult = PredicateResult(bool: false, message: .expectedTo(nextBeingNilNotAllowed)) /// Returns a `Predicate` that matches `First` instances with a model that is equal to the supplied one. /// /// - Parameter expected: the expected model /// - Returns: a `Predicate` determining if a `First` contains the expected model public func haveModel<Model: Equatable, Effect>(_ expected: Model) -> Nimble.Predicate<First<Model, Effect>> { return Nimble.Predicate<First<Model, Effect>>.define(matcher: { actualExpression -> Nimble.PredicateResult in guard let first = try actualExpression.evaluate() else { return unexpectedNilParameterPredicateResult } let expectedDescription = String(describing: expected) let actualDescription = String(describing: first.model) return PredicateResult( bool: first.model == expected, message: .expectedCustomValueTo("be <\(expectedDescription)>", actual: "<\(actualDescription)>") ) }) } /// Returns a `Predicate` that matches `First` instances with no effects. /// /// - Returns: a `Predicate` determening if a `First` contains no effects public func haveNoEffects<Model, Effect>() -> Nimble.Predicate<First<Model, Effect>> { return Nimble.Predicate<First<Model, Effect>>.define(matcher: { actualExpression -> Nimble.PredicateResult in guard let first = try actualExpression.evaluate() else { return unexpectedNilParameterPredicateResult } let actualDescription = String(describing: first.effects) return PredicateResult( bool: first.effects.isEmpty, message: .expectedCustomValueTo("have no effect", actual: "<\(actualDescription)>") ) }) } /// Returns a `Predicate` that matches if all the supplied effects are present in the supplied `First` in any order. /// The `First` may have more effects than the ones included. /// /// - Parameter effects: the effects to match (possibly empty) /// - Returns: a `Predicate` that matches `First` instances that include all the supplied effects public func haveEffects<Model, Effect: Equatable>(_ effects: [Effect]) -> Nimble.Predicate<First<Model, Effect>> { return Nimble.Predicate<First<Model, Effect>>.define(matcher: { actualExpression -> Nimble.PredicateResult in guard let first = try actualExpression.evaluate() else { return unexpectedNilParameterPredicateResult } let expectedDescription = String(describing: effects) let actualDescription = String(describing: first.effects) return PredicateResult( bool: effects.allSatisfy(first.effects.contains), message: .expectedCustomValueTo( "contain <\(expectedDescription)>", actual: "<\(actualDescription)> (order doesn't matter)" ) ) }) } /// Returns a `Predicate` that matches if only the supplied effects are present in the supplied `First`, in any order. /// /// - Parameter effects: the effects to match (possibly empty) /// - Returns: a `Predicate` that matches `First` instances that include all the supplied effects public func haveOnlyEffects<Model, Effect: Equatable>(_ effects: [Effect]) -> Nimble.Predicate<First<Model, Effect>> { return Nimble.Predicate<First<Model, Effect>>.define(matcher: { actualExpression -> Nimble.PredicateResult in guard let first = try actualExpression.evaluate() else { return unexpectedNilParameterPredicateResult } var unmatchedActual = first.effects var unmatchedExpected = effects zip(first.effects, effects).forEach { _ = unmatchedActual.firstIndex(of: $1).map { unmatchedActual.remove(at: $0) } _ = unmatchedExpected.firstIndex(of: $0).map { unmatchedExpected.remove(at: $0) } } let expectedDescription = String(describing: effects) let actualDescription = String(describing: first.effects) return PredicateResult( bool: unmatchedActual.isEmpty && unmatchedExpected.isEmpty, message: .expectedCustomValueTo( "contain only <\(expectedDescription)>", actual: "<\(actualDescription)> (order doesn't matter)" ) ) }) } /// Returns a `Predicate` that matches if the supplied effects are equal to the supplied `First`. /// /// - Parameter effects: the effects to match (possibly empty) /// - Returns: a `Predicate` that matches `First` instances that include all the supplied effects public func haveExactlyEffects<Model, Effect: Equatable>(_ effects: [Effect]) -> Nimble.Predicate<First<Model, Effect>> { return Nimble.Predicate<First<Model, Effect>>.define(matcher: { actualExpression -> Nimble.PredicateResult in guard let first = try actualExpression.evaluate() else { return unexpectedNilParameterPredicateResult } let expectedDescription = String(describing: effects) let actualDescription = String(describing: first.effects) return PredicateResult( bool: effects == first.effects, message: .expectedCustomValueTo( "equal <\(expectedDescription)>", actual: "<\(actualDescription)>" ) ) }) }