Files
damus/damusTests/AutoSaveViewModelTests.swift
Daniel D’Aquino 9eda7e5886 Improve draft saving mechanism to start timer on first edit
Modified AutoSaveViewModel.needsSaving() to not reset the timer if already
counting down. This ensures the timer starts when the user begins typing and
continues counting even if they keep typing continuously, leading to auto-save
every few seconds instead of waiting for the user to stop typing.

Added automated tests for the new behavior.

Fixes the issue where drafts would only save after user stops typing,
potentially leading to data loss if the app is closed too quickly.

Closes: https://github.com/damus-io/damus/issues/3164
Changelog-Changed: Improved draft saving feature to prevent data loss if app closes too quickly
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-12-10 16:12:10 -08:00

119 lines
3.6 KiB
Swift

//
// AutoSaveViewModelTests.swift
// damusTests
//
// Created by Daniel D'Aquino on 2025-12-10.
//
import XCTest
@testable import damus
@MainActor
final class AutoSaveViewModelTests: XCTestCase {
func testTimerStartsOnFirstEdit() async throws {
// Given
var saveCount = 0
let viewModel = AutoSaveIndicatorView.AutoSaveViewModel(
save: { saveCount += 1 },
saveDelay: 2
)
// When - user starts typing
viewModel.needsSaving()
// Then - timer should be started
if case .needsSaving(let secondsRemaining) = viewModel.savedState {
XCTAssertEqual(secondsRemaining, 2)
} else {
XCTFail("Expected needsSaving state")
}
}
func testTimerDoesNotResetOnContinuousTyping() async throws {
// Given
var saveCount = 0
let viewModel = AutoSaveIndicatorView.AutoSaveViewModel(
save: { saveCount += 1 },
saveDelay: 3
)
// When - user starts typing
viewModel.needsSaving()
// Verify initial state
if case .needsSaving(let secondsRemaining) = viewModel.savedState {
XCTAssertEqual(secondsRemaining, 3)
} else {
XCTFail("Expected needsSaving state")
}
// Simulate timer countdown by waiting a bit
try await Task.sleep(nanoseconds: 1_500_000_000) // 1.5 seconds
// When - user continues typing (timer should be around 1-2 seconds now)
viewModel.needsSaving()
// Then - timer should NOT reset to 3 seconds
if case .needsSaving(let secondsRemaining) = viewModel.savedState {
XCTAssertLessThan(secondsRemaining, 3, "Timer should not reset on continuous typing")
} else {
XCTFail("Expected needsSaving state")
}
}
func testTimerRestartsAfterSave() async throws {
// Given
var saveCount = 0
let viewModel = AutoSaveIndicatorView.AutoSaveViewModel(
save: {
saveCount += 1
},
saveDelay: 1
)
// When - user starts typing
viewModel.needsSaving()
// Wait for save to complete
try await Task.sleep(nanoseconds: 2_000_000_000) // 2 seconds
// Then - should have saved
XCTAssertEqual(saveCount, 1)
XCTAssertEqual(viewModel.savedState, .saved)
// When - user types again after save
viewModel.needsSaving()
// Then - timer should start again
if case .needsSaving(let secondsRemaining) = viewModel.savedState {
XCTAssertEqual(secondsRemaining, 1)
} else {
XCTFail("Expected needsSaving state after typing post-save")
}
}
func testAutoSaveEveryFewSecondsWithContinuousTyping() async throws {
// Given
var saveCount = 0
let viewModel = AutoSaveIndicatorView.AutoSaveViewModel(
save: {
saveCount += 1
},
saveDelay: 1
)
// When - user starts typing
viewModel.needsSaving()
// Simulate continuous typing every 0.5 seconds for 5 seconds
for _ in 0..<10 {
try await Task.sleep(nanoseconds: 500_000_000) // 0.5 seconds
viewModel.needsSaving()
}
// Then - should have saved multiple times
XCTAssertGreaterThan(saveCount, 1, "Should auto-save multiple times with continuous typing")
}
}