添加提交任务流程并抽取资料编辑页

This commit is contained in:
2026-07-07 10:12:20 +08:00
parent af450fb4cc
commit ef1d3b4af5
23 changed files with 3186 additions and 170 deletions

View File

@ -0,0 +1,96 @@
//
// ProfileEditViewModelTests.swift
// suixinkanTests
//
import XCTest
@testable import suixinkan
@MainActor
/// ViewModel
final class ProfileEditViewModelTests: XCTestCase {
func testInitPrefillsNicknameWhenSet() {
let viewModel = ProfileEditViewModel(nickname: "测试昵称", avatarURL: "https://example.com/a.jpg")
XCTAssertEqual(viewModel.editingNickname, "测试昵称")
}
func testInitClearsPlaceholderNickname() {
let viewModel = ProfileEditViewModel(nickname: "未设置昵称", avatarURL: "")
XCTAssertEqual(viewModel.editingNickname, "")
}
func testSaveProfileRejectsEmptyNickname() async {
let viewModel = ProfileEditViewModel(nickname: "未设置昵称", avatarURL: "")
let session = MockURLSession(responses: [])
let api = ProfileAPI(client: APIClient(environment: .testing, session: session))
do {
try await viewModel.saveProfile(
api: api,
uploader: MockOSSUploadService(),
scenicId: 1
)
XCTFail("空昵称应抛出校验错误")
} catch {
guard let validationError = error as? ProfileValidationError else {
return XCTFail("期望 ProfileValidationError实际为 \(error)")
}
XCTAssertEqual(validationError, .emptyNickname)
}
}
func testSaveProfilePostsNotificationAndUpdatesAppStore() async throws {
let viewModel = ProfileEditViewModel(nickname: "旧昵称", avatarURL: "")
viewModel.updateEditingNickname("新昵称")
let expectation = expectation(forNotification: NotificationName.userProfileDidUpdate, object: nil)
let responseData = try TestJSON.envelope(data: EmptyPayload())
let session = MockURLSession(responses: [responseData])
let api = ProfileAPI(client: APIClient(environment: .testing, session: session))
try await viewModel.saveProfile(
api: api,
uploader: MockOSSUploadService(),
scenicId: 1
)
await fulfillment(of: [expectation], timeout: 1)
XCTAssertEqual(AppStore.shared.userName, "新昵称")
}
}
/// OSS URL
@MainActor
private struct MockOSSUploadService: OSSUploadServing {
func uploadUserAvatar(
data: Data,
fileName: String,
scenicId: Int,
onProgress: @escaping (Int) -> Void
) async throws -> String {
onProgress(100)
return "https://cdn.example.com/avatar.jpg"
}
func uploadRealNameImage(
data: Data,
fileName: String,
scenicId: Int,
onProgress: @escaping (Int) -> Void
) async throws -> String {
onProgress(100)
return "https://cdn.example.com/realname.jpg"
}
func uploadBankCardImage(
data: Data,
fileName: String,
scenicId: Int,
onProgress: @escaping (Int) -> Void
) async throws -> String {
onProgress(100)
return "https://cdn.example.com/bank.jpg"
}
}