tests: target MealMoodUITests que recorre el onboarding completo

El bug de los pasos 5 y 6 era invisible a los tests unitarios: el contrato de
datos estaba bien, lo que fallaba era que la vista se desmontaba antes de pintar
el aha moment y el paywall. Hace falta recorrer la UI de verdad.

Anade el target MealMoodUITests (solo simulador, sin firma) y un test que hace
el onboarding entero en instalacion limpia y comprueba que Week Ready y el
paywall aparecen antes de la Home.

Los CTA del onboarding llevan ahora accessibilityIdentifier para que el test no
dependa de los textos, que estan en 6 idiomas.

Validado en los dos sentidos: pasa con el fix, y revirtiendo temporalmente
markFinished: false falla en la asercion de Week Ready. Suite completa: 27 tests.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Ks8uUcMA9mjypVK7F2Pkt
This commit is contained in:
alexandrev-tibco
2026-08-05 23:06:04 +02:00
parent 9f5db245a9
commit 79423e3511
11 changed files with 213 additions and 9 deletions
+123 -9
View File
@@ -51,11 +51,15 @@
867A511F154A33E9DD7FE9BC /* CalendarService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A631C6D0CF7A0724ACF6A5 /* CalendarService.swift */; };
87EB371FD3B6C241AC721C55 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8E15828872FAEA71A7384099 /* Localizable.strings */; };
8CEAC8993D33BFBBEB5319A8 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EE10285FEE9BE0684CC66A3 /* NotificationService.swift */; };
8DB624B469178CFD8CD87672 /* OnboardingFlowUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CBD59AC26D5C6D138D67E35 /* OnboardingFlowUITests.swift */; };
8F739A4496501009B8D829B3 /* Color+MealMood.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB771E85924AE086647A5113 /* Color+MealMood.swift */; };
962BC821D22DE718E846BD64 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0614F890C8CA1620F8CF331 /* Foundation.framework */; };
9BC60C30918C117608E43699 /* DefaultDataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17E00AB23342224B3407838B /* DefaultDataService.swift */; };
9BE86765B8F0368AD5C86FC4 /* TagViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9BFFE7A4BC6961815D2334 /* TagViewModel.swift */; };
9D40600B9AC21F0FF64B77F9 /* StoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 103374A7D91B4F884278DA72 /* StoreManager.swift */; };
9EE4C3257DCE32D2872CD435 /* MealMoodWidget.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 574CB4BDE993DA0CEC251ABD /* MealMoodWidget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
A1B2C3D4E5F60718293A4B01 /* IngredientParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F60718293A4B02 /* IngredientParser.swift */; };
A1B2C3D4E5F60718293A4B03 /* SpeechDictationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F60718293A4B04 /* SpeechDictationService.swift */; };
A4D50C0B4E91B3542C63017A /* MealMoodWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = B042BB2F81973D854E84BABD /* MealMoodWidget.swift */; };
A6F130EC376B7BC270AD908D /* TagPill.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E1C12C3A693D7C41E9F80B /* TagPill.swift */; };
A7CAD588CF78F404C8065CF1 /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDEE086FD20D16DE507B247 /* HomeViewModel.swift */; };
@@ -79,8 +83,6 @@
CD2ADCDADFEC2D562E23D8E6 /* AutocompleteEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7AB2ECCA7D330E50AC549E /* AutocompleteEngine.swift */; };
D2840F9D92D16026338CE83C /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7C00EED19123F00AE87FF24 /* AppSettings.swift */; };
D39BED40CA59849F181D80B5 /* IngredientGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F0ADA350289AE410D572A7 /* IngredientGenerator.swift */; };
A1B2C3D4E5F60718293A4B01 /* IngredientParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F60718293A4B02 /* IngredientParser.swift */; };
A1B2C3D4E5F60718293A4B03 /* SpeechDictationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F60718293A4B04 /* SpeechDictationService.swift */; };
D8149D55E74F7BBFC0402F46 /* DishFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63CA2A8666E1FBBAD65F141 /* DishFormView.swift */; };
D89413095E37C3D677856437 /* PremiumAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0200C0C11C912711C724558 /* PremiumAccess.swift */; };
DE43F08AAC939C083BF511C1 /* FirstDishesStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10594EA55F6BEFF4241D364C /* FirstDishesStepView.swift */; };
@@ -100,6 +102,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
A552B5A17E222A1BF7AF79A0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 7C11D794B7E45A145D5ADBFD /* Project object */;
proxyType = 1;
remoteGlobalIDString = DE69ED74719D07B4A5770741;
remoteInfo = MealMood;
};
EFE9F2305F13749C6FCAC8E5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 7C11D794B7E45A145D5ADBFD /* Project object */;
@@ -141,11 +150,13 @@
1975C49FFC7D91AEB7932C97 /* SocialShareSheet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SocialShareSheet.swift; sourceTree = "<group>"; };
1E8AD94FC874467D863D464C /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; };
20F23BF9788A3217E3D63721 /* HapticManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticManager.swift; sourceTree = "<group>"; };
212FF80B5B9E9E5243CA79D5 /* MealMoodUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MealMoodUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
23957C735075B0DFB1DF51D7 /* DishDrawerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DishDrawerView.swift; sourceTree = "<group>"; };
2C20D9FC522FEBF7206CFD32 /* MealMoodApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealMoodApp.swift; sourceTree = "<group>"; };
2C5CD466A50C339519544347 /* TagRulesEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagRulesEditView.swift; sourceTree = "<group>"; };
2E113A29DEC0551C41AB7EBB /* WeekPlanShareView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekPlanShareView.swift; sourceTree = "<group>"; };
324D77FEC4C8C371695F27CC /* PremiumView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PremiumView.swift; sourceTree = "<group>"; };
3CBD59AC26D5C6D138D67E35 /* OnboardingFlowUITests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = OnboardingFlowUITests.swift; sourceTree = "<group>"; };
3FC93C653B5540D7205BF294 /* ShoppingItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ShoppingItem.swift; sourceTree = "<group>"; };
4EDEE086FD20D16DE507B247 /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = "<group>"; };
50E1C12C3A693D7C41E9F80B /* TagPill.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagPill.swift; sourceTree = "<group>"; };
@@ -163,8 +174,6 @@
6EA1948B5DAEEEA09B78C8A9 /* SecondaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondaryButton.swift; sourceTree = "<group>"; };
70D140A6E835CA6227C2D4D9 /* WelcomeStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeStepView.swift; sourceTree = "<group>"; };
71F0ADA350289AE410D572A7 /* IngredientGenerator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = IngredientGenerator.swift; sourceTree = "<group>"; };
A1B2C3D4E5F60718293A4B02 /* IngredientParser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = IngredientParser.swift; sourceTree = "<group>"; };
A1B2C3D4E5F60718293A4B04 /* SpeechDictationService.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SpeechDictationService.swift; sourceTree = "<group>"; };
7603187713D390BD4D35770C /* AutocompleteEngineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutocompleteEngineTests.swift; sourceTree = "<group>"; };
76719058DD3A4177B2B4163E /* CalendarStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarStepView.swift; sourceTree = "<group>"; };
797A41D292142240FBDF9F2A /* MealSlot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealSlot.swift; sourceTree = "<group>"; };
@@ -179,6 +188,8 @@
965555A7774FF7EB975F0A07 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
9F86EB5DDB126D73486FD956 /* MealMood.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MealMood.entitlements; sourceTree = "<group>"; };
9FBA11C8A81D5C0CA8A3C13D /* MealWindowsStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealWindowsStepView.swift; sourceTree = "<group>"; };
A1B2C3D4E5F60718293A4B02 /* IngredientParser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = IngredientParser.swift; sourceTree = "<group>"; };
A1B2C3D4E5F60718293A4B04 /* SpeechDictationService.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SpeechDictationService.swift; sourceTree = "<group>"; };
A1C3CBAFE191AD6643F861B7 /* DeduplicationService.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DeduplicationService.swift; sourceTree = "<group>"; };
A23267BAA116E1214870416C /* WeekPlan.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekPlan.swift; sourceTree = "<group>"; };
A3C5F7891B2D4E6F80A2C4E6 /* FeedbackStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackStore.swift; sourceTree = "<group>"; };
@@ -205,6 +216,7 @@
D954415A7AC73D41309D2BDF /* WeekReadyStepView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = WeekReadyStepView.swift; sourceTree = "<group>"; };
DBD33453B7DF86EDBAEB80F1 /* DateHelpersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateHelpersTests.swift; sourceTree = "<group>"; };
DFA97AD958C922D8AFF73314 /* PrimaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryButton.swift; sourceTree = "<group>"; };
E0614F890C8CA1620F8CF331 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS26.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
E18A2F6B56D64C1EBC91E219 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
E18A2F6B56D64C1EBC91E21A /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
E18A2F6B56D64C1EBC91E21B /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
@@ -246,6 +258,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
DB8099D2344E92202D8D0A2F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
962BC821D22DE718E846BD64 /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -257,6 +277,7 @@
5BEE00A9E25BDAA086585241 /* Products */,
F4FE25386665429412E51DB4 /* Frameworks */,
2BC91A087BC03FF7810C529F /* MealMoodWidget */,
DB44D4C1DDBFC6CE205A5AB1 /* MealMoodUITests */,
);
sourceTree = "<group>";
};
@@ -365,6 +386,7 @@
C7B13F4D4D362E50CA65A0C1 /* MealMood.app */,
F766AA7898D6F391DE1B9A81 /* MealMoodTests.xctest */,
574CB4BDE993DA0CEC251ABD /* MealMoodWidget.appex */,
212FF80B5B9E9E5243CA79D5 /* MealMoodUITests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -453,6 +475,15 @@
path = Tags;
sourceTree = "<group>";
};
DB44D4C1DDBFC6CE205A5AB1 /* MealMoodUITests */ = {
isa = PBXGroup;
children = (
3CBD59AC26D5C6D138D67E35 /* OnboardingFlowUITests.swift */,
);
name = MealMoodUITests;
path = MealMoodUITests;
sourceTree = "<group>";
};
E452C59D71AAD6175947503D /* Onboarding */ = {
isa = PBXGroup;
children = (
@@ -490,6 +521,7 @@
isa = PBXGroup;
children = (
C77C350D4A839B76A3D2EFE7 /* Foundation.framework */,
E0614F890C8CA1620F8CF331 /* Foundation.framework */,
);
name = iOS;
sourceTree = "<group>";
@@ -592,6 +624,24 @@
productReference = C7B13F4D4D362E50CA65A0C1 /* MealMood.app */;
productType = "com.apple.product-type.application";
};
FCA0786EFDD5E822F3A19767 /* MealMoodUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0959BF24DE5F9F75760976C6 /* Build configuration list for PBXNativeTarget "MealMoodUITests" */;
buildPhases = (
206068DB5ADFE1BAE187090F /* Sources */,
DB8099D2344E92202D8D0A2F /* Frameworks */,
3136679DF5632E7E8BB4A829 /* Resources */,
);
buildRules = (
);
dependencies = (
4276CDDEA774B000AE30090D /* PBXTargetDependency */,
);
name = MealMoodUITests;
productName = MealMoodUITests;
productReference = 212FF80B5B9E9E5243CA79D5 /* MealMoodUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -627,11 +677,19 @@
DE69ED74719D07B4A5770741 /* MealMood */,
71CBF84DA3DD6F325A40C1A1 /* MealMoodTests */,
C82BD86FE72A5E319983AEE3 /* MealMoodWidget */,
FCA0786EFDD5E822F3A19767 /* MealMoodUITests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
3136679DF5632E7E8BB4A829 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
60AC9519630B6008787A8859 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -678,6 +736,14 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
206068DB5ADFE1BAE187090F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8DB624B469178CFD8CD87672 /* OnboardingFlowUITests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
A6B405631E28429E34B68811 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -779,6 +845,12 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
4276CDDEA774B000AE30090D /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = MealMood;
target = DE69ED74719D07B4A5770741 /* MealMood */;
targetProxy = A552B5A17E222A1BF7AF79A0 /* PBXContainerItemProxy */;
};
C4E8F43173BDFCAE4D6DFF49 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DE69ED74719D07B4A5770741 /* MealMood */;
@@ -803,6 +875,23 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
0DF34D8B955C5894B2816878 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = NO;
CODE_SIGNING_ALLOWED = NO;
CODE_SIGN_STYLE = Automatic;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.mealmood.uitests;
SDKROOT = iphoneos;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = MealMood;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
1FCA89A600CDAC66078A97A2 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -866,14 +955,14 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_OBJC_WEAK = NO;
CODE_SIGN_ENTITLEMENTS = MealMoodWidget/MealMoodWidget.entitlements;
CODE_SIGN_STYLE = Manual;
CODE_SIGN_IDENTITY = "Apple Distribution";
PROVISIONING_PROFILE_SPECIFIER = "com.alexandrevazquez.mealmood.widget AppStore";
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = 2825Q76T7H;
INFOPLIST_FILE = MealMoodWidget/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.mealmood.widget;
PRODUCT_NAME = MealMoodWidget;
PROVISIONING_PROFILE_SPECIFIER = "com.alexandrevazquez.mealmood.widget AppStore";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
@@ -923,12 +1012,11 @@
B25BBFBD5E65ABC45B72EE38 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
APS_ENVIRONMENT = production;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = MealMood/Resources/MealMood.entitlements;
APS_ENVIRONMENT = production;
CODE_SIGN_IDENTITY = "Apple Distribution";
CODE_SIGN_STYLE = Manual;
PROVISIONING_PROFILE_SPECIFIER = "com.alexandrevazquez.mealmood AppStore";
CURRENT_PROJECT_VERSION = 64;
DEVELOPMENT_TEAM = 2825Q76T7H;
GENERATE_INFOPLIST_FILE = NO;
@@ -941,11 +1029,28 @@
MARKETING_VERSION = 2.0;
PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.mealmood;
PRODUCT_NAME = MealMood;
PROVISIONING_PROFILE_SPECIFIER = "com.alexandrevazquez.mealmood AppStore";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
B35D63A7AEDD7B78FD7484F5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = NO;
CODE_SIGNING_ALLOWED = NO;
CODE_SIGN_STYLE = Automatic;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.mealmood.uitests;
SDKROOT = iphoneos;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = MealMood;
};
name = Debug;
};
B6C55A4487CCCEE45AAF80D9 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1031,9 +1136,9 @@
E9CAFA13F28C3B0E740A13AC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
APS_ENVIRONMENT = development;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = MealMood/Resources/MealMood.entitlements;
APS_ENVIRONMENT = development;
CODE_SIGN_IDENTITY = "iPhone Developer";
CURRENT_PROJECT_VERSION = 64;
DEVELOPMENT_TEAM = 2825Q76T7H;
@@ -1055,6 +1160,15 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
0959BF24DE5F9F75760976C6 /* Build configuration list for PBXNativeTarget "MealMoodUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0DF34D8B955C5894B2816878 /* Release */,
B35D63A7AEDD7B78FD7484F5 /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
14CECD46CDA9DFDC2D928807 /* Build configuration list for PBXNativeTarget "MealMoodWidget" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -48,6 +48,17 @@
ReferencedContainer = "container:MealMood.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FCA0786EFDD5E822F3A19767"
BuildableName = "MealMoodUITests.xctest"
BlueprintName = "MealMoodUITests"
ReferencedContainer = "container:MealMood.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
+1
View File
@@ -115,6 +115,7 @@ struct HomeView: View {
}
}
}
.accessibilityIdentifier("home_root")
.toast(isShowing: $viewModel.showToast, message: viewModel.toastMessage)
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
@@ -137,6 +137,7 @@ struct CalendarStepView: View {
})
PrimaryButton(title: String(localized: "onboarding_continue"), action: onNext)
.accessibilityIdentifier("onboarding_cta_calendar")
}
.padding(.horizontal, 24)
.padding(.bottom, 40)
@@ -241,6 +241,7 @@ struct FirstDishesStepView: View {
},
isEnabled: true
)
.accessibilityIdentifier("onboarding_cta_first_dishes")
.padding(.horizontal, 24)
.padding(.top, 12)
.padding(.bottom, 24)
@@ -38,6 +38,7 @@ struct MealWindowsStepView: View {
.padding(.horizontal, 24)
PrimaryButton(title: String(localized: "onboarding_continue"), action: onNext)
.accessibilityIdentifier("onboarding_cta_meal_windows")
.padding(.horizontal, 24)
.padding(.top, 8)
.padding(.bottom, 40)
@@ -59,6 +59,7 @@ struct PaywallStepView: View {
.font(.mealMoodSmall)
.foregroundColor(.mealMoodTextSecondary)
}
.accessibilityIdentifier("onboarding_cta_paywall_skip")
}
.padding(.horizontal, 24)
.padding(.bottom, 24)
@@ -26,6 +26,7 @@ struct WeekReadyStepView: View {
.font(.mealMoodH1)
.foregroundColor(.mealMoodTextPrimary)
.multilineTextAlignment(.center)
.accessibilityIdentifier("onboarding_week_ready_title")
Text(subtitleText)
.font(.mealMoodBody)
@@ -81,6 +82,7 @@ struct WeekReadyStepView: View {
.foregroundColor(.mealMoodTextSecondary)
}
.buttonStyle(.plain)
.accessibilityIdentifier("onboarding_cta_week_ready_skip")
}
}
.padding(.horizontal, 24)
@@ -41,6 +41,7 @@ struct WeekendsStepView: View {
.foregroundColor(.mealMoodTextSecondary)
PrimaryButton(title: String(localized: "onboarding_continue"), action: onNext)
.accessibilityIdentifier("onboarding_cta_weekends")
.padding(.horizontal, 24)
.padding(.top, 8)
.padding(.bottom, 40)
@@ -30,6 +30,7 @@ struct WelcomeStepView: View {
Spacer()
PrimaryButton(title: String(localized: "onboarding_start"), icon: "", action: onNext)
.accessibilityIdentifier("onboarding_cta_welcome")
.padding(.horizontal, 24)
.padding(.bottom, 40)
}
@@ -0,0 +1,70 @@
import XCTest
/// Walks the whole onboarding on a clean install.
///
/// This exists because of a bug that no unit test could see: the flow used to
/// write `onboardingCompleted` at the dishes step, which swapped the root view
/// for the home and silently skipped the last two steps the Week Ready "aha
/// moment" and the paywall. Shipped in 2.0 and live for a week before GA4
/// (no step 5/6 events despite completions) gave it away.
final class OnboardingFlowUITests: XCTestCase {
private var app: XCUIApplication!
override func setUpWithError() throws {
continueAfterFailure = false
app = XCUIApplication()
// Pin the language so the run doesn't depend on the simulator's locale.
app.launchArguments += ["-AppleLanguages", "(en)", "-AppleLocale", "en_US"]
}
func testOnboardingReachesWeekReadyAndPaywallBeforeTheHome() {
app.launch()
dismissSystemAlertIfPresent()
tap("onboarding_cta_welcome", "welcome")
tap("onboarding_cta_meal_windows", "meal windows")
tap("onboarding_cta_weekends", "weekends")
tap("onboarding_cta_calendar", "calendar")
tap("onboarding_cta_first_dishes", "first dishes")
// The two steps the bug used to skip.
let weekReady = app.staticTexts["onboarding_week_ready_title"]
XCTAssertTrue(weekReady.waitForExistence(timeout: 10),
"Week Ready (the aha moment) never appeared — the flow skipped step 5")
tap("onboarding_cta_week_ready_skip", "week ready")
let paywallSkip = app.buttons["onboarding_cta_paywall_skip"]
XCTAssertTrue(paywallSkip.waitForExistence(timeout: 10),
"The paywall never appeared — the flow skipped step 6")
paywallSkip.tap()
// Only now should onboarding hand over to the home.
XCTAssertTrue(app.otherElements["home_root"].waitForExistence(timeout: 15),
"Onboarding finished but the home never came up")
}
// MARK: Helpers
private func tap(_ identifier: String, _ step: String, file: StaticString = #filePath, line: UInt = #line) {
let button = app.buttons[identifier]
XCTAssertTrue(button.waitForExistence(timeout: 15),
"Never reached the \(step) step", file: file, line: line)
button.tap()
dismissSystemAlertIfPresent()
}
/// The simulator can raise an Apple Account / iCloud prompt on launch, which
/// would swallow the taps that follow.
private func dismissSystemAlertIfPresent() {
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
for label in ["Not Now", "Cancel", "Don't Allow", "Allow"] {
let button = springboard.buttons[label]
if button.exists && button.isHittable {
button.tap()
return
}
}
}
}