修复有线传图新拍照片不显示,并优化旅拍相册列表交互。
未绑定相册的新拍照片被错误过滤导致列表始终为空;同时调整入口卡片点击区域与详情素材网格布局。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -0,0 +1,156 @@
|
||||
//
|
||||
// WiredCameraTransferViewModelTests.swift
|
||||
// suixinkanTests
|
||||
//
|
||||
// Created by Codex on 2026/6/30.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import suixinkan
|
||||
|
||||
@MainActor
|
||||
/// 有线传图 ViewModel 测试。
|
||||
final class WiredCameraTransferViewModelTests: XCTestCase {
|
||||
/// 测试相机管道回调的首张新照片应出现在列表中。
|
||||
func testFirstNewPipelineTaskAppearsInPhotos() async throws {
|
||||
let camera = MockWiredCameraService()
|
||||
let context = WiredTransferContext(
|
||||
albumId: 1,
|
||||
albumName: "测试相册",
|
||||
phone: "",
|
||||
orderNumber: ""
|
||||
)
|
||||
let viewModel = WiredCameraTransferViewModel(context: context, cameraService: camera)
|
||||
let account = AccountContext()
|
||||
account.applyLogin(profile: AccountProfile(userId: "101", displayName: "测试"))
|
||||
|
||||
await viewModel.start(
|
||||
api: MockTravelAlbumAPIForWiredTransfer(),
|
||||
ossService: WiredTransferMockOSSUploadService(),
|
||||
scenicID: 1,
|
||||
accountContext: account
|
||||
)
|
||||
|
||||
let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("DSC_NEW.JPG")
|
||||
try Data(repeating: 0xFF, count: 16).write(to: tempURL)
|
||||
defer { try? FileManager.default.removeItem(at: tempURL) }
|
||||
|
||||
let asset = CameraAsset(id: "ptp_new_001", filename: "DSC_NEW.JPG", fileSize: 16)
|
||||
camera.downloadURL = tempURL
|
||||
camera.onNewAsset?(asset)
|
||||
try await Task.sleep(nanoseconds: 500_000_000)
|
||||
|
||||
XCTAssertEqual(viewModel.photos.count, 1)
|
||||
XCTAssertEqual(viewModel.photos.first?.id, "ptp_new_001")
|
||||
XCTAssertEqual(viewModel.photos.first?.fileName, "DSC_NEW.JPG")
|
||||
}
|
||||
|
||||
/// 测试绑定到其他相册的照片不会出现在当前相册列表。
|
||||
func testPhotoBoundToOtherAlbumIsFilteredOut() async throws {
|
||||
let bindingStore = WiredTransferPhotoStore(
|
||||
accountPrefixProvider: { "user_101" },
|
||||
userIDProvider: { "101" }
|
||||
)
|
||||
bindingStore.bindPhotoToAlbum(photoID: "ptp_other_album", albumID: 99)
|
||||
defer { bindingStore.bindPhotoToAlbum(photoID: "ptp_other_album", albumID: 1) }
|
||||
|
||||
let camera = MockWiredCameraService()
|
||||
let context = WiredTransferContext(
|
||||
albumId: 1,
|
||||
albumName: "当前相册",
|
||||
phone: "",
|
||||
orderNumber: ""
|
||||
)
|
||||
let viewModel = WiredCameraTransferViewModel(context: context, cameraService: camera)
|
||||
let account = AccountContext()
|
||||
account.applyLogin(profile: AccountProfile(userId: "101", displayName: "测试"), accountType: "user")
|
||||
|
||||
await viewModel.start(
|
||||
api: MockTravelAlbumAPIForWiredTransfer(),
|
||||
ossService: WiredTransferMockOSSUploadService(),
|
||||
scenicID: 1,
|
||||
accountContext: account
|
||||
)
|
||||
|
||||
let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("DSC_OTHER.JPG")
|
||||
try Data(repeating: 0xAA, count: 8).write(to: tempURL)
|
||||
defer { try? FileManager.default.removeItem(at: tempURL) }
|
||||
|
||||
camera.downloadURL = tempURL
|
||||
camera.onNewAsset?(CameraAsset(id: "ptp_other_album", filename: "DSC_OTHER.JPG", fileSize: 8))
|
||||
try await Task.sleep(nanoseconds: 500_000_000)
|
||||
|
||||
XCTAssertTrue(viewModel.photos.isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private final class MockWiredCameraService: CameraServiceProtocol {
|
||||
let brand: CameraBrand = .sony
|
||||
var connectionState: CameraConnectionState = .connected(deviceName: "Mock")
|
||||
var onConnectionStateChange: ((CameraConnectionState) -> Void)?
|
||||
var onNewAsset: ((CameraAsset) -> Void)?
|
||||
var downloadURL: URL?
|
||||
|
||||
func connect() async {}
|
||||
func disconnect() async {}
|
||||
|
||||
func listAssets() async throws -> [CameraAsset] {
|
||||
[]
|
||||
}
|
||||
|
||||
func downloadAsset(_ asset: CameraAsset) async throws -> URL {
|
||||
if let downloadURL {
|
||||
return downloadURL
|
||||
}
|
||||
let url = FileManager.default.temporaryDirectory.appendingPathComponent(asset.filename)
|
||||
if !FileManager.default.fileExists(atPath: url.path) {
|
||||
try Data(repeating: 0xFF, count: 8).write(to: url)
|
||||
}
|
||||
return url
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private final class MockTravelAlbumAPIForWiredTransfer: TravelAlbumServing {
|
||||
func availableOrders() async throws -> [TravelAlbumAvailableOrder] { [] }
|
||||
func createAlbum(_ request: TravelAlbumCreateRequest) async throws -> TravelAlbumCreateResponse {
|
||||
TravelAlbumCreateResponse(id: 1)
|
||||
}
|
||||
func albumList(page: Int, pageSize: Int) async throws -> ListPayload<TravelAlbumItem> {
|
||||
ListPayload(total: 0, list: [])
|
||||
}
|
||||
func albumInfo(id: Int) async throws -> TravelAlbumItem { throw APIError.emptyData }
|
||||
func editAlbum(_ request: TravelAlbumEditRequest) async throws {}
|
||||
func deleteAlbum(_ request: TravelAlbumDeleteRequest) async throws {}
|
||||
func materialList(userEquityTravelId: Int, page: Int, pageSize: Int) async throws -> ListPayload<TravelAlbumMaterial> {
|
||||
ListPayload(total: 0, list: [])
|
||||
}
|
||||
func uploadMaterial(_ request: TravelAlbumUploadMaterialRequest) async throws -> TravelAlbumMaterial {
|
||||
let data = Data(
|
||||
#"{"code":100000,"msg":"success","data":{"id":"11","user_equity_travel_id":"8","status":"1","order_number":"","user_id":"100","file_name":"DSC_001.JPG","file_type":"2","file_url":"https://cdn/a.jpg","file_size":"2048","cover_url":"","is_purchased":"0","created_at":"2026","updated_at":"2026"}}"#.utf8
|
||||
)
|
||||
let envelope = try JSONDecoder().decode(APIEnvelope<TravelAlbumMaterial>.self, from: data)
|
||||
return try XCTUnwrap(envelope.data)
|
||||
}
|
||||
func deleteMaterial(_ request: TravelAlbumDeleteMaterialRequest) async throws {}
|
||||
func mpCode(id: Int) async throws -> TravelAlbumMpCodeResponse {
|
||||
TravelAlbumMpCodeResponse(mpCodeOssURL: "")
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private final class WiredTransferMockOSSUploadService: OSSUploadServing {
|
||||
func uploadUserAvatar(data: Data, fileName: String, scenicId: Int, onProgress: @escaping (Int) -> Void) async throws -> String { "" }
|
||||
func uploadRealNameImage(data: Data, fileName: String, scenicId: Int, onProgress: @escaping (Int) -> Void) async throws -> String { "" }
|
||||
func uploadCloudFile(data: Data, fileName: String, fileType: Int, scenicId: Int, onProgress: @escaping (Int) -> Void) async throws -> String { "" }
|
||||
func uploadAlbumFile(data: Data, fileName: String, fileType: Int, scenicId: Int, onProgress: @escaping (Int) -> Void) async throws -> String {
|
||||
onProgress(100)
|
||||
return "https://cdn/mock/\(fileName)"
|
||||
}
|
||||
func uploadTaskFile(data: Data, fileName: String, fileType: Int, scenicId: Int, onProgress: @escaping (Int) -> Void) async throws -> String { "" }
|
||||
func uploadProjectImage(data: Data, fileName: String, scenicId: Int, onProgress: @escaping (Int) -> Void) async throws -> String { "" }
|
||||
func uploadPunchPointImage(data: Data, fileName: String, scenicId: Int, onProgress: @escaping (Int) -> Void) async throws -> String { "" }
|
||||
func uploadScenicApplicationImage(data: Data, fileName: String, scenicId: Int, onProgress: @escaping (Int) -> Void) async throws -> String { "" }
|
||||
func uploadBankCardImage(data: Data, fileName: String, scenicId: Int, onProgress: @escaping (Int) -> Void) async throws -> String { "" }
|
||||
}
|
||||
Reference in New Issue
Block a user