接入友盟统计与 U-APM,并优化宣传页打卡点标签交互。

友盟 SDK 改为隐私同意后再初始化;宣传页标签新增滑块位移动画,超过 5 个时选中项自动滚动居中。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-03 09:23:15 +08:00
parent c7f557f361
commit 00c3cd0a93
134 changed files with 6555 additions and 590 deletions

View File

@ -45,6 +45,60 @@ final class StorageTests: XCTestCase {
XCTAssertTrue(store.loadPrivacyAgreementAccepted())
}
///
func testAppPreferencesStoreClearRemovesPrivacyAgreement() {
let defaults = makeIsolatedDefaults()
let store = AppPreferencesStore(defaults: defaults)
store.savePrivacyAgreementAccepted(true)
store.clear()
XCTAssertFalse(store.loadPrivacyAgreementAccepted())
}
///
@MainActor
func testAuthSessionCoordinatorPrivacyPersistenceTriggersCallback() {
let defaults = makeIsolatedDefaults()
let store = AppPreferencesStore(defaults: defaults)
var persistedValues: [Bool] = []
let coordinator = AuthSessionCoordinator(
tokenStore: SessionTokenStore(defaults: defaults, key: "session.token.privacy.test"),
snapshotStore: AccountSnapshotStore(defaults: defaults, key: "account.snapshot.privacy.test"),
preferencesStore: store,
onPrivacyAgreementPersisted: { persistedValues.append($0) }
)
coordinator.savePrivacyAgreementAccepted(true)
XCTAssertTrue(store.loadPrivacyAgreementAccepted())
XCTAssertEqual(persistedValues, [true])
}
/// 退
@MainActor
func testAuthSessionCoordinatorLogoutKeepsPrivacyAgreement() {
let defaults = makeIsolatedDefaults()
let store = AppPreferencesStore(defaults: defaults)
let coordinator = AuthSessionCoordinator(
tokenStore: SessionTokenStore(defaults: defaults, key: "session.token.logout.test"),
snapshotStore: AccountSnapshotStore(defaults: defaults, key: "account.snapshot.logout.test"),
preferencesStore: store
)
coordinator.savePrivacyAgreementAccepted(true)
coordinator.logout(
appSession: AppSession(),
accountContext: AccountContext(),
permissionContext: PermissionContext(),
scenicSpotContext: ScenicSpotContext(),
appRouter: AppRouter(),
toastCenter: ToastCenter()
)
XCTAssertTrue(store.loadPrivacyAgreementAccepted())
}
///
func testAccountSnapshotStorePersistsAccountContextSnapshot() {
let defaults = makeIsolatedDefaults()

View File

@ -0,0 +1,118 @@
//
// UMengSDKBootstrapTests.swift
// suixinkanTests
//
// Created by Codex on 2026/7/2.
//
import XCTest
@testable import suixinkan
@MainActor
/// SDK APM
final class UMengSDKBootstrapTests: XCTestCase {
///
func testConfigureIfAllowedSkipsWhenPrivacyNotAccepted() {
let preferences = AppPreferencesStore(defaults: makeIsolatedDefaults())
let configurator = RecordingUMengConfigurator()
let bootstrap = UMengSDKBootstrap(configurator: configurator)
let didConfigure = bootstrap.configureIfAllowed(
preferencesStore: preferences,
appKey: "app-key",
channel: "App Store"
)
XCTAssertFalse(didConfigure)
XCTAssertTrue(configurator.calls.isEmpty)
}
/// AppKey
func testConfigureIfAllowedSkipsWhenAppKeyIsBlank() {
let preferences = AppPreferencesStore(defaults: makeIsolatedDefaults())
preferences.savePrivacyAgreementAccepted(true)
let configurator = RecordingUMengConfigurator()
let bootstrap = UMengSDKBootstrap(configurator: configurator)
let didConfigure = bootstrap.configureIfAllowed(
preferencesStore: preferences,
appKey: " ",
channel: "App Store"
)
XCTAssertFalse(didConfigure)
XCTAssertTrue(configurator.calls.isEmpty)
}
/// AppKey
func testConfigureIfAllowedConfiguresOnceWhenAllowed() {
let preferences = AppPreferencesStore(defaults: makeIsolatedDefaults())
preferences.savePrivacyAgreementAccepted(true)
let configurator = RecordingUMengConfigurator()
let bootstrap = UMengSDKBootstrap(configurator: configurator)
let firstResult = bootstrap.configureIfAllowed(
preferencesStore: preferences,
appKey: " app-key ",
channel: " App Store "
)
let secondResult = bootstrap.configureIfAllowed(
preferencesStore: preferences,
appKey: "app-key",
channel: "App Store"
)
XCTAssertTrue(firstResult)
XCTAssertFalse(secondResult)
XCTAssertEqual(configurator.calls.count, 1)
XCTAssertEqual(configurator.calls.first?.appKey, "app-key")
XCTAssertEqual(configurator.calls.first?.channel, "App Store")
}
/// APM OOM
func testConfigureIfAllowedUsesMinimumComplianceAPMOptions() {
let preferences = AppPreferencesStore(defaults: makeIsolatedDefaults())
preferences.savePrivacyAgreementAccepted(true)
let configurator = RecordingUMengConfigurator()
let bootstrap = UMengSDKBootstrap(configurator: configurator)
bootstrap.configureIfAllowed(
preferencesStore: preferences,
appKey: "app-key",
channel: nil
)
XCTAssertEqual(configurator.calls.first?.options, .minimumCompliance)
XCTAssertTrue(UMengAPMOptions.minimumCompliance.crashAndBlockMonitorEnable)
XCTAssertTrue(UMengAPMOptions.minimumCompliance.launchMonitorEnable)
XCTAssertFalse(UMengAPMOptions.minimumCompliance.networkEnable)
XCTAssertFalse(UMengAPMOptions.minimumCompliance.memMonitorEnable)
XCTAssertFalse(UMengAPMOptions.minimumCompliance.oomMonitorEnable)
XCTAssertFalse(UMengAPMOptions.minimumCompliance.logCollectEnable)
}
private func makeIsolatedDefaults() -> UserDefaults {
let suiteName = "UMengSDKBootstrapTests.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suiteName)!
defaults.removePersistentDomain(forName: suiteName)
return defaults
}
}
///
@MainActor
private final class RecordingUMengConfigurator: UMengSDKConfiguring {
private(set) var calls: [Call] = []
///
func configure(appKey: String, channel: String?, options: UMengAPMOptions) {
calls.append(Call(appKey: appKey, channel: channel, options: options))
}
///
struct Call: Equatable {
let appKey: String
let channel: String?
let options: UMengAPMOptions
}
}