补充 Auth/Tasks/ScenicPermission/Live 单元测试,并完善 UI Test 登录与会话稳定性。
移除 LoginViewModel 硬编码账号,扩展登录流程测试覆盖;调整 ZLoginSmokeUITests 执行顺序与路由直达逻辑,避免清空 Keychain 影响其它用例。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -293,6 +293,90 @@ final class LiveViewModelTests: XCTestCase {
|
||||
await XCTAssertThrowsErrorAsync(try await missingURLViewModel.prepare())
|
||||
XCTAssertEqual(missingURLViewModel.errorMessage, LivePushReadinessError.missingPushURL.localizedDescription)
|
||||
}
|
||||
|
||||
/// 测试播放地址解析会跳过 RTMP/FLV 并优先选择可用 HTTP 地址。
|
||||
func testLivePlaybackResolverCandidatePriority() {
|
||||
let live = LiveEntity(
|
||||
playUrl: "rtmp://pull.example.com/live/1",
|
||||
pullUrl: "https://cdn.example.com/live/1.flv",
|
||||
hlsUrl: "https://cdn.example.com/live/1.m3u8",
|
||||
liveUrl: "https://cdn.example.com/live/1.mp4"
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
LivePlaybackURLResolver.playableURL(from: live)?.absoluteString,
|
||||
"https://cdn.example.com/live/1.m3u8"
|
||||
)
|
||||
XCTAssertEqual(
|
||||
LivePlaybackURLResolver.playableURL(from: ["rtmp://a", "https://cdn.example.com/fallback.mp4"])?.absoluteString,
|
||||
"https://cdn.example.com/fallback.mp4"
|
||||
)
|
||||
}
|
||||
|
||||
/// 测试播放器可从 LiveEntity 初始化并支持 reload。
|
||||
func testLivePlaybackViewModelLoadLiveEntityAndReload() {
|
||||
let live = LiveEntity(hlsUrl: "https://cdn.example.com/live/1.m3u8")
|
||||
let viewModel = LivePlaybackViewModel(live: live)
|
||||
|
||||
XCTAssertEqual(viewModel.playableURL?.absoluteString, "https://cdn.example.com/live/1.m3u8")
|
||||
XCTAssertNotNil(viewModel.player)
|
||||
|
||||
viewModel.play()
|
||||
XCTAssertTrue(viewModel.isPlaying)
|
||||
|
||||
viewModel.reload()
|
||||
XCTAssertFalse(viewModel.isPlaying)
|
||||
XCTAssertNotNil(viewModel.player)
|
||||
XCTAssertEqual(viewModel.playableURL?.absoluteString, "https://cdn.example.com/live/1.m3u8")
|
||||
}
|
||||
|
||||
/// 测试无效推流地址配置会提示格式错误。
|
||||
func testLivePushReadinessInvalidPushURL() {
|
||||
let viewModel = LivePushReadinessViewModel(
|
||||
permissionProvider: LivePermissionMock(camera: .granted, microphone: .granted),
|
||||
networkMonitor: LiveNetworkMock(state: .wifi),
|
||||
adapter: LivePushAdapterMock(available: true)
|
||||
)
|
||||
|
||||
viewModel.configure(pushURL: "not-a-valid-url")
|
||||
|
||||
XCTAssertEqual(viewModel.errorMessage, LivePushReadinessError.invalidPushURL.localizedDescription)
|
||||
}
|
||||
|
||||
/// 测试可用 SDK 时 prepare、start、stop 和 dispose 的完整流程。
|
||||
func testLivePushReadinessStartStopAndDispose() async throws {
|
||||
let permissions = LivePermissionMock(camera: .granted, microphone: .granted)
|
||||
let network = LiveNetworkMock(state: .wifi)
|
||||
let adapter = LivePushAdapterMock(available: true)
|
||||
let viewModel = LivePushReadinessViewModel(permissionProvider: permissions, networkMonitor: network, adapter: adapter)
|
||||
|
||||
viewModel.configure(pushURL: "rtmp://push.example.com/live/1")
|
||||
viewModel.startMonitoring()
|
||||
await viewModel.refreshPermissions()
|
||||
|
||||
try viewModel.runDiagnostics()
|
||||
try await viewModel.prepare()
|
||||
try await viewModel.startPush()
|
||||
|
||||
XCTAssertTrue(adapter.prepareCalled)
|
||||
XCTAssertTrue(adapter.startCalled)
|
||||
XCTAssertTrue(viewModel.running)
|
||||
XCTAssertTrue(viewModel.prepared)
|
||||
|
||||
await viewModel.stopPush()
|
||||
XCTAssertTrue(adapter.stopCalled)
|
||||
XCTAssertFalse(viewModel.running)
|
||||
|
||||
await viewModel.dispose()
|
||||
XCTAssertFalse(viewModel.prepared)
|
||||
}
|
||||
|
||||
/// 测试权限和网络状态展示文案。
|
||||
func testLivePushStateDisplayText() {
|
||||
XCTAssertEqual(LivePushPermissionState.granted.displayText, "已授权")
|
||||
XCTAssertEqual(LivePushNetworkState.wifi.displayText, "Wi-Fi")
|
||||
XCTAssertEqual(LivePushNetworkState.unavailable.displayText, "网络不可用")
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
|
||||
Reference in New Issue
Block a user