diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 10f4ea3..69613cb 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -7,26 +7,75 @@ on: branches: [ master ] jobs: - build: + MacOS: + name: macOS runs-on: macos-10.15 env: - WORKSPACE: Example/STDevRxExt.xcworkspace - DEVELOPER_DIR: /Applications/Xcode_11.app/Contents/Developer + PROJECT: STDevRxExt.xcodeproj + SCHEME: STDevRxExt-Package + DEVELOPER_DIR: /Applications/Xcode_11.5.app/Contents/Developer steps: - - uses: actions/checkout@v2 - + + - name: Bundle Install + run: bundle install + - name: CocoaPods run: | gem install cocoapods pod install --project-directory=Example --repo-update - + + - name: Restore SPM Cache + uses: actions/cache@v1 + with: + path: .build + key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} + restore-keys: | + ${{ runner.os }}-spm- + + - name: Build and test (SPM) + run: | + swift build + swift test + + - name: Generate Xcodeproj + run: | + swift package generate-xcodeproj + - name: Test iOS - run: xcodebuild test -enableCodeCoverage YES -workspace $WORKSPACE -scheme $SCHEME -destination "$DESTINATION" ONLY_ACTIVE_ARCH=NO | xcpretty + run: | + xcodebuild clean build test -project $PROJECT -scheme $SCHEME -destination "$DESTINATION" | XCPRETTY_JSON_FILE_OUTPUT="xcodebuild-ios.json" xcpretty -f `xcpretty-json-formatter` env: - SCHEME: STDevRxExt-Example - DESTINATION: 'platform=iOS Simulator,name=iPhone 8,OS=13.0' - - - name: Pod Lib Lint - run: pod lib lint --allow-warnings + DESTINATION: platform=iOS Simulator,name=iPhone 11 + + - name: Test MacOS + run: | + xcodebuild clean build test -project $PROJECT -scheme $SCHEME -destination "$DESTINATION" | XCPRETTY_JSON_FILE_OUTPUT="xcodebuild-macos.json" xcpretty -f `xcpretty-json-formatter` + env: + DESTINATION: platform=OS X + + - name: Test TVOS + run: | + xcodebuild clean build test -project $PROJECT -scheme $SCHEME -destination "$DESTINATION" | XCPRETTY_JSON_FILE_OUTPUT="xcodebuild-tvos.json" xcpretty -f `xcpretty-json-formatter` + env: + DESTINATION: platform=tvOS Simulator,name=Apple TV 4K (at 1080p) + + - name: Build WatchOS + run: xcodebuild clean build -project $PROJECT -scheme $SCHEME -destination "$DESTINATION" + env: + DESTINATION: name=Apple Watch Series 5 - 40mm + + CocoaPods: + name: CocoaPods + runs-on: macos-10.15 + strategy: + matrix: + platform: ['ios', 'macos', 'tvos', 'watchos'] + env: + DEVELOPER_DIR: /Applications/Xcode_11.5.app/Contents/Developer + steps: + - uses: actions/checkout@v1 + + - name: CocoaPods ${{ matrix.platform }} + run: pod lib lint --skip-tests --allow-warnings --verbose --platforms=${{ matrix.platform }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..79b11b8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,20 @@ +name: Release + +on: + push: + tags: '*' + +jobs: + push: + runs-on: macos-10.15 + + steps: + - uses: actions/checkout@v2 + + - name: Deploy to Cocoapods + run: | + set -eo pipefail + pod lib lint --allow-warnings + pod trunk push --allow-warnings + env: + COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} diff --git a/.gitignore b/.gitignore index d175cd8..495831c 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,7 @@ Carthage/Build # `pod install` in .travis.yml # Pods/ + +# Swift Package Manager +.build +Package.resolved \ No newline at end of file diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Example/Example.playground/Contents.swift b/Example/Example.playground/Contents.swift index 83b2339..a88f495 100644 --- a/Example/Example.playground/Contents.swift +++ b/Example/Example.playground/Contents.swift @@ -8,57 +8,49 @@ ---- */ -import UIKit -import STDevRxExt -import RxSwift import RxCocoa +import RxSwift +import STDevRxExt +import UIKit /*: ## Filter Extensions -*/ + */ example("allowTrue") { - let disposeBag = DisposeBag() Observable.of(true, false, false, true, true) .allowTrue() .subscribe(onNext: { dump($0) }) .disposed(by: disposeBag) - } example("allowTrue Optional") { - let disposeBag = DisposeBag() Observable.of(true, false, nil, true, nil, true) .allowTrue() .subscribe(onNext: { dump($0) }) .disposed(by: disposeBag) - } example("allowFalse") { - let disposeBag = DisposeBag() Observable.of(true, false, false, true, false) .allowFalse() .subscribe(onNext: { dump($0) }) .disposed(by: disposeBag) - } example("allowFalse Optional") { - let disposeBag = DisposeBag() Observable.of(true, false, nil, true, nil, true, false) .allowFalse() .subscribe(onNext: { dump($0) }) .disposed(by: disposeBag) - } example("allowTrueOrNil") { @@ -68,7 +60,6 @@ example("allowTrueOrNil") { .allowTrueOrNil() .subscribe(onNext: { dump($0) }) .disposed(by: disposeBag) - } example("allowFalseOrNil") { @@ -78,57 +69,6 @@ example("allowFalseOrNil") { .allowFalseOrNil() .subscribe(onNext: { dump($0) }) .disposed(by: disposeBag) - -} - -example("filterIfNil") { - - let disposeBag = DisposeBag() - - var optional: String? = nil - - let subject = PublishSubject() - - subject - .filterIfNil(optional) - .subscribe(onNext: { dump($0, name: "Subscription1") }) - .disposed(by: disposeBag) - - optional = "enable" - - subject - .filterIfNil(optional) - .subscribe(onNext: { dump($0, name: "Subscription2") }) - .disposed(by: disposeBag) - - subject.onNext("🐹") - - subject.onNext("🐭") -} - -example("filterIfNotNil") { - - let disposeBag = DisposeBag() - - var optional: String? = nil - - let subject = PublishSubject() - - subject - .filterIfNotNil(optional) - .subscribe(onNext: { dump($0, name: "Subscription1") }) - .disposed(by: disposeBag) - - optional = "enable" - - subject - .filterIfNotNil(optional) - .subscribe(onNext: { dump($0, name: "Subscription2") }) - .disposed(by: disposeBag) - - subject.onNext("🐹") - - subject.onNext("🐭") } example("Allow nil") { @@ -142,28 +82,25 @@ example("Allow nil") { /*: ## Map Extensions -*/ + */ example("map(to:)") { - let disposeBag = DisposeBag() Observable.of(1, 5, 7, 8) .map(to: "ping") .subscribe(onNext: { dump($0 as String) }) .disposed(by: disposeBag) - } example("map(at:)") { + let disposeBag = DisposeBag() - let disposeBag = DisposeBag() - - let observable = Observable.of( - Book(title: "Twenty Thousand Leagues Under the Sea", author: Author("Jules", "Verne")), - Book(title: "Hamlet", author: Author("William", "Shakespeare")), - Book(title: "Hearts of Three", author: Author("Jack", "London")) - ) + let observable = Observable.of( + Book(title: "Twenty Thousand Leagues Under the Sea", author: Author("Jules", "Verne")), + Book(title: "Hamlet", author: Author("William", "Shakespeare")), + Book(title: "Hearts of Three", author: Author("Jack", "London")) + ) observable .map(at: \.title) @@ -174,12 +111,10 @@ example("map(at:)") { .map(at: \.author.firstName) .subscribe(onNext: { dump($0) }) .disposed(by: disposeBag) - } - /*: ## Cast Extensions -*/ + */ example("cast(to:)") { let disposeBag = DisposeBag() @@ -188,7 +123,6 @@ example("cast(to:)") { .cast(to: String.self) .subscribe(onNext: { dump($0) }) .disposed(by: disposeBag) - } example("forceCast(to:)") { @@ -198,7 +132,6 @@ example("forceCast(to:)") { .forceCast(to: String.self) .subscribe(onNext: { dump($0) }) .disposed(by: disposeBag) - } /*: @@ -218,6 +151,4 @@ example("update(_:with:)") { .update(subject, with: true) .subscribe(onNext: { dump($0) }) .disposed(by: disposeBag) - - } diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 55d4bb4..30dafa5 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -26,8 +26,8 @@ SPEC CHECKSUMS: RxCocoa: 32065309a38d29b5b0db858819b5bf9ef038b601 RxRelay: d77f7d771495f43c556cbc43eebd1bb54d01e8e9 RxSwift: 81470a2074fa8780320ea5fe4102807cb7118178 - STDevRxExt: 2bd7d233a4990c8e227b6af52ac3cc51e7ecb333 + STDevRxExt: 57d7368115b81b92e06728276504ece2c52fda2b PODFILE CHECKSUM: 83769abc0b7bdc83a928dcac34cf2b1154077760 -COCOAPODS: 1.9.1 +COCOAPODS: 1.9.3 diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..d5762a9 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +gem 'xcpretty' +gem 'xcpretty-json-formatter' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..02a5a86 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,18 @@ +GEM + remote: https://rubygems.org/ + specs: + rouge (2.0.7) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-json-formatter (0.1.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + ruby + +DEPENDENCIES + xcpretty + xcpretty-json-formatter + +BUNDLED WITH + 2.1.4 diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..982abb3 --- /dev/null +++ b/Package.swift @@ -0,0 +1,36 @@ +// swift-tools-version:5.0 + +import PackageDescription + +let package = Package( + name: "STDevRxExt", + platforms: [ + .macOS(.v10_10), .iOS(.v8), .tvOS(.v9), .watchOS(.v3), + ], + products: [ + .library( + name: "STDevRxExt", + targets: ["STDevRxExt"] + ), + ], + dependencies: [ + .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "5.1.0")), + ], + targets: [ + .target( + name: "STDevRxExt", + dependencies: [ + .product(name: "RxSwift", package: "RxSwift"), + .product(name: "RxCocoa", package: "RxSwift"), + ] + ), + .testTarget( + name: "STDevRxExtTests", + dependencies: [ + "STDevRxExt", + .product(name: "RxTest", package: "RxSwift") + ] + ), + ], + swiftLanguageVersions: [.v5] +) diff --git a/README.md b/README.md index e64cc7e..25b00b9 100644 --- a/README.md +++ b/README.md @@ -11,18 +11,50 @@ To run the [Example.playground](Example/Example.playground), clone the repo, and ## Requirements -* iOS 10.0+ +* iOS 8.0+ +* tvOS 9.0+ +* macOS 10.10+ +* watchOS 3.0+ * Swift 5.0+ * Xcode 11+ ## Installation -STDevRxExt is available through [CocoaPods](http://cocoapods.org). To install -it, simply add the following line to your Podfile: +
+CocoaPods +
+

STDevRxExt is available through CocoaPods. To install it, simply add the following line to your Podfile:

+ +
pod 'STDevRxExt'
+
+ +
+Swift Package Manager +
+

You can use The Swift Package Manager to install STDevRxExt by adding the proper description to your Package.swift file:

+ +
import PackageDescription
+
+let package = Package(
+    name: "YOUR_PROJECT_NAME",
+    targets: [],
+    dependencies: [
+        .package(url: "https://github.com/STDevTM/STDevRxExt.git", from: "1.0.0")
+    ]
+)
+
+ +

Next, add STDevRxExt to your targets dependencies like so:

+
.target(
+    name: "YOUR_TARGET_NAME",
+    dependencies: [
+        "STDevRxExt",
+    ]
+),
+

Then run swift package update.

+ +
-```ruby -pod 'STDevRxExt' -``` ## List of All Extensions diff --git a/STDevRxExt.podspec b/STDevRxExt.podspec index 7110f3a..2f91862 100644 --- a/STDevRxExt.podspec +++ b/STDevRxExt.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'STDevRxExt' - s.version = '0.2.0' + s.version = '1.0.0' s.summary = 'STDevRxExt contains some extension functions for RxSwift and RxCoca which makes our live easy.' s.swift_version = '5.0' @@ -17,9 +17,12 @@ STDevRxExt contains following 3 type of extensions for RxSwift and RxCocoa: s.author = { 'Tigran Hambardzumyan' => 'tigran@stdevmail.com' } s.source = { :git => 'https://github.com/STDevTM/STDevRxExt.git', :tag => s.version.to_s } - s.ios.deployment_target = '10.0' + s.ios.deployment_target = '8.0' + s.osx.deployment_target = '10.9' + s.watchos.deployment_target = '3.0' + s.tvos.deployment_target = '9.0' - s.source_files = 'STDevRxExt/Classes/**/*' + s.source_files = 'Sources/STDevRxExt/**/*' s.dependency 'RxSwift', '~> 5.1' s.dependency 'RxCocoa', '~> 5.1' diff --git a/STDevRxExt/Assets/.gitkeep b/STDevRxExt/Assets/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/STDevRxExt/Classes/.gitkeep b/STDevRxExt/Classes/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/STDevRxExt/Classes/CastExtensions.swift b/Sources/STDevRxExt/CastExtensions.swift similarity index 61% rename from STDevRxExt/Classes/CastExtensions.swift rename to Sources/STDevRxExt/CastExtensions.swift index ee87a9e..985be87 100644 --- a/STDevRxExt/Classes/CastExtensions.swift +++ b/Sources/STDevRxExt/CastExtensions.swift @@ -20,24 +20,20 @@ public extension ObservableType { } func forceCast(to type: T.Type) -> Observable { - return flatMap { value in - Observable.create { observer in - if let casted = value as? T { - observer.onNext(casted) - observer.onCompleted() - } else { - observer.onError(RxCastError.castFailed) - } - return Disposables.create() + return map { value in + if let casted = value as? T { + return casted + } else { + throw RxCastError.castFailed } } } } -// MARK: - Driver +// MARK: - SharedSequence -public extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy { - func cast(to type: T.Type) -> Driver { +public extension SharedSequenceConvertibleType { + func cast(to type: T.Type) -> SharedSequence { return map { $0 as? T } } } diff --git a/STDevRxExt/Classes/FilterExtensions.swift b/Sources/STDevRxExt/FilterExtensions.swift similarity index 63% rename from STDevRxExt/Classes/FilterExtensions.swift rename to Sources/STDevRxExt/FilterExtensions.swift index 8cfe35b..d5e35d3 100644 --- a/STDevRxExt/Classes/FilterExtensions.swift +++ b/Sources/STDevRxExt/FilterExtensions.swift @@ -43,62 +43,48 @@ public extension ObservableType where Element == Bool? { } public extension ObservableType { - func filterIfNil(_ param: Any?) -> Observable { - return filter { _ in param != nil } - } - - func filterIfNotNil(_ param: Any?) -> Observable { - return filter { _ in param == nil } - } -} - -public extension ObservableType where Element == Any? { - func allowNil() -> Observable { + func allowNil() -> Observable where Element == T? { return filter { $0 == nil } } } -// MARK: - Driver +// MARK: - SharedSequence -public extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy, Element == Bool { - func allowTrue() -> Driver { +public extension SharedSequenceConvertibleType where Element == Bool { + func allowTrue() -> SharedSequence { return filter { $0 } } - func allowFalse() -> Driver { + func allowFalse() -> SharedSequence { return filter { !$0 } } } -public extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy, Element == Bool? { - func allowTrue() -> Driver { +public extension SharedSequenceConvertibleType where Element == Bool? { + func allowTrue() -> SharedSequence { return map { $0 ?? false } .allowTrue() } - func allowTrueOrNil() -> Driver { + func allowTrueOrNil() -> SharedSequence { return map { $0 ?? true } .allowTrue() } - func allowFalse() -> Driver { + func allowFalse() -> SharedSequence { return map { $0 ?? true } .allowFalse() } - func allowFalseOrNil() -> Driver { + func allowFalseOrNil() -> SharedSequence { return map { $0 ?? false } .allowFalse() } } -public extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy { - func filterIfNil(_ param: Any?) -> Driver { - return filter { _ in param == nil } - } - - func filterIfNotNil(_ param: Any?) -> Driver { - return filter { _ in param != nil } +public extension SharedSequenceConvertibleType { + func allowNil() -> SharedSequence where Element == T? { + return filter { $0 == nil } } } @@ -137,17 +123,7 @@ public extension PrimitiveSequenceType where Trait == SingleTrait, Element == Bo } public extension PrimitiveSequenceType where Trait == SingleTrait { - func filterIfNil(_ param: Any?) -> Maybe { - return filter { _ in param != nil } - } - - func filterIfNotNil(_ param: Any?) -> Maybe { - return filter { _ in param == nil } - } -} - -public extension PrimitiveSequenceType where Trait == SingleTrait, Element == Any? { - func allowNil() -> Maybe { + func allowNil() -> Maybe where Element == T? { return filter { $0 == nil } } } @@ -187,17 +163,7 @@ public extension PrimitiveSequenceType where Trait == MaybeTrait, Element == Boo } public extension PrimitiveSequenceType where Trait == MaybeTrait { - func filterIfNil(_ param: Any?) -> Maybe { - return filter { _ in param != nil } - } - - func filterIfNotNil(_ param: Any?) -> Maybe { - return filter { _ in param == nil } - } -} - -public extension PrimitiveSequenceType where Trait == MaybeTrait, Element == Any? { - func allowNil() -> Maybe { + func allowNil() -> Maybe where Element == T? { return filter { $0 == nil } } } diff --git a/STDevRxExt/Classes/MapExtensions.swift b/Sources/STDevRxExt/MapExtensions.swift similarity index 82% rename from STDevRxExt/Classes/MapExtensions.swift rename to Sources/STDevRxExt/MapExtensions.swift index 8fd71ed..f4a3998 100644 --- a/STDevRxExt/Classes/MapExtensions.swift +++ b/Sources/STDevRxExt/MapExtensions.swift @@ -20,14 +20,14 @@ public extension ObservableType { } } -// MARK: - Driver +// MARK: - SharedSequence -public extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy { - func map(to value: T) -> Driver { +public extension SharedSequenceConvertibleType { + func map(to value: T) -> SharedSequence { return map { _ in value } } - func map(at keyPath: KeyPath) -> Driver { + func map(at keyPath: KeyPath) -> SharedSequence { return map { $0[keyPath: keyPath] } } } diff --git a/STDevRxExt/Classes/OtherExtensions.swift b/Sources/STDevRxExt/OtherExtensions.swift similarity index 86% rename from STDevRxExt/Classes/OtherExtensions.swift rename to Sources/STDevRxExt/OtherExtensions.swift index 53a0ec4..f5400a7 100644 --- a/STDevRxExt/Classes/OtherExtensions.swift +++ b/Sources/STDevRxExt/OtherExtensions.swift @@ -18,10 +18,10 @@ public extension ObservableType { } } -// MARK: - Driver +// MARK: - SharedSequence -public extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy { - func update(_ observer: T, with value: T.Element) -> SharedSequence { +public extension SharedSequenceConvertibleType { + func update(_ observer: T, with value: T.Element) -> SharedSequence { return self.do(onNext: { [observer] _ in observer.onNext(value) }) diff --git a/Tests/STDevRxExtTests/CastExtensionsTests.swift b/Tests/STDevRxExtTests/CastExtensionsTests.swift new file mode 100644 index 0000000..a597691 --- /dev/null +++ b/Tests/STDevRxExtTests/CastExtensionsTests.swift @@ -0,0 +1,168 @@ +// +// CastExtensionsTests.swift +// STDevRxExtTests +// +// Created by Tigran Hambardzumyan on 8/06/20. +// Copyright © 2020 STDev. All rights reserved. +// + +import RxCocoa +import RxSwift +import RxTest +import STDevRxExt + +import XCTest + +class CastExtensionsTests: XCTestCase { + private let scheduler = TestScheduler(initialClock: 0) + private var coldObservable: TestableObservable! + private let correctSoftCastEvents = Recorded.events([ + .next(10, FirstChild(value: 1)), + .next(20, FirstChild(value: 2)), + .next(30, nil), + .next(40, FirstChild(value: 4)), + .completed(50) + ]) + private let correctForceCastEvents = Recorded.events([ + .next(10, FirstChild(value: 1)), + .next(20, FirstChild(value: 2)), + .error(30, RxCastError.castFailed) + ]) + + override func setUp() { + super.setUp() + + coldObservable = scheduler.createColdObservable([ + .next(10, FirstChild(value: 1)), + .next(20, FirstChild(value: 2)), + .next(30, SecondChild(value: 3)), + .next(40, FirstChild(value: 4)), + .completed(50) + ]) + } + + // MARK: - Observable + + func testObservableCast() { + // Given + let observer = scheduler.createObserver(FirstChild?.self) + + // When + _ = coldObservable + .cast(to: FirstChild.self) + .subscribe(observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, correctSoftCastEvents) + } + + func testObservableForceCast() { + // Given + let observer = scheduler.createObserver(FirstChild.self) + + // When + _ = coldObservable + .forceCast(to: FirstChild.self) + .subscribe(observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, correctForceCastEvents) + } + + // MARK: - SharedSequence + + func testDriverCast() { + // Given + let observer = scheduler.createObserver(FirstChild?.self) + let driver = coldObservable.asDriver(onErrorDriveWith: .empty()) + + // When + _ = driver + .cast(to: FirstChild.self) + .drive(observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, correctSoftCastEvents) + } + + func testSignalCast() { + // Given + let observer = scheduler.createObserver(FirstChild?.self) + let signal = coldObservable.asSignal(onErrorSignalWith: .empty()) + + // When + _ = signal + .cast(to: FirstChild.self) + .emit(to: observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, correctSoftCastEvents) + } + + // MARK: - PrimitiveSequence + + func testSingleCast() { + // Given + let observer = scheduler.createObserver(FirstChild?.self) + let single = coldObservable.first() + + // When + _ = single + .cast(to: FirstChild.self) + .asObservable() + .subscribe(observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, [ + .next(10, FirstChild(value: 1)), + .completed(10) + ]) + } + + func testMaybeCast() { + // Given + let observer = scheduler.createObserver(FirstChild?.self) + let maybe = coldObservable.first().asMaybe() + + // When + _ = maybe + .cast(to: FirstChild.self) + .asObservable() + .subscribe(observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, [ + .next(10, FirstChild(value: 1)), + .completed(10) + ]) + } + + // MARK: - Helper Classes + + class Base: Equatable { + let value: Int + + init(value: Int) { + self.value = value + } + + static func == (lhs: Base, rhs: Base) -> Bool { + lhs.value == rhs.value + } + } + + class FirstChild: Base {} + class SecondChild: Base {} +} diff --git a/Tests/STDevRxExtTests/Filter/ObservableFilterExtensionsTests.swift b/Tests/STDevRxExtTests/Filter/ObservableFilterExtensionsTests.swift new file mode 100644 index 0000000..ee8b2ea --- /dev/null +++ b/Tests/STDevRxExtTests/Filter/ObservableFilterExtensionsTests.swift @@ -0,0 +1,209 @@ +// +// ObservableFilterExtensionsTests.swift +// STDevRxExtTests +// +// Created by Tigran Hambardzumyan on 8/06/20. +// Copyright © 2020 STDev. All rights reserved. +// + +import RxCocoa +import RxSwift +import RxTest +import STDevRxExt + +import XCTest + +class ObservableFilterExtensionsTests: XCTestCase { + private let scheduler = TestScheduler(initialClock: 0) + + func testObservableAllowTrue() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, true), + .next(20, false), + .next(30, false), + .next(40, true), + .completed(50) + ]) + let correctEvents = Recorded.events([ + .next(10, true), + .next(40, true), + .completed(50) + ]) + let observer = scheduler.createObserver(Bool.self) + + // When + _ = coldObservable + .allowTrue() + .subscribe(observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, correctEvents) + } + + func testObservableAllowFalse() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, true), + .next(20, false), + .next(30, false), + .next(40, true), + .completed(50) + ]) + let correctEvents = Recorded.events([ + .next(20, false), + .next(30, false), + .completed(50) + ]) + let observer = scheduler.createObserver(Bool.self) + + // When + _ = coldObservable + .allowFalse() + .subscribe(observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, correctEvents) + } + + func testObservableAllowTrueOptional() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, true), + .next(20, nil), + .next(30, false), + .next(40, true), + .completed(50) + ]) + let correctEvents = Recorded.events([ + .next(10, true), + .next(40, true), + .completed(50) + ]) + let observer = scheduler.createObserver(Bool.self) + + // When + _ = coldObservable + .allowTrue() + .subscribe(observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, correctEvents) + } + + func testObservableAllowTrueOrNil() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, true), + .next(20, nil), + .next(30, false), + .next(40, true), + .completed(50) + ]) + let correctEvents = Recorded.events([ + .next(10, true), + .next(20, true), + .next(40, true), + .completed(50) + ]) + let observer = scheduler.createObserver(Bool.self) + + // When + _ = coldObservable + .allowTrueOrNil() + .subscribe(observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, correctEvents) + } + + func testObservableAllowFalseOptional() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, true), + .next(20, false), + .next(30, false), + .next(40, nil), + .completed(50) + ]) + let correctEvents = Recorded.events([ + .next(20, false), + .next(30, false), + .completed(50) + ]) + let observer = scheduler.createObserver(Bool.self) + + // When + _ = coldObservable + .allowFalse() + .subscribe(observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, correctEvents) + } + + func testObservableAllowFalseOrNil() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, true), + .next(20, false), + .next(30, false), + .next(40, nil), + .completed(50) + ]) + let correctEvents = Recorded.events([ + .next(20, false), + .next(30, false), + .next(40, false), + .completed(50) + ]) + let observer = scheduler.createObserver(Bool.self) + + // When + _ = coldObservable + .allowFalseOrNil() + .subscribe(observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, correctEvents) + } + + func testObservableAllowNil() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, true), + .next(20, nil), + .next(30, false), + .next(40, nil), + .completed(50) + ]) + let correctEvents = Recorded>.events([ + .next(20, nil), + .next(40, nil), + .completed(50) + ]) + let observer = scheduler.createObserver(Bool?.self) + + // When + _ = coldObservable + .allowNil() + .subscribe(observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, correctEvents) + } +} diff --git a/Tests/STDevRxExtTests/Filter/PrimitiveSequenceFilterExtensionsTests.swift b/Tests/STDevRxExtTests/Filter/PrimitiveSequenceFilterExtensionsTests.swift new file mode 100644 index 0000000..37f3847 --- /dev/null +++ b/Tests/STDevRxExtTests/Filter/PrimitiveSequenceFilterExtensionsTests.swift @@ -0,0 +1,297 @@ +// +// PrimitiveSequenceFilterExtensionsTests.swift +// STDevRxExtTests +// +// Created by Tigran Hambardzumyan on 8/06/20. +// Copyright © 2020 STDev. All rights reserved. +// + +import RxCocoa +import RxSwift +import RxTest +import STDevRxExt + +import XCTest + +class PrimitiveSequenceFilterExtensionsTests: XCTestCase { + func testPrimitiveSequenceAllowTrue() { + singleAllowTrueTest(sequence: Single.just(true), expectedEvents: [.success(true)]) + singleAllowTrueTest(sequence: Single.just(false), expectedEvents: [.completed]) + maybeAllowTrueTest(sequence: Maybe.just(true), expectedEvents: [.success(true)]) + maybeAllowTrueTest(sequence: Maybe.just(false), expectedEvents: [.completed]) + } + + func testPrimitiveSequenceAllowFalse() { + singleAllowFalseTest(sequence: Single.just(false), expectedEvents: [.success(false)]) + singleAllowFalseTest(sequence: Single.just(true), expectedEvents: [.completed]) + maybeAllowFalseTest(sequence: Maybe.just(false), expectedEvents: [.success(false)]) + maybeAllowFalseTest(sequence: Maybe.just(true), expectedEvents: [.completed]) + } + + func testPrimitiveSequenceAllowTrueOptional() { + singleAllowTrueOptionalTest(sequence: Single.just(true), expectedEvents: [.success(true)]) + singleAllowTrueOptionalTest(sequence: Single.just(nil), expectedEvents: [.completed]) + singleAllowTrueOptionalTest(sequence: Single.just(false), expectedEvents: [.completed]) + maybeAllowTrueOptionalTest(sequence: Maybe.just(true), expectedEvents: [.success(true)]) + maybeAllowTrueOptionalTest(sequence: Maybe.just(nil), expectedEvents: [.completed]) + maybeAllowTrueOptionalTest(sequence: Maybe.just(false), expectedEvents: [.completed]) + } + + func testPrimitiveSequenceAllowFalseOptional() { + singleAllowFalseOptionalTest(sequence: Single.just(false), expectedEvents: [.success(false)]) + singleAllowFalseOptionalTest(sequence: Single.just(nil), expectedEvents: [.completed]) + singleAllowFalseOptionalTest(sequence: Single.just(true), expectedEvents: [.completed]) + maybeAllowFalseOptionalTest(sequence: Maybe.just(false), expectedEvents: [.success(false)]) + maybeAllowFalseOptionalTest(sequence: Maybe.just(nil), expectedEvents: [.completed]) + maybeAllowFalseOptionalTest(sequence: Maybe.just(true), expectedEvents: [.completed]) + } + + func testPrimitiveSequenceAllowTrueOrNil() { + singleAllowTrueOrNilTest(sequence: Single.just(true), expectedEvents: [.success(true)]) + singleAllowTrueOrNilTest(sequence: Single.just(nil), expectedEvents: [.success(true)]) + singleAllowTrueOrNilTest(sequence: Single.just(false), expectedEvents: [.completed]) + maybeAllowTrueOrNilTest(sequence: Maybe.just(true), expectedEvents: [.success(true)]) + maybeAllowTrueOrNilTest(sequence: Maybe.just(nil), expectedEvents: [.success(true)]) + maybeAllowTrueOrNilTest(sequence: Maybe.just(false), expectedEvents: [.completed]) + } + + func testPrimitiveSequenceAllowFalseOrNil() { + singleAllowFalseOrNilTest(sequence: Single.just(false), expectedEvents: [.success(false)]) + singleAllowFalseOrNilTest(sequence: Single.just(nil), expectedEvents: [.success(false)]) + singleAllowFalseOrNilTest(sequence: Single.just(true), expectedEvents: [.completed]) + maybeAllowFalseOrNilTest(sequence: Maybe.just(false), expectedEvents: [.success(false)]) + maybeAllowFalseOrNilTest(sequence: Maybe.just(nil), expectedEvents: [.success(false)]) + maybeAllowFalseOrNilTest(sequence: Maybe.just(true), expectedEvents: [.completed]) + } + + func testPrimitiveSequenceAllowNil() { + singleAllowNil(sequence: Single.just(nil), expectedEvents: [.success(nil)]) + singleAllowNil(sequence: Single.just(1), expectedEvents: [.completed]) + maybeAllowNil(sequence: Maybe.just(nil), expectedEvents: [.success(nil)]) + maybeAllowNil(sequence: Maybe.just(1), expectedEvents: [.completed]) + } + + private func singleAllowTrueTest(sequence: Single, expectedEvents: [MaybeEvent]) { + // Given + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = sequence + .allowTrue() + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + private func maybeAllowTrueTest(sequence: Maybe, expectedEvents: [MaybeEvent]) { + // Given + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = sequence + .allowTrue() + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + private func singleAllowFalseTest(sequence: Single, expectedEvents: [MaybeEvent]) { + // Given + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = sequence + .allowFalse() + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + private func maybeAllowFalseTest(sequence: Maybe, expectedEvents: [MaybeEvent]) { + // Given + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = sequence + .allowFalse() + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + private func singleAllowTrueOptionalTest(sequence: Single, expectedEvents: [MaybeEvent]) { + // Given + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = sequence + .allowTrue() + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + private func maybeAllowTrueOptionalTest(sequence: Maybe, expectedEvents: [MaybeEvent]) { + // Given + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = sequence + .allowTrue() + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + private func singleAllowFalseOptionalTest(sequence: Single, expectedEvents: [MaybeEvent]) { + // Given + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = sequence + .allowFalse() + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + private func maybeAllowFalseOptionalTest(sequence: Maybe, expectedEvents: [MaybeEvent]) { + // Given + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = sequence + .allowFalse() + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + private func singleAllowTrueOrNilTest(sequence: Single, expectedEvents: [MaybeEvent]) { + // Given + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = sequence + .allowTrueOrNil() + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + private func maybeAllowTrueOrNilTest(sequence: Maybe, expectedEvents: [MaybeEvent]) { + // Given + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = sequence + .allowTrueOrNil() + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + private func singleAllowFalseOrNilTest(sequence: Single, expectedEvents: [MaybeEvent]) { + // Given + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = sequence + .allowFalseOrNil() + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + private func maybeAllowFalseOrNilTest(sequence: Maybe, expectedEvents: [MaybeEvent]) { + // Given + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = sequence + .allowFalseOrNil() + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + private func singleAllowNil(sequence: Single, expectedEvents: [MaybeEvent]) { + // Given + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = sequence + .allowNil() + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + private func maybeAllowNil(sequence: Maybe, expectedEvents: [MaybeEvent]) { + // Given + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = sequence + .allowNil() + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } +} diff --git a/Tests/STDevRxExtTests/Filter/SharedSequenceFilterExtensionsTests.swift b/Tests/STDevRxExtTests/Filter/SharedSequenceFilterExtensionsTests.swift new file mode 100644 index 0000000..2ffa873 --- /dev/null +++ b/Tests/STDevRxExtTests/Filter/SharedSequenceFilterExtensionsTests.swift @@ -0,0 +1,263 @@ +// +// SharedSequenceFilterExtensionsTests.swift +// STDevRxExtTests +// +// Created by Tigran Hambardzumyan on 8/7/20. +// Copyright © 2020 STDev. All rights reserved. +// + +import RxCocoa +import RxSwift +import RxTest +import STDevRxExt + +import XCTest + +class SharedSequenceFilterExtensionsTests: XCTestCase { + private let scheduler = TestScheduler(initialClock: 0) + + func testSharedSequenceAllowTrue() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, true), + .next(20, false), + .next(30, false), + .next(40, true), + .completed(50) + ]) + let correctEvents = Recorded.events([ + .next(10, true), + .next(40, true), + .completed(50) + ]) + let driver = coldObservable.asDriver(onErrorDriveWith: .empty()) + let observerForDriver = scheduler.createObserver(Bool.self) + let signal = coldObservable.asDriver(onErrorDriveWith: .empty()) + let observerForSignal = scheduler.createObserver(Bool.self) + + // When + _ = driver + .allowTrue() + .drive(observerForDriver) + + _ = signal + .allowTrue() + .drive(observerForSignal) + + scheduler.start() + + // Then + XCTAssertEqual(observerForDriver.events, correctEvents) + XCTAssertEqual(observerForSignal.events, correctEvents) + } + + func testSharedSequenceAllowFalse() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, true), + .next(20, false), + .next(30, false), + .next(40, true), + .completed(50) + ]) + let correctEvents = Recorded.events([ + .next(20, false), + .next(30, false), + .completed(50) + ]) + let driver = coldObservable.asDriver(onErrorDriveWith: .empty()) + let observerForDriver = scheduler.createObserver(Bool.self) + let signal = coldObservable.asDriver(onErrorDriveWith: .empty()) + let observerForSignal = scheduler.createObserver(Bool.self) + + // When + _ = driver + .allowFalse() + .drive(observerForDriver) + + _ = signal + .allowFalse() + .drive(observerForSignal) + + scheduler.start() + + // Then + XCTAssertEqual(observerForDriver.events, correctEvents) + XCTAssertEqual(observerForSignal.events, correctEvents) + } + + func testSharedSequenceAllowTrueOptional() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, true), + .next(20, nil), + .next(30, false), + .next(40, true), + .completed(50) + ]) + let correctEvents = Recorded.events([ + .next(10, true), + .next(40, true), + .completed(50) + ]) + let driver = coldObservable.asDriver(onErrorDriveWith: .empty()) + let observerForDriver = scheduler.createObserver(Bool.self) + let signal = coldObservable.asDriver(onErrorDriveWith: .empty()) + let observerForSignal = scheduler.createObserver(Bool.self) + + // When + _ = driver + .allowTrue() + .drive(observerForDriver) + + _ = signal + .allowTrue() + .drive(observerForSignal) + + scheduler.start() + + // Then + XCTAssertEqual(observerForDriver.events, correctEvents) + XCTAssertEqual(observerForSignal.events, correctEvents) + } + + func testSharedSequenceAllowTrueOrNil() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, true), + .next(20, nil), + .next(30, false), + .next(40, true), + .completed(50) + ]) + let correctEvents = Recorded.events([ + .next(10, true), + .next(20, true), + .next(40, true), + .completed(50) + ]) + let driver = coldObservable.asDriver(onErrorDriveWith: .empty()) + let observerForDriver = scheduler.createObserver(Bool.self) + let signal = coldObservable.asDriver(onErrorDriveWith: .empty()) + let observerForSignal = scheduler.createObserver(Bool.self) + + // When + _ = driver + .allowTrueOrNil() + .drive(observerForDriver) + + _ = signal + .allowTrueOrNil() + .drive(observerForSignal) + + scheduler.start() + + // Then + XCTAssertEqual(observerForDriver.events, correctEvents) + XCTAssertEqual(observerForSignal.events, correctEvents) + } + + func testSharedSequenceAllowFalseOptional() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, true), + .next(20, false), + .next(30, false), + .next(40, nil), + .completed(50) + ]) + let correctEvents = Recorded.events([ + .next(20, false), + .next(30, false), + .completed(50) + ]) + let driver = coldObservable.asDriver(onErrorDriveWith: .empty()) + let observerForDriver = scheduler.createObserver(Bool.self) + let signal = coldObservable.asDriver(onErrorDriveWith: .empty()) + let observerForSignal = scheduler.createObserver(Bool.self) + + // When + _ = driver + .allowFalse() + .drive(observerForDriver) + _ = signal + .allowFalse() + .drive(observerForSignal) + + scheduler.start() + + // Then + XCTAssertEqual(observerForDriver.events, correctEvents) + XCTAssertEqual(observerForSignal.events, correctEvents) + } + + func testSharedSequenceAllowFalseOrNil() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, true), + .next(20, false), + .next(30, false), + .next(40, nil), + .completed(50) + ]) + let correctEvents = Recorded.events([ + .next(20, false), + .next(30, false), + .next(40, false), + .completed(50) + ]) + let driver = coldObservable.asDriver(onErrorDriveWith: .empty()) + let observerForDriver = scheduler.createObserver(Bool.self) + let signal = coldObservable.asDriver(onErrorDriveWith: .empty()) + let observerForSignal = scheduler.createObserver(Bool.self) + + // When + _ = driver + .allowFalseOrNil() + .drive(observerForDriver) + _ = signal + .allowFalseOrNil() + .drive(observerForSignal) + + scheduler.start() + + // Then + XCTAssertEqual(observerForDriver.events, correctEvents) + XCTAssertEqual(observerForSignal.events, correctEvents) + } + + func testSharedSequenceAllowNil() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, true), + .next(20, nil), + .next(30, false), + .next(40, nil), + .completed(50) + ]) + let correctEvents = Recorded>.events([ + .next(20, nil), + .next(40, nil), + .completed(50) + ]) + let driver = coldObservable.asDriver(onErrorDriveWith: .empty()) + let observerForDriver = scheduler.createObserver(Bool?.self) + let signal = coldObservable.asDriver(onErrorDriveWith: .empty()) + let observerForSignal = scheduler.createObserver(Bool?.self) + + // When + _ = driver + .allowNil() + .drive(observerForDriver) + _ = signal + .allowNil() + .drive(observerForSignal) + + scheduler.start() + + // Then + XCTAssertEqual(observerForDriver.events, correctEvents) + XCTAssertEqual(observerForSignal.events, correctEvents) + } + +} diff --git a/Tests/STDevRxExtTests/MapExtensionsTests.swift b/Tests/STDevRxExtTests/MapExtensionsTests.swift new file mode 100644 index 0000000..438f056 --- /dev/null +++ b/Tests/STDevRxExtTests/MapExtensionsTests.swift @@ -0,0 +1,211 @@ +// +// MapExtensionsTests.swift +// STDevRxExtTests +// +// Created by Tigran Hambardzumyan on 8/06/20. +// Copyright © 2020 STDev. All rights reserved. +// + +import RxCocoa +import RxSwift +import RxTest +import STDevRxExt + +import XCTest + +class MapExtensionsTests: XCTestCase { + private let scheduler = TestScheduler(initialClock: 0) + private let models = [ + Model(property: "value1"), + Model(property: "value2"), + Model(property: "value3") + ] + + // MARK: - Observable + + func testObservableMapTo() { + // Given + let expectedEvents = Recorded.events([ + .next(0, "value"), + .next(0, "value"), + .next(0, "value"), + .completed(0) + ]) + let observer = scheduler.createObserver(String.self) + + // When + _ = Observable.from(models) + .map(to: "value") + .bind(to: observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, expectedEvents) + } + + func testObservableMapAt() { + // Given + let expectedEvents = Recorded.events([ + .next(0, "value1"), + .next(0, "value2"), + .next(0, "value3"), + .completed(0) + ]) + let observer = scheduler.createObserver(String.self) + + // When + _ = Observable.from(models) + .map(at: \.property) + .bind(to: observer) + + scheduler.start() + + // Then + XCTAssertEqual(observer.events, expectedEvents) + } + + // MARK: - SharedSequnce + + func testSharedSequnceMapTo() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, Model(property: "value1")), + .next(20, Model(property: "value2")), + .next(30, Model(property: "value3")), + .completed(40) + ]) + let expectedEvents = Recorded.events([ + .next(10, "value"), + .next(20, "value"), + .next(30, "value"), + .completed(40) + ]) + let observerForDriver = scheduler.createObserver(String.self) + let observerForSignal = scheduler.createObserver(String.self) + + // When + _ = coldObservable.asDriver(onErrorDriveWith: .empty()) + .map(to: "value") + .drive(observerForDriver) + + _ = coldObservable.asSignal(onErrorSignalWith: .empty()) + .map(to: "value") + .emit(to: observerForSignal) + + scheduler.start() + + // Then + XCTAssertEqual(observerForDriver.events, expectedEvents) + XCTAssertEqual(observerForSignal.events, expectedEvents) + } + + func testSharedSequnceMapAt() { + // Given + let coldObservable = scheduler.createColdObservable([ + .next(10, Model(property: "value1")), + .next(20, Model(property: "value2")), + .next(30, Model(property: "value3")), + .completed(40) + ]) + let expectedEvents = Recorded.events([ + .next(10, "value1"), + .next(20, "value2"), + .next(30, "value3"), + .completed(40) + ]) + let observerForDriver = scheduler.createObserver(String.self) + let observerForSignal = scheduler.createObserver(String.self) + + // When + _ = coldObservable.asDriver(onErrorDriveWith: .empty()) + .map(at: \.property) + .drive(observerForDriver) + + _ = coldObservable.asSignal(onErrorSignalWith: .empty()) + .map(at: \.property) + .emit(to: observerForSignal) + + scheduler.start() + + // Then + XCTAssertEqual(observerForDriver.events, expectedEvents) + XCTAssertEqual(observerForSignal.events, expectedEvents) + } + + // MARK: - PrimitiveSequence + + func testSingleMapTo() { + // Given + let expectedEvents = [SingleEvent.success("string")] + var events: [SingleEvent] = [] + let observer: ((SingleEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = Single.just(0) + .map(to: "string") + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + func testMaybeMapTo() { + // Given + let expectedEvents = [MaybeEvent.success("string")] + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = Maybe.just(0) + .map(to: "string") + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + func testSingleMapAt() { + // Given + let expectedEvents = [SingleEvent.success("string")] + var events: [SingleEvent] = [] + let observer: ((SingleEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = Single.just(Model(property: "string")) + .map(at: \.property) + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + func testMaybeMapAt() { + // Given + let expectedEvents = [MaybeEvent.success("string")] + var events: [MaybeEvent] = [] + let observer: ((MaybeEvent) -> Void) = { event in + events.append(event) + } + + // When + _ = Maybe.just(Model(property: "string")) + .map(at: \.property) + .subscribe(observer) + + // Then + XCTAssertEqual(events, expectedEvents) + } + + // MARK: - Helper Struct + + struct Model { + let property: String + } +} diff --git a/Tests/STDevRxExtTests/OtherExtensionsTests.swift b/Tests/STDevRxExtTests/OtherExtensionsTests.swift new file mode 100644 index 0000000..3ea24dd --- /dev/null +++ b/Tests/STDevRxExtTests/OtherExtensionsTests.swift @@ -0,0 +1,99 @@ +// +// OtherExtensionsTests.swift +// STDevRxExtTests +// +// Created by Tigran Hambardzumyan on 8/06/20. +// Copyright © 2020 STDev. All rights reserved. +// + +import RxCocoa +import RxSwift +import RxTest +import STDevRxExt + +import XCTest + +class OtherExtensionsTests: XCTestCase { + private let scheduler = TestScheduler(initialClock: 0) + + private var coldObservable: TestableObservable! + private let expectedEvents = Recorded.events([ + .next(10, "string"), + .next(20, "string"), + .next(30, "string"), + ]) + + override func setUp() { + super.setUp() + + coldObservable = scheduler.createColdObservable([ + .next(10, ()), + .next(20, ()), + .next(30, ()), + .completed(40), + ]) + } + + // MARK: - Observable + + func testObservableUpdate() { + // Given + let observerToUpdate = scheduler.createObserver(String.self) + + // When + _ = coldObservable + .update(observerToUpdate, with: "string") + .subscribe() + + scheduler.start() + + // Then + XCTAssertEqual(observerToUpdate.events, expectedEvents) + } + + // MARK: - SharedSequence + + func testSharedSequenceUpdate() { + // Given + let observerToUpdateForDriver = scheduler.createObserver(String.self) + let observerToUpdateForSignal = scheduler.createObserver(String.self) + + // When + _ = coldObservable.asDriver(onErrorDriveWith: .empty()) + .update(observerToUpdateForDriver, with: "string") + .drive() + + _ = coldObservable.asSignal(onErrorSignalWith: .empty()) + .update(observerToUpdateForSignal, with: "string") + .emit() + + scheduler.start() + + // Then + XCTAssertEqual(observerToUpdateForDriver.events, expectedEvents) + XCTAssertEqual(observerToUpdateForSignal.events, expectedEvents) + } + + // MARK: - PrimitiveSequence + + func testPrimitiveSequenceUpdate() { + // Given + let observerToUpdateForSingle = scheduler.createObserver(String.self) + let observerToUpdateForMaybe = scheduler.createObserver(String.self) + + // When + _ = Single.just(0) + .update(observerToUpdateForSingle, with: "string") + .subscribe() + + _ = Maybe.just(0) + .update(observerToUpdateForMaybe, with: "string") + .subscribe() + + scheduler.start() + + // Then + XCTAssertEqual(observerToUpdateForSingle.events, [.next(0, "string")]) + XCTAssertEqual(observerToUpdateForMaybe.events, [.next(0, "string")]) + } +}