Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save vrutberg/124a26d5e1e3f2fcdbbf50574b653bad to your computer and use it in GitHub Desktop.
Save vrutberg/124a26d5e1e3f2fcdbbf50574b653bad to your computer and use it in GitHub Desktop.
Navigator unit test
@MainActor
struct ContinueOnboardingFlowViewModelTests {
let services: Services
let testCoreDataStack: TestCoreDataStack
var managedObjectContext: NSManagedObjectContext {
testCoreDataStack.persistentContainer.viewContext
}
init() {
self.services = .mock
self.testCoreDataStack = TestCoreDataStack()
}
@Test
func happyPath() async throws {
let navigator = InspectableNavigator().typed(
to: ContinueOnboardingFlowViewModel.NavigationDestination.self
)
let viewModel = ContinueOnboardingFlowViewModel(
email: "[email protected]",
services: services,
premiumManager: PremiumManagerMock(),
trackEvent: { _ in },
managedObjectContext: managedObjectContext,
navigator: navigator
)
viewModel.continueOnboardingViewModel.didPressContinue()
let setPasswordViewModel = try #require(navigator.lastItem(\.setPassword))
#expect(setPasswordViewModel.input == .enterPassword)
setPasswordViewModel.onComplete(.passwordEntered(password: "hejhopp123"))
let setPasswordViewModel2 = try #require(navigator.lastItem(\.setPassword))
#expect(setPasswordViewModel2.input == .confirmPassword(password: "hejhopp123"))
setPasswordViewModel2.onComplete(.passwordSet)
let setupNotificationsViewModel = try #require(navigator.lastItem(\.setupNotifications))
setupNotificationsViewModel.didFinish(.notificationsEnabled)
#expect(navigator.lastItem.is(\.success))
}
}
/// A navigator that allows inspection of the navigation path for testing purposes.
class InspectableNavigator: Navigator {
var inspectableNavigationPath = [any Hashable]()
override func push<T: Hashable>(_ value: T) {
super.push(value)
inspectableNavigationPath.append(value)
}
override func pop() {
super.pop()
inspectableNavigationPath.removeLast()
}
override func popToRoot() {
super.popToRoot()
inspectableNavigationPath.removeAll()
}
func typed<Destination: CasePathable>(
to: Destination.Type
) -> TypedInspectableNavigator<Destination> {
TypedInspectableNavigator<Destination>(navigator: self)
}
}
/// A typed navigator that allows inspection of the navigation path for testing purposes.
/// It won't interfere with pushing or popping items, but it will allow you to retrieve the last item of a specific type.
class TypedInspectableNavigator<Destination: CasePathable & Hashable>: Navigator {
private let navigator: InspectableNavigator
init(navigator: InspectableNavigator) {
self.navigator = navigator
}
override func push<T: Hashable>(_ value: T) {
navigator.push(value)
}
override func pop() {
navigator.pop()
}
override func popToRoot() {
navigator.popToRoot()
}
var lastItem: Destination? {
navigator.inspectableNavigationPath.last as? Destination
}
func lastItem<Value>(
_ casePath: CaseKeyPath<Destination, Value>
) -> Value? {
lastItem?[case: casePath]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment