219 lines
6.7 KiB
Swift
219 lines
6.7 KiB
Swift
import Testing
|
|
import UIKit
|
|
|
|
@testable import ExpoRouter
|
|
|
|
// MARK: - Mock views
|
|
|
|
/// Mock tab screen: has @objc tabKey property, mimicking RNScreens tab screen views.
|
|
private class MockTabScreenView: UIView {
|
|
@objc var tabKey: String?
|
|
}
|
|
|
|
/// Mock tab host: has @objc controller property, mimicking RNScreens tab host views.
|
|
private class MockTabHostView: UIView {
|
|
@objc var controller: UIViewController?
|
|
}
|
|
|
|
/// Mock tab host with a non-UIViewController controller property.
|
|
private class MockTabHostWithBadController: UIView {
|
|
@objc var controller: NSObject? = NSObject()
|
|
}
|
|
|
|
/// Mock view with a reactViewController() method that returns a UIViewController.
|
|
private class MockTabScreenWithReactVC: UIView {
|
|
@objc var tabKey: String?
|
|
private var _reactViewController: UIViewController?
|
|
|
|
func configure(reactViewController: UIViewController) {
|
|
_reactViewController = reactViewController
|
|
}
|
|
|
|
@objc override func reactViewController() -> UIViewController? {
|
|
return _reactViewController
|
|
}
|
|
}
|
|
|
|
// MARK: - Unit tests
|
|
|
|
@Suite("RNScreensTabCompat unit tests")
|
|
struct RNScreensTabCompatUnitTests {
|
|
|
|
@Suite("isTabScreen")
|
|
struct IsTabScreen {
|
|
@Test
|
|
func `detects mock tab screen`() {
|
|
let tabScreen = MockTabScreenView()
|
|
#expect(RNScreensTabCompat.isTabScreen(tabScreen))
|
|
}
|
|
|
|
@Test
|
|
func `rejects plain UIView`() {
|
|
let plainView = UIView()
|
|
#expect(!RNScreensTabCompat.isTabScreen(plainView))
|
|
}
|
|
|
|
@Test
|
|
func `rejects mock tab host`() {
|
|
let tabHost = MockTabHostView()
|
|
#expect(!RNScreensTabCompat.isTabScreen(tabHost))
|
|
}
|
|
}
|
|
|
|
@Suite("tabKey")
|
|
struct TabKey {
|
|
@Test
|
|
func `reads value`() {
|
|
let tabScreen = MockTabScreenView()
|
|
tabScreen.tabKey = "home"
|
|
#expect(RNScreensTabCompat.tabKey(from: tabScreen) == "home")
|
|
}
|
|
|
|
@Test
|
|
func `returns nil for nil tab key`() {
|
|
let tabScreen = MockTabScreenView()
|
|
tabScreen.tabKey = nil
|
|
#expect(RNScreensTabCompat.tabKey(from: tabScreen) == nil)
|
|
}
|
|
|
|
@Test
|
|
func `returns nil for plain UIView`() {
|
|
let plainView = UIView()
|
|
#expect(RNScreensTabCompat.tabKey(from: plainView) == nil)
|
|
}
|
|
}
|
|
|
|
@Suite("tabBarController(fromTabHost:)")
|
|
struct TabBarControllerFromTabHost {
|
|
@Test
|
|
func `returns tab bar controller`() {
|
|
let tabHost = MockTabHostView()
|
|
let tabBarController = UITabBarController()
|
|
tabHost.controller = tabBarController
|
|
#expect(RNScreensTabCompat.tabBarController(fromTabHost: tabHost) === tabBarController)
|
|
}
|
|
|
|
@Test
|
|
func `returns nil for non-tab bar controller`() {
|
|
let tabHost = MockTabHostView()
|
|
tabHost.controller = UIViewController()
|
|
#expect(RNScreensTabCompat.tabBarController(fromTabHost: tabHost) == nil)
|
|
}
|
|
|
|
@Test
|
|
func `returns nil for nil controller`() {
|
|
let tabHost = MockTabHostView()
|
|
tabHost.controller = nil
|
|
#expect(RNScreensTabCompat.tabBarController(fromTabHost: tabHost) == nil)
|
|
}
|
|
|
|
@Test
|
|
func `returns nil for non-UIViewController type`() {
|
|
let tabHost = MockTabHostWithBadController()
|
|
#expect(RNScreensTabCompat.tabBarController(fromTabHost: tabHost) == nil)
|
|
}
|
|
|
|
@Test
|
|
func `returns nil for plain UIView`() {
|
|
let plainView = UIView()
|
|
#expect(RNScreensTabCompat.tabBarController(fromTabHost: plainView) == nil)
|
|
}
|
|
}
|
|
|
|
@Suite("tabBarController(fromTabScreen:)")
|
|
struct TabBarControllerFromTabScreen {
|
|
@Test
|
|
func `returns tab bar controller via reactViewController`() {
|
|
let tabBarController = UITabBarController()
|
|
let childVC = UIViewController()
|
|
tabBarController.viewControllers = [childVC]
|
|
|
|
let mockView = MockTabScreenWithReactVC()
|
|
mockView.tabKey = "tab1"
|
|
mockView.configure(reactViewController: childVC)
|
|
childVC.view.addSubview(mockView)
|
|
|
|
let result = RNScreensTabCompat.tabBarController(fromTabScreen: mockView)
|
|
#expect(result === tabBarController)
|
|
}
|
|
|
|
@Test
|
|
func `returns nil when reactViewController returns nil`() {
|
|
let mockView = MockTabScreenWithReactVC()
|
|
mockView.tabKey = "tab1"
|
|
// Don't configure — reactViewController() returns nil
|
|
#expect(RNScreensTabCompat.tabBarController(fromTabScreen: mockView) == nil)
|
|
}
|
|
|
|
@Test
|
|
func `returns nil when no reactViewController method`() {
|
|
// MockTabScreenView has tabKey but no reactViewController() method
|
|
let mockView = MockTabScreenView()
|
|
mockView.tabKey = "tab1"
|
|
#expect(RNScreensTabCompat.tabBarController(fromTabScreen: mockView) == nil)
|
|
}
|
|
|
|
@Test
|
|
func `returns nil for plain UIView`() {
|
|
let plainView = UIView()
|
|
#expect(RNScreensTabCompat.tabBarController(fromTabScreen: plainView) == nil)
|
|
}
|
|
|
|
@Test
|
|
func `returns nil when not in tab bar controller`() {
|
|
let navController = UINavigationController()
|
|
let childVC = UIViewController()
|
|
navController.viewControllers = [childVC]
|
|
|
|
let mockView = MockTabScreenWithReactVC()
|
|
mockView.tabKey = "tab1"
|
|
mockView.configure(reactViewController: childVC)
|
|
childVC.view.addSubview(mockView)
|
|
|
|
#expect(RNScreensTabCompat.tabBarController(fromTabScreen: mockView) == nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Integration tests (RNScreens API contract)
|
|
|
|
@Suite("RNScreens API contract")
|
|
struct RNScreensAPIContractTests {
|
|
|
|
@Test
|
|
func `tab screen class responds to tabKey`() throws {
|
|
let cls = NSClassFromString("RNSTabsScreenComponentView")
|
|
?? NSClassFromString("RNSBottomTabsScreenComponentView")
|
|
guard let cls else {
|
|
Issue.record("No tab screen class found — neither RNSTabsScreenComponentView nor RNSBottomTabsScreenComponentView")
|
|
return
|
|
}
|
|
let view = try #require((cls as? UIView.Type)?.init(), "Failed to instantiate tab screen class")
|
|
#expect(view.responds(to: NSSelectorFromString("tabKey")))
|
|
}
|
|
|
|
@Test
|
|
func `tab host class responds to controller`() throws {
|
|
let cls = NSClassFromString("RNSTabsHostComponentView")
|
|
?? NSClassFromString("RNSBottomTabsHostComponentView")
|
|
guard let cls else {
|
|
Issue.record("No tab host class found — neither RNSTabsHostComponentView nor RNSBottomTabsHostComponentView")
|
|
return
|
|
}
|
|
let view = try #require((cls as? UIView.Type)?.init(), "Failed to instantiate tab host class")
|
|
#expect(view.responds(to: NSSelectorFromString("controller")))
|
|
}
|
|
|
|
@Test
|
|
func `tab screen class responds to reactViewController`() throws {
|
|
let cls = NSClassFromString("RNSTabsScreenComponentView")
|
|
?? NSClassFromString("RNSBottomTabsScreenComponentView")
|
|
guard let cls else {
|
|
Issue.record("No tab screen class found")
|
|
return
|
|
}
|
|
let view = try #require((cls as? UIView.Type)?.init(), "Failed to instantiate tab screen class")
|
|
#expect(view.responds(to: NSSelectorFromString("reactViewController")))
|
|
}
|
|
}
|