Skip to content

Commit

Permalink
Merge pull request #31 from Ryu0118/feature/pullback-test
Browse files Browse the repository at this point in the history
Add Pullback Tests
  • Loading branch information
Ryu0118 authored Sep 19, 2023
2 parents 8bf3d06 + 857cee6 commit 997de56
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 18 deletions.
41 changes: 25 additions & 16 deletions Sources/SimplexArchitecture/Store/Store+send.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,36 +30,45 @@ extension Store {
_ action: Reducer.Action,
container: StateContainer<Reducer.Target>
) -> SendTask {
defer {
if let _ = Reducer.Action.self as? Pullbackable.Type, let pullbackAction {
pullbackAction(action)
}
}

return sendAction(.action(action), container: container)
sendAction(.action(action), container: container)
}

@usableFromInline
func sendAction(
_ action: Reducer.ReducerAction,
container: StateContainer<Reducer.Target>
) -> SendTask {
defer {
if let _ = Reducer.ReducerAction.self as? Pullbackable.Type,
let pullbackReducerAction
{
pullbackReducerAction(action)
}
}

return sendAction(.action(action), container: container)
sendAction(.action(action), container: container)
}

@inline(__always)
func sendAction(
_ action: CombineAction<Reducer>,
container: StateContainer<Reducer.Target>
) -> SendTask {
defer {
switch action.kind {
case .viewAction(let action):
guard let pullbackAction else {
break
}
if let _ = Reducer.Action.self as? Pullbackable.Type {
pullbackAction(action)
} else {
runtimeWarning("\(Reducer.Action.self) must be conformed to Pullbackable in order to pullback to parent reducer")
}
case .reducerAction(let action):
guard let pullbackReducerAction else {
break
}
if let _ = Reducer.ReducerAction.self as? Pullbackable.Type {
pullbackReducerAction(action)
} else {
runtimeWarning("\(Reducer.ReducerAction.self) must be conformed to Pullbackable in order to pullback to parent reducer")
}
}
}

let sideEffect: SideEffect<Reducer>
// If Unit Testing is in progress and an action is sent from SideEffect
if _XCTIsTesting, let effectContext = EffectContext.id {
Expand Down
90 changes: 88 additions & 2 deletions Tests/SimplexArchitectureTests/StoreTests.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@testable import SimplexArchitecture
import SwiftUI
import XCTest
import CasePaths

final class StoreTests: XCTestCase {
fileprivate var store: Store<TestReducer>!
Expand Down Expand Up @@ -95,15 +96,100 @@ final class StoreTests: XCTestCase {

XCTAssertNotNil(sendTask2.task)
}

func testPullbackAction() throws {
let parent = ParentView()
parent.store.setContainerIfNeeded(for: parent, states: .init())
store.setContainerIfNeeded(for: TestView())
XCTAssertNil(store.pullbackAction)
store.pullback(to: /ParentReducer.Action.child, parent: parent)
store.sendIfNeeded(.c1)
XCTAssertNotNil(store.pullbackAction)
XCTAssertEqual(parent.store.container?.count, 1)
}

func testPullbackReducerAction() throws {
let parent = ParentView()
parent.store.setContainerIfNeeded(for: parent, states: .init())
store.setContainerIfNeeded(for: TestView())
XCTAssertNil(store.pullbackReducerAction)
store.pullback(to: /ParentReducer.Action.childReducerAction, parent: parent)
store.sendIfNeeded(.c4)
XCTAssertNotNil(store.pullbackReducerAction)
XCTAssertEqual(parent.store.container?.count, 1)
}

func testPullbackActionForId() throws {
let parent = ParentView()
parent.store.setContainerIfNeeded(for: parent, states: .init())
store.setContainerIfNeeded(for: TestView())
XCTAssertNil(store.pullbackAction)
let uuid = UUID()
store.pullback(to: /ParentReducer.Action.childId, parent: parent, id: uuid)
store.sendIfNeeded(.c1)
XCTAssertNotNil(store.pullbackAction)
XCTAssertEqual(parent.store.container?.id, uuid)
}

func testPullbackReducerActionForId() throws {
let parent = ParentView()
parent.store.setContainerIfNeeded(for: parent, states: .init())
store.setContainerIfNeeded(for: TestView())
XCTAssertNil(store.pullbackReducerAction)
let uuid = UUID()
store.pullback(to: /ParentReducer.Action.childIdReducerAction, parent: parent, id: uuid)
store.sendIfNeeded(.c4)
XCTAssertNotNil(store.pullbackReducerAction)
XCTAssertEqual(parent.store.container?.id, uuid)
}
}

@ScopeState
private struct ParentView: View {
@State var count = 0
@State var id: UUID?
let store: Store<ParentReducer> = .init(reducer: ParentReducer())
var body: some View {
EmptyView()
}
}

private struct ParentReducer: ReducerProtocol {
enum Action {
case child(TestReducer.Action)
case childReducerAction(TestReducer.ReducerAction)
case childId(id: UUID, action: TestReducer.Action)
case childIdReducerAction(id: UUID, action: TestReducer.ReducerAction)
}

func reduce(into state: StateContainer<ParentView>, action: Action) -> SideEffect<ParentReducer> {
switch action {
case .child:
state.count += 1
return .none

case .childReducerAction:
state.count += 1
return .none

case .childId(let id, _):
state.id = id
return .none

case .childIdReducerAction(let id, _):
state.id = id
return .none
}
}
}

private struct TestReducer: ReducerProtocol {
enum ReducerAction {
enum ReducerAction: Equatable, Pullbackable {
case c3
case c4
}

enum Action: Equatable {
enum Action: Equatable, Pullbackable {
case c1
case c2
}
Expand Down

0 comments on commit 997de56

Please sign in to comment.