// // ShareableContentTests.swift // SportsTimeTests // // TDD specification tests for ShareableContent types. // import Testing import Foundation import SwiftUI @testable import SportsTime // MARK: - ShareCardType Tests @Suite("ShareCardType") struct ShareCardTypeTests { // MARK: - Specification Tests: CaseIterable /// - Expected Behavior: Includes all expected card types @Test("allCases: includes all card types") func allCases_includesAll() { let allTypes = ShareCardType.allCases #expect(allTypes.contains(.tripSummary)) #expect(allTypes.contains(.achievementSpotlight)) #expect(allTypes.contains(.achievementCollection)) #expect(allTypes.contains(.achievementMilestone)) #expect(allTypes.contains(.achievementContext)) #expect(allTypes.contains(.stadiumProgress)) } // MARK: - Invariant Tests /// - Invariant: Each type has a unique rawValue @Test("Invariant: unique rawValues") func invariant_uniqueRawValues() { let allTypes = ShareCardType.allCases let rawValues = allTypes.map { $0.rawValue } let uniqueRawValues = Set(rawValues) #expect(rawValues.count == uniqueRawValues.count) } /// - Invariant: Count matches expected number @Test("Invariant: correct count") func invariant_correctCount() { #expect(ShareCardType.allCases.count == 6) } } // MARK: - ShareTheme Tests @Suite("ShareTheme") struct ShareThemeTests { // MARK: - Specification Tests: Static Themes /// - Expected Behavior: All preset themes are accessible @Test("Static themes: all presets exist") func staticThemes_allExist() { // Access each theme to ensure they exist let _ = ShareTheme.dark let _ = ShareTheme.light let _ = ShareTheme.midnight let _ = ShareTheme.forest let _ = ShareTheme.sunset let _ = ShareTheme.berry let _ = ShareTheme.ocean let _ = ShareTheme.slate #expect(true) // If we got here, all themes exist } /// - Expected Behavior: all array contains all themes @Test("all: contains all preset themes") func all_containsAllThemes() { let all = ShareTheme.all #expect(all.count == 8) #expect(all.contains(where: { $0.id == "dark" })) #expect(all.contains(where: { $0.id == "light" })) #expect(all.contains(where: { $0.id == "midnight" })) #expect(all.contains(where: { $0.id == "forest" })) #expect(all.contains(where: { $0.id == "sunset" })) #expect(all.contains(where: { $0.id == "berry" })) #expect(all.contains(where: { $0.id == "ocean" })) #expect(all.contains(where: { $0.id == "slate" })) } // MARK: - Specification Tests: theme(byId:) /// - Expected Behavior: Returns matching theme by id @Test("theme(byId:): returns matching theme") func themeById_returnsMatching() { let dark = ShareTheme.theme(byId: "dark") let light = ShareTheme.theme(byId: "light") #expect(dark.id == "dark") #expect(light.id == "light") } /// - Expected Behavior: Returns dark theme for unknown id @Test("theme(byId:): returns dark for unknown id") func themeById_unknownReturnsDark() { let unknown = ShareTheme.theme(byId: "nonexistent") #expect(unknown.id == "dark") } /// - Expected Behavior: Finds all valid themes by id @Test("theme(byId:): finds all valid themes") func themeById_findsAllValid() { let ids = ["dark", "light", "midnight", "forest", "sunset", "berry", "ocean", "slate"] for id in ids { let theme = ShareTheme.theme(byId: id) #expect(theme.id == id) } } // MARK: - Specification Tests: Properties /// - Expected Behavior: Each theme has required properties @Test("Properties: themes have all required fields") func properties_allRequired() { for theme in ShareTheme.all { #expect(!theme.id.isEmpty) #expect(!theme.name.isEmpty) #expect(theme.gradientColors.count >= 2) // Colors exist (can't easily test Color values) } } // MARK: - Invariant Tests /// - Invariant: All themes have unique ids @Test("Invariant: unique theme ids") func invariant_uniqueIds() { let ids = ShareTheme.all.map { $0.id } let uniqueIds = Set(ids) #expect(ids.count == uniqueIds.count) } /// - Invariant: All themes are Hashable and Identifiable @Test("Invariant: themes are Hashable") func invariant_hashable() { var themeSet: Set = [] for theme in ShareTheme.all { themeSet.insert(theme) } #expect(themeSet.count == ShareTheme.all.count) } } // MARK: - ShareError Tests @Suite("ShareError") struct ShareErrorTests { // MARK: - Specification Tests: errorDescription /// - Expected Behavior: renderingFailed explains render failure @Test("errorDescription: renderingFailed mentions render") func errorDescription_renderingFailed() { let error = ShareError.renderingFailed #expect(error.errorDescription != nil) #expect(error.errorDescription!.lowercased().contains("render") || error.errorDescription!.lowercased().contains("failed")) } /// - Expected Behavior: mapSnapshotFailed explains snapshot failure @Test("errorDescription: mapSnapshotFailed mentions map") func errorDescription_mapSnapshotFailed() { let error = ShareError.mapSnapshotFailed #expect(error.errorDescription != nil) #expect(error.errorDescription!.lowercased().contains("map") || error.errorDescription!.lowercased().contains("snapshot")) } /// - Expected Behavior: instagramNotInstalled explains Instagram requirement @Test("errorDescription: instagramNotInstalled mentions Instagram") func errorDescription_instagramNotInstalled() { let error = ShareError.instagramNotInstalled #expect(error.errorDescription != nil) #expect(error.errorDescription!.lowercased().contains("instagram")) } // MARK: - Invariant Tests /// - Invariant: All errors have non-empty descriptions @Test("Invariant: all errors have descriptions") func invariant_allHaveDescriptions() { let errors: [ShareError] = [ .renderingFailed, .mapSnapshotFailed, .instagramNotInstalled ] for error in errors { #expect(error.errorDescription != nil) #expect(!error.errorDescription!.isEmpty) } } } // MARK: - ShareCardDimensions Tests @Suite("ShareCardDimensions") struct ShareCardDimensionsTests { // MARK: - Specification Tests: Static Constants /// - Expected Behavior: Card size is standard Instagram story size @Test("cardSize: is 1080x1920") func cardSize_instagramStory() { #expect(ShareCardDimensions.cardSize.width == 1080) #expect(ShareCardDimensions.cardSize.height == 1920) } /// - Expected Behavior: Map snapshot size fits within card with padding @Test("mapSnapshotSize: has reasonable dimensions") func mapSnapshotSize_reasonable() { #expect(ShareCardDimensions.mapSnapshotSize.width == 960) #expect(ShareCardDimensions.mapSnapshotSize.height == 480) } /// - Expected Behavior: Route map size fits within card with padding @Test("routeMapSize: has reasonable dimensions") func routeMapSize_reasonable() { #expect(ShareCardDimensions.routeMapSize.width == 960) #expect(ShareCardDimensions.routeMapSize.height == 576) } /// - Expected Behavior: Padding value is positive @Test("padding: is positive") func padding_positive() { #expect(ShareCardDimensions.padding == 60) #expect(ShareCardDimensions.padding > 0) } /// - Expected Behavior: Header height is positive @Test("headerHeight: is positive") func headerHeight_positive() { #expect(ShareCardDimensions.headerHeight == 120) #expect(ShareCardDimensions.headerHeight > 0) } /// - Expected Behavior: Footer height is positive @Test("footerHeight: is positive") func footerHeight_positive() { #expect(ShareCardDimensions.footerHeight == 100) #expect(ShareCardDimensions.footerHeight > 0) } // MARK: - Invariant Tests /// - Invariant: Card aspect ratio is 9:16 (portrait) @Test("Invariant: card is portrait aspect ratio") func invariant_portraitAspectRatio() { let aspectRatio = ShareCardDimensions.cardSize.width / ShareCardDimensions.cardSize.height // 9:16 = 0.5625 #expect(abs(aspectRatio - 0.5625) < 0.001) } /// - Invariant: Map sizes fit within card with padding @Test("Invariant: maps fit within card") func invariant_mapsFitWithinCard() { let availableWidth = ShareCardDimensions.cardSize.width - (ShareCardDimensions.padding * 2) #expect(ShareCardDimensions.mapSnapshotSize.width <= availableWidth) #expect(ShareCardDimensions.routeMapSize.width <= availableWidth) } }