Skip to content

Commit

Permalink
uispin: fix render function and add UT
Browse files Browse the repository at this point in the history
  • Loading branch information
twittemb committed Mar 10, 2020
1 parent d3cc38f commit 9b776c7
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 7 deletions.
4 changes: 4 additions & 0 deletions Sources/Spin.Combine/CombineUISpin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public final class CombineUISpin<State, Event>: CombineSpin<State, Event>, State
self.effects = [uiFeedback.effect] + spin.effects
}

public func render<Container: AnyObject>(on container: Container, using function: @escaping (Container) -> (State) -> Void) {
self.externalRenderFunction = weakify(container: container, function: function)
}

public func emit(_ event: Event) {
self.events.send(event)
}
Expand Down
4 changes: 4 additions & 0 deletions Sources/Spin.ReactiveSwift/ReactiveUISpin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public final class ReactiveUISpin<State, Event>: ReactiveSpin<State, Event>, Sta
}, on: UIScheduler())
self.effects = [uiFeedback.effect] + spin.effects
}

public func render<Container: AnyObject>(on container: Container, using function: @escaping (Container) -> (State) -> Void) {
self.externalRenderFunction = weakify(container: container, function: function)
}

public func emit(_ event: Event) {
self.eventsObserver.send(value: event)
Expand Down
33 changes: 33 additions & 0 deletions Tests/Spin.CombineTests/CombineSwiftUISpinTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,37 @@ final class CombineSwiftUISpinTests: XCTestCase {
// Then: the reactive stream is launched and the initialState is received in the effect
XCTAssertEqual(receivedState, initialState)
}

func test_CombineUISwiftSpin_mutates_the_inner_state() throws {
// Given: a Spin with an initialState and 1 effect
let initialState = "initialState"

let feedback = CombineFeedback<String, String>(effect: { states in
states.map { state -> String in
return "event"
}.eraseToAnyPublisher()
})

let reducer = CombineReducer<String, String>({ state, _ in
return "newState"
})

let spin = CombineSpin<String, String>(initialState: initialState, reducer: reducer) {
feedback
}

// When: building a CombineUISpin with the Spin
// When: starting the spin
let sut = CombineUISpin(spin: spin)

let recorder = AnyPublisher
.stream(from: sut)
.output(in: (0...2))
.record()

_ = try wait(for: recorder.completion, timeout: 5)

// Then: the state is mutated
XCTAssertEqual(sut.state, "newState")
}
}
40 changes: 39 additions & 1 deletion Tests/Spin.CombineTests/CombineUISpinTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Combine
import Spin_Combine
import XCTest

fileprivate class SpyContainer {
fileprivate class SpyRenderer {

var isRenderCalled = false
var receivedState = ""
Expand Down Expand Up @@ -142,4 +142,42 @@ final class CombineUISpinTests: XCTestCase {
// Then: the reactive stream is launched and the initialState is received in the effect
XCTAssertEqual(receivedState, initialState)
}

func test_CombineUISpin_runs_the_external_render_function () throws {
// Given: a Spin with an initialState and 1 effect
// Given: a SpyRenderer that will render the state mutations
let spyRenderer = SpyRenderer()

let initialState = "initialState"

let feedback = CombineFeedback<String, String>(effect: { states in
states.map { state -> String in
return "event"
}.eraseToAnyPublisher()
})

let reducer = CombineReducer<String, String>({ state, _ in
return "newState"
})

let spin = CombineSpin<String, String>(initialState: initialState, reducer: reducer) {
feedback
}

// When: building a CombineUISpin with the Spin and attaching the spyRenderer as the renderer of the uiSpin
// When: starting the spin
let sut = CombineUISpin(spin: spin)
sut.render(on: spyRenderer, using: { $0.render(state:) })

let recorder = AnyPublisher
.stream(from: sut)
.output(in: (0...2))
.record()

_ = try wait(for: recorder.completion, timeout: 5)

// Then: the spyRenderer is called
XCTAssertTrue(spyRenderer.isRenderCalled)
XCTAssertEqual(spyRenderer.receivedState, "newState")
}
}
38 changes: 38 additions & 0 deletions Tests/Spin.ReactiveSwiftTests/ReactiveSwiftUISpinTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,42 @@ final class ReactiveSwiftUISpinTests: XCTestCase {
// Then: the reactive stream is launched and the initialState is received in the effect
XCTAssertEqual(receivedState, initialState)
}

func test_ReactiveUISwiftSpin_mutates_the_inner_state() {
// Given: a Spin with an initialState and 1 effect
let exp = expectation(description: "spin")
let initialState = "initialState"

let feedback = ReactiveFeedback<String, String>(effect: { states in
states.map { state -> String in
if state == "newState" {
exp.fulfill()
}
return "event"
}
})

let reducer = ReactiveReducer<String, String>({ state, _ in
return "newState"
})

let spin = ReactiveSpin<String, String>(initialState: initialState, reducer: reducer) {
feedback
}

// When: building a ReactiveUISpin with the Spin
// When: starting the spin
let sut = ReactiveUISpin(spin: spin)

SignalProducer
.stream(from: sut)
.take(first: 2)
.start()
.disposed(by: self.disposeBag)

waitForExpectations(timeout: 5)

// Then: the state is mutated
XCTAssertEqual(sut.state, "newState")
}
}
49 changes: 44 additions & 5 deletions Tests/Spin.ReactiveSwiftTests/ReactiveUISpinTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Spin_ReactiveSwift
import Spin_Swift
import XCTest

fileprivate class SpyContainer {
fileprivate class SpyRenderer {

var isRenderCalled = false
var receivedState = ""
Expand Down Expand Up @@ -128,10 +128,6 @@ final class ReactiveUISpinTests: XCTestCase {
return "newState"
})

// let spin = ReactiveSpin<String, String>(initialState: initialState, reducer: reducer) {
// feedback
// }

let spin = Spinner
.initialState(initialState)
.feedback(feedback)
Expand All @@ -148,4 +144,47 @@ final class ReactiveUISpinTests: XCTestCase {
// Then: the reactive stream is launched and the initialState is received in the effect
XCTAssertEqual(receivedState, initialState)
}

func test_ReactiveUISpin_runs_the_external_render_function() {
// Given: a Spin with an initialState and 1 effect
// Given: a SpyRenderer that will render the state mutations
let exp = expectation(description: "spin")
let spyRenderer = SpyRenderer()

let initialState = "initialState"

let feedback = ReactiveFeedback<String, String>(effect: { states in
states.map { state -> String in
if state == "newState" {
exp.fulfill()
}
return "event"
}
})

let reducer = ReactiveReducer<String, String>({ state, _ in
return "newState"
})

let spin = ReactiveSpin<String, String>(initialState: initialState, reducer: reducer) {
feedback
}

// When: building a ReactiveUISpin with the Spin and attaching the spyRenderer as the renderer of the uiSpin
// When: starting the spin
let sut = ReactiveUISpin(spin: spin)
sut.render(on: spyRenderer, using: { $0.render(state:) })

SignalProducer
.stream(from: sut)
.take(first: 2)
.start()
.disposed(by: self.disposeBag)

waitForExpectations(timeout: 5)

// Then: the spyRenderer is called
XCTAssertTrue(spyRenderer.isRenderCalled)
XCTAssertEqual(spyRenderer.receivedState, "newState")
}
}
39 changes: 39 additions & 0 deletions Tests/Spin.RxSwiftTests/RxSwiftUISpinTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,43 @@ final class RxSwiftUISpinTests: XCTestCase {
// Then: the reactive stream is launched and the initialState is received in the effect
XCTAssertEqual(receivedState, initialState)
}

func test_RxUISwiftSpin_mutates_the_inner_state() {
// Given: a Spin with an initialState and 1 effect
let exp = expectation(description: "spin")

let initialState = "initialState"

let feedback = RxFeedback<String, String>(effect: { states in
states.map { state -> String in
if state == "newState" {
exp.fulfill()
}
return "event"
}
})

let reducer = RxReducer<String, String>({ state, _ in
return "newState"
})

let spin = RxSpin<String, String>(initialState: initialState, reducer: reducer) {
feedback
}

// When: building a RxUISpin with the Spin
// When: starting the spin
let sut = RxUISpin(spin: spin)

Observable
.stream(from: sut)
.take(2)
.subscribe()
.disposed(by: self.disposeBag)

waitForExpectations(timeout: 5)

// Then: the state is mutated
XCTAssertEqual(sut.state, "newState")
}
}
45 changes: 44 additions & 1 deletion Tests/Spin.RxSwiftTests/RxUISpinTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import RxSwift
import Spin_RxSwift
import XCTest

fileprivate class SpyContainer {
fileprivate class SpyRenderer {

var isRenderCalled = false
var receivedState = ""
Expand Down Expand Up @@ -142,4 +142,47 @@ final class RxUISpinTests: XCTestCase {
// Then: the reactive stream is launched and the initialState is received in the effect
XCTAssertEqual(receivedState, initialState)
}

func test_RxUISpin_runs_the_external_render_function() {
// Given: a Spin with an initialState and 1 effect
// Given: a SpyRenderer that will render the state mutations
let exp = expectation(description: "spin")
let spyRenderer = SpyRenderer()

let initialState = "initialState"

let feedback = RxFeedback<String, String>(effect: { states in
states.map { state -> String in
if state == "newState" {
exp.fulfill()
}
return "event"
}
})

let reducer = RxReducer<String, String>({ state, _ in
return "newState"
})

let spin = RxSpin<String, String>(initialState: initialState, reducer: reducer) {
feedback
}

// When: building a RxUISpin with the Spin and attaching the spyRenderer as the renderer of the uiSpin
// When: starting the spin
let sut = RxUISpin(spin: spin)
sut.render(on: spyRenderer, using: { $0.render(state:) })

Observable
.stream(from: sut)
.take(2)
.subscribe()
.disposed(by: self.disposeBag)

waitForExpectations(timeout: 5)

// Then: the spyRenderer is called
XCTAssertTrue(spyRenderer.isRenderCalled)
XCTAssertEqual(spyRenderer.receivedState, "newState")
}
}

0 comments on commit 9b776c7

Please sign in to comment.