Add 4 new unit test suites for greenfield test plan coverage
Add 87 new tests (384 total) covering ValidationRules/ValidationError, PasswordResetViewModel navigation and client-side validation, WidgetAction Codable/Equatable/accessors, parseDate, and ThemeID enum properties. Updates greenfield CSV for AUTH-012/017/018, NOTIF-006/007, WID-003, THEME-002. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
218
iosApp/CaseraTests/PasswordResetViewModelTests.swift
Normal file
218
iosApp/CaseraTests/PasswordResetViewModelTests.swift
Normal file
@@ -0,0 +1,218 @@
|
||||
//
|
||||
// PasswordResetViewModelTests.swift
|
||||
// CaseraTests
|
||||
//
|
||||
// Unit tests for PasswordResetViewModel navigation, state management,
|
||||
// and client-side validation (no network calls).
|
||||
//
|
||||
|
||||
import Testing
|
||||
@testable import Casera
|
||||
|
||||
// MARK: - PasswordResetStep Tests
|
||||
|
||||
struct PasswordResetStepTests {
|
||||
|
||||
@Test func allCasesHasFiveValues() {
|
||||
#expect(PasswordResetStep.allCases.count == 5)
|
||||
}
|
||||
|
||||
@Test func allCasesContainsExpectedSteps() {
|
||||
let cases = PasswordResetStep.allCases
|
||||
#expect(cases.contains(.requestCode))
|
||||
#expect(cases.contains(.verifyCode))
|
||||
#expect(cases.contains(.resetPassword))
|
||||
#expect(cases.contains(.loggingIn))
|
||||
#expect(cases.contains(.success))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Initialization Tests
|
||||
|
||||
@MainActor
|
||||
struct PasswordResetViewModelInitTests {
|
||||
|
||||
@Test func defaultInitStartsAtRequestCode() {
|
||||
let vm = PasswordResetViewModel()
|
||||
#expect(vm.currentStep == .requestCode)
|
||||
#expect(vm.resetToken == nil)
|
||||
}
|
||||
|
||||
@Test func initWithTokenStartsAtResetPassword() {
|
||||
let vm = PasswordResetViewModel(resetToken: "abc123")
|
||||
#expect(vm.currentStep == .resetPassword)
|
||||
#expect(vm.resetToken == "abc123")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Navigation Tests
|
||||
|
||||
@MainActor
|
||||
struct PasswordResetViewModelNavigationTests {
|
||||
|
||||
@Test func moveToNextStepFromRequestCode() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.currentStep = .requestCode
|
||||
vm.moveToNextStep()
|
||||
#expect(vm.currentStep == .verifyCode)
|
||||
}
|
||||
|
||||
@Test func moveToNextStepFromVerifyCode() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.currentStep = .verifyCode
|
||||
vm.moveToNextStep()
|
||||
#expect(vm.currentStep == .resetPassword)
|
||||
}
|
||||
|
||||
@Test func moveToNextStepFromResetPassword() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.currentStep = .resetPassword
|
||||
vm.moveToNextStep()
|
||||
#expect(vm.currentStep == .loggingIn)
|
||||
}
|
||||
|
||||
@Test func moveToNextStepFromLoggingIn() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.currentStep = .loggingIn
|
||||
vm.moveToNextStep()
|
||||
#expect(vm.currentStep == .success)
|
||||
}
|
||||
|
||||
@Test func moveToNextStepFromSuccessIsNoOp() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.currentStep = .success
|
||||
vm.moveToNextStep()
|
||||
#expect(vm.currentStep == .success)
|
||||
}
|
||||
|
||||
@Test func moveToPreviousStepFromVerifyCode() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.currentStep = .verifyCode
|
||||
vm.moveToPreviousStep()
|
||||
#expect(vm.currentStep == .requestCode)
|
||||
}
|
||||
|
||||
@Test func moveToPreviousStepFromResetPassword() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.currentStep = .resetPassword
|
||||
vm.moveToPreviousStep()
|
||||
#expect(vm.currentStep == .verifyCode)
|
||||
}
|
||||
|
||||
@Test func moveToPreviousStepFromRequestCodeIsNoOp() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.currentStep = .requestCode
|
||||
vm.moveToPreviousStep()
|
||||
#expect(vm.currentStep == .requestCode)
|
||||
}
|
||||
|
||||
@Test func moveToPreviousStepFromLoggingInIsNoOp() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.currentStep = .loggingIn
|
||||
vm.moveToPreviousStep()
|
||||
#expect(vm.currentStep == .loggingIn)
|
||||
}
|
||||
|
||||
@Test func moveToPreviousStepFromSuccessIsNoOp() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.currentStep = .success
|
||||
vm.moveToPreviousStep()
|
||||
#expect(vm.currentStep == .success)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Reset and Clear Tests
|
||||
|
||||
@MainActor
|
||||
struct PasswordResetViewModelResetTests {
|
||||
|
||||
@Test func resetClearsAllState() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.email = "test@example.com"
|
||||
vm.code = "123456"
|
||||
vm.newPassword = "password123"
|
||||
vm.confirmPassword = "password123"
|
||||
vm.resetToken = "token"
|
||||
vm.errorMessage = "error"
|
||||
vm.successMessage = "success"
|
||||
vm.currentStep = .resetPassword
|
||||
vm.isLoading = true
|
||||
|
||||
vm.reset()
|
||||
|
||||
#expect(vm.email == "")
|
||||
#expect(vm.code == "")
|
||||
#expect(vm.newPassword == "")
|
||||
#expect(vm.confirmPassword == "")
|
||||
#expect(vm.resetToken == nil)
|
||||
#expect(vm.errorMessage == nil)
|
||||
#expect(vm.successMessage == nil)
|
||||
#expect(vm.currentStep == .requestCode)
|
||||
#expect(vm.isLoading == false)
|
||||
}
|
||||
|
||||
@Test func clearErrorNilsOutErrorMessage() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.errorMessage = "Something went wrong"
|
||||
vm.clearError()
|
||||
#expect(vm.errorMessage == nil)
|
||||
}
|
||||
|
||||
@Test func clearSuccessNilsOutSuccessMessage() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.successMessage = "Code sent!"
|
||||
vm.clearSuccess()
|
||||
#expect(vm.successMessage == nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Client-Side Validation Tests (verifyResetCode / resetPassword gates)
|
||||
|
||||
@MainActor
|
||||
struct PasswordResetViewModelValidationTests {
|
||||
|
||||
@Test func verifyResetCodeWithEmptyCodeSetsError() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.code = ""
|
||||
vm.verifyResetCode()
|
||||
#expect(vm.errorMessage == "Code is required")
|
||||
}
|
||||
|
||||
@Test func verifyResetCodeWithNonNumericCodeSetsError() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.code = "abcdef"
|
||||
vm.verifyResetCode()
|
||||
#expect(vm.errorMessage == "Code must be 6 digits")
|
||||
}
|
||||
|
||||
@Test func resetPasswordWithEmptyPasswordSetsError() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.newPassword = ""
|
||||
vm.resetPassword()
|
||||
#expect(vm.errorMessage == "Password is required")
|
||||
}
|
||||
|
||||
@Test func resetPasswordWithWeakPasswordSetsError() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.newPassword = "abcdefgh" // no number
|
||||
vm.resetPassword()
|
||||
#expect(vm.errorMessage == "Password must contain at least one number")
|
||||
}
|
||||
|
||||
@Test func resetPasswordWithMismatchedPasswordsSetsError() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.newPassword = "abc12345"
|
||||
vm.confirmPassword = "xyz12345"
|
||||
vm.resetPassword()
|
||||
#expect(vm.errorMessage == "Passwords do not match")
|
||||
}
|
||||
|
||||
@Test func resetPasswordWithNilTokenSetsError() {
|
||||
let vm = PasswordResetViewModel()
|
||||
vm.newPassword = "abc12345"
|
||||
vm.confirmPassword = "abc12345"
|
||||
vm.resetToken = nil
|
||||
vm.resetPassword()
|
||||
#expect(vm.errorMessage == "Invalid reset token. Please start over.")
|
||||
}
|
||||
}
|
||||
50
iosApp/CaseraTests/ThemeIDTests.swift
Normal file
50
iosApp/CaseraTests/ThemeIDTests.swift
Normal file
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// ThemeIDTests.swift
|
||||
// CaseraTests
|
||||
//
|
||||
// Unit tests for ThemeID enum properties and round-tripping.
|
||||
//
|
||||
|
||||
import Testing
|
||||
@testable import Casera
|
||||
|
||||
// MARK: - ThemeID Enum Tests
|
||||
|
||||
struct ThemeIDTests {
|
||||
|
||||
@Test func allCasesCountIsEleven() {
|
||||
#expect(ThemeID.allCases.count == 11)
|
||||
}
|
||||
|
||||
@Test func allDisplayNamesAreNonEmpty() {
|
||||
for theme in ThemeID.allCases {
|
||||
#expect(!theme.displayName.isEmpty, "ThemeID.\(theme) has empty displayName")
|
||||
}
|
||||
}
|
||||
|
||||
@Test func allDescriptionsAreNonEmpty() {
|
||||
for theme in ThemeID.allCases {
|
||||
#expect(!theme.description.isEmpty, "ThemeID.\(theme) has empty description")
|
||||
}
|
||||
}
|
||||
|
||||
@Test func rawValueRoundTripsForAllCases() {
|
||||
for theme in ThemeID.allCases {
|
||||
let roundTripped = ThemeID(rawValue: theme.rawValue)
|
||||
#expect(roundTripped == theme, "ThemeID.\(theme) failed rawValue round-trip")
|
||||
}
|
||||
}
|
||||
|
||||
@Test func allRawValuesAreUnique() {
|
||||
let rawValues = ThemeID.allCases.map(\.rawValue)
|
||||
#expect(Set(rawValues).count == rawValues.count)
|
||||
}
|
||||
|
||||
@Test func brightDisplayNameIsDefault() {
|
||||
#expect(ThemeID.bright.displayName == "Default")
|
||||
}
|
||||
|
||||
@Test func oceanRawValueIsOcean() {
|
||||
#expect(ThemeID.ocean.rawValue == "Ocean")
|
||||
}
|
||||
}
|
||||
266
iosApp/CaseraTests/ValidationRulesTests.swift
Normal file
266
iosApp/CaseraTests/ValidationRulesTests.swift
Normal file
@@ -0,0 +1,266 @@
|
||||
//
|
||||
// ValidationRulesTests.swift
|
||||
// CaseraTests
|
||||
//
|
||||
// Unit tests for ValidationError and ValidationRules (distinct from ValidationHelpers).
|
||||
//
|
||||
|
||||
import Testing
|
||||
@testable import Casera
|
||||
|
||||
// MARK: - ValidationError errorDescription Tests
|
||||
|
||||
struct ValidationErrorTests {
|
||||
|
||||
@Test func requiredFieldErrorDescription() {
|
||||
let error = ValidationError.required(field: "Email")
|
||||
#expect(error.errorDescription == "Email is required")
|
||||
}
|
||||
|
||||
@Test func invalidEmailErrorDescription() {
|
||||
let error = ValidationError.invalidEmail
|
||||
#expect(error.errorDescription == "Please enter a valid email address")
|
||||
}
|
||||
|
||||
@Test func passwordTooShortErrorDescription() {
|
||||
let error = ValidationError.passwordTooShort(minLength: 8)
|
||||
#expect(error.errorDescription == "Password must be at least 8 characters")
|
||||
}
|
||||
|
||||
@Test func passwordMismatchErrorDescription() {
|
||||
let error = ValidationError.passwordMismatch
|
||||
#expect(error.errorDescription == "Passwords do not match")
|
||||
}
|
||||
|
||||
@Test func passwordMissingLetterErrorDescription() {
|
||||
let error = ValidationError.passwordMissingLetter
|
||||
#expect(error.errorDescription == "Password must contain at least one letter")
|
||||
}
|
||||
|
||||
@Test func passwordMissingNumberErrorDescription() {
|
||||
let error = ValidationError.passwordMissingNumber
|
||||
#expect(error.errorDescription == "Password must contain at least one number")
|
||||
}
|
||||
|
||||
@Test func invalidCodeErrorDescription() {
|
||||
let error = ValidationError.invalidCode(expectedLength: 6)
|
||||
#expect(error.errorDescription == "Code must be 6 digits")
|
||||
}
|
||||
|
||||
@Test func invalidUsernameErrorDescription() {
|
||||
let error = ValidationError.invalidUsername
|
||||
#expect(error.errorDescription == "Username can only contain letters, numbers, and underscores")
|
||||
}
|
||||
|
||||
@Test func customMessageErrorDescription() {
|
||||
let error = ValidationError.custom(message: "Something went wrong")
|
||||
#expect(error.errorDescription == "Something went wrong")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Email Validation Tests
|
||||
|
||||
struct ValidationRulesEmailTests {
|
||||
|
||||
@Test func emptyEmailReturnsRequired() {
|
||||
let error = ValidationRules.validateEmail("")
|
||||
#expect(error?.errorDescription == "Email is required")
|
||||
}
|
||||
|
||||
@Test func whitespaceOnlyEmailReturnsRequired() {
|
||||
let error = ValidationRules.validateEmail(" ")
|
||||
#expect(error?.errorDescription == "Email is required")
|
||||
}
|
||||
|
||||
@Test func invalidEmailReturnsInvalidEmail() {
|
||||
let error = ValidationRules.validateEmail("notanemail")
|
||||
#expect(error?.errorDescription == "Please enter a valid email address")
|
||||
}
|
||||
|
||||
@Test func validEmailReturnsNil() {
|
||||
let error = ValidationRules.validateEmail("user@example.com")
|
||||
#expect(error == nil)
|
||||
}
|
||||
|
||||
@Test func isValidEmailReturnsTrueForValid() {
|
||||
#expect(ValidationRules.isValidEmail("user@example.com"))
|
||||
}
|
||||
|
||||
@Test func isValidEmailReturnsFalseForInvalid() {
|
||||
#expect(!ValidationRules.isValidEmail("notanemail"))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Password Validation Tests
|
||||
|
||||
struct ValidationRulesPasswordTests {
|
||||
|
||||
@Test func emptyPasswordReturnsRequired() {
|
||||
let error = ValidationRules.validatePassword("")
|
||||
#expect(error?.errorDescription == "Password is required")
|
||||
}
|
||||
|
||||
@Test func shortPasswordReturnsTooShort() {
|
||||
let error = ValidationRules.validatePassword("abc")
|
||||
#expect(error?.errorDescription == "Password must be at least 8 characters")
|
||||
}
|
||||
|
||||
@Test func validPasswordReturnsNil() {
|
||||
let error = ValidationRules.validatePassword("longpassword")
|
||||
#expect(error == nil)
|
||||
}
|
||||
|
||||
@Test func exactMinLengthPasswordReturnsNil() {
|
||||
let error = ValidationRules.validatePassword("12345678")
|
||||
#expect(error == nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Password Strength Tests
|
||||
|
||||
struct ValidationRulesPasswordStrengthTests {
|
||||
|
||||
@Test func emptyPasswordReturnsRequired() {
|
||||
let error = ValidationRules.validatePasswordStrength("")
|
||||
#expect(error?.errorDescription == "Password is required")
|
||||
}
|
||||
|
||||
@Test func noLetterReturnsMissingLetter() {
|
||||
let error = ValidationRules.validatePasswordStrength("123456")
|
||||
#expect(error?.errorDescription == "Password must contain at least one letter")
|
||||
}
|
||||
|
||||
@Test func noNumberReturnsMissingNumber() {
|
||||
let error = ValidationRules.validatePasswordStrength("abcdef")
|
||||
#expect(error?.errorDescription == "Password must contain at least one number")
|
||||
}
|
||||
|
||||
@Test func letterAndNumberReturnsNil() {
|
||||
let error = ValidationRules.validatePasswordStrength("abc123")
|
||||
#expect(error == nil)
|
||||
}
|
||||
|
||||
@Test func isValidPasswordReturnsTrueForStrong() {
|
||||
#expect(ValidationRules.isValidPassword("abc123"))
|
||||
}
|
||||
|
||||
@Test func isValidPasswordReturnsFalseForLettersOnly() {
|
||||
#expect(!ValidationRules.isValidPassword("abcdef"))
|
||||
}
|
||||
|
||||
@Test func isValidPasswordReturnsFalseForNumbersOnly() {
|
||||
#expect(!ValidationRules.isValidPassword("123456"))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Password Match Tests
|
||||
|
||||
struct ValidationRulesPasswordMatchTests {
|
||||
|
||||
@Test func matchingPasswordsReturnsNil() {
|
||||
let error = ValidationRules.validatePasswordMatch("abc123", "abc123")
|
||||
#expect(error == nil)
|
||||
}
|
||||
|
||||
@Test func mismatchedPasswordsReturnsMismatch() {
|
||||
let error = ValidationRules.validatePasswordMatch("abc123", "xyz789")
|
||||
#expect(error?.errorDescription == "Passwords do not match")
|
||||
}
|
||||
|
||||
@Test func caseSensitiveMismatch() {
|
||||
let error = ValidationRules.validatePasswordMatch("Password", "password")
|
||||
#expect(error?.errorDescription == "Passwords do not match")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Code Validation Tests
|
||||
|
||||
struct ValidationRulesCodeTests {
|
||||
|
||||
@Test func emptyCodeReturnsRequired() {
|
||||
let error = ValidationRules.validateCode("")
|
||||
#expect(error?.errorDescription == "Code is required")
|
||||
}
|
||||
|
||||
@Test func wrongLengthReturnsInvalidCode() {
|
||||
let error = ValidationRules.validateCode("123")
|
||||
#expect(error?.errorDescription == "Code must be 6 digits")
|
||||
}
|
||||
|
||||
@Test func nonNumericCodeReturnsInvalidCode() {
|
||||
let error = ValidationRules.validateCode("abcdef")
|
||||
#expect(error?.errorDescription == "Code must be 6 digits")
|
||||
}
|
||||
|
||||
@Test func validSixDigitCodeReturnsNil() {
|
||||
let error = ValidationRules.validateCode("123456")
|
||||
#expect(error == nil)
|
||||
}
|
||||
|
||||
@Test func customLengthFourDigitCode() {
|
||||
let error = ValidationRules.validateCode("1234", expectedLength: 4)
|
||||
#expect(error == nil)
|
||||
}
|
||||
|
||||
@Test func customLengthWrongDigitCount() {
|
||||
let error = ValidationRules.validateCode("123", expectedLength: 4)
|
||||
#expect(error?.errorDescription == "Code must be 4 digits")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Username Validation Tests
|
||||
|
||||
struct ValidationRulesUsernameTests {
|
||||
|
||||
@Test func emptyUsernameReturnsRequired() {
|
||||
let error = ValidationRules.validateUsername("")
|
||||
#expect(error?.errorDescription == "Username is required")
|
||||
}
|
||||
|
||||
@Test func validAlphanumericUsernameReturnsNil() {
|
||||
let error = ValidationRules.validateUsername("john_doe123")
|
||||
#expect(error == nil)
|
||||
}
|
||||
|
||||
@Test func usernameWithSpacesReturnsInvalid() {
|
||||
let error = ValidationRules.validateUsername("john doe")
|
||||
#expect(error?.errorDescription == "Username can only contain letters, numbers, and underscores")
|
||||
}
|
||||
|
||||
@Test func usernameWithSpecialCharsReturnsInvalid() {
|
||||
let error = ValidationRules.validateUsername("user@name!")
|
||||
#expect(error?.errorDescription == "Username can only contain letters, numbers, and underscores")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Required Field Validation Tests
|
||||
|
||||
struct ValidationRulesRequiredTests {
|
||||
|
||||
@Test func emptyValueReturnsRequired() {
|
||||
let error = ValidationRules.validateRequired("", fieldName: "Name")
|
||||
#expect(error?.errorDescription == "Name is required")
|
||||
}
|
||||
|
||||
@Test func whitespaceOnlyReturnsRequired() {
|
||||
let error = ValidationRules.validateRequired(" ", fieldName: "Name")
|
||||
#expect(error?.errorDescription == "Name is required")
|
||||
}
|
||||
|
||||
@Test func nonEmptyReturnsNil() {
|
||||
let error = ValidationRules.validateRequired("hello", fieldName: "Name")
|
||||
#expect(error == nil)
|
||||
}
|
||||
|
||||
@Test func isNotEmptyReturnsTrueForNonEmpty() {
|
||||
#expect(ValidationRules.isNotEmpty("hello"))
|
||||
}
|
||||
|
||||
@Test func isNotEmptyReturnsFalseForEmpty() {
|
||||
#expect(!ValidationRules.isNotEmpty(""))
|
||||
}
|
||||
|
||||
@Test func isNotEmptyReturnsFalseForWhitespace() {
|
||||
#expect(!ValidationRules.isNotEmpty(" "))
|
||||
}
|
||||
}
|
||||
102
iosApp/CaseraTests/WidgetActionTests.swift
Normal file
102
iosApp/CaseraTests/WidgetActionTests.swift
Normal file
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// WidgetActionTests.swift
|
||||
// CaseraTests
|
||||
//
|
||||
// Unit tests for WidgetDataManager.WidgetAction (Codable, Equatable, accessors)
|
||||
// and WidgetDataManager.parseDate static helper.
|
||||
//
|
||||
|
||||
import Testing
|
||||
import Foundation
|
||||
@testable import Casera
|
||||
|
||||
// MARK: - WidgetAction Codable Tests
|
||||
|
||||
struct WidgetActionCodableTests {
|
||||
|
||||
@Test func encodeDecodeRoundTrip() throws {
|
||||
let original = WidgetDataManager.WidgetAction.completeTask(taskId: 42, taskTitle: "Fix leak")
|
||||
let data = try JSONEncoder().encode(original)
|
||||
let decoded = try JSONDecoder().decode(WidgetDataManager.WidgetAction.self, from: data)
|
||||
#expect(decoded == original)
|
||||
}
|
||||
|
||||
@Test func decodedValuesMatch() throws {
|
||||
let original = WidgetDataManager.WidgetAction.completeTask(taskId: 99, taskTitle: "Paint walls")
|
||||
let data = try JSONEncoder().encode(original)
|
||||
let decoded = try JSONDecoder().decode(WidgetDataManager.WidgetAction.self, from: data)
|
||||
#expect(decoded.taskId == 99)
|
||||
#expect(decoded.taskTitle == "Paint walls")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WidgetAction Equatable Tests
|
||||
|
||||
struct WidgetActionEquatableTests {
|
||||
|
||||
@Test func sameValuesAreEqual() {
|
||||
let a = WidgetDataManager.WidgetAction.completeTask(taskId: 1, taskTitle: "Test")
|
||||
let b = WidgetDataManager.WidgetAction.completeTask(taskId: 1, taskTitle: "Test")
|
||||
#expect(a == b)
|
||||
}
|
||||
|
||||
@Test func differentTaskIdNotEqual() {
|
||||
let a = WidgetDataManager.WidgetAction.completeTask(taskId: 1, taskTitle: "Test")
|
||||
let b = WidgetDataManager.WidgetAction.completeTask(taskId: 2, taskTitle: "Test")
|
||||
#expect(a != b)
|
||||
}
|
||||
|
||||
@Test func differentTaskTitleNotEqual() {
|
||||
let a = WidgetDataManager.WidgetAction.completeTask(taskId: 1, taskTitle: "Alpha")
|
||||
let b = WidgetDataManager.WidgetAction.completeTask(taskId: 1, taskTitle: "Beta")
|
||||
#expect(a != b)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WidgetAction Accessor Tests
|
||||
|
||||
struct WidgetActionAccessorTests {
|
||||
|
||||
@Test func taskIdReturnsCorrectValue() {
|
||||
let action = WidgetDataManager.WidgetAction.completeTask(taskId: 55, taskTitle: "Mow lawn")
|
||||
#expect(action.taskId == 55)
|
||||
}
|
||||
|
||||
@Test func taskTitleReturnsCorrectValue() {
|
||||
let action = WidgetDataManager.WidgetAction.completeTask(taskId: 55, taskTitle: "Mow lawn")
|
||||
#expect(action.taskTitle == "Mow lawn")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - parseDate Tests
|
||||
|
||||
struct ParseDateTests {
|
||||
|
||||
@Test func validDateStringReturnsDate() {
|
||||
let date = WidgetDataManager.parseDate("2024-06-15")
|
||||
#expect(date != nil)
|
||||
}
|
||||
|
||||
@Test func nilInputReturnsNil() {
|
||||
let date = WidgetDataManager.parseDate(nil)
|
||||
#expect(date == nil)
|
||||
}
|
||||
|
||||
@Test func emptyStringReturnsNil() {
|
||||
let date = WidgetDataManager.parseDate("")
|
||||
#expect(date == nil)
|
||||
}
|
||||
|
||||
@Test func isoDateTimeExtractsDatePart() {
|
||||
let date = WidgetDataManager.parseDate("2025-12-26T00:00:00Z")
|
||||
#expect(date != nil)
|
||||
// Should parse the same as just the date part
|
||||
let dateDirect = WidgetDataManager.parseDate("2025-12-26")
|
||||
#expect(date == dateDirect)
|
||||
}
|
||||
|
||||
@Test func invalidStringReturnsNil() {
|
||||
let date = WidgetDataManager.parseDate("not-a-date")
|
||||
#expect(date == nil)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user