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:
@@ -8,9 +8,9 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
06E4767B5977FAC8B644FC92 /* IntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CFAE86F485C853DB3239DD9 /* IntegrationTests.swift */; };
|
||||
A1B2C3D4E5F607080910ABCD /* DayViewViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4E5F60708091011ABCDE001 /* DayViewViewModelTests.swift */; };
|
||||
1C0DAB51279DB0FB003B1F21 /* Feels/Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 1C0DAB50279DB0FB003B1F21 /* Feels/Localizable.xcstrings */; };
|
||||
1C0DAB52279DB0FB003B1F22 /* Feels/Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 1C0DAB50279DB0FB003B1F21 /* Feels/Localizable.xcstrings */; };
|
||||
1AB245144C89927264D16645 /* InsightsEmptyStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6988985985DE9C29CFDFA96 /* InsightsEmptyStateTests.swift */; };
|
||||
1C0DAB51279DB0FB003B1F21 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 1C0DAB50279DB0FB003B1F21 /* Localizable.xcstrings */; };
|
||||
1C0DAB52279DB0FB003B1F22 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 1C0DAB50279DB0FB003B1F21 /* Localizable.xcstrings */; };
|
||||
1C9566442EF8F5F70032E68F /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = 1C9566432EF8F5F70032E68F /* Algorithms */; };
|
||||
1CB4D0A028787D8A00902A56 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CB4D09F28787D8A00902A56 /* StoreKit.framework */; };
|
||||
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 */; };
|
||||
1CDEFBBF2F3B8736006AE6A1 /* 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 */; };
|
||||
4F1C717B7747918A459322CB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4D304CD05CC7C662CCD7DCB /* Foundation.framework */; };
|
||||
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 */; };
|
||||
B8C9D0E100000000D6E7F8A9 /* MonthViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8C9D0E1F2A3B4C5D6E7F8A9 /* MonthViewTests.swift */; };
|
||||
C9D0E1F200000000E7F8A9B0 /* SettingsActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D0E1F2A3B4C5D6E7F8A9B0 /* SettingsActionTests.swift */; };
|
||||
D0E1F2A300000000F8A9B0C1 /* CustomizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E1F2A3B4C5D6E7F8A9B0C1 /* CustomizationTests.swift */; };
|
||||
E1F2A3B400000000A9B0C1D2 /* OnboardingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F2A3B4C5D6E7F8A9B0C1D2 /* OnboardingTests.swift */; };
|
||||
F2A3B400000000B0C1D2E3F4 /* StabilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2A3B4C5D6E7F8A9B0C1D2E3 /* StabilityTests.swift */; };
|
||||
756B9857B0657D2DB2D6D4E2 /* AppResumeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0359E1D32D936859E5A0C9F3 /* AppResumeTests.swift */; };
|
||||
92C1523E0398F866DB4CA027 /* SettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881CA8B21231D67DED575502 /* SettingsScreen.swift */; };
|
||||
9559409B5AEEAB40EBCB6AF9 /* VoteLogicsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD717F91BD65382B7DDFE3C4 /* VoteLogicsTests.swift */; };
|
||||
A018FE95582C04ED0F1806DC /* BaseUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29CE4110A0D8FBBAD7F92BDF /* BaseUITestCase.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 */; };
|
||||
B4C5D6E700000000D2E3F4A5 /* PaywallGateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4C5D6E7F8A9B0C1D2E3F4A5 /* PaywallGateTests.swift */; };
|
||||
C5D6E7F800000000E3F4A5B6 /* AppThemeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D6E7F8A9B0C1D2E3F4A5B6 /* AppThemeTests.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 */; };
|
||||
A4B459F8CE7F5534DE4FADCA /* DarkModeStylesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8114D2CE12EC5392371BB415 /* DarkModeStylesTests.swift */; };
|
||||
A7B8C9D000000000C5D6E7F8 /* NotesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7B8C9D0E1F2A3B4C5D6E7F8 /* NotesTests.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 */; };
|
||||
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 */; };
|
||||
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 */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -117,7 +119,10 @@
|
||||
/* End PBXCopyFilesBuildPhase 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; };
|
||||
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; };
|
||||
@@ -139,58 +144,57 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
8114D2CE12EC5392371BB415 /* DarkModeStylesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DarkModeStylesTests.swift; sourceTree = "<group>"; };
|
||||
A6988985985DE9C29CFDFA96 /* InsightsEmptyStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsightsEmptyStateTests.swift; sourceTree = "<group>"; };
|
||||
0359E1D32D936859E5A0C9F3 /* AppResumeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppResumeTests.swift; sourceTree = "<group>"; };
|
||||
29CE4110A0D8FBBAD7F92BDF /* BaseUITestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseUITestCase.swift; sourceTree = "<group>"; };
|
||||
29E2A2FC314F88244CA946BF /* StreakTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = StreakTests.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>"; };
|
||||
B8C9D0E1F2A3B4C5D6E7F8A9 /* MonthViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthViewTests.swift; sourceTree = "<group>"; };
|
||||
C9D0E1F2A3B4C5D6E7F8A9B0 /* SettingsActionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsActionTests.swift; sourceTree = "<group>"; };
|
||||
D0E1F2A3B4C5D6E7F8A9B0C1 /* CustomizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizationTests.swift; sourceTree = "<group>"; };
|
||||
E1F2A3B4C5D6E7F8A9B0C1D2 /* OnboardingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTests.swift; sourceTree = "<group>"; };
|
||||
F2A3B4C5D6E7F8A9B0C1D2E3 /* StabilityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StabilityTests.swift; sourceTree = "<group>"; };
|
||||
5354C23DD5FC67C1C97482F2 /* WaitHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitHelpers.swift; sourceTree = "<group>"; };
|
||||
5566271983AEDF1D33C34FE6 /* DataControllerCRUDTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataControllerCRUDTests.swift; sourceTree = "<group>"; };
|
||||
7E35564DEA72EB6F8447CDAA /* EntryDetailScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryDetailScreen.swift; sourceTree = "<group>"; };
|
||||
8114D2CE12EC5392371BB415 /* DarkModeStylesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DarkModeStylesTests.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>"; };
|
||||
B4C5D6E7F8A9B0C1D2E3F4A5 /* PaywallGateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallGateTests.swift; sourceTree = "<group>"; };
|
||||
C5D6E7F8A9B0C1D2E3F4A5B6 /* AppThemeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppThemeTests.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>"; };
|
||||
A6988985985DE9C29CFDFA96 /* InsightsEmptyStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsightsEmptyStateTests.swift; sourceTree = "<group>"; };
|
||||
A7B8C9D0E1F2A3B4C5D6E7F8 /* NotesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesTests.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>"; };
|
||||
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>"; };
|
||||
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 */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
1C000C162EE93AE3009C9ED5 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
||||
1C000C162EE93AE3009C9ED5 /* Exceptions for "Shared" folder in "FeelsWidgetExtension" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
AccessibilityIdentifiers.swift,
|
||||
@@ -221,7 +225,7 @@
|
||||
);
|
||||
target = 1CD90B44278C7E7A001C4FEA /* FeelsWidgetExtension */;
|
||||
};
|
||||
2166CE8AA7264FC2B4BFAAAC /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
||||
2166CE8AA7264FC2B4BFAAAC /* Exceptions for "Shared" folder in "Feels Watch App" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Models/Mood.swift,
|
||||
@@ -236,9 +240,41 @@
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
1C00073D2EE9388A009C9ED5 /* Shared */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2166CE8AA7264FC2B4BFAAAC /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 1C000C162EE93AE3009C9ED5 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Shared; sourceTree = "<group>"; };
|
||||
1C0009922EE938FC009C9ED5 /* FeelsWidget2 */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = FeelsWidget2; sourceTree = "<group>"; };
|
||||
579031D619ED4B989145EEB1 /* Feels Watch App */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = "Feels Watch App"; sourceTree = "<group>"; };
|
||||
1C00073D2EE9388A009C9ED5 /* Shared */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
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 */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -306,9 +342,9 @@
|
||||
1CD90AE5278C7DDF001C4FEA = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B8AB4CD73C2B4DC89C6FE84D /* Feels Watch App/Feels Watch App.entitlements */,
|
||||
B60015D02A064FF582E232FD /* Feels Watch App/Feels Watch AppDebug.entitlements */,
|
||||
1C0DAB50279DB0FB003B1F21 /* Feels/Localizable.xcstrings */,
|
||||
B8AB4CD73C2B4DC89C6FE84D /* Feels Watch App.entitlements */,
|
||||
B60015D02A064FF582E232FD /* Feels Watch AppDebug.entitlements */,
|
||||
1C0DAB50279DB0FB003B1F21 /* Localizable.xcstrings */,
|
||||
1CDEFBBE2F3B8736006AE6A1 /* Configuration.storekit */,
|
||||
1CD90B6A278C7F75001C4FEA /* Feels (iOS).entitlements */,
|
||||
1CD90B70278C8000001C4FEA /* Feels (iOS)Dev.entitlements */,
|
||||
@@ -386,33 +422,12 @@
|
||||
A9B0C1D2E3F4A5B6C7D8E9FA /* DayViewGroupingTests.swift */,
|
||||
B0C1D2E3F4A5B6C7D8E9FA0B /* AllDayViewStylesTests.swift */,
|
||||
C1D2E3F4A5B6C7D8E9FA0B1C /* MonthViewInteractionTests.swift */,
|
||||
0246E9F406F872E5DEEB7269 /* YearViewDisplayTests.swift */,
|
||||
DFDAD20AE6C6914EDD87DCBC /* SettingsOnboardingTests.swift */,
|
||||
);
|
||||
path = "Tests iOS";
|
||||
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 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -446,6 +461,15 @@
|
||||
path = FeelsTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3A62ED77167DA212DE1CCB7D /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
29CE4110A0D8FBBAD7F92BDF /* BaseUITestCase.swift */,
|
||||
5354C23DD5FC67C1C97482F2 /* WaitHelpers.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
88F4C25CA0D11FB136B0B8A6 /* iOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -454,6 +478,20 @@
|
||||
name = iOS;
|
||||
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 */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -676,7 +714,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1C0DAB51279DB0FB003B1F21 /* Feels/Localizable.xcstrings in Resources */,
|
||||
1C0DAB51279DB0FB003B1F21 /* Localizable.xcstrings in Resources */,
|
||||
1CDEFBBF2F3B8736006AE6A1 /* Configuration.storekit in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -707,7 +745,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1CDEFBC02F3B8736006AE6A1 /* Configuration.storekit in Resources */,
|
||||
1C0DAB52279DB0FB003B1F22 /* Feels/Localizable.xcstrings in Resources */,
|
||||
1C0DAB52279DB0FB003B1F22 /* Localizable.xcstrings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -787,6 +825,8 @@
|
||||
A9B0C1D200000000C7D8E9FA /* DayViewGroupingTests.swift in Sources */,
|
||||
B0C1D2E300000000D8E9FA0B /* AllDayViewStylesTests.swift in Sources */,
|
||||
C1D2E3F400000000E9FA0B1C /* MonthViewInteractionTests.swift in Sources */,
|
||||
D1AD0A0469EADFB1446E9B09 /* YearViewDisplayTests.swift in Sources */,
|
||||
FD30D4508D4C61AB10AC1E71 /* SettingsOnboardingTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -125,6 +125,11 @@ enum AccessibilityID {
|
||||
// MARK: - Year View
|
||||
enum YearView {
|
||||
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
|
||||
|
||||
@@ -25,16 +25,7 @@ struct OnboardingDay: View {
|
||||
@ObservedObject var onboardingData: OnboardingData
|
||||
|
||||
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()
|
||||
|
||||
// Icon
|
||||
@@ -103,8 +94,15 @@ struct OnboardingDay: View {
|
||||
}
|
||||
.padding(.horizontal, 30)
|
||||
.padding(.bottom, 80)
|
||||
}
|
||||
}
|
||||
.background(
|
||||
LinearGradient(
|
||||
colors: [Color(hex: "4facfe"), Color(hex: "00f2fe")],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
.ignoresSafeArea()
|
||||
)
|
||||
.accessibilityIdentifier(AccessibilityID.Onboarding.dayScreen)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,17 +12,7 @@ struct OnboardingStyle: View {
|
||||
@State private var selectedTheme: AppTheme = .celestial
|
||||
|
||||
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) {
|
||||
// Icon
|
||||
ZStack {
|
||||
@@ -85,8 +75,16 @@ struct OnboardingStyle: View {
|
||||
.padding(.top, 24)
|
||||
.padding(.bottom, 80)
|
||||
}
|
||||
}
|
||||
}
|
||||
.background(
|
||||
LinearGradient(
|
||||
colors: selectedTheme.previewColors,
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
.ignoresSafeArea()
|
||||
.animation(.easeInOut(duration: 0.4), value: selectedTheme)
|
||||
)
|
||||
.onAppear {
|
||||
// Apply default theme on appear
|
||||
selectedTheme.apply()
|
||||
|
||||
@@ -15,16 +15,7 @@ struct OnboardingSubscription: View {
|
||||
let completionClosure: ((OnboardingData) -> Void)
|
||||
|
||||
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()
|
||||
|
||||
// Crown icon
|
||||
@@ -137,8 +128,15 @@ struct OnboardingSubscription: View {
|
||||
}
|
||||
.padding(.horizontal, 24)
|
||||
.padding(.bottom, 50)
|
||||
}
|
||||
}
|
||||
.background(
|
||||
LinearGradient(
|
||||
colors: [Color(hex: "11998e"), Color(hex: "38ef7d")],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
.ignoresSafeArea()
|
||||
)
|
||||
.accessibilityIdentifier(AccessibilityID.Onboarding.subscriptionScreen)
|
||||
.sheet(isPresented: $showSubscriptionStore, onDismiss: {
|
||||
// After subscription store closes, complete onboarding
|
||||
|
||||
@@ -16,44 +16,35 @@ struct OnboardingTitle: View {
|
||||
@ObservedObject var onboardingData: OnboardingData
|
||||
|
||||
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 {
|
||||
VStack{
|
||||
Text(String(localized: "onboarding_title_title"))
|
||||
.font(.title)
|
||||
.foregroundColor(Color(UIColor.white))
|
||||
.padding([.trailing, .leading], 55)
|
||||
.padding([.top], 25)
|
||||
|
||||
ForEach(OnboardingTitle.titleOptions, id: \.self) { option in
|
||||
Button(action: {
|
||||
ScrollView {
|
||||
VStack{
|
||||
Text(String(localized: "onboarding_title_title"))
|
||||
.font(.title)
|
||||
.foregroundColor(Color(UIColor.white))
|
||||
.padding([.trailing, .leading], 55)
|
||||
.padding([.top], 25)
|
||||
|
||||
ForEach(OnboardingTitle.titleOptions, id: \.self) { option in
|
||||
Button(action: {
|
||||
// onboardingData.title = option
|
||||
}, label: {
|
||||
Text(option)
|
||||
.font(.subheadline.weight(.bold))
|
||||
.foregroundColor(.white)
|
||||
.padding(10)
|
||||
.background(RoundedRectangle(cornerRadius: 10).stroke().foregroundColor(Color.white))
|
||||
.cornerRadius(10)
|
||||
})
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
.padding([.top], 10)
|
||||
}
|
||||
|
||||
Text(String(localized: "onboarding_title_type_your_own"))
|
||||
.font(.body)
|
||||
.foregroundColor(Color(UIColor.white))
|
||||
.padding([.top], 25)
|
||||
.padding([.trailing, .leading], 55)
|
||||
|
||||
}, label: {
|
||||
Text(option)
|
||||
.font(.subheadline.weight(.bold))
|
||||
.foregroundColor(.white)
|
||||
.padding(10)
|
||||
.background(RoundedRectangle(cornerRadius: 10).stroke().foregroundColor(Color.white))
|
||||
.cornerRadius(10)
|
||||
})
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
.padding([.top], 10)
|
||||
}
|
||||
|
||||
Text(String(localized: "onboarding_title_type_your_own"))
|
||||
.font(.body)
|
||||
.foregroundColor(Color(UIColor.white))
|
||||
.padding([.top], 25)
|
||||
.padding([.trailing, .leading], 55)
|
||||
|
||||
// TextField("Notification", text: $onboardingData.title)
|
||||
// .frame(height: 44)
|
||||
// .foregroundColor(Color(UIColor.white))
|
||||
@@ -63,12 +54,21 @@ struct OnboardingTitle: View {
|
||||
// .overlay(RoundedRectangle(cornerRadius: 16).stroke(Color.white))
|
||||
// .padding([.leading, .trailing], 75)
|
||||
// .padding([.top], 45)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,56 @@ struct OnboardingWrapup: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
GeometryReader { geometry in
|
||||
GeometryReader { geometry in
|
||||
VStack {
|
||||
ScrollView {
|
||||
|
||||
Spacer()
|
||||
|
||||
Text(String(localized: "onboarding_wrap_up_1"))
|
||||
.padding(.top)
|
||||
.padding()
|
||||
.font(.title)
|
||||
.foregroundColor(Color(UIColor.white))
|
||||
|
||||
Text(formatter.string(from: onboardingData.date))
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
.padding()
|
||||
.foregroundColor(Color(UIColor.white))
|
||||
|
||||
Text(String(localized: "onboarding_wrap_up_3"))
|
||||
.font(.title)
|
||||
.padding()
|
||||
.foregroundColor(Color(UIColor.white))
|
||||
|
||||
Text(onboardingData.inputDay.localizedValue)
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
.padding()
|
||||
.foregroundColor(Color(UIColor.white))
|
||||
|
||||
Button(action: {
|
||||
AnalyticsManager.shared.track(.onboardingCompleted(dayId: String(onboardingData.inputDay.rawValue)))
|
||||
completionClosure(onboardingData)
|
||||
}, label: {
|
||||
Text(String(localized: "onboarding_wrap_up_complete_button"))
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(Color(hex: "31d158"))
|
||||
.padding()
|
||||
.background(RoundedRectangle(cornerRadius: 10).fill().foregroundColor(Color.white))
|
||||
.cornerRadius(10)
|
||||
})
|
||||
.padding([.top], 65)
|
||||
}
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.frame(maxWidth: geometry.size.width)
|
||||
}
|
||||
.background {
|
||||
ZStack {
|
||||
Color(hex: "31d158")
|
||||
VStack {
|
||||
Spacer()
|
||||
Image("great", bundle: .main)
|
||||
@@ -30,55 +78,8 @@ struct OnboardingWrapup: View {
|
||||
.accessibilityHidden(true)
|
||||
Spacer()
|
||||
}
|
||||
|
||||
VStack {
|
||||
ScrollView {
|
||||
|
||||
Spacer()
|
||||
|
||||
Text(String(localized: "onboarding_wrap_up_1"))
|
||||
.padding(.top)
|
||||
.padding()
|
||||
.font(.title)
|
||||
.foregroundColor(Color(UIColor.white))
|
||||
|
||||
Text(formatter.string(from: onboardingData.date))
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
.padding()
|
||||
.foregroundColor(Color(UIColor.white))
|
||||
|
||||
Text(String(localized: "onboarding_wrap_up_3"))
|
||||
.font(.title)
|
||||
.padding()
|
||||
.foregroundColor(Color(UIColor.white))
|
||||
|
||||
Text(onboardingData.inputDay.localizedValue)
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
.padding()
|
||||
.foregroundColor(Color(UIColor.white))
|
||||
|
||||
Button(action: {
|
||||
AnalyticsManager.shared.track(.onboardingCompleted(dayId: String(onboardingData.inputDay.rawValue)))
|
||||
completionClosure(onboardingData)
|
||||
}, label: {
|
||||
Text(String(localized: "onboarding_wrap_up_complete_button"))
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(Color(hex: "31d158"))
|
||||
.padding()
|
||||
.background(RoundedRectangle(cornerRadius: 10).fill().foregroundColor(Color.white))
|
||||
.cornerRadius(10)
|
||||
})
|
||||
.padding([.top], 65)
|
||||
}
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.frame(maxWidth: geometry.size.width)
|
||||
}
|
||||
}
|
||||
.background(Color(hex: "31d158"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,6 @@ struct AddMoodHeaderView: View {
|
||||
Text(String(imagePack.rawValue))
|
||||
.hidden()
|
||||
|
||||
theme.currentTheme.secondaryBGColor
|
||||
|
||||
VStack(spacing: 16) {
|
||||
Text(ShowBasedOnVoteLogics.getVotingTitle(onboardingData: onboardingData))
|
||||
.font(.title2.bold())
|
||||
@@ -66,6 +64,7 @@ struct AddMoodHeaderView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.background(theme.currentTheme.secondaryBGColor)
|
||||
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.accessibilityIdentifier(AccessibilityID.DayView.moodHeader)
|
||||
|
||||
@@ -24,17 +24,17 @@ struct CustomizeContentView: View {
|
||||
Button(action: { showThemePicker = true }) {
|
||||
HStack(spacing: 16) {
|
||||
// Emoji preview
|
||||
ZStack {
|
||||
LinearGradient(
|
||||
colors: [.purple.opacity(0.8), .blue.opacity(0.8), .cyan.opacity(0.8)],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
Text("🎨")
|
||||
.font(.title)
|
||||
.frame(width: 56, height: 56)
|
||||
.background(
|
||||
LinearGradient(
|
||||
colors: [.purple.opacity(0.8), .blue.opacity(0.8), .cyan.opacity(0.8)],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
)
|
||||
Text("🎨")
|
||||
.font(.title)
|
||||
}
|
||||
.frame(width: 56, height: 56)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Browse Themes")
|
||||
|
||||
@@ -259,26 +259,25 @@ struct AppThemePreviewSheet: View {
|
||||
}
|
||||
|
||||
private var heroSection: some View {
|
||||
ZStack {
|
||||
// Gradient background
|
||||
VStack(spacing: 16) {
|
||||
Text(theme.emoji)
|
||||
.font(.system(size: 72))
|
||||
.shadow(color: .black.opacity(0.3), radius: 8, x: 0, y: 4)
|
||||
|
||||
Text(theme.tagline)
|
||||
.font(.title3.weight(.medium))
|
||||
.foregroundColor(.white)
|
||||
.shadow(color: .black.opacity(0.3), radius: 4, x: 0, y: 2)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 200)
|
||||
.background(
|
||||
LinearGradient(
|
||||
colors: theme.previewColors + [theme.previewColors[0].opacity(0.5)],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
|
||||
VStack(spacing: 16) {
|
||||
Text(theme.emoji)
|
||||
.font(.system(size: 72))
|
||||
.shadow(color: .black.opacity(0.3), radius: 8, x: 0, y: 4)
|
||||
|
||||
Text(theme.tagline)
|
||||
.font(.title3.weight(.medium))
|
||||
.foregroundColor(.white)
|
||||
.shadow(color: .black.opacity(0.3), radius: 4, x: 0, y: 2)
|
||||
}
|
||||
}
|
||||
.frame(height: 200)
|
||||
)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20))
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.top, 16)
|
||||
|
||||
@@ -22,40 +22,37 @@ struct DayFilterPickerView: View {
|
||||
(Calendar.current.shortWeekdaySymbols[6], 7)]
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
theme.currentTheme.secondaryBGColor
|
||||
|
||||
VStack {
|
||||
HStack {
|
||||
ForEach(weekdays.indices, id: \.self) { dayIdx in
|
||||
let day = String(weekdays[dayIdx].0)
|
||||
let value = weekdays[dayIdx].1
|
||||
let isSelected = filteredDays.currentFilters.contains(value)
|
||||
Button(action: {
|
||||
if isSelected {
|
||||
filteredDays.removeFilter(filter: value)
|
||||
} else {
|
||||
filteredDays.addFilter(newFilter: value)
|
||||
}
|
||||
let impactMed = UIImpactFeedbackGenerator(style: .heavy)
|
||||
impactMed.impactOccurred()
|
||||
}) {
|
||||
Text(day.capitalized)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 12)
|
||||
.background(Color(uiColor: .tertiarySystemBackground))
|
||||
.foregroundColor(isSelected ? .green : .red)
|
||||
.cornerRadius(8)
|
||||
VStack {
|
||||
HStack {
|
||||
ForEach(weekdays.indices, id: \.self) { dayIdx in
|
||||
let day = String(weekdays[dayIdx].0)
|
||||
let value = weekdays[dayIdx].1
|
||||
let isSelected = filteredDays.currentFilters.contains(value)
|
||||
Button(action: {
|
||||
if isSelected {
|
||||
filteredDays.removeFilter(filter: value)
|
||||
} else {
|
||||
filteredDays.addFilter(newFilter: value)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
let impactMed = UIImpactFeedbackGenerator(style: .heavy)
|
||||
impactMed.impactOccurred()
|
||||
}) {
|
||||
Text(day.capitalized)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 12)
|
||||
.background(Color(uiColor: .tertiarySystemBackground))
|
||||
.foregroundColor(isSelected ? .green : .red)
|
||||
.cornerRadius(8)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
Text(String(localized: "day_picker_view_text"))
|
||||
.padding(.top)
|
||||
.foregroundColor(textColor)
|
||||
}
|
||||
.padding()
|
||||
Text(String(localized: "day_picker_view_text"))
|
||||
.padding(.top)
|
||||
.foregroundColor(textColor)
|
||||
}
|
||||
.padding()
|
||||
.background(theme.currentTheme.secondaryBGColor)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
|
||||
}
|
||||
|
||||
@@ -50,47 +50,45 @@ struct IconPickerView: View {
|
||||
]
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
theme.currentTheme.secondaryBGColor
|
||||
VStack {
|
||||
ScrollView(.horizontal) {
|
||||
HStack {
|
||||
VStack {
|
||||
ScrollView(.horizontal) {
|
||||
HStack {
|
||||
Button(action: {
|
||||
UIApplication.shared.setAlternateIconName(nil)
|
||||
AnalyticsManager.shared.track(.appIconChanged(iconTitle: "default"))
|
||||
}, label: {
|
||||
Image("AppIconImage", bundle: .main)
|
||||
.resizable()
|
||||
.frame(width: 50, height:50)
|
||||
.cornerRadius(10)
|
||||
})
|
||||
.accessibilityLabel(String(localized: "Default app icon"))
|
||||
.accessibilityHint(String(localized: "Double tap to select"))
|
||||
|
||||
|
||||
ForEach(iconSets, id: \.self.0){ iconSet in
|
||||
Button(action: {
|
||||
UIApplication.shared.setAlternateIconName(nil)
|
||||
AnalyticsManager.shared.track(.appIconChanged(iconTitle: "default"))
|
||||
UIApplication.shared.setAlternateIconName(iconSet.1) { (error) in
|
||||
// FIXME: Handle error
|
||||
}
|
||||
AnalyticsManager.shared.track(.appIconChanged(iconTitle: iconSet.1))
|
||||
}, label: {
|
||||
Image("AppIconImage", bundle: .main)
|
||||
Image(iconSet.0, bundle: .main)
|
||||
.resizable()
|
||||
.frame(width: 50, height:50)
|
||||
.cornerRadius(10)
|
||||
})
|
||||
.accessibilityLabel(String(localized: "Default app icon"))
|
||||
.accessibilityLabel(String(localized: "App icon style \(iconSet.1.replacingOccurrences(of: "AppIcon", with: "").replacingOccurrences(of: "Image", with: ""))"))
|
||||
.accessibilityHint(String(localized: "Double tap to select"))
|
||||
|
||||
|
||||
ForEach(iconSets, id: \.self.0){ iconSet in
|
||||
Button(action: {
|
||||
UIApplication.shared.setAlternateIconName(iconSet.1) { (error) in
|
||||
// FIXME: Handle error
|
||||
}
|
||||
AnalyticsManager.shared.track(.appIconChanged(iconTitle: iconSet.1))
|
||||
}, label: {
|
||||
Image(iconSet.0, bundle: .main)
|
||||
.resizable()
|
||||
.frame(width: 50, height:50)
|
||||
.cornerRadius(10)
|
||||
})
|
||||
.accessibilityLabel(String(localized: "App icon style \(iconSet.1.replacingOccurrences(of: "AppIcon", with: "").replacingOccurrences(of: "Image", with: ""))"))
|
||||
.accessibilityHint(String(localized: "Double tap to select"))
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.background(RoundedRectangle(cornerRadius: 10).fill().foregroundColor(theme.currentTheme.bgColor))
|
||||
.padding()
|
||||
.cornerRadius(10)
|
||||
}
|
||||
.background(RoundedRectangle(cornerRadius: 10).fill().foregroundColor(theme.currentTheme.bgColor))
|
||||
.padding()
|
||||
.cornerRadius(10)
|
||||
}
|
||||
.background(theme.currentTheme.secondaryBGColor)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
|
||||
}
|
||||
|
||||
@@ -16,58 +16,56 @@ struct PersonalityPackPickerView: View {
|
||||
private var textColor: Color { theme.currentTheme.labelColor }
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
theme.currentTheme.secondaryBGColor
|
||||
VStack {
|
||||
ForEach(PersonalityPack.allCases, id: \.self) { aPack in
|
||||
VStack(spacing: 10) {
|
||||
Text(String(aPack.title()))
|
||||
.font(.body)
|
||||
.foregroundColor(textColor)
|
||||
|
||||
Text(aPack.randomPushNotificationStrings().title)
|
||||
.font(.body)
|
||||
.foregroundColor(Color(UIColor.systemGray))
|
||||
Text(aPack.randomPushNotificationStrings().body)
|
||||
.font(.body)
|
||||
.foregroundColor(Color(UIColor.systemGray))
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity)
|
||||
.padding()
|
||||
.contentShape(Rectangle())
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 10, style: .continuous)
|
||||
.fill(personalityPack == aPack ? theme.currentTheme.bgColor : .clear)
|
||||
.padding(5)
|
||||
)
|
||||
.onTapGesture {
|
||||
let impactMed = UIImpactFeedbackGenerator(style: .heavy)
|
||||
impactMed.impactOccurred()
|
||||
personalityPack = aPack
|
||||
AnalyticsManager.shared.track(.personalityPackChanged(packTitle: aPack.title()))
|
||||
LocalNotification.rescheduleNotifiations()
|
||||
// }
|
||||
}
|
||||
// .blur(radius: aPack.rawValue == PersonalityPack.Rude.rawValue && !showNSFW ? 5 : 0)
|
||||
.alert(isPresented: $showOver18Alert) {
|
||||
let primaryButton = Alert.Button.default(Text(String(localized: "customize_view_over18alert_ok"))) {
|
||||
showNSFW = true
|
||||
}
|
||||
let secondaryButton = Alert.Button.cancel(Text(String(localized: "customize_view_over18alert_no"))) {
|
||||
showNSFW = false
|
||||
}
|
||||
return Alert(title: Text(String(localized: "customize_view_over18alert_title")),
|
||||
message: Text(String(localized: "customize_view_over18alert_body")),
|
||||
primaryButton: primaryButton,
|
||||
secondaryButton: secondaryButton)
|
||||
}
|
||||
if aPack.rawValue != (PersonalityPack.allCases.sorted(by: { $0.rawValue > $1.rawValue }).first?.rawValue) ?? 0 {
|
||||
Divider()
|
||||
}
|
||||
VStack {
|
||||
ForEach(PersonalityPack.allCases, id: \.self) { aPack in
|
||||
VStack(spacing: 10) {
|
||||
Text(String(aPack.title()))
|
||||
.font(.body)
|
||||
.foregroundColor(textColor)
|
||||
|
||||
Text(aPack.randomPushNotificationStrings().title)
|
||||
.font(.body)
|
||||
.foregroundColor(Color(UIColor.systemGray))
|
||||
Text(aPack.randomPushNotificationStrings().body)
|
||||
.font(.body)
|
||||
.foregroundColor(Color(UIColor.systemGray))
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity)
|
||||
.padding()
|
||||
.contentShape(Rectangle())
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 10, style: .continuous)
|
||||
.fill(personalityPack == aPack ? theme.currentTheme.bgColor : .clear)
|
||||
.padding(5)
|
||||
)
|
||||
.onTapGesture {
|
||||
let impactMed = UIImpactFeedbackGenerator(style: .heavy)
|
||||
impactMed.impactOccurred()
|
||||
personalityPack = aPack
|
||||
AnalyticsManager.shared.track(.personalityPackChanged(packTitle: aPack.title()))
|
||||
LocalNotification.rescheduleNotifiations()
|
||||
// }
|
||||
}
|
||||
// .blur(radius: aPack.rawValue == PersonalityPack.Rude.rawValue && !showNSFW ? 5 : 0)
|
||||
.alert(isPresented: $showOver18Alert) {
|
||||
let primaryButton = Alert.Button.default(Text(String(localized: "customize_view_over18alert_ok"))) {
|
||||
showNSFW = true
|
||||
}
|
||||
let secondaryButton = Alert.Button.cancel(Text(String(localized: "customize_view_over18alert_no"))) {
|
||||
showNSFW = false
|
||||
}
|
||||
return Alert(title: Text(String(localized: "customize_view_over18alert_title")),
|
||||
message: Text(String(localized: "customize_view_over18alert_body")),
|
||||
primaryButton: primaryButton,
|
||||
secondaryButton: secondaryButton)
|
||||
}
|
||||
if aPack.rawValue != (PersonalityPack.allCases.sorted(by: { $0.rawValue > $1.rawValue }).first?.rawValue) ?? 0 {
|
||||
Divider()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
.background(theme.currentTheme.secondaryBGColor)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
|
||||
}
|
||||
|
||||
@@ -15,19 +15,17 @@ struct ThemePickerView: View {
|
||||
private var textColor: Color { selectedTheme.currentTheme.labelColor }
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
selectedTheme.currentTheme.secondaryBGColor
|
||||
VStack {
|
||||
HStack(spacing: 0) {
|
||||
themeButton(for: .system)
|
||||
themeButton(for: .iFeel)
|
||||
themeButton(for: .dark)
|
||||
themeButton(for: .light)
|
||||
}
|
||||
.padding(.top)
|
||||
VStack {
|
||||
HStack(spacing: 0) {
|
||||
themeButton(for: .system)
|
||||
themeButton(for: .iFeel)
|
||||
themeButton(for: .dark)
|
||||
themeButton(for: .light)
|
||||
}
|
||||
.padding()
|
||||
.padding(.top)
|
||||
}
|
||||
.padding()
|
||||
.background(selectedTheme.currentTheme.secondaryBGColor)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
|
||||
.onAppear {
|
||||
|
||||
@@ -18,57 +18,54 @@ struct VotingLayoutPickerView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
theme.currentTheme.secondaryBGColor
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("Voting Layout")
|
||||
.font(.headline)
|
||||
.foregroundColor(textColor)
|
||||
.padding(.horizontal)
|
||||
.padding(.top)
|
||||
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("Voting Layout")
|
||||
.font(.headline)
|
||||
.foregroundColor(textColor)
|
||||
.padding(.horizontal)
|
||||
.padding(.top)
|
||||
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack(spacing: 8) {
|
||||
ForEach(VotingLayoutStyle.allCases, id: \.rawValue) { layout in
|
||||
Button(action: {
|
||||
if UIAccessibility.isReduceMotionEnabled {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack(spacing: 8) {
|
||||
ForEach(VotingLayoutStyle.allCases, id: \.rawValue) { layout in
|
||||
Button(action: {
|
||||
if UIAccessibility.isReduceMotionEnabled {
|
||||
votingLayoutStyle = layout.rawValue
|
||||
} else {
|
||||
withAnimation(.easeInOut(duration: 0.2)) {
|
||||
votingLayoutStyle = layout.rawValue
|
||||
} else {
|
||||
withAnimation(.easeInOut(duration: 0.2)) {
|
||||
votingLayoutStyle = layout.rawValue
|
||||
}
|
||||
}
|
||||
AnalyticsManager.shared.track(.votingLayoutChanged(layout: layout.displayName))
|
||||
}) {
|
||||
VStack(spacing: 6) {
|
||||
layoutIcon(for: layout)
|
||||
.frame(width: 44, height: 44)
|
||||
.foregroundColor(currentLayout == layout ? .accentColor : textColor.opacity(0.6))
|
||||
|
||||
Text(layout.displayName)
|
||||
.font(.caption)
|
||||
.foregroundColor(currentLayout == layout ? .accentColor : textColor.opacity(0.8))
|
||||
}
|
||||
.frame(width: 70)
|
||||
.padding(.vertical, 12)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.fill(currentLayout == layout ? Color.accentColor.opacity(0.15) : Color.clear)
|
||||
)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.stroke(currentLayout == layout ? Color.accentColor : Color.clear, lineWidth: 2)
|
||||
)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
AnalyticsManager.shared.track(.votingLayoutChanged(layout: layout.displayName))
|
||||
}) {
|
||||
VStack(spacing: 6) {
|
||||
layoutIcon(for: layout)
|
||||
.frame(width: 44, height: 44)
|
||||
.foregroundColor(currentLayout == layout ? .accentColor : textColor.opacity(0.6))
|
||||
|
||||
Text(layout.displayName)
|
||||
.font(.caption)
|
||||
.foregroundColor(currentLayout == layout ? .accentColor : textColor.opacity(0.8))
|
||||
}
|
||||
.frame(width: 70)
|
||||
.padding(.vertical, 12)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.fill(currentLayout == layout ? Color.accentColor.opacity(0.15) : Color.clear)
|
||||
)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.stroke(currentLayout == layout ? Color.accentColor : Color.clear, lineWidth: 2)
|
||||
)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
.padding(.bottom)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
.padding(.bottom)
|
||||
}
|
||||
.background(theme.currentTheme.secondaryBGColor)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -522,6 +522,7 @@ struct YearCard: View, Equatable {
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityIdentifier(AccessibilityID.YearView.cardHeader(year: year))
|
||||
|
||||
Spacer()
|
||||
|
||||
@@ -533,6 +534,7 @@ struct YearCard: View, Equatable {
|
||||
.foregroundColor(textColor.opacity(0.6))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityIdentifier(AccessibilityID.YearView.shareButton)
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
@@ -543,6 +545,7 @@ struct YearCard: View, Equatable {
|
||||
// Donut Chart
|
||||
MoodDonutChart(metrics: animatedMetrics, moodTint: moodTint)
|
||||
.frame(width: 100, height: 100)
|
||||
.accessibilityIdentifier(AccessibilityID.YearView.donutChart)
|
||||
|
||||
// Bar Chart
|
||||
VStack(spacing: 6) {
|
||||
@@ -574,10 +577,12 @@ struct YearCard: View, Equatable {
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.accessibilityIdentifier(AccessibilityID.YearView.barChart)
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.bottom, 12)
|
||||
.transition(.opacity.combined(with: .move(edge: .top)))
|
||||
.accessibilityIdentifier(AccessibilityID.YearView.statsSection)
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
@@ -82,6 +82,14 @@ enum UITestID {
|
||||
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 {
|
||||
static let grid = "month_grid"
|
||||
}
|
||||
|
||||
48
Tests iOS/SettingsOnboardingTests.swift
Normal file
48
Tests iOS/SettingsOnboardingTests.swift
Normal 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"
|
||||
)
|
||||
}
|
||||
}
|
||||
51
Tests iOS/YearViewDisplayTests.swift
Normal file
51
Tests iOS/YearViewDisplayTests.swift
Normal 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.
100
uiTestPrompt.md
100
uiTestPrompt.md
@@ -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
|
||||
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:
|
||||
- Project root: /Users/treyt/Desktop/code/Feels
|
||||
- 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
|
||||
Optional explicit test IDs/names from me (if provided, prioritize these):
|
||||
<PASTE_TEST_IDS_AND_NAMES_HERE_OR_LEAVE_EMPTY>
|
||||
|
||||
Implementation requirements:
|
||||
1. Use the established pattern:
|
||||
Mandatory references (read before coding):
|
||||
- /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`
|
||||
- `UITestID` / accessibility identifier selectors first
|
||||
- screen objects in `Tests iOS/Screens/`
|
||||
- wait helpers (`tapWhenReady`, `waitForExistence`, `waitForDisappearance`)
|
||||
2. Do NOT use `sleep(...)`.
|
||||
3. Do NOT rely on localized/raw text selectors as primary selectors.
|
||||
4. If needed, add missing accessibility IDs in app code and wire them into tests.
|
||||
5. Keep the test deterministic using fixture + launch flags from `BaseUITestCase`.
|
||||
6. Add screenshots at meaningful checkpoints for triage.
|
||||
- `UITestID` and accessibility identifiers
|
||||
- screen objects under `Tests iOS/Screens/`
|
||||
- wait helpers (`tapWhenReady`, `waitForExistence`, `waitForDisappearance`, etc.)
|
||||
2. No `sleep(...)`.
|
||||
3. No raw/localized text selectors as primary locators.
|
||||
4. Add missing accessibility IDs only when required, then wire them through current helper patterns.
|
||||
5. Keep tests deterministic with fixtures and launch flags from `BaseUITestCase`.
|
||||
6. Follow existing naming/style conventions from current passing tests.
|
||||
7. Add screenshots at meaningful checkpoints for triage.
|
||||
|
||||
Test setup choices:
|
||||
- Suggested suite file: `Tests iOS/<SUITE_NAME>Tests.swift`
|
||||
- Suggested test method: `test<FEATURE>_<BEHAVIOR>()`
|
||||
- Fixture to use: `<empty | single_mood | week_of_moods>`
|
||||
- Launch overrides if needed:
|
||||
- `skipOnboarding = <true/false>`
|
||||
- `bypassSubscription = <true/false>`
|
||||
- `expireTrial = <true/false>`
|
||||
Flake-resistance checklist (must satisfy):
|
||||
- Each test has deterministic starting state (fixture + launch args).
|
||||
- No arbitrary timing waits.
|
||||
- Assertions target stable identifiers.
|
||||
- Test does not depend on current date text formatting unless already stabilized by existing helpers.
|
||||
- If a new selector is needed, add app-side accessibility identifier first.
|
||||
|
||||
Validation requirements:
|
||||
1. Run targeted suite:
|
||||
- `xcodebuild -project Feels.xcodeproj -scheme "Feels (iOS)" -destination 'platform=iOS Simulator,name=iPhone 16 Pro' -only-testing:"Tests iOS/<SUITE_NAME>" test`
|
||||
2. Report pass/fail summary.
|
||||
3. If failures occur, fix and rerun until green.
|
||||
Deliverable shape:
|
||||
- Prefer one suite file with 3 test methods, unless existing suite placement is clearly better.
|
||||
- Method naming: `test<Feature>_<Behavior>()`.
|
||||
- Keep helper logic in screen objects/helpers instead of duplicating in test body.
|
||||
|
||||
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:
|
||||
1. Files changed
|
||||
2. Why each change was needed
|
||||
3. Test run summary
|
||||
4. Any follow-up risks/gaps
|
||||
1. Selected QA test IDs/names with short reason for selection.
|
||||
2. Files changed.
|
||||
3. Key architecture decisions (how existing patterns were reused).
|
||||
4. Exact test command(s) executed.
|
||||
5. Run results for pass #1 and pass #2.
|
||||
6. Any residual risk/gaps.
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user