Replace CYLTabBarController with native UITabBarController for main tabs.

Use a system scan tab slot with MainScanTabItem interception so scanner presentation no longer depends on CYL PlusButton, and remove the CYLTabBarController pod dependency.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-26 19:17:38 +08:00
parent 43179abf2c
commit 7469f92177
12 changed files with 217 additions and 208 deletions

View File

@ -1,73 +0,0 @@
//
// MainScanPlusButtonTests.swift
// suixinkanTests
//
import XCTest
@testable import suixinkan_ios
/// TabBar CYL PlusButton
@MainActor
final class MainScanPlusButtonTests: XCTestCase {
/// Tab
func testIndexOfPlusButtonInTabBarIsBetweenOrdersAndStatistics() {
XCTAssertEqual(MainScanPlusButton.indexOfPlusButtonInTabBar(), 2)
}
/// accessibility
func testPlusButtonAccessibilityIdentifier() {
guard let button = MainScanPlusButton.plusButton() as? UIButton else {
XCTFail("plusButton 应返回 UIButton 实例")
return
}
XCTAssertEqual(button.accessibilityIdentifier, "main.scan")
XCTAssertEqual(button.accessibilityLabel, "扫码核销")
}
/// Tab CYL Tab
func testMainTabBarControllerLoadsWithMatchingCYLAttributes() {
let services = AppServices(apiClient: APIClient(session: MainTabRecordingURLSession()))
let controller = MainTabBarController(services: services)
controller.loadViewIfNeeded()
for tab in AppTab.allCases {
XCTAssertNotNil(controller.navigationController(for: tab))
XCTAssertEqual(
controller.navigationController(for: tab)?.tabBarItem.accessibilityIdentifier,
"main.tab.\(tab.rawValue)"
)
}
}
/// Tab tabBarItem
func testMainTabBarControllerTabBarItemsHaveImages() {
let services = AppServices(apiClient: APIClient(session: MainTabRecordingURLSession()))
let controller = MainTabBarController(services: services)
controller.loadViewIfNeeded()
for tab in AppTab.allCases {
let item = controller.navigationController(for: tab)?.tabBarItem
XCTAssertNotNil(item?.image, "tab \(tab.rawValue) image should not be nil")
XCTAssertNotNil(item?.selectedImage, "tab \(tab.rawValue) selectedImage should not be nil")
XCTAssertGreaterThan(item?.image?.size.width ?? 0, 0)
XCTAssertGreaterThan(item?.selectedImage?.size.width ?? 0, 0)
}
}
}
/// Tab 使
private final class MainTabRecordingURLSession: URLSessionProtocol {
///
func data(for request: URLRequest) async throws -> (Data, URLResponse) {
let response = HTTPURLResponse(
url: request.url ?? URL(string: "https://example.com")!,
statusCode: 200,
httpVersion: nil,
headerFields: nil
)!
return (Data(), response)
}
}

View File

@ -0,0 +1,115 @@
//
// MainScanTabItemTests.swift
// suixinkanTests
//
import XCTest
@testable import suixinkan_ios
/// TabBar TabBar
@MainActor
final class MainScanTabItemTests: XCTestCase {
/// Tab Tab
func testMainTabBarControllerLoadsFiveSystemTabItems() {
let services = AppServices(apiClient: APIClient(session: MainTabRecordingURLSession()))
let controller = MainTabBarController(services: services)
controller.loadViewIfNeeded()
XCTAssertEqual(controller.viewControllers?.count, 5)
for tab in AppTab.allCases {
XCTAssertNotNil(controller.navigationController(for: tab))
XCTAssertEqual(
controller.navigationController(for: tab)?.tabBarItem.accessibilityIdentifier,
"main.tab.\(tab.rawValue)"
)
}
}
///
func testScanTabItemIsCenteredBetweenOrdersAndStatistics() throws {
let services = AppServices(apiClient: APIClient(session: MainTabRecordingURLSession()))
let controller = MainTabBarController(services: services)
controller.loadViewIfNeeded()
let scanItem = try XCTUnwrap(controller.viewControllers?[2].tabBarItem)
XCTAssertEqual(scanItem.title, "扫码")
XCTAssertEqual(scanItem.accessibilityIdentifier, "main.scan")
XCTAssertEqual(scanItem.accessibilityLabel, "扫码核销")
XCTAssertNotNil(scanItem.image)
XCTAssertNotNil(scanItem.selectedImage)
}
///
func testSelectingScanTabPresentsScannerWithoutSelectingPlaceholder() throws {
let services = AppServices(apiClient: APIClient(session: MainTabRecordingURLSession()))
let controller = MainTabBarController(services: services)
var scanTapCount = 0
controller.onScanTabSelected = {
scanTapCount += 1
}
controller.loadViewIfNeeded()
controller.selectedIndex = 1
let scanController = try XCTUnwrap(controller.viewControllers?[2])
XCTAssertFalse(controller.tabBarController(controller, shouldSelect: scanController))
XCTAssertEqual(scanTapCount, 1)
XCTAssertEqual(controller.selectedIndex, 1)
}
/// Tab TabBar
func testSelectingBusinessTabIsAllowed() throws {
let services = AppServices(apiClient: APIClient(session: MainTabRecordingURLSession()))
let controller = MainTabBarController(services: services)
controller.loadViewIfNeeded()
let ordersController = try XCTUnwrap(controller.viewControllers?[1])
XCTAssertTrue(controller.tabBarController(controller, shouldSelect: ordersController))
}
/// Tab tabBarItem
func testMainTabBarControllerTabBarItemsHaveImages() {
let services = AppServices(apiClient: APIClient(session: MainTabRecordingURLSession()))
let controller = MainTabBarController(services: services)
controller.loadViewIfNeeded()
for tab in AppTab.allCases {
let item = controller.navigationController(for: tab)?.tabBarItem
XCTAssertNotNil(item?.image, "tab \(tab.rawValue) image should not be nil")
XCTAssertNotNil(item?.selectedImage, "tab \(tab.rawValue) selectedImage should not be nil")
XCTAssertGreaterThan(item?.image?.size.width ?? 0, 0)
XCTAssertGreaterThan(item?.selectedImage?.size.width ?? 0, 0)
}
}
/// TabBarItem
func testOrderBadgeUsesOrdersNavigationTabBarItem() throws {
let services = AppServices(apiClient: APIClient(session: MainTabRecordingURLSession()))
let controller = MainTabBarController(services: services)
controller.loadViewIfNeeded()
let ordersNavigationController = try XCTUnwrap(controller.navigationController(for: .orders))
ordersNavigationController.tabBarItem.badgeValue = "3"
XCTAssertEqual(controller.viewControllers?[1].tabBarItem.badgeValue, "3")
}
}
/// Tab 使
private final class MainTabRecordingURLSession: URLSessionProtocol {
///
func data(for request: URLRequest) async throws -> (Data, URLResponse) {
let response = HTTPURLResponse(
url: request.url ?? URL(string: "https://example.com")!,
statusCode: 200,
httpVersion: nil,
headerFields: nil
)!
return (Data(), response)
}
}