feat: 接通图片预览 AI 修图流程
This commit is contained in:
@@ -6,28 +6,30 @@
|
||||
import XCTest
|
||||
@testable import suixinkan
|
||||
|
||||
/// AI 修图模板选择状态测试。
|
||||
/// AI 修图模板工作流、选择状态与请求分流测试。
|
||||
@MainActor
|
||||
final class TravelAlbumAIRetouchTemplateViewModelTests: XCTestCase {
|
||||
func testLoadDefaultsRequiredSelectionsAndLeavesAtmosphereEmpty() async {
|
||||
func testInitialWorkflowDefaultsRequiredSelectionsAndLeavesAtmosphereEmpty() async {
|
||||
let api = makeAPI()
|
||||
let viewModel = TravelAlbumAIRetouchTemplateViewModel(
|
||||
albumId: 8,
|
||||
scenicId: 18,
|
||||
materialIds: [4, 3, 2, 1]
|
||||
materialIds: [4, 3, 2, 1, 1]
|
||||
)
|
||||
|
||||
await viewModel.loadTemplates(api: api)
|
||||
|
||||
XCTAssertEqual(api.aiRetouchTemplateScenicIds, [18])
|
||||
XCTAssertEqual(viewModel.workflow, .initial(albumId: 8, materialIds: [1, 2, 3, 4]))
|
||||
XCTAssertEqual(viewModel.visibleCategories, [.refined, .atmosphere, .cover])
|
||||
XCTAssertEqual(viewModel.selectedRefinedTemplateId, 11)
|
||||
XCTAssertNil(viewModel.selectedAtmosphereTemplateId)
|
||||
XCTAssertEqual(viewModel.selectedCoverTemplateId, 31)
|
||||
XCTAssertTrue(viewModel.showsCoverTemplates)
|
||||
XCTAssertTrue(viewModel.isOptional(.atmosphere))
|
||||
XCTAssertTrue(viewModel.canSubmit)
|
||||
}
|
||||
|
||||
func testAtmosphereSelectionTogglesOffWhenTappedAgain() async {
|
||||
func testInitialAtmosphereSelectionTogglesOffWhenTappedAgain() async {
|
||||
let api = makeAPI()
|
||||
let viewModel = TravelAlbumAIRetouchTemplateViewModel(
|
||||
albumId: 8,
|
||||
@@ -43,70 +45,141 @@ final class TravelAlbumAIRetouchTemplateViewModelTests: XCTestCase {
|
||||
XCTAssertNil(viewModel.selectedAtmosphereTemplateId)
|
||||
}
|
||||
|
||||
func testThreeMaterialsHideCoverAndOmitCoverFromSubmission() async {
|
||||
func testInitialSubmissionUsesCoverTemplateOnlyForFourOrMoreMaterials() async {
|
||||
let api = makeAPI()
|
||||
let viewModel = TravelAlbumAIRetouchTemplateViewModel(
|
||||
let threePhotoViewModel = TravelAlbumAIRetouchTemplateViewModel(
|
||||
albumId: 8,
|
||||
scenicId: 18,
|
||||
materialIds: [3, 1, 2]
|
||||
)
|
||||
var submitted = false
|
||||
viewModel.onSubmitted = { submitted = true }
|
||||
await viewModel.loadTemplates(api: api)
|
||||
await threePhotoViewModel.loadTemplates(api: api)
|
||||
await threePhotoViewModel.submit(api: api)
|
||||
|
||||
XCTAssertFalse(viewModel.showsCoverTemplates)
|
||||
XCTAssertNil(viewModel.selectedCoverTemplateId)
|
||||
XCTAssertEqual(
|
||||
api.aiRetouchRequests[0],
|
||||
TravelAlbumAIRetouchRequest(
|
||||
userEquityTravelId: 8,
|
||||
materialIds: [1, 2, 3],
|
||||
refinedTemplateId: 11,
|
||||
atmosphereTemplateId: nil,
|
||||
coverTemplateId: nil
|
||||
)
|
||||
)
|
||||
|
||||
await viewModel.submit(api: api)
|
||||
|
||||
XCTAssertTrue(submitted)
|
||||
XCTAssertEqual(api.aiRetouchRequests.count, 1)
|
||||
XCTAssertEqual(api.aiRetouchRequests.first?.materialIds, [1, 2, 3])
|
||||
XCTAssertNil(api.aiRetouchRequests.first?.aiPreviewTabs)
|
||||
}
|
||||
|
||||
func testFourMaterialsSubmitAllSelectedTemplateIds() async {
|
||||
let api = makeAPI()
|
||||
let viewModel = TravelAlbumAIRetouchTemplateViewModel(
|
||||
let fourPhotoViewModel = TravelAlbumAIRetouchTemplateViewModel(
|
||||
albumId: 8,
|
||||
scenicId: 18,
|
||||
materialIds: [1, 2, 3, 4]
|
||||
)
|
||||
await viewModel.loadTemplates(api: api)
|
||||
viewModel.toggleTemplate(id: 12, category: .refined)
|
||||
viewModel.toggleTemplate(id: 21, category: .atmosphere)
|
||||
|
||||
await viewModel.submit(api: api)
|
||||
await fourPhotoViewModel.loadTemplates(api: api)
|
||||
fourPhotoViewModel.toggleTemplate(id: 12, category: .refined)
|
||||
fourPhotoViewModel.toggleTemplate(id: 21, category: .atmosphere)
|
||||
await fourPhotoViewModel.submit(api: api)
|
||||
|
||||
XCTAssertEqual(
|
||||
api.aiRetouchRequests.first,
|
||||
api.aiRetouchRequests[1],
|
||||
TravelAlbumAIRetouchRequest(
|
||||
userEquityTravelId: 8,
|
||||
materialIds: [1, 2, 3, 4],
|
||||
refinedTemplateId: 12,
|
||||
atmosphereTemplateId: 21,
|
||||
aiPreviewTabs: 31
|
||||
coverTemplateId: 31
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func testMissingRequiredCoverTemplateDisablesSubmission() async {
|
||||
func testReretouchWorkflowShowsAndSubmitsOnlyRequiredCategories() async {
|
||||
let api = makeAPI()
|
||||
let refined = TravelAlbumAIRetouchTemplateViewModel(
|
||||
scenicId: 18,
|
||||
workflow: .reretouch(materialId: 7, batchId: 70, type: .refined)
|
||||
)
|
||||
await refined.loadTemplates(api: api)
|
||||
XCTAssertEqual(refined.visibleCategories, [.refined])
|
||||
XCTAssertEqual(refined.selectedRefinedTemplateId, 11)
|
||||
XCTAssertNil(refined.selectedAtmosphereTemplateId)
|
||||
await refined.submit(api: api)
|
||||
|
||||
let atmosphere = TravelAlbumAIRetouchTemplateViewModel(
|
||||
scenicId: 18,
|
||||
workflow: .reretouch(materialId: 8, batchId: 80, type: .atmosphere)
|
||||
)
|
||||
await atmosphere.loadTemplates(api: api)
|
||||
XCTAssertEqual(atmosphere.visibleCategories, [.atmosphere])
|
||||
XCTAssertEqual(atmosphere.selectedAtmosphereTemplateId, 21)
|
||||
atmosphere.toggleTemplate(id: 21, category: .atmosphere)
|
||||
XCTAssertEqual(atmosphere.selectedAtmosphereTemplateId, 21)
|
||||
await atmosphere.submit(api: api)
|
||||
|
||||
let all = TravelAlbumAIRetouchTemplateViewModel(
|
||||
scenicId: 18,
|
||||
workflow: .reretouch(materialId: 9, batchId: 90, type: .all)
|
||||
)
|
||||
await all.loadTemplates(api: api)
|
||||
XCTAssertEqual(all.visibleCategories, [.refined, .atmosphere])
|
||||
XCTAssertEqual(all.selectedRefinedTemplateId, 11)
|
||||
XCTAssertEqual(all.selectedAtmosphereTemplateId, 21)
|
||||
await all.submit(api: api)
|
||||
|
||||
XCTAssertEqual(api.aiReretouchRequests, [
|
||||
TravelAlbumAIReretouchRequest(
|
||||
id: 7,
|
||||
aiRetouchBatchId: 70,
|
||||
type: .refined,
|
||||
refinedTemplateId: 11,
|
||||
atmosphereTemplateId: nil
|
||||
),
|
||||
TravelAlbumAIReretouchRequest(
|
||||
id: 8,
|
||||
aiRetouchBatchId: 80,
|
||||
type: .atmosphere,
|
||||
refinedTemplateId: nil,
|
||||
atmosphereTemplateId: 21
|
||||
),
|
||||
TravelAlbumAIReretouchRequest(
|
||||
id: 9,
|
||||
aiRetouchBatchId: 90,
|
||||
type: .all,
|
||||
refinedTemplateId: 11,
|
||||
atmosphereTemplateId: 21
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
func testInvalidReretouchBatchDisablesSubmission() async {
|
||||
let api = makeAPI()
|
||||
let viewModel = TravelAlbumAIRetouchTemplateViewModel(
|
||||
scenicId: 18,
|
||||
workflow: .reretouch(materialId: 7, batchId: 0, type: .refined)
|
||||
)
|
||||
var message: String?
|
||||
viewModel.onShowMessage = { message = $0 }
|
||||
|
||||
await viewModel.loadTemplates(api: api)
|
||||
await viewModel.submit(api: api)
|
||||
|
||||
XCTAssertFalse(viewModel.canSubmit)
|
||||
XCTAssertEqual(viewModel.validationMessage, "当前图片缺少修图批次,请刷新后重试")
|
||||
XCTAssertEqual(message, "当前图片缺少修图批次,请刷新后重试")
|
||||
XCTAssertTrue(api.aiReretouchRequests.isEmpty)
|
||||
}
|
||||
|
||||
func testMissingRequiredTemplateDisablesMatchingWorkflow() async {
|
||||
let api = makeAPI()
|
||||
api.aiRetouchTemplatesResponse = TravelAlbumAIRetouchTemplatesResponse(
|
||||
refinedTemplates: [template(11, "清透")],
|
||||
atmosphereTemplates: [template(21, "暖阳")],
|
||||
atmosphereTemplates: [],
|
||||
coverTemplates: []
|
||||
)
|
||||
let viewModel = TravelAlbumAIRetouchTemplateViewModel(
|
||||
albumId: 8,
|
||||
scenicId: 18,
|
||||
materialIds: [1, 2, 3, 4]
|
||||
workflow: .reretouch(materialId: 7, batchId: 70, type: .all)
|
||||
)
|
||||
|
||||
await viewModel.loadTemplates(api: api)
|
||||
|
||||
XCTAssertFalse(viewModel.canSubmit)
|
||||
XCTAssertEqual(viewModel.validationMessage, "暂无可用的封面风格模板")
|
||||
XCTAssertEqual(viewModel.validationMessage, "暂无可用的氛围感修图模板")
|
||||
}
|
||||
|
||||
func testLoadFailureExposesRetryMessageAndKeepsSubmissionDisabled() async {
|
||||
@@ -127,11 +200,10 @@ final class TravelAlbumAIRetouchTemplateViewModelTests: XCTestCase {
|
||||
|
||||
func testSubmitFailureKeepsSelectionAndAllowsRetry() async {
|
||||
let api = makeAPI()
|
||||
api.submitAIRetouchError = APIError.serverCode(500, "提交服务繁忙")
|
||||
api.submitAIReretouchError = APIError.serverCode(500, "提交服务繁忙")
|
||||
let viewModel = TravelAlbumAIRetouchTemplateViewModel(
|
||||
albumId: 8,
|
||||
scenicId: 18,
|
||||
materialIds: [1]
|
||||
workflow: .reretouch(materialId: 7, batchId: 70, type: .refined)
|
||||
)
|
||||
var message: String?
|
||||
var submitted = false
|
||||
@@ -141,7 +213,7 @@ final class TravelAlbumAIRetouchTemplateViewModelTests: XCTestCase {
|
||||
|
||||
await viewModel.submit(api: api)
|
||||
|
||||
XCTAssertEqual(api.aiRetouchRequests.count, 1)
|
||||
XCTAssertEqual(api.aiReretouchRequests.count, 1)
|
||||
XCTAssertEqual(message, "提交服务繁忙")
|
||||
XCTAssertFalse(submitted)
|
||||
XCTAssertFalse(viewModel.isSubmitting)
|
||||
|
||||
Reference in New Issue
Block a user