新增举报摄影师模块并接入首页路由,桌面应用名改为随心瞰商家版。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -16,13 +16,14 @@ final class HomeAllFunctionsBuilderTests: XCTestCase {
|
||||
topLevelPermissions: [
|
||||
permission(name: "基本信息", uri: "basic_info"),
|
||||
permission(name: "位置上报", uri: "location_report"),
|
||||
permission(name: "举报摄影师", uri: "photographer_report"),
|
||||
permission(name: "相册管理", uri: "album_list"),
|
||||
permission(name: "我的钱包", uri: "wallet")
|
||||
],
|
||||
commonURIs: []
|
||||
)
|
||||
|
||||
XCTAssertEqual(snapshot.allFunctions.map(\.uri), ["location_report", "wallet"])
|
||||
XCTAssertEqual(snapshot.allFunctions.map(\.uri), ["location_report", "photographer_report", "wallet"])
|
||||
}
|
||||
|
||||
/// 测试保持 API 顶层顺序,不使用 preferredOrder 重排。
|
||||
|
||||
@ -269,6 +269,22 @@ final class HomeCommonMenuStoreTests: XCTestCase {
|
||||
XCTAssertEqual(uris, ["wallet", "message_center"])
|
||||
}
|
||||
|
||||
/// 测试 photographer_report 属于 Android menuList 白名单,且可加入常用应用。
|
||||
func testPhotographerReportIsWhitelistedAndCanBeAddedToCommon() {
|
||||
XCTAssertTrue(HomeCommonMenuStore.androidHomeMenuURIs.contains("photographer_report"))
|
||||
|
||||
let store = HomeCommonMenuStore(defaults: makeIsolatedDefaults())
|
||||
let uris = store.add(
|
||||
"photographer_report",
|
||||
current: ["location_report"],
|
||||
topLevelPermissionURIs: ["location_report", "photographer_report"],
|
||||
roleCode: "photographer",
|
||||
accountScope: "scenic_user_photographer_report"
|
||||
)
|
||||
|
||||
XCTAssertEqual(uris, ["location_report", "photographer_report"])
|
||||
}
|
||||
|
||||
/// 测试非白名单顶层项占用前 4 名额时,默认常用不会用后续白名单项补位。
|
||||
func testDefaultCommonDoesNotBackfillAfterNonCatalogSlots() {
|
||||
let uris = HomeCommonMenuStore.defaultCommonURIs(
|
||||
|
||||
@ -22,52 +22,59 @@ final class HomeMenuRouterTests: XCTestCase {
|
||||
|
||||
/// 测试已迁移的首页路由会解析到真实目标。
|
||||
func testMigratedRoutesResolveToDestinations() {
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "space_settings", title: ""), .destination(.profileSpace))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "scenicselection", title: ""), .destination(.scenicSelection))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "store", title: ""), .destination(.moreFunctions))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "wallet", title: ""), .destination(.wallet))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "payment_collection", title: ""), .destination(.paymentCollection))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "payment_qr", title: ""), .destination(.paymentCollection))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "payment_code", title: ""), .destination(.paymentCollection))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "permission_apply", title: ""), .destination(.permissionApply))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "permission_apply_status", title: ""), .destination(.permissionApplyStatus))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "scenicapplication", title: ""), .destination(.scenicApplication))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "task_management", title: ""), .destination(.taskManagement))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "task_management_editor", title: ""), .destination(.taskManagement))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "task_create", title: ""), .destination(.taskCreate))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "cloud_management", title: ""), .destination(.cloudStorage))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "cloud_storage_transit", title: ""), .destination(.cloudStorageTransit))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "asset_management", title: ""), .destination(.materialLibrary))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "material_upload", title: ""), .destination(.materialUpload))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "album_list", title: ""), .destination(.albumList))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "album_trailer", title: ""), .destination(.albumTrailer))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "sample_management", title: ""), .destination(.sampleLibrary))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "sample_upload", title: ""), .destination(.sampleUpload))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "checkin_points", title: ""), .destination(.punchPointList))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "location_report", title: ""), .destination(.locationReport))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "location_report_history", title: ""), .destination(.locationReportHistory))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "pm", title: ""), .destination(.projectManagement))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "project_edit", title: ""), .destination(.projectManagement))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "pm_manager", title: ""), .destination(.pmProjectManagement))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "schedule_management", title: ""), .destination(.scheduleManagement))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "registration_invitation", title: ""), .destination(.photographerInvite))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "photographer_invite", title: ""), .destination(.photographerInvite))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "invite_record", title: ""), .destination(.inviteRecord))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "deposit_order_detail", title: ""), .destination(.depositOrders))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "deposit_order", title: ""), .destination(.depositOrders))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "deposit_order_shooting_info", title: ""), .destination(.depositOrders))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "withdrawal_audit", title: ""), .destination(.withdrawalAudit))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "cooperation_order", title: ""), .destination(.cooperationOrder))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "scenic_settlement", title: ""), .destination(.scenicSettlement))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "scenic_settlement_review", title: ""), .destination(.scenicSettlementReview))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "message_center", title: ""), .destination(.messageCenter))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "/scenic-queue", title: ""), .destination(.queueManagement))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "queue_management", title: ""), .destination(.queueManagement))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "live_stream_management", title: ""), .destination(.liveManagement))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "live_album", title: ""), .destination(.liveAlbum))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "travel_album", title: ""), .destination(.travelAlbumEntry))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "operating-area", title: ""), .destination(.operatingArea))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "pilot_cert", title: ""), .destination(.pilotCertification))
|
||||
let expectations: [(String, HomeMenuResolvedRoute)] = [
|
||||
("space_settings", .destination(.profileSpaceSettings)),
|
||||
("scenicselection", .destination(.scenicSelection)),
|
||||
("store", .destination(.moreFunctions)),
|
||||
("wallet", .destination(.wallet)),
|
||||
("payment_collection", .destination(.paymentCollection)),
|
||||
("payment_qr", .destination(.paymentCollection)),
|
||||
("payment_code", .destination(.paymentCollection)),
|
||||
("permission_apply", .destination(.permissionApply)),
|
||||
("permission_apply_status", .destination(.permissionApplyStatus)),
|
||||
("scenicapplication", .destination(.scenicApplication)),
|
||||
("task_management", .destination(.taskManagement)),
|
||||
("task_management_editor", .destination(.taskManagement)),
|
||||
("task_create", .destination(.taskCreate)),
|
||||
("cloud_management", .destination(.cloudStorage)),
|
||||
("cloud_storage_transit", .destination(.cloudStorageTransit)),
|
||||
("asset_management", .destination(.materialLibrary)),
|
||||
("material_upload", .destination(.materialUpload)),
|
||||
("album_list", .destination(.albumList)),
|
||||
("album_trailer", .destination(.albumTrailer)),
|
||||
("sample_management", .destination(.sampleLibrary)),
|
||||
("sample_upload", .destination(.sampleUpload)),
|
||||
("checkin_points", .destination(.punchPointList)),
|
||||
("location_report", .destination(.locationReport)),
|
||||
("location_report_history", .destination(.locationReportHistory)),
|
||||
("photographer_report", .destination(.photographerReport)),
|
||||
("pm", .destination(.projectManagement)),
|
||||
("project_edit", .destination(.projectManagement)),
|
||||
("pm_manager", .destination(.pmProjectManagement)),
|
||||
("schedule_management", .destination(.scheduleManagement)),
|
||||
("registration_invitation", .destination(.photographerInvite)),
|
||||
("photographer_invite", .destination(.photographerInvite)),
|
||||
("invite_record", .destination(.inviteRecord)),
|
||||
("deposit_order_detail", .destination(.depositOrders)),
|
||||
("deposit_order", .destination(.depositOrders)),
|
||||
("deposit_order_shooting_info", .destination(.depositOrders)),
|
||||
("withdrawal_audit", .destination(.withdrawalAudit)),
|
||||
("cooperation_order", .destination(.cooperationOrder)),
|
||||
("scenic_settlement", .destination(.scenicSettlement)),
|
||||
("scenic_settlement_review", .destination(.scenicSettlementReview)),
|
||||
("message_center", .destination(.messageCenter)),
|
||||
("/scenic-queue", .destination(.queueManagement)),
|
||||
("queue_management", .destination(.queueManagement)),
|
||||
("live_stream_management", .destination(.liveManagement)),
|
||||
("live_album", .destination(.liveAlbum)),
|
||||
("travel_album", .destination(.travelAlbumEntry)),
|
||||
("operating-area", .destination(.operatingArea)),
|
||||
("pilot_cert", .destination(.pilotCertification))
|
||||
]
|
||||
|
||||
for (uri, expected) in expectations {
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: uri, title: ""), expected, "URI \(uri) should resolve to migrated destination")
|
||||
}
|
||||
}
|
||||
|
||||
/// 测试 Tab 路由仍集中在 HomeMenuRouter。
|
||||
@ -184,6 +191,7 @@ final class HomeMenuRouterTests: XCTestCase {
|
||||
XCTAssertTrue(uris.contains("sample_management"))
|
||||
XCTAssertTrue(uris.contains("checkin_points"))
|
||||
XCTAssertTrue(uris.contains("location_report"))
|
||||
XCTAssertTrue(uris.contains("photographer_report"))
|
||||
XCTAssertTrue(uris.contains("pm"))
|
||||
XCTAssertTrue(uris.contains("pm_manager"))
|
||||
XCTAssertTrue(uris.contains("schedule_management"))
|
||||
@ -201,6 +209,7 @@ final class HomeMenuRouterTests: XCTestCase {
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "sample_upload", title: ""), .destination(.sampleUpload))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "checkin_points", title: ""), .destination(.punchPointList))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "location_report", title: ""), .destination(.locationReport))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "photographer_report", title: ""), .destination(.photographerReport))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "pm", title: ""), .destination(.projectManagement))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "pm_manager", title: ""), .destination(.pmProjectManagement))
|
||||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "schedule_management", title: ""), .destination(.scheduleManagement))
|
||||
|
||||
@ -0,0 +1,141 @@
|
||||
//
|
||||
// WildPhotographerReportTests.swift
|
||||
// suixinkanTests
|
||||
//
|
||||
// Created by Codex on 2026/7/1.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import suixinkan
|
||||
|
||||
/// 举报摄影师模块测试,覆盖 Mock 数据、提交校验和列表筛选。
|
||||
@MainActor
|
||||
final class WildPhotographerReportHomeViewModelTests: XCTestCase {
|
||||
/// 测试种子数据包含风险点和举报记录。
|
||||
func testSeedDataLoadsReportsAndRiskPoints() {
|
||||
let viewModel = WildPhotographerReportHomeViewModel()
|
||||
|
||||
XCTAssertFalse(viewModel.reports.isEmpty)
|
||||
XCTAssertFalse(viewModel.riskPoints.isEmpty)
|
||||
XCTAssertEqual(viewModel.reports.count, WildPhotographerReportMockStore.seedReports.count)
|
||||
}
|
||||
|
||||
/// 测试提交后生成 JB 前缀编号并插入列表首位。
|
||||
func testSubmitReportGeneratesDynamicID() {
|
||||
let viewModel = WildPhotographerReportHomeViewModel()
|
||||
let beforeCount = viewModel.reports.count
|
||||
|
||||
let record = viewModel.submitReport(
|
||||
reportType: .suspectedWild,
|
||||
description: "测试举报说明",
|
||||
location: "测试景区 · 测试点位",
|
||||
contact: "13800138000",
|
||||
imageCount: 1,
|
||||
videoCount: 0
|
||||
)
|
||||
|
||||
XCTAssertEqual(viewModel.reports.count, beforeCount + 1)
|
||||
XCTAssertEqual(viewModel.reports.first?.id, record.id)
|
||||
XCTAssertTrue(record.id.hasPrefix("JB"))
|
||||
XCTAssertEqual(record.status, .pending)
|
||||
XCTAssertEqual(record.reportType, .suspectedWild)
|
||||
XCTAssertEqual(record.phoneNumber, "13800138000")
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
final class WildPhotographerReportSubmitViewModelTests: XCTestCase {
|
||||
/// 测试无证据时提交会被拦截。
|
||||
func testSubmitFailsWithoutEvidence() {
|
||||
let homeViewModel = WildPhotographerReportHomeViewModel()
|
||||
let viewModel = WildPhotographerReportSubmitViewModel(homeViewModel: homeViewModel)
|
||||
viewModel.images = []
|
||||
viewModel.videos = []
|
||||
viewModel.description = "有说明"
|
||||
viewModel.reportLocation = "测试位置"
|
||||
viewModel.locationConfirmed = true
|
||||
|
||||
viewModel.submitReport()
|
||||
|
||||
XCTAssertEqual(viewModel.validationMessage, "请至少上传一项现场证据。")
|
||||
XCTAssertNil(viewModel.submitResult)
|
||||
}
|
||||
|
||||
/// 测试无说明时提交会被拦截。
|
||||
func testSubmitFailsWithoutDescription() {
|
||||
let homeViewModel = WildPhotographerReportHomeViewModel()
|
||||
let viewModel = WildPhotographerReportSubmitViewModel(homeViewModel: homeViewModel)
|
||||
viewModel.description = " "
|
||||
viewModel.reportLocation = "测试位置"
|
||||
viewModel.locationConfirmed = true
|
||||
|
||||
viewModel.submitReport()
|
||||
|
||||
XCTAssertEqual(viewModel.validationMessage, "请填写举报说明。")
|
||||
XCTAssertNil(viewModel.submitResult)
|
||||
}
|
||||
|
||||
/// 测试定位未确认时提交会被拦截。
|
||||
func testSubmitFailsWithoutConfirmedLocation() {
|
||||
let homeViewModel = WildPhotographerReportHomeViewModel()
|
||||
let viewModel = WildPhotographerReportSubmitViewModel(homeViewModel: homeViewModel)
|
||||
viewModel.description = "测试说明"
|
||||
viewModel.locationConfirmed = false
|
||||
|
||||
viewModel.submitReport()
|
||||
|
||||
XCTAssertEqual(viewModel.validationMessage, "请先更新举报位置后再提交举报。")
|
||||
XCTAssertNil(viewModel.submitResult)
|
||||
}
|
||||
|
||||
/// 测试定位更新中时提交会被拦截。
|
||||
func testSubmitFailsWhileUpdatingLocation() {
|
||||
let homeViewModel = WildPhotographerReportHomeViewModel()
|
||||
let viewModel = WildPhotographerReportSubmitViewModel(homeViewModel: homeViewModel)
|
||||
viewModel.description = "测试说明"
|
||||
viewModel.isUpdatingLocation = true
|
||||
viewModel.locationConfirmed = true
|
||||
viewModel.reportLocation = "测试位置"
|
||||
|
||||
viewModel.submitReport()
|
||||
|
||||
XCTAssertEqual(viewModel.validationMessage, "正在获取定位,请稍后再提交。")
|
||||
XCTAssertNil(viewModel.submitResult)
|
||||
}
|
||||
|
||||
/// 测试校验通过后会写入共享 Mock 列表。
|
||||
func testSubmitSuccessCreatesRecord() {
|
||||
let homeViewModel = WildPhotographerReportHomeViewModel()
|
||||
let viewModel = WildPhotographerReportSubmitViewModel(homeViewModel: homeViewModel)
|
||||
viewModel.description = "测试说明"
|
||||
viewModel.reportLocation = "测试景区 · 测试点"
|
||||
viewModel.locationConfirmed = true
|
||||
|
||||
viewModel.submitReport()
|
||||
|
||||
XCTAssertNil(viewModel.validationMessage)
|
||||
XCTAssertNotNil(viewModel.submitResult)
|
||||
XCTAssertEqual(homeViewModel.reports.first?.id, viewModel.submitResult?.record.id)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
final class WildPhotographerReportListViewModelTests: XCTestCase {
|
||||
/// 测试列表筛选只返回对应状态记录。
|
||||
func testFilterReturnsMatchingStatus() {
|
||||
let homeViewModel = WildPhotographerReportHomeViewModel()
|
||||
let viewModel = WildPhotographerReportListViewModel(homeViewModel: homeViewModel)
|
||||
|
||||
viewModel.selectFilter(.pending)
|
||||
XCTAssertTrue(viewModel.filteredReports.allSatisfy { $0.status == .pending })
|
||||
|
||||
viewModel.selectFilter(.processing)
|
||||
XCTAssertTrue(viewModel.filteredReports.allSatisfy { $0.status == .processing })
|
||||
|
||||
viewModel.selectFilter(.processed)
|
||||
XCTAssertTrue(viewModel.filteredReports.allSatisfy { $0.status == .processed })
|
||||
|
||||
viewModel.selectFilter(.all)
|
||||
XCTAssertEqual(viewModel.filteredReports.count, homeViewModel.reports.count)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user