Fix various flakey tests.

This commit is contained in:
Doug 2024-09-19 11:03:26 +01:00 committed by Doug
parent d2d3b28dc7
commit 4996137006
16 changed files with 87 additions and 66 deletions

View File

@ -31,42 +31,7 @@ struct SwipeRightAction<Label: View>: ViewModifier {
content
.offset(x: xOffset, y: 0.0)
.animation(.interactiveSpring().speed(0.5), value: xOffset)
.gesture(DragGesture()
.updating($dragGestureActive) { _, state, _ in
// Available actions should be computed on the fly so we use a gesture state change
// to ask whether the move should be started or not.
state = true
}
.onChanged { value in
guard canStartAction else {
return
}
// We want to add a spring like behavior to the drag in which the view
// moves slower the more it's dragged. We use a circular easing function
// to generate those values up to the `swipeThreshold`
// The final translation will be between 0 and `swipeThreshold` with the action being enabled from
// `actionThreshold` onwards
let screenWidthNormalisedTranslation = max(0.0, min(value.translation.width, swipeThreshold)) / swipeThreshold
let easedTranslation = circularEaseOut(screenWidthNormalisedTranslation)
xOffset = easedTranslation * xOffsetThreshold
if xOffset > actionThreshold {
if !hasReachedActionThreshold {
feedbackGenerator.impactOccurred()
hasReachedActionThreshold = true
}
} else {
hasReachedActionThreshold = false
}
}
.onEnded { _ in
if xOffset > actionThreshold {
action()
}
xOffset = 0.0
})
.simultaneousGesture(gesture)
.onChange(of: dragGestureActive) { value in
if value == true {
if shouldStartAction() {
@ -86,6 +51,55 @@ struct SwipeRightAction<Label: View>: ViewModifier {
}
}
private var gesture: some Gesture {
DragGesture()
.updating($dragGestureActive) { _, state, _ in
// Available actions should be computed on the fly so we use a gesture state change
// to ask whether the move should be started or not.
state = true
}
.onChanged { value in
guard canStartAction, value.translation.width > value.translation.height else {
return
}
// Due to https://forums.developer.apple.com/forums/thread/760035 we had to make
// the drag a simultaneous gesture otherwise it was impossible to scroll the timeline.
// Therefore we need to prevent the animation to run if the user is to scrolling vertically.
// It would be nice if we could somehow abort the gesture in this case.
let width: CGFloat = if value.translation.width > abs(value.translation.height) {
value.translation.width
} else {
0.0
}
// We want to add a spring like behaviour to the drag in which the view
// moves slower the more it's dragged. We use a circular easing function
// to generate those values up to the `swipeThreshold`
// The final translation will be between 0 and `swipeThreshold` with the action being enabled from
// `actionThreshold` onwards
let screenWidthNormalisedTranslation = max(0.0, min(width, swipeThreshold)) / swipeThreshold
let easedTranslation = circularEaseOut(screenWidthNormalisedTranslation)
xOffset = easedTranslation * xOffsetThreshold
if xOffset > actionThreshold {
if !hasReachedActionThreshold {
feedbackGenerator.impactOccurred()
hasReachedActionThreshold = true
}
} else {
hasReachedActionThreshold = false
}
}
.onEnded { _ in
if xOffset > actionThreshold {
action()
}
xOffset = 0.0
}
}
/// Used to compute the horizontal translation amount.
/// The more it's dragged the less it moves on a circular ease out curve
private func circularEaseOut(_ value: Double) -> Double {

View File

@ -379,6 +379,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
return
}
await self?.updatePinnedEventIDs()
await self?.updatePermissions()
}
}
.store(in: &cancellables)

View File

@ -380,10 +380,13 @@ class TimelineViewModelTests: XCTestCase {
func testPinnedEvents() async throws {
ServiceLocator.shared.settings.pinningEnabled = true
// Note: We need to start the test with a non-default value so we know the view model has finished the Task.
let roomProxyMock = JoinedRoomProxyMock(.init(name: "",
pinnedEventIDs: .init(["test1"])))
let actionsSubject = PassthroughSubject<JoinedRoomProxyAction, Never>()
roomProxyMock.underlyingActionsPublisher = actionsSubject.eraseToAnyPublisher()
let viewModel = TimelineViewModel(roomProxy: roomProxyMock,
timelineController: MockRoomTimelineController(),
mediaProvider: MockMediaProvider(),
@ -409,9 +412,12 @@ class TimelineViewModelTests: XCTestCase {
func testCanUserPinEvents() async throws {
ServiceLocator.shared.settings.pinningEnabled = true
let roomProxyMock = JoinedRoomProxyMock(.init(name: "", canUserPin: false))
// Note: We need to start the test with the non-default value so we know the view model has finished the Task.
let roomProxyMock = JoinedRoomProxyMock(.init(name: "", canUserPin: true))
let actionsSubject = PassthroughSubject<JoinedRoomProxyAction, Never>()
roomProxyMock.underlyingActionsPublisher = actionsSubject.eraseToAnyPublisher()
let viewModel = TimelineViewModel(roomProxy: roomProxyMock,
timelineController: MockRoomTimelineController(),
mediaProvider: MockMediaProvider(),
@ -423,13 +429,13 @@ class TimelineViewModelTests: XCTestCase {
analyticsService: ServiceLocator.shared.analytics)
var deferred = deferFulfillment(viewModel.context.$viewState) { value in
!value.canCurrentUserPin
value.canCurrentUserPin
}
try await deferred.fulfill()
roomProxyMock.canUserPinOrUnpinUserIDReturnValue = .success(true)
roomProxyMock.canUserPinOrUnpinUserIDReturnValue = .success(false)
deferred = deferFulfillment(viewModel.context.$viewState) { value in
value.canCurrentUserPin
!value.canCurrentUserPin
}
actionsSubject.send(.roomInfoUpdate)
try await deferred.fulfill()