模块化 AppStore 并完善素材管理与个人空间设置。
将会话、权限、位置、收款与排队存储拆分为独立模块,同步更新各 ViewModel 与单元测试,并补充素材管理 UI 与个人空间设置交互。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -26,14 +26,14 @@ final class AMapBootstrapTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testConfigureReturnsFalseWhenPrivacyNotAccepted() {
|
||||
appStore.privacyAgreementAccepted = false
|
||||
appStore.session.privacyAgreementAccepted = false
|
||||
|
||||
XCTAssertFalse(AMapBootstrap.configureIfNeeded(appStore: appStore))
|
||||
XCTAssertFalse(AMapBootstrap.isConfigured)
|
||||
}
|
||||
|
||||
func testConfigureIsIdempotentWhenPrivacyAccepted() {
|
||||
appStore.privacyAgreementAccepted = true
|
||||
appStore.session.privacyAgreementAccepted = true
|
||||
|
||||
XCTAssertTrue(AMapBootstrap.configureIfNeeded(appStore: appStore))
|
||||
XCTAssertTrue(AMapBootstrap.isConfigured)
|
||||
@ -42,7 +42,7 @@ final class AMapBootstrapTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testRequireConfiguredThrowsWhenPrivacyNotAccepted() {
|
||||
appStore.privacyAgreementAccepted = false
|
||||
appStore.session.privacyAgreementAccepted = false
|
||||
|
||||
XCTAssertThrowsError(try AMapBootstrap.requireConfigured(appStore: appStore)) { error in
|
||||
XCTAssertEqual(error as? LocationProviderError, .privacyNotAccepted)
|
||||
|
||||
@ -96,4 +96,59 @@ final class APIClientTests: XCTestCase {
|
||||
|
||||
await fulfillment(of: [expectation], timeout: 1)
|
||||
}
|
||||
|
||||
func testMultipartUploadBuildsAndroidCompatibleRequest() async throws {
|
||||
let session = MockURLSession(responses: [try TestJSON.envelope(data: EmptyPayload())])
|
||||
let client = APIClient(
|
||||
environment: .testing,
|
||||
session: session,
|
||||
appVersion: "9.0.0",
|
||||
osType: "iOS"
|
||||
)
|
||||
client.bindAuthTokenProvider { "avatar-token" }
|
||||
let bytes = Data([0x01, 0x02, 0xFE, 0xFF])
|
||||
|
||||
let _: EmptyPayload = try await client.sendMultipart(
|
||||
path: "/api/yf-handset-app/photog/userinfo-update-avatar",
|
||||
fileName: "avatar.jpg",
|
||||
mimeType: "image/jpeg",
|
||||
data: bytes
|
||||
)
|
||||
|
||||
let request = try XCTUnwrap(session.requests.first)
|
||||
XCTAssertEqual(request.httpMethod, "POST")
|
||||
XCTAssertEqual(request.timeoutInterval, 60)
|
||||
XCTAssertEqual(request.value(forHTTPHeaderField: "token"), "avatar-token")
|
||||
XCTAssertEqual(request.value(forHTTPHeaderField: "X-APP-VERSION"), "9.0.0")
|
||||
XCTAssertEqual(request.value(forHTTPHeaderField: "X-OS-TYPE"), "iOS")
|
||||
XCTAssertTrue(request.value(forHTTPHeaderField: "Content-Type")?.hasPrefix("multipart/form-data; boundary=") == true)
|
||||
let body = try XCTUnwrap(request.httpBody)
|
||||
let bodyText = String(decoding: body, as: UTF8.self)
|
||||
XCTAssertTrue(bodyText.contains("name=\"file\"; filename=\"avatar.jpg\""))
|
||||
XCTAssertTrue(bodyText.contains("Content-Type: image/jpeg"))
|
||||
XCTAssertNotNil(body.range(of: bytes))
|
||||
}
|
||||
|
||||
func testMultipartUploadMapsBusinessFailure() async {
|
||||
let session = MockURLSession(responses: [try! TestJSON.errorEnvelope(code: 100001, msg: "头像上传失败")])
|
||||
let client = APIClient(environment: .testing, session: session)
|
||||
|
||||
do {
|
||||
let _: EmptyPayload = try await client.sendMultipart(
|
||||
path: "/api/yf-handset-app/photog/userinfo-update-avatar",
|
||||
fileName: "avatar.jpg",
|
||||
mimeType: "image/jpeg",
|
||||
data: Data([1])
|
||||
)
|
||||
XCTFail("业务失败时应抛出 serverCode")
|
||||
} catch let error as APIError {
|
||||
guard case let .serverCode(code, message) = error else {
|
||||
return XCTFail("期望 serverCode,实际为 \(error)")
|
||||
}
|
||||
XCTAssertEqual(code, 100001)
|
||||
XCTAssertEqual(message, "头像上传失败")
|
||||
} catch {
|
||||
XCTFail("期望 APIError,实际为 \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,9 +19,9 @@ final class AllFunctionsViewModelTests: XCTestCase {
|
||||
defaults.removePersistentDomain(forName: "AllFunctionsViewModelTests")
|
||||
appStore = AppStore(defaults: defaults)
|
||||
menuStore = HomeCommonMenuStore(defaults: defaults)
|
||||
appStore.userId = "1001"
|
||||
appStore.accountType = "scenic_user"
|
||||
appStore.roleCode = AppRoleCode.photographer.rawValue
|
||||
appStore.session.userId = "1001"
|
||||
appStore.session.accountType = .scenicUser
|
||||
appStore.session.roleCode = AppRoleCode.photographer.rawValue
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
@ -30,7 +30,7 @@ final class AllFunctionsViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testLoadFunctionsUsesDefaultFirstFourWhenNoSavedURIs() {
|
||||
appStore.savePermissionItems([
|
||||
appStore.permissions.savePermissionItems([
|
||||
HomePermissionItem(id: "1", name: "钱包", uri: "wallet"),
|
||||
HomePermissionItem(id: "2", name: "云盘", uri: "cloud_management"),
|
||||
HomePermissionItem(id: "3", name: "任务", uri: "task_management"),
|
||||
@ -47,23 +47,23 @@ final class AllFunctionsViewModelTests: XCTestCase {
|
||||
XCTAssertEqual(viewModel.moreMenus.map(\.uri), ["message_center"])
|
||||
XCTAssertEqual(
|
||||
menuStore.savedCommonURIs(
|
||||
accountScope: appStore.accountCachePrefix,
|
||||
roleCode: appStore.roleCode
|
||||
accountScope: appStore.session.accountCachePrefix,
|
||||
roleCode: appStore.session.roleCode
|
||||
),
|
||||
["wallet", "cloud_management", "task_management", "system_settings"]
|
||||
)
|
||||
}
|
||||
|
||||
func testLoadFunctionsUsesSavedURIsOverDefault() {
|
||||
appStore.savePermissionItems([
|
||||
appStore.permissions.savePermissionItems([
|
||||
HomePermissionItem(id: "1", name: "钱包", uri: "wallet"),
|
||||
HomePermissionItem(id: "2", name: "云盘", uri: "cloud_management"),
|
||||
HomePermissionItem(id: "3", name: "任务", uri: "task_management"),
|
||||
])
|
||||
menuStore.saveCommonURIs(
|
||||
["cloud_management"],
|
||||
accountScope: appStore.accountCachePrefix,
|
||||
roleCode: appStore.roleCode
|
||||
accountScope: appStore.session.accountCachePrefix,
|
||||
roleCode: appStore.session.roleCode
|
||||
)
|
||||
let viewModel = AllFunctionsViewModel(appStore: appStore, commonMenuStore: menuStore)
|
||||
|
||||
@ -74,14 +74,14 @@ final class AllFunctionsViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testAddToCommonMovesMenuAndPersists() {
|
||||
appStore.savePermissionItems([
|
||||
appStore.permissions.savePermissionItems([
|
||||
HomePermissionItem(id: "1", name: "钱包", uri: "wallet"),
|
||||
HomePermissionItem(id: "2", name: "云盘", uri: "cloud_management"),
|
||||
])
|
||||
menuStore.saveCommonURIs(
|
||||
["wallet"],
|
||||
accountScope: appStore.accountCachePrefix,
|
||||
roleCode: appStore.roleCode
|
||||
accountScope: appStore.session.accountCachePrefix,
|
||||
roleCode: appStore.session.roleCode
|
||||
)
|
||||
let viewModel = AllFunctionsViewModel(appStore: appStore, commonMenuStore: menuStore)
|
||||
viewModel.loadFunctions()
|
||||
@ -94,22 +94,22 @@ final class AllFunctionsViewModelTests: XCTestCase {
|
||||
XCTAssertTrue(viewModel.didCustomize)
|
||||
XCTAssertEqual(
|
||||
menuStore.savedCommonURIs(
|
||||
accountScope: appStore.accountCachePrefix,
|
||||
roleCode: appStore.roleCode
|
||||
accountScope: appStore.session.accountCachePrefix,
|
||||
roleCode: appStore.session.roleCode
|
||||
),
|
||||
["wallet", "cloud_management"]
|
||||
)
|
||||
}
|
||||
|
||||
func testRemoveFromCommonMovesMenuAndPersists() {
|
||||
appStore.savePermissionItems([
|
||||
appStore.permissions.savePermissionItems([
|
||||
HomePermissionItem(id: "1", name: "钱包", uri: "wallet"),
|
||||
HomePermissionItem(id: "2", name: "云盘", uri: "cloud_management"),
|
||||
])
|
||||
menuStore.saveCommonURIs(
|
||||
["wallet", "cloud_management"],
|
||||
accountScope: appStore.accountCachePrefix,
|
||||
roleCode: appStore.roleCode
|
||||
accountScope: appStore.session.accountCachePrefix,
|
||||
roleCode: appStore.session.roleCode
|
||||
)
|
||||
let viewModel = AllFunctionsViewModel(appStore: appStore, commonMenuStore: menuStore)
|
||||
viewModel.loadFunctions()
|
||||
@ -123,14 +123,14 @@ final class AllFunctionsViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testPhotographerReportMenuVisibleAndCanBeAddedToCommon() {
|
||||
appStore.savePermissionItems([
|
||||
appStore.permissions.savePermissionItems([
|
||||
HomePermissionItem(id: "1", name: "位置上报", uri: "location_report"),
|
||||
HomePermissionItem(id: "2", name: "举报摄影师", uri: "report_photographer"),
|
||||
])
|
||||
menuStore.saveCommonURIs(
|
||||
["location_report"],
|
||||
accountScope: appStore.accountCachePrefix,
|
||||
roleCode: appStore.roleCode
|
||||
accountScope: appStore.session.accountCachePrefix,
|
||||
roleCode: appStore.session.roleCode
|
||||
)
|
||||
let viewModel = AllFunctionsViewModel(appStore: appStore, commonMenuStore: menuStore)
|
||||
|
||||
|
||||
134
suixinkanTests/AppStoreModuleTests.swift
Normal file
134
suixinkanTests/AppStoreModuleTests.swift
Normal file
@ -0,0 +1,134 @@
|
||||
//
|
||||
// AppStoreModuleTests.swift
|
||||
// suixinkanTests
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import suixinkan
|
||||
|
||||
/// 验证 AppStore 子模块的持久化、账号隔离与统一清理行为。
|
||||
final class AppStoreModuleTests: XCTestCase {
|
||||
|
||||
private var suiteName: String!
|
||||
private var defaults: UserDefaults!
|
||||
private var appStore: AppStore!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
suiteName = "AppStoreModuleTests.\(UUID().uuidString)"
|
||||
defaults = UserDefaults(suiteName: suiteName)
|
||||
defaults.removePersistentDomain(forName: suiteName)
|
||||
appStore = AppStore(defaults: defaults)
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
defaults.removePersistentDomain(forName: suiteName)
|
||||
appStore = nil
|
||||
defaults = nil
|
||||
suiteName = nil
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testAccountTypeKnownAndUnknownRawValueRoundTrips() {
|
||||
XCTAssertEqual(AppAccountType(rawValue: "scenic_user"), .scenicUser)
|
||||
XCTAssertEqual(AppAccountType(rawValue: "store_user"), .storeUser)
|
||||
XCTAssertEqual(AppAccountType(rawValue: "future_account"), .unknown("future_account"))
|
||||
XCTAssertEqual(AppAccountType.scenicUser.rawValue, "scenic_user")
|
||||
XCTAssertEqual(AppAccountType.storeUser.rawValue, "store_user")
|
||||
XCTAssertEqual(AppAccountType.unknown("future_account").rawValue, "future_account")
|
||||
}
|
||||
|
||||
func testSessionUsesExistingKeysAndPreservesUnknownAccountType() {
|
||||
defaults.set("legacy-token", forKey: "key_in_token")
|
||||
defaults.set("future_account", forKey: "key_in_account_type")
|
||||
|
||||
XCTAssertEqual(appStore.session.token, "legacy-token")
|
||||
XCTAssertEqual(appStore.session.accountType, .unknown("future_account"))
|
||||
|
||||
appStore.session.userId = "1001"
|
||||
appStore.session.currentScenicId = 22
|
||||
|
||||
XCTAssertEqual(defaults.string(forKey: "key_in_user_id"), "1001")
|
||||
XCTAssertEqual(defaults.string(forKey: "key_in_current_scenic_id"), "22")
|
||||
XCTAssertEqual(appStore.session.accountCachePrefix, "1001_future_account")
|
||||
}
|
||||
|
||||
func testFeatureStoresAreIsolatedByAccountAndDecodeSnapshots() {
|
||||
selectAccount(userId: "1001", type: .scenicUser)
|
||||
let permissions = [HomePermissionItem(id: "1", name: "钱包", uri: "wallet")]
|
||||
let scenicList = [ScenicInfo(id: 22, name: "测试景区", status: 1)]
|
||||
var queueSettings = ScenicQueueSettingsSnapshot()
|
||||
queueSettings.shootMinute = 8
|
||||
|
||||
appStore.permissions.savePermissionItems(permissions)
|
||||
appStore.permissions.saveRoleScenicList(scenicList)
|
||||
appStore.location.onlineStatus = true
|
||||
appStore.location.lastReportTime = 123_456
|
||||
appStore.location.reminderMinutes = 10
|
||||
appStore.payment.isOpenReceiveVoice = true
|
||||
appStore.scenicQueue.punchSpotId = "33"
|
||||
appStore.scenicQueue.punchSpotName = "东门"
|
||||
appStore.scenicQueue.saveSettingsSnapshot(queueSettings)
|
||||
appStore.scenicQueue.savePresetVoices([" A ", "", "B"])
|
||||
|
||||
selectAccount(userId: "2002", type: .storeUser)
|
||||
XCTAssertTrue(appStore.permissions.permissionItems().isEmpty)
|
||||
XCTAssertTrue(appStore.permissions.roleScenicList().isEmpty)
|
||||
XCTAssertFalse(appStore.location.onlineStatus)
|
||||
XCTAssertEqual(appStore.location.lastReportTime, 0)
|
||||
XCTAssertEqual(appStore.location.reminderMinutes, 0)
|
||||
XCTAssertFalse(appStore.payment.isOpenReceiveVoice)
|
||||
XCTAssertEqual(appStore.scenicQueue.punchSpotId, "")
|
||||
XCTAssertNil(appStore.scenicQueue.settingsSnapshot())
|
||||
|
||||
selectAccount(userId: "1001", type: .scenicUser)
|
||||
XCTAssertEqual(appStore.permissions.permissionItems(), permissions)
|
||||
XCTAssertEqual(appStore.permissions.roleScenicList(), scenicList)
|
||||
XCTAssertTrue(appStore.location.onlineStatus)
|
||||
XCTAssertEqual(appStore.location.lastReportTime, 123_456)
|
||||
XCTAssertEqual(appStore.location.reminderMinutes, 10)
|
||||
XCTAssertTrue(appStore.payment.isOpenReceiveVoice)
|
||||
XCTAssertTrue(appStore.scenicQueue.hasPunchSpotSaved())
|
||||
XCTAssertEqual(appStore.scenicQueue.punchSpotName, "东门")
|
||||
XCTAssertEqual(appStore.scenicQueue.settingsSnapshot(), queueSettings)
|
||||
XCTAssertEqual(appStore.scenicQueue.presetVoices(), ["A", "B"])
|
||||
}
|
||||
|
||||
func testLogoutClearsCurrentAccountModulesAndPreservesLoginPreferences() {
|
||||
appStore.session.lastLoginUsername = "13800000000"
|
||||
appStore.session.privacyAgreementAccepted = true
|
||||
|
||||
selectAccount(userId: "other", type: .storeUser)
|
||||
appStore.scenicQueue.punchSpotId = "99"
|
||||
|
||||
selectAccount(userId: "current", type: .scenicUser)
|
||||
appStore.session.token = "token"
|
||||
appStore.permissions.legacyRoleId = 41
|
||||
appStore.permissions.savePermissionItems([HomePermissionItem(id: "1", name: "钱包", uri: "wallet")])
|
||||
appStore.location.onlineStatus = true
|
||||
appStore.payment.isOpenReceiveVoice = true
|
||||
appStore.scenicQueue.punchSpotId = "33"
|
||||
|
||||
appStore.logout()
|
||||
|
||||
XCTAssertFalse(appStore.session.isLoggedIn)
|
||||
XCTAssertEqual(appStore.session.userId, "")
|
||||
XCTAssertEqual(appStore.session.lastLoginUsername, "13800000000")
|
||||
XCTAssertTrue(appStore.session.privacyAgreementAccepted)
|
||||
|
||||
selectAccount(userId: "current", type: .scenicUser)
|
||||
XCTAssertEqual(appStore.permissions.legacyRoleId, 0)
|
||||
XCTAssertTrue(appStore.permissions.permissionItems().isEmpty)
|
||||
XCTAssertFalse(appStore.location.onlineStatus)
|
||||
XCTAssertFalse(appStore.payment.isOpenReceiveVoice)
|
||||
XCTAssertEqual(appStore.scenicQueue.punchSpotId, "")
|
||||
|
||||
selectAccount(userId: "other", type: .storeUser)
|
||||
XCTAssertEqual(appStore.scenicQueue.punchSpotId, "99")
|
||||
}
|
||||
|
||||
private func selectAccount(userId: String, type: AppAccountType) {
|
||||
appStore.session.userId = userId
|
||||
appStore.session.accountType = type
|
||||
}
|
||||
}
|
||||
@ -16,7 +16,7 @@ final class CooperationAcquirerViewModelTests: XCTestCase {
|
||||
defaults = UserDefaults(suiteName: "CooperationAcquirerViewModelTests")!
|
||||
defaults.removePersistentDomain(forName: "CooperationAcquirerViewModelTests")
|
||||
appStore = AppStore(defaults: defaults)
|
||||
appStore.currentStoreId = 5
|
||||
appStore.session.currentStoreId = 5
|
||||
}
|
||||
|
||||
func testSaveRemarkWithValidLengthCallsAPI() async throws {
|
||||
|
||||
@ -10,8 +10,8 @@ import XCTest
|
||||
final class DepositOrderListViewModelTests: XCTestCase {
|
||||
|
||||
override func tearDown() {
|
||||
AppStore.shared.currentStoreId = 0
|
||||
AppStore.shared.currentScenicId = 0
|
||||
AppStore.shared.session.currentStoreId = 0
|
||||
AppStore.shared.session.currentScenicId = 0
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ final class DepositOrderListViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testStoreIdZeroAbortsWithMessage() async {
|
||||
AppStore.shared.currentStoreId = 0
|
||||
AppStore.shared.session.currentStoreId = 0
|
||||
let viewModel = DepositOrderListViewModel()
|
||||
var message: String?
|
||||
viewModel.onShowMessage = { message = $0 }
|
||||
|
||||
@ -16,8 +16,8 @@ final class HomeLocationStateStoreTests: XCTestCase {
|
||||
defaults = UserDefaults(suiteName: "HomeLocationStateStoreTests")!
|
||||
defaults.removePersistentDomain(forName: "HomeLocationStateStoreTests")
|
||||
appStore = AppStore(defaults: defaults)
|
||||
appStore.userId = "1001"
|
||||
appStore.accountType = "scenic_user"
|
||||
appStore.session.userId = "1001"
|
||||
appStore.session.accountType = .scenicUser
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
@ -27,21 +27,21 @@ final class HomeLocationStateStoreTests: XCTestCase {
|
||||
|
||||
func testExpiredOnlineSessionIsClearedOnRestore() {
|
||||
let expiredTimestamp = Int64(Date().timeIntervalSince1970 * 1000) - HomeLocationStateStore.onlineDurationMillis - 1_000
|
||||
appStore.onlineStatus = true
|
||||
appStore.lastLocationReportTime = expiredTimestamp
|
||||
appStore.location.onlineStatus = true
|
||||
appStore.location.lastReportTime = expiredTimestamp
|
||||
|
||||
let stateStore = HomeLocationStateStore(store: appStore)
|
||||
stateStore.restoreStateIfNeeded()
|
||||
|
||||
XCTAssertFalse(stateStore.isOnline)
|
||||
XCTAssertFalse(appStore.onlineStatus)
|
||||
XCTAssertEqual(appStore.lastLocationReportTime, 0)
|
||||
XCTAssertFalse(appStore.location.onlineStatus)
|
||||
XCTAssertEqual(appStore.location.lastReportTime, 0)
|
||||
}
|
||||
|
||||
func testActiveOnlineSessionRestoresCountdown() {
|
||||
let recentTimestamp = Int64(Date().timeIntervalSince1970 * 1000) - 60_000
|
||||
appStore.onlineStatus = true
|
||||
appStore.lastLocationReportTime = recentTimestamp
|
||||
appStore.location.onlineStatus = true
|
||||
appStore.location.lastReportTime = recentTimestamp
|
||||
|
||||
let stateStore = HomeLocationStateStore(store: appStore)
|
||||
stateStore.restoreStateIfNeeded()
|
||||
@ -56,7 +56,7 @@ final class HomeLocationStateStoreTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testShouldTriggerTimeoutReminderWithinWindow() {
|
||||
appStore.locationReminderMinutes = 10
|
||||
appStore.location.reminderMinutes = 10
|
||||
let stateStore = HomeLocationStateStore(store: appStore)
|
||||
stateStore.startLocationReport(at: Int64(Date().timeIntervalSince1970 * 1000))
|
||||
stateStore.updateReminderMinutes(10)
|
||||
@ -124,22 +124,22 @@ final class HomeLocationStateStoreTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testRestoreClearsOnlineStateWhenStoredLastReportTimeIsMissing() {
|
||||
appStore.onlineStatus = true
|
||||
appStore.lastLocationReportTime = 0
|
||||
appStore.location.onlineStatus = true
|
||||
appStore.location.lastReportTime = 0
|
||||
|
||||
let stateStore = HomeLocationStateStore(store: appStore)
|
||||
stateStore.restoreStateIfNeeded()
|
||||
|
||||
XCTAssertFalse(stateStore.isOnline)
|
||||
XCTAssertFalse(appStore.onlineStatus)
|
||||
XCTAssertEqual(appStore.lastLocationReportTime, 0)
|
||||
XCTAssertFalse(appStore.location.onlineStatus)
|
||||
XCTAssertEqual(appStore.location.lastReportTime, 0)
|
||||
XCTAssertEqual(stateStore.nextReportCountdownSeconds, 0)
|
||||
}
|
||||
|
||||
func testRestoreClearsRuntimeStateWhenPersistedOffline() {
|
||||
let stateStore = HomeLocationStateStore(store: appStore)
|
||||
stateStore.startLocationReport(at: Int64(Date().timeIntervalSince1970 * 1000))
|
||||
appStore.onlineStatus = false
|
||||
appStore.location.onlineStatus = false
|
||||
|
||||
stateStore.restoreStateIfNeeded()
|
||||
|
||||
|
||||
@ -18,12 +18,12 @@ final class HomeViewModelTests: XCTestCase {
|
||||
defaults = UserDefaults(suiteName: "HomeViewModelTests")!
|
||||
defaults.removePersistentDomain(forName: "HomeViewModelTests")
|
||||
appStore = AppStore(defaults: defaults)
|
||||
appStore.userId = "2001"
|
||||
appStore.accountType = "scenic_user"
|
||||
appStore.roleCode = AppRoleCode.photographer.rawValue
|
||||
appStore.roleName = "摄影师"
|
||||
appStore.currentScenicId = 10
|
||||
appStore.currentScenicName = "测试景区"
|
||||
appStore.session.userId = "2001"
|
||||
appStore.session.accountType = .scenicUser
|
||||
appStore.session.roleCode = AppRoleCode.photographer.rawValue
|
||||
appStore.session.roleName = "摄影师"
|
||||
appStore.session.currentScenicId = 10
|
||||
appStore.session.currentScenicName = "测试景区"
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
@ -32,12 +32,12 @@ final class HomeViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testMinimalTopRoleFlag() {
|
||||
appStore.roleCode = AppRoleCode.storeAdmin.rawValue
|
||||
appStore.session.roleCode = AppRoleCode.storeAdmin.rawValue
|
||||
let viewModel = HomeViewModel(appStore: appStore)
|
||||
viewModel.refreshLocalDisplayState()
|
||||
XCTAssertTrue(viewModel.isMinimalTopRole)
|
||||
|
||||
appStore.roleCode = AppRoleCode.photographer.rawValue
|
||||
appStore.session.roleCode = AppRoleCode.photographer.rawValue
|
||||
viewModel.refreshLocalDisplayState()
|
||||
XCTAssertFalse(viewModel.isMinimalTopRole)
|
||||
}
|
||||
@ -77,7 +77,7 @@ final class HomeViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testRebuildCommonMenusFiltersUnknownURIs() {
|
||||
appStore.savePermissionItems([
|
||||
appStore.permissions.savePermissionItems([
|
||||
HomePermissionItem(id: "1", name: "钱包", uri: "wallet"),
|
||||
HomePermissionItem(id: "2", name: "未知", uri: "unknown_uri"),
|
||||
])
|
||||
@ -88,7 +88,7 @@ final class HomeViewModelTests: XCTestCase {
|
||||
|
||||
func testRebuildCommonMenusPersistsDefaultURIsOnFirstLoad() {
|
||||
let menuStore = HomeCommonMenuStore(defaults: defaults)
|
||||
appStore.savePermissionItems([
|
||||
appStore.permissions.savePermissionItems([
|
||||
HomePermissionItem(id: "1", name: "钱包", uri: "wallet"),
|
||||
HomePermissionItem(id: "2", name: "云盘", uri: "cloud_management"),
|
||||
HomePermissionItem(id: "3", name: "任务", uri: "task_management"),
|
||||
@ -104,8 +104,8 @@ final class HomeViewModelTests: XCTestCase {
|
||||
])
|
||||
XCTAssertEqual(
|
||||
menuStore.savedCommonURIs(
|
||||
accountScope: appStore.accountCachePrefix,
|
||||
roleCode: appStore.roleCode
|
||||
accountScope: appStore.session.accountCachePrefix,
|
||||
roleCode: appStore.session.roleCode
|
||||
),
|
||||
["wallet", "cloud_management", "task_management", "system_settings"]
|
||||
)
|
||||
@ -124,7 +124,7 @@ final class HomeViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testRolePermissionBuildsCommonMenusAndStoreCardRole() async throws {
|
||||
appStore.roleCode = AppRoleCode.storeAdmin.rawValue
|
||||
appStore.session.roleCode = AppRoleCode.storeAdmin.rawValue
|
||||
let permissionResponse = RolePermissionResponse(
|
||||
role: RoleInfo(
|
||||
id: 46,
|
||||
@ -170,7 +170,7 @@ final class HomeViewModelTests: XCTestCase {
|
||||
),
|
||||
scenic: []
|
||||
)
|
||||
appStore.savePermissionItems([HomePermissionItem(id: "1", name: "钱包", uri: "wallet")])
|
||||
appStore.permissions.savePermissionItems([HomePermissionItem(id: "1", name: "钱包", uri: "wallet")])
|
||||
|
||||
let permissionJSON = try TestJSON.envelope(data: [permissionResponse])
|
||||
let session = MockURLSession(responses: [permissionJSON])
|
||||
@ -185,26 +185,26 @@ final class HomeViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testRefreshLocalDisplayStateClearsWhenNameMissing() {
|
||||
appStore.currentScenicId = 10
|
||||
appStore.currentScenicName = ""
|
||||
appStore.session.currentScenicId = 10
|
||||
appStore.session.currentScenicName = ""
|
||||
let viewModel = HomeViewModel(appStore: appStore)
|
||||
|
||||
viewModel.refreshLocalDisplayState()
|
||||
|
||||
XCTAssertEqual(viewModel.currentScenicName, "")
|
||||
XCTAssertEqual(appStore.currentScenicId, 0)
|
||||
XCTAssertEqual(appStore.currentScenicName, "")
|
||||
XCTAssertEqual(appStore.session.currentScenicId, 0)
|
||||
XCTAssertEqual(appStore.session.currentScenicName, "")
|
||||
}
|
||||
|
||||
func testRefreshLocalDisplayStateClearsWhenIdMissing() {
|
||||
appStore.currentScenicId = 0
|
||||
appStore.currentScenicName = "残留名称"
|
||||
appStore.session.currentScenicId = 0
|
||||
appStore.session.currentScenicName = "残留名称"
|
||||
let viewModel = HomeViewModel(appStore: appStore)
|
||||
|
||||
viewModel.refreshLocalDisplayState()
|
||||
|
||||
XCTAssertEqual(viewModel.currentScenicName, "")
|
||||
XCTAssertEqual(appStore.currentScenicName, "")
|
||||
XCTAssertEqual(appStore.session.currentScenicName, "")
|
||||
}
|
||||
|
||||
private func statusActionContainer(in view: HomeQuickActionsView) throws -> UIView {
|
||||
|
||||
@ -23,7 +23,7 @@ final class LocationProviderTests: XCTestCase {
|
||||
func testScenicSelectionUsesInjectedProviderSnapshot() async {
|
||||
let mock = MockLocationProvider()
|
||||
mock.snapshot = HomeLocationSnapshot(latitude: 30.2, longitude: 120.1, address: "杭州")
|
||||
appStore.saveRoleScenicList([
|
||||
appStore.permissions.saveRoleScenicList([
|
||||
ScenicInfo(id: 1, name: "西湖", status: 1, location: ScenicLocationInfo(lng: 120.1, lat: 30.2, address: "杭州"), coverImg: nil),
|
||||
])
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ final class LocationReportHistoryViewModelTests: XCTestCase {
|
||||
defaults = UserDefaults(suiteName: "LocationReportHistoryViewModelTests")!
|
||||
defaults.removePersistentDomain(forName: "LocationReportHistoryViewModelTests")
|
||||
appStore = AppStore(defaults: defaults)
|
||||
appStore.userId = "staff-99"
|
||||
appStore.session.userId = "staff-99"
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
|
||||
@ -17,8 +17,8 @@ final class LocationReportServiceTests: XCTestCase {
|
||||
defaults = UserDefaults(suiteName: "LocationReportServiceTests")!
|
||||
defaults.removePersistentDomain(forName: "LocationReportServiceTests")
|
||||
appStore = AppStore(defaults: defaults)
|
||||
appStore.userId = "staff-1"
|
||||
appStore.currentScenicId = 100
|
||||
appStore.session.userId = "staff-1"
|
||||
appStore.session.currentScenicId = 100
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
@ -122,7 +122,7 @@ final class LocationReportServiceTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testMissingScenicThrows() async {
|
||||
appStore.currentScenicId = 0
|
||||
appStore.session.currentScenicId = 0
|
||||
let api = HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [])))
|
||||
let stateStore = HomeLocationStateStore(store: appStore)
|
||||
let service = LocationReportService(appStore: appStore, locationStateStore: stateStore)
|
||||
|
||||
@ -10,17 +10,27 @@ import XCTest
|
||||
/// 素材管理 ViewModel 测试。
|
||||
@MainActor
|
||||
final class MaterialManagementViewModelTests: XCTestCase {
|
||||
func testDetailCarouselLoopsAcrossCopiedBoundaryPages() {
|
||||
XCTAssertEqual(MaterialCarouselIndexMapper.initialRenderedPage(mediaCount: 3), 1)
|
||||
XCTAssertEqual(MaterialCarouselIndexMapper.normalizedRenderedPage(0, mediaCount: 3), 3)
|
||||
XCTAssertEqual(MaterialCarouselIndexMapper.logicalPage(renderedPage: 3, mediaCount: 3), 2)
|
||||
XCTAssertEqual(MaterialCarouselIndexMapper.normalizedRenderedPage(4, mediaCount: 3), 1)
|
||||
XCTAssertEqual(MaterialCarouselIndexMapper.logicalPage(renderedPage: 1, mediaCount: 3), 0)
|
||||
XCTAssertEqual(MaterialCarouselIndexMapper.initialRenderedPage(mediaCount: 1), 0)
|
||||
}
|
||||
|
||||
func testListLoadsMoreSearchesAndMapsFilterStatus() async {
|
||||
let api = MockMaterialAPI()
|
||||
api.listResponses = [
|
||||
MaterialListResponse(total: 12, list: (0 ..< 10).map { makeItem(id: $0) }, order: MaterialOrderInfo(totalNum: 1386, avgOrderAmount: "12", refundTotal: "3")),
|
||||
MaterialListResponse(total: 12, list: [makeItem(id: 10), makeItem(id: 11)]),
|
||||
MaterialListResponse(total: 12, list: [makeItem(id: 9), makeItem(id: 10), makeItem(id: 11)]),
|
||||
MaterialListResponse(total: 1, list: [makeItem(id: 99)]),
|
||||
]
|
||||
let viewModel = MaterialListViewModel()
|
||||
|
||||
await viewModel.loadInitial(api: api)
|
||||
await viewModel.loadMoreIfNeeded(lastVisibleIndex: 8, api: api)
|
||||
XCTAssertEqual(viewModel.items.map(\.id), Array(0 ... 11))
|
||||
await viewModel.selectFilter(.pending, api: api)
|
||||
|
||||
XCTAssertEqual(api.listCalls.map(\.status), [4, 4, 0])
|
||||
@ -118,43 +128,69 @@ final class MaterialManagementViewModelTests: XCTestCase {
|
||||
XCTAssertNil(api.lastUploadRequest?.id)
|
||||
}
|
||||
|
||||
func testCloudMediaImportBuildsMaterialRequestWithoutUploadingAgain() async {
|
||||
let api = MockMaterialAPI()
|
||||
api.spotResponse = ScenicSpotListResponse(list: [ScenicSpotItem(id: 9, name: "打卡点")], total: 1)
|
||||
func testChangingMediaTypeClearsSelectedMedia() async {
|
||||
let viewModel = MaterialFormViewModel(mode: .create, currentScenicIdProvider: { 3 })
|
||||
var didSucceed = false
|
||||
viewModel.onSubmitSuccess = { didSucceed = true }
|
||||
await viewModel.addLocalMedia(
|
||||
[makeUploadItem(fileName: "image.jpg")],
|
||||
uploader: MockMaterialUploader(urls: ["https://cdn/image.jpg"])
|
||||
)
|
||||
|
||||
await viewModel.loadInitial(api: api)
|
||||
viewModel.updateMaterialName("云盘素材")
|
||||
viewModel.selectScenicSpot(ScenicSpotItem(id: 9, name: "打卡点"))
|
||||
viewModel.addCloudMediaFiles([
|
||||
CloudFile(id: 31, fileUrl: "https://cdn/cloud.jpg", coverUrl: "https://cdn/cover-thumb.jpg", name: "cloud.jpg", type: 2, fileSize: 88),
|
||||
])
|
||||
await viewModel.setCoverImage(makeUploadItem(fileName: "cover.jpg"), uploader: MockMaterialUploader(urls: ["https://cdn/cover.jpg"]))
|
||||
await viewModel.submit(api: api)
|
||||
viewModel.selectMediaType(.video)
|
||||
|
||||
XCTAssertTrue(didSucceed)
|
||||
XCTAssertEqual(viewModel.importedCloudFileIDs, Set([31]))
|
||||
XCTAssertEqual(api.lastUploadRequest?.mediaList.first?.originalName, "cloud.jpg")
|
||||
XCTAssertEqual(api.lastUploadRequest?.mediaList.first?.ossUrl, "https://cdn/cloud.jpg")
|
||||
XCTAssertEqual(api.lastUploadRequest?.mediaList.first?.size, 88)
|
||||
XCTAssertEqual(viewModel.mediaType, .video)
|
||||
XCTAssertTrue(viewModel.uploadedMediaList.isEmpty)
|
||||
}
|
||||
|
||||
func testCloudMediaImportFiltersByCurrentMaterialTypeAndDeduplicates() {
|
||||
func testMediaLimitsAndSequentialUploadMatchAndroid() async {
|
||||
let viewModel = MaterialFormViewModel(mode: .create, currentScenicIdProvider: { 3 })
|
||||
var messages: [String] = []
|
||||
viewModel.onShowMessage = { messages.append($0) }
|
||||
let imageItems = (0 ..< 11).map { makeUploadItem(fileName: "\($0).jpg") }
|
||||
let uploader = MockMaterialUploader(urls: (0 ..< 11).map { "https://cdn/\($0).jpg" })
|
||||
|
||||
viewModel.addCloudMediaFiles([
|
||||
CloudFile(id: 1, fileUrl: "https://cdn/video.mp4", name: "video.mp4", type: 1),
|
||||
CloudFile(id: 2, fileUrl: "https://cdn/image.jpg", name: "image.jpg", type: 2),
|
||||
CloudFile(id: 2, fileUrl: "https://cdn/image.jpg", name: "image.jpg", type: 2),
|
||||
])
|
||||
await viewModel.addLocalMedia(imageItems, uploader: uploader)
|
||||
|
||||
XCTAssertEqual(viewModel.uploadedMediaList.map(\.ossUrl), ["https://cdn/image.jpg"])
|
||||
XCTAssertEqual(viewModel.importedCloudFileIDs, Set([2]))
|
||||
XCTAssertTrue(messages.isEmpty)
|
||||
XCTAssertEqual(viewModel.uploadedMediaList.count, 9)
|
||||
XCTAssertEqual(uploader.uploadedFileNames, (0 ..< 9).map { "\($0).jpg" })
|
||||
|
||||
viewModel.selectMediaType(.video)
|
||||
let videoUploader = MockMaterialUploader(urls: ["https://cdn/a.mp4", "https://cdn/b.mp4"])
|
||||
await viewModel.addLocalMedia(
|
||||
[makeUploadItem(fileName: "a.mp4"), makeUploadItem(fileName: "b.mp4")],
|
||||
uploader: videoUploader
|
||||
)
|
||||
|
||||
XCTAssertEqual(viewModel.uploadedMediaList.map(\.fileName), ["a.mp4"])
|
||||
XCTAssertEqual(videoUploader.uploadedFileNames, ["a.mp4"])
|
||||
}
|
||||
|
||||
func testTagDeletionRequiresConfirmation() async {
|
||||
let api = MockMaterialAPI()
|
||||
let viewModel = MaterialFormViewModel(mode: .create, currentScenicIdProvider: { 3 })
|
||||
viewModel.updateTagInput("snow-tag")
|
||||
await viewModel.addTag(api: api)
|
||||
let tag = try! XCTUnwrap(viewModel.tagList.first)
|
||||
|
||||
viewModel.requestDeleteTag(tag)
|
||||
XCTAssertEqual(viewModel.pendingDeleteTag, tag)
|
||||
XCTAssertEqual(viewModel.tagList, [tag])
|
||||
|
||||
viewModel.cancelDeleteTag()
|
||||
XCTAssertNil(viewModel.pendingDeleteTag)
|
||||
XCTAssertEqual(viewModel.tagList, [tag])
|
||||
|
||||
viewModel.requestDeleteTag(tag)
|
||||
viewModel.confirmDeleteTag()
|
||||
XCTAssertNil(viewModel.pendingDeleteTag)
|
||||
XCTAssertTrue(viewModel.tagList.isEmpty)
|
||||
}
|
||||
|
||||
func testInputLengthsAreCappedLikeAndroid() {
|
||||
let viewModel = MaterialFormViewModel(mode: .create, currentScenicIdProvider: { 3 })
|
||||
|
||||
viewModel.updateMaterialName(String(repeating: "名", count: 35))
|
||||
viewModel.updateTagInput(String(repeating: "标", count: 20))
|
||||
|
||||
XCTAssertEqual(viewModel.materialName.count, 30)
|
||||
XCTAssertEqual(viewModel.tagInput.count, 15)
|
||||
}
|
||||
|
||||
func testUploadDialogStaysHiddenAfterCompletionProgressCallback() async {
|
||||
@ -261,6 +297,7 @@ private final class MockMaterialAPI: MaterialManagementServing {
|
||||
@MainActor
|
||||
private final class MockMaterialUploader: MaterialOSSUploading {
|
||||
private var urls: [String]
|
||||
private(set) var uploadedFileNames: [String] = []
|
||||
|
||||
init(urls: [String]) {
|
||||
self.urls = urls
|
||||
@ -273,6 +310,7 @@ private final class MockMaterialUploader: MaterialOSSUploading {
|
||||
scenicId: Int,
|
||||
onProgress: @escaping (Int) -> Void
|
||||
) async throws -> String {
|
||||
uploadedFileNames.append(fileName)
|
||||
onProgress(100)
|
||||
return urls.isEmpty ? "https://cdn/default.jpg" : urls.removeFirst()
|
||||
}
|
||||
|
||||
@ -10,9 +10,9 @@ import XCTest
|
||||
final class OrderListViewModelTests: XCTestCase {
|
||||
|
||||
override func tearDown() {
|
||||
AppStore.shared.roleCode = ""
|
||||
AppStore.shared.roleName = ""
|
||||
AppStore.shared.currentScenicId = 0
|
||||
AppStore.shared.session.roleCode = ""
|
||||
AppStore.shared.session.roleName = ""
|
||||
AppStore.shared.session.currentScenicId = 0
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
@ -37,12 +37,12 @@ final class OrderListViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testRoleRoutingPath() {
|
||||
AppStore.shared.roleCode = AppRoleCode.scenicAdmin.rawValue
|
||||
AppStore.shared.session.roleCode = AppRoleCode.scenicAdmin.rawValue
|
||||
let scenicVM = OrderListViewModel()
|
||||
XCTAssertEqual(scenicVM.listAPIPath, "/api/app/scenic-admin/order/list")
|
||||
XCTAssertTrue(scenicVM.isScenicAdminReadOnly)
|
||||
|
||||
AppStore.shared.roleCode = AppRoleCode.photographer.rawValue
|
||||
AppStore.shared.session.roleCode = AppRoleCode.photographer.rawValue
|
||||
let photoVM = OrderListViewModel()
|
||||
XCTAssertEqual(photoVM.listAPIPath, "/api/yf-handset-app/photog/order/listv2")
|
||||
XCTAssertFalse(photoVM.isScenicAdminReadOnly)
|
||||
|
||||
@ -16,9 +16,9 @@ final class PaymentCollectionDetailsViewModelTests: XCTestCase {
|
||||
let defaults = UserDefaults(suiteName: "PaymentCollectionDetailsViewModelTests")!
|
||||
defaults.removePersistentDomain(forName: "PaymentCollectionDetailsViewModelTests")
|
||||
appStore = AppStore(defaults: defaults)
|
||||
appStore.userId = "10001"
|
||||
appStore.currentScenicId = 10
|
||||
appStore.currentScenicName = "测试景区"
|
||||
appStore.session.userId = "10001"
|
||||
appStore.session.currentScenicId = 10
|
||||
appStore.session.currentScenicName = "测试景区"
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
@ -76,12 +76,12 @@ final class PaymentCollectionDetailsViewModelTests: XCTestCase {
|
||||
|
||||
XCTAssertFalse(viewModel.isVoiceBroadcastOpen)
|
||||
viewModel.toggleReceiveVoice()
|
||||
XCTAssertTrue(appStore.isOpenReceiveVoice)
|
||||
XCTAssertTrue(appStore.payment.isOpenReceiveVoice)
|
||||
XCTAssertTrue(viewModel.isVoiceBroadcastOpen)
|
||||
}
|
||||
|
||||
func testLoadPayCodeClearsQRWhenScenicMissing() async {
|
||||
appStore.currentScenicId = 0
|
||||
appStore.session.currentScenicId = 0
|
||||
let session = MockURLSession(responses: [])
|
||||
let api = PaymentAPI(client: APIClient(environment: .testing, session: session))
|
||||
let viewModel = PaymentCollectionDetailsViewModel(appStore: appStore)
|
||||
|
||||
@ -54,7 +54,7 @@ final class PaymentCollectionRecordViewModelTests: XCTestCase {
|
||||
let defaults = UserDefaults(suiteName: "PaymentCollectionRecordViewModelTests")!
|
||||
defaults.removePersistentDomain(forName: "PaymentCollectionRecordViewModelTests")
|
||||
let appStore = AppStore(defaults: defaults)
|
||||
appStore.currentScenicId = 10
|
||||
appStore.session.currentScenicId = 10
|
||||
|
||||
let recordJSON = """
|
||||
{"code":100000,"msg":"success","data":{"analyse":[{"date":"2024-01-15","order_count":1,"order_amount_sum":"50.00"}],"list":[{"order_number":"NO001","user_phone":"13800138000","order_amount":"50.00","create_date":"2024-01-15","create_time":"10:00:00"}]}}
|
||||
|
||||
@ -17,7 +17,7 @@ final class PermissionApplyViewModelTests: XCTestCase {
|
||||
defaults = UserDefaults(suiteName: "PermissionApplyViewModelTests")!
|
||||
defaults.removePersistentDomain(forName: "PermissionApplyViewModelTests")
|
||||
appStore = AppStore(defaults: defaults)
|
||||
appStore.saveRolePermissionList([
|
||||
appStore.permissions.saveRolePermissionList([
|
||||
RolePermissionResponse(
|
||||
role: RoleInfo(id: 10, name: "摄影师", roleCode: "PHOTOGRAPHER", notes: "拍摄"),
|
||||
scenic: [ScenicInfo(id: 100, name: "已有景区", status: 1)]
|
||||
|
||||
47
suixinkanTests/ProfileAvatarCropTests.swift
Normal file
47
suixinkanTests/ProfileAvatarCropTests.swift
Normal file
@ -0,0 +1,47 @@
|
||||
//
|
||||
// ProfileAvatarCropTests.swift
|
||||
// suixinkanTests
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import suixinkan
|
||||
|
||||
@MainActor
|
||||
/// 空间设置头像裁剪输出测试。
|
||||
final class ProfileAvatarCropTests: XCTestCase {
|
||||
func testCropOutputIs480PixelSquare() throws {
|
||||
let source = UIGraphicsImageRenderer(size: CGSize(width: 800, height: 600)).image { context in
|
||||
UIColor.red.setFill()
|
||||
context.fill(CGRect(x: 0, y: 0, width: 800, height: 600))
|
||||
}
|
||||
let controller = ProfileAvatarCropViewController(image: source) { _ in }
|
||||
controller.loadViewIfNeeded()
|
||||
controller.view.frame = CGRect(x: 0, y: 0, width: 390, height: 844)
|
||||
controller.view.setNeedsLayout()
|
||||
controller.view.layoutIfNeeded()
|
||||
|
||||
let data = try XCTUnwrap(controller.renderedCropData())
|
||||
let image = try XCTUnwrap(UIImage(data: data))
|
||||
XCTAssertEqual(image.size.width * image.scale, 480)
|
||||
XCTAssertEqual(image.size.height * image.scale, 480)
|
||||
}
|
||||
|
||||
func testCropNormalizesImageOrientation() throws {
|
||||
let base = UIGraphicsImageRenderer(size: CGSize(width: 600, height: 800)).image { context in
|
||||
UIColor.blue.setFill()
|
||||
context.fill(CGRect(x: 0, y: 0, width: 600, height: 800))
|
||||
}
|
||||
let cgImage = try XCTUnwrap(base.cgImage)
|
||||
let rotated = UIImage(cgImage: cgImage, scale: 1, orientation: .left)
|
||||
let controller = ProfileAvatarCropViewController(image: rotated) { _ in }
|
||||
controller.loadViewIfNeeded()
|
||||
controller.view.frame = CGRect(x: 0, y: 0, width: 390, height: 844)
|
||||
controller.view.layoutIfNeeded()
|
||||
|
||||
let data = try XCTUnwrap(controller.renderedCropData())
|
||||
let output = try XCTUnwrap(UIImage(data: data))
|
||||
XCTAssertEqual(output.imageOrientation, .up)
|
||||
XCTAssertEqual(output.size.width * output.scale, 480)
|
||||
XCTAssertEqual(output.size.height * output.scale, 480)
|
||||
}
|
||||
}
|
||||
@ -57,7 +57,7 @@ final class ProfileEditViewModelTests: XCTestCase {
|
||||
)
|
||||
|
||||
await fulfillment(of: [expectation], timeout: 1)
|
||||
XCTAssertEqual(AppStore.shared.userName, "新昵称")
|
||||
XCTAssertEqual(AppStore.shared.session.userName, "新昵称")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,14 +20,14 @@ final class ProfileSpaceSettingsViewModelTests: XCTestCase {
|
||||
defaults = UserDefaults(suiteName: "ProfileSpaceSettingsViewModelTests")!
|
||||
defaults.removePersistentDomain(forName: "ProfileSpaceSettingsViewModelTests")
|
||||
appStore = AppStore(defaults: defaults)
|
||||
appStore.currentScenicId = 11
|
||||
appStore.session.currentScenicId = 11
|
||||
calendar = Calendar(identifier: .gregorian)
|
||||
calendar.timeZone = TimeZone(secondsFromGMT: 0)!
|
||||
fixedNow = calendar.date(from: DateComponents(year: 2026, month: 7, day: 8))!
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
AppStore.shared.currentScenicId = 0
|
||||
AppStore.shared.session.currentScenicId = 0
|
||||
appStore.logout()
|
||||
defaults.removePersistentDomain(forName: "ProfileSpaceSettingsViewModelTests")
|
||||
super.tearDown()
|
||||
@ -170,7 +170,8 @@ final class ProfileSpaceSettingsViewModelTests: XCTestCase {
|
||||
viewModel.updateHolidayEndTime("20:00")
|
||||
|
||||
viewModel.updateBusinessEndTime("08:00")
|
||||
XCTAssertThrowsError(try viewModel.makeSaveRequest())
|
||||
XCTAssertEqual(viewModel.businessEndTime, "18:00")
|
||||
XCTAssertNoThrow(try viewModel.makeSaveRequest())
|
||||
}
|
||||
|
||||
func testHasChangesAndSaveRequestFieldsMatchAndroid() async throws {
|
||||
@ -250,7 +251,7 @@ final class ProfileSpaceSettingsViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSpaceSettingsRoutePushesProfileSpaceSettingsPage() {
|
||||
AppStore.shared.currentScenicId = 11
|
||||
AppStore.shared.session.currentScenicId = 11
|
||||
let root = UIViewController()
|
||||
let navigationController = UINavigationController(rootViewController: root)
|
||||
|
||||
@ -259,8 +260,26 @@ final class ProfileSpaceSettingsViewModelTests: XCTestCase {
|
||||
XCTAssertTrue(navigationController.topViewController is ProfileSpaceSettingsViewController)
|
||||
}
|
||||
|
||||
func testCertificationExpansionAndAvatarUploadRefresh() async {
|
||||
let response = PhotographerProfileInfoResponse.loaded(certifications: ["景区1", "景区2", "景区3", "景区4"])
|
||||
let api = FakeProfileSpaceSettingsAPI(response: response)
|
||||
let viewModel = makeViewModel()
|
||||
await viewModel.load(api: api)
|
||||
|
||||
XCTAssertFalse(viewModel.isCertificationExpanded)
|
||||
viewModel.toggleCertificationExpanded()
|
||||
XCTAssertTrue(viewModel.isCertificationExpanded)
|
||||
|
||||
let avatarData = Data([1, 2, 3])
|
||||
await viewModel.uploadAvatar(data: avatarData, fileName: "avatar.jpg", api: api)
|
||||
|
||||
XCTAssertEqual(api.uploadedAvatarData, avatarData)
|
||||
XCTAssertEqual(api.uploadedAvatarFileName, "avatar.jpg")
|
||||
XCTAssertEqual(viewModel.avatarURL, "https://example.com/new-avatar.jpg")
|
||||
}
|
||||
|
||||
func testSystemSettingsRoutePushesSettingPage() {
|
||||
AppStore.shared.currentScenicId = 11
|
||||
AppStore.shared.session.currentScenicId = 11
|
||||
let tabBarController = MainTabBarController()
|
||||
tabBarController.loadViewIfNeeded()
|
||||
guard
|
||||
@ -292,6 +311,8 @@ private final class FakeProfileSpaceSettingsAPI: ProfileSpaceSettingsAPI {
|
||||
var lastUpdateRequest: UpdatePhotographerProfileInfoRequest?
|
||||
var lastAddScheduleRequest: AddPhotographerScheduleRequest?
|
||||
var lastDeletedScheduleId: Int?
|
||||
var uploadedAvatarData: Data?
|
||||
var uploadedAvatarFileName: String?
|
||||
|
||||
init(response: PhotographerProfileInfoResponse) {
|
||||
self.response = response
|
||||
@ -335,10 +356,40 @@ private final class FakeProfileSpaceSettingsAPI: ProfileSpaceSettingsAPI {
|
||||
func deleteSchedule(id: Int) async throws {
|
||||
lastDeletedScheduleId = id
|
||||
}
|
||||
|
||||
func uploadAvatar(data: Data, fileName: String) async throws {
|
||||
uploadedAvatarData = data
|
||||
uploadedAvatarFileName = fileName
|
||||
response = PhotographerProfileInfoResponse(
|
||||
id: response.id,
|
||||
scenicId: response.scenicId,
|
||||
photogUid: response.photogUid,
|
||||
realName: response.realName,
|
||||
nickname: response.nickname,
|
||||
avatar: "https://example.com/new-avatar.jpg",
|
||||
scenicCertification: response.scenicCertification,
|
||||
description: response.description,
|
||||
attrLabel: response.attrLabel,
|
||||
shootLabel: response.shootLabel,
|
||||
cameraDevice: response.cameraDevice,
|
||||
businessStartTime: response.businessStartTime,
|
||||
businessEndTime: response.businessEndTime,
|
||||
holidayStartTime: response.holidayStartTime,
|
||||
holidayEndTime: response.holidayEndTime,
|
||||
businessRange: response.businessRange,
|
||||
acceptOrderStatus: response.acceptOrderStatus,
|
||||
schedule: response.schedule,
|
||||
virtualPhone: response.virtualPhone
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private extension PhotographerProfileInfoResponse {
|
||||
static func loaded() -> PhotographerProfileInfoResponse {
|
||||
loaded(certifications: ["西湖"])
|
||||
}
|
||||
|
||||
static func loaded(certifications: [String]) -> PhotographerProfileInfoResponse {
|
||||
PhotographerProfileInfoResponse(
|
||||
id: 1,
|
||||
scenicId: 11,
|
||||
@ -346,7 +397,7 @@ private extension PhotographerProfileInfoResponse {
|
||||
realName: "林摄影",
|
||||
nickname: "小林",
|
||||
avatar: "https://example.com/a.jpg",
|
||||
scenicCertification: ["西湖"],
|
||||
scenicCertification: certifications,
|
||||
description: "简介",
|
||||
attrLabel: ["航拍"],
|
||||
shootLabel: ["亲子"],
|
||||
|
||||
@ -15,10 +15,10 @@ final class ProfileViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testShowPhotographerFieldsWhenRoleCodeIsPhotographer() {
|
||||
AppStore.shared.roleCode = AppRoleCode.photographer.rawValue
|
||||
AppStore.shared.session.roleCode = AppRoleCode.photographer.rawValue
|
||||
let viewModel = ProfileViewModel()
|
||||
XCTAssertTrue(viewModel.showPhotographerFields)
|
||||
AppStore.shared.roleCode = ""
|
||||
AppStore.shared.session.roleCode = ""
|
||||
}
|
||||
|
||||
func testMaskCardNumberShowsLastFourDigits() {
|
||||
@ -45,24 +45,24 @@ final class ProfileViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testAccountTypeLabelForStoreAccount() {
|
||||
AppStore.shared.accountType = V9StoreUser.accountTypeValue
|
||||
AppStore.shared.session.accountType = .storeUser
|
||||
let viewModel = ProfileViewModel()
|
||||
XCTAssertEqual(viewModel.accountTypeLabel, "门店账号")
|
||||
XCTAssertTrue(viewModel.isStoreAccount)
|
||||
AppStore.shared.accountType = ""
|
||||
AppStore.shared.session.accountType = .unknown("")
|
||||
}
|
||||
|
||||
func testAccountTypeLabelForScenicAccount() {
|
||||
AppStore.shared.accountType = V9ScenicUser.accountTypeValue
|
||||
AppStore.shared.session.accountType = .scenicUser
|
||||
let viewModel = ProfileViewModel()
|
||||
XCTAssertEqual(viewModel.accountTypeLabel, "景区账号")
|
||||
XCTAssertFalse(viewModel.isStoreAccount)
|
||||
AppStore.shared.accountType = ""
|
||||
AppStore.shared.session.accountType = .unknown("")
|
||||
}
|
||||
|
||||
func testApplyLocalProfileUpdateSyncsNicknameAndAvatar() {
|
||||
AppStore.shared.userName = "全局昵称"
|
||||
AppStore.shared.avatar = "https://cdn.example.com/new.jpg"
|
||||
AppStore.shared.session.userName = "全局昵称"
|
||||
AppStore.shared.session.avatar = "https://cdn.example.com/new.jpg"
|
||||
|
||||
let viewModel = ProfileViewModel()
|
||||
viewModel.applyLocalProfileUpdate()
|
||||
@ -70,7 +70,7 @@ final class ProfileViewModelTests: XCTestCase {
|
||||
XCTAssertEqual(viewModel.displayNickname, "全局昵称")
|
||||
XCTAssertEqual(viewModel.displayAvatarURL, "https://cdn.example.com/new.jpg")
|
||||
|
||||
AppStore.shared.userName = ""
|
||||
AppStore.shared.avatar = ""
|
||||
AppStore.shared.session.userName = ""
|
||||
AppStore.shared.session.avatar = ""
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,9 +118,9 @@ final class ScenicQueueFeatureTests: XCTestCase {
|
||||
let defaults = UserDefaults(suiteName: "ScenicQueueFeatureTests.settings")!
|
||||
defaults.removePersistentDomain(forName: "ScenicQueueFeatureTests.settings")
|
||||
let store = AppStore(defaults: defaults)
|
||||
store.userId = "u1"
|
||||
store.accountType = "photog"
|
||||
store.currentScenicId = 11
|
||||
store.session.userId = "u1"
|
||||
store.session.accountType = .unknown("photog")
|
||||
store.session.currentScenicId = 11
|
||||
let api = MockScenicQueueAPI()
|
||||
let tts = MockScenicQueueTTSManager()
|
||||
let viewModel = ScenicQueueSettingsViewModel(appStore: store, ttsManager: tts)
|
||||
@ -165,20 +165,20 @@ final class ScenicQueueFeatureTests: XCTestCase {
|
||||
XCTAssertEqual(request?.missCallRequeueOffset, 2)
|
||||
XCTAssertEqual(request?.autoCallNextCount, 3)
|
||||
XCTAssertEqual(request?.voiceBroadcasts.map(\.content), ["请准备拍摄", "第二条"])
|
||||
XCTAssertEqual(store.scenicQueuePunchSpotName, "东门打卡点")
|
||||
XCTAssertEqual(store.scenicQueueSettingsSnapshot()?.shootMinute, 9)
|
||||
XCTAssertEqual(store.scenicQueue.punchSpotName, "东门打卡点")
|
||||
XCTAssertEqual(store.scenicQueue.settingsSnapshot()?.shootMinute, 9)
|
||||
}
|
||||
|
||||
func testMainViewModelGatePagingActionsTTSAndSocketFiltering() async {
|
||||
let defaults = UserDefaults(suiteName: "ScenicQueueFeatureTests.main")!
|
||||
defaults.removePersistentDomain(forName: "ScenicQueueFeatureTests.main")
|
||||
let store = AppStore(defaults: defaults)
|
||||
store.userId = "9"
|
||||
store.accountType = "photog"
|
||||
store.currentScenicId = 11
|
||||
store.scenicQueuePunchSpotId = "22"
|
||||
store.scenicQueuePunchSpotName = "东门打卡点"
|
||||
store.saveScenicQueueSettingsSnapshot(
|
||||
store.session.userId = "9"
|
||||
store.session.accountType = .unknown("photog")
|
||||
store.session.currentScenicId = 11
|
||||
store.scenicQueue.punchSpotId = "22"
|
||||
store.scenicQueue.punchSpotName = "东门打卡点"
|
||||
store.scenicQueue.saveSettingsSnapshot(
|
||||
ScenicQueueSettingsSnapshot(showStartShootingButton: true, quickCallButtonEnabled: true, prepareCallButtonEnabled: true)
|
||||
)
|
||||
let api = MockScenicQueueAPI()
|
||||
@ -248,28 +248,28 @@ final class ScenicQueueFeatureTests: XCTestCase {
|
||||
defaults.removePersistentDomain(forName: "ScenicQueueFeatureTests.store")
|
||||
let store = AppStore(defaults: defaults)
|
||||
|
||||
store.userId = "1"
|
||||
store.accountType = "photog"
|
||||
store.scenicQueuePunchSpotId = "22"
|
||||
store.scenicQueuePunchSpotName = "东门打卡点"
|
||||
store.scenicQueueRemark = "播报"
|
||||
store.saveScenicQueueSettingsSnapshot(ScenicQueueSettingsSnapshot(shootMinute: 3))
|
||||
store.saveScenicQueuePresetVoices(["a", "b", "c", "d", "e", "f"])
|
||||
store.session.userId = "1"
|
||||
store.session.accountType = .unknown("photog")
|
||||
store.scenicQueue.punchSpotId = "22"
|
||||
store.scenicQueue.punchSpotName = "东门打卡点"
|
||||
store.scenicQueue.remark = "播报"
|
||||
store.scenicQueue.saveSettingsSnapshot(ScenicQueueSettingsSnapshot(shootMinute: 3))
|
||||
store.scenicQueue.savePresetVoices(["a", "b", "c", "d", "e", "f"])
|
||||
|
||||
store.userId = "2"
|
||||
store.accountType = "photog"
|
||||
XCTAssertEqual(store.scenicQueuePunchSpotId, "")
|
||||
XCTAssertNil(store.scenicQueueSettingsSnapshot())
|
||||
store.session.userId = "2"
|
||||
store.session.accountType = .unknown("photog")
|
||||
XCTAssertEqual(store.scenicQueue.punchSpotId, "")
|
||||
XCTAssertNil(store.scenicQueue.settingsSnapshot())
|
||||
|
||||
store.userId = "1"
|
||||
store.accountType = "photog"
|
||||
XCTAssertEqual(store.scenicQueuePunchSpotId, "22")
|
||||
XCTAssertEqual(store.scenicQueuePresetVoices(), ["a", "b", "c", "d", "e"])
|
||||
store.session.userId = "1"
|
||||
store.session.accountType = .unknown("photog")
|
||||
XCTAssertEqual(store.scenicQueue.punchSpotId, "22")
|
||||
XCTAssertEqual(store.scenicQueue.presetVoices(), ["a", "b", "c", "d", "e"])
|
||||
store.logout()
|
||||
store.userId = "1"
|
||||
store.accountType = "photog"
|
||||
XCTAssertEqual(store.scenicQueuePunchSpotId, "")
|
||||
XCTAssertNil(store.scenicQueueSettingsSnapshot())
|
||||
store.session.userId = "1"
|
||||
store.session.accountType = .unknown("photog")
|
||||
XCTAssertEqual(store.scenicQueue.punchSpotId, "")
|
||||
XCTAssertNil(store.scenicQueue.settingsSnapshot())
|
||||
}
|
||||
|
||||
func testAliyunNLSTokenProviderBuildsSignedCreateTokenRequest() async throws {
|
||||
@ -321,8 +321,8 @@ final class ScenicQueueFeatureTests: XCTestCase {
|
||||
let defaults = UserDefaults(suiteName: "ScenicQueueFeatureTests.offlineTTS")!
|
||||
defaults.removePersistentDomain(forName: "ScenicQueueFeatureTests.offlineTTS")
|
||||
let store = AppStore(defaults: defaults)
|
||||
store.userId = "u1"
|
||||
store.accountType = "photog"
|
||||
store.session.userId = "u1"
|
||||
store.session.accountType = .unknown("photog")
|
||||
let tts = MockScenicQueueTTSManager()
|
||||
let viewModel = ScenicQueueSettingsViewModel(appStore: store, ttsManager: tts)
|
||||
var messages: [String] = []
|
||||
@ -330,7 +330,7 @@ final class ScenicQueueFeatureTests: XCTestCase {
|
||||
|
||||
viewModel.setUseOfflineTTS(true)
|
||||
|
||||
XCTAssertFalse(store.scenicQueueUseOfflineTTS)
|
||||
XCTAssertFalse(store.scenicQueue.useOfflineTTS)
|
||||
XCTAssertEqual(messages.last, "离线语音资源未安装")
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ final class ScenicSelectionViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSearchFiltersByNameAndAddress() {
|
||||
appStore.saveRoleScenicList([
|
||||
appStore.permissions.saveRoleScenicList([
|
||||
ScenicInfo(id: 1, name: "西湖景区", status: 1, location: ScenicLocationInfo(lng: 120.1, lat: 30.2, address: "杭州西湖"), coverImg: nil),
|
||||
ScenicInfo(id: 2, name: "灵隐寺", status: 1, location: ScenicLocationInfo(lng: 120.2, lat: 30.3, address: "杭州灵隐"), coverImg: nil),
|
||||
])
|
||||
@ -43,12 +43,12 @@ final class ScenicSelectionViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testNearestTagWhenLocationProvided() {
|
||||
appStore.saveRoleScenicList([
|
||||
appStore.permissions.saveRoleScenicList([
|
||||
ScenicInfo(id: 1, name: "近", status: 1, location: ScenicLocationInfo(lng: 116.40, lat: 39.91, address: "A"), coverImg: nil),
|
||||
ScenicInfo(id: 2, name: "远", status: 1, location: ScenicLocationInfo(lng: 116.50, lat: 39.95, address: "B"), coverImg: nil),
|
||||
])
|
||||
let items = ScenicSpotDisplayMapper.buildItems(
|
||||
scenicList: appStore.roleScenicList(),
|
||||
scenicList: appStore.permissions.roleScenicList(),
|
||||
searchQuery: "",
|
||||
currentLat: 39.9087,
|
||||
currentLng: 116.3975,
|
||||
@ -58,7 +58,7 @@ final class ScenicSelectionViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testClosedStatusTag() {
|
||||
appStore.saveRoleScenicList([
|
||||
appStore.permissions.saveRoleScenicList([
|
||||
ScenicInfo(id: 1, name: "停业", status: 2, location: nil, coverImg: nil),
|
||||
])
|
||||
let viewModel = ScenicSelectionViewModel(appStore: appStore)
|
||||
@ -68,7 +68,7 @@ final class ScenicSelectionViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSelectSpotWritesAppStore() {
|
||||
appStore.saveRoleScenicList([
|
||||
appStore.permissions.saveRoleScenicList([
|
||||
ScenicInfo(id: 5, name: "测试景区", status: 1),
|
||||
])
|
||||
let viewModel = ScenicSelectionViewModel(appStore: appStore)
|
||||
@ -78,8 +78,8 @@ final class ScenicSelectionViewModelTests: XCTestCase {
|
||||
viewModel.loadFromLocal()
|
||||
viewModel.selectSpot(viewModel.spots[0])
|
||||
|
||||
XCTAssertEqual(appStore.currentScenicId, 5)
|
||||
XCTAssertEqual(appStore.currentScenicName, "测试景区")
|
||||
XCTAssertEqual(appStore.session.currentScenicId, 5)
|
||||
XCTAssertEqual(appStore.session.currentScenicName, "测试景区")
|
||||
XCTAssertTrue(completed)
|
||||
XCTAssertTrue(viewModel.spots.first?.isSelected == true)
|
||||
}
|
||||
|
||||
@ -11,10 +11,10 @@ import XCTest
|
||||
final class StatisticsViewModelTests: XCTestCase {
|
||||
|
||||
override func tearDown() {
|
||||
AppStore.shared.roleCode = ""
|
||||
AppStore.shared.roleName = ""
|
||||
AppStore.shared.currentScenicId = 0
|
||||
AppStore.shared.currentStoreId = 0
|
||||
AppStore.shared.session.roleCode = ""
|
||||
AppStore.shared.session.roleName = ""
|
||||
AppStore.shared.session.currentScenicId = 0
|
||||
AppStore.shared.session.currentStoreId = 0
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ final class StatisticsViewModelTests: XCTestCase {
|
||||
{"code":100000,"msg":"success","data":{"total":0,"data":[]}}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
AppStore.shared.currentScenicId = 10
|
||||
AppStore.shared.session.currentScenicId = 10
|
||||
let session = MockURLSession(responses: [listJSON])
|
||||
let api = StatisticsAPI(client: APIClient(environment: .testing, session: session))
|
||||
let viewModel = StatisticsViewModel()
|
||||
@ -93,7 +93,7 @@ final class StatisticsViewModelTests: XCTestCase {
|
||||
{"code":100000,"msg":"success","data":{"total":1,"data":[{"date":"2025-12-09","order_count":2,"order_price":"50","refund":"10","received":"90"}]}}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
AppStore.shared.currentScenicId = 10
|
||||
AppStore.shared.session.currentScenicId = 10
|
||||
let session = MockURLSession(responses: [summaryJSON, listJSON])
|
||||
let api = StatisticsAPI(client: APIClient(environment: .testing, session: session))
|
||||
let viewModel = StatisticsViewModel()
|
||||
@ -120,8 +120,8 @@ final class StatisticsViewModelTests: XCTestCase {
|
||||
{"code":100000,"msg":"success","data":{"total":0,"data":[]}}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
AppStore.shared.roleCode = AppRoleCode.scenicAdmin.rawValue
|
||||
AppStore.shared.currentScenicId = 20
|
||||
AppStore.shared.session.roleCode = AppRoleCode.scenicAdmin.rawValue
|
||||
AppStore.shared.session.currentScenicId = 20
|
||||
let session = MockURLSession(responses: [summaryJSON, listJSON])
|
||||
let api = StatisticsAPI(client: APIClient(environment: .testing, session: session))
|
||||
let viewModel = StatisticsViewModel()
|
||||
@ -141,8 +141,8 @@ final class StatisticsViewModelTests: XCTestCase {
|
||||
{"code":100000,"msg":"success","data":{"total":0,"data":[]}}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
AppStore.shared.roleCode = AppRoleCode.storeAdmin.rawValue
|
||||
AppStore.shared.currentStoreId = 30
|
||||
AppStore.shared.session.roleCode = AppRoleCode.storeAdmin.rawValue
|
||||
AppStore.shared.session.currentStoreId = 30
|
||||
let session = MockURLSession(responses: [summaryJSON, listJSON])
|
||||
let api = StatisticsAPI(client: APIClient(environment: .testing, session: session))
|
||||
let viewModel = StatisticsViewModel()
|
||||
@ -156,8 +156,8 @@ final class StatisticsViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testStoreAdminWithoutStoreIdDoesNotRequest() async {
|
||||
AppStore.shared.roleCode = AppRoleCode.storeAdmin.rawValue
|
||||
AppStore.shared.currentStoreId = 0
|
||||
AppStore.shared.session.roleCode = AppRoleCode.storeAdmin.rawValue
|
||||
AppStore.shared.session.currentStoreId = 0
|
||||
let session = MockURLSession(responses: [])
|
||||
let api = StatisticsAPI(client: APIClient(environment: .testing, session: session))
|
||||
let viewModel = StatisticsViewModel()
|
||||
@ -175,7 +175,7 @@ final class StatisticsViewModelTests: XCTestCase {
|
||||
{"code":100000,"msg":"success","data":{"total":1,"data":[{"date":"2025-12-09","order_count":1,"order_price":"10","refund":"0","received":"10"}]}}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
AppStore.shared.currentScenicId = 10
|
||||
AppStore.shared.session.currentScenicId = 10
|
||||
let session = MockURLSession(responses: [page1JSON])
|
||||
let api = StatisticsAPI(client: APIClient(environment: .testing, session: session))
|
||||
let viewModel = StatisticsViewModel()
|
||||
|
||||
@ -17,7 +17,7 @@ final class TaskAddViewModelTests: XCTestCase {
|
||||
defaults = UserDefaults(suiteName: "TaskAddViewModelTests")!
|
||||
defaults.removePersistentDomain(forName: "TaskAddViewModelTests")
|
||||
appStore = AppStore(defaults: defaults)
|
||||
appStore.currentScenicId = 10
|
||||
appStore.session.currentScenicId = 10
|
||||
}
|
||||
|
||||
func testValidateRequiresTaskName() async {
|
||||
|
||||
@ -17,7 +17,7 @@ final class TaskOrderSelectViewModelTests: XCTestCase {
|
||||
defaults = UserDefaults(suiteName: "TaskOrderSelectViewModelTests")!
|
||||
defaults.removePersistentDomain(forName: "TaskOrderSelectViewModelTests")
|
||||
appStore = AppStore(defaults: defaults)
|
||||
appStore.currentScenicId = 10
|
||||
appStore.session.currentScenicId = 10
|
||||
}
|
||||
|
||||
func testToggleSelectionSelectsAndDeselects() {
|
||||
|
||||
@ -86,9 +86,9 @@ final class TravelAlbumOTGCoreTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testOSSPunchPointObjectKeyUsesAndroidUserPath() {
|
||||
let previousUserId = AppStore.shared.userId
|
||||
AppStore.shared.userId = "u900"
|
||||
defer { AppStore.shared.userId = previousUserId }
|
||||
let previousUserId = AppStore.shared.session.userId
|
||||
AppStore.shared.session.userId = "u900"
|
||||
defer { AppStore.shared.session.userId = previousUserId }
|
||||
|
||||
var calendar = Calendar(identifier: .gregorian)
|
||||
calendar.timeZone = TimeZone(secondsFromGMT: 0)!
|
||||
|
||||
@ -782,10 +782,10 @@ private func makeOTGTestDefaults() -> UserDefaults {
|
||||
|
||||
@MainActor
|
||||
private func prepareAppStoreForOTGTests() {
|
||||
AppStore.shared.userId = "u1"
|
||||
AppStore.shared.accountType = "photog"
|
||||
AppStore.shared.currentScenicId = 11
|
||||
AppStore.shared.currentStoreId = 22
|
||||
AppStore.shared.session.userId = "u1"
|
||||
AppStore.shared.session.accountType = .unknown("photog")
|
||||
AppStore.shared.session.currentScenicId = 11
|
||||
AppStore.shared.session.currentStoreId = 22
|
||||
}
|
||||
|
||||
private func waitUntil(
|
||||
|
||||
@ -30,7 +30,7 @@ final class UmengBootstrapTests: XCTestCase {
|
||||
UmengBootstrap.setConfigureHandlerForTesting { _, _ in
|
||||
callCount += 1
|
||||
}
|
||||
appStore.privacyAgreementAccepted = false
|
||||
appStore.session.privacyAgreementAccepted = false
|
||||
|
||||
XCTAssertFalse(UmengBootstrap.configureIfNeeded(appStore: appStore))
|
||||
XCTAssertFalse(UmengBootstrap.isConfigured)
|
||||
@ -42,7 +42,7 @@ final class UmengBootstrapTests: XCTestCase {
|
||||
UmengBootstrap.setConfigureHandlerForTesting { appKey, channel in
|
||||
calls.append((appKey, channel))
|
||||
}
|
||||
appStore.privacyAgreementAccepted = true
|
||||
appStore.session.privacyAgreementAccepted = true
|
||||
|
||||
XCTAssertTrue(UmengBootstrap.configureIfNeeded(appStore: appStore))
|
||||
XCTAssertTrue(UmengBootstrap.isConfigured)
|
||||
|
||||
Reference in New Issue
Block a user