diff --git a/Reflect Watch App/ContentView.swift b/Reflect Watch App/ContentView.swift index 614d25e..f4d1c79 100644 --- a/Reflect Watch App/ContentView.swift +++ b/Reflect Watch App/ContentView.swift @@ -111,6 +111,7 @@ struct MoodButton: View { .cornerRadius(12) } .buttonStyle(.plain) + .accessibilityIdentifier(AccessibilityID.Watch.moodButton(mood.strValue)) } } diff --git a/ReflectWidget/ReflectVoteWidget.swift b/ReflectWidget/ReflectVoteWidget.swift index 6c3315c..f64cdda 100644 --- a/ReflectWidget/ReflectVoteWidget.swift +++ b/ReflectWidget/ReflectVoteWidget.swift @@ -214,6 +214,7 @@ struct NonSubscriberView: View { } .accessibilityLabel(String(localized: "Track Your Mood")) .accessibilityHint(String(localized: "Tap to open app and subscribe")) + .accessibilityIdentifier(AccessibilityID.Widget.subscribeLink) } } diff --git a/ReflectWidget/WidgetSharedViews.swift b/ReflectWidget/WidgetSharedViews.swift index b24c2a7..7874f6c 100644 --- a/ReflectWidget/WidgetSharedViews.swift +++ b/ReflectWidget/WidgetSharedViews.swift @@ -93,6 +93,7 @@ struct VotingView: View { .buttonStyle(.plain) .accessibilityLabel(mood.strValue) .accessibilityHint(String(localized: "Log this mood")) + .accessibilityIdentifier(AccessibilityID.Widget.voteMoodButton(mood.strValue)) } else { Link(destination: URL(string: "reflect://subscribe")!) { moodIcon(for: mood, size: size) @@ -100,6 +101,7 @@ struct VotingView: View { } .accessibilityLabel(mood.strValue) .accessibilityHint(String(localized: "Open app to subscribe")) + .accessibilityIdentifier(AccessibilityID.Widget.subscribeLink) } } @@ -119,12 +121,14 @@ struct VotingView: View { .buttonStyle(.plain) .accessibilityLabel(mood.strValue) .accessibilityHint(String(localized: "Log this mood")) + .accessibilityIdentifier(AccessibilityID.Widget.voteMoodButton(mood.strValue)) } else { Link(destination: URL(string: "reflect://subscribe")!) { content } .accessibilityLabel(mood.strValue) .accessibilityHint(String(localized: "Open app to subscribe")) + .accessibilityIdentifier(AccessibilityID.Widget.subscribeLink) } } @@ -196,12 +200,14 @@ struct LargeVotingView: View { .buttonStyle(.plain) .accessibilityLabel(mood.strValue) .accessibilityHint(String(localized: "Log this mood")) + .accessibilityIdentifier(AccessibilityID.Widget.voteMoodButton(mood.strValue)) } else { Link(destination: URL(string: "reflect://subscribe")!) { moodButtonContent(for: mood) } .accessibilityLabel(mood.strValue) .accessibilityHint(String(localized: "Open app to subscribe")) + .accessibilityIdentifier(AccessibilityID.Widget.subscribeLink) } } @@ -261,12 +267,14 @@ struct InlineVotingView: View { .buttonStyle(.plain) .accessibilityLabel(mood.strValue) .accessibilityHint(String(localized: "Log this mood")) + .accessibilityIdentifier(AccessibilityID.Widget.voteMoodButton(mood.strValue)) } else { Link(destination: URL(string: "reflect://subscribe")!) { moodIcon(for: mood) } .accessibilityLabel(mood.strValue) .accessibilityHint(String(localized: "Open app to subscribe")) + .accessibilityIdentifier(AccessibilityID.Widget.subscribeLink) } } diff --git a/Shared/AccessibilityIdentifiers.swift b/Shared/AccessibilityIdentifiers.swift index 8ddcb03..f437489 100644 --- a/Shared/AccessibilityIdentifiers.swift +++ b/Shared/AccessibilityIdentifiers.swift @@ -120,6 +120,45 @@ enum AccessibilityID { static let reminderTimePicker = "settings_reminder_time_picker" static let reminderSaveButton = "settings_reminder_save" static let reminderCancelButton = "settings_reminder_cancel" + static let reminderTimeButton = "settings_reminder_time" + static let changeTrialDateButton = "settings_change_trial_date" + static let trialDatePickerDoneButton = "settings_trial_date_done" + static let trialDatePicker = "settings_trial_date_picker" + static let paywallPreviewButton = "settings_paywall_preview" + static let tipsPreviewButton = "settings_tips_preview" + static let testNotificationsButton = "settings_test_notifications" + static let exportWidgetsButton = "settings_export_widgets" + static let exportVotingLayoutsButton = "settings_export_voting_layouts" + static let exportWatchViewsButton = "settings_export_watch_views" + static let exportInsightsButton = "settings_export_insights" + static let generateScreenshotsButton = "settings_generate_screenshots" + static let addTestDataButton = "settings_add_test_data" + static let deleteHealthKitButton = "settings_delete_health_kit" + static let locationAlertOpenSettingsButton = "settings_location_open_settings" + static let locationAlertCancelButton = "settings_location_cancel" + static let fontAwesomeLink = "settings_font_awesome_link" + static let chartsLink = "settings_charts_link" + static let exportDataButton = "settings_export_data" + static let closeButton = "settings_close" + static let resetLaunchDateButton = "settings_reset_launch_date" + static let fixWeekdayButton = "settings_fix_weekday" + static let whyBackgroundModeButton = "settings_why_bg_mode" + static let exportLegacyButton = "settings_export_legacy" + static let importButton = "settings_import" + static let randomIconsButton = "settings_random_icons" + static let doneButton = "settings_done" + static let specialThanksButton = "settings_special_thanks" + } + + // MARK: - TipModal + enum TipModal { + static let dismissButton = "tip_modal_dismiss" + static let resetTipsButton = "tip_modal_reset_tips" + static let tipsEnabledToggle = "tip_modal_tips_enabled" + static let doneButton = "tip_modal_done" + static func tipPreviewButton(_ index: Int) -> String { + "tip_modal_preview_\(index)" + } } // MARK: - Customize @@ -129,6 +168,7 @@ enum AccessibilityID { static let appThemePickerDoneButton = "apptheme_picker_done" static let appThemePreviewCancelButton = "apptheme_preview_cancel" static let appThemePreviewApplyButton = "apptheme_preview_apply" + static let widgetHowToLink = "customize_widget_how_to_link" static func themeButton(_ name: String) -> String { "customize_theme_\(name.lowercased())" } @@ -147,6 +187,31 @@ enum AccessibilityID { static func appThemeCard(_ name: String) -> String { "apptheme_card_\(name.lowercased())" } + static func customWidget(_ index: Int) -> String { + "customize_widget_\(index)" + } + static let customWidgetAdd = "customize_widget_add" + static func shapeOption(_ name: String) -> String { + "customize_shape_\(name.lowercased())" + } + static let shapeRefresh = "customize_shape_refresh" + static func imagePackOption(_ name: String) -> String { + "customize_imagepack_option_\(name.lowercased())" + } + static func personalityPackOption(_ name: String) -> String { + "customize_personalitypack_option_\(name.lowercased())" + } + static func celebrationAnimationButton(_ name: String) -> String { + "customize_celebration_\(name.lowercased())" + } + static let manageSubscriptionButton = "customize_manage_subscription" + static let unlockPremiumButton = "customize_unlock_premium" + static func dayFilterButton(_ day: String) -> String { + "customize_day_filter_\(day.lowercased())" + } + static func iconButton(_ name: String) -> String { + "customize_icon_\(name.lowercased())" + } } // MARK: - Paywall @@ -173,21 +238,32 @@ enum AccessibilityID { static let monthSection = "insights_month_section" static let yearSection = "insights_year_section" static let allTimeSection = "insights_all_time_section" + static let expandCollapseButton = "insights_expand_collapse" } // MARK: - Month View enum MonthView { static let grid = "month_grid" static let shareButton = "month_share_button" + static let statsToggleButton = "month_stats_toggle" + static let settingsButton = "month_settings_button" + static func dayCell(dateString: String) -> String { + "month_day_cell_\(dateString)" + } + static let debugDemoToggle = "month_debug_demo_toggle" } // MARK: - Month Detail enum MonthDetail { static let shareButton = "month_detail_share" static let deleteButton = "month_detail_delete" + static let cancelButton = "month_detail_cancel" static func moodButton(_ mood: String) -> String { "month_detail_mood_\(mood.lowercased())" } + static func entryCell(_ dateString: String) -> String { + "month_detail_entry_\(dateString)" + } } // MARK: - Year View @@ -198,6 +274,7 @@ enum AccessibilityID { static let statsSection = "year_stats_section" static func cardHeader(year: Int) -> String { "year_card_header_\(year)" } static let shareButton = "year_share_button" + static let debugDemoToggle = "year_debug_demo_toggle" } // MARK: - Onboarding @@ -213,12 +290,23 @@ enum AccessibilityID { static let subscribeButton = "onboarding_subscribe_button" static let skipButton = "onboarding_skip_button" static let nextButton = "onboarding_next_button" + static let timePicker = "onboarding_time_picker" + static let wrapupContinue = "onboarding_wrapup_continue" + static let titleOptionButton = "onboarding_title_option" + static func styleThemeButton(_ name: String) -> String { + "onboarding_style_theme_\(name.lowercased())" + } } // MARK: - Reports enum Reports { static let segmentedPicker = "reports_segmented_picker" static let dateRangePicker = "reports_date_range_picker" + static let previousMonthButton = "reports_previous_month" + static let nextMonthButton = "reports_next_month" + static func dayCell(dateString: String) -> String { + "reports_day_cell_\(dateString)" + } static let quickSummaryButton = "reports_quick_summary_button" static let detailedReportButton = "reports_detailed_report_button" static let generateButton = "reports_generate_button" @@ -229,6 +317,8 @@ enum AccessibilityID { static let minimumEntriesWarning = "reports_minimum_entries_warning" static let exportDataButton = "reports_export_data_button" static let retryButton = "reports_retry_button" + static let privacyShareButton = "reports_privacy_share" + static let privacyCancelButton = "reports_privacy_cancel" } // MARK: - Purchase / Subscription @@ -239,6 +329,11 @@ enum AccessibilityID { static let subscribeButton = "purchase_subscribe" } + // MARK: - Subscription Store + enum SubscriptionStore { + static let closeButton = "subscription_store_close" + } + // MARK: - IAP Warning enum IAPWarning { static let subscribeButton = "iap_warning_subscribe" @@ -249,6 +344,7 @@ enum AccessibilityID { static let unlockButton = "lock_screen_unlock" static let tryAgainButton = "lock_screen_try_again" static let cancelButton = "lock_screen_cancel" + static let passcodeUnlockButton = "lock_screen_passcode_unlock" static func passcodeButton(_ digit: Int) -> String { "lock_screen_passcode_\(digit)" } @@ -260,6 +356,135 @@ enum AccessibilityID { static let dismissArea = "full_screen_photo_dismiss" } + // MARK: - Export + enum Export { + static let cancelButton = "export_cancel" + static let exportButton = "export_export" + static let alertOKButton = "export_alert_ok" + static func formatButton(_ format: String) -> String { + "export_format_\(format.lowercased())" + } + static func rangeButton(_ range: String) -> String { + "export_range_\(range.lowercased())" + } + } + + // MARK: - Photo Picker + enum PhotoPicker { + static let cameraButton = "photo_picker_camera" + static let cancelButton = "photo_picker_cancel" + static let closeButton = "photo_picker_close" + static let shareButton = "photo_picker_share" + static let deleteButton = "photo_picker_delete" + static let deleteConfirmButton = "photo_picker_delete_confirm" + static let deleteCancelButton = "photo_picker_delete_cancel" + static let photosPicker = "photo_picker_library" + static let photoImage = "photo_picker_image" + static let menuButton = "photo_picker_menu" + } + + // MARK: - Sharing + enum Sharing { + static let exitButton = "sharing_exit" + static let shareButton = "sharing_share" + static func moodMenuButton(_ mood: String) -> String { + "sharing_mood_menu_\(mood.lowercased())" + } + static let moodMenu = "sharing_mood_menu" + static func templateButton(_ description: String) -> String { + "sharing_template_\(description.lowercased().replacingOccurrences(of: " ", with: "_"))" + } + } + + // MARK: - Sharing Templates + enum SharingTemplate { + static let dismissButton = "sharing_template_dismiss" + static let shareButton = "sharing_template_share" + static let moodMenu = "sharing_template_mood_menu" + static func moodMenuButton(_ mood: String) -> String { + "sharing_template_mood_menu_\(mood.lowercased())" + } + } + + // MARK: - Custom Widget + enum CustomWidget { + static func colorPicker(_ name: String) -> String { + "custom_widget_color_\(name.lowercased())" + } + static let leftEyeButton = "custom_widget_left_eye" + static let rightEyeButton = "custom_widget_right_eye" + static let mouthButton = "custom_widget_mouth" + static func backgroundOption(_ index: Int) -> String { + "custom_widget_bg_\(index)" + } + static let randomBackgroundButton = "custom_widget_random_bg" + static let shuffleButton = "custom_widget_shuffle" + static let saveButton = "custom_widget_save" + static let useButton = "custom_widget_use" + static let deleteButton = "custom_widget_delete" + static func imageOption(_ name: String) -> String { + "custom_widget_image_\(name.lowercased())" + } + } + + // MARK: - Debug / Preview + enum Debug { + static let animationDoneButton = "debug_animation_done" + static func animationCard(_ name: String) -> String { + "debug_animation_\(name.lowercased())" + } + static func debugMoodButton(_ mood: String) -> String { + "debug_mood_\(mood.lowercased())" + } + static let paywallPreviewDoneButton = "debug_paywall_done" + static let viewFullPaywallButton = "debug_view_full_paywall" + static func paywallStyleOption(_ name: String) -> String { + "debug_paywall_style_\(name.lowercased())" + } + static let liveActivityResetButton = "debug_live_activity_reset" + static let liveActivityToggleButton = "debug_live_activity_toggle" + static let liveActivityRecordButton = "debug_live_activity_record" + static let liveActivityDismissButton = "debug_live_activity_dismiss" + static let liveActivityExportButton = "debug_live_activity_export" + } + + // MARK: - Sample Entry + enum SampleEntry { + static let refreshButton = "sample_entry_refresh" + } + + // MARK: - Switchable View + enum SwitchableView { + static let headerToggle = "switchable_view_header_toggle" + } + + // MARK: - Neon Mood Button (voting layout) + enum NeonMoodButton { + static func id(for mood: String) -> String { + "neon_mood_button_\(mood.lowercased())" + } + } + + // MARK: - App Alerts + enum AppAlert { + static let storageUnavailableOK = "app_alert_storage_ok" + } + + // MARK: - Watch + enum Watch { + static func moodButton(_ mood: String) -> String { + "watch_mood_button_\(mood.lowercased())" + } + } + + // MARK: - Widget + enum Widget { + static func voteMoodButton(_ mood: String) -> String { + "widget_vote_mood_\(mood.lowercased())" + } + static let subscribeLink = "widget_subscribe_link" + } + // MARK: - Common enum Common { static let lockScreen = "lock_screen" diff --git a/Shared/Onboarding/views/OnboardingStyle.swift b/Shared/Onboarding/views/OnboardingStyle.swift index d2a6be8..8e027f7 100644 --- a/Shared/Onboarding/views/OnboardingStyle.swift +++ b/Shared/Onboarding/views/OnboardingStyle.swift @@ -195,6 +195,7 @@ struct OnboardingThemeCard: View { ) } .buttonStyle(.plain) + .accessibilityIdentifier(AccessibilityID.Onboarding.styleThemeButton(theme.name)) } } diff --git a/Shared/Onboarding/views/OnboardingTime.swift b/Shared/Onboarding/views/OnboardingTime.swift index e0ed072..84be446 100644 --- a/Shared/Onboarding/views/OnboardingTime.swift +++ b/Shared/Onboarding/views/OnboardingTime.swift @@ -65,6 +65,7 @@ struct OnboardingTime: View { .datePickerStyle(.wheel) .labelsHidden() .colorScheme(.light) + .accessibilityIdentifier(AccessibilityID.Onboarding.timePicker) .accessibilityLabel(String(localized: "Reminder time")) .accessibilityHint(String(localized: "Select when you want to be reminded")) } diff --git a/Shared/Onboarding/views/OnboardingTitle.swift b/Shared/Onboarding/views/OnboardingTitle.swift index 959a5ff..a358182 100644 --- a/Shared/Onboarding/views/OnboardingTitle.swift +++ b/Shared/Onboarding/views/OnboardingTitle.swift @@ -36,6 +36,7 @@ struct OnboardingTitle: View { .cornerRadius(10) }) .buttonStyle(PlainButtonStyle()) + .accessibilityIdentifier(AccessibilityID.Onboarding.titleOptionButton) .padding([.top], 10) } diff --git a/Shared/Onboarding/views/OnboardingWrapup.swift b/Shared/Onboarding/views/OnboardingWrapup.swift index 02dfe9b..f9581f4 100644 --- a/Shared/Onboarding/views/OnboardingWrapup.swift +++ b/Shared/Onboarding/views/OnboardingWrapup.swift @@ -60,6 +60,7 @@ struct OnboardingWrapup: View { .background(RoundedRectangle(cornerRadius: 10).fill().foregroundColor(Color.white)) .cornerRadius(10) }) + .accessibilityIdentifier(AccessibilityID.Onboarding.wrapupContinue) .padding([.top], 65) } .multilineTextAlignment(.center) diff --git a/Shared/ReflectApp.swift b/Shared/ReflectApp.swift index bbc580d..308c4dc 100644 --- a/Shared/ReflectApp.swift +++ b/Shared/ReflectApp.swift @@ -73,6 +73,7 @@ struct ReflectApp: App { .alert("Data Storage Unavailable", isPresented: $showStorageFallbackAlert) { Button("OK", role: .cancel) { } + .accessibilityIdentifier(AccessibilityID.AppAlert.storageUnavailableOK) } message: { Text("Your mood data cannot be saved permanently. Please restart the app. If the problem persists, reinstall the app.") } diff --git a/Shared/Views/AddMoodHeaderView.swift b/Shared/Views/AddMoodHeaderView.swift index e3d77c8..090884b 100644 --- a/Shared/Views/AddMoodHeaderView.swift +++ b/Shared/Views/AddMoodHeaderView.swift @@ -696,7 +696,7 @@ struct NeonEqualizerBar: View { } .buttonStyle(NeonBarButtonStyle(isPressed: $isPressed)) .frame(maxWidth: .infinity) - .accessibilityIdentifier(AccessibilityID.MoodButton.id(for: mood.widgetDisplayName)) + .accessibilityIdentifier(AccessibilityID.NeonMoodButton.id(for: mood.widgetDisplayName)) .accessibilityLabel(mood.strValue) .accessibilityHint(String(localized: "Select this mood")) } diff --git a/Shared/Views/CustomIcon/CreateWidgetView.swift b/Shared/Views/CustomIcon/CreateWidgetView.swift index 8c2037f..308e184 100644 --- a/Shared/Views/CustomIcon/CreateWidgetView.swift +++ b/Shared/Views/CustomIcon/CreateWidgetView.swift @@ -109,7 +109,8 @@ struct CreateWidgetView: View { .frame(minWidth: 0, maxWidth: .infinity) .frame(minHeight: 40, maxHeight: .infinity) .background(.blue) - + .accessibilityIdentifier(AccessibilityID.CustomWidget.shuffleButton) + Button(action: { AnalyticsManager.shared.track(.widgetCreated) UserDefaultsStore.saveCustomWidget(widgetModel: customWidget, inUse: false) @@ -127,7 +128,8 @@ struct CreateWidgetView: View { .frame(minWidth: 0, maxWidth: .infinity) .frame(minHeight: 40, maxHeight: .infinity) .background(.green) - + .accessibilityIdentifier(AccessibilityID.CustomWidget.saveButton) + Button(action: { AnalyticsManager.shared.track(.widgetUsed) UserDefaultsStore.saveCustomWidget(widgetModel: customWidget, inUse: true) @@ -145,7 +147,8 @@ struct CreateWidgetView: View { .frame(minWidth: 0, maxWidth: .infinity) .frame(minHeight: 40, maxHeight: .infinity) .background(.pink) - + .accessibilityIdentifier(AccessibilityID.CustomWidget.useButton) + if customWidget.isSaved { Button(action: { AnalyticsManager.shared.track(.widgetDeleted) @@ -163,6 +166,7 @@ struct CreateWidgetView: View { .frame(minWidth: 0, maxWidth: .infinity) .frame(minHeight: 40, maxHeight: .infinity) .background(.orange) + .accessibilityIdentifier(AccessibilityID.CustomWidget.deleteButton) } } .frame(minHeight: 40, maxHeight: .infinity) @@ -178,6 +182,7 @@ struct CreateWidgetView: View { AnalyticsManager.shared.track(.widgetColorUpdated(part: "background")) } .labelsHidden() + .accessibilityIdentifier(AccessibilityID.CustomWidget.colorPicker("bg")) } .frame(minWidth: 0, maxWidth: .infinity) @@ -188,6 +193,7 @@ struct CreateWidgetView: View { AnalyticsManager.shared.track(.widgetColorUpdated(part: "inner")) } .labelsHidden() + .accessibilityIdentifier(AccessibilityID.CustomWidget.colorPicker("inner")) } .frame(minWidth: 0, maxWidth: .infinity) @@ -198,6 +204,7 @@ struct CreateWidgetView: View { AnalyticsManager.shared.track(.widgetColorUpdated(part: "outline")) } .labelsHidden() + .accessibilityIdentifier(AccessibilityID.CustomWidget.colorPicker("stroke")) } .frame(minWidth: 0, maxWidth: .infinity) } @@ -210,6 +217,7 @@ struct CreateWidgetView: View { AnalyticsManager.shared.track(.widgetColorUpdated(part: "left_eye")) } .labelsHidden() + .accessibilityIdentifier(AccessibilityID.CustomWidget.colorPicker("leftEye")) } .frame(minWidth: 0, maxWidth: .infinity) @@ -220,6 +228,7 @@ struct CreateWidgetView: View { AnalyticsManager.shared.track(.widgetColorUpdated(part: "right_eye")) } .labelsHidden() + .accessibilityIdentifier(AccessibilityID.CustomWidget.colorPicker("rightEye")) } .frame(minWidth: 0, maxWidth: .infinity) @@ -230,6 +239,7 @@ struct CreateWidgetView: View { AnalyticsManager.shared.track(.widgetColorUpdated(part: "mouth")) } .labelsHidden() + .accessibilityIdentifier(AccessibilityID.CustomWidget.colorPicker("mouth")) } .frame(minWidth: 0, maxWidth: .infinity) } @@ -250,16 +260,19 @@ struct CreateWidgetView: View { .frame(minWidth: 10, idealWidth: 40, maxWidth: 40, minHeight: 10, idealHeight: 40, maxHeight: 40, alignment: .center) + .accessibilityIdentifier(AccessibilityID.CustomWidget.backgroundOption(CustomWidgetBackGroundOptions.selectable.firstIndex(of: bg) ?? 0)) .onTapGesture { update(background: bg) } } mixBG + .accessibilityIdentifier(AccessibilityID.CustomWidget.randomBackgroundButton) .onTapGesture { update(background: .random) } Divider() ColorPicker("", selection: $customWidget.bgOverlayColor) + .accessibilityIdentifier(AccessibilityID.CustomWidget.colorPicker("bgOverlay")) } .padding() .background( @@ -270,6 +283,7 @@ struct CreateWidgetView: View { var faceImageOptions: some View { HStack(alignment: .center) { Text(String(localized: "create_widget_view_left_eye")) + .accessibilityIdentifier(AccessibilityID.CustomWidget.leftEyeButton) .onTapGesture(perform: { showLeftEyeImagePicker.toggle() }) @@ -278,6 +292,7 @@ struct CreateWidgetView: View { .frame(minWidth: 0, maxWidth: .infinity) Divider() Text(String(localized: "create_widget_view_right_eye")) + .accessibilityIdentifier(AccessibilityID.CustomWidget.rightEyeButton) .onTapGesture(perform: { showRightEyeImagePicker.toggle() }) @@ -285,6 +300,7 @@ struct CreateWidgetView: View { .frame(minWidth: 0, maxWidth: .infinity) Divider() Text(String(localized: "create_widget_view_mouth")) + .accessibilityIdentifier(AccessibilityID.CustomWidget.mouthButton) .onTapGesture(perform: { showMuthImagePicker.toggle() }) diff --git a/Shared/Views/CustomizeView/CustomizeView.swift b/Shared/Views/CustomizeView/CustomizeView.swift index c93fbb6..f56cc68 100644 --- a/Shared/Views/CustomizeView/CustomizeView.swift +++ b/Shared/Views/CustomizeView/CustomizeView.swift @@ -538,6 +538,7 @@ struct CelebrationAnimationPickerCompact: View { ) } .buttonStyle(.plain) + .accessibilityIdentifier(AccessibilityID.Customize.celebrationAnimationButton(animation.rawValue)) } } .padding(.horizontal, 4) @@ -666,6 +667,7 @@ struct CustomWidgetSection: View { CustomWidgetView(customWidgetModel: widget) .frame(width: 60, height: 60) .cornerRadius(12) + .accessibilityIdentifier(AccessibilityID.Customize.customWidget(UserDefaultsStore.getCustomWidgets().firstIndex(where: { $0.uuid == widget.uuid }) ?? 0)) .onTapGesture { AnalyticsManager.shared.track(.widgetViewed) selectedWidget.selectedItem = widget.copy() as? CustomWidgetModel @@ -689,6 +691,7 @@ struct CustomWidgetSection: View { .foregroundColor(.secondary) } } + .accessibilityIdentifier(AccessibilityID.Customize.customWidgetAdd) } } @@ -701,6 +704,7 @@ struct CustomWidgetSection: View { } .foregroundColor(.accentColor) } + .accessibilityIdentifier(AccessibilityID.Customize.widgetHowToLink) } .sheet(isPresented: $selectedWidget.showSheet) { if let selectedItem = selectedWidget.selectedItem { @@ -822,6 +826,7 @@ struct SubscriptionBannerView: View { .padding(.horizontal, 16) .padding(.vertical, 8) .background(Capsule().fill(Color.green.opacity(0.15))) + .accessibilityIdentifier(AccessibilityID.Customize.manageSubscriptionButton) } .padding(16) } @@ -866,6 +871,7 @@ struct SubscriptionBannerView: View { ) } .buttonStyle(.plain) + .accessibilityIdentifier(AccessibilityID.Customize.unlockPremiumButton) } private func openSubscriptionManagement() async { diff --git a/Shared/Views/CustomizeView/SubViews/CustomWigetView.swift b/Shared/Views/CustomizeView/SubViews/CustomWigetView.swift index a79d4f0..7ec5197 100644 --- a/Shared/Views/CustomizeView/SubViews/CustomWigetView.swift +++ b/Shared/Views/CustomizeView/SubViews/CustomWigetView.swift @@ -23,6 +23,7 @@ struct CustomWigetView: View { CustomWidgetView(customWidgetModel: widget) .frame(width: 50, height: 50) .cornerRadius(10) + .accessibilityIdentifier(AccessibilityID.Customize.customWidget(UserDefaultsStore.getCustomWidgets().firstIndex(where: { $0.uuid == widget.uuid }) ?? 0)) .onTapGesture { AnalyticsManager.shared.track(.widgetViewed) selectedWidget.selectedItem = widget.copy() as? CustomWidgetModel @@ -34,6 +35,7 @@ struct CustomWigetView: View { .overlay( Image(systemName: "plus") ) + .accessibilityIdentifier(AccessibilityID.Customize.customWidgetAdd) .onTapGesture { AnalyticsManager.shared.track(.widgetCreateTapped) selectedWidget.selectedItem = CustomWidgetModel.randomWidget @@ -47,6 +49,7 @@ struct CustomWigetView: View { .cornerRadius(10) Text("[\(String(localized: "how_to_add_widget"))](https://support.apple.com/guide/iphone/add-widgets-iphb8f1bf206/ios)") + .accessibilityIdentifier(AccessibilityID.Customize.widgetHowToLink) .accentColor(textColor) .padding(.bottom) } diff --git a/Shared/Views/CustomizeView/SubViews/DayFilterPickerView.swift b/Shared/Views/CustomizeView/SubViews/DayFilterPickerView.swift index 80a9c0d..7da0138 100644 --- a/Shared/Views/CustomizeView/SubViews/DayFilterPickerView.swift +++ b/Shared/Views/CustomizeView/SubViews/DayFilterPickerView.swift @@ -45,6 +45,7 @@ struct DayFilterPickerView: View { .cornerRadius(8) } .buttonStyle(.plain) + .accessibilityIdentifier(AccessibilityID.Customize.dayFilterButton(day)) } } Text(String(localized: "day_picker_view_text")) diff --git a/Shared/Views/CustomizeView/SubViews/IconPickerView.swift b/Shared/Views/CustomizeView/SubViews/IconPickerView.swift index 98040f0..b5af9b4 100644 --- a/Shared/Views/CustomizeView/SubViews/IconPickerView.swift +++ b/Shared/Views/CustomizeView/SubViews/IconPickerView.swift @@ -64,6 +64,7 @@ struct IconPickerView: View { }) .accessibilityLabel(String(localized: "Default app icon")) .accessibilityHint(String(localized: "Double tap to select")) + .accessibilityIdentifier(AccessibilityID.Customize.iconButton("default")) ForEach(iconSets, id: \.self.0){ iconSet in @@ -80,6 +81,7 @@ struct IconPickerView: View { }) .accessibilityLabel(String(localized: "App icon style \(iconSet.1.replacingOccurrences(of: "AppIcon", with: "").replacingOccurrences(of: "Image", with: ""))")) .accessibilityHint(String(localized: "Double tap to select")) + .accessibilityIdentifier(AccessibilityID.Customize.iconButton(iconSet.1)) } } .padding() diff --git a/Shared/Views/CustomizeView/SubViews/ImagePackPickerView.swift b/Shared/Views/CustomizeView/SubViews/ImagePackPickerView.swift index e279d5a..17e4288 100644 --- a/Shared/Views/CustomizeView/SubViews/ImagePackPickerView.swift +++ b/Shared/Views/CustomizeView/SubViews/ImagePackPickerView.swift @@ -41,6 +41,7 @@ struct ImagePackPickerView: View { .fill(imagePack == images ? theme.currentTheme.bgColor : .clear) .padding([.top, .bottom], -3) ) + .accessibilityIdentifier(AccessibilityID.Customize.imagePackOption(String(describing: images))) .onTapGesture { let impactMed = UIImpactFeedbackGenerator(style: .heavy) impactMed.impactOccurred() diff --git a/Shared/Views/CustomizeView/SubViews/PersonalityPackPickerView.swift b/Shared/Views/CustomizeView/SubViews/PersonalityPackPickerView.swift index 5b60844..a17fd5d 100644 --- a/Shared/Views/CustomizeView/SubViews/PersonalityPackPickerView.swift +++ b/Shared/Views/CustomizeView/SubViews/PersonalityPackPickerView.swift @@ -38,6 +38,7 @@ struct PersonalityPackPickerView: View { .fill(personalityPack == aPack ? theme.currentTheme.bgColor : .clear) .padding(5) ) + .accessibilityIdentifier(AccessibilityID.Customize.personalityPackOption(aPack.title())) .onTapGesture { let impactMed = UIImpactFeedbackGenerator(style: .heavy) impactMed.impactOccurred() diff --git a/Shared/Views/CustomizeView/SubViews/ShapePickerView.swift b/Shared/Views/CustomizeView/SubViews/ShapePickerView.swift index 9266249..fcd15e1 100644 --- a/Shared/Views/CustomizeView/SubViews/ShapePickerView.swift +++ b/Shared/Views/CustomizeView/SubViews/ShapePickerView.swift @@ -31,6 +31,7 @@ struct ShapePickerView: View { .resizable() .frame(width: 20, height: 20, alignment: .trailing) .foregroundColor(Color(UIColor.systemGray)) + .accessibilityIdentifier(AccessibilityID.Customize.shapeRefresh) .onTapGesture { shapeRefreshToggleThing.toggle() } @@ -43,6 +44,7 @@ struct ShapePickerView: View { bgColor: moodTint.color(forMood: Mood.allValues.randomElement()!), textColor: textColor) .frame(height: 50) .frame(minWidth: 0, maxWidth: .infinity) + .accessibilityIdentifier(AccessibilityID.Customize.shapeOption(String(describing: ashape))) .onTapGesture { let impactMed = UIImpactFeedbackGenerator(style: .heavy) impactMed.impactOccurred() diff --git a/Shared/Views/CustomizeView/SubViews/ThemePickerView.swift b/Shared/Views/CustomizeView/SubViews/ThemePickerView.swift index 6201dd7..d306390 100644 --- a/Shared/Views/CustomizeView/SubViews/ThemePickerView.swift +++ b/Shared/Views/CustomizeView/SubViews/ThemePickerView.swift @@ -58,6 +58,7 @@ struct ThemePickerView: View { .fill(selectedTheme == theme ? selectedTheme.currentTheme.bgColor : .clear) .padding(-5) ) + .accessibilityIdentifier(AccessibilityID.Customize.themeButton(theme.title)) } private func selectTheme(_ theme: Theme) { diff --git a/Shared/Views/CustomizeView/SubViews/VotingLayoutPickerView.swift b/Shared/Views/CustomizeView/SubViews/VotingLayoutPickerView.swift index 9028354..bb2c490 100644 --- a/Shared/Views/CustomizeView/SubViews/VotingLayoutPickerView.swift +++ b/Shared/Views/CustomizeView/SubViews/VotingLayoutPickerView.swift @@ -59,6 +59,7 @@ struct VotingLayoutPickerView: View { ) } .buttonStyle(.plain) + .accessibilityIdentifier(AccessibilityID.Customize.votingLayoutButton(layout.displayName)) } } .padding(.horizontal) diff --git a/Shared/Views/ExportView.swift b/Shared/Views/ExportView.swift index c1cf8ad..29c5ba3 100644 --- a/Shared/Views/ExportView.swift +++ b/Shared/Views/ExportView.swift @@ -104,6 +104,7 @@ struct ExportView: View { Button("Cancel") { dismiss() } + .accessibilityIdentifier(AccessibilityID.Export.cancelButton) } } .sheet(isPresented: $showShareSheet) { @@ -113,6 +114,7 @@ struct ExportView: View { } .alert("Export Failed", isPresented: $showError) { Button("OK", role: .cancel) { } + .accessibilityIdentifier(AccessibilityID.Export.alertOKButton) } message: { Text(errorMessage) } @@ -230,6 +232,7 @@ struct ExportView: View { ) } .buttonStyle(.plain) + .accessibilityIdentifier(AccessibilityID.Export.formatButton(format.rawValue)) } } } @@ -260,6 +263,7 @@ struct ExportView: View { .background(Color(.systemBackground)) } .buttonStyle(.plain) + .accessibilityIdentifier(AccessibilityID.Export.rangeButton(range.rawValue)) if range != DateRange.allCases.last { Divider() @@ -293,6 +297,7 @@ struct ExportView: View { .clipShape(RoundedRectangle(cornerRadius: 14)) } .disabled(isExporting || validEntries.isEmpty) + .accessibilityIdentifier(AccessibilityID.Export.exportButton) .padding(.top, 8) } diff --git a/Shared/Views/ImagePickerGridView.swift b/Shared/Views/ImagePickerGridView.swift index ff30a0e..468f1e9 100644 --- a/Shared/Views/ImagePickerGridView.swift +++ b/Shared/Views/ImagePickerGridView.swift @@ -29,6 +29,7 @@ struct ImagePickerGridView: View { .scaledToFit() .frame(width: 40, height: 40) .foregroundColor(textColor) + .accessibilityIdentifier(AccessibilityID.CustomWidget.imageOption(item.rawValue)) .onTapGesture { pickedImageClosure(item) presentationMode.wrappedValue.dismiss() diff --git a/Shared/Views/InsightsView/InsightsView.swift b/Shared/Views/InsightsView/InsightsView.swift index 31545fa..b49f6f8 100644 --- a/Shared/Views/InsightsView/InsightsView.swift +++ b/Shared/Views/InsightsView/InsightsView.swift @@ -277,6 +277,7 @@ struct InsightsSectionView: View { .padding(.vertical, 14) } .buttonStyle(.plain) + .accessibilityIdentifier(AccessibilityID.Insights.expandCollapseButton) .accessibilityAddTraits(.isHeader) // Insights List (collapsible) diff --git a/Shared/Views/InsightsView/ReportDateRangePicker.swift b/Shared/Views/InsightsView/ReportDateRangePicker.swift index 4920d80..d9f921b 100644 --- a/Shared/Views/InsightsView/ReportDateRangePicker.swift +++ b/Shared/Views/InsightsView/ReportDateRangePicker.swift @@ -146,6 +146,7 @@ struct ReportDateRangePicker: View { .background(Color.accentColor.opacity(0.15)) .clipShape(Circle()) } + .accessibilityIdentifier(AccessibilityID.Reports.previousMonthButton) .accessibilityLabel("Previous month") Spacer() @@ -172,6 +173,7 @@ struct ReportDateRangePicker: View { .background(Color.accentColor.opacity(0.15)) .clipShape(Circle()) } + .accessibilityIdentifier(AccessibilityID.Reports.nextMonthButton) .accessibilityLabel("Next month") .disabled(isDisplayingCurrentMonth) } @@ -341,6 +343,7 @@ private struct ReportDayCell: View { } .buttonStyle(.plain) .disabled(isFuture) + .accessibilityIdentifier(AccessibilityID.Reports.dayCell(dateString: dayNumber)) .frame(height: 40) } } diff --git a/Shared/Views/InsightsView/ReportsView.swift b/Shared/Views/InsightsView/ReportsView.swift index 1b74554..365652e 100644 --- a/Shared/Views/InsightsView/ReportsView.swift +++ b/Shared/Views/InsightsView/ReportsView.swift @@ -95,7 +95,9 @@ struct ReportsView: View { viewModel.exportDataPDF() } } + .accessibilityIdentifier(AccessibilityID.Reports.privacyShareButton) Button(String(localized: "Cancel"), role: .cancel) {} + .accessibilityIdentifier(AccessibilityID.Reports.privacyCancelButton) } message: { Text("This report contains your personal mood data and journal notes. Only share it with people you trust.") } diff --git a/Shared/Views/LockScreenView.swift b/Shared/Views/LockScreenView.swift index 62f7dc7..e288257 100644 --- a/Shared/Views/LockScreenView.swift +++ b/Shared/Views/LockScreenView.swift @@ -1691,6 +1691,7 @@ struct LockScreenView: View { .disabled(authManager.isAuthenticating) .padding(.top, 16) .opacity(showContent ? 1 : 0) + .accessibilityIdentifier(AccessibilityID.LockScreen.passcodeUnlockButton) .accessibilityLabel("Use device passcode") .accessibilityHint("Double tap to authenticate with your device passcode") } diff --git a/Shared/Views/MonthView/MonthDetailView.swift b/Shared/Views/MonthView/MonthDetailView.swift index d6bb53e..03b0cee 100644 --- a/Shared/Views/MonthView/MonthDetailView.swift +++ b/Shared/Views/MonthView/MonthDetailView.swift @@ -117,6 +117,7 @@ struct MonthDetailView: View { selectedEntry = nil showUpdateEntryAlert = false }) + .accessibilityIdentifier(AccessibilityID.MonthDetail.cancelButton) } } @@ -153,6 +154,7 @@ struct MonthDetailView: View { LazyVGrid(columns: columns, spacing: 25) { ForEach(entries, id: \.self) { entry in listViewEntry(forEntry: entry) + .accessibilityIdentifier(AccessibilityID.MonthDetail.entryCell(DateFormattingCache.shared.string(for: entry.forDate, format: .dateMedium))) .onTapGesture(perform: { if entry.canEdit { selectedEntry = entry diff --git a/Shared/Views/MonthView/MonthView.swift b/Shared/Views/MonthView/MonthView.swift index 63c018e..91f49be 100644 --- a/Shared/Views/MonthView/MonthView.swift +++ b/Shared/Views/MonthView/MonthView.swift @@ -378,6 +378,7 @@ struct MonthView: View { .preferredColorScheme(theme.preferredColorScheme) #if DEBUG // Triple-tap to toggle demo mode for video recording + .accessibilityIdentifier(AccessibilityID.MonthView.debugDemoToggle) .onTapGesture(count: 3) { if demoManager.isDemoMode { demoManager.stopDemoMode() @@ -591,6 +592,7 @@ struct MonthCard: View, Equatable { } } .buttonStyle(.plain) + .accessibilityIdentifier(AccessibilityID.MonthView.statsToggleButton) .accessibilityLabel("\(Random.monthName(fromMonthInt: month)) \(String(year)), \(showStats ? "expanded" : "collapsed")") .accessibilityHint("Double tap to toggle statistics") @@ -661,6 +663,7 @@ struct MonthCard: View, Equatable { .fill(theme.currentTheme.secondaryBGColor) ) .contentShape(Rectangle()) + .accessibilityIdentifier(AccessibilityID.MonthView.dayCell(dateString: "\(month)_\(year)")) .onTapGesture { onTap() } @@ -867,6 +870,7 @@ extension MonthView { } .padding(.top, 60) .padding(.trailing) + .accessibilityIdentifier(AccessibilityID.MonthView.settingsButton) Spacer() } } diff --git a/Shared/Views/PhotoPickerView.swift b/Shared/Views/PhotoPickerView.swift index 7013080..352c139 100644 --- a/Shared/Views/PhotoPickerView.swift +++ b/Shared/Views/PhotoPickerView.swift @@ -76,6 +76,7 @@ struct PhotoPickerView: View { ) } .disabled(isProcessing) + .accessibilityIdentifier(AccessibilityID.PhotoPicker.photosPicker) // Camera Button { @@ -111,6 +112,7 @@ struct PhotoPickerView: View { ) } .disabled(isProcessing) + .accessibilityIdentifier(AccessibilityID.PhotoPicker.cameraButton) } .padding(.horizontal) @@ -130,6 +132,7 @@ struct PhotoPickerView: View { Button("Cancel") { dismiss() } + .accessibilityIdentifier(AccessibilityID.PhotoPicker.cancelButton) } } .onChange(of: selectedItem) { _, newItem in @@ -278,6 +281,7 @@ struct PhotoGalleryView: View { } } } + .accessibilityIdentifier(AccessibilityID.PhotoPicker.photoImage) } else { VStack(spacing: 16) { Image(systemName: "photo.badge.exclamationmark") @@ -301,6 +305,7 @@ struct PhotoGalleryView: View { .font(.title2) .foregroundStyle(.white.opacity(0.7)) } + .accessibilityIdentifier(AccessibilityID.PhotoPicker.closeButton) } ToolbarItem(placement: .primaryAction) { @@ -310,17 +315,20 @@ struct PhotoGalleryView: View { } label: { Label("Share", systemImage: "square.and.arrow.up") } + .accessibilityIdentifier(AccessibilityID.PhotoPicker.shareButton) Button(role: .destructive) { showDeleteConfirmation = true } label: { Label("Delete", systemImage: "trash") } + .accessibilityIdentifier(AccessibilityID.PhotoPicker.deleteButton) } label: { Image(systemName: "ellipsis.circle.fill") .font(.title2) .foregroundStyle(.white.opacity(0.7)) } + .accessibilityIdentifier(AccessibilityID.PhotoPicker.menuButton) } } .confirmationDialog("Delete Photo", isPresented: $showDeleteConfirmation, titleVisibility: .visible) { @@ -328,7 +336,9 @@ struct PhotoGalleryView: View { onDelete() dismiss() } + .accessibilityIdentifier(AccessibilityID.PhotoPicker.deleteConfirmButton) Button("Cancel", role: .cancel) { } + .accessibilityIdentifier(AccessibilityID.PhotoPicker.deleteCancelButton) } message: { Text("Are you sure you want to delete this photo?") } diff --git a/Shared/Views/PurchaseButtonView.swift b/Shared/Views/PurchaseButtonView.swift index 5ccfcbd..0d7c52c 100644 --- a/Shared/Views/PurchaseButtonView.swift +++ b/Shared/Views/PurchaseButtonView.swift @@ -175,6 +175,7 @@ struct PurchaseButtonView: View { .background(Color.pink) .cornerRadius(10) } + .accessibilityIdentifier(AccessibilityID.Purchase.subscribeButton) // Restore purchases Button { diff --git a/Shared/Views/ReflectSubscriptionStoreView.swift b/Shared/Views/ReflectSubscriptionStoreView.swift index c105c83..469d279 100644 --- a/Shared/Views/ReflectSubscriptionStoreView.swift +++ b/Shared/Views/ReflectSubscriptionStoreView.swift @@ -46,6 +46,7 @@ struct ReflectSubscriptionStoreView: View { } .padding(16) .accessibilityLabel("Close") + .accessibilityIdentifier(AccessibilityID.SubscriptionStore.closeButton) } .onAppear { AppLogger.iap.info("SubscriptionStoreView appeared — source: \(source), productIDs: \(IAPManager.productIdentifiers.sorted().joined(separator: ", ")), groupID: \(IAPManager.subscriptionGroupID)") diff --git a/Shared/Views/SampleEntryView.swift b/Shared/Views/SampleEntryView.swift index 73b5e2a..3eb0333 100644 --- a/Shared/Views/SampleEntryView.swift +++ b/Shared/Views/SampleEntryView.swift @@ -22,6 +22,7 @@ struct SampleEntryView: View { .resizable() .frame(width: 20, height: 20, alignment: .trailing) .foregroundColor(Color(UIColor.systemGray)) + .accessibilityIdentifier(AccessibilityID.SampleEntry.refreshButton) .onTapGesture { sampleListEntry = DataController.shared.generateObjectNotInArray(forDate: Date(), withMood: sampleListEntry.mood.next) } diff --git a/Shared/Views/SettingsView/DebugAnimationSettingsView.swift b/Shared/Views/SettingsView/DebugAnimationSettingsView.swift index 4e9dfb6..9d7537f 100644 --- a/Shared/Views/SettingsView/DebugAnimationSettingsView.swift +++ b/Shared/Views/SettingsView/DebugAnimationSettingsView.swift @@ -54,6 +54,7 @@ struct DebugAnimationSettingsView: View { Button("Done") { dismiss() } + .accessibilityIdentifier(AccessibilityID.Debug.animationDoneButton) } } } @@ -217,6 +218,7 @@ struct AnimationCard: View { ) .scaleEffect(isPressed ? 0.95 : (isSelected ? 1.02 : 1.0)) } + .accessibilityIdentifier(AccessibilityID.Debug.animationCard(type.rawValue)) .buttonStyle(PlainButtonStyle()) .onLongPressGesture(minimumDuration: .infinity, pressing: { pressing in withAnimation(.easeInOut(duration: 0.15)) { @@ -336,6 +338,7 @@ struct DebugVotingContentView: View { .fill(mood.color.opacity(0.15)) ) } + .accessibilityIdentifier(AccessibilityID.Debug.debugMoodButton(mood.strValue)) } } diff --git a/Shared/Views/SettingsView/LiveActivityPreviewView.swift b/Shared/Views/SettingsView/LiveActivityPreviewView.swift index 7e87e24..c6b4641 100644 --- a/Shared/Views/SettingsView/LiveActivityPreviewView.swift +++ b/Shared/Views/SettingsView/LiveActivityPreviewView.swift @@ -58,6 +58,7 @@ struct LiveActivityPreviewView: View { .background(Color.gray.opacity(0.2)) .cornerRadius(12) } + .accessibilityIdentifier(AccessibilityID.Debug.liveActivityResetButton) Button(action: toggleAnimation) { Label(isAnimating ? "Pause" : "Start", systemImage: isAnimating ? "pause.fill" : "play.fill") @@ -68,6 +69,7 @@ struct LiveActivityPreviewView: View { .foregroundColor(.white) .cornerRadius(12) } + .accessibilityIdentifier(AccessibilityID.Debug.liveActivityToggleButton) } Button(action: { showRecordingMode = true }) { @@ -79,6 +81,7 @@ struct LiveActivityPreviewView: View { .foregroundColor(.white) .cornerRadius(12) } + .accessibilityIdentifier(AccessibilityID.Debug.liveActivityRecordButton) } .padding(.horizontal, 20) .padding(.bottom, 40) @@ -264,6 +267,7 @@ struct LiveActivityRecordingView: View { .background(Color.orange) .foregroundColor(.white) .cornerRadius(12) + .accessibilityIdentifier(AccessibilityID.Debug.liveActivityDismissButton) } else if isExporting { Text("Exporting frames...") .font(.title2.bold()) @@ -282,6 +286,7 @@ struct LiveActivityRecordingView: View { } } } + .accessibilityIdentifier(AccessibilityID.Debug.liveActivityExportButton) .onTapGesture { if !isExporting && !exportComplete { startExport() diff --git a/Shared/Views/SettingsView/PaywallPreviewSettingsView.swift b/Shared/Views/SettingsView/PaywallPreviewSettingsView.swift index 0097714..774d6f3 100644 --- a/Shared/Views/SettingsView/PaywallPreviewSettingsView.swift +++ b/Shared/Views/SettingsView/PaywallPreviewSettingsView.swift @@ -35,6 +35,7 @@ struct PaywallPreviewSettingsView: View { Button("Done") { dismiss() } + .accessibilityIdentifier(AccessibilityID.Debug.paywallPreviewDoneButton) } } .sheet(isPresented: $showFullPreview) { @@ -160,6 +161,7 @@ struct PaywallPreviewSettingsView: View { .foregroundColor(.white) .clipShape(RoundedRectangle(cornerRadius: 14)) } + .accessibilityIdentifier(AccessibilityID.Debug.viewFullPaywallButton) } private var gradientColors: [Color] { @@ -241,6 +243,7 @@ struct StyleOptionRow: View { .stroke(isSelected ? Color.accentColor : Color.clear, lineWidth: 2) ) } + .accessibilityIdentifier(AccessibilityID.Debug.paywallStyleOption(style.displayName)) .buttonStyle(.plain) } diff --git a/Shared/Views/SettingsView/SettingsTabView.swift b/Shared/Views/SettingsView/SettingsTabView.swift index 1f339a8..723c235 100644 --- a/Shared/Views/SettingsView/SettingsTabView.swift +++ b/Shared/Views/SettingsView/SettingsTabView.swift @@ -255,6 +255,7 @@ struct WhyUpgradeView: View { Button("Done") { dismiss() } + .accessibilityIdentifier(AccessibilityID.Settings.doneButton) } } } diff --git a/Shared/Views/SettingsView/SettingsView.swift b/Shared/Views/SettingsView/SettingsView.swift index 65851ed..0c54640 100644 --- a/Shared/Views/SettingsView/SettingsView.swift +++ b/Shared/Views/SettingsView/SettingsView.swift @@ -149,6 +149,7 @@ struct SettingsContentView: View { } .padding() }) + .accessibilityIdentifier(AccessibilityID.Settings.reminderTimeButton) .accessibilityLabel(String(localized: "Reminder Time")) .accessibilityValue(formattedReminderTime) .accessibilityHint(String(localized: "Opens time picker to change reminder time")) @@ -269,6 +270,7 @@ struct SettingsContentView: View { showTrialDatePicker = true } .font(.subheadline.weight(.medium)) + .accessibilityIdentifier(AccessibilityID.Settings.changeTrialDateButton) } .padding() } @@ -283,6 +285,7 @@ struct SettingsContentView: View { displayedComponents: .date ) .datePickerStyle(.graphical) + .accessibilityIdentifier(AccessibilityID.Settings.trialDatePicker) .padding() .navigationTitle("Set Trial Start Date") .navigationBarTitleDisplayMode(.inline) @@ -295,6 +298,7 @@ struct SettingsContentView: View { await iapManager.checkSubscriptionStatus() } } + .accessibilityIdentifier(AccessibilityID.Settings.trialDatePickerDoneButton) } } } @@ -338,6 +342,7 @@ struct SettingsContentView: View { } .padding() } + .accessibilityIdentifier(AccessibilityID.Settings.paywallPreviewButton) .background(theme.currentTheme.secondaryBGColor) .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) @@ -381,6 +386,7 @@ struct SettingsContentView: View { } .padding() } + .accessibilityIdentifier(AccessibilityID.Settings.tipsPreviewButton) .background(theme.currentTheme.secondaryBGColor) .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) @@ -418,6 +424,7 @@ struct SettingsContentView: View { } .padding() } + .accessibilityIdentifier(AccessibilityID.Settings.testNotificationsButton) .background(theme.currentTheme.secondaryBGColor) .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) @@ -470,6 +477,7 @@ struct SettingsContentView: View { .padding() } .disabled(isExportingWidgets) + .accessibilityIdentifier(AccessibilityID.Settings.exportWidgetsButton) .background(theme.currentTheme.secondaryBGColor) .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) @@ -522,6 +530,7 @@ struct SettingsContentView: View { .padding() } .disabled(isExportingVotingLayouts) + .accessibilityIdentifier(AccessibilityID.Settings.exportVotingLayoutsButton) .background(theme.currentTheme.secondaryBGColor) .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) @@ -574,6 +583,7 @@ struct SettingsContentView: View { .padding() } .disabled(isExportingWatchViews) + .accessibilityIdentifier(AccessibilityID.Settings.exportWatchViewsButton) .background(theme.currentTheme.secondaryBGColor) .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) @@ -632,6 +642,7 @@ struct SettingsContentView: View { .padding() } .disabled(isExportingInsights) + .accessibilityIdentifier(AccessibilityID.Settings.exportInsightsButton) .background(theme.currentTheme.secondaryBGColor) .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) @@ -691,6 +702,7 @@ struct SettingsContentView: View { .padding() } .disabled(isGeneratingScreenshots) + .accessibilityIdentifier(AccessibilityID.Settings.generateScreenshotsButton) .background(theme.currentTheme.secondaryBGColor) .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) @@ -741,6 +753,7 @@ struct SettingsContentView: View { .padding() } .disabled(isDeletingHealthKitData) + .accessibilityIdentifier(AccessibilityID.Settings.deleteHealthKitButton) .background(theme.currentTheme.secondaryBGColor) .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) @@ -852,11 +865,12 @@ struct SettingsContentView: View { } .padding() } + .accessibilityIdentifier(AccessibilityID.Settings.addTestDataButton) .background(theme.currentTheme.secondaryBGColor) .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) } - + private var healthKitToggle: some View { VStack(spacing: 0) { HStack(spacing: 12) { @@ -1048,7 +1062,9 @@ struct SettingsContentView: View { UIApplication.shared.open(url) } } + .accessibilityIdentifier(AccessibilityID.Settings.locationAlertOpenSettingsButton) Button(String(localized: "Cancel"), role: .cancel) {} + .accessibilityIdentifier(AccessibilityID.Settings.locationAlertCancelButton) } message: { Text("Reflect needs location access to show weather. You can enable it in Settings.") } @@ -1084,6 +1100,7 @@ struct SettingsContentView: View { } .padding() }) + .accessibilityIdentifier(AccessibilityID.Settings.exportDataButton) .accessibilityLabel(String(localized: "Export Data")) .accessibilityHint(String(localized: "Export your mood data as CSV or PDF")) .background(theme.currentTheme.secondaryBGColor) @@ -1382,6 +1399,7 @@ struct SettingsView: View { Divider() Text("Test builds only") Toggle("Bypass Subscription", isOn: $iapManager.bypassSubscription) + .accessibilityIdentifier(AccessibilityID.Settings.bypassSubscriptionToggle) addTestDataCell clearDB // fixWeekday @@ -1745,7 +1763,9 @@ struct SettingsView: View { UIApplication.shared.open(url) } } + .accessibilityIdentifier(AccessibilityID.Settings.locationAlertOpenSettingsButton) Button(String(localized: "Cancel"), role: .cancel) {} + .accessibilityIdentifier(AccessibilityID.Settings.locationAlertCancelButton) } message: { Text("Reflect needs location access to show weather. You can enable it in Settings.") } @@ -1781,6 +1801,7 @@ struct SettingsView: View { } .padding() }) + .accessibilityIdentifier(AccessibilityID.Settings.exportDataButton) .background(theme.currentTheme.secondaryBGColor) .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) @@ -1797,6 +1818,7 @@ struct SettingsView: View { .font(.body) .foregroundColor(Color(UIColor.systemBlue)) }) + .accessibilityIdentifier(AccessibilityID.Settings.closeButton) } } @@ -1811,17 +1833,20 @@ struct SettingsView: View { Text(String(localized: "settings_view_special_thanks_to_title")) .foregroundColor(textColor) }) + .accessibilityIdentifier(AccessibilityID.Settings.specialThanksButton) .padding() if showSpecialThanks { Divider() Link("Font Awesome", destination: URL(string: "https://fontawesome.com")!) + .accessibilityIdentifier(AccessibilityID.Settings.fontAwesomeLink) .accentColor(textColor) .padding(.bottom) Divider() Link("Charts", destination: URL(string: "https://github.com/danielgindi/Charts")!) + .accessibilityIdentifier(AccessibilityID.Settings.chartsLink) .accentColor(textColor) .padding(.bottom) } @@ -1838,6 +1863,7 @@ struct SettingsView: View { Text("Add test data") .foregroundColor(textColor) }) + .accessibilityIdentifier(AccessibilityID.Settings.addTestDataButton) .padding() .frame(maxWidth: .infinity) .background(theme.currentTheme.secondaryBGColor) @@ -1867,6 +1893,7 @@ struct SettingsView: View { showTrialDatePicker = true } .font(.subheadline.weight(.medium)) + .accessibilityIdentifier(AccessibilityID.Settings.changeTrialDateButton) } .padding() .background(theme.currentTheme.secondaryBGColor) @@ -1880,6 +1907,7 @@ struct SettingsView: View { displayedComponents: .date ) .datePickerStyle(.graphical) + .accessibilityIdentifier(AccessibilityID.Settings.trialDatePicker) .padding() .navigationTitle("Set Trial Start Date") .navigationBarTitleDisplayMode(.inline) @@ -1892,6 +1920,7 @@ struct SettingsView: View { await iapManager.checkSubscriptionStatus() } } + .accessibilityIdentifier(AccessibilityID.Settings.trialDatePickerDoneButton) } } } @@ -1909,6 +1938,7 @@ struct SettingsView: View { Text("Reset luanch date to current date") .foregroundColor(textColor) }) + .accessibilityIdentifier(AccessibilityID.Settings.resetLaunchDateButton) .padding() .frame(maxWidth: .infinity) .background(theme.currentTheme.secondaryBGColor) @@ -1923,6 +1953,7 @@ struct SettingsView: View { Text("Clear DB") .foregroundColor(textColor) }) + .accessibilityIdentifier(AccessibilityID.Settings.clearDataButton) .padding() .frame(maxWidth: .infinity) .background(theme.currentTheme.secondaryBGColor) @@ -1937,6 +1968,7 @@ struct SettingsView: View { Text("Fix Weekday") .foregroundColor(textColor) }) + .accessibilityIdentifier(AccessibilityID.Settings.fixWeekdayButton) .padding() .frame(maxWidth: .infinity) .background(theme.currentTheme.secondaryBGColor) @@ -1954,6 +1986,7 @@ struct SettingsView: View { Text(String(localized: "settings_view_why_bg_mode_title")) .foregroundColor(textColor) }) + .accessibilityIdentifier(AccessibilityID.Settings.whyBackgroundModeButton) .padding() if showWhyBGMode { Text(String(localized: "settings_view_why_bg_mode_body")) @@ -2110,13 +2143,14 @@ struct SettingsView: View { Text("Export") .foregroundColor(textColor) }) + .accessibilityIdentifier(AccessibilityID.Settings.exportLegacyButton) .padding() .frame(maxWidth: .infinity) .background(theme.currentTheme.secondaryBGColor) .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) } - + private var importData: some View { Button(action: { showingImporter.toggle() @@ -2125,13 +2159,14 @@ struct SettingsView: View { Text("Import") .foregroundColor(textColor) }) + .accessibilityIdentifier(AccessibilityID.Settings.importButton) .padding() .frame(maxWidth: .infinity) .background(theme.currentTheme.secondaryBGColor) .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) } - + private var randomIcons: some View { Button(action: { var iconViews = [UIImage]() @@ -2255,6 +2290,7 @@ struct SettingsView: View { Text("Create random icons") .foregroundColor(textColor) }) + .accessibilityIdentifier(AccessibilityID.Settings.randomIconsButton) .padding() .frame(maxWidth: .infinity) .background(theme.currentTheme.secondaryBGColor) diff --git a/Shared/Views/Sharing/SharingListView.swift b/Shared/Views/Sharing/SharingListView.swift index a1a710a..4e6caa3 100644 --- a/Shared/Views/Sharing/SharingListView.swift +++ b/Shared/Views/Sharing/SharingListView.swift @@ -154,10 +154,10 @@ struct SharingListView: View { }, label: { ZStack { theme.currentTheme.secondaryBGColor - + item.preview .frame(height: 88) - + VStack { Spacer() Text(item.description) @@ -179,6 +179,7 @@ struct SharingListView: View { .contentShape(Rectangle()) .padding([.leading, .trailing]) }) + .accessibilityIdentifier(AccessibilityID.Sharing.templateButton(item.description)) } } diff --git a/Shared/Views/Sharing/SharingStylePickerView.swift b/Shared/Views/Sharing/SharingStylePickerView.swift index 9e5a2d1..8545481 100644 --- a/Shared/Views/Sharing/SharingStylePickerView.swift +++ b/Shared/Views/Sharing/SharingStylePickerView.swift @@ -52,6 +52,7 @@ struct SharingStylePickerView: View { .font(.headline) .foregroundColor(.red) } + .accessibilityIdentifier(AccessibilityID.Sharing.exitButton) Spacer() @@ -104,6 +105,7 @@ struct SharingStylePickerView: View { .background(Color.green) .cornerRadius(14) } + .accessibilityIdentifier(AccessibilityID.Sharing.shareButton) .padding(.horizontal, 24) .padding(.top, 12) .padding(.bottom, 24) @@ -160,6 +162,7 @@ struct LongestStreakPickerView: View { .font(.headline) .foregroundColor(.red) } + .accessibilityIdentifier(AccessibilityID.Sharing.exitButton) Spacer() @@ -169,6 +172,7 @@ struct LongestStreakPickerView: View { selectedMood = mood recomputeStreak() } + .accessibilityIdentifier(AccessibilityID.Sharing.moodMenuButton(mood.strValue)) } } label: { HStack(spacing: 6) { @@ -180,6 +184,7 @@ struct LongestStreakPickerView: View { .foregroundColor(textColor.opacity(0.6)) } } + .accessibilityIdentifier(AccessibilityID.Sharing.moodMenu) Spacer() @@ -225,6 +230,7 @@ struct LongestStreakPickerView: View { .background(Color.green) .cornerRadius(14) } + .accessibilityIdentifier(AccessibilityID.Sharing.shareButton) .padding(.horizontal, 24) .padding(.top, 12) .padding(.bottom, 24) diff --git a/Shared/Views/SharingTemplates/AllMoodsTotalTemplate.swift b/Shared/Views/SharingTemplates/AllMoodsTotalTemplate.swift index 15c3b65..c9198bb 100644 --- a/Shared/Views/SharingTemplates/AllMoodsTotalTemplate.swift +++ b/Shared/Views/SharingTemplates/AllMoodsTotalTemplate.swift @@ -176,12 +176,13 @@ struct AllMoodsTotalTemplate: View, SharingTemplate { .foregroundColor(Color.white) .padding(.top, 20) }) + .accessibilityIdentifier(AccessibilityID.SharingTemplate.shareButton) .frame(maxWidth: .infinity, alignment: .center) .background( Color.green ) .padding(.trailing, -5) - + Button(action: { presentationMode.wrappedValue.dismiss() }, label: { @@ -191,6 +192,7 @@ struct AllMoodsTotalTemplate: View, SharingTemplate { .foregroundColor(Color.white) .padding(.top, 20) }) + .accessibilityIdentifier(AccessibilityID.SharingTemplate.dismissButton) .frame(maxWidth: .infinity, alignment: .center) .background( Color.red diff --git a/Shared/Views/SharingTemplates/CurrentStreakTemplate.swift b/Shared/Views/SharingTemplates/CurrentStreakTemplate.swift index 33a77f2..78beb66 100644 --- a/Shared/Views/SharingTemplates/CurrentStreakTemplate.swift +++ b/Shared/Views/SharingTemplates/CurrentStreakTemplate.swift @@ -119,6 +119,7 @@ struct CurrentStreakTemplate: View, SharingTemplate { .foregroundColor(Color.white) .padding(.top, 20) }) + .accessibilityIdentifier(AccessibilityID.SharingTemplate.shareButton) .sheet(isPresented: self.$shareImage.showSheet) { if let uiImage = self.shareImage.selectedShareImage { ShareSheet(photo: uiImage) @@ -129,7 +130,7 @@ struct CurrentStreakTemplate: View, SharingTemplate { Color.green ) .padding(.trailing, -5) - + Button(action: { presentationMode.wrappedValue.dismiss() }, label: { @@ -139,6 +140,7 @@ struct CurrentStreakTemplate: View, SharingTemplate { .foregroundColor(Color.white) .padding(.top, 20) }) + .accessibilityIdentifier(AccessibilityID.SharingTemplate.dismissButton) .frame(maxWidth: .infinity, alignment: .center) .background( Color.red diff --git a/Shared/Views/SharingTemplates/LongestStreakTemplate.swift b/Shared/Views/SharingTemplates/LongestStreakTemplate.swift index a2859b0..dfe45ba 100644 --- a/Shared/Views/SharingTemplates/LongestStreakTemplate.swift +++ b/Shared/Views/SharingTemplates/LongestStreakTemplate.swift @@ -167,6 +167,7 @@ struct LongestStreakTemplate: View, SharingTemplate { selectedMood = mood configureData(fakeData: self.fakeData, mood: self.selectedMood) }) + .accessibilityIdentifier(AccessibilityID.SharingTemplate.moodMenuButton(mood.strValue)) } }, label: { Text("Pick Mood") @@ -174,6 +175,7 @@ struct LongestStreakTemplate: View, SharingTemplate { .foregroundColor(textColor) .padding() }) + .accessibilityIdentifier(AccessibilityID.SharingTemplate.moodMenu) .frame(maxWidth: .infinity, alignment: .center) .background( theme.currentTheme.secondaryBGColor @@ -194,6 +196,7 @@ struct LongestStreakTemplate: View, SharingTemplate { .foregroundColor(Color.white) .padding(.top, 20) }) + .accessibilityIdentifier(AccessibilityID.SharingTemplate.shareButton) .sheet(isPresented: self.$shareImage.showSheet) { if let uiImage = self.shareImage.selectedShareImage { ShareSheet(photo: uiImage) @@ -204,7 +207,7 @@ struct LongestStreakTemplate: View, SharingTemplate { Color.green ) .padding(.trailing, -5) - + Button(action: { presentationMode.wrappedValue.dismiss() }, label: { @@ -214,6 +217,7 @@ struct LongestStreakTemplate: View, SharingTemplate { .foregroundColor(Color.white) .padding(.top, 20) }) + .accessibilityIdentifier(AccessibilityID.SharingTemplate.dismissButton) .frame(maxWidth: .infinity, alignment: .center) .background( Color.red diff --git a/Shared/Views/SharingTemplates/MonthTotalTemplate.swift b/Shared/Views/SharingTemplates/MonthTotalTemplate.swift index 7022f25..9024e0c 100644 --- a/Shared/Views/SharingTemplates/MonthTotalTemplate.swift +++ b/Shared/Views/SharingTemplates/MonthTotalTemplate.swift @@ -159,6 +159,7 @@ struct MonthTotalTemplate: View, SharingTemplate { .foregroundColor(Color.white) .padding(.top, 20) }) + .accessibilityIdentifier(AccessibilityID.SharingTemplate.shareButton) .sheet(isPresented: self.$shareImage.showSheet) { if let uiImage = self.shareImage.selectedShareImage { ShareSheet(photo: uiImage) @@ -169,7 +170,7 @@ struct MonthTotalTemplate: View, SharingTemplate { Color.green ) .padding(.trailing, -5) - + Button(action: { presentationMode.wrappedValue.dismiss() }, label: { @@ -179,6 +180,7 @@ struct MonthTotalTemplate: View, SharingTemplate { .foregroundColor(Color.white) .padding(.top, 20) }) + .accessibilityIdentifier(AccessibilityID.SharingTemplate.dismissButton) .frame(maxWidth: .infinity, alignment: .center) .background( Color.red diff --git a/Shared/Views/SwitchableView.swift b/Shared/Views/SwitchableView.swift index 9f4acef..fe404ab 100644 --- a/Shared/Views/SwitchableView.swift +++ b/Shared/Views/SwitchableView.swift @@ -93,6 +93,7 @@ struct SwitchableView: View { theme.currentTheme.secondaryBGColor ) .contentShape(Rectangle()) + .accessibilityIdentifier(AccessibilityID.SwitchableView.headerToggle) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .padding(.bottom, 30) .onTapGesture { diff --git a/Shared/Views/TipModalView.swift b/Shared/Views/TipModalView.swift index 2d2f776..5a2c7c5 100644 --- a/Shared/Views/TipModalView.swift +++ b/Shared/Views/TipModalView.swift @@ -140,6 +140,7 @@ struct TipModalView: View { y: 6 ) } + .accessibilityIdentifier(AccessibilityID.TipModal.dismissButton) .padding(.horizontal, 24) .padding(.bottom, 24) .opacity(appeared ? 1 : 0) @@ -308,6 +309,7 @@ struct TipsPreviewView: View { } .padding(.vertical, 4) } + .accessibilityIdentifier(AccessibilityID.TipModal.tipPreviewButton(index)) } } header: { Text("Tap to preview") @@ -320,11 +322,13 @@ struct TipsPreviewView: View { ReflectTipsManager.shared.resetAllTips() } .foregroundColor(.red) + .accessibilityIdentifier(AccessibilityID.TipModal.resetTipsButton) Toggle("Tips Enabled", isOn: Binding( get: { ReflectTipsManager.shared.tipsEnabled }, set: { ReflectTipsManager.shared.tipsEnabled = $0 } )) + .accessibilityIdentifier(AccessibilityID.TipModal.tipsEnabledToggle) } header: { Text("Settings") } @@ -346,6 +350,7 @@ struct TipsPreviewView: View { Button("Done") { dismiss() } + .accessibilityIdentifier(AccessibilityID.TipModal.doneButton) } } .sheet(item: Binding( diff --git a/Shared/Views/YearView/YearView.swift b/Shared/Views/YearView/YearView.swift index 6c91291..c4ff14d 100644 --- a/Shared/Views/YearView/YearView.swift +++ b/Shared/Views/YearView/YearView.swift @@ -308,6 +308,7 @@ struct YearView: View { .preferredColorScheme(theme.preferredColorScheme) #if DEBUG // Triple-tap to toggle demo mode for video recording + .accessibilityIdentifier(AccessibilityID.YearView.debugDemoToggle) .onTapGesture(count: 3) { if demoManager.isDemoMode { demoManager.stopDemoMode()