// // TaskAddViewModelTests.swift // suixinkanTests // import XCTest @testable import suixinkan /// 添加任务 ViewModel 测试。 @MainActor final class TaskAddViewModelTests: XCTestCase { private var appStore: AppStore! private var defaults: UserDefaults! override func setUp() { defaults = UserDefaults(suiteName: "TaskAddViewModelTests")! defaults.removePersistentDomain(forName: "TaskAddViewModelTests") appStore = AppStore(defaults: defaults) appStore.session.configureTestAccount(scenicId: 10) } func testValidateRequiresTaskName() async { let viewModel = TaskAddViewModel(appStore: appStore) var message: String? viewModel.onShowMessage = { message = $0 } await viewModel.validateAndSubmit(api: TaskAPI(client: makeClient())) XCTAssertEqual(message, "请填写任务名称!") } func testTextInputsAreTrimmedToAndroidLimits() { let viewModel = TaskAddViewModel(appStore: appStore) viewModel.updateTaskName(String(repeating: "任", count: 25)) viewModel.updateTaskDetails(String(repeating: "描", count: 210)) viewModel.addCloudFiles([CloudFile(id: 1, name: "A", type: 2)]) viewModel.presentRemarkDialog(for: viewModel.selectedFiles[0].id) viewModel.updateRemarkDialogText(String(repeating: "注", count: 60)) XCTAssertEqual(viewModel.taskName.count, 20) XCTAssertEqual(viewModel.taskDetails.count, 200) XCTAssertEqual(viewModel.remarkDialogText.count, 50) } func testUploadingStateBlocksSubmitButKeepsButtonActionUseful() async { let viewModel = TaskAddViewModel(appStore: appStore) viewModel.updateTaskName("任务") viewModel.addUploadPlaceholder(fileName: "a.jpg", fileType: 2, uploadTaskId: "task1") var message: String? viewModel.onShowMessage = { message = $0 } await viewModel.validateAndSubmit(api: TaskAPI(client: makeClient())) XCTAssertEqual(message, "文件正在上传中,请稍后添加!") } func testClearingCustomUrgentHourKeepsConfirmedValue() { let viewModel = TaskAddViewModel(appStore: appStore) viewModel.setUrgentHour(6) viewModel.updateCustomUrgentHourText("") XCTAssertEqual(viewModel.urgentHour, 6) viewModel.updateCustomUrgentHourText("0") XCTAssertEqual(viewModel.urgentHour, 6) } func testBuildAddTaskRequestSplitsCloudAndUploadFiles() { let viewModel = TaskAddViewModel(appStore: appStore) viewModel.updateTaskName("任务A") viewModel.updateTaskDetails("描述") viewModel.setSelectedOrderNumber("NO001") viewModel.setUrgentHour(2) viewModel.addCloudFiles([CloudFile(id: 1, name: "云文件", type: 2)]) viewModel.addUploadPlaceholder(fileName: "a.jpg", fileType: 2, uploadTaskId: "task1") viewModel.markUploadSucceeded(uploadTaskId: "task1", fileURL: "https://cdn/a.jpg", autoRemarkWhenSingle: false) let request = viewModel.buildAddTaskRequest() XCTAssertEqual(request.name, "任务A") XCTAssertEqual(request.orderNumber, "NO001") XCTAssertEqual(request.urgentHour, 2) XCTAssertEqual(request.cloudFile.count, 1) XCTAssertEqual(request.uploadFile.count, 1) XCTAssertEqual(request.uploadFile[0].fileUrl, "https://cdn/a.jpg") } func testPreparedUploadKeepsOriginalFileNameAndPreview() { let viewModel = TaskAddViewModel(appStore: appStore) let previewData = Data([1, 2, 3]) viewModel.addUploadPlaceholder(fileName: "temporary.jpg", fileType: 2, uploadTaskId: "task1") viewModel.prepareUpload(uploadTaskId: "task1", fileName: "IMG_1001.HEIC", localPreviewData: previewData) viewModel.markUploadSucceeded( uploadTaskId: "task1", fileURL: "https://cdn/image.heic", fileName: "IMG_1001.HEIC", localPreviewData: previewData, autoRemarkWhenSingle: true ) XCTAssertEqual(viewModel.selectedFiles[0].uploadFileName, "IMG_1001.HEIC") XCTAssertEqual(viewModel.selectedFiles[0].localPreviewData, previewData) XCTAssertEqual(viewModel.buildAddTaskRequest().uploadFile[0].fileName, "IMG_1001.HEIC") XCTAssertEqual(viewModel.consumePendingRemarkFileID(), viewModel.selectedFiles[0].id) } func testTaskFileSourceBadgeTextMatchesAndroidLabels() { let cloudItem = TaskFileItem(source: .cloud, cloudFile: CloudFile(id: 1, name: "云文件", type: 2)) let uploadItem = TaskFileItem(source: .upload, uploadFileName: "a.jpg", uploadFileType: 2) XCTAssertEqual(cloudItem.sourceBadgeText, "云盘") XCTAssertEqual(uploadItem.sourceBadgeText, "本地") } func testAddCloudFilesDedupesAndAutoRemarkSingleFile() { let viewModel = TaskAddViewModel(appStore: appStore) viewModel.addCloudFiles([CloudFile(id: 1, name: "A", type: 2)]) XCTAssertEqual(viewModel.selectedFiles.count, 1) XCTAssertEqual(viewModel.consumePendingRemarkFileID(), viewModel.selectedFiles[0].id) viewModel.addCloudFiles([ CloudFile(id: 1, name: "A", type: 2), CloudFile(id: 2, name: "B", type: 2), CloudFile(id: 3, name: "C", type: 2), ]) XCTAssertEqual(viewModel.selectedFiles.count, 3) XCTAssertNil(viewModel.consumePendingRemarkFileID()) } func testResetAllStateClearsForm() { let viewModel = TaskAddViewModel(appStore: appStore) viewModel.updateTaskName("任务") viewModel.setSelectedOrderNumber("NO001") viewModel.addCloudFiles([CloudFile(id: 1, type: 2)]) viewModel.resetAllState() XCTAssertEqual(viewModel.taskName, "") XCTAssertEqual(viewModel.taskDetails, "") XCTAssertNil(viewModel.selectedOrderNumber) XCTAssertEqual(viewModel.urgentHour, 0) XCTAssertTrue(viewModel.selectedFiles.isEmpty) XCTAssertFalse(viewModel.isUploading) XCTAssertFalse(viewModel.showSuccessDialog) } private func makeClient() -> APIClient { APIClient(environment: .testing, session: MockURLSession(responses: [])) } }