Refactor ZStack layouts to .background(), add Year View accessibility IDs, triage QA test plan

Replace ZStack-with-gradient patterns with idiomatic .background() modifier
across onboarding, customize, and settings views. Add accessibility identifiers
to Year View charts for UI test automation. Mark 67 impossible-to-automate
tests RED in QA plan and scaffold initial Year View and Settings onboarding tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-02-20 09:17:52 -06:00
parent ffc74f1a27
commit 5895b387be
22 changed files with 1469 additions and 1378 deletions

View File

@@ -8,9 +8,9 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
06E4767B5977FAC8B644FC92 /* IntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CFAE86F485C853DB3239DD9 /* IntegrationTests.swift */; }; 06E4767B5977FAC8B644FC92 /* IntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CFAE86F485C853DB3239DD9 /* IntegrationTests.swift */; };
A1B2C3D4E5F607080910ABCD /* DayViewViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4E5F60708091011ABCDE001 /* DayViewViewModelTests.swift */; }; 1AB245144C89927264D16645 /* InsightsEmptyStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6988985985DE9C29CFDFA96 /* InsightsEmptyStateTests.swift */; };
1C0DAB51279DB0FB003B1F21 /* Feels/Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 1C0DAB50279DB0FB003B1F21 /* Feels/Localizable.xcstrings */; }; 1C0DAB51279DB0FB003B1F21 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 1C0DAB50279DB0FB003B1F21 /* Localizable.xcstrings */; };
1C0DAB52279DB0FB003B1F22 /* Feels/Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 1C0DAB50279DB0FB003B1F21 /* Feels/Localizable.xcstrings */; }; 1C0DAB52279DB0FB003B1F22 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 1C0DAB50279DB0FB003B1F21 /* Localizable.xcstrings */; };
1C9566442EF8F5F70032E68F /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = 1C9566432EF8F5F70032E68F /* Algorithms */; }; 1C9566442EF8F5F70032E68F /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = 1C9566432EF8F5F70032E68F /* Algorithms */; };
1CB4D0A028787D8A00902A56 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CB4D09F28787D8A00902A56 /* StoreKit.framework */; }; 1CB4D0A028787D8A00902A56 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CB4D09F28787D8A00902A56 /* StoreKit.framework */; };
1CD90B07278C7DE0001C4FEA /* Tests_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B06278C7DE0001C4FEA /* Tests_iOS.swift */; }; 1CD90B07278C7DE0001C4FEA /* Tests_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B06278C7DE0001C4FEA /* Tests_iOS.swift */; };
@@ -25,50 +25,52 @@
1CDE000F2F3BBD26006AE6A1 /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = 1CA00002300000000000002A /* PostHog */; }; 1CDE000F2F3BBD26006AE6A1 /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = 1CA00002300000000000002A /* PostHog */; };
1CDEFBBF2F3B8736006AE6A1 /* Configuration.storekit in Resources */ = {isa = PBXBuildFile; fileRef = 1CDEFBBE2F3B8736006AE6A1 /* Configuration.storekit */; }; 1CDEFBBF2F3B8736006AE6A1 /* Configuration.storekit in Resources */ = {isa = PBXBuildFile; fileRef = 1CDEFBBE2F3B8736006AE6A1 /* Configuration.storekit */; };
1CDEFBC02F3B8736006AE6A1 /* Configuration.storekit in Resources */ = {isa = PBXBuildFile; fileRef = 1CDEFBBE2F3B8736006AE6A1 /* Configuration.storekit */; }; 1CDEFBC02F3B8736006AE6A1 /* Configuration.storekit in Resources */ = {isa = PBXBuildFile; fileRef = 1CDEFBBE2F3B8736006AE6A1 /* Configuration.storekit */; };
2EE4D94530F6BF39B26FB4D4 /* DayScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427CD9C91D43AB6A0302B4DD /* DayScreen.swift */; };
46F07FA9D330456697C9AC29 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD90B47278C7E7A001C4FEA /* WidgetKit.framework */; }; 46F07FA9D330456697C9AC29 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD90B47278C7E7A001C4FEA /* WidgetKit.framework */; };
4F1C717B7747918A459322CB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4D304CD05CC7C662CCD7DCB /* Foundation.framework */; }; 4F1C717B7747918A459322CB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4D304CD05CC7C662CCD7DCB /* Foundation.framework */; };
54259F7B3F4E959B3F4055E4 /* StreakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29E2A2FC314F88244CA946BF /* StreakTests.swift */; }; 54259F7B3F4E959B3F4055E4 /* StreakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29E2A2FC314F88244CA946BF /* StreakTests.swift */; };
9559409B5AEEAB40EBCB6AF9 /* VoteLogicsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD717F91BD65382B7DDFE3C4 /* VoteLogicsTests.swift */; };
EEB21B1CAA8EAEB497BD9FB3 /* DataControllerCRUDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5566271983AEDF1D33C34FE6 /* DataControllerCRUDTests.swift */; };
A018FE95582C04ED0F1806DC /* BaseUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29CE4110A0D8FBBAD7F92BDF /* BaseUITestCase.swift */; };
E0579E66FFBBF124AC625ACD /* WaitHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5354C23DD5FC67C1C97482F2 /* WaitHelpers.swift */; };
C26D40397E1AA24816FB3751 /* TabBarScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7CDDCB9C85BAE71C679C0BF /* TabBarScreen.swift */; };
2EE4D94530F6BF39B26FB4D4 /* DayScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427CD9C91D43AB6A0302B4DD /* DayScreen.swift */; };
A371ED1B0784315F96FFC6BD /* EntryDetailScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E35564DEA72EB6F8447CDAA /* EntryDetailScreen.swift */; };
92C1523E0398F866DB4CA027 /* SettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881CA8B21231D67DED575502 /* SettingsScreen.swift */; };
AA11110011111100AAAAAAAA /* AppLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA11111111111111AAAAAAAA /* AppLaunchTests.swift */; };
BB22220022222200BBBBBBBB /* MoodLoggingEmptyStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB22222222222222BBBBBBBB /* MoodLoggingEmptyStateTests.swift */; };
CC33330033333300CCCCCCCC /* MoodLoggingWithDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC33333333333333CCCCCCCC /* MoodLoggingWithDataTests.swift */; };
DD44440044444400DDDDDDDD /* EntryDetailTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD44444444444444DDDDDDDD /* EntryDetailTests.swift */; };
EE55550055555500EEEEEEEE /* SettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE55555555555555EEEEEEEE /* SettingsTests.swift */; };
FF66660066666600FFFFFFFF /* SecondaryTabTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF66666666666666FFFFFFFF /* SecondaryTabTests.swift */; };
A1B2C3D400000000C9D0E1F2 /* NoteEditorScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F6A7B8C9D0E1F2 /* NoteEditorScreen.swift */; };
B2C3D4E500000000D0E1F2A3 /* CustomizeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C3D4E5F6A7B8C9D0E1F2A3 /* CustomizeScreen.swift */; };
C3D4E500000000E1F2A3B4C5 /* OnboardingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D4E5F6A7B8C9D0E1F2A3B4 /* OnboardingScreen.swift */; };
D4E5F6A700000000F2A3B4C5 /* MoodReplacementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4E5F6A7B8C9D0E1F2A3B4C5 /* MoodReplacementTests.swift */; };
E5F6A7B800000000A3B4C5D6 /* EmptyStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5F6A7B8C9D0E1F2A3B4C5D6 /* EmptyStateTests.swift */; };
F6A7B8C900000000B4C5D6E7 /* EntryDeleteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6A7B8C9D0E1F2A3B4C5D6E7 /* EntryDeleteTests.swift */; };
A7B8C9D000000000C5D6E7F8 /* NotesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7B8C9D0E1F2A3B4C5D6E7F8 /* NotesTests.swift */; };
F75470AA2BA1E9EFF8F5265A /* LocalizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DC4C498A1185DC831F4593 /* LocalizationTests.swift */; };
E3482DB0421C12E11517BDC8 /* TrialBannerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21CD463209E0909393545D62 /* TrialBannerTests.swift */; };
A4B459F8CE7F5534DE4FADCA /* DarkModeStylesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8114D2CE12EC5392371BB415 /* DarkModeStylesTests.swift */; };
1AB245144C89927264D16645 /* InsightsEmptyStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6988985985DE9C29CFDFA96 /* InsightsEmptyStateTests.swift */; };
756B9857B0657D2DB2D6D4E2 /* AppResumeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0359E1D32D936859E5A0C9F3 /* AppResumeTests.swift */; };
6F9C9C4B50CF8C1769171FF9 /* NoteEditTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 469470483072085BE9E04E12 /* NoteEditTests.swift */; }; 6F9C9C4B50CF8C1769171FF9 /* NoteEditTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 469470483072085BE9E04E12 /* NoteEditTests.swift */; };
B8C9D0E100000000D6E7F8A9 /* MonthViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8C9D0E1F2A3B4C5D6E7F8A9 /* MonthViewTests.swift */; }; 756B9857B0657D2DB2D6D4E2 /* AppResumeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0359E1D32D936859E5A0C9F3 /* AppResumeTests.swift */; };
C9D0E1F200000000E7F8A9B0 /* SettingsActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D0E1F2A3B4C5D6E7F8A9B0 /* SettingsActionTests.swift */; }; 92C1523E0398F866DB4CA027 /* SettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881CA8B21231D67DED575502 /* SettingsScreen.swift */; };
D0E1F2A300000000F8A9B0C1 /* CustomizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E1F2A3B4C5D6E7F8A9B0C1 /* CustomizationTests.swift */; }; 9559409B5AEEAB40EBCB6AF9 /* VoteLogicsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD717F91BD65382B7DDFE3C4 /* VoteLogicsTests.swift */; };
E1F2A3B400000000A9B0C1D2 /* OnboardingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F2A3B4C5D6E7F8A9B0C1D2 /* OnboardingTests.swift */; }; A018FE95582C04ED0F1806DC /* BaseUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29CE4110A0D8FBBAD7F92BDF /* BaseUITestCase.swift */; };
F2A3B400000000B0C1D2E3F4 /* StabilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2A3B4C5D6E7F8A9B0C1D2E3 /* StabilityTests.swift */; }; A1B2C3D400000000C9D0E1F2 /* NoteEditorScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F6A7B8C9D0E1F2 /* NoteEditorScreen.swift */; };
A1B2C3D4E5F607080910ABCD /* DayViewViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4E5F60708091011ABCDE001 /* DayViewViewModelTests.swift */; };
A371ED1B0784315F96FFC6BD /* EntryDetailScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E35564DEA72EB6F8447CDAA /* EntryDetailScreen.swift */; };
A3B4C5D600000000C1D2E3F4 /* DataPersistenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3B4C5D6E7F8A9B0C1D2E3F4 /* DataPersistenceTests.swift */; }; A3B4C5D600000000C1D2E3F4 /* DataPersistenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3B4C5D6E7F8A9B0C1D2E3F4 /* DataPersistenceTests.swift */; };
B4C5D6E700000000D2E3F4A5 /* PaywallGateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4C5D6E7F8A9B0C1D2E3F4A5 /* PaywallGateTests.swift */; }; A4B459F8CE7F5534DE4FADCA /* DarkModeStylesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8114D2CE12EC5392371BB415 /* DarkModeStylesTests.swift */; };
C5D6E7F800000000E3F4A5B6 /* AppThemeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D6E7F8A9B0C1D2E3F4A5B6 /* AppThemeTests.swift */; }; A7B8C9D000000000C5D6E7F8 /* NotesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7B8C9D0E1F2A3B4C5D6E7F8 /* NotesTests.swift */; };
D6E7F8A900000000F4A5B6C7 /* IconPackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E7F8A9B0C1D2E3F4A5B6C7 /* IconPackTests.swift */; };
E7F8A9B000000000A5B6C7D8 /* PremiumCustomizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7F8A9B0C1D2E3F4A5B6C7D8 /* PremiumCustomizationTests.swift */; };
F8A9B0C100000000B6C7D8E9 /* HeaderMoodLoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A9B0C1D2E3F4A5B6C7D8E9 /* HeaderMoodLoggingTests.swift */; };
A9B0C1D200000000C7D8E9FA /* DayViewGroupingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B0C1D2E3F4A5B6C7D8E9FA /* DayViewGroupingTests.swift */; }; A9B0C1D200000000C7D8E9FA /* DayViewGroupingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B0C1D2E3F4A5B6C7D8E9FA /* DayViewGroupingTests.swift */; };
AA11110011111100AAAAAAAA /* AppLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA11111111111111AAAAAAAA /* AppLaunchTests.swift */; };
B0C1D2E300000000D8E9FA0B /* AllDayViewStylesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C1D2E3F4A5B6C7D8E9FA0B /* AllDayViewStylesTests.swift */; }; B0C1D2E300000000D8E9FA0B /* AllDayViewStylesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C1D2E3F4A5B6C7D8E9FA0B /* AllDayViewStylesTests.swift */; };
B2C3D4E500000000D0E1F2A3 /* CustomizeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C3D4E5F6A7B8C9D0E1F2A3 /* CustomizeScreen.swift */; };
B4C5D6E700000000D2E3F4A5 /* PaywallGateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4C5D6E7F8A9B0C1D2E3F4A5 /* PaywallGateTests.swift */; };
B8C9D0E100000000D6E7F8A9 /* MonthViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8C9D0E1F2A3B4C5D6E7F8A9 /* MonthViewTests.swift */; };
BB22220022222200BBBBBBBB /* MoodLoggingEmptyStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB22222222222222BBBBBBBB /* MoodLoggingEmptyStateTests.swift */; };
C1D2E3F400000000E9FA0B1C /* MonthViewInteractionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1D2E3F4A5B6C7D8E9FA0B1C /* MonthViewInteractionTests.swift */; }; C1D2E3F400000000E9FA0B1C /* MonthViewInteractionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1D2E3F4A5B6C7D8E9FA0B1C /* MonthViewInteractionTests.swift */; };
C26D40397E1AA24816FB3751 /* TabBarScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7CDDCB9C85BAE71C679C0BF /* TabBarScreen.swift */; };
C3D4E500000000E1F2A3B4C5 /* OnboardingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D4E5F6A7B8C9D0E1F2A3B4 /* OnboardingScreen.swift */; };
C5D6E7F800000000E3F4A5B6 /* AppThemeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D6E7F8A9B0C1D2E3F4A5B6 /* AppThemeTests.swift */; };
C9D0E1F200000000E7F8A9B0 /* SettingsActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D0E1F2A3B4C5D6E7F8A9B0 /* SettingsActionTests.swift */; };
CC33330033333300CCCCCCCC /* MoodLoggingWithDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC33333333333333CCCCCCCC /* MoodLoggingWithDataTests.swift */; };
D0E1F2A300000000F8A9B0C1 /* CustomizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E1F2A3B4C5D6E7F8A9B0C1 /* CustomizationTests.swift */; };
D1AD0A0469EADFB1446E9B09 /* YearViewDisplayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0246E9F406F872E5DEEB7269 /* YearViewDisplayTests.swift */; };
D4E5F6A700000000F2A3B4C5 /* MoodReplacementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4E5F6A7B8C9D0E1F2A3B4C5 /* MoodReplacementTests.swift */; };
D6E7F8A900000000F4A5B6C7 /* IconPackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E7F8A9B0C1D2E3F4A5B6C7 /* IconPackTests.swift */; };
DD44440044444400DDDDDDDD /* EntryDetailTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD44444444444444DDDDDDDD /* EntryDetailTests.swift */; };
E0579E66FFBBF124AC625ACD /* WaitHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5354C23DD5FC67C1C97482F2 /* WaitHelpers.swift */; };
E1F2A3B400000000A9B0C1D2 /* OnboardingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F2A3B4C5D6E7F8A9B0C1D2 /* OnboardingTests.swift */; };
E3482DB0421C12E11517BDC8 /* TrialBannerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21CD463209E0909393545D62 /* TrialBannerTests.swift */; };
E5F6A7B800000000A3B4C5D6 /* EmptyStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5F6A7B8C9D0E1F2A3B4C5D6 /* EmptyStateTests.swift */; };
E7F8A9B000000000A5B6C7D8 /* PremiumCustomizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7F8A9B0C1D2E3F4A5B6C7D8 /* PremiumCustomizationTests.swift */; };
EE55550055555500EEEEEEEE /* SettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE55555555555555EEEEEEEE /* SettingsTests.swift */; };
EEB21B1CAA8EAEB497BD9FB3 /* DataControllerCRUDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5566271983AEDF1D33C34FE6 /* DataControllerCRUDTests.swift */; };
F2A3B400000000B0C1D2E3F4 /* StabilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2A3B4C5D6E7F8A9B0C1D2E3 /* StabilityTests.swift */; };
F6A7B8C900000000B4C5D6E7 /* EntryDeleteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6A7B8C9D0E1F2A3B4C5D6E7 /* EntryDeleteTests.swift */; };
F75470AA2BA1E9EFF8F5265A /* LocalizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DC4C498A1185DC831F4593 /* LocalizationTests.swift */; };
F8A9B0C100000000B6C7D8E9 /* HeaderMoodLoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A9B0C1D2E3F4A5B6C7D8E9 /* HeaderMoodLoggingTests.swift */; };
FD30D4508D4C61AB10AC1E71 /* SettingsOnboardingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFDAD20AE6C6914EDD87DCBC /* SettingsOnboardingTests.swift */; };
FF66660066666600FFFFFFFF /* SecondaryTabTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF66666666666666FFFFFFFF /* SecondaryTabTests.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@@ -117,7 +119,10 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1C0DAB50279DB0FB003B1F21 /* Feels/Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Feels/Localizable.xcstrings; sourceTree = "<group>"; }; 0246E9F406F872E5DEEB7269 /* YearViewDisplayTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = YearViewDisplayTests.swift; sourceTree = "<group>"; };
0359E1D32D936859E5A0C9F3 /* AppResumeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppResumeTests.swift; sourceTree = "<group>"; };
17DC4C498A1185DC831F4593 /* LocalizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationTests.swift; sourceTree = "<group>"; };
1C0DAB50279DB0FB003B1F21 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Feels/Localizable.xcstrings; sourceTree = "<group>"; };
1CB4D09F28787D8A00902A56 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.5.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; }; 1CB4D09F28787D8A00902A56 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.5.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; };
1CD90AF5278C7DE0001C4FEA /* Feels.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Feels.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1CD90AF5278C7DE0001C4FEA /* Feels.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Feels.app; sourceTree = BUILT_PRODUCTS_DIR; };
1CD90AFB278C7DE0001C4FEA /* Feels.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Feels.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1CD90AFB278C7DE0001C4FEA /* Feels.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Feels.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -139,58 +144,57 @@
1CD90B70278C8000001C4FEA /* Feels (iOS)Dev.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = "Feels (iOS)Dev.entitlements"; sourceTree = "<group>"; }; 1CD90B70278C8000001C4FEA /* Feels (iOS)Dev.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = "Feels (iOS)Dev.entitlements"; sourceTree = "<group>"; };
1CDEFBBE2F3B8736006AE6A1 /* Configuration.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Configuration.storekit; sourceTree = "<group>"; }; 1CDEFBBE2F3B8736006AE6A1 /* Configuration.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Configuration.storekit; sourceTree = "<group>"; };
1E594AEAB5F046E3B3ED7C47 /* Feels Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Feels Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 1E594AEAB5F046E3B3ED7C47 /* Feels Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Feels Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
29E2A2FC314F88244CA946BF /* StreakTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = StreakTests.swift; sourceTree = "<group>"; };
5566271983AEDF1D33C34FE6 /* DataControllerCRUDTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataControllerCRUDTests.swift; sourceTree = "<group>"; };
9CFAE86F485C853DB3239DD9 /* IntegrationTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = IntegrationTests.swift; sourceTree = "<group>"; };
D4E5F60708091011ABCDE001 /* DayViewViewModelTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DayViewViewModelTests.swift; sourceTree = "<group>"; };
B60015D02A064FF582E232FD /* Feels Watch App/Feels Watch AppDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Feels Watch App/Feels Watch AppDebug.entitlements"; sourceTree = "<group>"; };
B8AB4CD73C2B4DC89C6FE84D /* Feels Watch App/Feels Watch App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Feels Watch App/Feels Watch App.entitlements"; sourceTree = "<group>"; };
DA0D74ACDD741CFA1F14F50F /* FeelsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FeelsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
DD717F91BD65382B7DDFE3C4 /* VoteLogicsTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = VoteLogicsTests.swift; sourceTree = "<group>"; };
F4D304CD05CC7C662CCD7DCB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
29CE4110A0D8FBBAD7F92BDF /* BaseUITestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseUITestCase.swift; sourceTree = "<group>"; };
5354C23DD5FC67C1C97482F2 /* WaitHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitHelpers.swift; sourceTree = "<group>"; };
C7CDDCB9C85BAE71C679C0BF /* TabBarScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarScreen.swift; sourceTree = "<group>"; };
427CD9C91D43AB6A0302B4DD /* DayScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayScreen.swift; sourceTree = "<group>"; };
7E35564DEA72EB6F8447CDAA /* EntryDetailScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryDetailScreen.swift; sourceTree = "<group>"; };
881CA8B21231D67DED575502 /* SettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = "<group>"; };
AA11111111111111AAAAAAAA /* AppLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLaunchTests.swift; sourceTree = "<group>"; };
BB22222222222222BBBBBBBB /* MoodLoggingEmptyStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoodLoggingEmptyStateTests.swift; sourceTree = "<group>"; };
CC33333333333333CCCCCCCC /* MoodLoggingWithDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoodLoggingWithDataTests.swift; sourceTree = "<group>"; };
DD44444444444444DDDDDDDD /* EntryDetailTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryDetailTests.swift; sourceTree = "<group>"; };
EE55555555555555EEEEEEEE /* SettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTests.swift; sourceTree = "<group>"; };
FF66666666666666FFFFFFFF /* SecondaryTabTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondaryTabTests.swift; sourceTree = "<group>"; };
A1B2C3D4E5F6A7B8C9D0E1F2 /* NoteEditorScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteEditorScreen.swift; sourceTree = "<group>"; };
B2C3D4E5F6A7B8C9D0E1F2A3 /* CustomizeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeScreen.swift; sourceTree = "<group>"; };
C3D4E5F6A7B8C9D0E1F2A3B4 /* OnboardingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingScreen.swift; sourceTree = "<group>"; };
D4E5F6A7B8C9D0E1F2A3B4C5 /* MoodReplacementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoodReplacementTests.swift; sourceTree = "<group>"; };
E5F6A7B8C9D0E1F2A3B4C5D6 /* EmptyStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyStateTests.swift; sourceTree = "<group>"; };
F6A7B8C9D0E1F2A3B4C5D6E7 /* EntryDeleteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryDeleteTests.swift; sourceTree = "<group>"; };
A7B8C9D0E1F2A3B4C5D6E7F8 /* NotesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesTests.swift; sourceTree = "<group>"; };
17DC4C498A1185DC831F4593 /* LocalizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationTests.swift; sourceTree = "<group>"; };
21CD463209E0909393545D62 /* TrialBannerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrialBannerTests.swift; sourceTree = "<group>"; }; 21CD463209E0909393545D62 /* TrialBannerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrialBannerTests.swift; sourceTree = "<group>"; };
8114D2CE12EC5392371BB415 /* DarkModeStylesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DarkModeStylesTests.swift; sourceTree = "<group>"; }; 29CE4110A0D8FBBAD7F92BDF /* BaseUITestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseUITestCase.swift; sourceTree = "<group>"; };
A6988985985DE9C29CFDFA96 /* InsightsEmptyStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsightsEmptyStateTests.swift; sourceTree = "<group>"; }; 29E2A2FC314F88244CA946BF /* StreakTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = StreakTests.swift; sourceTree = "<group>"; };
0359E1D32D936859E5A0C9F3 /* AppResumeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppResumeTests.swift; sourceTree = "<group>"; }; 427CD9C91D43AB6A0302B4DD /* DayScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayScreen.swift; sourceTree = "<group>"; };
469470483072085BE9E04E12 /* NoteEditTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteEditTests.swift; sourceTree = "<group>"; }; 469470483072085BE9E04E12 /* NoteEditTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteEditTests.swift; sourceTree = "<group>"; };
B8C9D0E1F2A3B4C5D6E7F8A9 /* MonthViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthViewTests.swift; sourceTree = "<group>"; }; 5354C23DD5FC67C1C97482F2 /* WaitHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitHelpers.swift; sourceTree = "<group>"; };
C9D0E1F2A3B4C5D6E7F8A9B0 /* SettingsActionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsActionTests.swift; sourceTree = "<group>"; }; 5566271983AEDF1D33C34FE6 /* DataControllerCRUDTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataControllerCRUDTests.swift; sourceTree = "<group>"; };
D0E1F2A3B4C5D6E7F8A9B0C1 /* CustomizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizationTests.swift; sourceTree = "<group>"; }; 7E35564DEA72EB6F8447CDAA /* EntryDetailScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryDetailScreen.swift; sourceTree = "<group>"; };
E1F2A3B4C5D6E7F8A9B0C1D2 /* OnboardingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTests.swift; sourceTree = "<group>"; }; 8114D2CE12EC5392371BB415 /* DarkModeStylesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DarkModeStylesTests.swift; sourceTree = "<group>"; };
F2A3B4C5D6E7F8A9B0C1D2E3 /* StabilityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StabilityTests.swift; sourceTree = "<group>"; }; 881CA8B21231D67DED575502 /* SettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = "<group>"; };
9CFAE86F485C853DB3239DD9 /* IntegrationTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = IntegrationTests.swift; sourceTree = "<group>"; };
A1B2C3D4E5F6A7B8C9D0E1F2 /* NoteEditorScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteEditorScreen.swift; sourceTree = "<group>"; };
A3B4C5D6E7F8A9B0C1D2E3F4 /* DataPersistenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataPersistenceTests.swift; sourceTree = "<group>"; }; A3B4C5D6E7F8A9B0C1D2E3F4 /* DataPersistenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataPersistenceTests.swift; sourceTree = "<group>"; };
B4C5D6E7F8A9B0C1D2E3F4A5 /* PaywallGateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallGateTests.swift; sourceTree = "<group>"; }; A6988985985DE9C29CFDFA96 /* InsightsEmptyStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsightsEmptyStateTests.swift; sourceTree = "<group>"; };
C5D6E7F8A9B0C1D2E3F4A5B6 /* AppThemeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppThemeTests.swift; sourceTree = "<group>"; }; A7B8C9D0E1F2A3B4C5D6E7F8 /* NotesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesTests.swift; sourceTree = "<group>"; };
D6E7F8A9B0C1D2E3F4A5B6C7 /* IconPackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconPackTests.swift; sourceTree = "<group>"; };
E7F8A9B0C1D2E3F4A5B6C7D8 /* PremiumCustomizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PremiumCustomizationTests.swift; sourceTree = "<group>"; };
F8A9B0C1D2E3F4A5B6C7D8E9 /* HeaderMoodLoggingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderMoodLoggingTests.swift; sourceTree = "<group>"; };
A9B0C1D2E3F4A5B6C7D8E9FA /* DayViewGroupingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayViewGroupingTests.swift; sourceTree = "<group>"; }; A9B0C1D2E3F4A5B6C7D8E9FA /* DayViewGroupingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayViewGroupingTests.swift; sourceTree = "<group>"; };
AA11111111111111AAAAAAAA /* AppLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLaunchTests.swift; sourceTree = "<group>"; };
B0C1D2E3F4A5B6C7D8E9FA0B /* AllDayViewStylesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllDayViewStylesTests.swift; sourceTree = "<group>"; }; B0C1D2E3F4A5B6C7D8E9FA0B /* AllDayViewStylesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllDayViewStylesTests.swift; sourceTree = "<group>"; };
B2C3D4E5F6A7B8C9D0E1F2A3 /* CustomizeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeScreen.swift; sourceTree = "<group>"; };
B4C5D6E7F8A9B0C1D2E3F4A5 /* PaywallGateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallGateTests.swift; sourceTree = "<group>"; };
B60015D02A064FF582E232FD /* Feels Watch AppDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Feels Watch App/Feels Watch AppDebug.entitlements"; sourceTree = "<group>"; };
B8AB4CD73C2B4DC89C6FE84D /* Feels Watch App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Feels Watch App/Feels Watch App.entitlements"; sourceTree = "<group>"; };
B8C9D0E1F2A3B4C5D6E7F8A9 /* MonthViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthViewTests.swift; sourceTree = "<group>"; };
BB22222222222222BBBBBBBB /* MoodLoggingEmptyStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoodLoggingEmptyStateTests.swift; sourceTree = "<group>"; };
C1D2E3F4A5B6C7D8E9FA0B1C /* MonthViewInteractionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthViewInteractionTests.swift; sourceTree = "<group>"; }; C1D2E3F4A5B6C7D8E9FA0B1C /* MonthViewInteractionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthViewInteractionTests.swift; sourceTree = "<group>"; };
C3D4E5F6A7B8C9D0E1F2A3B4 /* OnboardingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingScreen.swift; sourceTree = "<group>"; };
C5D6E7F8A9B0C1D2E3F4A5B6 /* AppThemeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppThemeTests.swift; sourceTree = "<group>"; };
C7CDDCB9C85BAE71C679C0BF /* TabBarScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarScreen.swift; sourceTree = "<group>"; };
C9D0E1F2A3B4C5D6E7F8A9B0 /* SettingsActionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsActionTests.swift; sourceTree = "<group>"; };
CC33333333333333CCCCCCCC /* MoodLoggingWithDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoodLoggingWithDataTests.swift; sourceTree = "<group>"; };
D0E1F2A3B4C5D6E7F8A9B0C1 /* CustomizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizationTests.swift; sourceTree = "<group>"; };
D4E5F60708091011ABCDE001 /* DayViewViewModelTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DayViewViewModelTests.swift; sourceTree = "<group>"; };
D4E5F6A7B8C9D0E1F2A3B4C5 /* MoodReplacementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoodReplacementTests.swift; sourceTree = "<group>"; };
D6E7F8A9B0C1D2E3F4A5B6C7 /* IconPackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconPackTests.swift; sourceTree = "<group>"; };
DA0D74ACDD741CFA1F14F50F /* FeelsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FeelsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
DD44444444444444DDDDDDDD /* EntryDetailTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryDetailTests.swift; sourceTree = "<group>"; };
DD717F91BD65382B7DDFE3C4 /* VoteLogicsTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = VoteLogicsTests.swift; sourceTree = "<group>"; };
DFDAD20AE6C6914EDD87DCBC /* SettingsOnboardingTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SettingsOnboardingTests.swift; sourceTree = "<group>"; };
E1F2A3B4C5D6E7F8A9B0C1D2 /* OnboardingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTests.swift; sourceTree = "<group>"; };
E5F6A7B8C9D0E1F2A3B4C5D6 /* EmptyStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyStateTests.swift; sourceTree = "<group>"; };
E7F8A9B0C1D2E3F4A5B6C7D8 /* PremiumCustomizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PremiumCustomizationTests.swift; sourceTree = "<group>"; };
EE55555555555555EEEEEEEE /* SettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTests.swift; sourceTree = "<group>"; };
F2A3B4C5D6E7F8A9B0C1D2E3 /* StabilityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StabilityTests.swift; sourceTree = "<group>"; };
F4D304CD05CC7C662CCD7DCB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
F6A7B8C9D0E1F2A3B4C5D6E7 /* EntryDeleteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryDeleteTests.swift; sourceTree = "<group>"; };
F8A9B0C1D2E3F4A5B6C7D8E9 /* HeaderMoodLoggingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderMoodLoggingTests.swift; sourceTree = "<group>"; };
FF66666666666666FFFFFFFF /* SecondaryTabTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondaryTabTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
1C000C162EE93AE3009C9ED5 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { 1C000C162EE93AE3009C9ED5 /* Exceptions for "Shared" folder in "FeelsWidgetExtension" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet; isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = ( membershipExceptions = (
AccessibilityIdentifiers.swift, AccessibilityIdentifiers.swift,
@@ -221,7 +225,7 @@
); );
target = 1CD90B44278C7E7A001C4FEA /* FeelsWidgetExtension */; target = 1CD90B44278C7E7A001C4FEA /* FeelsWidgetExtension */;
}; };
2166CE8AA7264FC2B4BFAAAC /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { 2166CE8AA7264FC2B4BFAAAC /* Exceptions for "Shared" folder in "Feels Watch App" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet; isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = ( membershipExceptions = (
Models/Mood.swift, Models/Mood.swift,
@@ -236,9 +240,41 @@
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFileSystemSynchronizedRootGroup section */
1C00073D2EE9388A009C9ED5 /* Shared */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2166CE8AA7264FC2B4BFAAAC /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 1C000C162EE93AE3009C9ED5 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Shared; sourceTree = "<group>"; }; 1C00073D2EE9388A009C9ED5 /* Shared */ = {
1C0009922EE938FC009C9ED5 /* FeelsWidget2 */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = FeelsWidget2; sourceTree = "<group>"; }; isa = PBXFileSystemSynchronizedRootGroup;
579031D619ED4B989145EEB1 /* Feels Watch App */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = "Feels Watch App"; sourceTree = "<group>"; }; exceptions = (
2166CE8AA7264FC2B4BFAAAC /* Exceptions for "Shared" folder in "Feels Watch App" target */,
1C000C162EE93AE3009C9ED5 /* Exceptions for "Shared" folder in "FeelsWidgetExtension" target */,
);
explicitFileTypes = {
};
explicitFolders = (
);
path = Shared;
sourceTree = "<group>";
};
1C0009922EE938FC009C9ED5 /* FeelsWidget2 */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
);
explicitFileTypes = {
};
explicitFolders = (
);
path = FeelsWidget2;
sourceTree = "<group>";
};
579031D619ED4B989145EEB1 /* Feels Watch App */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
);
explicitFileTypes = {
};
explicitFolders = (
);
path = "Feels Watch App";
sourceTree = "<group>";
};
/* End PBXFileSystemSynchronizedRootGroup section */ /* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -306,9 +342,9 @@
1CD90AE5278C7DDF001C4FEA = { 1CD90AE5278C7DDF001C4FEA = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
B8AB4CD73C2B4DC89C6FE84D /* Feels Watch App/Feels Watch App.entitlements */, B8AB4CD73C2B4DC89C6FE84D /* Feels Watch App.entitlements */,
B60015D02A064FF582E232FD /* Feels Watch App/Feels Watch AppDebug.entitlements */, B60015D02A064FF582E232FD /* Feels Watch AppDebug.entitlements */,
1C0DAB50279DB0FB003B1F21 /* Feels/Localizable.xcstrings */, 1C0DAB50279DB0FB003B1F21 /* Localizable.xcstrings */,
1CDEFBBE2F3B8736006AE6A1 /* Configuration.storekit */, 1CDEFBBE2F3B8736006AE6A1 /* Configuration.storekit */,
1CD90B6A278C7F75001C4FEA /* Feels (iOS).entitlements */, 1CD90B6A278C7F75001C4FEA /* Feels (iOS).entitlements */,
1CD90B70278C8000001C4FEA /* Feels (iOS)Dev.entitlements */, 1CD90B70278C8000001C4FEA /* Feels (iOS)Dev.entitlements */,
@@ -386,33 +422,12 @@
A9B0C1D2E3F4A5B6C7D8E9FA /* DayViewGroupingTests.swift */, A9B0C1D2E3F4A5B6C7D8E9FA /* DayViewGroupingTests.swift */,
B0C1D2E3F4A5B6C7D8E9FA0B /* AllDayViewStylesTests.swift */, B0C1D2E3F4A5B6C7D8E9FA0B /* AllDayViewStylesTests.swift */,
C1D2E3F4A5B6C7D8E9FA0B1C /* MonthViewInteractionTests.swift */, C1D2E3F4A5B6C7D8E9FA0B1C /* MonthViewInteractionTests.swift */,
0246E9F406F872E5DEEB7269 /* YearViewDisplayTests.swift */,
DFDAD20AE6C6914EDD87DCBC /* SettingsOnboardingTests.swift */,
); );
path = "Tests iOS"; path = "Tests iOS";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
3A62ED77167DA212DE1CCB7D /* Helpers */ = {
isa = PBXGroup;
children = (
29CE4110A0D8FBBAD7F92BDF /* BaseUITestCase.swift */,
5354C23DD5FC67C1C97482F2 /* WaitHelpers.swift */,
);
path = Helpers;
sourceTree = "<group>";
};
B697A2092711045D69109EA1 /* Screens */ = {
isa = PBXGroup;
children = (
C7CDDCB9C85BAE71C679C0BF /* TabBarScreen.swift */,
427CD9C91D43AB6A0302B4DD /* DayScreen.swift */,
7E35564DEA72EB6F8447CDAA /* EntryDetailScreen.swift */,
881CA8B21231D67DED575502 /* SettingsScreen.swift */,
A1B2C3D4E5F6A7B8C9D0E1F2 /* NoteEditorScreen.swift */,
B2C3D4E5F6A7B8C9D0E1F2A3 /* CustomizeScreen.swift */,
C3D4E5F6A7B8C9D0E1F2A3B4 /* OnboardingScreen.swift */,
);
path = Screens;
sourceTree = "<group>";
};
1CD90B11278C7DE0001C4FEA /* Tests macOS */ = { 1CD90B11278C7DE0001C4FEA /* Tests macOS */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -446,6 +461,15 @@
path = FeelsTests; path = FeelsTests;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
3A62ED77167DA212DE1CCB7D /* Helpers */ = {
isa = PBXGroup;
children = (
29CE4110A0D8FBBAD7F92BDF /* BaseUITestCase.swift */,
5354C23DD5FC67C1C97482F2 /* WaitHelpers.swift */,
);
path = Helpers;
sourceTree = "<group>";
};
88F4C25CA0D11FB136B0B8A6 /* iOS */ = { 88F4C25CA0D11FB136B0B8A6 /* iOS */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -454,6 +478,20 @@
name = iOS; name = iOS;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
B697A2092711045D69109EA1 /* Screens */ = {
isa = PBXGroup;
children = (
C7CDDCB9C85BAE71C679C0BF /* TabBarScreen.swift */,
427CD9C91D43AB6A0302B4DD /* DayScreen.swift */,
7E35564DEA72EB6F8447CDAA /* EntryDetailScreen.swift */,
881CA8B21231D67DED575502 /* SettingsScreen.swift */,
A1B2C3D4E5F6A7B8C9D0E1F2 /* NoteEditorScreen.swift */,
B2C3D4E5F6A7B8C9D0E1F2A3 /* CustomizeScreen.swift */,
C3D4E5F6A7B8C9D0E1F2A3B4 /* OnboardingScreen.swift */,
);
path = Screens;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@@ -676,7 +714,7 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
1C0DAB51279DB0FB003B1F21 /* Feels/Localizable.xcstrings in Resources */, 1C0DAB51279DB0FB003B1F21 /* Localizable.xcstrings in Resources */,
1CDEFBBF2F3B8736006AE6A1 /* Configuration.storekit in Resources */, 1CDEFBBF2F3B8736006AE6A1 /* Configuration.storekit in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -707,7 +745,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
1CDEFBC02F3B8736006AE6A1 /* Configuration.storekit in Resources */, 1CDEFBC02F3B8736006AE6A1 /* Configuration.storekit in Resources */,
1C0DAB52279DB0FB003B1F22 /* Feels/Localizable.xcstrings in Resources */, 1C0DAB52279DB0FB003B1F22 /* Localizable.xcstrings in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -787,6 +825,8 @@
A9B0C1D200000000C7D8E9FA /* DayViewGroupingTests.swift in Sources */, A9B0C1D200000000C7D8E9FA /* DayViewGroupingTests.swift in Sources */,
B0C1D2E300000000D8E9FA0B /* AllDayViewStylesTests.swift in Sources */, B0C1D2E300000000D8E9FA0B /* AllDayViewStylesTests.swift in Sources */,
C1D2E3F400000000E9FA0B1C /* MonthViewInteractionTests.swift in Sources */, C1D2E3F400000000E9FA0B1C /* MonthViewInteractionTests.swift in Sources */,
D1AD0A0469EADFB1446E9B09 /* YearViewDisplayTests.swift in Sources */,
FD30D4508D4C61AB10AC1E71 /* SettingsOnboardingTests.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@@ -125,6 +125,11 @@ enum AccessibilityID {
// MARK: - Year View // MARK: - Year View
enum YearView { enum YearView {
static let heatmap = "year_heatmap" static let heatmap = "year_heatmap"
static let donutChart = "year_donut_chart"
static let barChart = "year_bar_chart"
static let statsSection = "year_stats_section"
static func cardHeader(year: Int) -> String { "year_card_header_\(year)" }
static let shareButton = "year_share_button"
} }
// MARK: - Onboarding // MARK: - Onboarding

View File

@@ -25,15 +25,6 @@ struct OnboardingDay: View {
@ObservedObject var onboardingData: OnboardingData @ObservedObject var onboardingData: OnboardingData
var body: some View { var body: some View {
ZStack {
// Gradient background
LinearGradient(
colors: [Color(hex: "4facfe"), Color(hex: "00f2fe")],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
.ignoresSafeArea()
VStack(spacing: 0) { VStack(spacing: 0) {
Spacer() Spacer()
@@ -104,7 +95,14 @@ struct OnboardingDay: View {
.padding(.horizontal, 30) .padding(.horizontal, 30)
.padding(.bottom, 80) .padding(.bottom, 80)
} }
} .background(
LinearGradient(
colors: [Color(hex: "4facfe"), Color(hex: "00f2fe")],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
.ignoresSafeArea()
)
.accessibilityIdentifier(AccessibilityID.Onboarding.dayScreen) .accessibilityIdentifier(AccessibilityID.Onboarding.dayScreen)
} }
} }

View File

@@ -12,16 +12,6 @@ struct OnboardingStyle: View {
@State private var selectedTheme: AppTheme = .celestial @State private var selectedTheme: AppTheme = .celestial
var body: some View { var body: some View {
ZStack {
// Gradient background that updates with theme
LinearGradient(
colors: selectedTheme.previewColors,
startPoint: .topLeading,
endPoint: .bottomTrailing
)
.ignoresSafeArea()
.animation(.easeInOut(duration: 0.4), value: selectedTheme)
ScrollView(showsIndicators: false) { ScrollView(showsIndicators: false) {
VStack(spacing: 0) { VStack(spacing: 0) {
// Icon // Icon
@@ -86,7 +76,15 @@ struct OnboardingStyle: View {
.padding(.bottom, 80) .padding(.bottom, 80)
} }
} }
} .background(
LinearGradient(
colors: selectedTheme.previewColors,
startPoint: .topLeading,
endPoint: .bottomTrailing
)
.ignoresSafeArea()
.animation(.easeInOut(duration: 0.4), value: selectedTheme)
)
.onAppear { .onAppear {
// Apply default theme on appear // Apply default theme on appear
selectedTheme.apply() selectedTheme.apply()

View File

@@ -15,15 +15,6 @@ struct OnboardingSubscription: View {
let completionClosure: ((OnboardingData) -> Void) let completionClosure: ((OnboardingData) -> Void)
var body: some View { var body: some View {
ZStack {
// Gradient background
LinearGradient(
colors: [Color(hex: "11998e"), Color(hex: "38ef7d")],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
.ignoresSafeArea()
VStack(spacing: 0) { VStack(spacing: 0) {
Spacer() Spacer()
@@ -138,7 +129,14 @@ struct OnboardingSubscription: View {
.padding(.horizontal, 24) .padding(.horizontal, 24)
.padding(.bottom, 50) .padding(.bottom, 50)
} }
} .background(
LinearGradient(
colors: [Color(hex: "11998e"), Color(hex: "38ef7d")],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
.ignoresSafeArea()
)
.accessibilityIdentifier(AccessibilityID.Onboarding.subscriptionScreen) .accessibilityIdentifier(AccessibilityID.Onboarding.subscriptionScreen)
.sheet(isPresented: $showSubscriptionStore, onDismiss: { .sheet(isPresented: $showSubscriptionStore, onDismiss: {
// After subscription store closes, complete onboarding // After subscription store closes, complete onboarding

View File

@@ -16,15 +16,6 @@ struct OnboardingTitle: View {
@ObservedObject var onboardingData: OnboardingData @ObservedObject var onboardingData: OnboardingData
var body: some View { var body: some View {
ZStack {
Image("average", bundle: .main)
.foregroundColor(Color(UIColor.darkText))
.opacity(0.04)
.scaleEffect(1.2)
.padding(.bottom, 55)
.accessibilityHidden(true)
ScrollView { ScrollView {
VStack{ VStack{
Text(String(localized: "onboarding_title_title")) Text(String(localized: "onboarding_title_title"))
@@ -67,8 +58,17 @@ struct OnboardingTitle: View {
Spacer() Spacer()
} }
} }
.background {
ZStack {
Color.orange
Image("average", bundle: .main)
.foregroundColor(Color(UIColor.darkText))
.opacity(0.04)
.scaleEffect(1.2)
.padding(.bottom, 55)
.accessibilityHidden(true)
}
} }
.background(.orange)
} }
} }

View File

@@ -19,18 +19,7 @@ struct OnboardingWrapup: View {
} }
var body: some View { var body: some View {
ZStack {
GeometryReader { geometry in GeometryReader { geometry in
VStack {
Spacer()
Image("great", bundle: .main)
.foregroundColor(Color(UIColor.darkText))
.opacity(0.04)
.scaleEffect(1.2, anchor: .trailing)
.accessibilityHidden(true)
Spacer()
}
VStack { VStack {
ScrollView { ScrollView {
@@ -77,8 +66,20 @@ struct OnboardingWrapup: View {
} }
.frame(maxWidth: geometry.size.width) .frame(maxWidth: geometry.size.width)
} }
.background {
ZStack {
Color(hex: "31d158")
VStack {
Spacer()
Image("great", bundle: .main)
.foregroundColor(Color(UIColor.darkText))
.opacity(0.04)
.scaleEffect(1.2, anchor: .trailing)
.accessibilityHidden(true)
Spacer()
}
}
} }
.background(Color(hex: "31d158"))
} }
} }

View File

@@ -41,8 +41,6 @@ struct AddMoodHeaderView: View {
Text(String(imagePack.rawValue)) Text(String(imagePack.rawValue))
.hidden() .hidden()
theme.currentTheme.secondaryBGColor
VStack(spacing: 16) { VStack(spacing: 16) {
Text(ShowBasedOnVoteLogics.getVotingTitle(onboardingData: onboardingData)) Text(ShowBasedOnVoteLogics.getVotingTitle(onboardingData: onboardingData))
.font(.title2.bold()) .font(.title2.bold())
@@ -66,6 +64,7 @@ struct AddMoodHeaderView: View {
} }
} }
} }
.background(theme.currentTheme.secondaryBGColor)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.accessibilityIdentifier(AccessibilityID.DayView.moodHeader) .accessibilityIdentifier(AccessibilityID.DayView.moodHeader)

View File

@@ -24,16 +24,16 @@ struct CustomizeContentView: View {
Button(action: { showThemePicker = true }) { Button(action: { showThemePicker = true }) {
HStack(spacing: 16) { HStack(spacing: 16) {
// Emoji preview // Emoji preview
ZStack { Text("🎨")
.font(.title)
.frame(width: 56, height: 56)
.background(
LinearGradient( LinearGradient(
colors: [.purple.opacity(0.8), .blue.opacity(0.8), .cyan.opacity(0.8)], colors: [.purple.opacity(0.8), .blue.opacity(0.8), .cyan.opacity(0.8)],
startPoint: .topLeading, startPoint: .topLeading,
endPoint: .bottomTrailing endPoint: .bottomTrailing
) )
Text("🎨") )
.font(.title)
}
.frame(width: 56, height: 56)
.clipShape(RoundedRectangle(cornerRadius: 12)) .clipShape(RoundedRectangle(cornerRadius: 12))
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {

View File

@@ -259,14 +259,6 @@ struct AppThemePreviewSheet: View {
} }
private var heroSection: some View { private var heroSection: some View {
ZStack {
// Gradient background
LinearGradient(
colors: theme.previewColors + [theme.previewColors[0].opacity(0.5)],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
VStack(spacing: 16) { VStack(spacing: 16) {
Text(theme.emoji) Text(theme.emoji)
.font(.system(size: 72)) .font(.system(size: 72))
@@ -277,8 +269,15 @@ struct AppThemePreviewSheet: View {
.foregroundColor(.white) .foregroundColor(.white)
.shadow(color: .black.opacity(0.3), radius: 4, x: 0, y: 2) .shadow(color: .black.opacity(0.3), radius: 4, x: 0, y: 2)
} }
} .frame(maxWidth: .infinity)
.frame(height: 200) .frame(height: 200)
.background(
LinearGradient(
colors: theme.previewColors + [theme.previewColors[0].opacity(0.5)],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.clipShape(RoundedRectangle(cornerRadius: 20)) .clipShape(RoundedRectangle(cornerRadius: 20))
.padding(.horizontal, 20) .padding(.horizontal, 20)
.padding(.top, 16) .padding(.top, 16)

View File

@@ -22,9 +22,6 @@ struct DayFilterPickerView: View {
(Calendar.current.shortWeekdaySymbols[6], 7)] (Calendar.current.shortWeekdaySymbols[6], 7)]
var body: some View { var body: some View {
ZStack {
theme.currentTheme.secondaryBGColor
VStack { VStack {
HStack { HStack {
ForEach(weekdays.indices, id: \.self) { dayIdx in ForEach(weekdays.indices, id: \.self) { dayIdx in
@@ -55,7 +52,7 @@ struct DayFilterPickerView: View {
.foregroundColor(textColor) .foregroundColor(textColor)
} }
.padding() .padding()
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }

View File

@@ -50,8 +50,6 @@ struct IconPickerView: View {
] ]
var body: some View { var body: some View {
ZStack {
theme.currentTheme.secondaryBGColor
VStack { VStack {
ScrollView(.horizontal) { ScrollView(.horizontal) {
HStack { HStack {
@@ -90,7 +88,7 @@ struct IconPickerView: View {
.padding() .padding()
.cornerRadius(10) .cornerRadius(10)
} }
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }

View File

@@ -16,8 +16,6 @@ struct PersonalityPackPickerView: View {
private var textColor: Color { theme.currentTheme.labelColor } private var textColor: Color { theme.currentTheme.labelColor }
var body: some View { var body: some View {
ZStack {
theme.currentTheme.secondaryBGColor
VStack { VStack {
ForEach(PersonalityPack.allCases, id: \.self) { aPack in ForEach(PersonalityPack.allCases, id: \.self) { aPack in
VStack(spacing: 10) { VStack(spacing: 10) {
@@ -67,7 +65,7 @@ struct PersonalityPackPickerView: View {
} }
} }
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }

View File

@@ -15,8 +15,6 @@ struct ThemePickerView: View {
private var textColor: Color { selectedTheme.currentTheme.labelColor } private var textColor: Color { selectedTheme.currentTheme.labelColor }
var body: some View { var body: some View {
ZStack {
selectedTheme.currentTheme.secondaryBGColor
VStack { VStack {
HStack(spacing: 0) { HStack(spacing: 0) {
themeButton(for: .system) themeButton(for: .system)
@@ -27,7 +25,7 @@ struct ThemePickerView: View {
.padding(.top) .padding(.top)
} }
.padding() .padding()
} .background(selectedTheme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
.onAppear { .onAppear {

View File

@@ -18,9 +18,6 @@ struct VotingLayoutPickerView: View {
} }
var body: some View { var body: some View {
ZStack {
theme.currentTheme.secondaryBGColor
VStack(alignment: .leading, spacing: 12) { VStack(alignment: .leading, spacing: 12) {
Text("Voting Layout") Text("Voting Layout")
.font(.headline) .font(.headline)
@@ -68,7 +65,7 @@ struct VotingLayoutPickerView: View {
} }
.padding(.bottom) .padding(.bottom)
} }
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }

View File

@@ -118,8 +118,6 @@ struct SettingsContentView: View {
// MARK: - Reminder Time Button // MARK: - Reminder Time Button
private var reminderTimeButton: some View { private var reminderTimeButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
AnalyticsManager.shared.track(.reminderTimeTapped) AnalyticsManager.shared.track(.reminderTimeTapped)
showReminderTimePicker = true showReminderTimePicker = true
@@ -150,7 +148,7 @@ struct SettingsContentView: View {
.accessibilityLabel(String(localized: "Reminder Time")) .accessibilityLabel(String(localized: "Reminder Time"))
.accessibilityValue(formattedReminderTime) .accessibilityValue(formattedReminderTime)
.accessibilityHint(String(localized: "Opens time picker to change reminder time")) .accessibilityHint(String(localized: "Opens time picker to change reminder time"))
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
@@ -217,8 +215,6 @@ struct SettingsContentView: View {
} }
private var bypassSubscriptionToggle: some View { private var bypassSubscriptionToggle: some View {
ZStack {
theme.currentTheme.secondaryBGColor
HStack(spacing: 12) { HStack(spacing: 12) {
Image(systemName: "lock.open.fill") Image(systemName: "lock.open.fill")
.font(.title2) .font(.title2)
@@ -240,14 +236,12 @@ struct SettingsContentView: View {
.labelsHidden() .labelsHidden()
} }
.padding() .padding()
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var trialDateButton: some View { private var trialDateButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
VStack(spacing: 12) { VStack(spacing: 12) {
HStack(spacing: 12) { HStack(spacing: 12) {
Image(systemName: "calendar.badge.clock") Image(systemName: "calendar.badge.clock")
@@ -273,7 +267,7 @@ struct SettingsContentView: View {
} }
.padding() .padding()
} }
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
.sheet(isPresented: $showTrialDatePicker) { .sheet(isPresented: $showTrialDatePicker) {
@@ -308,8 +302,6 @@ struct SettingsContentView: View {
@State private var showTipsPreview = false @State private var showTipsPreview = false
private var animationLabButton: some View { private var animationLabButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button { Button {
showAnimationLab = true showAnimationLab = true
} label: { } label: {
@@ -336,7 +328,7 @@ struct SettingsContentView: View {
} }
.padding() .padding()
} }
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
.sheet(isPresented: $showAnimationLab) { .sheet(isPresented: $showAnimationLab) {
@@ -347,8 +339,6 @@ struct SettingsContentView: View {
} }
private var paywallPreviewButton: some View { private var paywallPreviewButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button { Button {
showPaywallPreview = true showPaywallPreview = true
} label: { } label: {
@@ -381,7 +371,7 @@ struct SettingsContentView: View {
} }
.padding() .padding()
} }
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
.sheet(isPresented: $showPaywallPreview) { .sheet(isPresented: $showPaywallPreview) {
@@ -392,8 +382,6 @@ struct SettingsContentView: View {
} }
private var tipsPreviewButton: some View { private var tipsPreviewButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button { Button {
showTipsPreview = true showTipsPreview = true
} label: { } label: {
@@ -426,7 +414,7 @@ struct SettingsContentView: View {
} }
.padding() .padding()
} }
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
.sheet(isPresented: $showTipsPreview) { .sheet(isPresented: $showTipsPreview) {
@@ -437,8 +425,6 @@ struct SettingsContentView: View {
} }
private var testNotificationsButton: some View { private var testNotificationsButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button { Button {
LocalNotification.sendAllPersonalityNotificationsForScreenshot() LocalNotification.sendAllPersonalityNotificationsForScreenshot()
} label: { } label: {
@@ -465,14 +451,12 @@ struct SettingsContentView: View {
} }
.padding() .padding()
} }
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var exportWidgetsButton: some View { private var exportWidgetsButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button { Button {
isExportingWidgets = true isExportingWidgets = true
Task { Task {
@@ -519,14 +503,12 @@ struct SettingsContentView: View {
.padding() .padding()
} }
.disabled(isExportingWidgets) .disabled(isExportingWidgets)
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var exportVotingLayoutsButton: some View { private var exportVotingLayoutsButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button { Button {
isExportingVotingLayouts = true isExportingVotingLayouts = true
Task { Task {
@@ -573,14 +555,12 @@ struct SettingsContentView: View {
.padding() .padding()
} }
.disabled(isExportingVotingLayouts) .disabled(isExportingVotingLayouts)
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var exportWatchViewsButton: some View { private var exportWatchViewsButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button { Button {
isExportingWatchViews = true isExportingWatchViews = true
Task { Task {
@@ -627,14 +607,12 @@ struct SettingsContentView: View {
.padding() .padding()
} }
.disabled(isExportingWatchViews) .disabled(isExportingWatchViews)
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var exportInsightsButton: some View { private var exportInsightsButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button { Button {
isExportingInsights = true isExportingInsights = true
Task { Task {
@@ -687,14 +665,12 @@ struct SettingsContentView: View {
.padding() .padding()
} }
.disabled(isExportingInsights) .disabled(isExportingInsights)
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var generateAndExportButton: some View { private var generateAndExportButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button { Button {
isGeneratingScreenshots = true isGeneratingScreenshots = true
Task { Task {
@@ -748,14 +724,12 @@ struct SettingsContentView: View {
.padding() .padding()
} }
.disabled(isGeneratingScreenshots) .disabled(isGeneratingScreenshots)
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var deleteHealthKitDataButton: some View { private var deleteHealthKitDataButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button { Button {
isDeletingHealthKitData = true isDeletingHealthKitData = true
healthKitDeleteResult = nil healthKitDeleteResult = nil
@@ -800,14 +774,12 @@ struct SettingsContentView: View {
.padding() .padding()
} }
.disabled(isDeletingHealthKitData) .disabled(isDeletingHealthKitData)
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var clearDataButton: some View { private var clearDataButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button { Button {
MoodLogger.shared.deleteAllData() MoodLogger.shared.deleteAllData()
} label: { } label: {
@@ -830,7 +802,7 @@ struct SettingsContentView: View {
} }
.padding() .padding()
} }
} .background(theme.currentTheme.secondaryBGColor)
.accessibilityIdentifier(AccessibilityID.Settings.clearDataButton) .accessibilityIdentifier(AccessibilityID.Settings.clearDataButton)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
@@ -842,8 +814,6 @@ struct SettingsContentView: View {
@ViewBuilder @ViewBuilder
private var privacyLockToggle: some View { private var privacyLockToggle: some View {
if authManager.canUseBiometrics { if authManager.canUseBiometrics {
ZStack {
theme.currentTheme.secondaryBGColor
HStack(spacing: 12) { HStack(spacing: 12) {
Image(systemName: authManager.biometricIcon) Image(systemName: authManager.biometricIcon)
.font(.title2) .font(.title2)
@@ -881,7 +851,7 @@ struct SettingsContentView: View {
.accessibilityHint(String(localized: "Require biometric authentication to open app")) .accessibilityHint(String(localized: "Require biometric authentication to open app"))
} }
.padding() .padding()
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
@@ -892,8 +862,6 @@ struct SettingsContentView: View {
@ObservedObject private var healthKitManager = HealthKitManager.shared @ObservedObject private var healthKitManager = HealthKitManager.shared
private var addTestDataButton: some View { private var addTestDataButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button { Button {
DataController.shared.populateTestData() DataController.shared.populateTestData()
} label: { } label: {
@@ -916,7 +884,7 @@ struct SettingsContentView: View {
} }
.padding() .padding()
} }
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
@@ -1031,8 +999,6 @@ struct SettingsContentView: View {
// MARK: - Export Data Button // MARK: - Export Data Button
private var exportDataButton: some View { private var exportDataButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
AnalyticsManager.shared.track(.exportTapped) AnalyticsManager.shared.track(.exportTapped)
showExportView = true showExportView = true
@@ -1062,14 +1028,12 @@ struct SettingsContentView: View {
}) })
.accessibilityLabel(String(localized: "Export Data")) .accessibilityLabel(String(localized: "Export Data"))
.accessibilityHint(String(localized: "Export your mood data as CSV or PDF")) .accessibilityHint(String(localized: "Export your mood data as CSV or PDF"))
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var showOnboardingButton: some View { private var showOnboardingButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
AnalyticsManager.shared.track(.onboardingReshown) AnalyticsManager.shared.track(.onboardingReshown)
showOnboarding.toggle() showOnboarding.toggle()
@@ -1080,14 +1044,13 @@ struct SettingsContentView: View {
.accessibilityHint(String(localized: "View the app introduction again")) .accessibilityHint(String(localized: "View the app introduction again"))
.accessibilityIdentifier(AccessibilityID.Settings.showOnboardingButton) .accessibilityIdentifier(AccessibilityID.Settings.showOnboardingButton)
.padding() .padding()
} .frame(maxWidth: .infinity)
.background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var canDelete: some View { private var canDelete: some View {
ZStack {
theme.currentTheme.secondaryBGColor
VStack { VStack {
Toggle(String(localized: "settings_use_delete_enable"), Toggle(String(localized: "settings_use_delete_enable"),
isOn: $deleteEnabled) isOn: $deleteEnabled)
@@ -1098,14 +1061,12 @@ struct SettingsContentView: View {
.accessibilityHint(String(localized: "Allow deleting mood entries by swiping")) .accessibilityHint(String(localized: "Allow deleting mood entries by swiping"))
.padding() .padding()
} }
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var eulaButton: some View { private var eulaButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
AnalyticsManager.shared.track(.eulaViewed) AnalyticsManager.shared.track(.eulaViewed)
if let url = URL(string: "https://feels.88oakapps.com/eula.html") { if let url = URL(string: "https://feels.88oakapps.com/eula.html") {
@@ -1117,14 +1078,13 @@ struct SettingsContentView: View {
}) })
.accessibilityHint(String(localized: "Opens End User License Agreement in browser")) .accessibilityHint(String(localized: "Opens End User License Agreement in browser"))
.padding() .padding()
} .frame(maxWidth: .infinity)
.background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var privacyButton: some View { private var privacyButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
AnalyticsManager.shared.track(.privacyPolicyViewed) AnalyticsManager.shared.track(.privacyPolicyViewed)
if let url = URL(string: "https://feels.88oakapps.com/privacy.html") { if let url = URL(string: "https://feels.88oakapps.com/privacy.html") {
@@ -1136,7 +1096,8 @@ struct SettingsContentView: View {
}) })
.accessibilityHint(String(localized: "Opens Privacy Policy in browser")) .accessibilityHint(String(localized: "Opens Privacy Policy in browser"))
.padding() .padding()
} .frame(maxWidth: .infinity)
.background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
@@ -1144,8 +1105,6 @@ struct SettingsContentView: View {
// MARK: - Analytics Toggle // MARK: - Analytics Toggle
private var analyticsToggle: some View { private var analyticsToggle: some View {
ZStack {
theme.currentTheme.secondaryBGColor
HStack(spacing: 12) { HStack(spacing: 12) {
Image(systemName: "chart.bar.xaxis") Image(systemName: "chart.bar.xaxis")
.font(.title2) .font(.title2)
@@ -1179,7 +1138,7 @@ struct SettingsContentView: View {
.accessibilityHint("Toggle anonymous usage analytics") .accessibilityHint("Toggle anonymous usage analytics")
} }
.padding() .padding()
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
@@ -1458,8 +1417,6 @@ struct SettingsView: View {
@ViewBuilder @ViewBuilder
private var privacyLockToggle: some View { private var privacyLockToggle: some View {
if authManager.canUseBiometrics { if authManager.canUseBiometrics {
ZStack {
theme.currentTheme.secondaryBGColor
HStack(spacing: 12) { HStack(spacing: 12) {
Image(systemName: authManager.biometricIcon) Image(systemName: authManager.biometricIcon)
.font(.title2) .font(.title2)
@@ -1495,7 +1452,7 @@ struct SettingsView: View {
.labelsHidden() .labelsHidden()
} }
.padding() .padding()
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
@@ -1607,8 +1564,6 @@ struct SettingsView: View {
// MARK: - Export Data Button // MARK: - Export Data Button
private var exportDataButton: some View { private var exportDataButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
AnalyticsManager.shared.track(.exportTapped) AnalyticsManager.shared.track(.exportTapped)
showExportView = true showExportView = true
@@ -1636,7 +1591,7 @@ struct SettingsView: View {
} }
.padding() .padding()
}) })
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
@@ -1656,8 +1611,6 @@ struct SettingsView: View {
} }
private var specialThanksCell: some View { private var specialThanksCell: some View {
ZStack {
theme.currentTheme.secondaryBGColor
VStack { VStack {
Button(action: { Button(action: {
AnalyticsManager.shared.track(.specialThanksViewed) AnalyticsManager.shared.track(.specialThanksViewed)
@@ -1683,14 +1636,12 @@ struct SettingsView: View {
.padding(.bottom) .padding(.bottom)
} }
} }
} .background(theme.currentTheme.secondaryBGColor)
.frame(minWidth: 0, maxWidth: .infinity) .frame(minWidth: 0, maxWidth: .infinity)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var addTestDataCell: some View { private var addTestDataCell: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
DataController.shared.populateTestData() DataController.shared.populateTestData()
}, label: { }, label: {
@@ -1698,14 +1649,13 @@ struct SettingsView: View {
.foregroundColor(textColor) .foregroundColor(textColor)
}) })
.padding() .padding()
} .frame(maxWidth: .infinity)
.background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var editFirstLaunchDatePast: some View { private var editFirstLaunchDatePast: some View {
ZStack {
theme.currentTheme.secondaryBGColor
HStack(spacing: 12) { HStack(spacing: 12) {
Image(systemName: "calendar.badge.clock") Image(systemName: "calendar.badge.clock")
.font(.title2) .font(.title2)
@@ -1729,7 +1679,7 @@ struct SettingsView: View {
.font(.subheadline.weight(.medium)) .font(.subheadline.weight(.medium))
} }
.padding() .padding()
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
.sheet(isPresented: $showTrialDatePicker) { .sheet(isPresented: $showTrialDatePicker) {
@@ -1760,8 +1710,6 @@ struct SettingsView: View {
} }
private var resetLaunchDate: some View { private var resetLaunchDate: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
firstLaunchDate = Date() firstLaunchDate = Date()
Task { Task {
@@ -1772,14 +1720,13 @@ struct SettingsView: View {
.foregroundColor(textColor) .foregroundColor(textColor)
}) })
.padding() .padding()
} .frame(maxWidth: .infinity)
.background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var clearDB: some View { private var clearDB: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
MoodLogger.shared.deleteAllData() MoodLogger.shared.deleteAllData()
}, label: { }, label: {
@@ -1787,14 +1734,13 @@ struct SettingsView: View {
.foregroundColor(textColor) .foregroundColor(textColor)
}) })
.padding() .padding()
} .frame(maxWidth: .infinity)
.background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var fixWeekday: some View { private var fixWeekday: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
DataController.shared.fixWrongWeekdays() DataController.shared.fixWrongWeekdays()
}, label: { }, label: {
@@ -1802,14 +1748,13 @@ struct SettingsView: View {
.foregroundColor(textColor) .foregroundColor(textColor)
}) })
.padding() .padding()
} .frame(maxWidth: .infinity)
.background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var whyBackgroundMode: some View { private var whyBackgroundMode: some View {
ZStack {
theme.currentTheme.secondaryBGColor
VStack { VStack {
Button(action: { Button(action: {
withAnimation{ withAnimation{
@@ -1826,14 +1771,12 @@ struct SettingsView: View {
.padding() .padding()
} }
} }
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var showOnboardingButton: some View { private var showOnboardingButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
AnalyticsManager.shared.track(.onboardingReshown) AnalyticsManager.shared.track(.onboardingReshown)
showOnboarding.toggle() showOnboarding.toggle()
@@ -1842,14 +1785,13 @@ struct SettingsView: View {
.foregroundColor(textColor) .foregroundColor(textColor)
}) })
.padding() .padding()
} .frame(maxWidth: .infinity)
.background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var eulaButton: some View { private var eulaButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
AnalyticsManager.shared.track(.eulaViewed) AnalyticsManager.shared.track(.eulaViewed)
openURL(URL(string: "https://feels.88oakapps.com/eula.html")!) openURL(URL(string: "https://feels.88oakapps.com/eula.html")!)
@@ -1858,14 +1800,13 @@ struct SettingsView: View {
.foregroundColor(textColor) .foregroundColor(textColor)
}) })
.padding() .padding()
} .frame(maxWidth: .infinity)
.background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var privacyButton: some View { private var privacyButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
AnalyticsManager.shared.track(.privacyPolicyViewed) AnalyticsManager.shared.track(.privacyPolicyViewed)
openURL(URL(string: "https://feels.88oakapps.com/privacy.html")!) openURL(URL(string: "https://feels.88oakapps.com/privacy.html")!)
@@ -1874,14 +1815,13 @@ struct SettingsView: View {
.foregroundColor(textColor) .foregroundColor(textColor)
}) })
.padding() .padding()
} .frame(maxWidth: .infinity)
.background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var analyticsToggle: some View { private var analyticsToggle: some View {
ZStack {
theme.currentTheme.secondaryBGColor
HStack(spacing: 12) { HStack(spacing: 12) {
Image(systemName: "chart.bar.xaxis") Image(systemName: "chart.bar.xaxis")
.font(.title2) .font(.title2)
@@ -1915,14 +1855,12 @@ struct SettingsView: View {
.accessibilityHint("Toggle anonymous usage analytics") .accessibilityHint("Toggle anonymous usage analytics")
} }
.padding() .padding()
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var canDelete: some View { private var canDelete: some View {
ZStack {
theme.currentTheme.secondaryBGColor
VStack { VStack {
Toggle(String(localized: "settings_use_delete_enable"), Toggle(String(localized: "settings_use_delete_enable"),
isOn: $deleteEnabled) isOn: $deleteEnabled)
@@ -1932,14 +1870,12 @@ struct SettingsView: View {
.foregroundColor(textColor) .foregroundColor(textColor)
.padding() .padding()
} }
} .background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var exportData: some View { private var exportData: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
showingExporter.toggle() showingExporter.toggle()
AnalyticsManager.shared.track(.exportTapped) AnalyticsManager.shared.track(.exportTapped)
@@ -1948,14 +1884,13 @@ struct SettingsView: View {
.foregroundColor(textColor) .foregroundColor(textColor)
}) })
.padding() .padding()
} .frame(maxWidth: .infinity)
.background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var importData: some View { private var importData: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
showingImporter.toggle() showingImporter.toggle()
AnalyticsManager.shared.track(.importTapped) AnalyticsManager.shared.track(.importTapped)
@@ -1964,14 +1899,13 @@ struct SettingsView: View {
.foregroundColor(textColor) .foregroundColor(textColor)
}) })
.padding() .padding()
} .frame(maxWidth: .infinity)
.background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var randomIcons: some View { private var randomIcons: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: { Button(action: {
var iconViews = [UIImage]() var iconViews = [UIImage]()
@@ -2095,7 +2029,8 @@ struct SettingsView: View {
.foregroundColor(textColor) .foregroundColor(textColor)
}) })
.padding() .padding()
} .frame(maxWidth: .infinity)
.background(theme.currentTheme.secondaryBGColor)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }

View File

@@ -522,6 +522,7 @@ struct YearCard: View, Equatable {
} }
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.accessibilityIdentifier(AccessibilityID.YearView.cardHeader(year: year))
Spacer() Spacer()
@@ -533,6 +534,7 @@ struct YearCard: View, Equatable {
.foregroundColor(textColor.opacity(0.6)) .foregroundColor(textColor.opacity(0.6))
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.accessibilityIdentifier(AccessibilityID.YearView.shareButton)
} }
.padding(.horizontal, 16) .padding(.horizontal, 16)
.padding(.vertical, 12) .padding(.vertical, 12)
@@ -543,6 +545,7 @@ struct YearCard: View, Equatable {
// Donut Chart // Donut Chart
MoodDonutChart(metrics: animatedMetrics, moodTint: moodTint) MoodDonutChart(metrics: animatedMetrics, moodTint: moodTint)
.frame(width: 100, height: 100) .frame(width: 100, height: 100)
.accessibilityIdentifier(AccessibilityID.YearView.donutChart)
// Bar Chart // Bar Chart
VStack(spacing: 6) { VStack(spacing: 6) {
@@ -574,10 +577,12 @@ struct YearCard: View, Equatable {
} }
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.accessibilityIdentifier(AccessibilityID.YearView.barChart)
} }
.padding(.horizontal, 16) .padding(.horizontal, 16)
.padding(.bottom, 12) .padding(.bottom, 12)
.transition(.opacity.combined(with: .move(edge: .top))) .transition(.opacity.combined(with: .move(edge: .top)))
.accessibilityIdentifier(AccessibilityID.YearView.statsSection)
} }
Divider() Divider()

View File

@@ -82,6 +82,14 @@ enum UITestID {
static let header = "insights_header" static let header = "insights_header"
} }
enum Year {
static let donutChart = "year_donut_chart"
static let barChart = "year_bar_chart"
static let statsSection = "year_stats_section"
static func cardHeader(year: Int) -> String { "year_card_header_\(year)" }
static let shareButton = "year_share_button"
}
enum Month { enum Month {
static let grid = "month_grid" static let grid = "month_grid"
} }

View File

@@ -0,0 +1,48 @@
//
// SettingsOnboardingTests.swift
// Tests iOS
//
// TC-124: Show onboarding from Settings.
//
import XCTest
final class SettingsOnboardingTests: BaseUITestCase {
override var seedFixture: String? { "empty" }
override var bypassSubscription: Bool { true }
/// TC-124: Tapping Show Onboarding in Settings replays the onboarding flow.
func testSettings_ShowOnboarding_OpensOnboardingFlow() {
let tabBar = TabBarScreen(app: app)
let settingsScreen = tabBar.tapSettings()
settingsScreen.assertVisible()
// Switch to the Settings sub-tab (not Customize)
settingsScreen.tapSettingsTab()
captureScreenshot(name: "settings_before_show_onboarding")
// Scroll to and tap "Show Onboarding" button
let showOnboardingBtn = app.element("settings_show_onboarding")
guard showOnboardingBtn.waitForExistence(timeout: 2) ||
app.swipeUntilExists(showOnboardingBtn, direction: .up, maxSwipes: 8) else {
captureScreenshot(name: "settings_show_onboarding_not_found")
XCTFail("Show Onboarding button not found in Settings")
return
}
showOnboardingBtn.tapWhenReady()
// The sheet may take a moment to animate in.
// Look for the onboarding welcome screen or any text unique to onboarding.
let welcomeScreen = app.element(UITestID.Onboarding.welcome)
let welcomeText = app.staticTexts["Welcome to Feels"]
let found = welcomeScreen.waitForExistence(timeout: 8) ||
welcomeText.waitForExistence(timeout: 3)
captureScreenshot(name: "settings_show_onboarding_result")
XCTAssertTrue(found,
"Onboarding should appear after tapping Show Onboarding"
)
}
}

View File

@@ -0,0 +1,51 @@
//
// YearViewDisplayTests.swift
// Tests iOS
//
// Year View display tests: donut chart, bar chart.
// TC-035, TC-036
//
import XCTest
final class YearViewDisplayTests: BaseUITestCase {
override var seedFixture: String? { "week_of_moods" }
override var bypassSubscription: Bool { true }
/// TC-035: Year View shows donut chart with mood distribution.
func testYearView_DonutChartVisible() {
let tabBar = TabBarScreen(app: app)
tabBar.tapYear()
// Wait for Year tab to be selected and content to load
XCTAssertTrue(tabBar.yearTab.isSelected, "Year tab should be selected")
captureScreenshot(name: "year_view_loaded")
// The donut chart is inside the stats section of the first YearCard.
// Try finding by accessibility identifier first, then fall back to presence of "days" text.
let donutChart = app.element(UITestID.Year.donutChart)
let found = donutChart.waitForExistence(timeout: 8) ||
app.swipeUntilExists(donutChart, direction: .up, maxSwipes: 3, timeoutPerTry: 1.0)
captureScreenshot(name: "year_donut_chart")
XCTAssertTrue(found, "Donut chart should be visible in Year View")
}
/// TC-036: Year View shows bar chart with mood percentages.
func testYearView_BarChartVisible() {
let tabBar = TabBarScreen(app: app)
tabBar.tapYear()
XCTAssertTrue(tabBar.yearTab.isSelected, "Year tab should be selected")
let barChart = app.element(UITestID.Year.barChart)
let found = barChart.waitForExistence(timeout: 8) ||
app.swipeUntilExists(barChart, direction: .up, maxSwipes: 3, timeoutPerTry: 1.0)
captureScreenshot(name: "year_bar_chart")
XCTAssertTrue(found, "Bar chart should be visible in Year View")
}
}

Binary file not shown.

View File

@@ -1,53 +1,71 @@
# UI Test Prompt Template # UI Test Prompt Template (QA Plan Driven)
Copy/paste this prompt into Codex or Claude and replace the placeholders. Copy/paste this into Claude (or Codex), then replace placeholders.
```md ```md
Create an iOS UI test for this behavior: Task:
Create 3 solid iOS UI tests from the QA plan that compile and run reliably using the existing test architecture.
<DESCRIBE USER FLOW / EXPECTED BEHAVIOR> Project:
- Root: /Users/treyt/Desktop/code/Feels
- QA source: /Users/treyt/Desktop/code/Feels/docs/Feels_QA_Test_Plan.xlsx
Repository context: Optional explicit test IDs/names from me (if provided, prioritize these):
- Project root: /Users/treyt/Desktop/code/Feels <PASTE_TEST_IDS_AND_NAMES_HERE_OR_LEAVE_EMPTY>
- Follow these files strictly:
- /Users/treyt/Desktop/code/Feels/docs/XCUITest-Authoring.md
- /Users/treyt/Desktop/code/Feels/AGENTS.md
- /Users/treyt/Desktop/code/Feels/Tests iOS/README.md
- /Users/treyt/Desktop/code/Feels/Tests iOS/Helpers/BaseUITestCase.swift
- /Users/treyt/Desktop/code/Feels/Tests iOS/Helpers/WaitHelpers.swift
- /Users/treyt/Desktop/code/Feels/Shared/AccessibilityIdentifiers.swift
Implementation requirements: Mandatory references (read before coding):
1. Use the established pattern: - /Users/treyt/Desktop/code/Feels/docs/XCUITest-Authoring.md
- /Users/treyt/Desktop/code/Feels/AGENTS.md
- /Users/treyt/Desktop/code/Feels/Tests iOS/README.md
- /Users/treyt/Desktop/code/Feels/Tests iOS/Helpers/BaseUITestCase.swift
- /Users/treyt/Desktop/code/Feels/Tests iOS/Helpers/WaitHelpers.swift
- /Users/treyt/Desktop/code/Feels/Shared/AccessibilityIdentifiers.swift
- Existing suites in /Users/treyt/Desktop/code/Feels/Tests iOS/ (use them for style/patterns)
Selection rules:
1. If I pasted specific QA IDs/names, use those first.
2. If fewer than 3 are provided, choose remaining from the spreadsheet.
3. Pick the easiest automatable tests (low setup complexity, deterministic UI state, no external dependencies).
4. Skip cases likely to be flaky or blocked (network dependency, unstable animation-only behavior, uncertain app hooks).
5. Briefly justify why each selected test is “easy + stable”.
Implementation rules (do not reinvent):
1. Reuse existing architecture only:
- `BaseUITestCase` - `BaseUITestCase`
- `UITestID` / accessibility identifier selectors first - `UITestID` and accessibility identifiers
- screen objects in `Tests iOS/Screens/` - screen objects under `Tests iOS/Screens/`
- wait helpers (`tapWhenReady`, `waitForExistence`, `waitForDisappearance`) - wait helpers (`tapWhenReady`, `waitForExistence`, `waitForDisappearance`, etc.)
2. Do NOT use `sleep(...)`. 2. No `sleep(...)`.
3. Do NOT rely on localized/raw text selectors as primary selectors. 3. No raw/localized text selectors as primary locators.
4. If needed, add missing accessibility IDs in app code and wire them into tests. 4. Add missing accessibility IDs only when required, then wire them through current helper patterns.
5. Keep the test deterministic using fixture + launch flags from `BaseUITestCase`. 5. Keep tests deterministic with fixtures and launch flags from `BaseUITestCase`.
6. Add screenshots at meaningful checkpoints for triage. 6. Follow existing naming/style conventions from current passing tests.
7. Add screenshots at meaningful checkpoints for triage.
Test setup choices: Flake-resistance checklist (must satisfy):
- Suggested suite file: `Tests iOS/<SUITE_NAME>Tests.swift` - Each test has deterministic starting state (fixture + launch args).
- Suggested test method: `test<FEATURE>_<BEHAVIOR>()` - No arbitrary timing waits.
- Fixture to use: `<empty | single_mood | week_of_moods>` - Assertions target stable identifiers.
- Launch overrides if needed: - Test does not depend on current date text formatting unless already stabilized by existing helpers.
- `skipOnboarding = <true/false>` - If a new selector is needed, add app-side accessibility identifier first.
- `bypassSubscription = <true/false>`
- `expireTrial = <true/false>`
Validation requirements: Deliverable shape:
1. Run targeted suite: - Prefer one suite file with 3 test methods, unless existing suite placement is clearly better.
- `xcodebuild -project Feels.xcodeproj -scheme "Feels (iOS)" -destination 'platform=iOS Simulator,name=iPhone 16 Pro' -only-testing:"Tests iOS/<SUITE_NAME>" test` - Method naming: `test<Feature>_<Behavior>()`.
2. Report pass/fail summary. - Keep helper logic in screen objects/helpers instead of duplicating in test body.
3. If failures occur, fix and rerun until green.
Validation gates (required before done):
1. Run only the 3 new tests (not full suite), e.g.:
- `xcodebuild -project Feels.xcodeproj -scheme "Feels (iOS)" -destination 'platform=iOS Simulator,name=iPhone 16 Pro' -only-testing:"Tests iOS/<SuiteName>/testA" -only-testing:"Tests iOS/<SuiteName>/testB" -only-testing:"Tests iOS/<SuiteName>/testC" test`
2. If failures occur, fix and rerun until green.
3. Run the same targeted command a second time to check flakiness.
4. Only mark complete if both runs pass.
Output format: Output format:
1. Files changed 1. Selected QA test IDs/names with short reason for selection.
2. Why each change was needed 2. Files changed.
3. Test run summary 3. Key architecture decisions (how existing patterns were reused).
4. Any follow-up risks/gaps 4. Exact test command(s) executed.
5. Run results for pass #1 and pass #2.
6. Any residual risk/gaps.
``` ```