Wait on durable state in three flaky test suites

These three suites assert against state that is still being loaded or has
already moved on, so they fail whenever a busy runner stretches the window.
All three passed on CI only because `-retry-tests-on-failure` re-ran them.

- MediaUploadPreviewScreenViewModelTests: the processing failure tests treated
  a one second `deferFailure` window as if it also meant the send had finished.
  Preprocessing the valid files outlasts it, so wait for the interaction to be
  re-enabled instead.
- SecurityAndPrivacyScreenViewModelTests: the screen loads its parent spaces and
  its room directory visibility in two independent tasks, and `isSaveDisabled`
  needs both. Every wait now gates on the visibility having landed too.
- RoomNotificationSettingsScreenViewModelTests: the delete tests waited on a
  transient `deletingCustomSetting` flag that the observation coalesces away
  under load. They now wait on the dismiss action and the alert.
This commit is contained in:
Mauro Romito
2026-07-28 18:52:10 +02:00
committed by Mauro
parent c296eb0e43
commit 58fb7c4fcb
3 changed files with 39 additions and 28 deletions
@@ -254,22 +254,16 @@ struct RoomNotificationSettingsScreenViewModelTests {
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill()
var actionSent: RoomNotificationSettingsScreenViewModelAction?
viewModel.actions
.sink { action in
actionSent = action
}
.store(in: &cancellables)
let deferredViewState = deferFulfillment(viewModel.context.observe(\.viewState.deletingCustomSetting),
transitionValues: [false, true, false])
// The `deletingCustomSetting` flag is only raised for the duration of the call, which is short enough
// that the observation can coalesce it away. Wait on the outcome instead.
let deferredDismiss = deferFulfillment(viewModel.actions) { $0 == .dismiss }
viewModel.context.send(viewAction: .deleteCustomSettingTapped)
try await deferredViewState.fulfill()
// the `dismiss` action must have been sent
#expect(actionSent == .dismiss)
try await deferredDismiss.fulfill()
#expect(!viewModel.context.viewState.deletingCustomSetting)
// `restoreDefaultNotificationMode` should have been called
#expect(notificationSettingsProxyMock.restoreDefaultNotificationModeRoomIdCalled)
#expect(notificationSettingsProxyMock.restoreDefaultNotificationModeRoomIdReceivedInvocations == [roomProxyMock.id])
@@ -299,15 +293,17 @@ struct RoomNotificationSettingsScreenViewModelTests {
.store(in: &cancellables)
#expect(!viewModel.context.viewState.deletingCustomSetting)
let deferredViewState = deferFulfillment(viewModel.context.observe(\.viewState.deletingCustomSetting),
transitionValues: [true, false])
// The `deletingCustomSetting` flag is only raised for the duration of the call, which is short enough
// that the observation can coalesce it away. Wait on the alert instead, it is raised before the flag
// is lowered again.
let deferredAlert = deferFulfillment(viewModel.context.observe(\.viewState.bindings.alertInfo)) { $0?.id == .restoreDefaultFailed }
viewModel.context.send(viewAction: .deleteCustomSettingTapped)
try await deferredViewState.fulfill()
// an alert is expected
#expect(viewModel.context.alertInfo?.id == .restoreDefaultFailed)
try await deferredAlert.fulfill()
#expect(!viewModel.context.viewState.deletingCustomSetting)
// the `dismiss` action must not have been sent
#expect(actionSent == nil)
}