feat: 品牌化那拉提景区收款页
This commit is contained in:
@ -79,6 +79,24 @@ final class PaymentCollectionDetailsViewModelTests: XCTestCase {
|
||||
XCTAssertTrue(viewModel.isVoiceBroadcastOpen)
|
||||
}
|
||||
|
||||
func testNalatiAccountInfoUsesLoggedInPhotographerAndAllPermissionStores() {
|
||||
appStore.session.userName = "摄影师小王"
|
||||
appStore.permissions.saveRolePermissionList([
|
||||
RolePermissionResponse(
|
||||
role: RoleInfo(id: 41, name: "摄影师", roleCode: "photographer"),
|
||||
store: [
|
||||
RolePermissionStoreInfo(id: 1, name: "空中草原店"),
|
||||
RolePermissionStoreInfo(id: 2, name: "河谷草原店"),
|
||||
]
|
||||
),
|
||||
])
|
||||
|
||||
let viewModel = PaymentCollectionDetailsViewModel(appStore: appStore)
|
||||
|
||||
XCTAssertEqual(viewModel.displayPhotographerName, "摄影师小王")
|
||||
XCTAssertEqual(viewModel.displayStoreNames, "空中草原店、河谷草原店")
|
||||
}
|
||||
|
||||
func testLoadPayCodeClearsQRWhenScenicMissing() async {
|
||||
appStore.session.currentScenicId = 0
|
||||
let session = MockURLSession(responses: [])
|
||||
@ -90,4 +108,74 @@ final class PaymentCollectionDetailsViewModelTests: XCTestCase {
|
||||
XCTAssertFalse(viewModel.hasPayCode)
|
||||
XCTAssertEqual(session.requests.count, 0)
|
||||
}
|
||||
|
||||
func testNalatiLoadsCachedConfigBeforeRefreshingRemoteConfig() async throws {
|
||||
let defaults = UserDefaults(suiteName: "PaymentCollectionDetailsViewModelTests.Config")!
|
||||
defaults.removePersistentDomain(forName: "PaymentCollectionDetailsViewModelTests.Config")
|
||||
let cache = PayPageConfigCache(defaults: defaults, environment: .testing)
|
||||
let cached = PayPageConfig(
|
||||
background: "https://cache.example.com/background.png",
|
||||
logo: "https://cache.example.com/logo.png",
|
||||
title: "缓存标题",
|
||||
subtitle: "缓存副标题"
|
||||
)
|
||||
cache.save(cached, scenicId: 128)
|
||||
appStore.session.currentScenicId = 128
|
||||
appStore.session.currentScenicName = "那拉提景区"
|
||||
|
||||
let responseJSON = """
|
||||
{"code":100000,"msg":"success","data":{
|
||||
"background":"https://new.example.com/background.png",
|
||||
"logo":"",
|
||||
"title":"最新标题",
|
||||
"subtitle":""
|
||||
}}
|
||||
""".data(using: .utf8)!
|
||||
let session = MockURLSession(responses: [responseJSON])
|
||||
let api = PaymentAPI(client: APIClient(environment: .testing, session: session))
|
||||
let viewModel = PaymentCollectionDetailsViewModel(
|
||||
appStore: appStore,
|
||||
payPageConfigCache: cache
|
||||
)
|
||||
|
||||
XCTAssertEqual(viewModel.payPageConfig, cached)
|
||||
await viewModel.loadPayPageConfig(api: api)
|
||||
|
||||
XCTAssertEqual(viewModel.payPageConfig.background, "https://new.example.com/background.png")
|
||||
XCTAssertEqual(viewModel.payPageConfig.logo, cached.logo)
|
||||
XCTAssertEqual(viewModel.payPageConfig.title, "最新标题")
|
||||
XCTAssertEqual(viewModel.payPageConfig.subtitle, cached.subtitle)
|
||||
XCTAssertEqual(viewModel.brandingRefreshVersion, 1)
|
||||
XCTAssertEqual(session.requests.first?.url?.path, "/api/yf-handset-app/photog/pay-page-config")
|
||||
XCTAssertEqual(
|
||||
URLComponents(url: try XCTUnwrap(session.requests.first?.url), resolvingAgainstBaseURL: false)?
|
||||
.queryItems?
|
||||
.first(where: { $0.name == "scenic_id" })?
|
||||
.value,
|
||||
"128"
|
||||
)
|
||||
}
|
||||
|
||||
func testNonNalatiDoesNotRequestPayPageConfig() async {
|
||||
let session = MockURLSession(responses: [])
|
||||
let api = PaymentAPI(client: APIClient(environment: .testing, session: session))
|
||||
let viewModel = PaymentCollectionDetailsViewModel(appStore: appStore)
|
||||
|
||||
await viewModel.loadPayPageConfig(api: api)
|
||||
|
||||
XCTAssertFalse(viewModel.usesNalatiBranding)
|
||||
XCTAssertEqual(session.requests.count, 0)
|
||||
}
|
||||
|
||||
func testPayPageConfigCacheIsEnvironmentScoped() {
|
||||
let defaults = UserDefaults(suiteName: "PaymentCollectionDetailsViewModelTests.Environment")!
|
||||
defaults.removePersistentDomain(forName: "PaymentCollectionDetailsViewModelTests.Environment")
|
||||
let production = PayPageConfigCache(defaults: defaults, environment: .production)
|
||||
let testing = PayPageConfigCache(defaults: defaults, environment: .testing)
|
||||
|
||||
production.save(.nalatiDefault, scenicId: 128)
|
||||
|
||||
XCTAssertEqual(production.config(scenicId: 128), .nalatiDefault)
|
||||
XCTAssertNil(testing.config(scenicId: 128))
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,88 @@ import XCTest
|
||||
|
||||
/// 收款模型解码测试。
|
||||
final class PaymentModelsTests: XCTestCase {
|
||||
func testPayPageConfigResponseDecodesBrandFields() throws {
|
||||
let json = """
|
||||
{
|
||||
"background": "https://example.com/background.png",
|
||||
"logo": "https://example.com/logo.png",
|
||||
"title": "那拉提旅拍管理收费平台",
|
||||
"subtitle": "那拉提景区"
|
||||
}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
let response = try JSONDecoder().decode(PayPageConfigResponse.self, from: json)
|
||||
|
||||
XCTAssertEqual(response.background, "https://example.com/background.png")
|
||||
XCTAssertEqual(response.logo, "https://example.com/logo.png")
|
||||
XCTAssertEqual(response.title, "那拉提旅拍管理收费平台")
|
||||
XCTAssertEqual(response.subtitle, "那拉提景区")
|
||||
}
|
||||
|
||||
func testPayPageConfigMergesOnlyUsableRemoteFields() {
|
||||
let cached = PayPageConfig(
|
||||
background: "https://cache.example.com/background.png",
|
||||
logo: "https://cache.example.com/logo.png",
|
||||
title: "缓存标题",
|
||||
subtitle: "缓存副标题"
|
||||
)
|
||||
|
||||
let merged = cached.merging(
|
||||
PayPageConfigResponse(
|
||||
background: "",
|
||||
logo: "http://unsafe.example.com/logo.png",
|
||||
title: " 最新标题 ",
|
||||
subtitle: ""
|
||||
)
|
||||
)
|
||||
|
||||
XCTAssertEqual(merged.background, cached.background)
|
||||
XCTAssertEqual(merged.logo, cached.logo)
|
||||
XCTAssertEqual(merged.title, "最新标题")
|
||||
XCTAssertEqual(merged.subtitle, cached.subtitle)
|
||||
}
|
||||
|
||||
func testPayPageBrandingOnlyEnablesNalatiScenicId() {
|
||||
XCTAssertTrue(PayPageBrandingPolicy.isNalati(scenicId: 128))
|
||||
XCTAssertFalse(PayPageBrandingPolicy.isNalati(scenicId: 127))
|
||||
XCTAssertFalse(PayPageBrandingPolicy.isNalati(scenicId: 0))
|
||||
}
|
||||
|
||||
func testPaymentAccountDisplayFormatterUsesCurrentPhotographerAndAllUniqueStores() {
|
||||
XCTAssertEqual(
|
||||
PaymentAccountDisplayFormatter.photographerName(userName: " 当前摄影师 ", realName: "实名"),
|
||||
"当前摄影师"
|
||||
)
|
||||
XCTAssertEqual(
|
||||
PaymentAccountDisplayFormatter.storeNames(["一号店", " 二号店 ", "一号店", ""]),
|
||||
"一号店、二号店"
|
||||
)
|
||||
XCTAssertEqual(PaymentAccountDisplayFormatter.storeNames([]), "-")
|
||||
}
|
||||
|
||||
func testRolePermissionResponseDecodesAllAccountStores() throws {
|
||||
let json = """
|
||||
{
|
||||
"role": {
|
||||
"id": 41,
|
||||
"name": "摄影师",
|
||||
"role_code": "photographer",
|
||||
"notes": null,
|
||||
"permission": []
|
||||
},
|
||||
"scenic": [],
|
||||
"store": [
|
||||
{"id": 37, "name": "随心旅拍", "status": 1},
|
||||
{"id": 38, "name": "空中草原店", "status": 1}
|
||||
]
|
||||
}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
let response = try JSONDecoder().decode(RolePermissionResponse.self, from: json)
|
||||
|
||||
XCTAssertEqual(response.store.map(\.name), ["随心旅拍", "空中草原店"])
|
||||
}
|
||||
|
||||
func testPayCodeResponseDecodesSnakeCaseFields() throws {
|
||||
let json = """
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user