Skip to content

Commit 9653681

Browse files
committed
Fix all tests
1 parent 9da9c28 commit 9653681

File tree

14 files changed

+351
-263
lines changed

14 files changed

+351
-263
lines changed
Lines changed: 89 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,104 @@
1-
import ComposableArchitecture
1+
import Combine
2+
import CombineRex
3+
@testable import SwiftRex
24
import XCTest
35

46
enum StepType {
5-
case send
6-
case receive
7+
case send
8+
case receive
79
}
810

911
struct Step<Value, Action> {
10-
let type: StepType
11-
let action: Action
12-
let update: (inout Value) -> Void
13-
let file: StaticString
14-
let line: UInt
12+
let type: StepType
13+
let action: Action
14+
let update: (inout Value) -> Void
15+
let file: StaticString
16+
let line: UInt
1517

16-
init(
17-
_ type: StepType,
18-
_ action: Action,
19-
file: StaticString = #file,
20-
line: UInt = #line,
21-
_ update: @escaping (inout Value) -> Void
22-
) {
23-
self.type = type
24-
self.action = action
25-
self.update = update
26-
self.file = file
27-
self.line = line
28-
}
18+
init(
19+
_ type: StepType,
20+
_ action: Action,
21+
file: StaticString = #file,
22+
line: UInt = #line,
23+
_ update: @escaping (inout Value) -> Void
24+
) {
25+
self.type = type
26+
self.action = action
27+
self.update = update
28+
self.file = file
29+
self.line = line
30+
}
2931
}
3032

31-
func assert<Value: Equatable, Action: Equatable>(
32-
initialValue: Value,
33-
reducer: Reducer<Value, Action>,
34-
steps: Step<Value, Action>...,
35-
file: StaticString = #file,
36-
line: UInt = #line
37-
) {
38-
var state = initialValue
39-
var effects: [Effect<Action>] = []
33+
func assert<M: Middleware>(
34+
initialValue: M.StateType,
35+
reducer: Reducer<M.InputActionType, M.StateType>,
36+
middleware: M,
37+
steps: Step<M.StateType, M.InputActionType>...,
38+
file: StaticString = #file,
39+
line: UInt = #line
40+
) where M.InputActionType == M.OutputActionType, M.InputActionType: Equatable, M.StateType: Equatable {
41+
var state = initialValue
42+
var middlewareResponses: [M.OutputActionType] = []
43+
let gotAction = XCTestExpectation(description: "got action")
44+
gotAction.assertForOverFulfill = false
45+
let anyActionHandler = AnyActionHandler<M.OutputActionType>.init { (action, _) in
46+
middlewareResponses.append(action)
47+
gotAction.fulfill()
48+
}
49+
middleware.receiveContext(getState: { state }, output: anyActionHandler)
4050

41-
steps.forEach { step in
42-
var expected = state
51+
steps.forEach { step in
52+
var expected = state
4353

44-
switch step.type {
45-
case .send:
46-
if !effects.isEmpty {
47-
XCTFail("Action sent before handling \(effects.count) pending effect(s)", file: step.file, line: step.line)
48-
}
49-
effects.append(contentsOf: reducer(&state, step.action))
54+
switch step.type {
55+
case .send:
56+
if !middlewareResponses.isEmpty {
57+
XCTFail("Action sent before handling \(middlewareResponses.count) pending effect(s)", file: step.file, line: step.line)
58+
}
59+
var afterReducer: AfterReducer = .doNothing()
60+
middleware.handle(action: step.action, from: .init(file: "\(step.file)", function: "", line: step.line, info: nil), afterReducer: &afterReducer)
61+
state = reducer.reduce(step.action, state)
62+
afterReducer.reducerIsDone()
63+
case .receive:
64+
if middlewareResponses.isEmpty {
65+
_ = XCTWaiter.wait(for: [gotAction], timeout: 0.2)
66+
}
67+
guard !middlewareResponses.isEmpty else {
68+
XCTFail("No pending effects to receive from", file: step.file, line: step.line)
69+
break
70+
}
71+
let first = middlewareResponses.removeFirst()
72+
XCTAssertEqual(first, step.action, file: step.file, line: step.line)
73+
var afterReducer: AfterReducer = .doNothing()
74+
middleware.handle(action: step.action, from: .init(file: "\(step.file)", function: "", line: step.line, info: nil), afterReducer: &afterReducer)
75+
state = reducer.reduce(step.action, state)
76+
afterReducer.reducerIsDone()
77+
}
5078

51-
case .receive:
52-
guard !effects.isEmpty else {
53-
XCTFail("No pending effects to receive from", file: step.file, line: step.line)
54-
break
55-
}
56-
let effect = effects.removeFirst()
57-
var action: Action!
58-
let receivedCompletion = XCTestExpectation(description: "receivedCompletion")
59-
let e = effect.sink(
60-
receiveCompletion: { _ in
61-
receivedCompletion.fulfill()
62-
},
63-
receiveValue: { action = $0 }
64-
)
65-
if XCTWaiter.wait(for: [receivedCompletion], timeout: 0.01) != .completed {
66-
XCTFail("Timed out waiting for the effect to complete", file: step.file, line: step.line)
67-
}
68-
XCTAssertEqual(action, step.action, file: step.file, line: step.line)
69-
effects.append(contentsOf: reducer(&state, action))
79+
step.update(&expected)
80+
XCTAssertEqual(state, expected, file: step.file, line: step.line)
81+
}
82+
if !middlewareResponses.isEmpty {
83+
XCTFail("Assertion failed to handle \(middlewareResponses.count) pending effect(s)", file: file, line: line)
7084
}
7185

72-
step.update(&expected)
73-
XCTAssertEqual(state, expected, file: step.file, line: step.line)
74-
}
75-
if !effects.isEmpty {
76-
XCTFail("Assertion failed to handle \(effects.count) pending effect(s)", file: file, line: line)
77-
}
86+
func handle<M: Middleware>(
87+
action: M.InputActionType,
88+
state: M.StateType,
89+
middleware: M,
90+
reducer: Reducer<M.InputActionType, M.StateType>,
91+
middlewareActions: PassthroughSubject<M.OutputActionType, Never>
92+
) -> M.StateType where M.InputActionType == M.OutputActionType {
93+
var afterReducer = AfterReducer.doNothing()
94+
var state = state
95+
middleware.receiveContext(
96+
getState: { state },
97+
output: .init { action, _ in middlewareActions.send(action) }
98+
)
99+
middleware.handle(action: action, from: .here(), afterReducer: &afterReducer)
100+
state = reducer.reduce(action, state)
101+
afterReducer.reducerIsDone()
102+
return state
103+
}
78104
}

0 commit comments

Comments
 (0)