Skip to content

Commit 5ed4d16

Browse files
committed
Added Initialization tests
1 parent acff1c2 commit 5ed4d16

File tree

3 files changed

+163
-8
lines changed

3 files changed

+163
-8
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright 2019-2024 Spotify AB.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import Foundation
16+
@testable import MobiusCore
17+
import Nimble
18+
import Quick
19+
20+
class InitializationTests: QuickSpec {
21+
// swiftlint:disable:next function_body_length
22+
override func spec() {
23+
describe("Initialization") {
24+
var builder: Mobius.Builder<String, String, String>!
25+
var updateFunction: Update<String, String, String>!
26+
var loop: MobiusLoop<String, String, String>!
27+
var receivedModels: [String]!
28+
var modelObserver: Consumer<String>!
29+
var effectHandler: RecordingTestConnectable!
30+
var eventSource: TestEventSource<String>!
31+
var connectableEventSource: TestConnectableEventSource<String, String>!
32+
33+
beforeEach {
34+
receivedModels = []
35+
36+
modelObserver = { receivedModels.append($0) }
37+
38+
updateFunction = Update<String, String, String> { _, event in
39+
if event == "event that triggers effect" {
40+
return Next.next(event, effects: [event])
41+
} else {
42+
return Next.next(event)
43+
}
44+
}
45+
46+
effectHandler = RecordingTestConnectable()
47+
eventSource = TestEventSource()
48+
connectableEventSource = .init()
49+
50+
}
51+
52+
it("should process init") {
53+
builder = Mobius.loop(update: updateFunction, effectHandler: effectHandler)
54+
55+
loop = builder.start(from: "the first model")
56+
57+
loop.addObserver(modelObserver)
58+
59+
expect(receivedModels).to(equal(["the first model"]))
60+
}
61+
62+
it("should process init and then events") {
63+
builder = Mobius.loop(update: updateFunction, effectHandler: effectHandler)
64+
65+
loop = builder.start(from: "the first model")
66+
67+
loop.addObserver(modelObserver)
68+
loop.dispatchEvent("event that triggers effect")
69+
70+
expect(receivedModels).to(equal(["the first model", "event that triggers effect"]))
71+
}
72+
73+
it("should process init before events from connectable event source") {
74+
builder = Mobius.loop(update: updateFunction, effectHandler: effectHandler)
75+
.withEventSource(connectableEventSource)
76+
77+
connectableEventSource.dispatch("ignored event from connectable event source")
78+
loop = builder.start(from: "the first model")
79+
loop.addObserver(modelObserver)
80+
81+
connectableEventSource.dispatch("second event from connectable event source")
82+
83+
// The first event was sent before the loop started so it should be ignored. The second should go through
84+
expect(receivedModels).to(equal(["the first model", "second event from connectable event source"]))
85+
}
86+
87+
it("should process init before events from event source") {
88+
builder = Mobius.loop(update: updateFunction, effectHandler: effectHandler)
89+
.withEventSource(eventSource)
90+
91+
eventSource.dispatch("ignored event from event source")
92+
loop = builder.start(from: "the first model")
93+
loop.addObserver(modelObserver)
94+
95+
eventSource.dispatch("second event from event source")
96+
97+
// The first event was sent before the loop started so it should be ignored. The second should go through
98+
expect(receivedModels).to(equal(["the first model", "second event from event source"]))
99+
}
100+
}
101+
}
102+
}
103+
104+
// Emits values before returning the connection
105+
class EagerTestConnectable: Connectable {
106+
private(set) var consumer: Consumer<String>?
107+
private(set) var recorder: Recorder<String>
108+
private(set) var eagerValue: String
109+
110+
private(set) var connection: Connection<String>!
111+
112+
init(eagerValue: String) {
113+
self.recorder = Recorder()
114+
self.eagerValue = eagerValue
115+
}
116+
117+
func connect(_ consumer: @escaping (String) -> Void) -> Connection<String> {
118+
self.consumer = consumer
119+
connection = Connection(acceptClosure: accept, disposeClosure: dispose) // Will retain self
120+
connection.accept(eagerValue) // emit before returning
121+
return connection
122+
}
123+
124+
func dispatch(_ string: String) {
125+
consumer?(string)
126+
}
127+
128+
func accept(_ value: String) {
129+
recorder.append(value)
130+
}
131+
132+
func dispose() {
133+
}
134+
}

MobiusCore/Test/MobiusControllerTests.swift

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,15 +389,36 @@ class MobiusControllerTests: QuickSpec {
389389
}
390390

391391
it("should allow the event source to change with model updates") {
392-
connectableEventSource.modelSwitch = { model in
393-
model != "S-stop"
392+
connectableEventSource.shouldProcessModel = { model in
393+
model != "S-ignore"
394394
}
395395

396-
view.dispatch("stop")
396+
view.dispatch("ignore")
397397
view.dispatch("new model 2")
398-
expect(connectableEventSource.models).toEventually(equal(["S", "S-new model 2"]))
398+
expect(connectableEventSource.models).toEventually(equal(["S", "S-ignore-new model 2"]))
399399
}
400400

401+
it("should replace the event source") {
402+
connectableEventSource = .init()
403+
404+
controller = Mobius.loop(update: updateFunction, effectHandler: effectHandler)
405+
.withEventSource(eventSource)
406+
.withEventSource(connectableEventSource)
407+
.makeController(
408+
from: "S",
409+
initiate: initiate,
410+
loopQueue: self.loopQueue,
411+
viewQueue: self.viewQueue
412+
)
413+
controller.connectView(view)
414+
controller.start()
415+
416+
eventSource.dispatch("event source event")
417+
connectableEventSource.dispatch("connectable event source event")
418+
419+
// The connectable event source should have replaced the original normal event source
420+
expect(connectableEventSource.models).toEventually(equal(["S", "S-connectable event source event"]))
421+
}
401422
}
402423

403424
describe("deallocating") {

MobiusCore/Test/TestingUtil.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ class TestConnectableEventSource<Model, Event>: Connectable {
205205
private(set) var connections: [Connection] = []
206206
private(set) var models: [Model] = []
207207
private var pendingEvent: Event?
208-
var modelSwitch: ((Model) -> Bool)?
208+
var shouldProcessModel: ((Model) -> Bool) = { _ in true }
209209

210210
var activeConnections: [Consumer<Event>] {
211211
return connections.compactMap {
@@ -233,9 +233,9 @@ class TestConnectableEventSource<Model, Event>: Connectable {
233233

234234
return .init(
235235
acceptClosure: { [weak self] model in
236-
let shouldProcessModel = self?.modelSwitch?(model) ?? false
237-
if shouldProcessModel {
238-
self?.models.append(model)
236+
guard let self else { return }
237+
if shouldProcessModel(model) {
238+
models.append(model)
239239
}
240240
}, disposeClosure: { [weak self] in
241241
self?.connections[index] = .disposed

0 commit comments

Comments
 (0)