模块化 AppStore 并完善素材管理与个人空间设置。
将会话、权限、位置、收款与排队存储拆分为独立模块,同步更新各 ViewModel 与单元测试,并补充素材管理 UI 与个人空间设置交互。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -20,14 +20,14 @@ final class ProfileSpaceSettingsViewModelTests: XCTestCase {
|
||||
defaults = UserDefaults(suiteName: "ProfileSpaceSettingsViewModelTests")!
|
||||
defaults.removePersistentDomain(forName: "ProfileSpaceSettingsViewModelTests")
|
||||
appStore = AppStore(defaults: defaults)
|
||||
appStore.currentScenicId = 11
|
||||
appStore.session.currentScenicId = 11
|
||||
calendar = Calendar(identifier: .gregorian)
|
||||
calendar.timeZone = TimeZone(secondsFromGMT: 0)!
|
||||
fixedNow = calendar.date(from: DateComponents(year: 2026, month: 7, day: 8))!
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
AppStore.shared.currentScenicId = 0
|
||||
AppStore.shared.session.currentScenicId = 0
|
||||
appStore.logout()
|
||||
defaults.removePersistentDomain(forName: "ProfileSpaceSettingsViewModelTests")
|
||||
super.tearDown()
|
||||
@ -170,7 +170,8 @@ final class ProfileSpaceSettingsViewModelTests: XCTestCase {
|
||||
viewModel.updateHolidayEndTime("20:00")
|
||||
|
||||
viewModel.updateBusinessEndTime("08:00")
|
||||
XCTAssertThrowsError(try viewModel.makeSaveRequest())
|
||||
XCTAssertEqual(viewModel.businessEndTime, "18:00")
|
||||
XCTAssertNoThrow(try viewModel.makeSaveRequest())
|
||||
}
|
||||
|
||||
func testHasChangesAndSaveRequestFieldsMatchAndroid() async throws {
|
||||
@ -250,7 +251,7 @@ final class ProfileSpaceSettingsViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSpaceSettingsRoutePushesProfileSpaceSettingsPage() {
|
||||
AppStore.shared.currentScenicId = 11
|
||||
AppStore.shared.session.currentScenicId = 11
|
||||
let root = UIViewController()
|
||||
let navigationController = UINavigationController(rootViewController: root)
|
||||
|
||||
@ -259,8 +260,26 @@ final class ProfileSpaceSettingsViewModelTests: XCTestCase {
|
||||
XCTAssertTrue(navigationController.topViewController is ProfileSpaceSettingsViewController)
|
||||
}
|
||||
|
||||
func testCertificationExpansionAndAvatarUploadRefresh() async {
|
||||
let response = PhotographerProfileInfoResponse.loaded(certifications: ["景区1", "景区2", "景区3", "景区4"])
|
||||
let api = FakeProfileSpaceSettingsAPI(response: response)
|
||||
let viewModel = makeViewModel()
|
||||
await viewModel.load(api: api)
|
||||
|
||||
XCTAssertFalse(viewModel.isCertificationExpanded)
|
||||
viewModel.toggleCertificationExpanded()
|
||||
XCTAssertTrue(viewModel.isCertificationExpanded)
|
||||
|
||||
let avatarData = Data([1, 2, 3])
|
||||
await viewModel.uploadAvatar(data: avatarData, fileName: "avatar.jpg", api: api)
|
||||
|
||||
XCTAssertEqual(api.uploadedAvatarData, avatarData)
|
||||
XCTAssertEqual(api.uploadedAvatarFileName, "avatar.jpg")
|
||||
XCTAssertEqual(viewModel.avatarURL, "https://example.com/new-avatar.jpg")
|
||||
}
|
||||
|
||||
func testSystemSettingsRoutePushesSettingPage() {
|
||||
AppStore.shared.currentScenicId = 11
|
||||
AppStore.shared.session.currentScenicId = 11
|
||||
let tabBarController = MainTabBarController()
|
||||
tabBarController.loadViewIfNeeded()
|
||||
guard
|
||||
@ -292,6 +311,8 @@ private final class FakeProfileSpaceSettingsAPI: ProfileSpaceSettingsAPI {
|
||||
var lastUpdateRequest: UpdatePhotographerProfileInfoRequest?
|
||||
var lastAddScheduleRequest: AddPhotographerScheduleRequest?
|
||||
var lastDeletedScheduleId: Int?
|
||||
var uploadedAvatarData: Data?
|
||||
var uploadedAvatarFileName: String?
|
||||
|
||||
init(response: PhotographerProfileInfoResponse) {
|
||||
self.response = response
|
||||
@ -335,10 +356,40 @@ private final class FakeProfileSpaceSettingsAPI: ProfileSpaceSettingsAPI {
|
||||
func deleteSchedule(id: Int) async throws {
|
||||
lastDeletedScheduleId = id
|
||||
}
|
||||
|
||||
func uploadAvatar(data: Data, fileName: String) async throws {
|
||||
uploadedAvatarData = data
|
||||
uploadedAvatarFileName = fileName
|
||||
response = PhotographerProfileInfoResponse(
|
||||
id: response.id,
|
||||
scenicId: response.scenicId,
|
||||
photogUid: response.photogUid,
|
||||
realName: response.realName,
|
||||
nickname: response.nickname,
|
||||
avatar: "https://example.com/new-avatar.jpg",
|
||||
scenicCertification: response.scenicCertification,
|
||||
description: response.description,
|
||||
attrLabel: response.attrLabel,
|
||||
shootLabel: response.shootLabel,
|
||||
cameraDevice: response.cameraDevice,
|
||||
businessStartTime: response.businessStartTime,
|
||||
businessEndTime: response.businessEndTime,
|
||||
holidayStartTime: response.holidayStartTime,
|
||||
holidayEndTime: response.holidayEndTime,
|
||||
businessRange: response.businessRange,
|
||||
acceptOrderStatus: response.acceptOrderStatus,
|
||||
schedule: response.schedule,
|
||||
virtualPhone: response.virtualPhone
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private extension PhotographerProfileInfoResponse {
|
||||
static func loaded() -> PhotographerProfileInfoResponse {
|
||||
loaded(certifications: ["西湖"])
|
||||
}
|
||||
|
||||
static func loaded(certifications: [String]) -> PhotographerProfileInfoResponse {
|
||||
PhotographerProfileInfoResponse(
|
||||
id: 1,
|
||||
scenicId: 11,
|
||||
@ -346,7 +397,7 @@ private extension PhotographerProfileInfoResponse {
|
||||
realName: "林摄影",
|
||||
nickname: "小林",
|
||||
avatar: "https://example.com/a.jpg",
|
||||
scenicCertification: ["西湖"],
|
||||
scenicCertification: certifications,
|
||||
description: "简介",
|
||||
attrLabel: ["航拍"],
|
||||
shootLabel: ["亲子"],
|
||||
|
||||
Reference in New Issue
Block a user