From edfb0ea9f5cf69ec32defcc925b9dc93aaeccb34 Mon Sep 17 00:00:00 2001 From: Mostafa Amer Date: Mon, 22 Apr 2019 12:01:06 +0200 Subject: [PATCH 01/76] Add `completions` property to `CompletableAction` --- Sources/Action/Action+Extensions.swift | 11 +++++++++++ Tests/ActionTests/ActionTests.swift | 11 +++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Sources/Action/Action+Extensions.swift b/Sources/Action/Action+Extensions.swift index 1d875b80..abbb361d 100644 --- a/Sources/Action/Action+Extensions.swift +++ b/Sources/Action/Action+Extensions.swift @@ -21,3 +21,14 @@ public extension Action where Input == Void { return execute(()) } } + +extension CompletableAction { + /// Emits everytime work factory completes. + public var completions: Observable { + return executionObservables + .flatMap { execution in + execution.flatMap {_ in Observable.empty() } + .concat(Observable.just(())) + } + } +} diff --git a/Tests/ActionTests/ActionTests.swift b/Tests/ActionTests/ActionTests.swift index 5d8ce029..a4da657a 100644 --- a/Tests/ActionTests/ActionTests.swift +++ b/Tests/ActionTests/ActionTests.swift @@ -18,8 +18,8 @@ class ActionTests: QuickSpec { describe("completable action") { var action: CompletableAction! beforeEach { - let work: Completable = Observable.empty().asCompletable() - action = CompletableAction {_ in work } + let work: TestableObservable = scheduler.createColdObservable([.completed(0)]) + action = CompletableAction {_ in work.asCompletable() } scheduler.scheduleAt(10) { action.inputs.onNext("a") } scheduler.scheduleAt(20) { action.inputs.onNext("b") } } @@ -41,6 +41,13 @@ class ActionTests: QuickSpec { scheduler.start() XCTAssertEqual(elements.events.count, 0) } + it("emits on `completions` when completed") { + let completions = scheduler.createObserver(Void.self) + action.completions.bind(to: completions).disposed(by: disposeBag) + scheduler.start() + XCTAssert(completions.events.contains { $0.time == 10}) + XCTAssert(completions.events.contains { $0.time == 20}) + } } describe("action properties") { From ae1e4ca1c2e5d3d0b087275c0365111135bbf77c Mon Sep 17 00:00:00 2001 From: Mostafa Amer Date: Mon, 22 Apr 2019 12:05:02 +0200 Subject: [PATCH 02/76] Update changelog --- Changelog.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index c64903af..f577d050 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,10 +4,12 @@ Changelog Current master -------------- +- Introdue `completions` property to `CompletableAction` + 3.11.0 ------- -- Introduction of `underlyingError` observable which returns a `Swift.Error` element type. -- Updated specs that were breaking in `Circle CI` pipeline +- Introduction of `underlyingError` observable which returns a `Swift.Error` element type. +- Updated specs that were breaking in `Circle CI` pipeline 3.10.2 ------- From cb8a78a9d90e12ad833eb93f0011bf75540a2870 Mon Sep 17 00:00:00 2001 From: Mostafa Amer Date: Tue, 23 Apr 2019 18:36:40 +0200 Subject: [PATCH 03/76] Address review comments --- Changelog.md | 2 +- Sources/Action/Action+Extensions.swift | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index f577d050..824b97fb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,7 +4,7 @@ Changelog Current master -------------- -- Introdue `completions` property to `CompletableAction` +- Add `completions` property to `CompletableAction` 3.11.0 ------- diff --git a/Sources/Action/Action+Extensions.swift b/Sources/Action/Action+Extensions.swift index abbb361d..04243272 100644 --- a/Sources/Action/Action+Extensions.swift +++ b/Sources/Action/Action+Extensions.swift @@ -22,12 +22,12 @@ public extension Action where Input == Void { } } -extension CompletableAction { +public extension CompletableAction { /// Emits everytime work factory completes. - public var completions: Observable { + var completions: Observable { return executionObservables .flatMap { execution in - execution.flatMap {_ in Observable.empty() } + execution.flatMap { _ in Observable.empty() } .concat(Observable.just(())) } } From 5e7fb77e86e99a45442ad531f0a0191b194777df Mon Sep 17 00:00:00 2001 From: Mostafa Amer Date: Tue, 23 Apr 2019 16:37:14 +0200 Subject: [PATCH 04/76] Change `inputs` type to `AnyObserver` (#193) * improve input tests Test that work factory is called with received inputs instead of testing that they are received on `inputs` subject * add test cases for input receiving terminating events * change `inputs` type to `AnyObserver` * drop `InputSubject` * Update changelog (cherry picked from commit d463ba95882b33e9568a26bddb4aefa91ca08337) --- Action.xcodeproj/project.pbxproj | 17 +--- Cartfile.resolved | 4 +- Changelog.md | 1 + Sources/Action/Action.swift | 15 ++-- Sources/Action/InputSubject.swift | 93 --------------------- Tests/ActionTests/ActionTests.swift | 123 +++++++++++++++++++++------- Tests/InputSubjectTests.swift | 113 ------------------------- 7 files changed, 107 insertions(+), 259 deletions(-) delete mode 100644 Sources/Action/InputSubject.swift delete mode 100644 Tests/InputSubjectTests.swift diff --git a/Action.xcodeproj/project.pbxproj b/Action.xcodeproj/project.pbxproj index b5d135eb..8833aa0b 100644 --- a/Action.xcodeproj/project.pbxproj +++ b/Action.xcodeproj/project.pbxproj @@ -24,7 +24,6 @@ 3D730DEB1F674044008534D3 /* Button+Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D730DE81F673FD9008534D3 /* Button+Action.swift */; }; 3DD965C01F5DC0E400C180FE /* Action.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FCDDA821EAC3295006EB95B /* Action.framework */; }; 3DD965C61F5DC2E100C180FE /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0569F01DE288EB007E1D0D /* ActionTests.swift */; }; - 3DD965C81F5DC2E700C180FE /* InputSubjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA2861C91ED6A41700BB327A /* InputSubjectTests.swift */; }; 3DD965DE1F5DF8C500C180FE /* BindToTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DD965DD1F5DF86B00C180FE /* BindToTests.swift */; }; 3DD965DF1F5DF8C900C180FE /* NSButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DD965DA1F5DF79800C180FE /* NSButtonTests.swift */; }; 5ED520241E1EA199007621B9 /* BindToTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ED520231E1EA199007621B9 /* BindToTests.swift */; }; @@ -72,10 +71,6 @@ C41E08EF2237D2700039D213 /* Action+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DE00042229758700FB50AB /* Action+Extensions.swift */; }; C41E08F02237D2740039D213 /* Action+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DE00042229758700FB50AB /* Action+Extensions.swift */; }; C4DE00052229758700FB50AB /* Action+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DE00042229758700FB50AB /* Action+Extensions.swift */; }; - CA2861C81ED6979400BB327A /* InputSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA2861C71ED6979400BB327A /* InputSubject.swift */; }; - CA2861CA1ED6A41700BB327A /* InputSubjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA2861C91ED6A41700BB327A /* InputSubjectTests.swift */; }; - CA2861CB1ED6B08300BB327A /* InputSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA2861C71ED6979400BB327A /* InputSubject.swift */; }; - CA2861CC1ED6B08400BB327A /* InputSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA2861C71ED6979400BB327A /* InputSubject.swift */; }; FA3F973C1EDAF46F00A84787 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0569E21DE28587007E1D0D /* Action.swift */; }; FA3F973D1EDAF46F00A84787 /* Action+Internal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0569E01DE28587007E1D0D /* Action+Internal.swift */; }; FA3F973E1EDAF46F00A84787 /* InputSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA2861C71ED6979400BB327A /* InputSubject.swift */; }; @@ -166,8 +161,8 @@ C4E0264220D11A3B00C8164C /* Readme.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Readme.md; sourceTree = ""; }; C4E0264320D11CDD00C8164C /* circle.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = circle.yml; sourceTree = ""; }; C4E0264420D1244900C8164C /* Changelog.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Changelog.md; sourceTree = ""; }; - CA2861C71ED6979400BB327A /* InputSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputSubject.swift; sourceTree = ""; }; - CA2861C91ED6A41700BB327A /* InputSubjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputSubjectTests.swift; sourceTree = ""; }; + FC9C4166215A99BE00541C4B /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = ""; }; + FC9C4167215A99BE00541C4B /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -291,7 +286,6 @@ 7F0569E21DE28587007E1D0D /* Action.swift */, C4DE00042229758700FB50AB /* Action+Extensions.swift */, 7F0569E01DE28587007E1D0D /* Action+Internal.swift */, - CA2861C71ED6979400BB327A /* InputSubject.swift */, 3D730DE11F673BCB008534D3 /* CommonUI */, 7F0569E41DE28587007E1D0D /* UIKitExtensions */, 7F0569EF1DE28598007E1D0D /* Supporting Files */, @@ -348,7 +342,6 @@ 3DD965DC1F5DF84800C180FE /* macOS-Tests */, 3DD965DB1F5DF83700C180FE /* iOS-Tests */, 7F0569F01DE288EB007E1D0D /* ActionTests.swift */, - CA2861C91ED6A41700BB327A /* InputSubjectTests.swift */, 7F0569F41DE288EB007E1D0D /* Info.plist */, ); path = Tests; @@ -719,7 +712,6 @@ C41E08EE2237D26D0039D213 /* Action+Extensions.swift in Sources */, 3D730DEA1F674043008534D3 /* Button+Action.swift in Sources */, 1FCDDA671EAC31EF006EB95B /* UIBarButtonItem+Action.swift in Sources */, - CA2861CB1ED6B08300BB327A /* InputSubject.swift in Sources */, 3D730DE41F673C64008534D3 /* Control+Action.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -728,7 +720,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - CA2861CC1ED6B08400BB327A /* InputSubject.swift in Sources */, C41E08EF2237D2700039D213 /* Action+Extensions.swift in Sources */, 3D730DE51F673C65008534D3 /* Control+Action.swift in Sources */, 1FCDDA8A1EAC329E006EB95B /* Action.swift in Sources */, @@ -743,7 +734,6 @@ files = ( FA3F973C1EDAF46F00A84787 /* Action.swift in Sources */, FA3F973D1EDAF46F00A84787 /* Action+Internal.swift in Sources */, - FA3F973E1EDAF46F00A84787 /* InputSubject.swift in Sources */, C41E08F02237D2740039D213 /* Action+Extensions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -755,7 +745,6 @@ 3DD965DE1F5DF8C500C180FE /* BindToTests.swift in Sources */, 3DD965C61F5DC2E100C180FE /* ActionTests.swift in Sources */, 3DD965DF1F5DF8C900C180FE /* NSButtonTests.swift in Sources */, - 3DD965C81F5DC2E700C180FE /* InputSubjectTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -769,7 +758,6 @@ 7F0569F51DE288EB007E1D0D /* ActionTests.swift in Sources */, 7B4BFE6320C290BF00D72FB0 /* RefreshControlTests.swift in Sources */, 7F0569F81DE288EB007E1D0D /* ButtonTests.swift in Sources */, - CA2861CA1ED6A41700BB327A /* InputSubjectTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -792,7 +780,6 @@ 7F0569ED1DE28587007E1D0D /* UIBarButtonItem+Action.swift in Sources */, C4DE00052229758700FB50AB /* Action+Extensions.swift in Sources */, 3D730DE91F673FD9008534D3 /* Button+Action.swift in Sources */, - CA2861C81ED6979400BB327A /* InputSubject.swift in Sources */, 3D730DE31F673BE4008534D3 /* Control+Action.swift in Sources */, 7F0569EA1DE28587007E1D0D /* Action.swift in Sources */, ); diff --git a/Cartfile.resolved b/Cartfile.resolved index e4195453..79eb0d72 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,3 +1,3 @@ github "Quick/Nimble" "v8.0.1" -github "Quick/Quick" "v2.0.0" -github "ReactiveX/RxSwift" "4.4.2" +github "Quick/Quick" "v2.1.0" +github "ReactiveX/RxSwift" "4.5.0" diff --git a/Changelog.md b/Changelog.md index 824b97fb..9b63a952 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Changelog Current master -------------- - Add `completions` property to `CompletableAction` +- Change `inputs` type to `AnyObserver` 3.11.0 ------- diff --git a/Sources/Action/Action.swift b/Sources/Action/Action.swift index 8ed51961..0dac422b 100644 --- a/Sources/Action/Action.swift +++ b/Sources/Action/Action.swift @@ -25,11 +25,8 @@ public final class Action { public let _enabledIf: Observable public let workFactory: WorkFactory - /// Inputs that triggers execution of action. - /// This subject also includes inputs as aguments of execute(). - /// All inputs are always appear in this subject even if the action is not enabled. - /// Thus, inputs count equals elements count + errors count. - public let inputs = InputSubject() + /// Bindable sink for inputs that triggers execution of action. + public let inputs: AnyObserver /// Errors aggrevated from invocations of execute(). /// Delivered on whatever scheduler they were sent from. @@ -76,7 +73,13 @@ public final class Action { let errorsSubject = PublishSubject() errors = errorsSubject.asObservable() - executionObservables = inputs + let inputsSubject = PublishSubject() + inputs = AnyObserver { event in + guard case .next(let value) = event else { return } + inputsSubject.onNext(value) + } + + executionObservables = inputsSubject .withLatestFrom(enabled) { input, enabled in (input, enabled) } .flatMap { input, enabled -> Observable> in if enabled { diff --git a/Sources/Action/InputSubject.swift b/Sources/Action/InputSubject.swift deleted file mode 100644 index e4fcb211..00000000 --- a/Sources/Action/InputSubject.swift +++ /dev/null @@ -1,93 +0,0 @@ -import Foundation -import RxSwift - -/// A special subject for Action.inputs. It only emits `.next` event. -public class InputSubject: ObservableType, Cancelable, SubjectType, ObserverType { - - public typealias E = Element - typealias Key = UInt - - /// Indicates whether the subject has any observers - public var hasObservers: Bool { - _lock.lock() - let count = _observers.count > 0 - _lock.unlock() - return count - } - - // state - private let _lock = NSRecursiveLock() - private var _nextKey: Key = 0 - private var _observers: [Key: (Event) -> Void] = [:] - private var _isDisposed = false - - /// Indicates whether the subject has been isDisposed. - public var isDisposed: Bool { - _lock.lock() - let isDisposed = _isDisposed - _lock.unlock() - return isDisposed - } - - /// Creates a subject. - public init() { - #if TRACE_RESOURCES - _ = Resources.incrementTotal() - #endif - } - - /// Notifies all subscribed observers abount only `.next` event. - /// - /// - parameter event: Event to send to the observers. - public func on(_ event: Event) { - _lock.lock() - switch event { - case .next where !_isDisposed: - _observers.values.forEach { $0(event) } - default: - break - } - _lock.unlock() - } - - /** - Subscribes an observer to the subject. - - - parameter observer: Observer to subscribe to the subject. - - returns: Disposable object that can be used to unsubscribe the observer from the subject. - */ - public func subscribe(_ observer: O) -> Disposable where O.E == Element { - _lock.lock() - - if _isDisposed { - observer.on(.error(RxError.disposed(object: self))) - return Disposables.create() - } - - let key = _nextKey - _nextKey += 1 - _observers[key] = observer.on - _lock.unlock() - - return Disposables.create { [weak self] in - self?._lock.lock() - self?._observers.removeValue(forKey: key) - self?._lock.unlock() - } - } - - /// Unsubscribe all observers and release resources. - public func dispose() { - _lock.lock() - _isDisposed = true - _observers.removeAll() - _lock.unlock() - } - - #if TRACE_RESOURCES - deinit { - _ = Resources.decrementTotal() - } - #endif - -} diff --git a/Tests/ActionTests/ActionTests.swift b/Tests/ActionTests/ActionTests.swift index a4da657a..fba1c2af 100644 --- a/Tests/ActionTests/ActionTests.swift +++ b/Tests/ActionTests/ActionTests.swift @@ -16,19 +16,21 @@ class ActionTests: QuickSpec { } describe("completable action") { + var inputs: TestableObserver! var action: CompletableAction! beforeEach { - let work: TestableObservable = scheduler.createColdObservable([.completed(0)]) - action = CompletableAction {_ in work.asCompletable() } + inputs = scheduler.createObserver(String.self) + action = CompletableAction { input -> Completable in + inputs.onNext(input) + return Observable.empty().asCompletable() + } scheduler.scheduleAt(10) { action.inputs.onNext("a") } scheduler.scheduleAt(20) { action.inputs.onNext("b") } } afterEach { action = nil } - it("inputs subject receives generated inputs") { - let inputs = scheduler.createObserver(String.self) - action.inputs.bind(to: inputs).disposed(by: disposeBag) + it("receives generated inputs") { scheduler.start() XCTAssertEqual(inputs.events, [ next(10, "a"), @@ -49,6 +51,72 @@ class ActionTests: QuickSpec { XCTAssert(completions.events.contains { $0.time == 20}) } } + + describe("Input observer behavior") { + var action: Action! + var inputs: TestableObserver! + var executions: TestableObserver>! + beforeEach { + inputs = scheduler.createObserver(String.self) + action = Action { + inputs.onNext($0) + return Observable.just($0) + } + executions = scheduler.createObserver(Observable.self) + action.executionObservables.bind(to: executions).disposed(by: disposeBag) + } + afterEach { + action = nil + inputs = nil + executions = nil + } + + it("execute on .next") { + scheduler.scheduleAt(10) { action.inputs.onNext("a") } + scheduler.start() + XCTAssertEqual(inputs.events, [next(10, "a")]) + XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 1) + } + it("ignore .error events") { + scheduler.scheduleAt(10) { action.inputs.onError(TestError) } + scheduler.start() + XCTAssertEqual(inputs.events, []) + XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 0) + } + it("ignore .completed events") { + scheduler.scheduleAt(10) { action.inputs.onCompleted() } + scheduler.start() + XCTAssertEqual(inputs.events, []) + XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 0) + } + it("accept multiple .next events") { + scheduler.scheduleAt(10) { action.inputs.onNext("a") } + scheduler.scheduleAt(20) { action.inputs.onNext("b") } + scheduler.start() + XCTAssertEqual(inputs.events, [ + next(10, "a"), + next(20, "b"), + ]) + XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 2) + XCTAssertEqual(executions.events.filter { $0.value.isStopEvent }.count, 0) + } + it("not terminate after .error event") { + scheduler.scheduleAt(10) { action.inputs.onError(TestError) } + scheduler.scheduleAt(20) { action.inputs.onNext("b") } + scheduler.start() + XCTAssertEqual(inputs.events, [next(20, "b")]) + XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 1) + XCTAssertEqual(executions.events.filter { $0.value.isStopEvent }.count, 0) + } + it("not terminate after .completed event") { + scheduler.scheduleAt(10) { action.inputs.onCompleted() } + scheduler.scheduleAt(20) { action.inputs.onNext("b") } + scheduler.start() + XCTAssertEqual(inputs.events, [next(20, "b")]) + XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 1) + XCTAssertEqual(executions.events.filter { $0.value.isStopEvent }.count, 0) + } + } describe("action properties") { var inputs: TestableObserver! @@ -68,12 +136,14 @@ class ActionTests: QuickSpec { executionObservables = scheduler.createObserver(Observable.self) underlyingError = scheduler.createObserver(Error.self) } - - func bindAction(action: Action) { - action.inputs - .bind(to: inputs) - .disposed(by: disposeBag) - + + func buildAction(enabledIf: Observable = Observable.just(true), + factory: @escaping (String) -> Observable) -> Action { + let action = Action(enabledIf: enabledIf) { + inputs.onNext($0) + return factory($0) + } + action.elements .bind(to: elements) .disposed(by: disposeBag) @@ -98,19 +168,20 @@ class ActionTests: QuickSpec { .bind(to: underlyingError) .disposed(by: disposeBag) - // Dummy subscription for multiple subcription tests - action.inputs.subscribe().disposed(by: disposeBag) + // Dummy subscription for multiple subcription tests action.elements.subscribe().disposed(by: disposeBag) action.errors.subscribe().disposed(by: disposeBag) action.enabled.subscribe().disposed(by: disposeBag) action.executing.subscribe().disposed(by: disposeBag) action.executionObservables.subscribe().disposed(by: disposeBag) action.underlyingError.subscribe().disposed(by: disposeBag) + + return action } describe("single element action") { sharedExamples("send elements to elements observable") { - it("inputs subject receives generated inputs") { + it("work factory receives inputs") { XCTAssertEqual(inputs.events, [ next(10, "a"), next(20, "b"), @@ -156,8 +227,7 @@ class ActionTests: QuickSpec { var action: Action! beforeEach { - action = Action { Observable.just($0) } - bindAction(action: action) + action = buildAction { Observable.just($0) } } context("trigger via inputs subject") { @@ -183,7 +253,7 @@ class ActionTests: QuickSpec { describe("multiple element action") { sharedExamples("send array elements to elements observable") { - it("inputs subject receives generated inputs") { + it("work factory receives inputs") { XCTAssertEqual(inputs.events, [ next(10, "a"), next(20, "b"), @@ -233,7 +303,7 @@ class ActionTests: QuickSpec { var action: Action! beforeEach { - action = Action { input in + action = buildAction { input in // "a" -> ["a", "b", "c"] let baseValue = UnicodeScalar(input)!.value let strings = (baseValue..<(baseValue + 3)) @@ -242,8 +312,6 @@ class ActionTests: QuickSpec { return Observable.from(strings) } - - bindAction(action: action) } context("trigger via inputs subject") { @@ -269,7 +337,7 @@ class ActionTests: QuickSpec { describe("error action") { sharedExamples("send errors to errors observable") { - it("inputs subject receives generated inputs") { + it("work factory receives inputs") { XCTAssertEqual(inputs.events, [ next(10, "a"), next(20, "b"), @@ -326,8 +394,7 @@ class ActionTests: QuickSpec { var action: Action! beforeEach { - action = Action { _ in Observable.error(TestError) } - bindAction(action: action) + action = buildAction { _ in Observable.error(TestError) } } context("trigger via inputs subject") { @@ -353,11 +420,8 @@ class ActionTests: QuickSpec { describe("disabled action") { sharedExamples("send notEnabled errors to errors observable") { - it("inputs subject receives generated inputs") { - XCTAssertEqual(inputs.events, [ - next(10, "a"), - next(20, "b"), - ]) + it("work factory receives nothing") { + XCTAssertEqual(inputs.events, []) } it("elements observable receives nothing") { @@ -395,8 +459,7 @@ class ActionTests: QuickSpec { var action: Action! beforeEach { - action = Action(enabledIf: Observable.just(false)) { Observable.just($0) } - bindAction(action: action) + action = buildAction(enabledIf: Observable.just(false)) { Observable.just($0) } } context("trigger via inputs subject") { diff --git a/Tests/InputSubjectTests.swift b/Tests/InputSubjectTests.swift deleted file mode 100644 index 69cd1b11..00000000 --- a/Tests/InputSubjectTests.swift +++ /dev/null @@ -1,113 +0,0 @@ -import Quick -import Nimble -import RxSwift -import RxTest -import Action - -class InputSubjectTests: QuickSpec { - override func spec() { - var scheduler: TestScheduler! - var disposeBag: DisposeBag! - - beforeEach { - scheduler = TestScheduler(initialClock: 0) - disposeBag = DisposeBag() - } - - describe("Disposable observable") { - it("observables can be dispose") { - let subject = InputSubject() - let disposable1 = subject.subscribe() - let disposable2 = subject.subscribe() - expect(subject.hasObservers).to(beTrue()) - disposable2.dispose() - expect(subject.hasObservers).to(beTrue()) - disposable1.dispose() - expect(subject.hasObservers).to(beFalse()) - } - - it("dispose all observables") { - let subject = InputSubject() - _ = subject.subscribe() - _ = subject.subscribe() - expect(subject.hasObservers).to(beTrue()) - subject.dispose() - expect(subject.hasObservers).to(beFalse()) - expect(subject.isDisposed).to(beTrue()) - } - } - - describe("emit events") { - it("emit .next events") { - let subject = InputSubject() - let observer = scheduler.createObserver(Int.self) - subject.asObservable() - .bind(to: observer) - .disposed(by: disposeBag) - scheduler.scheduleAt(10) { subject.onNext(1) } - scheduler.scheduleAt(20) { subject.onNext(2) } - scheduler.scheduleAt(30) { subject.onNext(3) } - scheduler.start() - - XCTAssertEqual(observer.events, [ - next(10, 1), - next(20, 2), - next(30, 3) - ]) - } - - it("ignore .error events") { - let subject = InputSubject() - let observer = scheduler.createObserver(Int.self) - subject.asObservable() - .bind(to: observer) - .disposed(by: disposeBag) - scheduler.scheduleAt(10) { subject.onNext(1) } - scheduler.scheduleAt(20) { subject.onError(TestError) } - scheduler.scheduleAt(30) { subject.onNext(3) } - scheduler.start() - - XCTAssertEqual(observer.events, [ - next(10, 1), - next(30, 3) - ]) - } - - it("ignore .completed events") { - let subject = InputSubject() - let observer = scheduler.createObserver(Int.self) - subject.asObservable() - .bind(to: observer) - .disposed(by: disposeBag) - scheduler.scheduleAt(10) { subject.onNext(1) } - scheduler.scheduleAt(20) { subject.onCompleted() } - scheduler.scheduleAt(30) { subject.onNext(3) } - scheduler.start() - - XCTAssertEqual(observer.events, [ - next(10, 1), - next(30, 3) - ]) - } - - it("event does not fire on disposed subject") { - let subject = InputSubject() - let observer = scheduler.createObserver(Int.self) - subject.asObservable() - .bind(to: observer) - .disposed(by: disposeBag) - scheduler.scheduleAt(10) { subject.onNext(1) } - scheduler.scheduleAt(20) { subject.onNext(2) } - scheduler.scheduleAt(30) { subject.dispose() } - scheduler.scheduleAt(40) { subject.onNext(4) } - scheduler.start() - - XCTAssertEqual(observer.events, [ - next(10, 1), - next(20, 2), - ]) - } - } - - } -} From 96f98bf206ab02c055c42891dd2d3dbe3bfcda07 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sat, 6 Apr 2019 15:22:36 -0300 Subject: [PATCH 05/76] Upating dependencies to Rx 4.5.0 --- Action.podspec | 4 ++-- Package.resolved | 4 ++-- Package.swift | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Action.podspec b/Action.podspec index 7e21b46d..87fb2c42 100644 --- a/Action.podspec +++ b/Action.podspec @@ -20,8 +20,8 @@ Pod::Spec.new do |s| s.source_files = "Sources/**/*.{swift}" s.frameworks = "Foundation" - s.dependency "RxSwift", "~> 4.0" - s.dependency "RxCocoa", "~> 4.0" + s.dependency "RxSwift", "~> 4.5.0" + s.dependency "RxCocoa", "~> 4.5.0" s.watchos.exclude_files = "Control+Action.swift", "Button+Action.swift", "UIBarButtonItem+Action.swift", "UIAlertAction+Action.swift" s.osx.exclude_files = "UIBarButtonItem+Action.swift", "UIAlertAction+Action.swift" diff --git a/Package.resolved b/Package.resolved index b77b1084..4f553d3c 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/ReactiveX/RxSwift.git", "state": { "branch": null, - "revision": "12cccb171ad9038251af6883807f0290c1d75a5b", - "version": "4.0.0" + "revision": "cce95dd704bc08cd3d69c087a05a6fc3118e2722", + "version": "4.5.0" } } ] diff --git a/Package.swift b/Package.swift index fbef694a..502b22a0 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,7 @@ let package = Package( targets: ["Action"]), ], dependencies: [ - .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "4.0.0") + .package(url: "https://github.com/ReactiveX/RxSwift.git", .exact("4.5.0")) ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. From c7f28f44ec4c4a8c96b5e5eeed32eea415502bea Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sun, 7 Apr 2019 14:09:48 -0300 Subject: [PATCH 06/76] Reverting version. --- Action.podspec | 4 ++-- Package.resolved | 4 ++-- Package.swift | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Action.podspec b/Action.podspec index 87fb2c42..41a195a4 100644 --- a/Action.podspec +++ b/Action.podspec @@ -20,8 +20,8 @@ Pod::Spec.new do |s| s.source_files = "Sources/**/*.{swift}" s.frameworks = "Foundation" - s.dependency "RxSwift", "~> 4.5.0" - s.dependency "RxCocoa", "~> 4.5.0" + s.dependency "RxSwift", "~> 4.0.0" + s.dependency "RxCocoa", "~> 4.0.0" s.watchos.exclude_files = "Control+Action.swift", "Button+Action.swift", "UIBarButtonItem+Action.swift", "UIAlertAction+Action.swift" s.osx.exclude_files = "UIBarButtonItem+Action.swift", "UIAlertAction+Action.swift" diff --git a/Package.resolved b/Package.resolved index 4f553d3c..b77b1084 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/ReactiveX/RxSwift.git", "state": { "branch": null, - "revision": "cce95dd704bc08cd3d69c087a05a6fc3118e2722", - "version": "4.5.0" + "revision": "12cccb171ad9038251af6883807f0290c1d75a5b", + "version": "4.0.0" } } ] diff --git a/Package.swift b/Package.swift index 502b22a0..3ecd8269 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,7 @@ let package = Package( targets: ["Action"]), ], dependencies: [ - .package(url: "https://github.com/ReactiveX/RxSwift.git", .exact("4.5.0")) + .package(url: "https://github.com/ReactiveX/RxSwift.git", .exact("4.0.0")) ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. From 21a60f8431656523457a3711585fd6b1660a7f97 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sun, 7 Apr 2019 14:45:41 -0300 Subject: [PATCH 07/76] From 4.0.0 on package --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 3ecd8269..fbef694a 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,7 @@ let package = Package( targets: ["Action"]), ], dependencies: [ - .package(url: "https://github.com/ReactiveX/RxSwift.git", .exact("4.0.0")) + .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "4.0.0") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. From e43b945475db24767b6b7c680223363ae47ea0fd Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Wed, 10 Apr 2019 12:12:45 -0300 Subject: [PATCH 08/76] Apply suggestions from code review --- Action.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Action.podspec b/Action.podspec index 41a195a4..7e21b46d 100644 --- a/Action.podspec +++ b/Action.podspec @@ -20,8 +20,8 @@ Pod::Spec.new do |s| s.source_files = "Sources/**/*.{swift}" s.frameworks = "Foundation" - s.dependency "RxSwift", "~> 4.0.0" - s.dependency "RxCocoa", "~> 4.0.0" + s.dependency "RxSwift", "~> 4.0" + s.dependency "RxCocoa", "~> 4.0" s.watchos.exclude_files = "Control+Action.swift", "Button+Action.swift", "UIBarButtonItem+Action.swift", "UIAlertAction+Action.swift" s.osx.exclude_files = "UIBarButtonItem+Action.swift", "UIAlertAction+Action.swift" From 58335d00a105ac386048def7c4bcccf08dc7e210 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Wed, 1 May 2019 10:49:48 -0300 Subject: [PATCH 09/76] Updating to Rx 5.0 --- .swift-version | 2 +- Action.podspec | 4 ++-- Package.resolved | 4 ++-- Package.swift | 2 +- Sources/Action/Action.swift | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.swift-version b/.swift-version index bf77d549..6e636605 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -4.2 +5.0 \ No newline at end of file diff --git a/Action.podspec b/Action.podspec index 7e21b46d..3cb08116 100644 --- a/Action.podspec +++ b/Action.podspec @@ -20,8 +20,8 @@ Pod::Spec.new do |s| s.source_files = "Sources/**/*.{swift}" s.frameworks = "Foundation" - s.dependency "RxSwift", "~> 4.0" - s.dependency "RxCocoa", "~> 4.0" + s.dependency "RxSwift", "~> 5.0.0" + s.dependency "RxCocoa", "~> 5.0.0" s.watchos.exclude_files = "Control+Action.swift", "Button+Action.swift", "UIBarButtonItem+Action.swift", "UIAlertAction+Action.swift" s.osx.exclude_files = "UIBarButtonItem+Action.swift", "UIAlertAction+Action.swift" diff --git a/Package.resolved b/Package.resolved index b77b1084..e3afef80 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/ReactiveX/RxSwift.git", "state": { "branch": null, - "revision": "12cccb171ad9038251af6883807f0290c1d75a5b", - "version": "4.0.0" + "revision": "b3e888b4972d9bc76495dd74d30a8c7fad4b9395", + "version": "5.0.1" } } ] diff --git a/Package.swift b/Package.swift index fbef694a..06e59def 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,7 @@ let package = Package( targets: ["Action"]), ], dependencies: [ - .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "4.0.0") + .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "5.0.0") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. diff --git a/Sources/Action/Action.swift b/Sources/Action/Action.swift index 0dac422b..c4917508 100644 --- a/Sources/Action/Action.swift +++ b/Sources/Action/Action.swift @@ -54,7 +54,7 @@ public final class Action { public convenience init( enabledIf: Observable = Observable.just(true), workFactory: @escaping (Input) -> O - ) where O.E == Element { + ) where O.Element == Element { self.init(enabledIf: enabledIf) { workFactory($0).asObservable() } From 1509604fdbda47a3e5917456ae9294721bb1a78a Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Wed, 1 May 2019 12:21:59 -0300 Subject: [PATCH 10/76] Updating to Rx 5.0 and fixing deprecated warnings. --- Cartfile | 2 +- Cartfile.resolved | 2 +- Tests/ActionTests/ActionTests.swift | 142 ++++++++++++++-------------- 3 files changed, 73 insertions(+), 73 deletions(-) diff --git a/Cartfile b/Cartfile index 0fb6d5b8..43e186b7 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveX/RxSwift" ~> 4.4.2 +github "ReactiveX/RxSwift" ~> 5.0.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index 79eb0d72..851ad793 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,3 +1,3 @@ github "Quick/Nimble" "v8.0.1" github "Quick/Quick" "v2.1.0" -github "ReactiveX/RxSwift" "4.5.0" +github "ReactiveX/RxSwift" "5.0.1" \ No newline at end of file diff --git a/Tests/ActionTests/ActionTests.swift b/Tests/ActionTests/ActionTests.swift index fba1c2af..82502d57 100644 --- a/Tests/ActionTests/ActionTests.swift +++ b/Tests/ActionTests/ActionTests.swift @@ -33,8 +33,8 @@ class ActionTests: QuickSpec { it("receives generated inputs") { scheduler.start() XCTAssertEqual(inputs.events, [ - next(10, "a"), - next(20, "b"), + Recorded.next(10, "a"), + Recorded.next(20, "b"), ]) } it("emits nothing on `elements`") { @@ -183,15 +183,15 @@ class ActionTests: QuickSpec { sharedExamples("send elements to elements observable") { it("work factory receives inputs") { XCTAssertEqual(inputs.events, [ - next(10, "a"), - next(20, "b"), + Recorded.next(10, "a"), + Recorded.next(20, "b"), ]) } it("elements observable receives generated elements") { XCTAssertEqual(elements.events, [ - next(10, "a"), - next(20, "b"), + Recorded.next(10, "a"), + Recorded.next(20, "b"), ]) } @@ -201,21 +201,21 @@ class ActionTests: QuickSpec { it("disabled until element returns") { XCTAssertEqual(enabled.events, [ - next(0, true), - next(10, false), - next(10, true), - next(20, false), - next(20, true), + Recorded.next(0, true), + Recorded.next(10, false), + Recorded.next(10, true), + Recorded.next(20, false), + Recorded.next(20, true), ]) } it("executing until element returns") { XCTAssertEqual(executing.events, [ - next(0, false), - next(10, true), - next(10, false), - next(20, true), - next(20, false), + Recorded.next(0, false), + Recorded.next(10, true), + Recorded.next(10, false), + Recorded.next(20, true), + Recorded.next(20, false), ]) } @@ -255,19 +255,19 @@ class ActionTests: QuickSpec { sharedExamples("send array elements to elements observable") { it("work factory receives inputs") { XCTAssertEqual(inputs.events, [ - next(10, "a"), - next(20, "b"), + Recorded.next(10, "a"), + Recorded.next(20, "b"), ]) } it("elements observable receives generated elements") { XCTAssertEqual(elements.events, [ - next(10, "a"), - next(10, "b"), - next(10, "c"), - next(20, "b"), - next(20, "c"), - next(20, "d"), + Recorded.next(10, "a"), + Recorded.next(10, "b"), + Recorded.next(10, "c"), + Recorded.next(20, "b"), + Recorded.next(20, "c"), + Recorded.next(20, "d"), ]) } @@ -277,21 +277,21 @@ class ActionTests: QuickSpec { it("disabled until element returns") { XCTAssertEqual(enabled.events, [ - next(0, true), - next(10, false), - next(10, true), - next(20, false), - next(20, true), + Recorded.next(0, true), + Recorded.next(10, false), + Recorded.next(10, true), + Recorded.next(20, false), + Recorded.next(20, true), ]) } it("executing until element returns") { XCTAssertEqual(executing.events, [ - next(0, false), - next(10, true), - next(10, false), - next(20, true), - next(20, false), + Recorded.next(0, false), + Recorded.next(10, true), + Recorded.next(10, false), + Recorded.next(20, true), + Recorded.next(20, false), ]) } @@ -339,8 +339,8 @@ class ActionTests: QuickSpec { sharedExamples("send errors to errors observable") { it("work factory receives inputs") { XCTAssertEqual(inputs.events, [ - next(10, "a"), - next(20, "b"), + Recorded.next(10, "a"), + Recorded.next(20, "b"), ]) } @@ -350,8 +350,8 @@ class ActionTests: QuickSpec { it("errors observable receives generated errors") { XCTAssertEqual(errors.events, [ - next(10, .underlyingError(TestError)), - next(20, .underlyingError(TestError)), + Recorded.next(10, .underlyingError(TestError)), + Recorded.next(20, .underlyingError(TestError)), ]) } @@ -368,21 +368,21 @@ class ActionTests: QuickSpec { it("disabled until error returns") { XCTAssertEqual(enabled.events, [ - next(0, true), - next(10, false), - next(10, true), - next(20, false), - next(20, true), + Recorded.next(0, true), + Recorded.next(10, false), + Recorded.next(10, true), + Recorded.next(20, false), + Recorded.next(20, true), ]) } it("executing until error returns") { XCTAssertEqual(executing.events, [ - next(0, false), - next(10, true), - next(10, false), - next(20, true), - next(20, false), + Recorded.next(0, false), + Recorded.next(10, true), + Recorded.next(10, false), + Recorded.next(20, true), + Recorded.next(20, false), ]) } @@ -430,8 +430,8 @@ class ActionTests: QuickSpec { it("errors observable receives generated errors") { XCTAssertEqual(errors.events, [ - next(10, .notEnabled), - next(20, .notEnabled), + Recorded.next(10, .notEnabled), + Recorded.next(20, .notEnabled), ]) } @@ -441,13 +441,13 @@ class ActionTests: QuickSpec { it("disabled") { XCTAssertEqual(enabled.events, [ - next(0, false), + Recorded.next(0, false), ]) } it("never be executing") { XCTAssertEqual(executing.events, [ - next(0, false), + Recorded.next(0, false), ]) } @@ -522,10 +522,10 @@ class ActionTests: QuickSpec { it("element receives single value for each execution") { XCTAssertEqual(element.events, [ - next(10, "a"), - completed(10), - next(20, "b"), - completed(20), + Recorded.next(10, "a"), + Recorded.completed(10), + Recorded.next(20, "b"), + Recorded.completed(20), ]) } @@ -542,14 +542,14 @@ class ActionTests: QuickSpec { it("element receives 3 values for each execution") { XCTAssertEqual(element.events, [ - next(10, "a"), - next(10, "a"), - next(10, "a"), - completed(10), - next(20, "b"), - next(20, "b"), - next(20, "b"), - completed(20), + Recorded.next(10, "a"), + Recorded.next(10, "a"), + Recorded.next(10, "a"), + Recorded.completed(10), + Recorded.next(20, "b"), + Recorded.next(20, "b"), + Recorded.next(20, "b"), + Recorded.completed(20), ]) } @@ -566,8 +566,8 @@ class ActionTests: QuickSpec { it("element fails with underlyingError") { XCTAssertEqual(element.events, [ - error(10, ActionError.underlyingError(TestError)), - error(20, ActionError.underlyingError(TestError)), + Recorded.error(10, ActionError.underlyingError(TestError)), + Recorded.error(20, ActionError.underlyingError(TestError)), ]) } @@ -584,8 +584,8 @@ class ActionTests: QuickSpec { it("element fails with notEnabled") { XCTAssertEqual(element.events, [ - error(10, ActionError.notEnabled), - error(20, ActionError.notEnabled), + Recorded.error(10, ActionError.notEnabled), + Recorded.error(20, ActionError.notEnabled), ]) } @@ -632,14 +632,14 @@ class ActionTests: QuickSpec { it("first element receives single value") { XCTAssertEqual(element.events, [ - next(30, "a"), - completed(30), + Recorded.next(30, "a"), + Recorded.completed(30), ]) } it("second element fails with notEnabled error") { XCTAssertEqual(secondElement.events, [ - error(20, ActionError.notEnabled) + Recorded.error(20, ActionError.notEnabled) ]) } From fc7662526f6bc61c45eff7da726d66973cbe41af Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Wed, 1 May 2019 12:27:12 -0300 Subject: [PATCH 11/76] Swiftlint autocorrect on test directory. --- Tests/ActionTests/ActionTests.swift | 217 +++++++++++----------- Tests/iOS-Tests/AlertActionTests.swift | 12 +- Tests/iOS-Tests/BarButtonTests.swift | 50 ++--- Tests/iOS-Tests/BindToTests.swift | 39 ++-- Tests/iOS-Tests/ButtonTests.swift | 6 +- Tests/iOS-Tests/RefreshControlTests.swift | 19 +- Tests/macOS-Tests/BindToTests.swift | 18 +- Tests/macOS-Tests/NSButtonTests.swift | 65 ++++--- 8 files changed, 210 insertions(+), 216 deletions(-) diff --git a/Tests/ActionTests/ActionTests.swift b/Tests/ActionTests/ActionTests.swift index 82502d57..2bba9007 100644 --- a/Tests/ActionTests/ActionTests.swift +++ b/Tests/ActionTests/ActionTests.swift @@ -9,7 +9,7 @@ class ActionTests: QuickSpec { override func spec() { var scheduler: TestScheduler! var disposeBag: DisposeBag! - + beforeEach { scheduler = TestScheduler(initialClock: 0) disposeBag = DisposeBag() @@ -34,7 +34,7 @@ class ActionTests: QuickSpec { scheduler.start() XCTAssertEqual(inputs.events, [ Recorded.next(10, "a"), - Recorded.next(20, "b"), + Recorded.next(20, "b") ]) } it("emits nothing on `elements`") { @@ -117,7 +117,6 @@ class ActionTests: QuickSpec { XCTAssertEqual(executions.events.filter { $0.value.isStopEvent }.count, 0) } } - describe("action properties") { var inputs: TestableObserver! var elements: TestableObserver! @@ -126,7 +125,7 @@ class ActionTests: QuickSpec { var executing: TestableObserver! var executionObservables: TestableObserver>! var underlyingError: TestableObserver! - + beforeEach { inputs = scheduler.createObserver(String.self) elements = scheduler.createObserver(String.self) @@ -147,23 +146,23 @@ class ActionTests: QuickSpec { action.elements .bind(to: elements) .disposed(by: disposeBag) - + action.errors .bind(to: errors) .disposed(by: disposeBag) - + action.enabled .bind(to: enabled) .disposed(by: disposeBag) - + action.executing .bind(to: executing) .disposed(by: disposeBag) - + action.executionObservables .bind(to: executionObservables) .disposed(by: disposeBag) - + action.underlyingError .bind(to: underlyingError) .disposed(by: disposeBag) @@ -178,88 +177,88 @@ class ActionTests: QuickSpec { return action } - + describe("single element action") { sharedExamples("send elements to elements observable") { it("work factory receives inputs") { XCTAssertEqual(inputs.events, [ Recorded.next(10, "a"), - Recorded.next(20, "b"), + Recorded.next(20, "b") ]) } - + it("elements observable receives generated elements") { XCTAssertEqual(elements.events, [ Recorded.next(10, "a"), - Recorded.next(20, "b"), + Recorded.next(20, "b") ]) } - + it("errors observable receives nothing") { XCTAssertEqual(errors.events, []) } - + it("disabled until element returns") { XCTAssertEqual(enabled.events, [ Recorded.next(0, true), Recorded.next(10, false), Recorded.next(10, true), Recorded.next(20, false), - Recorded.next(20, true), + Recorded.next(20, true) ]) } - + it("executing until element returns") { XCTAssertEqual(executing.events, [ Recorded.next(0, false), Recorded.next(10, true), Recorded.next(10, false), Recorded.next(20, true), - Recorded.next(20, false), + Recorded.next(20, false) ]) } - + it("executes twice") { expect(executionObservables.events.count) == 2 } } - + var action: Action! - + beforeEach { action = buildAction { Observable.just($0) } } - + context("trigger via inputs subject") { beforeEach { scheduler.scheduleAt(10) { action.inputs.onNext("a") } scheduler.scheduleAt(20) { action.inputs.onNext("b") } scheduler.start() } - + itBehavesLike("send elements to elements observable") } - + context("trigger via execute() method") { beforeEach { scheduler.scheduleAt(10) { action.execute("a") } scheduler.scheduleAt(20) { action.execute("b") } scheduler.start() } - + itBehavesLike("send elements to elements observable") } } - + describe("multiple element action") { sharedExamples("send array elements to elements observable") { it("work factory receives inputs") { XCTAssertEqual(inputs.events, [ Recorded.next(10, "a"), - Recorded.next(20, "b"), + Recorded.next(20, "b") ]) } - + it("elements observable receives generated elements") { XCTAssertEqual(elements.events, [ Recorded.next(10, "a"), @@ -267,41 +266,41 @@ class ActionTests: QuickSpec { Recorded.next(10, "c"), Recorded.next(20, "b"), Recorded.next(20, "c"), - Recorded.next(20, "d"), + Recorded.next(20, "d") ]) } - + it("errors observable receives nothing") { XCTAssertEqual(errors.events, []) } - + it("disabled until element returns") { XCTAssertEqual(enabled.events, [ Recorded.next(0, true), Recorded.next(10, false), Recorded.next(10, true), Recorded.next(20, false), - Recorded.next(20, true), + Recorded.next(20, true) ]) } - + it("executing until element returns") { XCTAssertEqual(executing.events, [ Recorded.next(0, false), Recorded.next(10, true), Recorded.next(10, false), Recorded.next(20, true), - Recorded.next(20, false), + Recorded.next(20, false) ]) } - + it("executes twice") { expect(executionObservables.events.count) == 2 } } - + var action: Action! - + beforeEach { action = buildAction { input in // "a" -> ["a", "b", "c"] @@ -309,237 +308,237 @@ class ActionTests: QuickSpec { let strings = (baseValue..<(baseValue + 3)) .compactMap { UnicodeScalar($0) } .map { String($0) } - + return Observable.from(strings) } } - + context("trigger via inputs subject") { beforeEach { scheduler.scheduleAt(10) { action.inputs.onNext("a") } scheduler.scheduleAt(20) { action.inputs.onNext("b") } scheduler.start() } - + itBehavesLike("send array elements to elements observable") } - + context("trigger via execute() method") { beforeEach { scheduler.scheduleAt(10) { action.execute("a") } scheduler.scheduleAt(20) { action.execute("b") } scheduler.start() } - + itBehavesLike("send array elements to elements observable") } } - + describe("error action") { sharedExamples("send errors to errors observable") { it("work factory receives inputs") { XCTAssertEqual(inputs.events, [ Recorded.next(10, "a"), - Recorded.next(20, "b"), + Recorded.next(20, "b") ]) } - + it("elements observable receives nothing") { XCTAssertEqual(elements.events, []) } - + it("errors observable receives generated errors") { XCTAssertEqual(errors.events, [ Recorded.next(10, .underlyingError(TestError)), - Recorded.next(20, .underlyingError(TestError)), + Recorded.next(20, .underlyingError(TestError)) ]) } - + it("underlyingError observable receives 2 generated errors") { XCTAssertEqual(underlyingError.events.count, 2) } - + it("underlyingError observable receives generated errors") { let first = underlyingError.events[0].value.element as! String let second = underlyingError.events[1].value.element as! String XCTAssertEqual(first, TestError) XCTAssertEqual(second, TestError) } - + it("disabled until error returns") { XCTAssertEqual(enabled.events, [ Recorded.next(0, true), Recorded.next(10, false), Recorded.next(10, true), Recorded.next(20, false), - Recorded.next(20, true), + Recorded.next(20, true) ]) } - + it("executing until error returns") { XCTAssertEqual(executing.events, [ Recorded.next(0, false), Recorded.next(10, true), Recorded.next(10, false), Recorded.next(20, true), - Recorded.next(20, false), + Recorded.next(20, false) ]) } - + it("executes twice") { expect(executionObservables.events.count) == 2 } } - + var action: Action! - + beforeEach { action = buildAction { _ in Observable.error(TestError) } } - + context("trigger via inputs subject") { beforeEach { scheduler.scheduleAt(10) { action.inputs.onNext("a") } scheduler.scheduleAt(20) { action.inputs.onNext("b") } scheduler.start() } - + itBehavesLike("send errors to errors observable") } - + context("trigger via execute() method") { beforeEach { scheduler.scheduleAt(10) { action.execute("a") } scheduler.scheduleAt(20) { action.execute("b") } scheduler.start() } - + itBehavesLike("send errors to errors observable") } } - + describe("disabled action") { sharedExamples("send notEnabled errors to errors observable") { it("work factory receives nothing") { XCTAssertEqual(inputs.events, []) } - + it("elements observable receives nothing") { XCTAssertEqual(elements.events, []) } - + it("errors observable receives generated errors") { XCTAssertEqual(errors.events, [ Recorded.next(10, .notEnabled), - Recorded.next(20, .notEnabled), + Recorded.next(20, .notEnabled) ]) } - + it("underlyingError observable receives zero generated errors") { XCTAssertEqual(underlyingError.events.count, 0) } - + it("disabled") { XCTAssertEqual(enabled.events, [ - Recorded.next(0, false), + Recorded.next(0, false) ]) } - + it("never be executing") { XCTAssertEqual(executing.events, [ - Recorded.next(0, false), + Recorded.next(0, false) ]) } - + it("never executes") { expect(executionObservables.events).to(beEmpty()) } } - + var action: Action! - + beforeEach { action = buildAction(enabledIf: Observable.just(false)) { Observable.just($0) } } - + context("trigger via inputs subject") { beforeEach { scheduler.scheduleAt(10) { action.inputs.onNext("a") } scheduler.scheduleAt(20) { action.inputs.onNext("b") } scheduler.start() } - + itBehavesLike("send notEnabled errors to errors observable") } - + context("trigger via execute() method") { beforeEach { scheduler.scheduleAt(10) { action.execute("a") } scheduler.scheduleAt(20) { action.execute("b") } scheduler.start() } - + itBehavesLike("send notEnabled errors to errors observable") } } } - + describe("execute function return value") { var action: Action! var element: TestableObserver! var executionObservables: TestableObserver>! - + beforeEach { element = scheduler.createObserver(String.self) executionObservables = scheduler.createObserver(Observable.self) } - + func bindAndExecuteTwice(action: Action) { action.executionObservables .bind(to: executionObservables) .disposed(by: disposeBag) - + scheduler.scheduleAt(10) { action.execute("a") .bind(to: element) .disposed(by: disposeBag) } - + scheduler.scheduleAt(20) { action.execute("b") .bind(to: element) .disposed(by: disposeBag) } - + scheduler.start() } - + context("single element action") { beforeEach { action = Action { Observable.just($0) } bindAndExecuteTwice(action: action) } - + it("element receives single value for each execution") { XCTAssertEqual(element.events, [ Recorded.next(10, "a"), Recorded.completed(10), Recorded.next(20, "b"), - Recorded.completed(20), + Recorded.completed(20) ]) } - + it("executes twice") { expect(executionObservables.events.count) == 2 } } - + context("multiple element action") { beforeEach { action = Action { Observable.of($0, $0, $0) } bindAndExecuteTwice(action: action) } - + it("element receives 3 values for each execution") { XCTAssertEqual(element.events, [ Recorded.next(10, "a"), @@ -549,76 +548,76 @@ class ActionTests: QuickSpec { Recorded.next(20, "b"), Recorded.next(20, "b"), Recorded.next(20, "b"), - Recorded.completed(20), + Recorded.completed(20) ]) } - + it("executes twice") { expect(executionObservables.events.count) == 2 } } - + context("error action") { beforeEach { action = Action { _ in Observable.error(TestError) } bindAndExecuteTwice(action: action) } - + it("element fails with underlyingError") { XCTAssertEqual(element.events, [ Recorded.error(10, ActionError.underlyingError(TestError)), - Recorded.error(20, ActionError.underlyingError(TestError)), + Recorded.error(20, ActionError.underlyingError(TestError)) ]) } - + it("executes twice") { expect(executionObservables.events.count) == 2 } } - + context("disabled") { beforeEach { action = Action(enabledIf: Observable.just(false)) { Observable.just($0) } bindAndExecuteTwice(action: action) } - + it("element fails with notEnabled") { XCTAssertEqual(element.events, [ Recorded.error(10, ActionError.notEnabled), - Recorded.error(20, ActionError.notEnabled), + Recorded.error(20, ActionError.notEnabled) ]) } - + it("never executes") { expect(executionObservables.events).to(beEmpty()) } } - + context("execute while executing") { var secondElement: TestableObserver! var trigger: PublishSubject! - + beforeEach { secondElement = scheduler.createObserver(String.self) trigger = PublishSubject() action = Action { Observable.just($0).sample(trigger) } - + action.executionObservables .bind(to: executionObservables) .disposed(by: disposeBag) - + scheduler.scheduleAt(10) { action.execute("a") .bind(to: element) .disposed(by: disposeBag) } - + scheduler.scheduleAt(20) { action.execute("b") .bind(to: secondElement) .disposed(by: disposeBag) } - + scheduler.scheduleAt(30) { #if swift(>=3.2) trigger.onNext(()) @@ -626,23 +625,23 @@ class ActionTests: QuickSpec { trigger.onNext() #endif } - + scheduler.start() } - + it("first element receives single value") { XCTAssertEqual(element.events, [ Recorded.next(30, "a"), - Recorded.completed(30), + Recorded.completed(30) ]) } - + it("second element fails with notEnabled error") { XCTAssertEqual(secondElement.events, [ Recorded.error(20, ActionError.notEnabled) ]) } - + it("executes once") { expect(executionObservables.events.count) == 1 } diff --git a/Tests/iOS-Tests/AlertActionTests.swift b/Tests/iOS-Tests/AlertActionTests.swift index 88ef4884..c4b8ab47 100644 --- a/Tests/iOS-Tests/AlertActionTests.swift +++ b/Tests/iOS-Tests/AlertActionTests.swift @@ -57,17 +57,17 @@ class AlertActionTests: QuickSpec { expect(subject.isEnabled) == false } - + it("disposes of old action subscriptions when re-set") { var subject = UIAlertAction.Action("Hi", style: .default) - + var disposed = false autoreleasepool { let disposeBag = DisposeBag() - + let action = emptyAction() subject.rx.action = action - + action .elements .subscribe(onNext: nil, onError: nil, onCompleted: nil, onDisposed: { @@ -75,9 +75,9 @@ class AlertActionTests: QuickSpec { }) .disposed(by: disposeBag) } - + subject.rx.action = nil - + expect(disposed) == true } } diff --git a/Tests/iOS-Tests/BarButtonTests.swift b/Tests/iOS-Tests/BarButtonTests.swift index 6bb9c679..062d98d7 100644 --- a/Tests/iOS-Tests/BarButtonTests.swift +++ b/Tests/iOS-Tests/BarButtonTests.swift @@ -6,25 +6,25 @@ import Action class BarButtonTests: QuickSpec { override func spec() { - + it("is nil by default") { let subject = UIBarButtonItem(barButtonSystemItem: .save, target: nil, action: nil) expect(subject.rx.action).to( beNil() ) } - + it("respects setter") { var subject = UIBarButtonItem(barButtonSystemItem: .save, target: nil, action: nil) - + let action = emptyAction() - + subject.rx.action = action - + expect(subject.rx.action) === action } - + it("disables the button while executing") { var subject = UIBarButtonItem(barButtonSystemItem: .save, target: nil, action: nil) - + var observer: AnyObserver! let action = CocoaAction(workFactory: { _ in return Observable.create { (obsv) -> Disposable in @@ -32,28 +32,28 @@ class BarButtonTests: QuickSpec { return Disposables.create() } }) - + subject.rx.action = action - + action.execute() expect(subject.isEnabled).toEventually( beFalse() ) - + observer.onCompleted() expect(subject.isEnabled).toEventually( beTrue() ) } - + it("disables the button if the Action is disabled") { var subject = UIBarButtonItem(barButtonSystemItem: .save, target: nil, action: nil) - + subject.rx.action = emptyAction(.just(false)) expect(subject.target).toEventuallyNot( beNil() ) - + expect(subject.isEnabled) == false } - + it("doesn't execute a disabled action when tapped") { var subject = UIBarButtonItem(barButtonSystemItem: .save, target: nil, action: nil) - + var executed = false subject.rx.action = CocoaAction(enabledIf: .just(false), workFactory: { _ in executed = true @@ -61,13 +61,13 @@ class BarButtonTests: QuickSpec { }) _ = subject.target?.perform(subject.action, with: subject) - + expect(executed) == false } - + it("executes the action when tapped") { var subject = UIBarButtonItem(barButtonSystemItem: .save, target: nil, action: nil) - + var executed = false let action = CocoaAction(workFactory: { _ in executed = true @@ -78,20 +78,20 @@ class BarButtonTests: QuickSpec { expect(subject.target).toEventuallyNot( beNil() ) _ = subject.target?.perform(subject.action, with: subject) - + expect(executed) == true } - + it("disposes of old action subscriptions when re-set") { var subject = UIBarButtonItem(barButtonSystemItem: .save, target: nil, action: nil) - + var disposed = false autoreleasepool { let disposeBag = DisposeBag() - + let action = emptyAction() subject.rx.action = action - + action .elements .subscribe(onNext: nil, onError: nil, onCompleted: nil, onDisposed: { @@ -99,9 +99,9 @@ class BarButtonTests: QuickSpec { }) .disposed(by: disposeBag) } - + subject.rx.action = nil - + expect(disposed) == true } } diff --git a/Tests/iOS-Tests/BindToTests.swift b/Tests/iOS-Tests/BindToTests.swift index 552be95c..5134f1f0 100644 --- a/Tests/iOS-Tests/BindToTests.swift +++ b/Tests/iOS-Tests/BindToTests.swift @@ -40,12 +40,12 @@ class BindToTests: QuickSpec { button.rx.bind(to: action, input: "Hi there!") // Setting the action has an asynchronous effect of adding a target. expect(button.allTargets.count) == 1 - + button.test_executeTap() - + expect(called).toEventually( beTrue() ) } - + it("activates a generic control event") { var called = false let button = UIButton() @@ -56,12 +56,12 @@ class BindToTests: QuickSpec { button.rx.bind(to: action, controlEvent: button.rx.tap, inputTransform: { input in "\(input)" }) // Setting the action has an asynchronous effect of adding a target. expect(button.allTargets.count) == 1 - + button.test_executeTap() - + expect(called).toEventually( beTrue() ) } - + it("actives a UIBarButtonItem") { var called = false let item = UIBarButtonItem() @@ -70,9 +70,9 @@ class BindToTests: QuickSpec { return .empty() }) item.rx.bind(to: action, input: "Hi there!") - + _ = item.target!.perform(item.action!, with: item) - + expect(called).toEventually( beTrue() ) } it("actives a UIRefreshControl") { @@ -83,12 +83,12 @@ class BindToTests: QuickSpec { return .empty() }) item.rx.bind(to: action, input: "Hi there!") - + item.test_executeRefresh() - + expect(called).toEventually( beTrue() ) } - + describe("unbinding") { it("unbinds actions for UIButton") { let button = UIButton() @@ -99,13 +99,13 @@ class BindToTests: QuickSpec { button.rx.bind(to: action, input: "Hi there!") // Setting the action has an asynchronous effect of adding a target. expect(button.allTargets.count) == 1 - + button.rx.unbindAction() button.test_executeTap() - + expect(button.allTargets.count) == 0 } - + it("unbinds actions for UIRefreshControl") { let refreshControl = UIRefreshControl() let action = Action(workFactory: { _ in @@ -115,13 +115,13 @@ class BindToTests: QuickSpec { refreshControl.rx.bind(to: action, input: "Hi there!") // Setting the action has an asynchronous effect of adding a target. expect(refreshControl.allTargets.count) == 1 - + refreshControl.rx.unbindAction() refreshControl.test_executeRefresh() - + expect(refreshControl.allTargets.count) == 0 } - + it("unbinds actions for UIBarButtonItem") { var called = false let item = UIBarButtonItem() @@ -130,13 +130,12 @@ class BindToTests: QuickSpec { return .empty() }) item.rx.bind(to: action, input: "Hi there!") - + item.rx.unbindAction() _ = item.target?.perform(item.action!, with: item) - + expect(called).to( beFalse() ) } } } } - diff --git a/Tests/iOS-Tests/ButtonTests.swift b/Tests/iOS-Tests/ButtonTests.swift index feeddcee..4ae7f5b0 100644 --- a/Tests/iOS-Tests/ButtonTests.swift +++ b/Tests/iOS-Tests/ButtonTests.swift @@ -104,9 +104,9 @@ class ButtonTests: QuickSpec { expect(disposed) == true } - + it("cancels the observable if the button is deallocated") { - + var disposed = false waitUntil { done in @@ -125,7 +125,7 @@ class ButtonTests: QuickSpec { subject.rx.action?.execute() } } - + expect(disposed) == true } } diff --git a/Tests/iOS-Tests/RefreshControlTests.swift b/Tests/iOS-Tests/RefreshControlTests.swift index 18390056..9d95d820 100644 --- a/Tests/iOS-Tests/RefreshControlTests.swift +++ b/Tests/iOS-Tests/RefreshControlTests.swift @@ -47,7 +47,7 @@ class RefreshControlTests: QuickSpec { subject.rx.action = emptyAction(.just(false)) expect(subject.allTargets.count) == 1 - + expect(subject.isEnabled) == false } @@ -77,9 +77,9 @@ class RefreshControlTests: QuickSpec { // Setting the action has an asynchronous effect of adding a target. expect(subject.allTargets.count) == 1 - + subject.test_executeRefresh() - + expect(executed).toEventually( beTrue() ) } @@ -105,17 +105,17 @@ class RefreshControlTests: QuickSpec { expect(disposed) == true } - + it("disposes of old action subscriptions when re-set") { var subject = UIBarButtonItem(barButtonSystemItem: .save, target: nil, action: nil) - + var disposed = false autoreleasepool { let disposeBag = DisposeBag() - + let action = emptyAction() subject.rx.action = action - + action .elements .subscribe(onNext: nil, onError: nil, onCompleted: nil, onDisposed: { @@ -123,11 +123,10 @@ class RefreshControlTests: QuickSpec { }) .disposed(by: disposeBag) } - + subject.rx.action = nil - + expect(disposed) == true } } } - diff --git a/Tests/macOS-Tests/BindToTests.swift b/Tests/macOS-Tests/BindToTests.swift index 37ecc910..b12135f7 100644 --- a/Tests/macOS-Tests/BindToTests.swift +++ b/Tests/macOS-Tests/BindToTests.swift @@ -26,12 +26,12 @@ class BindToTests: QuickSpec { button.rx.bind(to: action, input: "Hi there!") // Setting the action has an asynchronous effect of adding a target. expect(button.target).toEventuallyNot( beNil() ) - + button.test_executeAction() - + expect(called).toEventually( beTrue() ) } - + it("activates a generic control event") { var called = false let button = NSButton() @@ -42,13 +42,12 @@ class BindToTests: QuickSpec { button.rx.bind(to: action, controlEvent: button.rx.tap, inputTransform: { input in "\(input)" }) // Setting the action has an asynchronous effect of adding a target. expect(button.target).toEventuallyNot( beNil() ) - + button.test_executeAction() - + expect(called).toEventually( beTrue() ) } - - + describe("unbinding") { it("unbinds actions for UIButton") { let button = NSButton() @@ -59,13 +58,12 @@ class BindToTests: QuickSpec { button.rx.bind(to: action, input: "Hi there!") // Setting the action has an asynchronous effect of adding a target. expect(button.target).toEventuallyNot( beNil() ) - + button.rx.unbindAction() button.test_executeAction() - + expect(button.target).toEventually( beNil() ) } } } } - diff --git a/Tests/macOS-Tests/NSButtonTests.swift b/Tests/macOS-Tests/NSButtonTests.swift index 1c626430..b6abbe8f 100644 --- a/Tests/macOS-Tests/NSButtonTests.swift +++ b/Tests/macOS-Tests/NSButtonTests.swift @@ -7,25 +7,25 @@ import Action class NSButtonTests: QuickSpec { override func spec() { - + it("is nil by default") { let subject = NSButton() expect(subject.rx.action).to( beNil() ) } - + it("respects setter") { var subject = NSButton() - + let action = emptyAction() - + subject.rx.action = action - + expect(subject.rx.action) === action } - + it("disables the button while executing") { var subject = NSButton() - + var observer: AnyObserver! let action = CocoaAction(workFactory: { _ in return Observable.create { (obsv) -> Disposable in @@ -33,66 +33,66 @@ class NSButtonTests: QuickSpec { return Disposables.create() } }) - + subject.rx.action = action - + action.execute(()) expect(subject.isEnabled).toEventually( beFalse() ) - + observer.onCompleted() expect(subject.isEnabled).toEventually( beTrue() ) } - + it("disables the button if the Action is disabled") { var subject = NSButton() subject.rx.action = emptyAction(.just(false)) expect(subject.target).toEventuallyNot( beNil() ) - + expect(subject.isEnabled) == false } - + it("doesn't execute a disabled action when tapped") { var subject = NSButton() - + var executed = false subject.rx.action = CocoaAction(enabledIf: .just(false), workFactory: { _ in executed = true return .empty() }) - + subject.sendAction(on: .leftMouseDown) - + expect(executed) == false } - + it("executes the action when tapped") { var subject = NSButton() - + var executed = false let action = CocoaAction(workFactory: { _ in executed = true return .empty() }) subject.rx.action = action - + // Setting the action has an asynchronous effect of adding a target. expect(subject.target).toEventuallyNot( beNil() ) - + subject.test_executeAction() - + expect(executed).toEventually( beTrue() ) } - + it("disposes of old action subscriptions when re-set") { var subject = NSButton() - + var disposed = false autoreleasepool { let disposeBag = DisposeBag() - + let action = emptyAction() subject.rx.action = action - + action .elements .subscribe(onNext: nil, onError: nil, onCompleted: nil, onDisposed: { @@ -100,16 +100,16 @@ class NSButtonTests: QuickSpec { }) .disposed(by: disposeBag) } - + subject.rx.action = nil - + expect(disposed) == true } - + it("cancels the observable if the button is deallocated") { - + var disposed = false - + waitUntil { done in autoreleasepool { var subject = NSButton() @@ -121,7 +121,7 @@ class NSButtonTests: QuickSpec { } } } - + subject.rx.action = action #if swift(>=3.2) subject.rx.action?.execute(()) @@ -130,7 +130,7 @@ class NSButtonTests: QuickSpec { #endif } } - + expect(disposed) == true } } @@ -141,4 +141,3 @@ func emptyAction(_ enabledIf: Observable = .just(true)) -> CocoaAction { return .empty() }) } - From 0847f6479eb6032705858e76b9523f62e587808b Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Wed, 1 May 2019 14:21:19 -0300 Subject: [PATCH 12/76] Update all to 5.0 --- Action.podspec | 4 ++-- Cartfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Action.podspec b/Action.podspec index 3cb08116..d52bff15 100644 --- a/Action.podspec +++ b/Action.podspec @@ -20,8 +20,8 @@ Pod::Spec.new do |s| s.source_files = "Sources/**/*.{swift}" s.frameworks = "Foundation" - s.dependency "RxSwift", "~> 5.0.0" - s.dependency "RxCocoa", "~> 5.0.0" + s.dependency "RxSwift", "~> 5.0" + s.dependency "RxCocoa", "~> 5.0" s.watchos.exclude_files = "Control+Action.swift", "Button+Action.swift", "UIBarButtonItem+Action.swift", "UIAlertAction+Action.swift" s.osx.exclude_files = "UIBarButtonItem+Action.swift", "UIAlertAction+Action.swift" diff --git a/Cartfile b/Cartfile index 43e186b7..6a8bfd62 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveX/RxSwift" ~> 5.0.0 +github "ReactiveX/RxSwift" ~> 5.0 From 5c6ec0b167e188f6b9eac9a882354cba53ac76d5 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Wed, 1 May 2019 14:40:02 -0300 Subject: [PATCH 13/76] Recorded.events() --- Tests/ActionTests/ActionTests.swift | 224 +++++++++++++++------------- 1 file changed, 123 insertions(+), 101 deletions(-) diff --git a/Tests/ActionTests/ActionTests.swift b/Tests/ActionTests/ActionTests.swift index 2bba9007..f6558aa4 100644 --- a/Tests/ActionTests/ActionTests.swift +++ b/Tests/ActionTests/ActionTests.swift @@ -32,10 +32,11 @@ class ActionTests: QuickSpec { } it("receives generated inputs") { scheduler.start() - XCTAssertEqual(inputs.events, [ - Recorded.next(10, "a"), - Recorded.next(20, "b") + XCTAssertEqual(inputs.events, Recorded.events([ + .next(10, "a"), + .next(20, "b") ]) + ) } it("emits nothing on `elements`") { let elements = scheduler.createObserver(Never.self) @@ -181,17 +182,19 @@ class ActionTests: QuickSpec { describe("single element action") { sharedExamples("send elements to elements observable") { it("work factory receives inputs") { - XCTAssertEqual(inputs.events, [ - Recorded.next(10, "a"), - Recorded.next(20, "b") + XCTAssertEqual(inputs.events, Recorded.events([ + .next(10, "a"), + .next(20, "b") ]) + ) } it("elements observable receives generated elements") { - XCTAssertEqual(elements.events, [ - Recorded.next(10, "a"), - Recorded.next(20, "b") + XCTAssertEqual(elements.events, Recorded.events([ + .next(10, "a"), + .next(20, "b") ]) + ) } it("errors observable receives nothing") { @@ -199,23 +202,25 @@ class ActionTests: QuickSpec { } it("disabled until element returns") { - XCTAssertEqual(enabled.events, [ - Recorded.next(0, true), - Recorded.next(10, false), - Recorded.next(10, true), - Recorded.next(20, false), - Recorded.next(20, true) + XCTAssertEqual(enabled.events, Recorded.events([ + .next(0, true), + .next(10, false), + .next(10, true), + .next(20, false), + .next(20, true) ]) + ) } it("executing until element returns") { - XCTAssertEqual(executing.events, [ - Recorded.next(0, false), - Recorded.next(10, true), - Recorded.next(10, false), - Recorded.next(20, true), - Recorded.next(20, false) + XCTAssertEqual(executing.events, Recorded.events([ + .next(0, false), + .next(10, true), + .next(10, false), + .next(20, true), + .next(20, false) ]) + ) } it("executes twice") { @@ -253,21 +258,23 @@ class ActionTests: QuickSpec { describe("multiple element action") { sharedExamples("send array elements to elements observable") { it("work factory receives inputs") { - XCTAssertEqual(inputs.events, [ - Recorded.next(10, "a"), - Recorded.next(20, "b") - ]) + XCTAssertEqual(inputs.events, Recorded.events([ + .next(10, "a"), + .next(20, "b") + ]) + ) } it("elements observable receives generated elements") { - XCTAssertEqual(elements.events, [ - Recorded.next(10, "a"), - Recorded.next(10, "b"), - Recorded.next(10, "c"), - Recorded.next(20, "b"), - Recorded.next(20, "c"), - Recorded.next(20, "d") + XCTAssertEqual(elements.events, Recorded.events([ + .next(10, "a"), + .next(10, "b"), + .next(10, "c"), + .next(20, "b"), + .next(20, "c"), + .next(20, "d") ]) + ) } it("errors observable receives nothing") { @@ -275,23 +282,25 @@ class ActionTests: QuickSpec { } it("disabled until element returns") { - XCTAssertEqual(enabled.events, [ - Recorded.next(0, true), - Recorded.next(10, false), - Recorded.next(10, true), - Recorded.next(20, false), - Recorded.next(20, true) + XCTAssertEqual(enabled.events, Recorded.events([ + .next(0, true), + .next(10, false), + .next(10, true), + .next(20, false), + .next(20, true) ]) + ) } it("executing until element returns") { - XCTAssertEqual(executing.events, [ - Recorded.next(0, false), - Recorded.next(10, true), - Recorded.next(10, false), - Recorded.next(20, true), - Recorded.next(20, false) + XCTAssertEqual(executing.events, Recorded.events([ + .next(0, false), + .next(10, true), + .next(10, false), + .next(20, true), + .next(20, false) ]) + ) } it("executes twice") { @@ -337,10 +346,11 @@ class ActionTests: QuickSpec { describe("error action") { sharedExamples("send errors to errors observable") { it("work factory receives inputs") { - XCTAssertEqual(inputs.events, [ - Recorded.next(10, "a"), - Recorded.next(20, "b") - ]) + XCTAssertEqual(inputs.events, Recorded.events([ + .next(10, "a"), + .next(20, "b") + ]) + ) } it("elements observable receives nothing") { @@ -348,10 +358,11 @@ class ActionTests: QuickSpec { } it("errors observable receives generated errors") { - XCTAssertEqual(errors.events, [ - Recorded.next(10, .underlyingError(TestError)), - Recorded.next(20, .underlyingError(TestError)) + XCTAssertEqual(errors.events, Recorded.events([ + .next(10, .underlyingError(TestError)), + .next(20, .underlyingError(TestError)) ]) + ) } it("underlyingError observable receives 2 generated errors") { @@ -366,23 +377,25 @@ class ActionTests: QuickSpec { } it("disabled until error returns") { - XCTAssertEqual(enabled.events, [ - Recorded.next(0, true), - Recorded.next(10, false), - Recorded.next(10, true), - Recorded.next(20, false), - Recorded.next(20, true) + XCTAssertEqual(enabled.events, Recorded.events([ + .next(0, true), + .next(10, false), + .next(10, true), + .next(20, false), + .next(20, true) ]) + ) } it("executing until error returns") { - XCTAssertEqual(executing.events, [ - Recorded.next(0, false), - Recorded.next(10, true), - Recorded.next(10, false), - Recorded.next(20, true), - Recorded.next(20, false) + XCTAssertEqual(executing.events, Recorded.events([ + .next(0, false), + .next(10, true), + .next(10, false), + .next(20, true), + .next(20, false) ]) + ) } it("executes twice") { @@ -428,10 +441,11 @@ class ActionTests: QuickSpec { } it("errors observable receives generated errors") { - XCTAssertEqual(errors.events, [ - Recorded.next(10, .notEnabled), - Recorded.next(20, .notEnabled) + XCTAssertEqual(errors.events, Recorded.events([ + .next(10, .notEnabled), + .next(20, .notEnabled) ]) + ) } it("underlyingError observable receives zero generated errors") { @@ -439,15 +453,17 @@ class ActionTests: QuickSpec { } it("disabled") { - XCTAssertEqual(enabled.events, [ - Recorded.next(0, false) + XCTAssertEqual(enabled.events, Recorded.events([ + .next(0, false) ]) + ) } it("never be executing") { - XCTAssertEqual(executing.events, [ - Recorded.next(0, false) + XCTAssertEqual(executing.events, Recorded.events([ + .next(0, false) ]) + ) } it("never executes") { @@ -519,14 +535,15 @@ class ActionTests: QuickSpec { bindAndExecuteTwice(action: action) } - it("element receives single value for each execution") { - XCTAssertEqual(element.events, [ - Recorded.next(10, "a"), - Recorded.completed(10), - Recorded.next(20, "b"), - Recorded.completed(20) - ]) - } + it("element receives single value for each execution") { + XCTAssertEqual(element.events, Recorded.events([ + .next(10, "a"), + .completed(10), + .next(20, "b"), + .completed(20) + ]) + ) + } it("executes twice") { expect(executionObservables.events.count) == 2 @@ -539,18 +556,19 @@ class ActionTests: QuickSpec { bindAndExecuteTwice(action: action) } - it("element receives 3 values for each execution") { - XCTAssertEqual(element.events, [ - Recorded.next(10, "a"), - Recorded.next(10, "a"), - Recorded.next(10, "a"), - Recorded.completed(10), - Recorded.next(20, "b"), - Recorded.next(20, "b"), - Recorded.next(20, "b"), - Recorded.completed(20) - ]) - } + it("element receives 3 values for each execution") { + XCTAssertEqual(element.events, Recorded.events([ + .next(10, "a"), + .next(10, "a"), + .next(10, "a"), + .completed(10), + .next(20, "b"), + .next(20, "b"), + .next(20, "b"), + .completed(20) + ]) + ) + } it("executes twice") { expect(executionObservables.events.count) == 2 @@ -564,10 +582,11 @@ class ActionTests: QuickSpec { } it("element fails with underlyingError") { - XCTAssertEqual(element.events, [ - Recorded.error(10, ActionError.underlyingError(TestError)), - Recorded.error(20, ActionError.underlyingError(TestError)) + XCTAssertEqual(element.events, Recorded.events([ + .error(10, ActionError.underlyingError(TestError)), + .error(20, ActionError.underlyingError(TestError)) ]) + ) } it("executes twice") { @@ -582,10 +601,11 @@ class ActionTests: QuickSpec { } it("element fails with notEnabled") { - XCTAssertEqual(element.events, [ - Recorded.error(10, ActionError.notEnabled), - Recorded.error(20, ActionError.notEnabled) + XCTAssertEqual(element.events, Recorded.events([ + .error(10, ActionError.notEnabled), + .error(20, ActionError.notEnabled) ]) + ) } it("never executes") { @@ -630,16 +650,18 @@ class ActionTests: QuickSpec { } it("first element receives single value") { - XCTAssertEqual(element.events, [ - Recorded.next(30, "a"), - Recorded.completed(30) + XCTAssertEqual(element.events, Recorded.events([ + .next(30, "a"), + .completed(30) ]) + ) } it("second element fails with notEnabled error") { - XCTAssertEqual(secondElement.events, [ - Recorded.error(20, ActionError.notEnabled) + XCTAssertEqual(secondElement.events, Recorded.events([ + .error(20, ActionError.notEnabled) ]) + ) } it("executes once") { From 27997ec39e42000f68177c849d38c1525e0a9649 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Wed, 1 May 2019 14:45:31 -0300 Subject: [PATCH 14/76] Xcode ctrl + i --- Tests/ActionTests/ActionTests.swift | 480 ++++++++++++++-------------- 1 file changed, 240 insertions(+), 240 deletions(-) diff --git a/Tests/ActionTests/ActionTests.swift b/Tests/ActionTests/ActionTests.swift index f6558aa4..e79e7ef7 100644 --- a/Tests/ActionTests/ActionTests.swift +++ b/Tests/ActionTests/ActionTests.swift @@ -6,15 +6,15 @@ import RxTest @testable import Action class ActionTests: QuickSpec { - override func spec() { - var scheduler: TestScheduler! - var disposeBag: DisposeBag! - - beforeEach { - scheduler = TestScheduler(initialClock: 0) - disposeBag = DisposeBag() - } - + override func spec() { + var scheduler: TestScheduler! + var disposeBag: DisposeBag! + + beforeEach { + scheduler = TestScheduler(initialClock: 0) + disposeBag = DisposeBag() + } + describe("completable action") { var inputs: TestableObserver! var action: CompletableAction! @@ -33,10 +33,10 @@ class ActionTests: QuickSpec { it("receives generated inputs") { scheduler.start() XCTAssertEqual(inputs.events, Recorded.events([ - .next(10, "a"), - .next(20, "b") + .next(10, "a"), + .next(20, "b") ]) - ) + ) } it("emits nothing on `elements`") { let elements = scheduler.createObserver(Never.self) @@ -126,7 +126,7 @@ class ActionTests: QuickSpec { var executing: TestableObserver! var executionObservables: TestableObserver>! var underlyingError: TestableObserver! - + beforeEach { inputs = scheduler.createObserver(String.self) elements = scheduler.createObserver(String.self) @@ -183,43 +183,43 @@ class ActionTests: QuickSpec { sharedExamples("send elements to elements observable") { it("work factory receives inputs") { XCTAssertEqual(inputs.events, Recorded.events([ - .next(10, "a"), - .next(20, "b") - ]) + .next(10, "a"), + .next(20, "b") + ]) ) - } - - it("elements observable receives generated elements") { - XCTAssertEqual(elements.events, Recorded.events([ - .next(10, "a"), - .next(20, "b") - ]) + } + + it("elements observable receives generated elements") { + XCTAssertEqual(elements.events, Recorded.events([ + .next(10, "a"), + .next(20, "b") + ]) ) - } - - it("errors observable receives nothing") { - XCTAssertEqual(errors.events, []) - } - - it("disabled until element returns") { - XCTAssertEqual(enabled.events, Recorded.events([ - .next(0, true), - .next(10, false), - .next(10, true), - .next(20, false), - .next(20, true) - ]) + } + + it("errors observable receives nothing") { + XCTAssertEqual(errors.events, []) + } + + it("disabled until element returns") { + XCTAssertEqual(enabled.events, Recorded.events([ + .next(0, true), + .next(10, false), + .next(10, true), + .next(20, false), + .next(20, true) + ]) ) - } - - it("executing until element returns") { - XCTAssertEqual(executing.events, Recorded.events([ - .next(0, false), - .next(10, true), - .next(10, false), - .next(20, true), - .next(20, false) - ]) + } + + it("executing until element returns") { + XCTAssertEqual(executing.events, Recorded.events([ + .next(0, false), + .next(10, true), + .next(10, false), + .next(20, true), + .next(20, false) + ]) ) } @@ -263,43 +263,43 @@ class ActionTests: QuickSpec { .next(20, "b") ]) ) - } - - it("elements observable receives generated elements") { - XCTAssertEqual(elements.events, Recorded.events([ - .next(10, "a"), - .next(10, "b"), - .next(10, "c"), - .next(20, "b"), - .next(20, "c"), - .next(20, "d") - ]) + } + + it("elements observable receives generated elements") { + XCTAssertEqual(elements.events, Recorded.events([ + .next(10, "a"), + .next(10, "b"), + .next(10, "c"), + .next(20, "b"), + .next(20, "c"), + .next(20, "d") + ]) ) - } - - it("errors observable receives nothing") { - XCTAssertEqual(errors.events, []) - } - - it("disabled until element returns") { - XCTAssertEqual(enabled.events, Recorded.events([ - .next(0, true), - .next(10, false), - .next(10, true), - .next(20, false), - .next(20, true) - ]) + } + + it("errors observable receives nothing") { + XCTAssertEqual(errors.events, []) + } + + it("disabled until element returns") { + XCTAssertEqual(enabled.events, Recorded.events([ + .next(0, true), + .next(10, false), + .next(10, true), + .next(20, false), + .next(20, true) + ]) ) - } - - it("executing until element returns") { - XCTAssertEqual(executing.events, Recorded.events([ - .next(0, false), - .next(10, true), - .next(10, false), - .next(20, true), - .next(20, false) - ]) + } + + it("executing until element returns") { + XCTAssertEqual(executing.events, Recorded.events([ + .next(0, false), + .next(10, true), + .next(10, false), + .next(20, true), + .next(20, false) + ]) ) } @@ -351,50 +351,50 @@ class ActionTests: QuickSpec { .next(20, "b") ]) ) - } - - it("elements observable receives nothing") { - XCTAssertEqual(elements.events, []) - } - - it("errors observable receives generated errors") { + } + + it("elements observable receives nothing") { + XCTAssertEqual(elements.events, []) + } + + it("errors observable receives generated errors") { XCTAssertEqual(errors.events, Recorded.events([ - .next(10, .underlyingError(TestError)), - .next(20, .underlyingError(TestError)) - ]) + .next(10, .underlyingError(TestError)), + .next(20, .underlyingError(TestError)) + ]) ) - } - + } + it("underlyingError observable receives 2 generated errors") { XCTAssertEqual(underlyingError.events.count, 2) } - + it("underlyingError observable receives generated errors") { let first = underlyingError.events[0].value.element as! String let second = underlyingError.events[1].value.element as! String XCTAssertEqual(first, TestError) XCTAssertEqual(second, TestError) } - - it("disabled until error returns") { - XCTAssertEqual(enabled.events, Recorded.events([ - .next(0, true), - .next(10, false), - .next(10, true), - .next(20, false), - .next(20, true) - ]) + + it("disabled until error returns") { + XCTAssertEqual(enabled.events, Recorded.events([ + .next(0, true), + .next(10, false), + .next(10, true), + .next(20, false), + .next(20, true) + ]) ) - } - - it("executing until error returns") { - XCTAssertEqual(executing.events, Recorded.events([ - .next(0, false), - .next(10, true), - .next(10, false), - .next(20, true), - .next(20, false) - ]) + } + + it("executing until error returns") { + XCTAssertEqual(executing.events, Recorded.events([ + .next(0, false), + .next(10, true), + .next(10, false), + .next(20, true), + .next(20, false) + ]) ) } @@ -446,23 +446,23 @@ class ActionTests: QuickSpec { .next(20, .notEnabled) ]) ) - } - + } + it("underlyingError observable receives zero generated errors") { XCTAssertEqual(underlyingError.events.count, 0) } - - it("disabled") { - XCTAssertEqual(enabled.events, Recorded.events([ - .next(0, false) - ]) + + it("disabled") { + XCTAssertEqual(enabled.events, Recorded.events([ + .next(0, false) + ]) ) - } - - it("never be executing") { - XCTAssertEqual(executing.events, Recorded.events([ - .next(0, false) - ]) + } + + it("never be executing") { + XCTAssertEqual(executing.events, Recorded.events([ + .next(0, false) + ]) ) } @@ -544,18 +544,18 @@ class ActionTests: QuickSpec { ]) ) } - - it("executes twice") { - expect(executionObservables.events.count) == 2 - } - } - - context("multiple element action") { - beforeEach { - action = Action { Observable.of($0, $0, $0) } - bindAndExecuteTwice(action: action) - } - + + it("executes twice") { + expect(executionObservables.events.count) == 2 + } + } + + context("multiple element action") { + beforeEach { + action = Action { Observable.of($0, $0, $0) } + bindAndExecuteTwice(action: action) + } + it("element receives 3 values for each execution") { XCTAssertEqual(element.events, Recorded.events([ .next(10, "a"), @@ -569,121 +569,121 @@ class ActionTests: QuickSpec { ]) ) } - - it("executes twice") { - expect(executionObservables.events.count) == 2 - } - } - - context("error action") { - beforeEach { - action = Action { _ in Observable.error(TestError) } - bindAndExecuteTwice(action: action) - } - - it("element fails with underlyingError") { - XCTAssertEqual(element.events, Recorded.events([ + + it("executes twice") { + expect(executionObservables.events.count) == 2 + } + } + + context("error action") { + beforeEach { + action = Action { _ in Observable.error(TestError) } + bindAndExecuteTwice(action: action) + } + + it("element fails with underlyingError") { + XCTAssertEqual(element.events, Recorded.events([ .error(10, ActionError.underlyingError(TestError)), .error(20, ActionError.underlyingError(TestError)) - ]) + ]) ) - } - - it("executes twice") { - expect(executionObservables.events.count) == 2 - } - } - - context("disabled") { - beforeEach { - action = Action(enabledIf: Observable.just(false)) { Observable.just($0) } - bindAndExecuteTwice(action: action) - } - - it("element fails with notEnabled") { - XCTAssertEqual(element.events, Recorded.events([ + } + + it("executes twice") { + expect(executionObservables.events.count) == 2 + } + } + + context("disabled") { + beforeEach { + action = Action(enabledIf: Observable.just(false)) { Observable.just($0) } + bindAndExecuteTwice(action: action) + } + + it("element fails with notEnabled") { + XCTAssertEqual(element.events, Recorded.events([ .error(10, ActionError.notEnabled), .error(20, ActionError.notEnabled) - ]) + ]) ) - } - - it("never executes") { - expect(executionObservables.events).to(beEmpty()) - } - } - - context("execute while executing") { - var secondElement: TestableObserver! - var trigger: PublishSubject! - - beforeEach { - secondElement = scheduler.createObserver(String.self) - trigger = PublishSubject() - action = Action { Observable.just($0).sample(trigger) } - - action.executionObservables - .bind(to: executionObservables) - .disposed(by: disposeBag) - - scheduler.scheduleAt(10) { - action.execute("a") - .bind(to: element) - .disposed(by: disposeBag) - } - - scheduler.scheduleAt(20) { - action.execute("b") - .bind(to: secondElement) - .disposed(by: disposeBag) - } - - scheduler.scheduleAt(30) { - #if swift(>=3.2) - trigger.onNext(()) - #else - trigger.onNext() - #endif - } - - scheduler.start() - } - - it("first element receives single value") { - XCTAssertEqual(element.events, Recorded.events([ + } + + it("never executes") { + expect(executionObservables.events).to(beEmpty()) + } + } + + context("execute while executing") { + var secondElement: TestableObserver! + var trigger: PublishSubject! + + beforeEach { + secondElement = scheduler.createObserver(String.self) + trigger = PublishSubject() + action = Action { Observable.just($0).sample(trigger) } + + action.executionObservables + .bind(to: executionObservables) + .disposed(by: disposeBag) + + scheduler.scheduleAt(10) { + action.execute("a") + .bind(to: element) + .disposed(by: disposeBag) + } + + scheduler.scheduleAt(20) { + action.execute("b") + .bind(to: secondElement) + .disposed(by: disposeBag) + } + + scheduler.scheduleAt(30) { + #if swift(>=3.2) + trigger.onNext(()) + #else + trigger.onNext() + #endif + } + + scheduler.start() + } + + it("first element receives single value") { + XCTAssertEqual(element.events, Recorded.events([ .next(30, "a"), .completed(30) - ]) + ]) ) - } - - it("second element fails with notEnabled error") { - XCTAssertEqual(secondElement.events, Recorded.events([ + } + + it("second element fails with notEnabled error") { + XCTAssertEqual(secondElement.events, Recorded.events([ .error(20, ActionError.notEnabled) - ]) + ]) ) - } - - it("executes once") { - expect(executionObservables.events.count) == 1 - } - } - } - } + } + + it("executes once") { + expect(executionObservables.events.count) == 1 + } + } + } + } } extension ActionError: Equatable { - // Not accurate but convenient for testing. - public static func ==(lhs: ActionError, rhs: ActionError) -> Bool { - switch (lhs, rhs) { - case (.notEnabled, .notEnabled): - return true - case (.underlyingError, .underlyingError): - return true - default: - return false - } - } + // Not accurate but convenient for testing. + public static func ==(lhs: ActionError, rhs: ActionError) -> Bool { + switch (lhs, rhs) { + case (.notEnabled, .notEnabled): + return true + case (.underlyingError, .underlyingError): + return true + default: + return false + } + } } extension String: Error { } From d0055326e4bb12e61ff048784cd8479eb37c5cf6 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Wed, 1 May 2019 15:34:02 -0300 Subject: [PATCH 15/76] Align with XCT... --- Tests/ActionTests/ActionTests.swift | 144 ++++++++++++++-------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/Tests/ActionTests/ActionTests.swift b/Tests/ActionTests/ActionTests.swift index e79e7ef7..55512ee5 100644 --- a/Tests/ActionTests/ActionTests.swift +++ b/Tests/ActionTests/ActionTests.swift @@ -33,9 +33,9 @@ class ActionTests: QuickSpec { it("receives generated inputs") { scheduler.start() XCTAssertEqual(inputs.events, Recorded.events([ - .next(10, "a"), - .next(20, "b") - ]) + .next(10, "a"), + .next(20, "b") + ]) ) } it("emits nothing on `elements`") { @@ -214,12 +214,12 @@ class ActionTests: QuickSpec { it("executing until element returns") { XCTAssertEqual(executing.events, Recorded.events([ - .next(0, false), - .next(10, true), - .next(10, false), - .next(20, true), - .next(20, false) - ]) + .next(0, false), + .next(10, true), + .next(10, false), + .next(20, true), + .next(20, false) + ]) ) } @@ -267,13 +267,13 @@ class ActionTests: QuickSpec { it("elements observable receives generated elements") { XCTAssertEqual(elements.events, Recorded.events([ - .next(10, "a"), - .next(10, "b"), - .next(10, "c"), - .next(20, "b"), - .next(20, "c"), - .next(20, "d") - ]) + .next(10, "a"), + .next(10, "b"), + .next(10, "c"), + .next(20, "b"), + .next(20, "c"), + .next(20, "d") + ]) ) } @@ -283,23 +283,23 @@ class ActionTests: QuickSpec { it("disabled until element returns") { XCTAssertEqual(enabled.events, Recorded.events([ - .next(0, true), - .next(10, false), - .next(10, true), - .next(20, false), - .next(20, true) - ]) + .next(0, true), + .next(10, false), + .next(10, true), + .next(20, false), + .next(20, true) + ]) ) } it("executing until element returns") { XCTAssertEqual(executing.events, Recorded.events([ - .next(0, false), - .next(10, true), - .next(10, false), - .next(20, true), - .next(20, false) - ]) + .next(0, false), + .next(10, true), + .next(10, false), + .next(20, true), + .next(20, false) + ]) ) } @@ -359,9 +359,9 @@ class ActionTests: QuickSpec { it("errors observable receives generated errors") { XCTAssertEqual(errors.events, Recorded.events([ - .next(10, .underlyingError(TestError)), - .next(20, .underlyingError(TestError)) - ]) + .next(10, .underlyingError(TestError)), + .next(20, .underlyingError(TestError)) + ]) ) } @@ -378,23 +378,23 @@ class ActionTests: QuickSpec { it("disabled until error returns") { XCTAssertEqual(enabled.events, Recorded.events([ - .next(0, true), - .next(10, false), - .next(10, true), - .next(20, false), - .next(20, true) - ]) + .next(0, true), + .next(10, false), + .next(10, true), + .next(20, false), + .next(20, true) + ]) ) } it("executing until error returns") { XCTAssertEqual(executing.events, Recorded.events([ - .next(0, false), - .next(10, true), - .next(10, false), - .next(20, true), - .next(20, false) - ]) + .next(0, false), + .next(10, true), + .next(10, false), + .next(20, true), + .next(20, false) + ]) ) } @@ -454,15 +454,15 @@ class ActionTests: QuickSpec { it("disabled") { XCTAssertEqual(enabled.events, Recorded.events([ - .next(0, false) - ]) + .next(0, false) + ]) ) } it("never be executing") { XCTAssertEqual(executing.events, Recorded.events([ - .next(0, false) - ]) + .next(0, false) + ]) ) } @@ -537,11 +537,11 @@ class ActionTests: QuickSpec { it("element receives single value for each execution") { XCTAssertEqual(element.events, Recorded.events([ - .next(10, "a"), - .completed(10), - .next(20, "b"), - .completed(20) - ]) + .next(10, "a"), + .completed(10), + .next(20, "b"), + .completed(20) + ]) ) } @@ -558,15 +558,15 @@ class ActionTests: QuickSpec { it("element receives 3 values for each execution") { XCTAssertEqual(element.events, Recorded.events([ - .next(10, "a"), - .next(10, "a"), - .next(10, "a"), - .completed(10), - .next(20, "b"), - .next(20, "b"), - .next(20, "b"), - .completed(20) - ]) + .next(10, "a"), + .next(10, "a"), + .next(10, "a"), + .completed(10), + .next(20, "b"), + .next(20, "b"), + .next(20, "b"), + .completed(20) + ]) ) } @@ -583,9 +583,9 @@ class ActionTests: QuickSpec { it("element fails with underlyingError") { XCTAssertEqual(element.events, Recorded.events([ - .error(10, ActionError.underlyingError(TestError)), - .error(20, ActionError.underlyingError(TestError)) - ]) + .error(10, ActionError.underlyingError(TestError)), + .error(20, ActionError.underlyingError(TestError)) + ]) ) } @@ -602,9 +602,9 @@ class ActionTests: QuickSpec { it("element fails with notEnabled") { XCTAssertEqual(element.events, Recorded.events([ - .error(10, ActionError.notEnabled), - .error(20, ActionError.notEnabled) - ]) + .error(10, ActionError.notEnabled), + .error(20, ActionError.notEnabled) + ]) ) } @@ -651,16 +651,16 @@ class ActionTests: QuickSpec { it("first element receives single value") { XCTAssertEqual(element.events, Recorded.events([ - .next(30, "a"), - .completed(30) - ]) + .next(30, "a"), + .completed(30) + ]) ) } it("second element fails with notEnabled error") { XCTAssertEqual(secondElement.events, Recorded.events([ - .error(20, ActionError.notEnabled) - ]) + .error(20, ActionError.notEnabled) + ]) ) } From 956e0e673412c512b53a1bd1113b693ded8e7a93 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Wed, 1 May 2019 19:49:53 -0300 Subject: [PATCH 16/76] Adjustments on identation --- Tests/ActionTests/ActionTests.swift | 30 +++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/Tests/ActionTests/ActionTests.swift b/Tests/ActionTests/ActionTests.swift index 55512ee5..fa46768d 100644 --- a/Tests/ActionTests/ActionTests.swift +++ b/Tests/ActionTests/ActionTests.swift @@ -75,7 +75,10 @@ class ActionTests: QuickSpec { it("execute on .next") { scheduler.scheduleAt(10) { action.inputs.onNext("a") } scheduler.start() - XCTAssertEqual(inputs.events, [next(10, "a")]) + XCTAssertEqual(inputs.events, Recorded.events([ + .next(10, "a") + ]) + ) XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 1) } it("ignore .error events") { @@ -94,10 +97,11 @@ class ActionTests: QuickSpec { scheduler.scheduleAt(10) { action.inputs.onNext("a") } scheduler.scheduleAt(20) { action.inputs.onNext("b") } scheduler.start() - XCTAssertEqual(inputs.events, [ - next(10, "a"), - next(20, "b"), - ]) + XCTAssertEqual(inputs.events, Recorded.events([ + .next(10, "a"), + .next(20, "b"), + ]) + ) XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 2) XCTAssertEqual(executions.events.filter { $0.value.isStopEvent }.count, 0) } @@ -105,7 +109,10 @@ class ActionTests: QuickSpec { scheduler.scheduleAt(10) { action.inputs.onError(TestError) } scheduler.scheduleAt(20) { action.inputs.onNext("b") } scheduler.start() - XCTAssertEqual(inputs.events, [next(20, "b")]) + XCTAssertEqual(inputs.events, Recorded.events([ + .next(20, "b") + ]) + ) XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 1) XCTAssertEqual(executions.events.filter { $0.value.isStopEvent }.count, 0) } @@ -113,7 +120,10 @@ class ActionTests: QuickSpec { scheduler.scheduleAt(10) { action.inputs.onCompleted() } scheduler.scheduleAt(20) { action.inputs.onNext("b") } scheduler.start() - XCTAssertEqual(inputs.events, [next(20, "b")]) + XCTAssertEqual(inputs.events, Recorded.events([ + .next(20, "b") + ]) + ) XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 1) XCTAssertEqual(executions.events.filter { $0.value.isStopEvent }.count, 0) } @@ -442,9 +452,9 @@ class ActionTests: QuickSpec { it("errors observable receives generated errors") { XCTAssertEqual(errors.events, Recorded.events([ - .next(10, .notEnabled), - .next(20, .notEnabled) - ]) + .next(10, .notEnabled), + .next(20, .notEnabled) + ]) ) } From f656fec4ff9a7ec76eaef334ce0dc6ad84585fbb Mon Sep 17 00:00:00 2001 From: freak4pc Date: Thu, 2 May 2019 11:48:22 +0300 Subject: [PATCH 17/76] Action 4.0.0 --- Action.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Action.podspec b/Action.podspec index d52bff15..f065ec17 100644 --- a/Action.podspec +++ b/Action.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Action" - s.version = "3.11.0" + s.version = "4.0.0" s.summary = "Abstracts actions to be performed in RxSwift." s.description = <<-DESC Encapsulates an action to be performed, usually by a button press, but also useful to pass actions to execute later @@ -8,7 +8,7 @@ Pod::Spec.new do |s| DESC s.homepage = "https://github.com/RxSwiftCommunity/Action" s.license = { :type => "MIT", :file => "License.md" } - s.author = { "Ash Furrow" => "ash@ashfurrow.com" } + s.author = { "RxSwift Community" => "community@rxswift.org" } s.social_media_url = "http://twitter.com/ashfurrow" s.ios.deployment_target = '8.0' From b1e13581ba70265e2643ae3e4bb85deecb809c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sammy=20Guti=C3=A9rrez?= Date: Thu, 2 May 2019 10:40:37 -0500 Subject: [PATCH 18/76] Remove RxAtomic references --- Action.xcodeproj/project.pbxproj | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/Action.xcodeproj/project.pbxproj b/Action.xcodeproj/project.pbxproj index 8833aa0b..8dfc12c3 100644 --- a/Action.xcodeproj/project.pbxproj +++ b/Action.xcodeproj/project.pbxproj @@ -49,10 +49,8 @@ C41CE71F22283BBA0006E722 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE71A22283BB90006E722 /* Nimble.framework */; }; C41CE72022283BBA0006E722 /* RxBlocking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE71B22283BB90006E722 /* RxBlocking.framework */; }; C41CE72122283BBA0006E722 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE71C22283BB90006E722 /* Quick.framework */; }; - C41CE72222283BBA0006E722 /* RxAtomic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE71D22283BBA0006E722 /* RxAtomic.framework */; }; C41CE72322283BBA0006E722 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE71E22283BBA0006E722 /* RxSwift.framework */; }; C41CE72C22283BEE0006E722 /* iOS in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE72422283BEE0006E722 /* iOS */; }; - C41CE72D22283BEE0006E722 /* RxAtomic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE72522283BEE0006E722 /* RxAtomic.framework */; }; C41CE72E22283BEE0006E722 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE72622283BEE0006E722 /* Quick.framework */; }; C41CE72F22283BEE0006E722 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE72722283BEE0006E722 /* RxCocoa.framework */; }; C41CE73022283BEE0006E722 /* RxBlocking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE72822283BEE0006E722 /* RxBlocking.framework */; }; @@ -60,12 +58,8 @@ C41CE73222283BEE0006E722 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE72A22283BEE0006E722 /* RxSwift.framework */; }; C41CE73322283BEE0006E722 /* RxTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE72B22283BEE0006E722 /* RxTest.framework */; }; C41CE73622283CAD0006E722 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE71E22283BBA0006E722 /* RxSwift.framework */; }; - C41CE73722283CAD0006E722 /* RxAtomic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE71D22283BBA0006E722 /* RxAtomic.framework */; }; - C41CE73A22283CD00006E722 /* RxAtomic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE73822283CD00006E722 /* RxAtomic.framework */; }; C41CE73B22283CD00006E722 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE73922283CD00006E722 /* RxSwift.framework */; }; C41CE73C22283CF90006E722 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE72A22283BEE0006E722 /* RxSwift.framework */; }; - C41CE73D22283CF90006E722 /* RxAtomic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE72522283BEE0006E722 /* RxAtomic.framework */; }; - C41CE74022283D220006E722 /* RxAtomic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE73E22283D220006E722 /* RxAtomic.framework */; }; C41CE74122283D220006E722 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C41CE73F22283D220006E722 /* RxSwift.framework */; }; C41E08EE2237D26D0039D213 /* Action+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DE00042229758700FB50AB /* Action+Extensions.swift */; }; C41E08EF2237D2700039D213 /* Action+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DE00042229758700FB50AB /* Action+Extensions.swift */; }; @@ -73,7 +67,6 @@ C4DE00052229758700FB50AB /* Action+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DE00042229758700FB50AB /* Action+Extensions.swift */; }; FA3F973C1EDAF46F00A84787 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0569E21DE28587007E1D0D /* Action.swift */; }; FA3F973D1EDAF46F00A84787 /* Action+Internal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0569E01DE28587007E1D0D /* Action+Internal.swift */; }; - FA3F973E1EDAF46F00A84787 /* InputSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA2861C71ED6979400BB327A /* InputSubject.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -140,19 +133,15 @@ C41CE71A22283BB90006E722 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = ""; }; C41CE71B22283BB90006E722 /* RxBlocking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxBlocking.framework; path = Carthage/Build/iOS/RxBlocking.framework; sourceTree = ""; }; C41CE71C22283BB90006E722 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = ""; }; - C41CE71D22283BBA0006E722 /* RxAtomic.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxAtomic.framework; path = Carthage/Build/iOS/RxAtomic.framework; sourceTree = ""; }; C41CE71E22283BBA0006E722 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = ""; }; C41CE72422283BEE0006E722 /* iOS */ = {isa = PBXFileReference; lastKnownFileType = folder; name = iOS; path = Carthage/Build/iOS; sourceTree = ""; }; - C41CE72522283BEE0006E722 /* RxAtomic.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxAtomic.framework; path = Carthage/Build/Mac/RxAtomic.framework; sourceTree = ""; }; C41CE72622283BEE0006E722 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/Mac/Quick.framework; sourceTree = ""; }; C41CE72722283BEE0006E722 /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/Mac/RxCocoa.framework; sourceTree = ""; }; C41CE72822283BEE0006E722 /* RxBlocking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxBlocking.framework; path = Carthage/Build/Mac/RxBlocking.framework; sourceTree = ""; }; C41CE72922283BEE0006E722 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/Mac/Nimble.framework; sourceTree = ""; }; C41CE72A22283BEE0006E722 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/Mac/RxSwift.framework; sourceTree = ""; }; C41CE72B22283BEE0006E722 /* RxTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxTest.framework; path = Carthage/Build/Mac/RxTest.framework; sourceTree = ""; }; - C41CE73822283CD00006E722 /* RxAtomic.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxAtomic.framework; path = Carthage/Build/tvOS/RxAtomic.framework; sourceTree = ""; }; C41CE73922283CD00006E722 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/tvOS/RxSwift.framework; sourceTree = ""; }; - C41CE73E22283D220006E722 /* RxAtomic.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxAtomic.framework; path = Carthage/Build/watchOS/RxAtomic.framework; sourceTree = ""; }; C41CE73F22283D220006E722 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/watchOS/RxSwift.framework; sourceTree = ""; }; C4DE00042229758700FB50AB /* Action+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Action+Extensions.swift"; sourceTree = ""; }; C4E0263F20D119EE00C8164C /* Action.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = Action.podspec; sourceTree = ""; }; @@ -161,8 +150,6 @@ C4E0264220D11A3B00C8164C /* Readme.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Readme.md; sourceTree = ""; }; C4E0264320D11CDD00C8164C /* circle.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = circle.yml; sourceTree = ""; }; C4E0264420D1244900C8164C /* Changelog.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Changelog.md; sourceTree = ""; }; - FC9C4166215A99BE00541C4B /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = ""; }; - FC9C4167215A99BE00541C4B /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -170,7 +157,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C41CE73A22283CD00006E722 /* RxAtomic.framework in Frameworks */, C41CE73B22283CD00006E722 /* RxSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -180,7 +166,6 @@ buildActionMask = 2147483647; files = ( C41CE73C22283CF90006E722 /* RxSwift.framework in Frameworks */, - C41CE73D22283CF90006E722 /* RxAtomic.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -188,7 +173,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C41CE74022283D220006E722 /* RxAtomic.framework in Frameworks */, C41CE74122283D220006E722 /* RxSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -198,7 +182,6 @@ buildActionMask = 2147483647; files = ( C41CE72C22283BEE0006E722 /* iOS in Frameworks */, - C41CE72D22283BEE0006E722 /* RxAtomic.framework in Frameworks */, C41CE72E22283BEE0006E722 /* Quick.framework in Frameworks */, C41CE72F22283BEE0006E722 /* RxCocoa.framework in Frameworks */, C41CE73022283BEE0006E722 /* RxBlocking.framework in Frameworks */, @@ -216,7 +199,6 @@ C41CE71F22283BBA0006E722 /* Nimble.framework in Frameworks */, C41CE72022283BBA0006E722 /* RxBlocking.framework in Frameworks */, C41CE72122283BBA0006E722 /* Quick.framework in Frameworks */, - C41CE72222283BBA0006E722 /* RxAtomic.framework in Frameworks */, C41CE72322283BBA0006E722 /* RxSwift.framework in Frameworks */, 7F612AA01D7F103A00B93BC5 /* Action.framework in Frameworks */, ); @@ -235,7 +217,6 @@ buildActionMask = 2147483647; files = ( C41CE73622283CAD0006E722 /* RxSwift.framework in Frameworks */, - C41CE73722283CAD0006E722 /* RxAtomic.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -315,21 +296,17 @@ 7F5E6A5C1D7F06C4000B6076 /* Frameworks */ = { isa = PBXGroup; children = ( - C41CE73822283CD00006E722 /* RxAtomic.framework */, C41CE73922283CD00006E722 /* RxSwift.framework */, - C41CE73E22283D220006E722 /* RxAtomic.framework */, C41CE73F22283D220006E722 /* RxSwift.framework */, C41CE72422283BEE0006E722 /* iOS */, C41CE72922283BEE0006E722 /* Nimble.framework */, C41CE72622283BEE0006E722 /* Quick.framework */, - C41CE72522283BEE0006E722 /* RxAtomic.framework */, C41CE72822283BEE0006E722 /* RxBlocking.framework */, C41CE72722283BEE0006E722 /* RxCocoa.framework */, C41CE72A22283BEE0006E722 /* RxSwift.framework */, C41CE72B22283BEE0006E722 /* RxTest.framework */, C41CE71A22283BB90006E722 /* Nimble.framework */, C41CE71C22283BB90006E722 /* Quick.framework */, - C41CE71D22283BBA0006E722 /* RxAtomic.framework */, C41CE71B22283BB90006E722 /* RxBlocking.framework */, C41CE71E22283BBA0006E722 /* RxSwift.framework */, ); From f821c32c00e34c49352973cc862d60bd90de7980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sammy=20Guti=C3=A9rrez?= Date: Thu, 2 May 2019 10:41:46 -0500 Subject: [PATCH 19/76] Use Swift 5 in project settings --- Action.xcodeproj/project.pbxproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Action.xcodeproj/project.pbxproj b/Action.xcodeproj/project.pbxproj index 8dfc12c3..67cd3eb7 100644 --- a/Action.xcodeproj/project.pbxproj +++ b/Action.xcodeproj/project.pbxproj @@ -563,7 +563,7 @@ }; 7F5E6A661D7F08D2000B6076 = { CreatedOnToolsVersion = 8.0; - LastSwiftMigration = 1000; + LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; 7F612ACB1D7F13B800B93BC5 = { @@ -572,7 +572,7 @@ }; BE73AD1F1CDCD101006F8B98 = { CreatedOnToolsVersion = 7.3; - LastSwiftMigration = 1000; + LastSwiftMigration = 1020; }; }; }; @@ -1057,7 +1057,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -1077,7 +1077,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(PROJECT_DIR)/Carthage/Build/iOS"; PRODUCT_BUNDLE_IDENTIFIER = "-.Tests"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -1257,7 +1257,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -1281,7 +1281,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.ashfurrow.Action; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; From cdade63f7bbe1f5e1eff7779e5858a796dc2c001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sammy=20Guti=C3=A9rrez?= Date: Thu, 2 May 2019 14:58:41 -0500 Subject: [PATCH 20/76] Update changelog --- Changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Changelog.md b/Changelog.md index 9b63a952..f2c3df79 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,12 @@ Changelog Current master -------------- +- Added full support for Swift 5.0 +- Added full support for RxSwift 5.0 +- Remove RxAtomic references + +4.0.0 +------- - Add `completions` property to `CompletableAction` - Change `inputs` type to `AnyObserver` From e2144811f606a30844da5537dc8138d41d668fe1 Mon Sep 17 00:00:00 2001 From: bobgodwinx Date: Wed, 8 May 2019 18:25:40 +0200 Subject: [PATCH 21/76] Specs-Cleanup: Initial introduction of `Nimble+RxTest+Ext` and `TestEvents` --- Action.xcodeproj/project.pbxproj | 12 ++++++++++++ Tests/Nimble+RxTest+Ext.swift | 1 + Tests/TestEvents.swift | 2 ++ 3 files changed, 15 insertions(+) create mode 100644 Tests/Nimble+RxTest+Ext.swift create mode 100644 Tests/TestEvents.swift diff --git a/Action.xcodeproj/project.pbxproj b/Action.xcodeproj/project.pbxproj index 67cd3eb7..db7a3bd5 100644 --- a/Action.xcodeproj/project.pbxproj +++ b/Action.xcodeproj/project.pbxproj @@ -64,6 +64,10 @@ C41E08EE2237D26D0039D213 /* Action+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DE00042229758700FB50AB /* Action+Extensions.swift */; }; C41E08EF2237D2700039D213 /* Action+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DE00042229758700FB50AB /* Action+Extensions.swift */; }; C41E08F02237D2740039D213 /* Action+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DE00042229758700FB50AB /* Action+Extensions.swift */; }; + C4BFF33D2281D6E100D1AA27 /* Nimble+RxTest+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BFF33C2281D6E100D1AA27 /* Nimble+RxTest+Ext.swift */; }; + C4BFF33E2281D6E100D1AA27 /* Nimble+RxTest+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BFF33C2281D6E100D1AA27 /* Nimble+RxTest+Ext.swift */; }; + C4BFF3402282B79F00D1AA27 /* TestEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BFF33F2282B79F00D1AA27 /* TestEvents.swift */; }; + C4BFF3412282B79F00D1AA27 /* TestEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BFF33F2282B79F00D1AA27 /* TestEvents.swift */; }; C4DE00052229758700FB50AB /* Action+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DE00042229758700FB50AB /* Action+Extensions.swift */; }; FA3F973C1EDAF46F00A84787 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0569E21DE28587007E1D0D /* Action.swift */; }; FA3F973D1EDAF46F00A84787 /* Action+Internal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0569E01DE28587007E1D0D /* Action+Internal.swift */; }; @@ -143,6 +147,8 @@ C41CE72B22283BEE0006E722 /* RxTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxTest.framework; path = Carthage/Build/Mac/RxTest.framework; sourceTree = ""; }; C41CE73922283CD00006E722 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/tvOS/RxSwift.framework; sourceTree = ""; }; C41CE73F22283D220006E722 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/watchOS/RxSwift.framework; sourceTree = ""; }; + C4BFF33C2281D6E100D1AA27 /* Nimble+RxTest+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Nimble+RxTest+Ext.swift"; sourceTree = ""; }; + C4BFF33F2282B79F00D1AA27 /* TestEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestEvents.swift; sourceTree = ""; }; C4DE00042229758700FB50AB /* Action+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Action+Extensions.swift"; sourceTree = ""; }; C4E0263F20D119EE00C8164C /* Action.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = Action.podspec; sourceTree = ""; }; C4E0264020D11A0F00C8164C /* Cartfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile; sourceTree = ""; }; @@ -319,6 +325,8 @@ 3DD965DC1F5DF84800C180FE /* macOS-Tests */, 3DD965DB1F5DF83700C180FE /* iOS-Tests */, 7F0569F01DE288EB007E1D0D /* ActionTests.swift */, + C4BFF33C2281D6E100D1AA27 /* Nimble+RxTest+Ext.swift */, + C4BFF33F2282B79F00D1AA27 /* TestEvents.swift */, 7F0569F41DE288EB007E1D0D /* Info.plist */, ); path = Tests; @@ -720,7 +728,9 @@ buildActionMask = 2147483647; files = ( 3DD965DE1F5DF8C500C180FE /* BindToTests.swift in Sources */, + C4BFF3412282B79F00D1AA27 /* TestEvents.swift in Sources */, 3DD965C61F5DC2E100C180FE /* ActionTests.swift in Sources */, + C4BFF33E2281D6E100D1AA27 /* Nimble+RxTest+Ext.swift in Sources */, 3DD965DF1F5DF8C900C180FE /* NSButtonTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -729,11 +739,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C4BFF3402282B79F00D1AA27 /* TestEvents.swift in Sources */, 7F0569F71DE288EB007E1D0D /* BarButtonTests.swift in Sources */, 5ED520241E1EA199007621B9 /* BindToTests.swift in Sources */, 7F0569F61DE288EB007E1D0D /* AlertActionTests.swift in Sources */, 7F0569F51DE288EB007E1D0D /* ActionTests.swift in Sources */, 7B4BFE6320C290BF00D72FB0 /* RefreshControlTests.swift in Sources */, + C4BFF33D2281D6E100D1AA27 /* Nimble+RxTest+Ext.swift in Sources */, 7F0569F81DE288EB007E1D0D /* ButtonTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Tests/Nimble+RxTest+Ext.swift b/Tests/Nimble+RxTest+Ext.swift new file mode 100644 index 00000000..fecc4ab4 --- /dev/null +++ b/Tests/Nimble+RxTest+Ext.swift @@ -0,0 +1 @@ +import Foundation diff --git a/Tests/TestEvents.swift b/Tests/TestEvents.swift new file mode 100644 index 00000000..fbf28757 --- /dev/null +++ b/Tests/TestEvents.swift @@ -0,0 +1,2 @@ +import Foundation + From 87305644a1b382b9cff447d432a7bdf67cc680d2 Mon Sep 17 00:00:00 2001 From: bobgodwinx Date: Wed, 8 May 2019 18:28:57 +0200 Subject: [PATCH 22/76] Specs-Cleanup: Introduced `func asString() -> String` to get the describing strings for `Optional` Types --- Tests/Nimble+RxTest+Ext.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Tests/Nimble+RxTest+Ext.swift b/Tests/Nimble+RxTest+Ext.swift index fecc4ab4..8e3264fa 100644 --- a/Tests/Nimble+RxTest+Ext.swift +++ b/Tests/Nimble+RxTest+Ext.swift @@ -1 +1,14 @@ import Foundation +import Quick +import Nimble +import RxSwift +import RxTest + +/// `Foundation` + +extension Optional { + func asString() -> String { + guard let s = self else { return "nil" } + return String(describing: s) + } +} From 7f8254e9acaf27743d03ff97538dce175a5c718e Mon Sep 17 00:00:00 2001 From: bobgodwinx Date: Wed, 8 May 2019 18:31:09 +0200 Subject: [PATCH 23/76] Specs-Cleanup: Introduced `var isError: Bool` on `Event` Type --- Tests/Nimble+RxTest+Ext.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Tests/Nimble+RxTest+Ext.swift b/Tests/Nimble+RxTest+Ext.swift index 8e3264fa..b520f650 100644 --- a/Tests/Nimble+RxTest+Ext.swift +++ b/Tests/Nimble+RxTest+Ext.swift @@ -12,3 +12,15 @@ extension Optional { return String(describing: s) } } + +/// `RxSwift` + +extension Event { + var isError: Bool { + switch self { + case .error: return true + default: return false + } + } +} + From 3ffcdca52933892b868a62f61e29e33d08602269 Mon Sep 17 00:00:00 2001 From: bobgodwinx Date: Wed, 8 May 2019 18:36:02 +0200 Subject: [PATCH 24/76] Specs-Cleanup: `extension PredicateResult` to return an evaluationFailed PredicateResult and `isEqual` --- Tests/Nimble+RxTest+Ext.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Tests/Nimble+RxTest+Ext.swift b/Tests/Nimble+RxTest+Ext.swift index b520f650..eba72077 100644 --- a/Tests/Nimble+RxTest+Ext.swift +++ b/Tests/Nimble+RxTest+Ext.swift @@ -24,3 +24,16 @@ extension Event { } } +/// Nimble + +extension PredicateResult { + static var evaluationFailed: PredicateResult { + return PredicateResult(status: .doesNotMatch, + message: .fail("failed to evaluate given expression")) + } + + static func isEqual(actual: T?, expected: T?) -> PredicateResult { + return PredicateResult(bool: actual == expected, + message: .expectedCustomValueTo("get <\(expected.asString())>", "<\(actual.asString())>")) + } +} From 251a1dbf2c6fe8717a4d2ed8d2a5b3265673270a Mon Sep 17 00:00:00 2001 From: bobgodwinx Date: Wed, 8 May 2019 18:38:01 +0200 Subject: [PATCH 25/76] Specs-Cleanup: Added `public func match(_ expected: T) -> Predicate` for general purpose matching --- Tests/Nimble+RxTest+Ext.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Tests/Nimble+RxTest+Ext.swift b/Tests/Nimble+RxTest+Ext.swift index eba72077..3682e935 100644 --- a/Tests/Nimble+RxTest+Ext.swift +++ b/Tests/Nimble+RxTest+Ext.swift @@ -37,3 +37,18 @@ extension PredicateResult { message: .expectedCustomValueTo("get <\(expected.asString())>", "<\(actual.asString())>")) } } + +public func match(_ expected: T) -> Predicate { + return Predicate { events in + + guard let source = try events.evaluate() else { + return PredicateResult.evaluationFailed + } + guard source == expected else { + return PredicateResult(status: .doesNotMatch, message: .expectedCustomValueTo("get <\(expected)> events", "<\(source)> events")) + } + + + return PredicateResult(bool: true, message: .fail("matched values and timeline as expected")) + } +} From a6aeb78da23400eaec9ddd7f8de15637bb0dea00 Mon Sep 17 00:00:00 2001 From: bobgodwinx Date: Wed, 8 May 2019 18:39:29 +0200 Subject: [PATCH 26/76] Specs-Cleanup: Added `public func match(_ expected: [Recorded>]) -> Predicate<[Recorded>]> ` an Event Type matcher --- Tests/Nimble+RxTest+Ext.swift | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Tests/Nimble+RxTest+Ext.swift b/Tests/Nimble+RxTest+Ext.swift index 3682e935..0cff01e1 100644 --- a/Tests/Nimble+RxTest+Ext.swift +++ b/Tests/Nimble+RxTest+Ext.swift @@ -52,3 +52,25 @@ public func match(_ expected: T) -> Predicate { return PredicateResult(bool: true, message: .fail("matched values and timeline as expected")) } } + +public func match(_ expected: [Recorded>]) -> Predicate<[Recorded>]> where T: Equatable { + return Predicate { events in + + guard let source = try events.evaluate() else { + return PredicateResult.evaluationFailed + } + guard source.count == expected.count else { + return PredicateResult(bool: false, message: .expectedCustomValueTo("get <\(expected.count)> events", "<\(source.count)> events")) + } + + for (lhs, rhs) in zip(source, expected) { + guard lhs.time == rhs.time, + lhs.value == rhs.value else { + return PredicateResult(bool: rhs == lhs, message: .expectedCustomValueTo("match <\(rhs)>", "<\(lhs)>")) + } + continue + } + + return PredicateResult(bool: true, message: .fail("match timeline")) + } +} From b8e2b51e32b0d0b47716cf4f81dd0929a59c9ef2 Mon Sep 17 00:00:00 2001 From: bobgodwinx Date: Wed, 8 May 2019 18:41:26 +0200 Subject: [PATCH 27/76] Specs-Cleanup: Added `public func match(with expectedErrors: [Recorded>]) -> Predicate<[Recorded>]>` for Error specific matching --- Tests/Nimble+RxTest+Ext.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Tests/Nimble+RxTest+Ext.swift b/Tests/Nimble+RxTest+Ext.swift index 0cff01e1..d7ad1927 100644 --- a/Tests/Nimble+RxTest+Ext.swift +++ b/Tests/Nimble+RxTest+Ext.swift @@ -74,3 +74,21 @@ public func match(_ expected: [Recorded>]) -> Predicate<[Recorded(with expectedErrors: [Recorded>]) -> Predicate<[Recorded>]> where E: Equatable { + return Predicate { events in + guard let source = try events.evaluate() else { + return PredicateResult.evaluationFailed + } + let errorEvents = source.filter { $0.value.isError } + for (lhs, rhs) in zip(errorEvents, expectedErrors) { + guard lhs.time == rhs.time, + lhs.value.error.asString() == rhs.value.error.asString() else { + return PredicateResult(bool: false, message: .fail("did not error")) + } + continue + } + + return PredicateResult(bool: true, message: .fail("matched values and timeline as expected")) + } +} From 4eaea1460e8fb045b21d0a50d882317db9c193ab Mon Sep 17 00:00:00 2001 From: bobgodwinx Date: Wed, 8 May 2019 18:43:27 +0200 Subject: [PATCH 28/76] Spec-Cleanup: Cosmetic touches on matcher fucntions --- Tests/Nimble+RxTest+Ext.swift | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Tests/Nimble+RxTest+Ext.swift b/Tests/Nimble+RxTest+Ext.swift index d7ad1927..bc4ae2fd 100644 --- a/Tests/Nimble+RxTest+Ext.swift +++ b/Tests/Nimble+RxTest+Ext.swift @@ -38,14 +38,15 @@ extension PredicateResult { } } -public func match(_ expected: T) -> Predicate { +public func match(_ expected: T) -> Predicate where T: Equatable { return Predicate { events in guard let source = try events.evaluate() else { return PredicateResult.evaluationFailed } guard source == expected else { - return PredicateResult(status: .doesNotMatch, message: .expectedCustomValueTo("get <\(expected)> events", "<\(source)> events")) + return PredicateResult(status: .doesNotMatch, + message: .expectedCustomValueTo("get <\(expected)> events", "<\(source)> events")) } @@ -60,13 +61,15 @@ public func match(_ expected: [Recorded>]) -> Predicate<[Recorded events", "<\(source.count)> events")) + return PredicateResult(bool: false, + message: .expectedCustomValueTo("get <\(expected.count)> events", "<\(source.count)> events")) } for (lhs, rhs) in zip(source, expected) { guard lhs.time == rhs.time, lhs.value == rhs.value else { - return PredicateResult(bool: rhs == lhs, message: .expectedCustomValueTo("match <\(rhs)>", "<\(lhs)>")) + return PredicateResult(bool: rhs == lhs, + message: .expectedCustomValueTo("match <\(rhs)>", "<\(lhs)>")) } continue } @@ -84,7 +87,8 @@ public func match(with expectedErrors: [Recorded>]) -> Pre for (lhs, rhs) in zip(errorEvents, expectedErrors) { guard lhs.time == rhs.time, lhs.value.error.asString() == rhs.value.error.asString() else { - return PredicateResult(bool: false, message: .fail("did not error")) + return PredicateResult(bool: false, + message: .fail("did not error")) } continue } From 5022210ddce4ba5fca8a2f0dda526b58d08ba796 Mon Sep 17 00:00:00 2001 From: bobgodwinx Date: Wed, 8 May 2019 18:44:48 +0200 Subject: [PATCH 29/76] Specs-Cleanup: Added `enum TestEvents` which summarises events for tests --- Tests/TestEvents.swift | 89 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/Tests/TestEvents.swift b/Tests/TestEvents.swift index fbf28757..6ee16c06 100644 --- a/Tests/TestEvents.swift +++ b/Tests/TestEvents.swift @@ -1,2 +1,91 @@ import Foundation +import RxSwift +import RxTest +@testable import Action +enum TestEvents { + static let inputs = Recorded.events([ + .next(10, "a"), + .next(20, "b"), + ]) + + static let elements = Recorded.events([ + .next(10, "a"), + .next(20, "b"), + ]) + + static let multipleElements = Recorded.events([ + .next(10, "a"), + .next(10, "b"), + .next(10, "c"), + .next(20, "b"), + .next(20, "c"), + .next(20, "d"), + ]) + + static let enabled = Recorded.events([ + .next(0, true), + .next(10, false), + .next(10, true), + .next(20, false), + .next(20, true), + ]) + + static let disabled = Recorded.events([ + .next(0, true), + .next(10, false), + .next(10, true), + .next(20, false), + .next(20, true), + ]) + + static let executing = Recorded.events([ + .next(0, false), + .next(10, true), + .next(10, false), + .next(20, true), + .next(20, false), + ]) + + static let `false` = Recorded.events([ + .next(0, false), + ]) + + static let underlyingErrors = Recorded.events([ + .next(10, ActionError.underlyingError(TestError)), + .next(20, ActionError.underlyingError(TestError)), + ]) + + static let notEnabledErrors = Recorded.events([ + .next(10, ActionError.notEnabled), + .next(20, ActionError.notEnabled), + ]) + + static let executionStreams = Recorded.events([ + .next(10, "a"), + .completed(10), + .next(20, "b"), + .completed(20), + ]) + + static let elementUnderlyingErrors = Recorded.events([ + .error(10, ActionError.underlyingError(TestError), String.self), + .error(20, ActionError.underlyingError(TestError), String.self), + ]) + + static let elementNotEnabledErrors = Recorded.events([ + .error(10, ActionError.notEnabled, String.self), + .error(20, ActionError.notEnabled, String.self), + ]) + + static let multipleExecutionStreams = Recorded.events([ + .next(10, "a"), + .next(10, "a"), + .next(10, "a"), + .completed(10), + .next(20, "b"), + .next(20, "b"), + .next(20, "b"), + .completed(20), + ]) +} From 0a6c8984e1a07aabe125c89f942a6850aefd5821 Mon Sep 17 00:00:00 2001 From: bobgodwinx Date: Wed, 8 May 2019 18:46:47 +0200 Subject: [PATCH 30/76] Specs-Cleanup: Overhaul of `ActionTests` and dropped totally the use of `XCTAssert` in favor of `expect` grammer. --- Tests/ActionTests/ActionTests.swift | 242 +++++++--------------------- 1 file changed, 58 insertions(+), 184 deletions(-) diff --git a/Tests/ActionTests/ActionTests.swift b/Tests/ActionTests/ActionTests.swift index fa46768d..9b4ebd85 100644 --- a/Tests/ActionTests/ActionTests.swift +++ b/Tests/ActionTests/ActionTests.swift @@ -18,6 +18,7 @@ class ActionTests: QuickSpec { describe("completable action") { var inputs: TestableObserver! var action: CompletableAction! + beforeEach { inputs = scheduler.createObserver(String.self) action = CompletableAction { input -> Completable in @@ -27,29 +28,27 @@ class ActionTests: QuickSpec { scheduler.scheduleAt(10) { action.inputs.onNext("a") } scheduler.scheduleAt(20) { action.inputs.onNext("b") } } + afterEach { action = nil } + it("receives generated inputs") { scheduler.start() - XCTAssertEqual(inputs.events, Recorded.events([ - .next(10, "a"), - .next(20, "b") - ]) - ) + expect(inputs.events).to(match(TestEvents.inputs)) } it("emits nothing on `elements`") { let elements = scheduler.createObserver(Never.self) action.elements.bind(to: elements).disposed(by: disposeBag) scheduler.start() - XCTAssertEqual(elements.events.count, 0) + expect(elements.events.count).to(match(0)) } it("emits on `completions` when completed") { let completions = scheduler.createObserver(Void.self) action.completions.bind(to: completions).disposed(by: disposeBag) scheduler.start() - XCTAssert(completions.events.contains { $0.time == 10}) - XCTAssert(completions.events.contains { $0.time == 20}) + expect(completions.events.contains { $0.time == 10}).to(beTrue()) + expect(completions.events.contains { $0.time == 20}).to(beTrue()) } } @@ -75,59 +74,48 @@ class ActionTests: QuickSpec { it("execute on .next") { scheduler.scheduleAt(10) { action.inputs.onNext("a") } scheduler.start() - XCTAssertEqual(inputs.events, Recorded.events([ - .next(10, "a") - ]) - ) - XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 1) + let hasStopEvent = executions.events.filter { $0.value.isStopEvent }.isEmpty + expect(hasStopEvent).to(beTrue()) } it("ignore .error events") { scheduler.scheduleAt(10) { action.inputs.onError(TestError) } scheduler.start() - XCTAssertEqual(inputs.events, []) - XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 0) + let hasStopEvent = executions.events.filter { $0.value.isStopEvent }.isEmpty + expect(hasStopEvent).to(beTrue()) } it("ignore .completed events") { scheduler.scheduleAt(10) { action.inputs.onCompleted() } scheduler.start() - XCTAssertEqual(inputs.events, []) - XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 0) + let hasStopEvent = executions.events.filter { $0.value.isStopEvent }.isEmpty + expect(hasStopEvent).to(beTrue()) + expect(inputs.events.isEmpty).to(beTrue()) } it("accept multiple .next events") { scheduler.scheduleAt(10) { action.inputs.onNext("a") } scheduler.scheduleAt(20) { action.inputs.onNext("b") } scheduler.start() - XCTAssertEqual(inputs.events, Recorded.events([ - .next(10, "a"), - .next(20, "b"), - ]) - ) - XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 2) - XCTAssertEqual(executions.events.filter { $0.value.isStopEvent }.count, 0) + expect(inputs.events).to(match(TestEvents.inputs)) + let executionsCount = executions.events.filter { !$0.value.isStopEvent }.count + expect(executionsCount).to(match(2)) } it("not terminate after .error event") { scheduler.scheduleAt(10) { action.inputs.onError(TestError) } scheduler.scheduleAt(20) { action.inputs.onNext("b") } scheduler.start() - XCTAssertEqual(inputs.events, Recorded.events([ - .next(20, "b") - ]) - ) - XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 1) - XCTAssertEqual(executions.events.filter { $0.value.isStopEvent }.count, 0) + expect(inputs.events).to(match(Recorded.events([.next(20, "b")]))) + let executionsCount = executions.events.filter { !$0.value.isStopEvent }.count + expect(executionsCount).to(match(1)) } it("not terminate after .completed event") { scheduler.scheduleAt(10) { action.inputs.onCompleted() } scheduler.scheduleAt(20) { action.inputs.onNext("b") } scheduler.start() - XCTAssertEqual(inputs.events, Recorded.events([ - .next(20, "b") - ]) - ) - XCTAssertEqual(executions.events.filter { !$0.value.isStopEvent }.count, 1) - XCTAssertEqual(executions.events.filter { $0.value.isStopEvent }.count, 0) + expect(inputs.events).to(match(Recorded.events([.next(20, "b")]))) + let executionsCount = executions.events.filter { !$0.value.isStopEvent }.count + expect(executionsCount).to(match(1)) } } + describe("action properties") { var inputs: TestableObserver! var elements: TestableObserver! @@ -192,45 +180,23 @@ class ActionTests: QuickSpec { describe("single element action") { sharedExamples("send elements to elements observable") { it("work factory receives inputs") { - XCTAssertEqual(inputs.events, Recorded.events([ - .next(10, "a"), - .next(20, "b") - ]) - ) + expect(inputs.events).to(match(TestEvents.inputs)) } it("elements observable receives generated elements") { - XCTAssertEqual(elements.events, Recorded.events([ - .next(10, "a"), - .next(20, "b") - ]) - ) + expect(elements.events).to(match(TestEvents.elements)) } it("errors observable receives nothing") { - XCTAssertEqual(errors.events, []) + expect(errors.events.isEmpty).to(beTrue()) } it("disabled until element returns") { - XCTAssertEqual(enabled.events, Recorded.events([ - .next(0, true), - .next(10, false), - .next(10, true), - .next(20, false), - .next(20, true) - ]) - ) + expect(enabled.events).to(match(TestEvents.disabled)) } it("executing until element returns") { - XCTAssertEqual(executing.events, Recorded.events([ - .next(0, false), - .next(10, true), - .next(10, false), - .next(20, true), - .next(20, false) - ]) - ) + expect(executing.events).to(match(TestEvents.executing)) } it("executes twice") { @@ -268,53 +234,27 @@ class ActionTests: QuickSpec { describe("multiple element action") { sharedExamples("send array elements to elements observable") { it("work factory receives inputs") { - XCTAssertEqual(inputs.events, Recorded.events([ - .next(10, "a"), - .next(20, "b") - ]) - ) + expect(inputs.events).to(match(TestEvents.inputs)) } it("elements observable receives generated elements") { - XCTAssertEqual(elements.events, Recorded.events([ - .next(10, "a"), - .next(10, "b"), - .next(10, "c"), - .next(20, "b"), - .next(20, "c"), - .next(20, "d") - ]) - ) + expect(elements.events).to(match(TestEvents.multipleElements)) } it("errors observable receives nothing") { - XCTAssertEqual(errors.events, []) + expect(errors.events.isEmpty).to(beTrue()) } it("disabled until element returns") { - XCTAssertEqual(enabled.events, Recorded.events([ - .next(0, true), - .next(10, false), - .next(10, true), - .next(20, false), - .next(20, true) - ]) - ) + expect(enabled.events).to(match(TestEvents.disabled)) } it("executing until element returns") { - XCTAssertEqual(executing.events, Recorded.events([ - .next(0, false), - .next(10, true), - .next(10, false), - .next(20, true), - .next(20, false) - ]) - ) + expect(executing.events).to(match(TestEvents.executing)) } it("executes twice") { - expect(executionObservables.events.count) == 2 + expect(executionObservables.events.count).to(match(2)) } } @@ -356,60 +296,35 @@ class ActionTests: QuickSpec { describe("error action") { sharedExamples("send errors to errors observable") { it("work factory receives inputs") { - XCTAssertEqual(inputs.events, Recorded.events([ - .next(10, "a"), - .next(20, "b") - ]) - ) + expect(inputs.events).to(match(TestEvents.inputs)) } it("elements observable receives nothing") { - XCTAssertEqual(elements.events, []) + expect(elements.events.isEmpty).to(beTrue()) } it("errors observable receives generated errors") { - XCTAssertEqual(errors.events, Recorded.events([ - .next(10, .underlyingError(TestError)), - .next(20, .underlyingError(TestError)) - ]) - ) + expect(errors.events).to(match(TestEvents.underlyingErrors)) } it("underlyingError observable receives 2 generated errors") { - XCTAssertEqual(underlyingError.events.count, 2) + expect(underlyingError.events.count).to(match(2)) } it("underlyingError observable receives generated errors") { - let first = underlyingError.events[0].value.element as! String - let second = underlyingError.events[1].value.element as! String - XCTAssertEqual(first, TestError) - XCTAssertEqual(second, TestError) + expect(underlyingError.events).to(match(with: TestEvents.elementUnderlyingErrors)) } it("disabled until error returns") { - XCTAssertEqual(enabled.events, Recorded.events([ - .next(0, true), - .next(10, false), - .next(10, true), - .next(20, false), - .next(20, true) - ]) - ) + expect(enabled.events).to(match(TestEvents.disabled)) } it("executing until error returns") { - XCTAssertEqual(executing.events, Recorded.events([ - .next(0, false), - .next(10, true), - .next(10, false), - .next(20, true), - .next(20, false) - ]) - ) + expect(executing.events).to(match(TestEvents.executing)) } it("executes twice") { - expect(executionObservables.events.count) == 2 + expect(executionObservables.events.count).to(match(2)) } } @@ -443,37 +358,27 @@ class ActionTests: QuickSpec { describe("disabled action") { sharedExamples("send notEnabled errors to errors observable") { it("work factory receives nothing") { - XCTAssertEqual(inputs.events, []) + expect(inputs.events.isEmpty).to(beTrue()) } it("elements observable receives nothing") { - XCTAssertEqual(elements.events, []) + expect(elements.events.isEmpty).to(beTrue()) } it("errors observable receives generated errors") { - XCTAssertEqual(errors.events, Recorded.events([ - .next(10, .notEnabled), - .next(20, .notEnabled) - ]) - ) + expect(errors.events).to(match(TestEvents.notEnabledErrors)) } it("underlyingError observable receives zero generated errors") { - XCTAssertEqual(underlyingError.events.count, 0) + expect(underlyingError.events.count).to(match(0)) } it("disabled") { - XCTAssertEqual(enabled.events, Recorded.events([ - .next(0, false) - ]) - ) + expect(enabled.events).to(match(TestEvents.false)) } it("never be executing") { - XCTAssertEqual(executing.events, Recorded.events([ - .next(0, false) - ]) - ) + expect(executing.events).to(match(TestEvents.false)) } it("never executes") { @@ -546,17 +451,11 @@ class ActionTests: QuickSpec { } it("element receives single value for each execution") { - XCTAssertEqual(element.events, Recorded.events([ - .next(10, "a"), - .completed(10), - .next(20, "b"), - .completed(20) - ]) - ) + expect(element.events).to(match(TestEvents.executionStreams)) } it("executes twice") { - expect(executionObservables.events.count) == 2 + expect(executionObservables.events.count).to(match(2)) } } @@ -567,21 +466,11 @@ class ActionTests: QuickSpec { } it("element receives 3 values for each execution") { - XCTAssertEqual(element.events, Recorded.events([ - .next(10, "a"), - .next(10, "a"), - .next(10, "a"), - .completed(10), - .next(20, "b"), - .next(20, "b"), - .next(20, "b"), - .completed(20) - ]) - ) + expect(element.events).to(match(TestEvents.multipleExecutionStreams)) } it("executes twice") { - expect(executionObservables.events.count) == 2 + expect(executionObservables.events.count).to(match(2)) } } @@ -592,11 +481,7 @@ class ActionTests: QuickSpec { } it("element fails with underlyingError") { - XCTAssertEqual(element.events, Recorded.events([ - .error(10, ActionError.underlyingError(TestError)), - .error(20, ActionError.underlyingError(TestError)) - ]) - ) + expect(element.events).to(match(with: TestEvents.elementUnderlyingErrors)) } it("executes twice") { @@ -611,11 +496,7 @@ class ActionTests: QuickSpec { } it("element fails with notEnabled") { - XCTAssertEqual(element.events, Recorded.events([ - .error(10, ActionError.notEnabled), - .error(20, ActionError.notEnabled) - ]) - ) + expect(element.events).to(match(with: TestEvents.elementNotEnabledErrors)) } it("never executes") { @@ -660,22 +541,15 @@ class ActionTests: QuickSpec { } it("first element receives single value") { - XCTAssertEqual(element.events, Recorded.events([ - .next(30, "a"), - .completed(30) - ]) - ) + expect(element.events).to(match(Recorded.events([.next(30, "a"), .completed(30)]))) } it("second element fails with notEnabled error") { - XCTAssertEqual(secondElement.events, Recorded.events([ - .error(20, ActionError.notEnabled) - ]) - ) + expect(secondElement.events).to(match(Recorded.events([.error(20, ActionError.notEnabled)]))) } it("executes once") { - expect(executionObservables.events.count) == 1 + expect(executionObservables.events.count).to(match(1)) } } } From 92e338be51a8dd3ae53a68c1dbc8e21fb59440ad Mon Sep 17 00:00:00 2001 From: bobgodwinx Date: Wed, 8 May 2019 18:47:45 +0200 Subject: [PATCH 31/76] Specs-Cleanup: Cosmetic cleanup of `AlertActionTests` --- Tests/iOS-Tests/AlertActionTests.swift | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/Tests/iOS-Tests/AlertActionTests.swift b/Tests/iOS-Tests/AlertActionTests.swift index c4b8ab47..e7976627 100644 --- a/Tests/iOS-Tests/AlertActionTests.swift +++ b/Tests/iOS-Tests/AlertActionTests.swift @@ -8,22 +8,16 @@ class AlertActionTests: QuickSpec { override func spec() { it("is nil by default") { let subject = UIAlertAction.Action("Hi", style: .default) - expect(subject.rx.action).to( beNil() ) + expect(subject.rx.action).to(beNil()) } - it("respects setter") { var subject = UIAlertAction.Action("Hi", style: .default) - let action = emptyAction() - subject.rx.action = action - - expect(subject.rx.action) === action + expect(subject.rx.action).to(beAKindOf(CocoaAction.self)) } - it("disables the alert action while executing") { var subject = UIAlertAction.Action("Hi", style: .default) - var observer: AnyObserver! let action = CocoaAction(workFactory: { _ in return Observable.create { (obsv) -> Disposable in @@ -33,7 +27,6 @@ class AlertActionTests: QuickSpec { }) subject.rx.action = action - action.execute() expect(subject.isEnabled).toEventually( beFalse() ) @@ -55,7 +48,7 @@ class AlertActionTests: QuickSpec { .disposed(by: disposeBag) } - expect(subject.isEnabled) == false + expect(subject.isEnabled).to(beFalse()) } it("disposes of old action subscriptions when re-set") { @@ -77,8 +70,7 @@ class AlertActionTests: QuickSpec { } subject.rx.action = nil - - expect(disposed) == true + expect(disposed).to(beTrue()) } } } From b33fb9e7590b58eb5caa9709afa5df9154a872ea Mon Sep 17 00:00:00 2001 From: bobgodwinx Date: Wed, 8 May 2019 18:48:34 +0200 Subject: [PATCH 32/76] Specs-Cleanup: Cosmetic cleanup of `BarButtonTests` --- Tests/iOS-Tests/BarButtonTests.swift | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/Tests/iOS-Tests/BarButtonTests.swift b/Tests/iOS-Tests/BarButtonTests.swift index 062d98d7..e4b112f1 100644 --- a/Tests/iOS-Tests/BarButtonTests.swift +++ b/Tests/iOS-Tests/BarButtonTests.swift @@ -16,10 +16,8 @@ class BarButtonTests: QuickSpec { var subject = UIBarButtonItem(barButtonSystemItem: .save, target: nil, action: nil) let action = emptyAction() - subject.rx.action = action - - expect(subject.rx.action) === action + expect(subject.rx.action).to(beAKindOf(CocoaAction.self)) } it("disables the button while executing") { @@ -46,9 +44,8 @@ class BarButtonTests: QuickSpec { var subject = UIBarButtonItem(barButtonSystemItem: .save, target: nil, action: nil) subject.rx.action = emptyAction(.just(false)) - expect(subject.target).toEventuallyNot( beNil() ) - - expect(subject.isEnabled) == false + expect(subject.target).toEventuallyNot(beNil()) + expect(subject.isEnabled).to(beFalse()) } it("doesn't execute a disabled action when tapped") { @@ -61,7 +58,6 @@ class BarButtonTests: QuickSpec { }) _ = subject.target?.perform(subject.action, with: subject) - expect(executed) == false } @@ -75,11 +71,9 @@ class BarButtonTests: QuickSpec { }) subject.rx.action = action // Setting the action has the asynchronous effect of adding a target. - expect(subject.target).toEventuallyNot( beNil() ) - + expect(subject.target).toEventuallyNot(beNil()) _ = subject.target?.perform(subject.action, with: subject) - - expect(executed) == true + expect(executed).to(beTrue()) } it("disposes of old action subscriptions when re-set") { @@ -101,8 +95,7 @@ class BarButtonTests: QuickSpec { } subject.rx.action = nil - - expect(disposed) == true + expect(disposed).to(beTrue()) } } } From 90453e9579df8e4b729f48da1455aebb813d84f6 Mon Sep 17 00:00:00 2001 From: bobgodwinx Date: Wed, 8 May 2019 22:23:52 +0200 Subject: [PATCH 33/76] Specs-Cleanup: Cleanup `hasStopEvent` in favor of a more elegant `expect` as suggested. --- Tests/ActionTests/ActionTests.swift | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Tests/ActionTests/ActionTests.swift b/Tests/ActionTests/ActionTests.swift index 9b4ebd85..4d6d1aa9 100644 --- a/Tests/ActionTests/ActionTests.swift +++ b/Tests/ActionTests/ActionTests.swift @@ -74,20 +74,17 @@ class ActionTests: QuickSpec { it("execute on .next") { scheduler.scheduleAt(10) { action.inputs.onNext("a") } scheduler.start() - let hasStopEvent = executions.events.filter { $0.value.isStopEvent }.isEmpty - expect(hasStopEvent).to(beTrue()) + expect(executions.events.filter { $0.value.isStopEvent }).to(beEmpty()) } it("ignore .error events") { scheduler.scheduleAt(10) { action.inputs.onError(TestError) } scheduler.start() - let hasStopEvent = executions.events.filter { $0.value.isStopEvent }.isEmpty - expect(hasStopEvent).to(beTrue()) + expect(executions.events.filter { $0.value.isStopEvent }).to(beEmpty()) } it("ignore .completed events") { scheduler.scheduleAt(10) { action.inputs.onCompleted() } scheduler.start() - let hasStopEvent = executions.events.filter { $0.value.isStopEvent }.isEmpty - expect(hasStopEvent).to(beTrue()) + expect(executions.events.filter { $0.value.isStopEvent }).to(beEmpty()) expect(inputs.events.isEmpty).to(beTrue()) } it("accept multiple .next events") { From 7f651b3d316cb16c4ed932428a2a560c209d5180 Mon Sep 17 00:00:00 2001 From: Toshinari Nakamura Date: Thu, 9 May 2019 16:37:05 +0900 Subject: [PATCH 34/76] Fix warning for the dueTime argument --- Demo/ViewController.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Demo/ViewController.swift b/Demo/ViewController.swift index 415f907a..181dc7cd 100644 --- a/Demo/ViewController.swift +++ b/Demo/ViewController.swift @@ -29,7 +29,7 @@ class ViewController: UIViewController { var disposableBag = DisposeBag() let sharedAction = Action { input in switch input { - case .barButton: return Observable.just("UIBarButtonItem with 3 seconds delay").delaySubscription(3, scheduler: MainScheduler.instance) + case .barButton: return Observable.just("UIBarButtonItem with 3 seconds delay").delaySubscription(.seconds(3), scheduler: MainScheduler.instance) case .button(let title): return .just("UIButton " + title) } } @@ -61,7 +61,7 @@ class ViewController: UIViewController { // Demo: add an action to a UIBarButtonItem in the navigation item self.navigationItem.rightBarButtonItem?.rx.action = CocoaAction { print("Bar button item was pressed, simulating a 2 second action") - return Observable.empty().delaySubscription(2, scheduler: MainScheduler.instance) + return Observable.empty().delaySubscription(.seconds(2), scheduler: MainScheduler.instance) } // Demo: observe the output of both actions, spin an activity indicator @@ -92,7 +92,7 @@ class ViewController: UIViewController { } self.navigationItem.leftBarButtonItem?.rx.bind(to: sharedAction, input: .barButton) - sharedAction.executing.debounce(0, scheduler: MainScheduler.instance).subscribe(onNext: { [weak self] executing in + sharedAction.executing.debounce(.seconds(0), scheduler: MainScheduler.instance).subscribe(onNext: { [weak self] executing in if (executing) { self?.activityIndicator.startAnimating() } @@ -114,7 +114,7 @@ class ViewController: UIViewController { print("Reloading was triggered!") return Observable .empty() - .delaySubscription(2, scheduler: MainScheduler.instance) + .delaySubscription(.seconds(2), scheduler: MainScheduler.instance) .do(onCompleted:{ print ("Reloading completed!") }) From 944ddccf7d93211f4dabea8f1991c4cbb566a4af Mon Sep 17 00:00:00 2001 From: Toshinari Nakamura Date: Thu, 9 May 2019 16:37:36 +0900 Subject: [PATCH 35/76] Fix misplacement of activityIndicatorView --- Demo/Base.lproj/Main.storyboard | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Demo/Base.lproj/Main.storyboard b/Demo/Base.lproj/Main.storyboard index 08fa0bd8..10ba31f7 100644 --- a/Demo/Base.lproj/Main.storyboard +++ b/Demo/Base.lproj/Main.storyboard @@ -1,11 +1,11 @@ - + - + @@ -48,8 +48,8 @@ -