Skip to content

Commit

Permalink
Adds tests for child store
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Usbergo committed Mar 23, 2020
1 parent a5f497c commit a81a1cf
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 9 deletions.
23 changes: 14 additions & 9 deletions Sources/Store/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ public protocol StoreProtocol: AnyStoreProtocol {
}

open class Store<M>: StoreProtocol, ObservableObject {
/// A publisher that emits before the object has changed.
public let objectWillChange = ObservableObjectPublisher()

/// The current state of this store.
@Published public private(set) var model: M
public private(set) var model: M

/// Opaque reference to the model wrapped by this store.
public var opaqueModelRef: Any { model }
Expand All @@ -49,11 +52,9 @@ open class Store<M>: StoreProtocol, ObservableObject {
/// The parent store.
public var parent: AnyStoreProtocol?

/// Synchronizes the access to the state object.
private var _stateLock = SpinLock()

/// All of the children observers.
private var _childrenBag = Array<Cancellable>()
private var _reduceParent: ((M) -> Void)?

public init(model: M) {
self.model = model
Expand All @@ -69,12 +70,15 @@ open class Store<M>: StoreProtocol, ObservableObject {
_onMainThread {
self.model = new
}
didUpdateModel(transaction: transaction, old: old, new: new)
self._stateLock.unlock()
didUpdateModel(transaction: transaction, old: old, new: new)
}

/// Emits the `objectWillChange` event and propage the changes to its parent.
/// - note: Call `super` implementation if you override this function.
open func didUpdateModel(transaction: TransactionProtocol?, old: M, new: M) {
// Subclasses to override this.
_reduceParent?(new)
notifyObservers()
}

/// Notify the store observers for the change of this store.
Expand Down Expand Up @@ -118,10 +122,11 @@ open class Store<M>: StoreProtocol, ObservableObject {
) -> Store<M_1> {
let childStore = create(model[keyPath: keyPath]);
childStore.parent = self
let cancellable = childStore.objectWillChange.sink {
self.reduceModel { $0[keyPath: keyPath] = childStore.model }
childStore._reduceParent = { child in
self.reduceModel { parent in
parent[keyPath: keyPath] = child
}
}
_childrenBag.append(cancellable)
return childStore
}

Expand Down
29 changes: 29 additions & 0 deletions Tests/StoreTests/CombinedStores.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,32 @@ extension Root.Note {
}
}
}

@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *)
final class CombinedStoreTests: XCTestCase {

var sink: AnyCancellable?

func testChildStoreChangesRootStoreValue() {
let rootStore = RootStore(model: Root())
XCTAssertFalse(rootStore.model.todo.done)
XCTAssertFalse(rootStore.todoStore.model.done)
rootStore.todoStore.run(action: Root.Todo.Action_MarkAsDone(), mode: .sync)
XCTAssertTrue(rootStore.todoStore.model.done)
XCTAssertTrue(rootStore.model.todo.done)
}

func testChildStoreChangesTriggersRootObserver() {
let observerExpectation = expectation(description: "Observer called.")
let rootStore = RootStore(model: Root())
sink = rootStore.objectWillChange.sink {
XCTAssertTrue(rootStore.model.todo.done)
XCTAssertTrue(rootStore.todoStore.model.done)
observerExpectation.fulfill()
}
rootStore.todoStore.run(action: Root.Todo.Action_MarkAsDone(), mode: .sync)
XCTAssertTrue(rootStore.todoStore.model.done)
XCTAssertTrue(rootStore.model.todo.done)
waitForExpectations(timeout: 1)
}
}

0 comments on commit a81a1cf

Please sign in to comment.