diff --git a/.swiftlint.yml b/.swiftlint.yml index 2344cb6..6a6a844 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -12,7 +12,7 @@ identifier_name: included: - Example - - NBus + - Sources - Tests - Package.swift diff --git a/Example/NBus/AppDelegate.swift b/Example/NBus/AppDelegate.swift index a05b790..b96e51c 100644 --- a/Example/NBus/AppDelegate.swift +++ b/Example/NBus/AppDelegate.swift @@ -70,12 +70,10 @@ extension AppDelegate { // swiftlint:disable function_body_length private func pasteboardItems() -> Observable<[[String]]> { - NotificationCenter.default.rx - .notification(UIPasteboard.changedNotification) - .map { _ -> [[String]] in - let items = UIPasteboard.general.items - - return items.enumerated().map { index, item -> [String] in + UIPasteboard.general.rx + .items() + .map { items -> [[String]] in + items.enumerated().map { index, item -> [String] in item.map { key, value -> String in let identity: String let index = "(\(index + 1)/\(items.count))" @@ -126,30 +124,6 @@ extension AppDelegate { } // swiftlint:enable function_body_length - - private func canOpenURL() -> Observable { - UIApplication.shared.rx - .methodInvoked(#selector(UIApplication.canOpenURL(_:))) - .compactMap { args in - args[0] as? URL - } - } - - private func openURL() -> Observable { - let oldURL = UIApplication.shared.rx - .methodInvoked(#selector(UIApplication.openURL(_:))) - .compactMap { args in - args[0] as? URL - } - - let newURL = UIApplication.shared.rx - .methodInvoked(#selector(UIApplication.open(_:options:completionHandler:))) - .compactMap { args in - args[0] as? URL - } - - return Observable.merge([oldURL, newURL]) - } } extension AppDelegate { @@ -161,13 +135,15 @@ extension AppDelegate { }) .disposed(by: disposeBag) - canOpenURL() + UIApplication.shared.rx + .canOpenURL() .bind(onNext: { url in logger.debug("\(url)") }) .disposed(by: disposeBag) - openURL() + UIApplication.shared.rx + .openURL() .bind(onNext: { url in logger.debug("\(url)") }) @@ -175,49 +151,12 @@ extension AppDelegate { } } -extension AppDelegate { - - private func clearKeychains() { - let items = [ - kSecClassGenericPassword, - kSecClassInternetPassword, - kSecClassCertificate, - kSecClassKey, - kSecClassIdentity, - ] - - let status = items - .map { [kSecClass: $0] as CFDictionary } - .map { SecItemDelete($0) } - - assert(status.allSatisfy { - $0 == errSecSuccess || $0 == errSecItemNotFound - }) - } - - private func clearPasteboard() { - let pasteboard = UIPasteboard.general - - pasteboard.items = [] - - pasteboard.string = "NBus" - } - - private func clearUserDefaults() { - let defaults = UserDefaults.standard - - for (key, _) in defaults.dictionaryRepresentation() { - defaults.removeObject(forKey: key) - } - } -} - extension AppDelegate { private func clearStorage() { - clearKeychains() - clearPasteboard() - clearUserDefaults() +// AppState.shared.clearKeychains() + AppState.shared.clearPasteboard(shouldSetString: true) +// AppState.shared.clearUserDefaults() } } diff --git a/Example/NBus/AppDelegateSDK.swift b/Example/NBus/AppDelegateSDK.swift index cf37839..24791c8 100644 --- a/Example/NBus/AppDelegateSDK.swift +++ b/Example/NBus/AppDelegateSDK.swift @@ -21,6 +21,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? + #if BusMockTestSDK + var openURLToken: NSObjectProtocol? + var openUserActivityToken: NSObjectProtocol? + #endif + func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? @@ -44,6 +49,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { #elseif BusMockWeiboSDK let title = "isWeiboAppInstalled" let message = "\(WeiboSDK.isWeiboAppInstalled())" + #elseif BusMockTestSDK + let title = "Empty" + let message = "Empty" #else #error("ERROR") let title = "ERROR" @@ -64,4 +72,82 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } + + #if BusMockTestSDK + func application( + _ app: UIApplication, + open url: URL, + options: [UIApplication.OpenURLOptionsKey: Any] = [:] + ) -> Bool { + let group = DispatchGroup() + + var result: Bool! + + openURLToken = NotificationCenter.default.addObserver( + forName: AppState.OpenURL.responseName, + object: nil, + queue: nil, + using: { notification in + result = notification.userInfo?[AppState.OpenURL.responseResultKey] as? Bool + + group.leave() + } + ) + + group.enter() + + NotificationCenter.default.post( + name: AppState.OpenURL.requestName, + object: nil, + userInfo: [ + AppState.OpenURL.requestURLKey: url, + AppState.OpenURL.requestPasteboardKey: UIPasteboard.general.items, + ] + ) + + group.wait() + + NotificationCenter.default.removeObserver(openURLToken!) + + return result + } + + func application( + _ application: UIApplication, + continue userActivity: NSUserActivity, + restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void + ) -> Bool { + let group = DispatchGroup() + + var result: Bool! + + openUserActivityToken = NotificationCenter.default.addObserver( + forName: AppState.OpenUserActivity.responseName, + object: nil, + queue: nil, + using: { notification in + result = notification.userInfo?[AppState.OpenUserActivity.responseResultKey] as? Bool + + group.leave() + } + ) + + group.enter() + + NotificationCenter.default.post( + name: AppState.OpenUserActivity.requestName, + object: nil, + userInfo: [ + AppState.OpenUserActivity.requestUserActivityKey: userActivity, + AppState.OpenUserActivity.requestPasteboardKey: UIPasteboard.general.items, + ] + ) + + group.wait() + + NotificationCenter.default.removeObserver(openUserActivityToken!) + + return result + } + #endif } diff --git a/Example/NBus/Model/AppState+ClearStorage.swift b/Example/NBus/Model/AppState+ClearStorage.swift new file mode 100644 index 0000000..1d06ed2 --- /dev/null +++ b/Example/NBus/Model/AppState+ClearStorage.swift @@ -0,0 +1,49 @@ +// +// AppState+ClearStorage.swift +// BusMock +// +// Created by nuomi1 on 2022/4/1. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +import UIKit + +extension AppState { + + func clearKeychains() { + let items = [ + kSecClassGenericPassword, + kSecClassInternetPassword, + kSecClassCertificate, + kSecClassKey, + kSecClassIdentity, + ] + + let status = items + .map { [kSecClass: $0] as CFDictionary } + .map { SecItemDelete($0) } + + assert(status.allSatisfy { + $0 == errSecSuccess || $0 == errSecItemNotFound + }) + } + + func clearPasteboard(shouldSetString: Bool = false) { + let pasteboard = UIPasteboard.general + + pasteboard.items = [] + + if shouldSetString { + pasteboard.string = Self.defaultPasteboardString + } + } + + func clearUserDefaults() { + let defaults = UserDefaults.standard + + for (key, _) in defaults.dictionaryRepresentation() { + defaults.removeObject(forKey: key) + } + } +} diff --git a/Example/NBus/Model/AppState+Handler.swift b/Example/NBus/Model/AppState+Handler.swift new file mode 100644 index 0000000..e7ef765 --- /dev/null +++ b/Example/NBus/Model/AppState+Handler.swift @@ -0,0 +1,56 @@ +// +// AppState+Handler.swift +// NBus +// +// Created by nuomi1 on 2022/4/1. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +import NBus + +extension AppState { + + static let wechatSDKHandler = WechatSDKHandler( + appID: AppState.getAppID(for: Platforms.wechat)!, + universalLink: AppState.getUniversalLink(for: Platforms.wechat)! + ) + + static let wechatHandler = WechatHandler( + appID: AppState.getAppID(for: Platforms.wechat)!, + universalLink: AppState.getUniversalLink(for: Platforms.wechat)! + ) +} + +extension AppState { + + static let qqSDKHandler = QQSDKHandler( + appID: AppState.getAppID(for: Platforms.qq)!, + universalLink: AppState.getUniversalLink(for: Platforms.qq)! + ) + + static let qqHandler = QQHandler( + appID: AppState.getAppID(for: Platforms.qq)!, + universalLink: AppState.getUniversalLink(for: Platforms.qq)! + ) +} + +extension AppState { + + static let weiboSDKHandler = WeiboSDKHandler( + appID: AppState.getAppID(for: Platforms.weibo)!, + universalLink: AppState.getUniversalLink(for: Platforms.weibo)!, + redirectLink: AppState.getRedirectLink(for: Platforms.weibo)! + ) + + static let weiboHandler = WeiboHandler( + appID: AppState.getAppID(for: Platforms.weibo)!, + universalLink: AppState.getUniversalLink(for: Platforms.weibo)!, + redirectLink: AppState.getRedirectLink(for: Platforms.weibo)! + ) +} + +extension AppState { + + static let systemHandler = SystemHandler() +} diff --git a/Example/NBus/Model/AppState+Notification.swift b/Example/NBus/Model/AppState+Notification.swift new file mode 100644 index 0000000..4dcf403 --- /dev/null +++ b/Example/NBus/Model/AppState+Notification.swift @@ -0,0 +1,28 @@ +// +// AppState+Notification.swift +// NBus +// +// Created by nuomi1 on 2022/4/5. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation + +extension AppState { + + enum OpenURL { + static let requestName = Notification.Name("Bus.OpenURL") + static let responseName = Notification.Name("Bus.OpenURL.Result") + static let requestURLKey = "url" + static let requestPasteboardKey = "pasteboard" + static let responseResultKey = "result" + } + + enum OpenUserActivity { + static let requestName = Notification.Name("Bus.OpenUserActivity") + static let responseName = Notification.Name("Bus.OpenUserActivity.Result") + static let requestUserActivityKey = "userActivity" + static let requestPasteboardKey = "pasteboard" + static let responseResultKey = "result" + } +} diff --git a/Example/NBus/Model/AppState+PlatformItem.swift b/Example/NBus/Model/AppState+PlatformItem.swift new file mode 100644 index 0000000..504c055 --- /dev/null +++ b/Example/NBus/Model/AppState+PlatformItem.swift @@ -0,0 +1,59 @@ +// +// AppState+PlatformItem.swift +// NBus +// +// Created by nuomi1 on 2022/4/20. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +import NBus +import UIKit + +extension AppState { + + struct PlatformItem { + let platform: Platform + let category: Category + let handlers: [Category: HandlerType] + let viewController: () -> UIViewController + } +} + +extension AppState.PlatformItem { + + enum Category: Hashable { + case bus + case sdk + } +} + +extension AppState.PlatformItem.Category: CustomStringConvertible { + + var description: String { + switch self { + case .bus: + return "开源" + case .sdk: + return "官方" + } + } +} + +extension AppState.PlatformItem.Category { + + mutating func toggle() { + switch self { + case .bus: + self = .sdk + case .sdk: + self = .bus + } + } + + func toggled() -> Self { + var copy = self + copy.toggle() + return copy + } +} diff --git a/Example/NBus/Model/AppState+Setup.swift b/Example/NBus/Model/AppState+Setup.swift new file mode 100644 index 0000000..54a2782 --- /dev/null +++ b/Example/NBus/Model/AppState+Setup.swift @@ -0,0 +1,87 @@ +// +// AppState+Setup.swift +// NBus +// +// Created by nuomi1 on 2022/4/1. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +import NBus + +extension AppState { + + func setup() { + setupBusMock() + } + + private func setupWechatItem() -> PlatformItem { + let wechatItem = AppState.PlatformItem( + platform: Platforms.wechat, + category: .sdk, + handlers: [ + .bus: Self.wechatHandler, + .sdk: Self.wechatSDKHandler, + ], + viewController: { PlatformViewController() } + ) + + return wechatItem + } + + private func setupQQItem() -> PlatformItem { + let qqItem = AppState.PlatformItem( + platform: Platforms.qq, + category: .sdk, + handlers: [ + .bus: Self.qqHandler, + .sdk: Self.qqSDKHandler, + ], + viewController: { PlatformViewController() } + ) + + return qqItem + } + + private func setupWeiboItem() -> PlatformItem { + let weiboItem = AppState.PlatformItem( + platform: Platforms.weibo, + category: .sdk, + handlers: [ + .bus: Self.weiboHandler, + .sdk: Self.weiboSDKHandler, + ], + viewController: { PlatformViewController() } + ) + + return weiboItem + } + + private func setupSystemItem() -> PlatformItem { + let systemItem = AppState.PlatformItem( + platform: Platforms.system, + category: .bus, + handlers: [ + .bus: Self.systemHandler, + ], + viewController: { PlatformViewController() } + ) + + return systemItem + } + + private func setupBusMock() { + + let wechatItem = setupWechatItem() + let qqItem = setupQQItem() + let weiboItem = setupWeiboItem() + let systemItem = setupSystemItem() + + platformItems.accept([ + wechatItem, + qqItem, + weiboItem, + systemItem, + ]) + } +} diff --git a/Example/NBus/Model/AppState.swift b/Example/NBus/Model/AppState.swift index a2bda44..a47f5d8 100644 --- a/Example/NBus/Model/AppState.swift +++ b/Example/NBus/Model/AppState.swift @@ -6,165 +6,20 @@ // Copyright © 2020 nuomi1. All rights reserved. // -import NBus -import RxRelay -import UIKit +import Foundation +#if BusMockQQSDK || BusMockWechatSDK || BusMockWeiboSDK +import RxSwift +#endif class AppState { + #if BusMockQQSDK || BusMockWechatSDK || BusMockWeiboSDK let platformItems = BehaviorRelay<[PlatformItem]>(value: []) + #endif static let shared = AppState() - private init() {} -} - -extension AppState { - - struct PlatformItem { - let platform: Platform - let category: Category - let handlers: [Category: HandlerType] - let viewController: () -> UIViewController - } -} - -extension AppState.PlatformItem { - - enum Category: Hashable { - case bus - case sdk - } -} - -extension AppState.PlatformItem.Category: CustomStringConvertible { - - var description: String { - switch self { - case .bus: - return "开源" - case .sdk: - return "官方" - } - } -} - -extension AppState.PlatformItem.Category { - - mutating func toggle() { - switch self { - case .bus: - self = .sdk - case .sdk: - self = .bus - } - } - - func toggled() -> Self { - var copy = self - copy.toggle() - return copy - } -} - -extension AppState { - - func setup() { - setupBusMock() - } - - // swiftlint:disable function_body_length + static let defaultPasteboardString = "NBus" - private func setupBusMock() { - - // MARK: Wechat - - let wechatSDKHandler = WechatSDKHandler( - appID: AppState.getAppID(for: Platforms.wechat)!, - universalLink: AppState.getUniversalLink(for: Platforms.wechat)! - ) - - let wechatHandler = WechatHandler( - appID: AppState.getAppID(for: Platforms.wechat)!, - universalLink: AppState.getUniversalLink(for: Platforms.wechat)! - ) - - let wechatItem = AppState.PlatformItem( - platform: Platforms.wechat, - category: .sdk, - handlers: [ - .bus: wechatHandler, - .sdk: wechatSDKHandler, - ], - viewController: { PlatformViewController() } - ) - - // MARK: QQ - - let qqSDKHandler = QQSDKHandler( - appID: AppState.getAppID(for: Platforms.qq)!, - universalLink: AppState.getUniversalLink(for: Platforms.qq)! - ) - - let qqHandler = QQHandler( - appID: AppState.getAppID(for: Platforms.qq)!, - universalLink: AppState.getUniversalLink(for: Platforms.qq)! - ) - - let qqItem = AppState.PlatformItem( - platform: Platforms.qq, - category: .sdk, - handlers: [ - .bus: qqHandler, - .sdk: qqSDKHandler, - ], - viewController: { PlatformViewController() } - ) - - // MARK: Weibo - - let weiboSDKHandler = WeiboSDKHandler( - appID: AppState.getAppID(for: Platforms.weibo)!, - universalLink: AppState.getUniversalLink(for: Platforms.weibo)!, - redirectLink: AppState.getRedirectLink(for: Platforms.weibo)! - ) - - let weiboHandler = WeiboHandler( - appID: AppState.getAppID(for: Platforms.weibo)!, - universalLink: AppState.getUniversalLink(for: Platforms.weibo)!, - redirectLink: AppState.getRedirectLink(for: Platforms.weibo)! - ) - - let weiboItem = AppState.PlatformItem( - platform: Platforms.weibo, - category: .sdk, - handlers: [ - .bus: weiboHandler, - .sdk: weiboSDKHandler, - ], - viewController: { PlatformViewController() } - ) - - // MARK: System - - let systemHandler = SystemHandler() - - let systemItem = AppState.PlatformItem( - platform: Platforms.system, - category: .bus, - handlers: [ - .bus: systemHandler, - ], - viewController: { PlatformViewController() } - ) - - platformItems.accept([ - wechatItem, - qqItem, - weiboItem, - systemItem, - ]) - } - - // swiftlint:enable function_body_length + private init() {} } diff --git a/Example/NBus/Model/Endpoint.swift b/Example/NBus/Model/Endpoint.swift index 11d920a..f45f0dc 100644 --- a/Example/NBus/Model/Endpoint.swift +++ b/Example/NBus/Model/Endpoint.swift @@ -33,3 +33,28 @@ extension Endpoint: CustomStringConvertible { } } } + +extension Endpoint { + + var toPlatform: Platform { + switch self { + case Endpoints.Wechat.friend: + return Platforms.wechat + case Endpoints.Wechat.timeline: + return Platforms.wechat + case Endpoints.Wechat.favorite: + return Platforms.wechat + case Endpoints.QQ.friend: + return Platforms.qq + case Endpoints.QQ.timeline: + return Platforms.qq + case Endpoints.Weibo.timeline: + return Platforms.weibo + case Endpoints.System.activity: + return Platforms.system + default: + assertionFailure() + return Platform(rawValue: "unknown") + } + } +} diff --git a/Example/NBus/Model/MediaSource.swift b/Example/NBus/Model/MediaSource.swift index e0042bd..7ea03d5 100644 --- a/Example/NBus/Model/MediaSource.swift +++ b/Example/NBus/Model/MediaSource.swift @@ -11,11 +11,9 @@ import UIKit enum MediaSource { - static let text: MessageType = { - Messages.text( - text: defaultText - ) - }() + static let text: MessageType = Messages.text( + text: defaultText + ) static let image: MessageType = { let data = defaultJPEG.jpegData(compressionQuality: 1)! @@ -29,15 +27,32 @@ enum MediaSource { ) }() - static let gif: MessageType = { - Messages.image( - data: defaultGIF, + static let qqImage: MessageType = { + let data = defaultJPEG.jpegData(compressionQuality: 0.5)! + let thumbnail = defaultJPEG.jpegData(compressionQuality: 0.2)! + + return Messages.image( + data: data, title: defaultTitle, description: defaultDescription, - thumbnail: defaultThumbnail + thumbnail: thumbnail ) }() + static let wechatImage: MessageType = Messages.image( + data: defaultThumbnail, + title: defaultTitle, + description: defaultDescription, + thumbnail: defaultThumbnail + ) + + static let gif: MessageType = Messages.image( + data: defaultGIF, + title: defaultTitle, + description: defaultDescription, + thumbnail: defaultThumbnail + ) + static let audio: MessageType = { let url = URL(string: "https://music.163.com/#/song?id=25706284")! let dataURL = URL(string: "https://music.163.com/song/media/outer/url?id=25706284.mp3")! @@ -68,14 +83,12 @@ enum MediaSource { ) }() - static let webPage: MessageType = { - Messages.webPage( - link: defaultLink, - title: defaultTitle, - description: defaultDescription, - thumbnail: defaultThumbnail - ) - }() + static let webPage: MessageType = Messages.webPage( + link: defaultLink, + title: defaultTitle, + description: defaultDescription, + thumbnail: defaultThumbnail + ) static let file: MessageType = { let fileExtension = "gif" diff --git a/Example/NBus/Model/Reactive.swift b/Example/NBus/Model/Reactive.swift new file mode 100644 index 0000000..8bb0f98 --- /dev/null +++ b/Example/NBus/Model/Reactive.swift @@ -0,0 +1,69 @@ +// +// Reactive.swift +// NBus +// +// Created by nuomi1 on 2022/4/1. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +import RxCocoa +import RxSwift + +extension Reactive where Base: UIApplication { + + func canOpenURL() -> Observable { + methodInvoked(#selector(UIApplication.canOpenURL(_:))) + .compactMap { args in + args[0] as? URL + } + } + + func openURL() -> Observable { + let oldURL = methodInvoked(#selector(UIApplication.openURL(_:))) + .compactMap { args in + args[0] as? URL + } + + let newURL = methodInvoked(#selector(UIApplication.open(_:options:completionHandler:))) + .compactMap { args in + args[0] as? URL + } + + return Observable.merge([oldURL, newURL]) + } +} + +extension Reactive where Base: UIPasteboard { + + func items() -> Observable<[[String: Any]]> { + NotificationCenter.default.rx + .notification(UIPasteboard.changedNotification) + .observe(on: MainScheduler.asyncInstance) + .filter { ($0.object as! UIPasteboard) == base } + .map { _ in base.items } + } +} + +extension Reactive where Base: NotificationCenter { + + func openURL() -> Observable<(URL, [[String: Any]])> { + notification(AppState.OpenURL.requestName) + .map { + ( + $0.userInfo?[AppState.OpenURL.requestURLKey] as! URL, + $0.userInfo?[AppState.OpenURL.requestPasteboardKey] as! [[String: Any]] + ) + } + } + + func openUserActivity() -> Observable<(NSUserActivity, [[String: Any]])> { + notification(AppState.OpenUserActivity.requestName) + .map { + ( + $0.userInfo?[AppState.OpenUserActivity.requestUserActivityKey] as! NSUserActivity, + $0.userInfo?[AppState.OpenUserActivity.requestPasteboardKey] as! [[String: Any]] + ) + } + } +} diff --git a/Example/Podfile b/Example/Podfile index ac4a683..c788cb2 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -28,6 +28,21 @@ target "BusMockWeiboSDK" do pod "NBusWeiboSDK" end +target "BusMockTestSDK" do + pod "WoodPeckeriOS" +end + +target "BusTests" do + pod "NBus/BusHandlers", :path => "../" + pod "NBus/SDKHandlers", :path => "../" + + pod "NBusQQSDK" + pod "NBusWechatSDK" + pod "NBusWeiboSDK" + + pod "RxCocoa" +end + post_install do |installer| installer.pods_project.root_object.attributes["ORGANIZATIONNAME"] = "nuomi1" end diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 51da49a..68f5329 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -38,7 +38,7 @@ PODS: - RxSwift (= 6.5.0) - RxSwift (6.5.0) - SwiftTrace (6.6.0) - - WoodPeckeriOS (1.2.93) + - WoodPeckeriOS (1.3.0) DEPENDENCIES: - NBus/BusHandlers (from `../`) @@ -68,7 +68,7 @@ EXTERNAL SOURCES: :path: "../" SPEC CHECKSUMS: - NBus: 886bd839fdc1241f2de86f0b7636540a888d12ce + NBus: 20022fd04c4f9f36302ba7e3a0c4ea5c8aedde91 NBusQQSDK: 196ee749b74b67feda8c385d7a8da04c641bb21e NBusWechatSDK: efebd57656091bb33a3b54a67156bd293b37d423 NBusWeiboSDK: 9ece35d36da59080de85d7d5e34446111648da02 @@ -77,8 +77,8 @@ SPEC CHECKSUMS: RxRelay: 1de1523e604c72b6c68feadedd1af3b1b4d0ecbd RxSwift: 5710a9e6b17f3c3d6e40d6e559b9fa1e813b2ef8 SwiftTrace: affc6601a316add7c299a3916d9f43c286a583ef - WoodPeckeriOS: 12ec7f38c695e51cd94a476228888dfe85d9d916 + WoodPeckeriOS: 39e2bc92d88460dfd0c9429580ef71748d0ecf5e -PODFILE CHECKSUM: 7615e56e197a0fccb1a3d0d7e94e2eb4c3b595fa +PODFILE CHECKSUM: 02653ac45a0ec833b1ea1151b71b2ce38660fe07 COCOAPODS: 1.11.2 diff --git a/Example/project.yml b/Example/project.yml index 7c756a7..33fc191 100644 --- a/Example/project.yml +++ b/Example/project.yml @@ -8,10 +8,17 @@ configFiles: Release: NBus/Config.xcconfig targetTemplates: - BusMockTemplate: - type: application + BusTemplate: platform: iOS deploymentTarget: 10.0 + settings: + base: + SWIFT_VERSION: 5.0 + scheme: {} + BusMockTemplate: + templates: + - BusTemplate + type: application sources: - path: NBus excludes: @@ -25,8 +32,11 @@ targetTemplates: OTHER_SWIFT_FLAGS: - "$(inherited)" - "-D ${target_name}" - SWIFT_VERSION: 5.0 - scheme: {} + BusMockSDKTemplate: + templates: + - BusMockTemplate + sources: + - NBus/AppDelegateSDK.swift packages: Logging: @@ -47,16 +57,37 @@ targets: - package: Logging BusMockQQSDK: templates: - - BusMockTemplate - sources: - - NBus/AppDelegateSDK.swift + - BusMockSDKTemplate BusMockWechatSDK: templates: - - BusMockTemplate - sources: - - NBus/AppDelegateSDK.swift + - BusMockSDKTemplate BusMockWeiboSDK: templates: - - BusMockTemplate + - BusMockSDKTemplate + BusMockTestSDK: + templates: + - BusMockSDKTemplate sources: - - NBus/AppDelegateSDK.swift + - path: NBus + includes: + - Model/AppState.swift + - Model/AppState+ClearStorage.swift + - Model/AppState+Notification.swift + postBuildScripts: + - script: | + find "${PROJECT_DIR}" -name "WeiboSDK.bundle" -exec cp -R "{}" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/" \; + scheme: + testTargets: + - BusTests + BusTests: + templates: + - BusTemplate + type: bundle.unit-test + sources: + - path: ../Tests/NBusTests + - path: NBus/Model + excludes: + - AppState+Setup.swift + dependencies: + - target: BusMockTestSDK + - package: Logging diff --git a/NBus.podspec b/NBus.podspec index 4fd0bff..4c0fb02 100644 --- a/NBus.podspec +++ b/NBus.podspec @@ -29,51 +29,51 @@ Pod::Spec.new do |s| end s.subspec "Core" do |ss| - ss.source_files = ["NBus/Classes/Core/**/*.swift"] + ss.source_files = ["Sources/NBusCore/*.swift"] end s.subspec "QQSDKHandler" do |ss| ss.dependency "NBus/Core" ss.dependency "NBusQQSDK" - ss.source_files = ["NBus/Classes/Handler/QQSDKHandler.swift"] + ss.source_files = ["Sources/NBusQQSDKHandler/*.swift"] end s.subspec "QQHandler" do |ss| ss.dependency "NBus/Core" - ss.source_files = ["NBus/Classes/Handler/QQHandler.swift"] + ss.source_files = ["Sources/NBusQQHandler/*.swift"] end s.subspec "WechatSDKHandler" do |ss| ss.dependency "NBus/Core" ss.dependency "NBusWechatSDK" - ss.source_files = ["NBus/Classes/Handler/WechatSDKHandler.swift"] + ss.source_files = ["Sources/NBusWechatSDKHandler/*.swift"] end s.subspec "WechatHandler" do |ss| ss.dependency "NBus/Core" - ss.source_files = ["NBus/Classes/Handler/WechatHandler.swift"] + ss.source_files = ["Sources/NBusWechatHandler/*.swift"] end s.subspec "WeiboSDKHandler" do |ss| ss.dependency "NBus/Core" ss.dependency "NBusWeiboSDK" - ss.source_files = ["NBus/Classes/Handler/WeiboSDKHandler.swift"] + ss.source_files = ["Sources/NBusWeiboSDKHandler/*.swift"] end s.subspec "WeiboHandler" do |ss| ss.dependency "NBus/Core" - ss.source_files = ["NBus/Classes/Handler/WeiboHandler.swift"] + ss.source_files = ["Sources/NBusWeiboHandler/*.swift"] end s.subspec "SystemHandler" do |ss| ss.dependency "NBus/Core" - ss.source_files = ["NBus/Classes/Handler/SystemHandler.swift"] + ss.source_files = ["Sources/NBusSystemHandler/*.swift"] end end diff --git a/NBus/Assets/.gitkeep b/NBus/Assets/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/Package.swift b/Package.swift index acd97a4..229a862 100644 --- a/Package.swift +++ b/Package.swift @@ -18,32 +18,23 @@ let package = Package( ], targets: [ .target( - name: "NBusCore", - path: "NBus/Classes/Core" + name: "NBusCore" ), .target( name: "NBusQQHandler", - dependencies: ["NBusCore"], - path: "NBus/Classes/Handler", - sources: ["QQHandler.swift"] + dependencies: ["NBusCore"] ), .target( name: "NBusWechatHandler", - dependencies: ["NBusCore"], - path: "NBus/Classes/Handler", - sources: ["WechatHandler.swift"] + dependencies: ["NBusCore"] ), .target( name: "NBusWeiboHandler", - dependencies: ["NBusCore"], - path: "NBus/Classes/Handler", - sources: ["WeiboHandler.swift"] + dependencies: ["NBusCore"] ), .target( name: "NBusSystemHandler", - dependencies: ["NBusCore"], - path: "NBus/Classes/Handler", - sources: ["SystemHandler.swift"] + dependencies: ["NBusCore"] ), .testTarget( name: "NBusTests", diff --git a/NBus/Classes/Core/Bus+Debug.swift b/Sources/NBusCore/Bus+Debug.swift similarity index 100% rename from NBus/Classes/Core/Bus+Debug.swift rename to Sources/NBusCore/Bus+Debug.swift diff --git a/NBus/Classes/Core/Bus+Endpoint.swift b/Sources/NBusCore/Bus+Endpoint.swift similarity index 100% rename from NBus/Classes/Core/Bus+Endpoint.swift rename to Sources/NBusCore/Bus+Endpoint.swift diff --git a/NBus/Classes/Core/Bus+Error.swift b/Sources/NBusCore/Bus+Error.swift similarity index 100% rename from NBus/Classes/Core/Bus+Error.swift rename to Sources/NBusCore/Bus+Error.swift diff --git a/NBus/Classes/Core/Bus+Handler.swift b/Sources/NBusCore/Bus+Handler.swift similarity index 100% rename from NBus/Classes/Core/Bus+Handler.swift rename to Sources/NBusCore/Bus+Handler.swift diff --git a/NBus/Classes/Core/Bus+Helper.swift b/Sources/NBusCore/Bus+Helper.swift similarity index 100% rename from NBus/Classes/Core/Bus+Helper.swift rename to Sources/NBusCore/Bus+Helper.swift diff --git a/NBus/Classes/Core/Bus+Message.swift b/Sources/NBusCore/Bus+Message.swift similarity index 100% rename from NBus/Classes/Core/Bus+Message.swift rename to Sources/NBusCore/Bus+Message.swift diff --git a/NBus/Classes/Core/Bus+Platform.swift b/Sources/NBusCore/Bus+Platform.swift similarity index 100% rename from NBus/Classes/Core/Bus+Platform.swift rename to Sources/NBusCore/Bus+Platform.swift diff --git a/NBus/Classes/Core/Bus+Wrapper.swift b/Sources/NBusCore/Bus+Wrapper.swift similarity index 100% rename from NBus/Classes/Core/Bus+Wrapper.swift rename to Sources/NBusCore/Bus+Wrapper.swift diff --git a/NBus/Classes/Core/Bus.swift b/Sources/NBusCore/Bus.swift similarity index 100% rename from NBus/Classes/Core/Bus.swift rename to Sources/NBusCore/Bus.swift diff --git a/NBus/Classes/Handler/QQHandler.swift b/Sources/NBusQQHandler/QQHandler.swift similarity index 99% rename from NBus/Classes/Handler/QQHandler.swift rename to Sources/NBusQQHandler/QQHandler.swift index a108694..0243164 100644 --- a/NBus/Classes/Handler/QQHandler.swift +++ b/Sources/NBusQQHandler/QQHandler.swift @@ -9,7 +9,7 @@ import Foundation import UIKit -#if canImport(NBusCore) +#if SWIFT_PACKAGE import NBusCore #endif diff --git a/NBus/Classes/Handler/QQSDKHandler.swift b/Sources/NBusQQSDKHandler/QQSDKHandler.swift similarity index 98% rename from NBus/Classes/Handler/QQSDKHandler.swift rename to Sources/NBusQQSDKHandler/QQSDKHandler.swift index 7a38829..4e73179 100644 --- a/NBus/Classes/Handler/QQSDKHandler.swift +++ b/Sources/NBusQQSDKHandler/QQSDKHandler.swift @@ -47,6 +47,8 @@ public class QQSDKHandler { coordinator = Coordinator(owner: self) + TencentOAuth.setIsUserAgreedAuthorization(true) + oauthCoordinator = TencentOAuth( appId: appID.trimmingCharacters(in: .letters), enableUniveralLink: true, @@ -173,7 +175,7 @@ extension QQSDKHandler: ShareHandlerType { case Endpoints.QQ.friend: let cflag = self.cflag(endpoint, message.identifier) .reduce(0) { result, flag in result | flag.rawValue } - request.apiObject.cflag |= UInt64(cflag) + request.apiObject?.cflag |= UInt64(cflag) code = QQApiInterface.send(request) case Endpoints.QQ.timeline: code = QQApiInterface.sendReq(toQZone: request) @@ -190,6 +192,8 @@ extension QQSDKHandler: ShareHandlerType { completionHandler(.failure(.invalidParameter)) case .EQQAPIVERSIONNEEDUPDATE: completionHandler(.failure(.unsupportedApplication)) + case .EQQAPI_ERROR_USER_NOT_AGREED_AUTHORIZATION: + completionHandler(.failure(.invalidParameter)) default: busAssertionFailure() completionHandler(.failure(.unknown)) diff --git a/NBus/Classes/Handler/SystemHandler.swift b/Sources/NBusSystemHandler/SystemHandler.swift similarity index 99% rename from NBus/Classes/Handler/SystemHandler.swift rename to Sources/NBusSystemHandler/SystemHandler.swift index 23e1de9..c1fa9dd 100644 --- a/NBus/Classes/Handler/SystemHandler.swift +++ b/Sources/NBusSystemHandler/SystemHandler.swift @@ -10,7 +10,7 @@ import AuthenticationServices import Foundation import UIKit -#if canImport(NBusCore) +#if SWIFT_PACKAGE import NBusCore #endif diff --git a/NBus/Classes/Handler/WechatHandler.swift b/Sources/NBusWechatHandler/WechatHandler.swift similarity index 99% rename from NBus/Classes/Handler/WechatHandler.swift rename to Sources/NBusWechatHandler/WechatHandler.swift index 543c2ec..e8781f9 100644 --- a/NBus/Classes/Handler/WechatHandler.swift +++ b/Sources/NBusWechatHandler/WechatHandler.swift @@ -9,7 +9,7 @@ import Foundation import UIKit -#if canImport(NBusCore) +#if SWIFT_PACKAGE import NBusCore #endif diff --git a/NBus/Classes/Handler/WechatSDKHandler.swift b/Sources/NBusWechatSDKHandler/WechatSDKHandler.swift similarity index 100% rename from NBus/Classes/Handler/WechatSDKHandler.swift rename to Sources/NBusWechatSDKHandler/WechatSDKHandler.swift diff --git a/NBus/Classes/Handler/WeiboHandler.swift b/Sources/NBusWeiboHandler/WeiboHandler.swift similarity index 99% rename from NBus/Classes/Handler/WeiboHandler.swift rename to Sources/NBusWeiboHandler/WeiboHandler.swift index 17a4911..364e9a0 100644 --- a/NBus/Classes/Handler/WeiboHandler.swift +++ b/Sources/NBusWeiboHandler/WeiboHandler.swift @@ -9,7 +9,7 @@ import Foundation import UIKit -#if canImport(NBusCore) +#if SWIFT_PACKAGE import NBusCore #endif @@ -34,7 +34,7 @@ public class WeiboHandler { public let appID: String public let universalLink: URL - private let redirectLink: URL + public let redirectLink: URL private lazy var dateFormatter: DateFormatter = { let dateFormatter = DateFormatter() diff --git a/NBus/Classes/Handler/WeiboSDKHandler.swift b/Sources/NBusWeiboSDKHandler/WeiboSDKHandler.swift similarity index 99% rename from NBus/Classes/Handler/WeiboSDKHandler.swift rename to Sources/NBusWeiboSDKHandler/WeiboSDKHandler.swift index a1d6275..46c6a48 100644 --- a/NBus/Classes/Handler/WeiboSDKHandler.swift +++ b/Sources/NBusWeiboSDKHandler/WeiboSDKHandler.swift @@ -30,7 +30,7 @@ public class WeiboSDKHandler { public let appID: String public let universalLink: URL - private let redirectLink: URL + public let redirectLink: URL private var coordinator: Coordinator! diff --git a/Tests/NBusTests/Base/GeneralTestCase.swift b/Tests/NBusTests/Base/GeneralTestCase.swift new file mode 100644 index 0000000..e36e860 --- /dev/null +++ b/Tests/NBusTests/Base/GeneralTestCase.swift @@ -0,0 +1,112 @@ +// +// GeneralTestCase.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +import RxRelay +import XCTest + +// MARK: - General - Scheme + +protocol GeneralSchemeTestCase: XCTestCase { + + /// Report general scheme + func report_general_scheme() -> Set +} + +// MARK: - General - UniversalLink - Request + +protocol GeneralUniversalLinkRequestTestCase: XCTestCase { + + /// Test general universal link request scheme + func test_general_ul_request(scheme: @autoclosure () throws -> String) + + /// Test general universal link request host + func test_general_ul_request(host: @autoclosure () throws -> String) + + /// Test general universal link request queryItems + func test_general_ul_request(queryItems: inout [URLQueryItem]) +} + +// MARK: - General - Pasteboard - Request + +protocol GeneralPasteboardRequestTestCase: XCTestCase { + + /// Extract pasteboard request major data + func extract_major_pb_request(items: inout [[String: Data]]) -> [String: Any] + + /// Test general pasteboard request dictionary + func test_general_pb_request(dictionary: inout [String: Any]) + + /// Test pasteboard request extra data + func test_extra_pb_request(items: inout [[String: Data]]) +} + +// MARK: - General - URLScheme - Response + +protocol GeneralURLSchemeResponseTestCase: XCTestCase { + + /// Test general url scheme response scheme + func test_general_us_response(scheme: @autoclosure () throws -> String) + + /// Test general url scheme response host + func test_general_us_response(host: @autoclosure () throws -> String) + + /// Test general url scheme response queryItems + func test_general_us_response(queryItems: inout [URLQueryItem]) +} + +// MARK: - General - UniversalLink - Response + +protocol GeneralUniversalLinkResponseTestCase: XCTestCase { + + /// Test general universal link response scheme + func test_general_ul_response(scheme: @autoclosure () throws -> String) + + /// Test general universal link response host + func test_general_ul_response(host: @autoclosure () throws -> String) + + /// Test general universal link response queryItems + func test_general_ul_response(queryItems: inout [URLQueryItem]) +} + +// MARK: - General - Pasteboard - Response + +protocol GeneralPasteboardResponseTestCase: XCTestCase { + + /// Extract pasteboard response major data + func extract_major_pb_response(items: inout [[String: Data]]) -> [String: Any] + + /// Test general pasteboard response dictionary + func test_general_pb_response(dictionary: inout [String: Any]) + + /// Test pasteboard response extra data + func test_extra_pb_response(items: inout [[String: Data]]) +} + +// MARK: - General - Completion + +protocol GeneralCompletionTestCase: XCTestCase { + + /// scheme relay + static var schemeRelay: PublishRelay { get } + + /// universal link request relay + static var universalLinkRequestRelay: PublishRelay { get } + + /// pasteboard request relay + static var pasteboardRequestRelay: PublishRelay<[[String: Any]]> { get } + + /// url scheme response relay + static var urlSchemeResponseRelay: PublishRelay { get } + + /// universal link response relay + static var universalLinkResponseRelay: PublishRelay { get } + + /// pasteboard response relay + static var pasteboardResponseRelay: PublishRelay<[[String: Any]]> { get } +} diff --git a/Tests/NBusTests/Base/HandlerBaseTests+General.swift b/Tests/NBusTests/Base/HandlerBaseTests+General.swift new file mode 100644 index 0000000..3b80f9c --- /dev/null +++ b/Tests/NBusTests/Base/HandlerBaseTests+General.swift @@ -0,0 +1,53 @@ +// +// HandlerBaseTests+General.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation + +// MARK: - General - UniversalLink - Request + +extension GeneralUniversalLinkRequestTestCase { + + /// Extract url using JSON + func extract_JSON_ul(queryItems: inout [URLQueryItem], key: String) -> [String: Any] { + let item = queryItems.removeFirst { $0.name == key }! + + let data = Data(base64Encoded: item.value!)! + let dictionary = try! JSONSerialization.jsonObject(with: data) as! [String: Any] + + return dictionary + } +} + +// MARK: - General - Pasteboard - Request + +extension GeneralPasteboardRequestTestCase { + + /// Extract pasteboard using KeyedArchiver + func extract_KeyedArchiver_pb(items: inout [[String: Data]], key: String) -> [String: Any] { + let item = items.removeFirst { $0.keys.contains(key) }! + + precondition(item.count == 1) + + let data = item[key]! + let dictionary = NSKeyedUnarchiver.unarchiveObject(with: data) as! [String: Any] + + return dictionary + } + + /// Extract pasteboard using PropertyList + func extract_PropertyList_pb(items: inout [[String: Data]], key: String) -> [String: Any] { + let item = items.removeFirst { $0.keys.contains(key) }! + + precondition(item.count == 1) + + let data = item[key]! + let dictionary = try! PropertyListSerialization.propertyList(from: data, format: nil) as! [String: Any] + + return dictionary + } +} diff --git a/Tests/NBusTests/Base/HandlerBaseTests+Launch.swift b/Tests/NBusTests/Base/HandlerBaseTests+Launch.swift new file mode 100644 index 0000000..3b4c03b --- /dev/null +++ b/Tests/NBusTests/Base/HandlerBaseTests+Launch.swift @@ -0,0 +1,145 @@ +// +// HandlerBaseTests+Launch.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import RxCocoa +import RxSwift +import XCTest + +// MARK: - Launch + +extension LaunchTestCase { + + func test_launch(_ platform: Platform, _ program: MiniProgramMessage) { + UIApplication.shared.rx + .canOpenURL() + .bind(onNext: { [unowned self] url in + self._test_launch(scheme: url, platform, program) + }) + .disposed(by: disposeBag) + + UIApplication.shared.rx + .openURL() + .bind(onNext: { [unowned self] url in + self._test_launch_request(url: url, platform, program) + }) + .disposed(by: disposeBag) + + UIPasteboard.general.rx + .items() + .filter { !$0.allSatisfy { $0.isEmpty } } + .bind(onNext: { [unowned self] items in + self._test_launch_request(items: items, platform, program) + }) + .disposed(by: disposeBag) + + Bus.shared.launch( + program: program, + with: platform, + completionHandler: { [unowned self] result in + self._test_launch(result: result, platform, program) + } + ) + } +} + +// MARK: - Launch - Scheme + +extension _LaunchSchemeTestCase { + + func _test_launch(scheme: URL, _ platform: Platform, _ program: MiniProgramMessage) { + var schemeList: Set = [] + schemeList.formUnion(report_general_scheme()) + schemeList.formUnion(report_launch_scheme(platform, program)) + + XCTAssertTrue(schemeList.contains(try XCTUnwrap(scheme.scheme))) + } +} + +// MARK: - Launch - UniversalLink - Request + +extension _LaunchUniversalLinkRequestTestCase { + + func _test_launch_request(url: URL, _ platform: Platform, _ program: MiniProgramMessage) { + let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)! + var queryItems = urlComponents.queryItems ?? [] + + logger.debug("\(URLComponents.self), start, \(queryItems.map(\.name).sorted())") + + // General - UniversalLink - Request + + test_general_ul_request(scheme: try XCTUnwrap(urlComponents.scheme)) + test_general_ul_request(host: try XCTUnwrap(urlComponents.host)) + test_general_ul_request(queryItems: &queryItems) + + // Launch - Program - UniversalLink - Request + + test_launch_ul_request(path: urlComponents.path) + test_launch_ul_request(queryItems: &queryItems, platform, program) + + logger.debug("\(URLComponents.self), end, \(queryItems.map(\.name).sorted())") + + XCTAssertTrue(queryItems.isEmpty) + + ulExpectation.fulfill() + } +} + +// MARK: - Launch - Pasteboard - Request + +extension _LaunchPasteboardRequestTestCase { + + func _test_launch_request(items: [[String: Any]], _ platform: Platform, _ program: MiniProgramMessage) { + var items = items as! [[String: Data]] + + logger.debug("\(UIPasteboard.self), start, \(items.map { $0.keys.sorted() })") + + _test_launch_pb_request(dictionary: extract_major_pb_request(items: &items), platform, program) + + test_extra_pb_request(items: &items) + + logger.debug("\(UIPasteboard.self), end, \(items.map { $0.keys.sorted() })") + + XCTAssertTrue(items.isEmpty) + + pbExpectation.fulfill() + } + + func _test_launch_pb_request(dictionary: [String: Any], _ platform: Platform, _ program: MiniProgramMessage) { + var dictionary = dictionary + + logger.debug("\(UIPasteboard.self), start, \(dictionary.keys.sorted())") + + // General - Pasteboard - Request + + test_general_pb_request(dictionary: &dictionary) + + // Launch - Program - Pasteboard - Request + + test_launch_pb_request(dictionary: &dictionary, platform, program) + + logger.debug("\(UIPasteboard.self), end, \(dictionary.keys.sorted())") + + XCTAssertTrue(dictionary.isEmpty) + } +} + +// MARK: - Launch - Completion + +extension _LaunchCompletionTestCase { + + func _test_launch(result: Result, _ platform: Platform, _ program: MiniProgramMessage) { + switch result { + case .success: + XCTAssertTrue(true) + case .failure: + XCTAssertTrue(false) + } + } +} diff --git a/Tests/NBusTests/Base/HandlerBaseTests+Oauth.swift b/Tests/NBusTests/Base/HandlerBaseTests+Oauth.swift new file mode 100644 index 0000000..ca60622 --- /dev/null +++ b/Tests/NBusTests/Base/HandlerBaseTests+Oauth.swift @@ -0,0 +1,165 @@ +// +// HandlerBaseTests+Oauth.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import RxCocoa +import RxSwift +import XCTest + +// MARK: - Oauth + +extension OauthTestCase { + + func test_oauth(_ platform: Platform) { + UIApplication.shared.rx + .canOpenURL() + .bind(onNext: { [unowned self] url in + self._test_oauth(scheme: url, platform) + }) + .disposed(by: disposeBag) + + UIApplication.shared.rx + .openURL() + .bind(onNext: { [unowned self] url in + self._test_oauth_request(url: url, platform) + }) + .disposed(by: disposeBag) + + UIPasteboard.general.rx + .items() + .skip(while: { [unowned self] items in + if self._avoid_oauth_pb_error(items, platform) { + precondition(items.pasteboardString() == AppState.defaultPasteboardString) + + self.pbExpectation.fulfill() + + return true + } + + if self.context.setPasteboardString { + return items.pasteboardString() == AppState.defaultPasteboardString + } + + return false + }) + .filter { !$0.allSatisfy { $0.isEmpty } } + .bind(onNext: { [unowned self] items in + self._test_oauth_request(items: items, platform) + }) + .disposed(by: disposeBag) + + Bus.shared.oauth( + with: platform, + completionHandler: { [unowned self] result in + self._test_oauth(result: result, platform) + } + ) + + wait(for: [ulExpectation, pbExpectation], timeout: 5) + } +} + +// MARK: - Oauth - Scheme + +extension _OauthSchemeTestCase { + + func _test_oauth(scheme: URL, _ platform: Platform) { + var schemeList: Set = [] + schemeList.formUnion(report_general_scheme()) + schemeList.formUnion(report_oauth_scheme(platform)) + + XCTAssertTrue(schemeList.contains(try XCTUnwrap(scheme.scheme))) + } +} + +// MARK: - Oauth - UniversalLink - Request + +extension _OauthUniversalLinkRequestTestCase { + + func _test_oauth_request(url: URL, _ platform: Platform) { + let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)! + var queryItems = urlComponents.queryItems ?? [] + + logger.debug("\(URLComponents.self), start, \(queryItems.map(\.name).sorted())") + + // General - UniversalLink - Request + + test_general_ul_request(scheme: try XCTUnwrap(urlComponents.scheme)) + test_general_ul_request(host: try XCTUnwrap(urlComponents.host)) + test_general_ul_request(queryItems: &queryItems) + + // Oauth - Platform - UniversalLink - Request + + test_oauth_ul_request(path: urlComponents.path) + test_oauth_ul_request(queryItems: &queryItems, platform) + + logger.debug("\(URLComponents.self), end, \(queryItems.map(\.name).sorted())") + + XCTAssertTrue(queryItems.isEmpty) + + ulExpectation.fulfill() + } +} + +// MARK: - Oauth - Pasteboard - Request + +extension _OauthPasteboardRequestTestCase { + + func _test_oauth_request(items: [[String: Any]], _ platform: Platform) { + var items = items as! [[String: Data]] + + logger.debug("\(UIPasteboard.self), start, \(items.map { $0.keys.sorted() })") + + _test_oauth_pb_request(dictionary: extract_major_pb_request(items: &items), platform) + + test_extra_pb_request(items: &items) + + logger.debug("\(UIPasteboard.self), end, \(items.map { $0.keys.sorted() })") + + XCTAssertTrue(items.isEmpty) + + pbExpectation.fulfill() + } + + func _test_oauth_pb_request(dictionary: [String: Any], _ platform: Platform) { + var dictionary = dictionary + + logger.debug("\(UIPasteboard.self), start, \(dictionary.keys.sorted())") + + // General - Pasteboard - Request + + test_general_pb_request(dictionary: &dictionary) + + // Oauth - Platform - Pasteboard - Request + + test_oauth_pb_request(dictionary: &dictionary, platform) + + logger.debug("\(UIPasteboard.self), end, \(dictionary.keys.sorted())") + + XCTAssertTrue(dictionary.isEmpty) + } + + func _avoid_oauth_pb_error(_ items: [[String: Any]], _ platform: Platform) -> Bool { + platform == Platforms.qq + } +} + +// MARK: - Oauth - Completion + +extension _OauthCompletionTestCase { + + func _test_oauth(result: Result<[Bus.OauthInfoKey: String], Bus.Error>, _ platform: Platform) { + switch result { + case .success: + XCTAssertTrue(true) + case .failure: + XCTAssertTrue(false) + } + } +} diff --git a/Tests/NBusTests/Base/HandlerBaseTests+Share.swift b/Tests/NBusTests/Base/HandlerBaseTests+Share.swift new file mode 100644 index 0000000..a162c8b --- /dev/null +++ b/Tests/NBusTests/Base/HandlerBaseTests+Share.swift @@ -0,0 +1,317 @@ +// +// HandlerBaseTests+Share.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import RxCocoa +import RxSwift +import XCTest + +// MARK: - Share + +extension ShareTestCase { + + func test_share(_ message: MessageType, _ endpoint: Endpoint) { + Self.schemeRelay + .bind(onNext: { [unowned self] url in + if url.scheme == "mqqopensdknopasteboard" { + self.context.skipPasteboard = true + } + + self._test_share(scheme: url, message, endpoint) + }) + .disposed(by: disposeBag) + + Self.universalLinkRequestRelay + .bind(onNext: { [unowned self] url in + if context.shareState == .responseSignToken { + context.shareState = .requestSecond + } + + self._test_share_request(url: url, message, endpoint) + }) + .disposed(by: disposeBag) + + Self.pasteboardRequestRelay + .bind(onNext: { [unowned self] items in + self._test_share_request(items: items, message, endpoint) + }) + .disposed(by: disposeBag) + + Self.urlSchemeResponseRelay + .bind(onNext: { [unowned self] url in + precondition(self.context.shareState == .requestFirst) + + self.context.shareState = .responseURLScheme + + self._test_share_response(us: url, message, endpoint) + }) + .disposed(by: disposeBag) + + Self.universalLinkResponseRelay + .bind(onNext: { [unowned self] url in + if endpoint.toPlatform == Platforms.qq { + if url.path.hasSuffix("\(self.context.bundleID)/mqqsignapp") { + self.context.shareState = .responseSignToken + } else if url.path.hasSuffix("\(self.context.bundleID)") { + self.context.shareState = .responseUniversalLink + } + } + + if endpoint.toPlatform == Platforms.wechat { + if url.path.hasSuffix("\(self.context.appID!)/refreshToken") { + self.context.shareState = .responseSignToken + } else if url.path.hasSuffix("\(self.context.appID!)") { + self.context.shareState = .responseUniversalLink + } + } + + if endpoint.toPlatform == Platforms.weibo { + if url.query!.contains("checkStatus") { + self.context.shareState = .responseSignToken + } else if url.query!.contains("sdkversion") { + self.context.shareState = .responseUniversalLink + } + } + + self._test_share_response(url: url, message, endpoint) + }) + .disposed(by: disposeBag) + + Self.pasteboardResponseRelay + .bind(onNext: { [unowned self] items in + if self.context.skipPasteboard, self.context.shareState == .success || self.context.shareState == .failure { + self._test_share_request(items: items, message, endpoint) + return + } + + self._test_share_response(items: items, message, endpoint) + }) + .disposed(by: disposeBag) + + context.shareState = .requestFirst + + Bus.shared.share( + message: message, + to: endpoint, + completionHandler: { [unowned self] result in + if case .failure(.unsupportedMessage) = result { + self.context.skipCompletion = true + } + + self._test_share(result: result, message, endpoint) + } + ) + + wait(for: [ulExpectation, pbExpectation], timeout: 30) + } +} + +// MARK: - Share - Scheme + +extension _ShareSchemeTestCase { + + func _test_share(scheme: URL, _ message: MessageType, _ endpoint: Endpoint) { + var schemeList: Set = [] + schemeList.formUnion(report_general_scheme()) + schemeList.formUnion(report_share_scheme(message, endpoint)) + + XCTAssertTrue(schemeList.contains(try XCTUnwrap(scheme.scheme))) + } +} + +// MARK: - Share - UniversalLink - Request + +extension _ShareUniversalLinkRequestTestCase { + + func _test_share_request(url: URL, _ message: MessageType, _ endpoint: Endpoint) { + let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)! + var queryItems = urlComponents.queryItems ?? [] + + logger.debug("\(URLComponents.self), start, \(queryItems.map(\.name).sorted())") + + // General - UniversalLink - Request + + test_general_ul_request(scheme: try XCTUnwrap(urlComponents.scheme)) + test_general_ul_request(host: try XCTUnwrap(urlComponents.host)) + test_general_ul_request(queryItems: &queryItems) + + // Share - Message - UniversalLink - Request + + test_share_ul_request(path: urlComponents.path) + test_share_ul_request(queryItems: &queryItems, message, endpoint) + + logger.debug("\(URLComponents.self), end, \(queryItems.map(\.name).sorted())") + + XCTAssertTrue(queryItems.isEmpty) + } +} + +// MARK: - Share - Pasteboard - Request + +extension _SharePasteboardRequestTestCase { + + func _test_share_request(items: [[String: Any]], _ message: MessageType, _ endpoint: Endpoint) { + var items = items as! [[String: Data]] + + logger.debug("\(UIPasteboard.self), start, \(items.map { $0.keys.sorted() })") + + _test_share_pb_request(dictionary: extract_major_pb_request(items: &items), message, endpoint) + + test_extra_pb_request(items: &items) + + logger.debug("\(UIPasteboard.self), end, \(items.map { $0.keys.sorted() })") + + XCTAssertTrue(items.isEmpty) + } + + func _test_share_pb_request(dictionary: [String: Any], _ message: MessageType, _ endpoint: Endpoint) { + var dictionary = dictionary + + logger.debug("\(UIPasteboard.self), start, \(dictionary.keys.sorted())") + + // General - Pasteboard - Request + + test_general_pb_request(dictionary: &dictionary) + + // Share - Message - Pasteboard - Request + + test_share_pb_request(dictionary: &dictionary, message, endpoint) + + logger.debug("\(UIPasteboard.self), end, \(dictionary.keys.sorted())") + + XCTAssertTrue(dictionary.isEmpty) + } +} + +// MARK: - Share - URLScheme - Response + +extension _ShareURLSchemeResponseTestCase { + + func _test_share_response(us: URL, _ message: MessageType, _ endpoint: Endpoint) { + let urlComponents = URLComponents(url: us, resolvingAgainstBaseURL: false)! + var queryItems = urlComponents.queryItems ?? [] + + logger.debug("\(URLComponents.self), start, \(queryItems.map(\.name).sorted())") + + // General - URLScheme - Response + + test_general_us_response(scheme: try XCTUnwrap(urlComponents.scheme)) + test_general_us_response(host: try XCTUnwrap(urlComponents.host)) + test_general_us_response(queryItems: &queryItems) + + // Share - Message - URLScheme - Response + + test_share_us_response(path: urlComponents.path) + test_share_us_response(queryItems: &queryItems, message, endpoint) + + logger.debug("\(URLComponents.self), end, \(queryItems.map(\.name).sorted())") + + XCTAssertTrue(queryItems.isEmpty) + } +} + +// MARK: - Share - UniversalLink - Response + +extension _ShareUniversalLinkResponseTestCase { + + func _test_share_response(url: URL, _ message: MessageType, _ endpoint: Endpoint) { + let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)! + var queryItems = urlComponents.queryItems ?? [] + + logger.debug("\(URLComponents.self), start, \(queryItems.map(\.name).sorted())") + + // General - UniversalLink - Response + + test_general_ul_response(scheme: try XCTUnwrap(urlComponents.scheme)) + test_general_ul_response(host: try XCTUnwrap(urlComponents.host)) + test_general_ul_response(queryItems: &queryItems) + + // Share - Message - UniversalLink - Response + + test_share_ul_response(path: urlComponents.path) + test_share_ul_response(queryItems: &queryItems, message, endpoint) + + logger.debug("\(URLComponents.self), end, \(queryItems.map(\.name).sorted())") + + XCTAssertTrue(queryItems.isEmpty) + } +} + +// MARK: - Share - Pasteboard - Response + +extension _SharePasteboardResponseTestCase { + + func _test_share_response(items: [[String: Any]], _ message: MessageType, _ endpoint: Endpoint) { + var items = items as! [[String: Data]] + + logger.debug("\(UIPasteboard.self), start, \(items.map { $0.keys.sorted() })") + + _test_share_pb_response(dictionary: extract_major_pb_response(items: &items), message, endpoint) + + test_extra_pb_response(items: &items) + + logger.debug("\(UIPasteboard.self), end, \(items.map { $0.keys.sorted() })") + + XCTAssertTrue(items.isEmpty) + } + + func _test_share_pb_response(dictionary: [String: Any], _ message: MessageType, _ endpoint: Endpoint) { + var dictionary = dictionary + + logger.debug("\(UIPasteboard.self), start, \(dictionary.keys.sorted())") + + // General - Pasteboard - Response + + test_general_pb_response(dictionary: &dictionary) + + // Share - Message - Pasteboard - Response + + test_share_pb_response(dictionary: &dictionary, message, endpoint) + + logger.debug("\(UIPasteboard.self), end, \(dictionary.keys.sorted())") + + XCTAssertTrue(dictionary.isEmpty) + } +} + +// MARK: - Share - Completion + +extension _ShareCompletionTestCase { + + func _test_share(result: Result, _ message: MessageType, _ endpoint: Endpoint) { + switch result { + case .success: + if context.shareState == .success { + XCTAssertTrue(true) + break + } + + XCTAssertTrue(false) + case let .failure(error): + logger.error("\(error)") + + if context.skipCompletion { + XCTAssertTrue(true) + break + } + + if context.shareState == .failure { + XCTAssertTrue(true) + break + } + + XCTAssertTrue(false) + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [unowned self] in + self.ulExpectation.fulfill() + self.pbExpectation.fulfill() + } + } +} diff --git a/Tests/NBusTests/Base/HandlerBaseTests.swift b/Tests/NBusTests/Base/HandlerBaseTests.swift new file mode 100644 index 0000000..c1218ab --- /dev/null +++ b/Tests/NBusTests/Base/HandlerBaseTests.swift @@ -0,0 +1,223 @@ +// +// HandlerBaseTests.swift +// BusTests +// +// Created by nuomi1 on 2022/3/30. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import RxCocoa +import RxRelay +import RxSwift +import XCTest + +class HandlerBaseTests: XCTestCase { + + var appID: String { + fatalError() + } + + final var appNumber: String { + appID.trimmingCharacters(in: .letters) + } + + final var bundleID: String { + Bundle.main.bus.identifier! + } + + var category: AppState.PlatformItem.Category { + fatalError() + } + + final var displayName: String { + Bundle.main.bus.displayName! + } + + var handler: HandlerType { + fatalError() + } + + var sdkShortVersion: String { + fatalError() + } + + var sdkVersion: String { + fatalError() + } + + var universalLink: URL { + fatalError() + } + + static var disposeBag = DisposeBag() + var disposeBag = DisposeBag() + + static let schemeRelay = PublishRelay() + + static let universalLinkRequestRelay = PublishRelay() + + static let pasteboardRequestRelay = PublishRelay<[[String: Any]]>() + + static let urlSchemeResponseRelay = PublishRelay() + + static let universalLinkResponseRelay = PublishRelay() + + static let pasteboardResponseRelay = PublishRelay<[[String: Any]]>() + + var context = HandlerTestContext() + + let ulExpectation = XCTestExpectation(description: "UniversalLink") + let pbExpectation = XCTestExpectation(description: "Pasteboard") + + override class func setUp() { + super.setUp() + + UIApplication.shared.rx + .canOpenURL() + .bind(onNext: { url in + schemeRelay.accept(url) + }) + .disposed(by: disposeBag) + + UIApplication.shared.rx + .openURL() + .bind(onNext: { url in + universalLinkRequestRelay.accept(url) + + let items = UIPasteboard.general.items + + if filter(items) { + pasteboardRequestRelay.accept(items) + } + }) + .disposed(by: disposeBag) + + NotificationCenter.default.rx + .openURL() + .bind(onNext: { url, items in + urlSchemeResponseRelay.accept(url) + + if filter(items) { + pasteboardResponseRelay.accept(items) + } + }) + .disposed(by: disposeBag) + + NotificationCenter.default.rx + .openUserActivity() + .bind(onNext: { userActivity, items in + universalLinkResponseRelay.accept(userActivity.webpageURL!) + + if filter(items) { + pasteboardResponseRelay.accept(items) + } + }) + .disposed(by: disposeBag) + + NotificationCenter.default.rx + .openURL() + .bind(onNext: { url, items in + logger.debug("\(url)") + logger.debug("\(items.map { $0.keys.sorted() })") + }) + .disposed(by: disposeBag) + + NotificationCenter.default.rx + .openUserActivity() + .bind(onNext: { userActivity, items in + logger.debug("\(userActivity.webpageURL!)") + logger.debug("\(items.map { $0.keys.sorted() })") + }) + .disposed(by: disposeBag) + + UIApplication.shared.rx + .canOpenURL() + .bind(onNext: { url in + logger.debug("\(url)") + }) + .disposed(by: disposeBag) + + UIApplication.shared.rx + .openURL() + .bind(onNext: { url in + logger.debug("\(url)") + }) + .disposed(by: disposeBag) + + UIPasteboard.general.rx + .items() + .bind(onNext: { items in + logger.debug("\(items.map { $0.keys.sorted() })") + }) + .disposed(by: disposeBag) + + NotificationCenter.default.rx + .openURL() + .bind(onNext: { url, _ in + openURL(url) + }) + .disposed(by: disposeBag) + + NotificationCenter.default.rx + .openUserActivity() + .bind(onNext: { userActivity, _ in + openUserActivity(userActivity) + }) + .disposed(by: disposeBag) + } + + static func openURL(_ url: URL) { + let result = Bus.shared.openURL(url) + + NotificationCenter.default.post( + name: AppState.OpenURL.responseName, + object: nil, + userInfo: [ + AppState.OpenURL.responseResultKey: result, + ] + ) + } + + static func openUserActivity(_ userActivity: NSUserActivity) { + let result = Bus.shared.openUserActivity(userActivity) + + NotificationCenter.default.post( + name: AppState.OpenUserActivity.responseName, + object: nil, + userInfo: [ + AppState.OpenUserActivity.responseResultKey: result, + ] + ) + } + + static func filter(_ items: [[String: Any]]) -> Bool { + !items.allSatisfy { $0.isEmpty } + && items.pasteboardString() != AppState.defaultPasteboardString + } +} + +extension HandlerBaseTests { + + override func setUp() { + super.setUp() + + context.setPasteboardString = true + + context.appID = appID + context.universalLink = universalLink + + AppState.shared.clearPasteboard(shouldSetString: context.setPasteboardString) + AppState.shared.clearKeychains() + AppState.shared.clearUserDefaults() + + Bus.shared.handlers = [handler] + } + + override func tearDown() { + super.tearDown() + + disposeBag = DisposeBag() + } +} diff --git a/Tests/NBusTests/Base/LaunchTestCase.swift b/Tests/NBusTests/Base/LaunchTestCase.swift new file mode 100644 index 0000000..b28d584 --- /dev/null +++ b/Tests/NBusTests/Base/LaunchTestCase.swift @@ -0,0 +1,105 @@ +// +// LaunchTestCase.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import RxSwift +import XCTest + +// MARK: - Launch - Program - Scheme + +protocol LaunchProgramSchemeTestCase: XCTestCase { + + /// Report launch scheme + func report_launch_scheme(_ platform: Platform, _ program: MiniProgramMessage) -> Set +} + +// MARK: - Launch - Scheme + +protocol _LaunchSchemeTestCase: + GeneralSchemeTestCase, + LaunchProgramSchemeTestCase { + + /// Test launch scheme + func _test_launch(scheme: URL, _ platform: Platform, _ program: MiniProgramMessage) +} + +// MARK: - Launch - Program - UniversalLink - Request + +protocol LaunchProgramUniversalLinkRequestTestCase: XCTestCase { + + /// Test launch universal link request path + func test_launch_ul_request(path: String) + + /// Test launch universal link request queryItems + func test_launch_ul_request(queryItems: inout [URLQueryItem], _ platform: Platform, _ program: MiniProgramMessage) +} + +// MARK: - Launch - UniversalLink - Request + +protocol _LaunchUniversalLinkRequestTestCase: + GeneralUniversalLinkRequestTestCase, + LaunchProgramUniversalLinkRequestTestCase { + + /// Universal link expectation + var ulExpectation: XCTestExpectation { get } + + /// Test launch universal link request + func _test_launch_request(url: URL, _ platform: Platform, _ program: MiniProgramMessage) +} + +// MARK: - Launch - Platform - Pasteboard - Request + +protocol LaunchProgramPasteboardRequestTestCase: XCTestCase { + + /// Test launch pasteboard request dictionary + func test_launch_pb_request(dictionary: inout [String: Any], _ platform: Platform, _ program: MiniProgramMessage) +} + +// MARK: - Launch - Pasteboard - Request + +protocol _LaunchPasteboardRequestTestCase: + GeneralPasteboardRequestTestCase, + LaunchProgramPasteboardRequestTestCase { + + /// Pasteboard expectation + var pbExpectation: XCTestExpectation { get } + + /// Test launch pasteboard request + func _test_launch_request(items: [[String: Any]], _ platform: Platform, _ program: MiniProgramMessage) + + /// Test launch pasteboard request dictionary + func _test_launch_pb_request(dictionary: [String: Any], _ platform: Platform, _ program: MiniProgramMessage) +} + +// MARK: - Launch - Completion + +protocol _LaunchCompletionTestCase: XCTestCase { + + /// Universal link expectation + var ulExpectation: XCTestExpectation { get } + + /// Pasteboard expectation + var pbExpectation: XCTestExpectation { get } + + /// Test launch completion + func _test_launch(result: Result, _ platform: Platform, _ program: MiniProgramMessage) +} + +// MARK: - Launch + +protocol LaunchTestCase: + _LaunchSchemeTestCase, + _LaunchUniversalLinkRequestTestCase, + _LaunchPasteboardRequestTestCase, + _LaunchCompletionTestCase { + + var disposeBag: DisposeBag { get } + + var context: HandlerTestContext { get } +} diff --git a/Tests/NBusTests/Base/OauthTestCase.swift b/Tests/NBusTests/Base/OauthTestCase.swift new file mode 100644 index 0000000..a8d2fcd --- /dev/null +++ b/Tests/NBusTests/Base/OauthTestCase.swift @@ -0,0 +1,108 @@ +// +// OauthTestCase.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import RxSwift +import XCTest + +// MARK: - Oauth - Platform - Scheme + +protocol OauthPlatformSchemeTestCase: XCTestCase { + + /// Report oauth scheme + func report_oauth_scheme(_ platform: Platform) -> Set +} + +// MARK: - Oauth - Scheme + +protocol _OauthSchemeTestCase: + GeneralSchemeTestCase, + OauthPlatformSchemeTestCase { + + /// Test oauth scheme + func _test_oauth(scheme: URL, _ platform: Platform) +} + +// MARK: - Oauth - Platform - UniversalLink - Request + +protocol OauthPlatformUniversalRequestLinkTestCase: XCTestCase { + + /// Test oauth universal link request path + func test_oauth_ul_request(path: String) + + /// Test oauth universal link request queryItems + func test_oauth_ul_request(queryItems: inout [URLQueryItem], _ platform: Platform) +} + +// MARK: - Oauth - UniversalLink - Request + +protocol _OauthUniversalLinkRequestTestCase: + GeneralUniversalLinkRequestTestCase, + OauthPlatformUniversalRequestLinkTestCase { + + /// Universal link expectation + var ulExpectation: XCTestExpectation { get } + + /// Test oauth universal link request + func _test_oauth_request(url: URL, _ platform: Platform) +} + +// MARK: - Oauth - Platform - Pasteboard - Request + +protocol OauthPlatformPasteboardRequestTestCase: XCTestCase { + + /// Test oauth pasteboard request dictionary + func test_oauth_pb_request(dictionary: inout [String: Any], _ platform: Platform) +} + +// MARK: - Oauth - Pasteboard - Request + +protocol _OauthPasteboardRequestTestCase: + GeneralPasteboardRequestTestCase, + OauthPlatformPasteboardRequestTestCase { + + /// Pasteboard expectation + var pbExpectation: XCTestExpectation { get } + + /// Test oauth pasteboard request + func _test_oauth_request(items: [[String: Any]], _ platform: Platform) + + /// Test oauth pasteboard request dictionary + func _test_oauth_pb_request(dictionary: [String: Any], _ platform: Platform) + + /// Avoid oauth pasteboard error + func _avoid_oauth_pb_error(_ items: [[String: Any]], _ platform: Platform) -> Bool +} + +// MARK: - Oauth - Completion + +protocol _OauthCompletionTestCase: XCTestCase { + + /// Universal link expectation + var ulExpectation: XCTestExpectation { get } + + /// Pasteboard expectation + var pbExpectation: XCTestExpectation { get } + + /// Test oauth completion + func _test_oauth(result: Result<[Bus.OauthInfoKey: String], Bus.Error>, _ platform: Platform) +} + +// MARK: - Oauth + +protocol OauthTestCase: + _OauthSchemeTestCase, + _OauthUniversalLinkRequestTestCase, + _OauthPasteboardRequestTestCase, + _OauthCompletionTestCase { + + var disposeBag: DisposeBag { get } + + var context: HandlerTestContext { get } +} diff --git a/Tests/NBusTests/Base/ShareTestCase.swift b/Tests/NBusTests/Base/ShareTestCase.swift new file mode 100644 index 0000000..347d6d4 --- /dev/null +++ b/Tests/NBusTests/Base/ShareTestCase.swift @@ -0,0 +1,167 @@ +// +// ShareTestCase.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import RxSwift +import XCTest + +// MARK: - Share - Message - Scheme + +protocol ShareMessageSchemeTestCase: XCTestCase { + + /// Report share scheme + func report_share_scheme(_ message: MessageType, _ endpoint: Endpoint) -> Set +} + +// MARK: - Share - Scheme + +protocol _ShareSchemeTestCase: + GeneralSchemeTestCase, + ShareMessageSchemeTestCase { + + /// Test share scheme + func _test_share(scheme: URL, _ message: MessageType, _ endpoint: Endpoint) +} + +// MARK: - Share - Message - UniversalLink - Request + +protocol ShareMessageUniversalLinkRequestTestCase: XCTestCase { + + /// Test share universal link request path + func test_share_ul_request(path: String) + + /// Test share universal link request queryItems + func test_share_ul_request(queryItems: inout [URLQueryItem], _ message: MessageType, _ endpoint: Endpoint) +} + +// MARK: - Share - UniversalLink - Request + +protocol _ShareUniversalLinkRequestTestCase: + GeneralUniversalLinkRequestTestCase, + ShareMessageUniversalLinkRequestTestCase { + + /// Test share universal link request + func _test_share_request(url: URL, _ message: MessageType, _ endpoint: Endpoint) +} + +// MARK: - Share - Message - Pasteboard - Request + +protocol ShareMessagePasteboardRequestTestCase: XCTestCase { + + /// Test share pasteboard request dictionary + func test_share_pb_request(dictionary: inout [String: Any], _ message: MessageType, _ endpoint: Endpoint) +} + +// MARK: - Share - Pasteboard - Request + +protocol _SharePasteboardRequestTestCase: + GeneralPasteboardRequestTestCase, + ShareMessagePasteboardRequestTestCase { + + /// Test share pasteboard request + func _test_share_request(items: [[String: Any]], _ message: MessageType, _ endpoint: Endpoint) + + /// Test share pasteboard request dictionary + func _test_share_pb_request(dictionary: [String: Any], _ message: MessageType, _ endpoint: Endpoint) +} + +// MARK: - Share - Message - URLScheme - Response + +protocol ShareMessageURLSchemeResponseTestCase: XCTestCase { + + /// Test share url scheme response path + func test_share_us_response(path: String) + + /// Test share url scheme response queryItems + func test_share_us_response(queryItems: inout [URLQueryItem], _ message: MessageType, _ endpoint: Endpoint) +} + +// MARK: - Share - URLScheme - Response + +protocol _ShareURLSchemeResponseTestCase: + GeneralURLSchemeResponseTestCase, + ShareMessageURLSchemeResponseTestCase { + + /// Test share url scheme response + func _test_share_response(us: URL, _ message: MessageType, _ endpoint: Endpoint) +} + +// MARK: - Share - Message - UniversalLink - Response + +protocol ShareMessageUniversalLinkResponseTestCase: XCTestCase { + + /// Test share universal link response path + func test_share_ul_response(path: String) + + /// Test share universal link response queryItems + func test_share_ul_response(queryItems: inout [URLQueryItem], _ message: MessageType, _ endpoint: Endpoint) +} + +// MARK: - Share - UniversalLink - Response + +protocol _ShareUniversalLinkResponseTestCase: + GeneralUniversalLinkResponseTestCase, + ShareMessageUniversalLinkResponseTestCase { + + /// Test share universal link response + func _test_share_response(url: URL, _ message: MessageType, _ endpoint: Endpoint) +} + +// MARK: - Share - Message - Pasteboard - Response + +protocol ShareMessagePasteboardResponseTestCase: XCTestCase { + + /// Test share pasteboard response dictionary + func test_share_pb_response(dictionary: inout [String: Any], _ message: MessageType, _ endpoint: Endpoint) +} + +// MARK: - Share - Pasteboard - Response + +protocol _SharePasteboardResponseTestCase: + GeneralPasteboardResponseTestCase, + ShareMessagePasteboardResponseTestCase { + + /// Test share pasteboard response + func _test_share_response(items: [[String: Any]], _ message: MessageType, _ endpoint: Endpoint) + + /// Test share pasteboard response dictionary + func _test_share_pb_response(dictionary: [String: Any], _ message: MessageType, _ endpoint: Endpoint) +} + +// MARK: - Share - Completion + +protocol _ShareCompletionTestCase: GeneralCompletionTestCase { + + /// Universal link expectation + var ulExpectation: XCTestExpectation { get } + + /// Pasteboard expectation + var pbExpectation: XCTestExpectation { get } + + var context: HandlerTestContext { get set } + + /// Test share completion + func _test_share(result: Result, _ message: MessageType, _ endpoint: Endpoint) +} + +// MARK: - Share + +protocol ShareTestCase: + _ShareSchemeTestCase, + _ShareUniversalLinkRequestTestCase, + _SharePasteboardRequestTestCase, + _ShareURLSchemeResponseTestCase, + _ShareUniversalLinkResponseTestCase, + _SharePasteboardResponseTestCase, + _ShareCompletionTestCase { + + var disposeBag: DisposeBag { get } + + var context: HandlerTestContext { get set } +} diff --git a/Tests/NBusTests/Helper.swift b/Tests/NBusTests/Helper.swift new file mode 100644 index 0000000..fb9aaf8 --- /dev/null +++ b/Tests/NBusTests/Helper.swift @@ -0,0 +1,66 @@ +// +// Helper.swift +// BusTests +// +// Created by nuomi1 on 2022/3/30. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +import UIKit + +extension Array { + + mutating func removeFirst(where shouldBeRemoved: (Element) throws -> Bool) rethrows -> Element? { + guard let index = try firstIndex(where: shouldBeRemoved) else { return nil } + return remove(at: index) + } +} + +extension Array where Element == [String: Any] { + + func pasteboardString() -> String? { + let typeListString = UIPasteboard.typeListString as! [String] + + let strings = flatMap { dictionary in + typeListString.compactMap { key in + dictionary[key] as? String + } + } + + precondition(strings.count <= 1) + + return strings.first + } +} + +struct HandlerTestContext { + + var setPasteboardString = false + + var skipPasteboard = false + var skipCompletion = false + + let bundleID = Bundle.main.bus.identifier! + + var appID: String! + var universalLink: URL! + + var signToken: String! + + var shareState: ShareState! +} + +extension HandlerTestContext { + + enum ShareState { + case requestFirst + case responseSignToken + case requestSecond + case responseURLScheme + case responseUniversalLink + case requestThird + case success + case failure + } +} diff --git a/Tests/NBusTests/Info.plist b/Tests/NBusTests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/Tests/NBusTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Tests/NBusTests/NBusTests.swift b/Tests/NBusTests/NBusTests.swift deleted file mode 100644 index 7598992..0000000 --- a/Tests/NBusTests/NBusTests.swift +++ /dev/null @@ -1,14 +0,0 @@ -@testable import NBusCore -import XCTest - -final class NBusTests: XCTestCase { - - func testExample() { - XCTAssertEqual(Bus.shared.isDebugEnabled, true) - XCTAssertEqual(Bus.shared.handlers.isEmpty, true) - } - - static var allTests = [ - ("testExample", testExample), - ] -} diff --git a/Tests/NBusTests/QQ/QQHandlerBaseTests+General.swift b/Tests/NBusTests/QQ/QQHandlerBaseTests+General.swift new file mode 100644 index 0000000..5877738 --- /dev/null +++ b/Tests/NBusTests/QQ/QQHandlerBaseTests+General.swift @@ -0,0 +1,193 @@ +// +// QQHandlerBaseTests+General.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +import XCTest + +// MARK: - General - Scheme + +extension QQHandlerBaseTests: GeneralSchemeTestCase { + + func report_general_scheme() -> Set { + [ + "mqq", + "mqqopensdkapiV2", + ] + } +} + +// MARK: - General - UniversalLink - Request + +extension QQHandlerBaseTests: GeneralUniversalLinkRequestTestCase { + + func test_general_ul_request(scheme: @autoclosure () throws -> String) { + XCTAssertEqual(try scheme(), "https") + } + + func test_general_ul_request(host: @autoclosure () throws -> String) { + XCTAssertEqual(try host(), "qm.qq.com") + } + + func test_general_ul_request(queryItems: inout [URLQueryItem]) { + let appsign_txid = queryItems.removeFirst { $0.name == "appsign_txid" }! + test_appsign_txid(appsign_txid) + + let bundleid = queryItems.removeFirst { $0.name == "bundleid" }! + test_bundleid(bundleid) + + let sdkv = queryItems.removeFirst { $0.name == "sdkv" }! + test_sdkv(sdkv) + } +} + +extension QQHandlerBaseTests { + + func test_appsign_txid(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), txID) + } + + func test_bundleid(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), bundleID.bus.base64EncodedString) + } + + func test_sdkv(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), sdkShortVersion) + } +} + +// MARK: - General - Pasteboard - Request + +extension QQHandlerBaseTests: GeneralPasteboardRequestTestCase { + + func extract_major_pb_request(items: inout [[String: Data]]) -> [String: Any] { + extract_KeyedArchiver_pb(items: &items, key: "com.tencent.mqq.api.apiLargeData") + } + + func test_general_pb_request(dictionary: inout [String: Any]) { + if !context.skipPasteboard, context.setPasteboardString { + let pasted_string = dictionary.removeValue(forKey: "pasted_string") as! String + test_pasted_string(pasted_string) + } + } + + func test_extra_pb_request(items: inout [[String: Data]]) { + XCTAssertTrue(true) + } +} + +extension QQHandlerBaseTests { + + func test_pasted_string(_ value: String) { + XCTAssertEqual(value, AppState.defaultPasteboardString) + } +} + +// MARK: - General - URLScheme - Response + +extension QQHandlerBaseTests: GeneralURLSchemeResponseTestCase { + + func test_general_us_response(scheme: @autoclosure () throws -> String) { + XCTAssertEqual(try scheme(), appID) + } + + func test_general_us_response(host: @autoclosure () throws -> String) { + XCTAssertEqual(try host(), "response_from_qq") + } + + func test_general_us_response(queryItems: inout [URLQueryItem]) { + let appsign_bundlenull = queryItems.removeFirst { $0.name == "appsign_bundlenull" }! + test_appsign_bundlenull_us(appsign_bundlenull) + + let source = queryItems.removeFirst { $0.name == "source" }! + test_source(source) + + let source_scheme = queryItems.removeFirst { $0.name == "source_scheme" }! + test_source_scheme(source_scheme) + + let version = queryItems.removeFirst { $0.name == "version" }! + test_version(version) + } +} + +extension QQHandlerBaseTests { + + func test_appsign_bundlenull_us(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "2") + } + + func test_source(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "qq") + } + + func test_source_scheme(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "mqqapi") + } +} + +// MARK: - General - UniversalLink - Response + +extension QQHandlerBaseTests: GeneralUniversalLinkResponseTestCase { + + func test_general_ul_response(scheme: @autoclosure () throws -> String) { + XCTAssertEqual(try scheme(), universalLink.scheme) + } + + func test_general_ul_response(host: @autoclosure () throws -> String) { + XCTAssertEqual(try host(), universalLink.host) + } + + func test_general_ul_response(queryItems: inout [URLQueryItem]) { + XCTAssertTrue(true) + } +} + +// MARK: - General - Pasteboard - Response + +extension QQHandlerBaseTests: GeneralPasteboardResponseTestCase { + + func extract_major_pb_response(items: inout [[String: Data]]) -> [String: Any] { + extract_KeyedArchiver_pb(items: &items, key: "com.tencent.\(appID)") + } + + func test_general_pb_response(dictionary: inout [String: Any]) { + let appsign_bundlenull = dictionary.removeValue(forKey: "appsign_bundlenull") as! String + test_appsign_bundlenull_pb(appsign_bundlenull) + + let appsign_redirect = dictionary.removeValue(forKey: "appsign_redirect") as! String + test_appsign_redirect(appsign_redirect) + + let appsign_retcode = dictionary.removeValue(forKey: "appsign_retcode") as! String + test_appsign_retcode(appsign_retcode) + + let appsign_token = dictionary.removeValue(forKey: "appsign_token") as! String + test_appsign_token(appsign_token) + } + + func test_extra_pb_response(items: inout [[String: Data]]) { + XCTAssertTrue(true) + } +} + +extension QQHandlerBaseTests { + + func test_appsign_bundlenull_pb(_ value: String) { + XCTAssertEqual(value, "2") + } + + func test_appsign_redirect(_ value: String) { + XCTAssertNotNil(URL(string: value)) + } + + func test_appsign_retcode(_ value: String) { + XCTAssertEqual(value, "25105") + } + + func test_appsign_token(_ value: String) { + XCTAssertEqual(value.count, 32) + } +} diff --git a/Tests/NBusTests/QQ/QQHandlerBaseTests+Launch.swift b/Tests/NBusTests/QQ/QQHandlerBaseTests+Launch.swift new file mode 100644 index 0000000..6c9780a --- /dev/null +++ b/Tests/NBusTests/QQ/QQHandlerBaseTests+Launch.swift @@ -0,0 +1,85 @@ +// +// QQHandlerBaseTests+Launch.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import XCTest + +// MARK: - Launch + +extension QQHandlerBaseTests: LaunchTestCase { + + func test_launch() { + test_launch(Platforms.qq, MediaSource.qqMiniProgram as! MiniProgramMessage) + } +} + +// MARK: - Launch - Program - Scheme + +extension QQHandlerBaseTests: LaunchProgramSchemeTestCase { + + func report_launch_scheme(_ platform: Platform, _ program: MiniProgramMessage) -> Set { + [ + "mqqopensdklaunchminiapp", + ] + } +} + +// MARK: - Launch - Program - UniversalLink - Request + +extension QQHandlerBaseTests: LaunchProgramUniversalLinkRequestTestCase { + + func test_launch_ul_request(path: String) { + XCTAssertEqual(path, "/opensdkul/mqqapi/profile/sdk_launch_mini_app") + } + + func test_launch_ul_request(queryItems: inout [URLQueryItem], _ platform: Platform, _ program: MiniProgramMessage) { + let appid = queryItems.removeFirst { $0.name == "appid" }! + test_appid(appid) + + let callback_name = queryItems.removeFirst { $0.name == "callback_name" }! + test_callback_name(callback_name) + + let callback_type = queryItems.removeFirst { $0.name == "callback_type" }! + test_callback_type(callback_type) + + let src_type = queryItems.removeFirst { $0.name == "src_type" }! + test_src_type(src_type) + + let thirdAppDisplayName = queryItems.removeFirst { $0.name == "thirdAppDisplayName" }! + test_thirdAppDisplayName(thirdAppDisplayName) + + let version = queryItems.removeFirst { $0.name == "version" }! + test_version(version) + + let mini_appid = queryItems.removeFirst { $0.name == "mini_appid" }! + test_mini_appid(mini_appid, program) + + let mini_path = queryItems.removeFirst { $0.name == "mini_path" }! + test_mini_path(mini_path, program) + + let mini_type = queryItems.removeFirst { $0.name == "mini_type" }! + test_mini_type(mini_type, program) + } +} + +extension QQHandlerBaseTests { + + func test_appid(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), appNumber) + } +} + +// MARK: - Launch - Program - Pasteboard - Request + +extension QQHandlerBaseTests: LaunchProgramPasteboardRequestTestCase { + + func test_launch_pb_request(dictionary: inout [String: Any], _ platform: Platform, _ program: MiniProgramMessage) { + XCTAssertTrue(true) + } +} diff --git a/Tests/NBusTests/QQ/QQHandlerBaseTests+Oauth.swift b/Tests/NBusTests/QQ/QQHandlerBaseTests+Oauth.swift new file mode 100644 index 0000000..590c384 --- /dev/null +++ b/Tests/NBusTests/QQ/QQHandlerBaseTests+Oauth.swift @@ -0,0 +1,169 @@ +// +// QQHandlerBaseTests+Oauth.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import XCTest + +// MARK: - Oauth + +extension QQHandlerBaseTests: OauthTestCase { + + func test_oauth() { + test_oauth(Platforms.qq) + } +} + +// MARK: - Oauth - Platform - Scheme + +extension QQHandlerBaseTests: OauthPlatformSchemeTestCase { + + func report_oauth_scheme(_ platform: Platform) -> Set { + [ + "mqqopensdknopasteboard", + ] + } +} + +// MARK: - Oauth - Platform - UniversalLink - Request + +extension QQHandlerBaseTests: OauthPlatformUniversalRequestLinkTestCase { + + func test_oauth_ul_request(path: String) { + XCTAssertEqual(path, "/opensdkul/mqqOpensdkSSoLogin/SSoLogin/\(appID)") + } + + func test_oauth_ul_request(queryItems: inout [URLQueryItem], _ platform: Platform) { + let objectlocation = queryItems.removeFirst { $0.name == "objectlocation" }! + test_objectlocation(objectlocation) + + let pasteboard = queryItems.removeFirst { $0.name == "pasteboard" }! + test_pasteboard(pasteboard) + } +} + +extension QQHandlerBaseTests { + + func test_objectlocation(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "url") + } + + func test_pasteboard(_ queryItem: URLQueryItem) { + let data = try! XCTUnwrap(Data(base64Encoded: XCTUnwrap(queryItem.value))) + var object = NSKeyedUnarchiver.unarchiveObject(with: data) as! [String: Any] + + logger.debug("\(URLComponents.self), start, \(object.keys.sorted())") + + let appsign_token = object.removeValue(forKey: "appsign_token") as! String + test_appsign_token(appsign_token) + + let app_id = object.removeValue(forKey: "app_id") as! String + test_app_id(app_id) + + let app_name = object.removeValue(forKey: "app_name") as! String + test_app_name(app_name) + + let bundleid = object.removeValue(forKey: "bundleid") as! String + test_bundleid_oauth(bundleid) + + let client_id = object.removeValue(forKey: "client_id") as! String + test_client_id(client_id) + + let refUniversallink = object.removeValue(forKey: "refUniversallink") as! String + test_refUniversallink(refUniversallink) + + let response_type = object.removeValue(forKey: "response_type") as! String + test_response_type(response_type) + + let scope = object.removeValue(forKey: "scope") as! String + test_scope(scope) + + let sdkp = object.removeValue(forKey: "sdkp") as! String + test_sdkp(sdkp) + + let sdkv = object.removeValue(forKey: "sdkv") as! String + test_sdkv_oauth(sdkv) + + let status_machine = object.removeValue(forKey: "status_machine") as! String + test_status_machine(status_machine) + + let status_os = object.removeValue(forKey: "status_os") as! String + test_status_os(status_os) + + let status_version = object.removeValue(forKey: "status_version") as! String + test_status_version(status_version) + + logger.debug("\(URLComponents.self), end, \(object.keys.sorted())") + + XCTAssertTrue(object.isEmpty) + } +} + +extension QQHandlerBaseTests { + + func test_appsign_token(_ value: String) { + XCTAssertEqual(value, "") + } + + func test_app_id(_ value: String) { + XCTAssertEqual(value, appNumber) + } + + func test_app_name(_ value: String) { + XCTAssertEqual(value, displayName) + } + + func test_bundleid_oauth(_ value: String) { + XCTAssertEqual(value, bundleID) + } + + func test_client_id(_ value: String) { + test_app_id(value) + } + + func test_refUniversallink(_ value: String) { + XCTAssertEqual(value, universalLink.absoluteString) + } + + func test_response_type(_ value: String) { + XCTAssertEqual(value, "token") + } + + func test_scope(_ value: String) { + XCTAssertEqual(value, "get_user_info") + } + + func test_sdkp(_ value: String) { + XCTAssertEqual(value, "i") + } + + func test_sdkv_oauth(_ value: String) { + XCTAssertEqual(value, sdkVersion) + } + + func test_status_machine(_ value: String) { + XCTAssertEqual(value, statusMachine) + } + + func test_status_os(_ value: String) { + XCTAssertEqual(value, statusOS) + } + + func test_status_version(_ value: String) { + XCTAssertEqual(value, statusVersion) + } +} + +// MARK: - Oauth - Platform - Pasteboard - Request + +extension QQHandlerBaseTests: OauthPlatformPasteboardRequestTestCase { + + func test_oauth_pb_request(dictionary: inout [String: Any], _ platform: Platform) { + XCTAssertTrue(true) + } +} diff --git a/Tests/NBusTests/QQ/QQHandlerBaseTests+Share.swift b/Tests/NBusTests/QQ/QQHandlerBaseTests+Share.swift new file mode 100644 index 0000000..155d3a5 --- /dev/null +++ b/Tests/NBusTests/QQ/QQHandlerBaseTests+Share.swift @@ -0,0 +1,730 @@ +// +// QQHandlerBaseTests+Share.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import XCTest + +// MARK: - Share + +extension QQHandlerBaseTests: ShareTestCase { + + func test_share_text_friend() { + test_share(MediaSource.text, Endpoints.QQ.friend) + } + + func test_share_text_timeline() { + test_share(MediaSource.text, Endpoints.QQ.timeline) + } + + func test_share_image_friend() { + test_share(MediaSource.qqImage, Endpoints.QQ.friend) + } + + func test_share_image_timeline() { + test_share(MediaSource.qqImage, Endpoints.QQ.timeline) + } + + func test_share_audio_friend() { + test_share(MediaSource.audio, Endpoints.QQ.friend) + } + + func test_share_audio_timeline() { + test_share(MediaSource.text, Endpoints.QQ.timeline) + } + + func test_share_video_friend() { + test_share(MediaSource.video, Endpoints.QQ.friend) + } + + func test_share_video_timeline() { + test_share(MediaSource.text, Endpoints.QQ.timeline) + } + + func test_share_webPage_friend() { + test_share(MediaSource.webPage, Endpoints.QQ.friend) + } + + func test_share_webPage_timeline() { + test_share(MediaSource.webPage, Endpoints.QQ.timeline) + } + + func test_share_file_friend() { + test_share(MediaSource.file, Endpoints.QQ.friend) + } + + func test_share_file_timeline() { + test_share(MediaSource.file, Endpoints.QQ.timeline) + } + + func test_share_miniProgram_friend() { + test_share(MediaSource.qqMiniProgram, Endpoints.QQ.friend) + } + + func test_share_miniProgram_timeline() { + test_share(MediaSource.webPage, Endpoints.QQ.timeline) + } +} + +// MARK: - Share - Message - Scheme + +extension QQHandlerBaseTests: ShareMessageSchemeTestCase { + + func report_share_scheme(_ message: MessageType, _ endpoint: Endpoint) -> Set { + switch message { + case is TextMessage, + is ImageMessage, + is AudioMessage, + is VideoMessage, + is FileMessage: + return [] + case is WebPageMessage: + return [ + "mqqopensdknopasteboard", + ] + case is MiniProgramMessage: + return [ + "mqqopensdkminiapp", + "mqqopensdknopasteboard", + ] + default: + fatalError() + } + } +} + +// MARK: - Share - Message - UniversalLink - Request + +extension QQHandlerBaseTests: ShareMessageUniversalLinkRequestTestCase { + + func test_share_ul_request(path: String) { + XCTAssertEqual(path, "/opensdkul/mqqapi/share/to_fri") + } + + func test_share_ul_request(queryItems: inout [URLQueryItem], _ message: MessageType, _ endpoint: Endpoint) { + if context.shareState == .requestSecond { + let appsign_token = queryItems.removeFirst(where: { $0.name == "appsign_token" })! + test_appsign_token_share(appsign_token) + } + + if context.shareState == .requestSecond { + let appsign_txid = queryItems.removeFirst(where: { $0.name == "appsign_txid" })! + test_appsign_txid(appsign_txid) + } + + let callback_name = queryItems.removeFirst { $0.name == "callback_name" }! + test_callback_name(callback_name) + + let callback_type = queryItems.removeFirst { $0.name == "callback_type" }! + test_callback_type(callback_type) + + let cflag = queryItems.removeFirst { $0.name == "cflag" }! + test_cflag(cflag, message, endpoint) + + let description = queryItems.removeFirst { $0.name == "description" } + test_description(description, message) + + let fileName = queryItems.removeFirst { $0.name == "fileName" } + test_fileName(fileName, message) + + let file_data = queryItems.removeFirst { $0.name == "file_data" } + test_file_data(file_data, message) + + let file_type = queryItems.removeFirst { $0.name == "file_type" }! + test_file_type(file_type, message) + + let flashurl = queryItems.removeFirst { $0.name == "flashurl" } + test_flashurl(flashurl, message) + + let generalpastboard = queryItems.removeFirst { $0.name == "generalpastboard" } + test_generalpastboard(generalpastboard) + + let mini_appid = queryItems.removeFirst { $0.name == "mini_appid" } + test_mini_appid(mini_appid, message) + + let mini_code64 = queryItems.removeFirst { $0.name == "mini_code64" } + test_mini_code64(mini_code64, message) + + let mini_path = queryItems.removeFirst { $0.name == "mini_path" } + test_mini_path(mini_path, message) + + let mini_type = queryItems.removeFirst { $0.name == "mini_type" } + test_mini_type(mini_type, message) + + let mini_weburl = queryItems.removeFirst { $0.name == "mini_weburl" } + test_mini_weburl(mini_weburl, message) + + let objectlocation = queryItems.removeFirst { $0.name == "objectlocation" } + test_objectlocation(objectlocation, message) + + if context.shareState == .requestSecond { + let openredirect = queryItems.removeFirst(where: { $0.name == "openredirect" })! + test_openredirect(openredirect) + } + + let pasteboard = queryItems.removeFirst { $0.name == "pasteboard" } + test_pasteboard(pasteboard, message) + + let shareType = queryItems.removeFirst { $0.name == "shareType" }! + test_shareType(shareType, message, endpoint) + + let src_type = queryItems.removeFirst { $0.name == "src_type" }! + test_src_type(src_type) + + let thirdAppDisplayName = queryItems.removeFirst { $0.name == "thirdAppDisplayName" }! + test_thirdAppDisplayName(thirdAppDisplayName) + + let title = queryItems.removeFirst { $0.name == "title" } + test_title(title, message) + + let url = queryItems.removeFirst { $0.name == "url" } + test_url(url, message) + + let version = queryItems.removeFirst { $0.name == "version" }! + test_version(version) + } +} + +extension QQHandlerBaseTests { + + func test_appsign_token_share(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value).count, 32) + } + + func test_callback_name(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), txID) + } + + func test_callback_type(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "scheme") + } + + func test_cflag(_ queryItem: URLQueryItem, _ message: MessageType, _ endpoint: Endpoint) { + switch endpoint { + case Endpoints.QQ.friend: + switch message.identifier { + case Messages.text, + Messages.image, + Messages.audio, + Messages.video, + Messages.webPage: + XCTAssertEqual(try XCTUnwrap(queryItem.value), "2") + case Messages.file: + XCTAssertEqual(try XCTUnwrap(queryItem.value), "18") + case Messages.miniProgram: + XCTAssertEqual(try XCTUnwrap(queryItem.value), "64") + default: + fatalError() + } + case Endpoints.QQ.timeline: + XCTAssertEqual(try XCTUnwrap(queryItem.value), "0") + default: + fatalError() + } + } + + func test_description(_ queryItem: URLQueryItem?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(queryItem) + case let message as ImageMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.description?.bus.base64EncodedString) + case let message as AudioMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.description?.bus.base64EncodedString) + case let message as VideoMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.description?.bus.base64EncodedString) + case let message as WebPageMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.description?.bus.base64EncodedString) + case let message as FileMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.description?.bus.base64EncodedString) + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.description?.bus.base64EncodedString) + default: + fatalError() + } + } + + func test_fileName(_ queryItem: URLQueryItem?, _ message: MessageType) { + switch message { + case is TextMessage, + is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is MiniProgramMessage: + XCTAssertNil(queryItem) + case let message as FileMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.fullName) + default: + fatalError() + } + } + + func test_file_data(_ queryItem: URLQueryItem?, _ message: MessageType) { + switch message { + case let message as TextMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.text.bus.base64EncodedString) + case is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage, + is MiniProgramMessage: + XCTAssertNil(queryItem) + default: + fatalError() + } + } + + func test_file_type(_ queryItem: URLQueryItem, _ message: MessageType) { + switch message.identifier { + case Messages.text: + XCTAssertEqual(try XCTUnwrap(queryItem.value), "text") + case Messages.image: + XCTAssertEqual(try XCTUnwrap(queryItem.value), "img") + case Messages.audio: + XCTAssertEqual(try XCTUnwrap(queryItem.value), "audio") + case Messages.video: + XCTAssertEqual(try XCTUnwrap(queryItem.value), "video") + case Messages.webPage: + XCTAssertEqual(try XCTUnwrap(queryItem.value), "news") + case Messages.file: + XCTAssertEqual(try XCTUnwrap(queryItem.value), "localFile") + case Messages.miniProgram: + XCTAssertEqual(try XCTUnwrap(queryItem.value), "news") + default: + fatalError() + } + } + + func test_flashurl(_ queryItem: URLQueryItem?, _ message: MessageType) { + switch message { + case is TextMessage, + is ImageMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage, + is MiniProgramMessage: + XCTAssertNil(queryItem) + case let message as AudioMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.dataLink?.absoluteString.bus.base64EncodedString) + default: + fatalError() + } + } + + func test_generalpastboard(_ queryItem: URLQueryItem?) { + XCTAssertEqual(try XCTUnwrap(queryItem?.value), "1") + } + + func test_mini_appid(_ queryItem: URLQueryItem?, _ message: MessageType) { + switch message { + case is TextMessage, + is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage: + XCTAssertNil(queryItem) + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.miniProgramID) + default: + fatalError() + } + } + + func test_mini_code64(_ queryItem: URLQueryItem?, _ message: MessageType) { + switch message { + case is TextMessage, + is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage: + XCTAssertNil(queryItem) + case is MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), "1") + default: + fatalError() + } + } + + func test_mini_path(_ queryItem: URLQueryItem?, _ message: MessageType) { + switch message { + case is TextMessage, + is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage: + XCTAssertNil(queryItem) + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.path.bus.base64EncodedString) + default: + fatalError() + } + } + + func test_mini_type(_ queryItem: URLQueryItem?, _ message: MessageType) { + let miniProgramType: (MiniProgramMessage.MiniProgramType) -> String = { miniProgramType in + switch miniProgramType { + case .release: + return "3" + case .test: + return "1" + case .preview: + return "4" + } + } + + switch message { + case is TextMessage, + is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage: + XCTAssertNil(queryItem) + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), miniProgramType(message.miniProgramType)) + default: + fatalError() + } + } + + func test_mini_weburl(_ queryItem: URLQueryItem?, _ message: MessageType) { + switch message { + case is TextMessage, + is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage: + XCTAssertNil(queryItem) + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.link.absoluteString.bus.base64EncodedString) + default: + fatalError() + } + } + + func test_objectlocation(_ queryItem: URLQueryItem?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(queryItem) + case is ImageMessage, + is AudioMessage, + is VideoMessage, + is FileMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), "pasteboard") + case is WebPageMessage, + is MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), "url") + default: + fatalError() + } + } + + func test_openredirect(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "1") + } + + func test_pasteboard(_ queryItem: URLQueryItem?, _ message: MessageType) { + let thumbnail: (String) throws -> Data = { value in + let data = try XCTUnwrap(Data(base64Encoded: value)) + var object = NSKeyedUnarchiver.unarchiveObject(with: data) as! [String: Any] + let thumbnail = object.removeValue(forKey: "previewimagedata") as! Data + XCTAssertTrue(object.isEmpty) + return thumbnail + } + + switch message { + case is TextMessage, + is ImageMessage, + is AudioMessage, + is VideoMessage, + is FileMessage: + XCTAssertNil(queryItem) + case let message as WebPageMessage: + XCTAssertEqual(try thumbnail(XCTUnwrap(queryItem?.value)), message.thumbnail) + case let message as MiniProgramMessage: + XCTAssertEqual(try thumbnail(XCTUnwrap(queryItem?.value)), message.thumbnail) + default: + fatalError() + } + } + + func test_shareType(_ queryItem: URLQueryItem, _ message: MessageType, _ endpoint: Endpoint) { + switch endpoint { + case Endpoints.QQ.friend: + switch message.identifier { + case Messages.text, + Messages.image, + Messages.audio, + Messages.video, + Messages.webPage, + Messages.file, + Messages.miniProgram: + XCTAssertEqual(try XCTUnwrap(queryItem.value), "0") + default: + fatalError() + } + case Endpoints.QQ.timeline: + switch message.identifier { + case Messages.text, + Messages.image: + XCTAssertEqual(try XCTUnwrap(queryItem.value), "0") + case Messages.audio, + Messages.video, + Messages.webPage: + XCTAssertEqual(try XCTUnwrap(queryItem.value), "1") + default: + fatalError() + } + default: + fatalError() + } + } + + func test_src_type(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "app") + } + + func test_thirdAppDisplayName(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), displayName.bus.base64EncodedString) + } + + func test_title(_ queryItem: URLQueryItem?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(queryItem) + case let message as ImageMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.title?.bus.base64EncodedString) + case let message as AudioMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.title?.bus.base64EncodedString) + case let message as VideoMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.title?.bus.base64EncodedString) + case let message as WebPageMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.title?.bus.base64EncodedString) + case let message as FileMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.title?.bus.base64EncodedString) + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.title?.bus.base64EncodedString) + default: + fatalError() + } + } + + func test_url(_ queryItem: URLQueryItem?, _ message: MessageType) { + switch message { + case is TextMessage, + is ImageMessage, + is FileMessage: + XCTAssertNil(queryItem) + case let message as AudioMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.link.absoluteString.bus.base64EncodedString) + case let message as VideoMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.link.absoluteString.bus.base64EncodedString) + case let message as WebPageMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.link.absoluteString.bus.base64EncodedString) + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(queryItem?.value), message.link.absoluteString.bus.base64EncodedString) + default: + fatalError() + } + } + + func test_version(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "1") + } +} + +// MARK: - Share - Message - Pasteboard - Request + +extension QQHandlerBaseTests: ShareMessagePasteboardRequestTestCase { + + func test_share_pb_request(dictionary: inout [String: Any], _ message: MessageType, _ endpoint: Endpoint) { + let file_data = dictionary.removeValue(forKey: "file_data") as? Data + test_file_data(file_data, message) + + let previewimagedata = dictionary.removeValue(forKey: "previewimagedata") as? Data + test_previewimagedata(previewimagedata, message) + } +} + +extension QQHandlerBaseTests { + + func test_file_data(_ value: Data?, _ message: MessageType) { + switch message { + case is TextMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is MiniProgramMessage: + XCTAssertNil(value) + case let message as ImageMessage: + XCTAssertEqual(try XCTUnwrap(value), message.data) + case let message as FileMessage: + XCTAssertEqual(try XCTUnwrap(value), message.data) + default: + fatalError() + } + } + + func test_previewimagedata(_ value: Data?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(value) + case let message as ImageMessage: + XCTAssertEqual(try XCTUnwrap(value), message.thumbnail) + case let message as AudioMessage: + XCTAssertEqual(try XCTUnwrap(value), message.thumbnail) + case let message as VideoMessage: + XCTAssertEqual(try XCTUnwrap(value), message.thumbnail) + case let message as WebPageMessage: + XCTAssertEqual(try XCTUnwrap(value), message.thumbnail) + case let message as FileMessage: + XCTAssertEqual(try XCTUnwrap(value), message.thumbnail) + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), message.thumbnail) + default: + fatalError() + } + } +} + +// MARK: - Share - Message - URLScheme - Response + +extension QQHandlerBaseTests: ShareMessageURLSchemeResponseTestCase { + + func test_share_us_response(path: String) { + XCTAssertEqual(path, "") + } + + func test_share_us_response(queryItems: inout [URLQueryItem], _ message: MessageType, _ endpoint: Endpoint) { + if context.shareState == .responseURLScheme { + let appsign_token = queryItems.removeFirst { $0.name == "appsign_token" }! + test_appsign_token_share(appsign_token) + } + + let error = queryItems.removeFirst { $0.name == "error" }! + let error_description = queryItems.removeFirst { $0.name == "error_description" } + + switch try! XCTUnwrap(error.value) { + case "0": + context.shareState = .success + + XCTAssertNil(error_description) + case "-4": + context.shareState = .failure + + XCTAssertEqual(try XCTUnwrap(error_description?.value), "the user give up the current operation".bus.base64EncodedString) + case "--1000710008": + context.shareState = .failure + + XCTAssertEqual(try XCTUnwrap(error_description?.value), "主体信息不一致,无法打开".bus.base64EncodedString) + default: + fatalError() + } + } +} + +// MARK: - Share - Message - UniversalLink - Response + +extension QQHandlerBaseTests: ShareMessageUniversalLinkResponseTestCase { + + func test_share_ul_response(path: String) { + switch context.shareState! { + case .requestFirst, + .requestSecond, + .responseURLScheme, + .requestThird, + .success, + .failure: + fatalError() + case .responseSignToken: + XCTAssertEqual(path, universalLink.appendingPathComponent("\(bundleID)/mqqsignapp").path) + case .responseUniversalLink: + XCTAssertEqual(path, universalLink.appendingPathComponent("\(bundleID)").path) + } + } + + func test_share_ul_response(queryItems: inout [URLQueryItem], _ message: MessageType, _ endpoint: Endpoint) { + switch context.shareState! { + case .requestFirst, + .requestSecond, + .responseURLScheme, + .requestThird, + .success, + .failure: + fatalError() + case .responseSignToken: + let generalpastboard = queryItems.removeFirst { $0.name == "generalpastboard" }! + test_generalpastboard(generalpastboard) + case .responseUniversalLink: + let sdkactioninfo = extract_JSON_ul(queryItems: &queryItems, key: "sdkactioninfo") + test_sdkactioninfo_share(sdkactioninfo, message, endpoint) + } + } +} + +extension QQHandlerBaseTests { + + func test_sdkactioninfo_us(_ value: [String: Any]) -> URL { + var dictionary = value as! [String: String] + + var urlComponents = URLComponents() + + urlComponents.scheme = dictionary.removeValue(forKey: "sdk_action_sheme")! + urlComponents.host = dictionary.removeValue(forKey: "sdk_action_host")! + urlComponents.path = dictionary.removeValue(forKey: "sdk_action_path")! + urlComponents.query = dictionary.removeValue(forKey: "sdk_action_query")! + + XCTAssertTrue(dictionary.isEmpty) + + return urlComponents.url! + } + + func test_sdkactioninfo_share(_ value: [String: Any], _ message: MessageType, _ endpoint: Endpoint) { + _test_share_response(us: test_sdkactioninfo_us(value), message, endpoint) + } +} + +// MARK: - Share - Message - Pasteboard - Response + +extension QQHandlerBaseTests: ShareMessagePasteboardResponseTestCase { + + func test_share_pb_response(dictionary: inout [String: Any], _ message: MessageType, _ endpoint: Endpoint) { + let appsign_redirect_pasteboard = dictionary.removeValue(forKey: "appsign_redirect_pasteboard") as! [String: Any] + test_appsign_redirect_pasteboard(appsign_redirect_pasteboard, message, endpoint) + } +} + +extension QQHandlerBaseTests { + + func test_appsign_redirect_pasteboard(_ value: [String: Any], _ message: MessageType, _ endpoint: Endpoint) { + var dictionary = value + + logger.debug("\(UIPasteboard.self), start, \(dictionary.keys.sorted())") + + let file_data = dictionary.removeValue(forKey: "file_data") as? Data + test_file_data(file_data, message) + + if !context.skipPasteboard, context.setPasteboardString { + let pasted_string = dictionary.removeValue(forKey: "pasted_string") as! String + test_pasted_string(pasted_string) + } + + let previewimagedata = dictionary.removeValue(forKey: "previewimagedata") as? Data + test_previewimagedata(previewimagedata, message) + + logger.debug("\(UIPasteboard.self), end, \(dictionary.keys.sorted())") + + XCTAssertTrue(dictionary.isEmpty) + } +} diff --git a/Tests/NBusTests/QQ/QQHandlerBaseTests.swift b/Tests/NBusTests/QQ/QQHandlerBaseTests.swift new file mode 100644 index 0000000..7266b41 --- /dev/null +++ b/Tests/NBusTests/QQ/QQHandlerBaseTests.swift @@ -0,0 +1,61 @@ +// +// QQHandlerBaseTests.swift +// BusTests +// +// Created by nuomi1 on 2022/3/30. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import RxSwift +import XCTest + +class QQHandlerBaseTests: HandlerBaseTests { + + override var appID: String { + switch handler { + case let handler as QQSDKHandler: + return handler.appID + case let handler as QQHandler: + return handler.appID + default: + fatalError() + } + } + + override var sdkShortVersion: String { + "3.5.11" + } + + override var sdkVersion: String { + "3.5.11_lite" + } + + var statusMachine: String { + UIDevice.current.bus.machine + } + + var statusOS: String { + UIDevice.current.systemVersion + } + + var statusVersion: String { + "\(ProcessInfo.processInfo.operatingSystemVersion.majorVersion)" + } + + var txID: String { + "QQ\(String(format: "%08llX", (appNumber as NSString).longLongValue))" + } + + override var universalLink: URL { + switch handler { + case let handler as QQSDKHandler: + return handler.universalLink + case let handler as QQHandler: + return handler.universalLink + default: + fatalError() + } + } +} diff --git a/Tests/NBusTests/QQ/QQSDKHandlerTests.swift b/Tests/NBusTests/QQ/QQSDKHandlerTests.swift new file mode 100644 index 0000000..762f09d --- /dev/null +++ b/Tests/NBusTests/QQ/QQSDKHandlerTests.swift @@ -0,0 +1,17 @@ +// +// QQSDKHandlerTests.swift +// BusTests +// +// Created by nuomi1 on 2022/3/30. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus + +class QQSDKHandlerTests: QQHandlerBaseTests { + + override var handler: HandlerType { AppState.qqSDKHandler } + + override var category: AppState.PlatformItem.Category { .sdk } +} diff --git a/Tests/NBusTests/Wechat/WechatHandlerBaseTests+General.swift b/Tests/NBusTests/Wechat/WechatHandlerBaseTests+General.swift new file mode 100644 index 0000000..6604a48 --- /dev/null +++ b/Tests/NBusTests/Wechat/WechatHandlerBaseTests+General.swift @@ -0,0 +1,240 @@ +// +// WechatHandlerBaseTests+General.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +import XCTest + +// MARK: - General - Scheme + +extension WechatHandlerBaseTests: GeneralSchemeTestCase { + + func report_general_scheme() -> Set { + [ + "weixin", + "weixinULAPI", + ] + } +} + +// MARK: - General - UniversalLink - Request + +extension WechatHandlerBaseTests: GeneralUniversalLinkRequestTestCase { + + func test_general_ul_request(scheme: @autoclosure () throws -> String) { + XCTAssertEqual(try scheme(), "https") + } + + func test_general_ul_request(host: @autoclosure () throws -> String) { + XCTAssertEqual(try host(), "help.wechat.com") + } + + func test_general_ul_request(queryItems: inout [URLQueryItem]) { + let wechat_app_bundleId = queryItems.removeFirst { $0.name == "wechat_app_bundleId" }! + test_wechat_app_bundleId(wechat_app_bundleId) + + if context.shareState == .requestFirst { + let wechat_auth_context_id = queryItems.removeFirst { $0.name == "wechat_auth_context_id" }! + test_wechat_auth_context_id(wechat_auth_context_id) + } + } +} + +extension WechatHandlerBaseTests { + + func test_wechat_app_bundleId(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), bundleID) + } + + func test_wechat_auth_context_id(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value).count, 64) + } +} + +// MARK: - General - Pasteboard - Request + +extension WechatHandlerBaseTests: GeneralPasteboardRequestTestCase { + + func extract_major_pb_request(items: inout [[String: Data]]) -> [String: Any] { + var plist = extract_PropertyList_pb(items: &items, key: "content") + + logger.debug("\(UIPasteboard.self), start, \(plist.keys.sorted())") + + let dictionary = plist.removeValue(forKey: appID) as! [String: Any] + + if context.setPasteboardString { + let old_text = plist.removeValue(forKey: "old_text") as! String + test_old_text(old_text) + } + + logger.debug("\(UIPasteboard.self), end, \(plist.keys.sorted())") + + XCTAssertTrue(plist.isEmpty) + + return dictionary + } + + func test_general_pb_request(dictionary: inout [String: Any]) { + let isAutoResend = dictionary.removeValue(forKey: "isAutoResend") as! Bool + test_isAutoResend(isAutoResend) + + let result = dictionary.removeValue(forKey: "result") as! String + test_result(result) + + let returnFromApp = dictionary.removeValue(forKey: "returnFromApp") as! String + test_returnFromApp(returnFromApp) + + let sdkver = dictionary.removeValue(forKey: "sdkver") as! String + test_sdkver(sdkver) + + let universalLink = dictionary.removeValue(forKey: "universalLink") as! String + test_universalLink(universalLink) + } + + func test_extra_pb_request(items: inout [[String: Data]]) { + XCTAssertTrue(true) + } +} + +extension WechatHandlerBaseTests { + + func test_old_text(_ value: String) { + XCTAssertEqual(value, AppState.defaultPasteboardString) + } +} + +extension WechatHandlerBaseTests { + + func test_isAutoResend(_ value: Bool) { + switch context.shareState! { + case .requestFirst, + .responseUniversalLink: + XCTAssertEqual(value, false) + case .requestSecond: + XCTAssertEqual(value, true) + case .responseSignToken, + .responseURLScheme, + .requestThird, + .success, + .failure: + fatalError() + } + } + + func test_result(_ value: String) { + XCTAssertEqual(value, "1") + } + + func test_returnFromApp(_ value: String) { + XCTAssertEqual(value, "0") + } + + func test_sdkver(_ value: String) { + XCTAssertEqual(value, sdkVersion) + } + + func test_universalLink(_ value: String) { + XCTAssertEqual(value, universalLink.absoluteString) + } +} + +// MARK: - General - URLScheme - Response + +extension WechatHandlerBaseTests: GeneralURLSchemeResponseTestCase { + + func test_general_us_response(scheme: @autoclosure () throws -> String) { + XCTAssertEqual(try scheme(), appID) + } + + func test_general_us_response(host: @autoclosure () throws -> String) { + XCTAssertEqual(try host(), "resendContextReqByScheme") + } + + func test_general_us_response(queryItems: inout [URLQueryItem]) { + let wechat_auth_context_id = queryItems.removeFirst { $0.name == "wechat_auth_context_id" }! + test_wechat_auth_context_id(wechat_auth_context_id) + } +} + +// MARK: - General - UniversalLink - Response + +extension WechatHandlerBaseTests: GeneralUniversalLinkResponseTestCase { + + func test_general_ul_response(scheme: @autoclosure () throws -> String) { + XCTAssertEqual(try scheme(), universalLink.scheme) + } + + func test_general_ul_response(host: @autoclosure () throws -> String) { + XCTAssertEqual(try host(), universalLink.host) + } + + func test_general_ul_response(queryItems: inout [URLQueryItem]) { + if context.shareState == .responseSignToken { + let wechat_auth_context_id = queryItems.removeFirst { $0.name == "wechat_auth_context_id" }! + test_wechat_auth_context_id(wechat_auth_context_id) + } + + if context.shareState == .responseSignToken { + let wechat_auth_token = queryItems.removeFirst { $0.name == "wechat_auth_token" }! + test_wechat_auth_token(wechat_auth_token) + } + } +} + +extension WechatHandlerBaseTests { + + func test_wechat_auth_token(_ queryItem: URLQueryItem) { + let token = try! XCTUnwrap(queryItem.value).split(separator: "_") + XCTAssertEqual(token.count, 2) + XCTAssertEqual(try XCTUnwrap(token.first).count, 64) + } +} + +// MARK: - General - Pasteboard - Response + +extension WechatHandlerBaseTests: GeneralPasteboardResponseTestCase { + + func extract_major_pb_response(items: inout [[String: Data]]) -> [String: Any] { + extract_major_pb_request(items: &items) + } + + func test_general_pb_response(dictionary: inout [String: Any]) { + let country = dictionary.removeValue(forKey: "country") as! String + test_country(country) + + let isAutoResend = dictionary.removeValue(forKey: "isAutoResend") as! Bool + test_isAutoResend(isAutoResend) + + let language = dictionary.removeValue(forKey: "language") as! String + test_language(language) + + let returnFromApp = dictionary.removeValue(forKey: "returnFromApp") as! String + test_returnFromApp(returnFromApp) + + let wechatVersion = dictionary.removeValue(forKey: "wechatVersion") as! Int + test_wechatVersion(wechatVersion) + } + + func test_extra_pb_response(items: inout [[String: Data]]) { + XCTAssertTrue(true) + } +} + +extension WechatHandlerBaseTests { + + func test_country(_ value: String) { + XCTAssertEqual(value, "") + } + + func test_language(_ value: String) { + XCTAssertEqual(value, "zh_CN") + } + + func test_wechatVersion(_ value: Int) { + XCTAssertEqual(value, remoteSDKShortVersion) + } +} diff --git a/Tests/NBusTests/Wechat/WechatHandlerBaseTests+Launch.swift b/Tests/NBusTests/Wechat/WechatHandlerBaseTests+Launch.swift new file mode 100644 index 0000000..148dac9 --- /dev/null +++ b/Tests/NBusTests/Wechat/WechatHandlerBaseTests+Launch.swift @@ -0,0 +1,114 @@ +// +// WechatHandlerBaseTests+Launch.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import XCTest + +// MARK: - Launch + +extension WechatHandlerBaseTests: LaunchTestCase { + + func test_launch() { + test_launch(Platforms.wechat, MediaSource.wechatMiniProgram as! MiniProgramMessage) + } +} + +// MARK: - Launch - Program - Scheme + +extension WechatHandlerBaseTests: LaunchProgramSchemeTestCase { + + func report_launch_scheme(_ platform: Platform, _ program: MiniProgramMessage) -> Set { + [] + } +} + +// MARK: - Launch - Program - UniversalLink - Request + +extension WechatHandlerBaseTests: LaunchProgramUniversalLinkRequestTestCase { + + func test_launch_ul_request(path: String) { + XCTAssertEqual(path, "/app/\(appID)/jumpWxa/") + } + + func test_launch_ul_request(queryItems: inout [URLQueryItem], _ platform: Platform, _ program: MiniProgramMessage) { + let extMsg = queryItems.removeFirst { $0.name == "extMsg" }! + test_extMsg(extMsg) + + let miniProgramType = queryItems.removeFirst { $0.name == "miniProgramType" }! + test_miniProgramType(miniProgramType, program) + + let path = queryItems.removeFirst { $0.name == "path" }! + test_path(path, program) + + let userName = queryItems.removeFirst { $0.name == "userName" }! + test_userName(userName, program) + } +} + +extension WechatHandlerBaseTests { + + func test_extMsg(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "") + } + + func test_miniProgramType(_ queryItem: URLQueryItem, _ message: MessageType) { + let miniProgramType: (MiniProgramMessage.MiniProgramType) -> String = { miniProgramType in + switch miniProgramType { + case .release: + return "0" + case .test: + return "1" + case .preview: + return "2" + } + } + + switch message { + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(queryItem.value), miniProgramType(message.miniProgramType)) + default: + fatalError() + } + } + + func test_path(_ queryItem: URLQueryItem, _ message: MessageType) { + switch message { + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(queryItem.value), message.path) + default: + fatalError() + } + } + + func test_userName(_ queryItem: URLQueryItem, _ message: MessageType) { + switch message { + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(queryItem.value), message.miniProgramID) + default: + fatalError() + } + } +} + +// MARK: - Launch - Program - Pasteboard - Request + +extension WechatHandlerBaseTests: LaunchProgramPasteboardRequestTestCase { + + func test_launch_pb_request(dictionary: inout [String: Any], _ platform: Platform, _ program: MiniProgramMessage) { + let command = dictionary.removeValue(forKey: "command") as! String + test_command_launch(command) + } +} + +extension WechatHandlerBaseTests { + + func test_command_launch(_ value: String) { + XCTAssertEqual(value, "1080") + } +} diff --git a/Tests/NBusTests/Wechat/WechatHandlerBaseTests+Oauth.swift b/Tests/NBusTests/Wechat/WechatHandlerBaseTests+Oauth.swift new file mode 100644 index 0000000..7784b58 --- /dev/null +++ b/Tests/NBusTests/Wechat/WechatHandlerBaseTests+Oauth.swift @@ -0,0 +1,74 @@ +// +// WechatHandlerBaseTests+Oauth.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import XCTest + +// MARK: - Oauth + +extension WechatHandlerBaseTests: OauthTestCase { + + func test_oauth() { + test_oauth(Platforms.wechat) + } +} + +// MARK: - Oauth - Platform - Scheme + +extension WechatHandlerBaseTests: OauthPlatformSchemeTestCase { + + func report_oauth_scheme(_ platform: Platform) -> Set { + [] + } +} + +// MARK: - Oauth - Platform - UniversalLink - Request + +extension WechatHandlerBaseTests: OauthPlatformUniversalRequestLinkTestCase { + + func test_oauth_ul_request(path: String) { + XCTAssertEqual(path, "/app/\(appID)/auth/") + } + + func test_oauth_ul_request(queryItems: inout [URLQueryItem], _ platform: Platform) { + let scope = queryItems.removeFirst { $0.name == "scope" }! + test_scope(scope) + + let state = queryItems.removeFirst { $0.name == "state" }! + test_state(state) + } +} + +extension WechatHandlerBaseTests { + + func test_scope(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "snsapi_userinfo") + } + + func test_state(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "") + } +} + +// MARK: - Oauth - Platform - Pasteboard - Request + +extension WechatHandlerBaseTests: OauthPlatformPasteboardRequestTestCase { + + func test_oauth_pb_request(dictionary: inout [String: Any], _ platform: Platform) { + let command = dictionary.removeValue(forKey: "command") as! String + test_command_oauth(command) + } +} + +extension WechatHandlerBaseTests { + + func test_command_oauth(_ value: String) { + XCTAssertEqual(value, "0") + } +} diff --git a/Tests/NBusTests/Wechat/WechatHandlerBaseTests+Share.swift b/Tests/NBusTests/Wechat/WechatHandlerBaseTests+Share.swift new file mode 100644 index 0000000..1fe3ad8 --- /dev/null +++ b/Tests/NBusTests/Wechat/WechatHandlerBaseTests+Share.swift @@ -0,0 +1,635 @@ +// +// WechatHandlerBaseTests+Share.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import XCTest + +// MARK: - Share + +extension WechatHandlerBaseTests: ShareTestCase { + + func test_share_text_friend() { + test_share(MediaSource.text, Endpoints.Wechat.friend) + } + + func test_share_text_timeline() { + test_share(MediaSource.text, Endpoints.Wechat.timeline) + } + + func test_share_text_favorite() { + test_share(MediaSource.text, Endpoints.Wechat.favorite) + } + + func test_share_image_friend() { + test_share(MediaSource.wechatImage, Endpoints.Wechat.friend) + } + + func test_share_image_timeline() { + test_share(MediaSource.wechatImage, Endpoints.Wechat.timeline) + } + + func test_share_image_favorite() { + test_share(MediaSource.wechatImage, Endpoints.Wechat.favorite) + } + + func test_share_audio_friend() { + test_share(MediaSource.audio, Endpoints.Wechat.friend) + } + + func test_share_audio_timeline() { + test_share(MediaSource.audio, Endpoints.Wechat.timeline) + } + + func test_share_audio_favorite() { + test_share(MediaSource.audio, Endpoints.Wechat.favorite) + } + + func test_share_video_friend() { + test_share(MediaSource.video, Endpoints.Wechat.friend) + } + + func test_share_video_timeline() { + test_share(MediaSource.video, Endpoints.Wechat.timeline) + } + + func test_share_video_favorite() { + test_share(MediaSource.video, Endpoints.Wechat.favorite) + } + + func test_share_webPage_friend() { + test_share(MediaSource.webPage, Endpoints.Wechat.friend) + } + + func test_share_webPage_timeline() { + test_share(MediaSource.webPage, Endpoints.Wechat.timeline) + } + + func test_share_webPage_favorite() { + test_share(MediaSource.webPage, Endpoints.Wechat.favorite) + } + + func test_share_file_friend() { + test_share(MediaSource.file, Endpoints.Wechat.friend) + } + + func test_share_file_timeline() { + test_share(MediaSource.file, Endpoints.Wechat.timeline) + } + + func test_share_file_favorite() { + test_share(MediaSource.file, Endpoints.Wechat.favorite) + } + + func test_share_miniProgram_friend() { + test_share(MediaSource.wechatMiniProgram, Endpoints.Wechat.friend) + } + + func test_share_miniProgram_timeline() { + test_share(MediaSource.wechatMiniProgram, Endpoints.Wechat.timeline) + } + + func test_share_miniProgram_favorite() { + test_share(MediaSource.wechatMiniProgram, Endpoints.Wechat.favorite) + } +} + +// MARK: - Share - Message - Scheme + +extension WechatHandlerBaseTests: ShareMessageSchemeTestCase { + + func report_share_scheme(_ message: MessageType, _ endpoint: Endpoint) -> Set { + switch message { + case is TextMessage, + is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage, + is MiniProgramMessage: + return [] + default: + fatalError() + } + } +} + +// MARK: - Share - Message - UniversalLink - Request + +extension WechatHandlerBaseTests: ShareMessageUniversalLinkRequestTestCase { + + func test_share_ul_request(path: String) { + XCTAssertEqual(path, "/app/\(appID)/sendreq/") + } + + func test_share_ul_request(queryItems: inout [URLQueryItem], _ message: MessageType, _ endpoint: Endpoint) { + if context.shareState == .requestSecond { + let wechat_auth_token = queryItems.removeFirst { $0.name == "wechat_auth_token" }! + test_wechat_auth_token(wechat_auth_token) + } + } +} + +// MARK: - Share - Message - Pasteboard - Request + +extension WechatHandlerBaseTests: ShareMessagePasteboardRequestTestCase { + + func test_share_pb_request(dictionary: inout [String: Any], _ message: MessageType, _ endpoint: Endpoint) { + let appBrandPath = dictionary.removeValue(forKey: "appBrandPath") as? String + test_appBrandPath(appBrandPath, message) + + let appBrandUserName = dictionary.removeValue(forKey: "appBrandUserName") as? String + test_appBrandUserName(appBrandUserName, message) + + let appbrandissecrectmessage = dictionary.removeValue(forKey: "appbrandissecrectmessage") as? Bool + test_appbrandissecrectmessage(appbrandissecrectmessage, message) + + let appbrandisupdatablemessage = dictionary.removeValue(forKey: "appbrandisupdatablemessage") as? Bool + test_appbrandisupdatablemessage(appbrandisupdatablemessage, message) + + let command = dictionary.removeValue(forKey: "command") as! String + test_command(command, message) + + let description = dictionary.removeValue(forKey: "description") as? String + test_description(description, message) + + let disableForward = dictionary.removeValue(forKey: "disableForward") as? Bool + test_disableForward(disableForward, message) + + let fileData = dictionary.removeValue(forKey: "fileData") as? Data + test_fileData(fileData, message) + + let fileExt = dictionary.removeValue(forKey: "fileExt") as? String + test_fileExt(fileExt, message) + + let hdThumbData = dictionary.removeValue(forKey: "hdThumbData") as? Data + test_hdThumbData(hdThumbData, message) + + let mediaDataUrl = dictionary.removeValue(forKey: "mediaDataUrl") as? String + test_mediaDataUrl(mediaDataUrl, message) + + let mediaUrl = dictionary.removeValue(forKey: "mediaUrl") as? String + test_mediaUrl(mediaUrl, message) + + let miniprogramType = dictionary.removeValue(forKey: "miniprogramType") as? Int + test_miniprogramType(miniprogramType, message) + + let musicVideoDuration = dictionary.removeValue(forKey: "musicVideoDuration") as? String + test_musicVideoDuration(musicVideoDuration, message) + + let musicVideoIssueData = dictionary.removeValue(forKey: "musicVideoIssueData") as? String + test_musicVideoIssueData(musicVideoIssueData, message) + + let objectType = dictionary.removeValue(forKey: "objectType") as? String + test_objectType(objectType, message) + + let scene = dictionary.removeValue(forKey: "scene") as! String + test_scene(scene, endpoint) + + let thumbData = dictionary.removeValue(forKey: "thumbData") as? Data + test_thumbData(thumbData, message) + + let title = dictionary.removeValue(forKey: "title") as! String + test_title(title, message) + + let weworkObjectSubType = dictionary.removeValue(forKey: "weworkObjectSubType") as? String + test_weworkObjectSubType(weworkObjectSubType, message) + + let withShareTicket = dictionary.removeValue(forKey: "withShareTicket") as? Bool + test_withShareTicket(withShareTicket, message) + } +} + +extension WechatHandlerBaseTests { + + func test_appBrandPath(_ value: String?, _ message: MessageType) { + switch message { + case is TextMessage, + is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage: + XCTAssertNil(value) + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), message.path) + default: + fatalError() + } + } + + func test_appBrandUserName(_ value: String?, _ message: MessageType) { + switch message { + case is TextMessage, + is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage: + XCTAssertNil(value) + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), message.miniProgramID) + default: + fatalError() + } + } + + func test_appbrandissecrectmessage(_ value: Bool?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(value) + case is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage, + is MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), false) + default: + fatalError() + } + } + + func test_appbrandisupdatablemessage(_ value: Bool?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(value) + case is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage, + is MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), false) + default: + fatalError() + } + } + + func test_command(_ value: String, _ message: MessageType) { + switch message.identifier { + case Messages.text: + XCTAssertEqual(value, "1020") + case Messages.image, + Messages.audio, + Messages.video, + Messages.webPage, + Messages.file, + Messages.miniProgram: + XCTAssertEqual(value, "1010") + default: + fatalError() + } + } + + func test_description(_ value: String?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(value) + case let message as ImageMessage: + XCTAssertEqual(try XCTUnwrap(value), message.description) + case let message as AudioMessage: + XCTAssertEqual(try XCTUnwrap(value), message.description) + case let message as VideoMessage: + XCTAssertEqual(try XCTUnwrap(value), message.description) + case let message as WebPageMessage: + XCTAssertEqual(try XCTUnwrap(value), message.description) + case let message as FileMessage: + XCTAssertEqual(try XCTUnwrap(value), message.description) + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), message.description) + default: + fatalError() + } + } + + func test_disableForward(_ value: Bool?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(value) + case is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage, + is MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), false) + default: + fatalError() + } + } + + func test_fileData(_ value: Data?, _ message: MessageType) { + switch message { + case is TextMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is MiniProgramMessage: + XCTAssertNil(value) + case let message as ImageMessage: + XCTAssertEqual(try XCTUnwrap(value), message.data) + case let message as FileMessage: + XCTAssertEqual(try XCTUnwrap(value), message.data) + default: + fatalError() + } + } + + func test_fileExt(_ value: String?, _ message: MessageType) { + switch message { + case is TextMessage, + is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is MiniProgramMessage: + XCTAssertNil(value) + case let message as FileMessage: + XCTAssertEqual(try XCTUnwrap(value), message.fileExtension) + default: + fatalError() + } + } + + func test_hdThumbData(_ value: Data?, _ message: MessageType) { + switch message { + case is TextMessage, + is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage: + XCTAssertNil(value) + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), message.thumbnail) + default: + fatalError() + } + } + + func test_mediaDataUrl(_ value: String?, _ message: MessageType) { + switch message { + case is TextMessage, + is ImageMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage, + is MiniProgramMessage: + XCTAssertNil(value) + case let message as AudioMessage: + XCTAssertEqual(try XCTUnwrap(value), message.dataLink?.absoluteString) + default: + fatalError() + } + } + + func test_mediaUrl(_ value: String?, _ message: MessageType) { + switch message { + case is TextMessage, + is ImageMessage, + is FileMessage: + XCTAssertNil(value) + case let message as AudioMessage: + XCTAssertEqual(try XCTUnwrap(value), message.link.absoluteString) + case let message as VideoMessage: + XCTAssertEqual(try XCTUnwrap(value), message.link.absoluteString) + case let message as WebPageMessage: + XCTAssertEqual(try XCTUnwrap(value), message.link.absoluteString) + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), message.link.absoluteString) + default: + fatalError() + } + } + + func test_miniprogramType(_ value: Int?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(value) + case is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage, + is MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), 0) + default: + fatalError() + } + } + + func test_musicVideoDuration(_ value: String?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(value) + case is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage, + is MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), "0") + default: + fatalError() + } + } + + func test_musicVideoIssueData(_ value: String?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(value) + case is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage, + is MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), "0") + default: + fatalError() + } + } + + func test_objectType(_ value: String?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(value) + case is ImageMessage: + XCTAssertEqual(try XCTUnwrap(value), "2") + case is AudioMessage: + XCTAssertEqual(try XCTUnwrap(value), "3") + case is VideoMessage: + XCTAssertEqual(try XCTUnwrap(value), "4") + case is WebPageMessage: + XCTAssertEqual(try XCTUnwrap(value), "5") + case is FileMessage: + XCTAssertEqual(try XCTUnwrap(value), "6") + case is MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), "36") + default: + fatalError() + } + } + + func test_scene(_ value: String, _ endpoint: Endpoint) { + switch endpoint { + case Endpoints.Wechat.friend: + XCTAssertEqual(value, "0") + case Endpoints.Wechat.timeline: + XCTAssertEqual(value, "1") + case Endpoints.Wechat.favorite: + XCTAssertEqual(value, "2") + default: + fatalError() + } + } + + func test_thumbData(_ value: Data?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(value) + case let message as ImageMessage: + XCTAssertEqual(try XCTUnwrap(value), message.thumbnail) + case let message as AudioMessage: + XCTAssertEqual(try XCTUnwrap(value), message.thumbnail) + case let message as VideoMessage: + XCTAssertEqual(try XCTUnwrap(value), message.thumbnail) + case let message as WebPageMessage: + XCTAssertEqual(try XCTUnwrap(value), message.thumbnail) + case let message as FileMessage: + XCTAssertEqual(try XCTUnwrap(value), message.thumbnail) + case let message as MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), message.thumbnail) + default: + fatalError() + } + } + + func test_title(_ value: String, _ message: MessageType) { + switch message { + case let message as TextMessage: + XCTAssertEqual(value, message.text) + case let message as ImageMessage: + XCTAssertEqual(value, message.title) + case let message as AudioMessage: + XCTAssertEqual(value, message.title) + case let message as VideoMessage: + XCTAssertEqual(value, message.title) + case let message as WebPageMessage: + XCTAssertEqual(value, message.title) + case let message as FileMessage: + XCTAssertEqual(value, message.title) + case let message as MiniProgramMessage: + XCTAssertEqual(value, message.title) + default: + fatalError() + } + } + + func test_weworkObjectSubType(_ value: String?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(value) + case is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage, + is MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), "0") + default: + fatalError() + } + } + + func test_withShareTicket(_ value: Bool?, _ message: MessageType) { + switch message { + case is TextMessage: + XCTAssertNil(value) + case is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage, + is MiniProgramMessage: + XCTAssertEqual(try XCTUnwrap(value), false) + default: + fatalError() + } + } +} + +// MARK: - Share - Message - URLScheme - Response + +extension WechatHandlerBaseTests: ShareMessageURLSchemeResponseTestCase { + + func test_share_us_response(path: String) { + XCTAssertEqual(path, "") + } + + func test_share_us_response(queryItems: inout [URLQueryItem], _ message: MessageType, _ endpoint: Endpoint) { + XCTAssertTrue(true) + } +} + +// MARK: - Share - Message - UniversalLink - Response + +extension WechatHandlerBaseTests: ShareMessageUniversalLinkResponseTestCase { + + func test_share_ul_response(path: String) { + switch context.shareState! { + case .requestFirst, + .requestSecond, + .responseURLScheme, + .requestThird, + .success, + .failure: + fatalError() + case .responseSignToken: + XCTAssertEqual(path, universalLink.appendingPathComponent("\(appID)/refreshToken").path) + case .responseUniversalLink: + XCTAssertEqual(path, universalLink.appendingPathComponent("\(appID)//").path) + } + } + + func test_share_ul_response(queryItems: inout [URLQueryItem], _ message: MessageType, _ endpoint: Endpoint) { + XCTAssertTrue(true) + } +} + +// MARK: - Share - Message - Pasteboard - Response + +extension WechatHandlerBaseTests: ShareMessagePasteboardResponseTestCase { + + func test_share_pb_response(dictionary: inout [String: Any], _ message: MessageType, _ endpoint: Endpoint) { + let command = dictionary.removeValue(forKey: "command") as! String + test_command_share(command) + + let result = dictionary.removeValue(forKey: "result") as! String + test_result_share(result) + } +} + +extension WechatHandlerBaseTests { + + func test_command_share(_ value: String) { + XCTAssertEqual(value, "2020") + } + + func test_result_share(_ value: String) { + switch value { + case "0": + context.shareState = .success + case "-2": + context.shareState = .failure + default: + fatalError() + } + } +} diff --git a/Tests/NBusTests/Wechat/WechatHandlerBaseTests.swift b/Tests/NBusTests/Wechat/WechatHandlerBaseTests.swift new file mode 100644 index 0000000..789eca9 --- /dev/null +++ b/Tests/NBusTests/Wechat/WechatHandlerBaseTests.swift @@ -0,0 +1,45 @@ +// +// WechatHandlerBaseTests.swift +// BusTests +// +// Created by nuomi1 on 2022/4/1. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import RxSwift +import XCTest + +class WechatHandlerBaseTests: HandlerBaseTests { + + override var appID: String { + switch handler { + case let handler as WechatSDKHandler: + return handler.appID + case let handler as WechatHandler: + return handler.appID + default: + fatalError() + } + } + + var remoteSDKShortVersion: Int { + 402_658_851 + } + + override var sdkVersion: String { + "1.9.2" + } + + override var universalLink: URL { + switch handler { + case let handler as WechatSDKHandler: + return handler.universalLink + case let handler as WechatHandler: + return handler.universalLink + default: + fatalError() + } + } +} diff --git a/Tests/NBusTests/Wechat/WechatSDKHandlerTests.swift b/Tests/NBusTests/Wechat/WechatSDKHandlerTests.swift new file mode 100644 index 0000000..421fa5c --- /dev/null +++ b/Tests/NBusTests/Wechat/WechatSDKHandlerTests.swift @@ -0,0 +1,17 @@ +// +// WechatSDKHandlerTests.swift +// BusTests +// +// Created by nuomi1 on 2022/4/1. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus + +class WechatSDKHandlerTests: WechatHandlerBaseTests { + + override var handler: HandlerType { AppState.wechatSDKHandler } + + override var category: AppState.PlatformItem.Category { .sdk } +} diff --git a/Tests/NBusTests/Weibo/WeiboHandlerBaseTests+General.swift b/Tests/NBusTests/Weibo/WeiboHandlerBaseTests+General.swift new file mode 100644 index 0000000..d0f1ef7 --- /dev/null +++ b/Tests/NBusTests/Weibo/WeiboHandlerBaseTests+General.swift @@ -0,0 +1,263 @@ +// +// WeiboHandlerBaseTests+General.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +import XCTest + +// MARK: - General - Scheme + +extension WeiboHandlerBaseTests: GeneralSchemeTestCase { + + func report_general_scheme() -> Set { + [ + "sinaweibo", + "weibosdk", + "weibosdk3.3", + ] + } +} + +// MARK: - General - UniversalLink - Request + +extension WeiboHandlerBaseTests: GeneralUniversalLinkRequestTestCase { + + func test_general_ul_request(scheme: @autoclosure () throws -> String) { + XCTAssertEqual(try scheme(), "https") + } + + func test_general_ul_request(host: @autoclosure () throws -> String) { + XCTAssertEqual(try host(), "open.weibo.com") + } + + func test_general_ul_request(queryItems: inout [URLQueryItem]) { + let newVersion = queryItems.removeFirst { $0.name == "newVersion" }! + test_newVersion(newVersion) + + let objId = queryItems.removeFirst { $0.name == "objId" }! + test_objId(objId) + } +} + +extension WeiboHandlerBaseTests { + + func test_newVersion(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), sdkShortVersion) + } + + func test_objId(_ queryItem: URLQueryItem) { + XCTAssertNotNil(try UUID(uuidString: XCTUnwrap(queryItem.value))) + } +} + +// MARK: - General - Pasteboard - Request + +extension WeiboHandlerBaseTests: GeneralPasteboardRequestTestCase { + + func extract_major_pb_request(items: inout [[String: Data]]) -> [String: Any] { + if context.shareState == .requestSecond { + return extract_KeyedArchiver_pb(items: &items, key: "transferObject") + } + + return [:] + } + + func test_general_pb_request(dictionary: inout [String: Any]) { + if context.shareState == .requestSecond { + let requestID = dictionary.removeValue(forKey: "requestID") as! String + test_requestID(requestID) + } + } + + func test_extra_pb_request(items: inout [[String: Data]]) { + test_app(&items) + + test_sdkVersion(&items) + + if context.shareState == .requestSecond { + test_userInfo_share(&items) + } + } +} + +extension WeiboHandlerBaseTests { + + func test_requestID(_ value: String) { + XCTAssertNotNil(UUID(uuidString: value)) + } +} + +extension WeiboHandlerBaseTests { + + func test_app(_ items: inout [[String: Data]]) { + var dictionary = extract_KeyedArchiver_pb(items: &items, key: "app") + + logger.debug("\(UIPasteboard.self), start, \(dictionary.keys.sorted())") + + let aid = dictionary.removeValue(forKey: "aid") as? String + test_aid(aid) + + let appKey = dictionary.removeValue(forKey: "appKey") as! String + test_appKey(appKey) + + let bundleID = dictionary.removeValue(forKey: "bundleID") as! String + test_bundleID(bundleID) + + switch context.shareState! { + case .requestFirst, + .responseSignToken, + .requestSecond: + let universalLink = dictionary.removeValue(forKey: "universalLink") as! String + test_universalLink(universalLink) + case .success, + .failure: + XCTAssertTrue(true) + case .responseURLScheme, + .responseUniversalLink, + .requestThird: + fatalError() + } + + logger.debug("\(UIPasteboard.self), end, \(dictionary.keys.sorted())") + + XCTAssertTrue(dictionary.isEmpty) + } +} + +extension WeiboHandlerBaseTests { + + func test_aid(_ value: String?) { + let isNil = value == nil + let isCountFifty = value?.count == 50 + XCTAssertTrue(isNil || isCountFifty) + } + + func test_appKey(_ value: String) { + XCTAssertEqual(value, appNumber) + } + + func test_bundleID(_ value: String) { + XCTAssertEqual(value, bundleID) + } + + func test_universalLink(_ value: String) { + XCTAssertEqual(value, universalLink.absoluteString) + } +} + +extension WeiboHandlerBaseTests { + + func test_sdkVersion(_ items: inout [[String: Data]]) { + let data = items.removeFirst { $0.keys.contains("sdkVersion") }!["sdkVersion"]! + + switch context.shareState! { + case .requestFirst, + .responseSignToken, + .requestSecond: + XCTAssertEqual(data, Data(sdkVersion.utf8)) + case .success, + .failure: + XCTAssertEqual(data, Data(remoteSDKShortVersion.utf8)) + case .responseURLScheme, + .responseUniversalLink, + .requestThird: + fatalError() + } + } +} + +extension WeiboHandlerBaseTests { + + func test_userInfo_share(_ items: inout [[String: Data]]) { + var dictionary = extract_KeyedArchiver_pb(items: &items, key: "userInfo") + + logger.debug("\(UIPasteboard.self), start, \(dictionary.keys.sorted())") + + let startTime = dictionary.removeValue(forKey: "startTime") as! String + test_startTime(startTime) + + logger.debug("\(UIPasteboard.self), end, \(dictionary.keys.sorted())") + + XCTAssertTrue(dictionary.isEmpty) + } +} + +extension WeiboHandlerBaseTests { + + func test_startTime(_ value: String) { + XCTAssertNotNil(dateFormatter.date(from: value)) + } +} + +// MARK: - General - URLScheme - Response + +extension WeiboHandlerBaseTests: GeneralURLSchemeResponseTestCase { + + func test_general_us_response(scheme: @autoclosure () throws -> String) { + fatalError() + } + + func test_general_us_response(host: @autoclosure () throws -> String) { + fatalError() + } + + func test_general_us_response(queryItems: inout [URLQueryItem]) { + fatalError() + } +} + +// MARK: - General - UniversalLink - Response + +extension WeiboHandlerBaseTests: GeneralUniversalLinkResponseTestCase { + + func test_general_ul_response(scheme: @autoclosure () throws -> String) { + XCTAssertEqual(try scheme(), universalLink.scheme) + } + + func test_general_ul_response(host: @autoclosure () throws -> String) { + XCTAssertEqual(try host(), universalLink.host) + } + + func test_general_ul_response(queryItems: inout [URLQueryItem]) { + let id = queryItems.removeFirst { $0.name == "id" }! + test_id(id) + } +} + +extension WeiboHandlerBaseTests { + + func test_id(_ queryItem: URLQueryItem) { + XCTAssertNotNil(try UUID(uuidString: XCTUnwrap(queryItem.value))) + } +} + +// MARK: - General - Pasteboard - Response + +extension WeiboHandlerBaseTests: GeneralPasteboardResponseTestCase { + + func extract_major_pb_response(items: inout [[String: Data]]) -> [String: Any] { + if context.shareState == .responseUniversalLink { + return extract_KeyedArchiver_pb(items: &items, key: "transferObject") + } + + return [:] + } + + func test_general_pb_response(dictionary: inout [String: Any]) { + test_general_pb_request(dictionary: &dictionary) + } + + func test_extra_pb_response(items: inout [[String: Data]]) { + test_app(&items) + + test_sdkVersion(&items) + + if context.shareState == .success { + test_userInfo_share(&items) + } + } +} diff --git a/Tests/NBusTests/Weibo/WeiboHandlerBaseTests+Oauth.swift b/Tests/NBusTests/Weibo/WeiboHandlerBaseTests+Oauth.swift new file mode 100644 index 0000000..483269d --- /dev/null +++ b/Tests/NBusTests/Weibo/WeiboHandlerBaseTests+Oauth.swift @@ -0,0 +1,66 @@ +// +// WeiboHandlerBaseTests+Oauth.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import XCTest + +// MARK: - Oauth + +extension WeiboHandlerBaseTests: OauthTestCase { + + func test_oauth() { + test_oauth(Platforms.weibo) + } +} + +// MARK: - Oauth - Platform - Scheme + +extension WeiboHandlerBaseTests: OauthPlatformSchemeTestCase { + + func report_oauth_scheme(_ platform: Platform) -> Set { + [] + } +} + +// MARK: - Oauth - Platform - UniversalLink - Request + +extension WeiboHandlerBaseTests: OauthPlatformUniversalRequestLinkTestCase { + + func test_oauth_ul_request(path: String) { + test_share_ul_request(path: path) + } + + func test_oauth_ul_request(queryItems: inout [URLQueryItem], _ platform: Platform) { + XCTAssertTrue(true) + } +} + +// MARK: - Oauth - Platform - Pasteboard - Request + +extension WeiboHandlerBaseTests: OauthPlatformPasteboardRequestTestCase { + + func test_oauth_pb_request(dictionary: inout [String: Any], _ platform: Platform) { + let `class` = dictionary.removeValue(forKey: "__class") as! String + test_class_oauth(`class`) + + let redirectURI = dictionary.removeValue(forKey: "redirectURI") as! String + test_redirectURI(redirectURI) + } +} + +extension WeiboHandlerBaseTests { + + func test_class_oauth(_ value: String) { + XCTAssertEqual(value, "WBAuthorizeRequest") + } + + func test_redirectURI(_ value: String) { + XCTAssertEqual(value, redirectLink.absoluteString) + } +} diff --git a/Tests/NBusTests/Weibo/WeiboHandlerBaseTests+Share.swift b/Tests/NBusTests/Weibo/WeiboHandlerBaseTests+Share.swift new file mode 100644 index 0000000..ef40ff5 --- /dev/null +++ b/Tests/NBusTests/Weibo/WeiboHandlerBaseTests+Share.swift @@ -0,0 +1,443 @@ +// +// WeiboHandlerBaseTests+Share.swift +// BusTests +// +// Created by nuomi1 on 2022/4/10. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import XCTest + +// MARK: - Share + +extension WeiboHandlerBaseTests: ShareTestCase { + + func test_share_text_timeline() { + test_share(MediaSource.text, Endpoints.Weibo.timeline) + } + + func test_share_image_timeline() { + test_share(MediaSource.image, Endpoints.Weibo.timeline) + } + + func test_share_audio_timeline() { + test_share(MediaSource.audio, Endpoints.Weibo.timeline) + } + + func test_share_video_timeline() { + test_share(MediaSource.video, Endpoints.Weibo.timeline) + } + + func test_share_webPage_timeline() { + test_share(MediaSource.webPage, Endpoints.Weibo.timeline) + } + + func test_share_file_timeline() { + test_share(MediaSource.file, Endpoints.Weibo.timeline) + } + + func test_share_miniProgram_timeline() { + test_share(MediaSource.wechatMiniProgram, Endpoints.Weibo.timeline) + } +} + +// MARK: - Share - Message - Scheme + +extension WeiboHandlerBaseTests: ShareMessageSchemeTestCase { + + func report_share_scheme(_ message: MessageType, _ endpoint: Endpoint) -> Set { + switch message { + case is TextMessage, + is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage, + is FileMessage, + is MiniProgramMessage: + return [] + default: + fatalError() + } + } +} + +// MARK: - Share - Message - UniversalLink - Request + +extension WeiboHandlerBaseTests: ShareMessageUniversalLinkRequestTestCase { + + func test_share_ul_request(path: String) { + XCTAssertEqual(path, "/weibosdk/request") + } + + func test_share_ul_request(queryItems: inout [URLQueryItem], _ message: MessageType, _ endpoint: Endpoint) { + if context.shareState == .requestFirst { + let checkLink = queryItems.removeFirst { $0.name == "checkLink" }! + test_checkLink_request(checkLink) + } + + if context.shareState == .requestSecond { + let lfid = queryItems.removeFirst { $0.name == "lfid" }! + test_lfid(lfid) + } + + if context.shareState == .requestSecond { + let luicode = queryItems.removeFirst { $0.name == "luicode" }! + test_luicode(luicode) + } + + if context.shareState == .requestSecond { + let sdkversion = queryItems.removeFirst { $0.name == "sdkversion" }! + test_sdkversion(sdkversion) + } + + if context.shareState == .requestSecond { + let urltype = queryItems.removeFirst { $0.name == "urltype" }! + test_urltype(urltype) + } + } +} + +extension WeiboHandlerBaseTests { + + func test_checkLink_request(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), universalLink.absoluteString) + } + + func test_lfid(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), bundleID) + } + + func test_luicode(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "10000360") + } + + func test_sdkversion(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), sdkVersion) + } + + func test_urltype(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "link") + } +} + +// MARK: - Share - Message - Pasteboard - Request + +extension WeiboHandlerBaseTests: ShareMessagePasteboardRequestTestCase { + + func test_share_pb_request(dictionary: inout [String: Any], _ message: MessageType, _ endpoint: Endpoint) { + if context.shareState == .requestSecond { + let `class` = dictionary.removeValue(forKey: "__class") as! String + test_class_share(`class`) + } + + if context.shareState == .requestSecond { + let _message = dictionary.removeValue(forKey: "message") as! [String: Any] + test_message(_message, message, endpoint) + } + } +} + +extension WeiboHandlerBaseTests { + + func test_class_share(_ value: String) { + XCTAssertEqual(value, "WBSendMessageToWeiboRequest") + } + + func test_message(_ value: [String: Any], _ message: MessageType, _ endpoint: Endpoint) { + var dictionary = value + + logger.debug("\(UIPasteboard.self), start, \(dictionary.keys.sorted())") + + let `class` = dictionary.removeValue(forKey: "__class") as! String + test_class_message(`class`) + + let imageObject = dictionary.removeValue(forKey: "imageObject") as? [String: Any] + test_imageObject(imageObject, message, endpoint) + + let mediaObject = dictionary.removeValue(forKey: "mediaObject") as? [String: Any] + test_mediaObject(mediaObject, message, endpoint) + + let text = dictionary.removeValue(forKey: "text") as? String + test_text(text, message) + + logger.debug("\(UIPasteboard.self), end, \(dictionary.keys.sorted())") + + XCTAssertTrue(dictionary.isEmpty) + } +} + +extension WeiboHandlerBaseTests { + + func test_class_message(_ value: String) { + XCTAssertEqual(value, "WBMessageObject") + } + + func test_imageObject(_ value: [String: Any]?, _ message: MessageType, _ endpoint: Endpoint) { + switch message { + case is TextMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage: + XCTAssertNil(value) + case is ImageMessage: + test_image(try! XCTUnwrap(value), message, endpoint) + default: + fatalError() + } + } + + func test_mediaObject(_ value: [String: Any]?, _ message: MessageType, _ endpoint: Endpoint) { + switch message { + case is TextMessage, + is ImageMessage: + XCTAssertNil(value) + case is AudioMessage, + is VideoMessage, + is WebPageMessage: + test_media(try! XCTUnwrap(value), message, endpoint) + default: + fatalError() + } + } + + func test_text(_ value: String?, _ message: MessageType) { + switch message { + case let message as TextMessage: + XCTAssertEqual(try XCTUnwrap(value), message.text) + case is ImageMessage, + is AudioMessage, + is VideoMessage, + is WebPageMessage: + XCTAssertNil(value) + default: + fatalError() + } + } +} + +extension WeiboHandlerBaseTests { + + func test_image(_ value: [String: Any], _ message: MessageType, _ endpoint: Endpoint) { + var dictionary = value + + logger.debug("\(UIPasteboard.self), start, \(dictionary.keys.sorted())") + + let imageData = dictionary.removeValue(forKey: "imageData") as! Data + test_imageData(imageData, message) + + logger.debug("\(UIPasteboard.self), end, \(dictionary.keys.sorted())") + + XCTAssertTrue(dictionary.isEmpty) + } +} + +extension WeiboHandlerBaseTests { + + func test_imageData(_ value: Data, _ message: MessageType) { + switch message { + case let message as ImageMessage: + XCTAssertEqual(value, message.data) + default: + fatalError() + } + } +} + +extension WeiboHandlerBaseTests { + + func test_media(_ value: [String: Any], _ message: MessageType, _ endpoint: Endpoint) { + var dictionary = value + + logger.debug("\(UIPasteboard.self), start, \(dictionary.keys.sorted())") + + let `class` = dictionary.removeValue(forKey: "__class") as! String + test_class_media(`class`) + + let description = dictionary.removeValue(forKey: "description") as! String + test_description(description, message) + + let objectID = dictionary.removeValue(forKey: "objectID") as! String + test_objectID(objectID) + + let thumbnailData = dictionary.removeValue(forKey: "thumbnailData") as! Data + test_thumbnailData(thumbnailData, message) + + let title = dictionary.removeValue(forKey: "title") as! String + test_title(title, message) + + let webpageUrl = dictionary.removeValue(forKey: "webpageUrl") as! String + test_webpageUrl(webpageUrl, message) + + logger.debug("\(UIPasteboard.self), end, \(dictionary.keys.sorted())") + + XCTAssertTrue(dictionary.isEmpty) + } +} + +extension WeiboHandlerBaseTests { + + func test_class_media(_ value: String) { + XCTAssertEqual(value, "WBWebpageObject") + } + + func test_description(_ value: String, _ message: MessageType) { + switch message { + case let message as AudioMessage: + XCTAssertEqual(value, message.description) + case let message as VideoMessage: + XCTAssertEqual(value, message.description) + case let message as WebPageMessage: + XCTAssertEqual(value, message.description) + default: + fatalError() + } + } + + func test_objectID(_ value: String) { + XCTAssertNotNil(UUID(uuidString: value)) + } + + func test_thumbnailData(_ value: Data, _ message: MessageType) { + switch message { + case let message as AudioMessage: + XCTAssertEqual(value, message.thumbnail) + case let message as VideoMessage: + XCTAssertEqual(value, message.thumbnail) + case let message as WebPageMessage: + XCTAssertEqual(value, message.thumbnail) + default: + fatalError() + } + } + + func test_title(_ value: String, _ message: MessageType) { + switch message { + case let message as AudioMessage: + XCTAssertEqual(value, message.title) + case let message as VideoMessage: + XCTAssertEqual(value, message.title) + case let message as WebPageMessage: + XCTAssertEqual(value, message.title) + default: + fatalError() + } + } + + func test_webpageUrl(_ value: String, _ message: MessageType) { + switch message { + case let message as AudioMessage: + XCTAssertEqual(value, message.link.absoluteString) + case let message as VideoMessage: + XCTAssertEqual(value, message.link.absoluteString) + case let message as WebPageMessage: + XCTAssertEqual(value, message.link.absoluteString) + default: + fatalError() + } + } +} + +// MARK: - Share - Message - URLScheme - Response + +extension WeiboHandlerBaseTests: ShareMessageURLSchemeResponseTestCase { + + func test_share_us_response(path: String) { + fatalError() + } + + func test_share_us_response(queryItems: inout [URLQueryItem], _ message: MessageType, _ endpoint: Endpoint) { + fatalError() + } +} + +// MARK: - Share - Message - UniversalLink - Response + +extension WeiboHandlerBaseTests: ShareMessageUniversalLinkResponseTestCase { + + func test_share_ul_response(path: String) { + XCTAssertEqual(path, universalLink.appendingPathComponent("weibosdk/response").path) + } + + func test_share_ul_response(queryItems: inout [URLQueryItem], _ message: MessageType, _ endpoint: Endpoint) { + if context.shareState == .responseSignToken { + let checkLink = queryItems.removeFirst { $0.name == "checkLink" }! + test_checkLink_response(checkLink) + } + + if context.shareState == .responseSignToken { + let checkStatus = queryItems.removeFirst { $0.name == "checkStatus" }! + test_checkStatus(checkStatus) + } + + if context.shareState == .responseUniversalLink { + let sdkversion = queryItems.removeFirst { $0.name == "sdkversion" }! + test_sdkversion_response(sdkversion) + } + } +} + +extension WeiboHandlerBaseTests { + + func test_checkLink_response(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "https://open.weibo.com/weibosdk") + } + + func test_checkStatus(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), "1") + } + + func test_sdkversion_response(_ queryItem: URLQueryItem) { + XCTAssertEqual(try XCTUnwrap(queryItem.value), remoteSDKShortVersion) + } +} + +// MARK: - Share - Message - Pasteboard - Response + +extension WeiboHandlerBaseTests: ShareMessagePasteboardResponseTestCase { + + func test_share_pb_response(dictionary: inout [String: Any], _ message: MessageType, _ endpoint: Endpoint) { + if context.shareState == .responseUniversalLink { + let `class` = dictionary.removeValue(forKey: "__class") as! String + test_class_share_response(`class`) + } + + if context.shareState == .responseUniversalLink { + let requestID = dictionary.removeValue(forKey: "requestID") as! String + test_requestID(requestID) + } + + if context.shareState == .responseUniversalLink { + let responseID = dictionary.removeValue(forKey: "responseID") as! String + test_responseID(responseID) + } + + if context.shareState == .responseUniversalLink { + let statusCode = dictionary.removeValue(forKey: "statusCode") as! Int + test_statusCode_share(statusCode) + } + } +} + +extension WeiboHandlerBaseTests { + + func test_class_share_response(_ value: String) { + XCTAssertEqual(value, "WBSendMessageToWeiboResponse") + } + + func test_responseID(_ value: String) { + XCTAssertNotNil(UUID(uuidString: value)) + } + + func test_statusCode_share(_ value: Int) { + switch value { + case 0: + context.shareState = .success + case -1: + context.shareState = .failure + default: + fatalError() + } + } +} diff --git a/Tests/NBusTests/Weibo/WeiboHandlerBaseTests.swift b/Tests/NBusTests/Weibo/WeiboHandlerBaseTests.swift new file mode 100644 index 0000000..0c893f1 --- /dev/null +++ b/Tests/NBusTests/Weibo/WeiboHandlerBaseTests.swift @@ -0,0 +1,67 @@ +// +// WeiboHandlerBaseTests.swift +// BusTests +// +// Created by nuomi1 on 2022/4/4. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus +import NBusWeiboSDK +import RxSwift +import XCTest + +class WeiboHandlerBaseTests: HandlerBaseTests { + + override var appID: String { + switch handler { + case let handler as WeiboSDKHandler: + return handler.appID + case let handler as WeiboHandler: + return handler.appID + default: + fatalError() + } + } + + var redirectLink: URL { + switch handler { + case let handler as WeiboSDKHandler: + return handler.redirectLink + case let handler as WeiboHandler: + return handler.redirectLink + default: + fatalError() + } + } + + var remoteSDKShortVersion: String { + "2.5" + } + + override var sdkShortVersion: String { + "3.3" + } + + override var sdkVersion: String { + "003233000" + } + + override var universalLink: URL { + switch handler { + case let handler as WeiboSDKHandler: + return handler.universalLink + case let handler as WeiboHandler: + return handler.universalLink + default: + fatalError() + } + } + + lazy var dateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss:SSS" + return dateFormatter + }() +} diff --git a/Tests/NBusTests/Weibo/WeiboSDKHandlerTests.swift b/Tests/NBusTests/Weibo/WeiboSDKHandlerTests.swift new file mode 100644 index 0000000..06b8e1c --- /dev/null +++ b/Tests/NBusTests/Weibo/WeiboSDKHandlerTests.swift @@ -0,0 +1,17 @@ +// +// WeiboSDKHandlerTests.swift +// BusTests +// +// Created by nuomi1 on 2022/4/4. +// Copyright © 2022 nuomi1. All rights reserved. +// + +import Foundation +@testable import NBus + +class WeiboSDKHandlerTests: WeiboHandlerBaseTests { + + override var handler: HandlerType { AppState.weiboSDKHandler } + + override var category: AppState.PlatformItem.Category { .sdk } +} diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 34c4c3d..620e6e6 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -240,7 +240,7 @@ end # download def _download_sdk(vendor, vendor_version, vendor_url, vendor_sha256, vendor_search) - root = (Pathname.pwd / "../NBus/Vendor").expand_path + root = (Pathname.pwd / "../Vendor").expand_path vendor_file = root / "Vendor_#{vendor}_#{vendor_version}.zip" vendor_zip_directory = root / "#{vendor}ZIP" @@ -392,7 +392,7 @@ end # upload def _upload_sdk(vendor, vendor_version, vendor_package) - root = (Pathname.pwd / "../NBus/Vendor").expand_path + root = (Pathname.pwd / "../Vendor").expand_path vendor_sdk_directory = root / "#{vendor}SDK" vendor_img_directory = root / "#{vendor}IMG"