将会话、权限、位置、收款与排队存储拆分为独立模块,同步更新各 ViewModel 与单元测试,并补充素材管理 UI 与个人空间设置交互。 Co-authored-by: Cursor <cursoragent@cursor.com>
103 lines
4.0 KiB
Swift
103 lines
4.0 KiB
Swift
//
|
|
// ScenicSelectionViewModelTests.swift
|
|
// suixinkanTests
|
|
//
|
|
|
|
import XCTest
|
|
@testable import suixinkan
|
|
|
|
/// 景区选择 ViewModel 测试。
|
|
final class ScenicSelectionViewModelTests: XCTestCase {
|
|
|
|
private var appStore: AppStore!
|
|
private var defaults: UserDefaults!
|
|
|
|
override func setUp() {
|
|
defaults = UserDefaults(suiteName: "ScenicSelectionViewModelTests")!
|
|
defaults.removePersistentDomain(forName: "ScenicSelectionViewModelTests")
|
|
appStore = AppStore(defaults: defaults)
|
|
}
|
|
|
|
override func tearDown() {
|
|
appStore.logout()
|
|
super.tearDown()
|
|
}
|
|
|
|
func testSearchFiltersByNameAndAddress() {
|
|
appStore.permissions.saveRoleScenicList([
|
|
ScenicInfo(id: 1, name: "西湖景区", status: 1, location: ScenicLocationInfo(lng: 120.1, lat: 30.2, address: "杭州西湖"), coverImg: nil),
|
|
ScenicInfo(id: 2, name: "灵隐寺", status: 1, location: ScenicLocationInfo(lng: 120.2, lat: 30.3, address: "杭州灵隐"), coverImg: nil),
|
|
])
|
|
let viewModel = ScenicSelectionViewModel(appStore: appStore)
|
|
|
|
viewModel.loadFromLocal()
|
|
XCTAssertEqual(viewModel.spots.count, 2)
|
|
|
|
viewModel.onSearchQueryChange("西湖")
|
|
XCTAssertEqual(viewModel.spots.count, 1)
|
|
XCTAssertEqual(viewModel.spots.first?.name, "西湖景区")
|
|
|
|
viewModel.onSearchQueryChange("灵隐")
|
|
XCTAssertEqual(viewModel.spots.count, 1)
|
|
XCTAssertEqual(viewModel.spots.first?.name, "灵隐寺")
|
|
}
|
|
|
|
func testNearestTagWhenLocationProvided() {
|
|
appStore.permissions.saveRoleScenicList([
|
|
ScenicInfo(id: 1, name: "近", status: 1, location: ScenicLocationInfo(lng: 116.40, lat: 39.91, address: "A"), coverImg: nil),
|
|
ScenicInfo(id: 2, name: "远", status: 1, location: ScenicLocationInfo(lng: 116.50, lat: 39.95, address: "B"), coverImg: nil),
|
|
])
|
|
let items = ScenicSpotDisplayMapper.buildItems(
|
|
scenicList: appStore.permissions.roleScenicList(),
|
|
searchQuery: "",
|
|
currentLat: 39.9087,
|
|
currentLng: 116.3975,
|
|
selectedScenicId: 0
|
|
)
|
|
XCTAssertEqual(items.first?.openStatus, .nearest)
|
|
}
|
|
|
|
func testClosedStatusTag() {
|
|
appStore.permissions.saveRoleScenicList([
|
|
ScenicInfo(id: 1, name: "停业", status: 2, location: nil, coverImg: nil),
|
|
])
|
|
let viewModel = ScenicSelectionViewModel(appStore: appStore)
|
|
viewModel.loadFromLocal()
|
|
XCTAssertEqual(viewModel.spots.first?.openStatus, .closed)
|
|
XCTAssertEqual(viewModel.spots.first?.statusLabel, "暂未营业")
|
|
}
|
|
|
|
func testSelectSpotWritesAppStore() {
|
|
appStore.permissions.saveRoleScenicList([
|
|
ScenicInfo(id: 5, name: "测试景区", status: 1),
|
|
])
|
|
let viewModel = ScenicSelectionViewModel(appStore: appStore)
|
|
var completed = false
|
|
viewModel.onSelectionComplete = { completed = true }
|
|
|
|
viewModel.loadFromLocal()
|
|
viewModel.selectSpot(viewModel.spots[0])
|
|
|
|
XCTAssertEqual(appStore.session.currentScenicId, 5)
|
|
XCTAssertEqual(appStore.session.currentScenicName, "测试景区")
|
|
XCTAssertTrue(completed)
|
|
XCTAssertTrue(viewModel.spots.first?.isSelected == true)
|
|
}
|
|
|
|
func testOnApplyNowRoutesToStatusWhenPendingReviewing() async throws {
|
|
let pendingJSON = """
|
|
{"code":100000,"msg":"success","data":[{"code":"AP001","role_id":1,"role_name":"摄影师","scenic_list":[],"status":1,"status_label":"审核中","created_at":"","audit_note":"","audited_at":null,"audited_by":null}]}
|
|
""".data(using: .utf8)!
|
|
let api = await MainActor.run {
|
|
HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [pendingJSON])))
|
|
}
|
|
let viewModel = ScenicSelectionViewModel(appStore: appStore)
|
|
var statusCode: String?
|
|
viewModel.onNavigateApplyStatus = { statusCode = $0 }
|
|
|
|
await viewModel.onApplyNow(api: api)
|
|
|
|
XCTAssertEqual(statusCode, "AP001")
|
|
}
|
|
}
|