Add 3 passing UI tests (batch 3): deep links and trial expiration
TC-125: feels://subscribe deep link opens subscription view TC-126: Malformed deep link (feels://invalidpath) does not crash TC-078: Trial expired state shows "Trial expired" banner in Settings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -48,12 +48,14 @@
|
||||
B8C9D0E100000000D6E7F8A9 /* MonthViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8C9D0E1F2A3B4C5D6E7F8A9 /* MonthViewTests.swift */; };
|
||||
BB22220022222200BBBBBBBB /* MoodLoggingEmptyStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB22222222222222BBBBBBBB /* MoodLoggingEmptyStateTests.swift */; };
|
||||
C0137E33A722F405FDC457B5 /* YearViewCollapseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542B1A71F9990806CD88B285 /* YearViewCollapseTests.swift */; };
|
||||
C1A23F0250FD3380F0A0F999 /* DeepLinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9A9583BE84E8DC13DB61F2B /* DeepLinkTests.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 */; };
|
||||
CFB41ED5D294B8997DB694E2 /* TrialExpirationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AF32CC88B36CDFCB338F2C /* TrialExpirationTests.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 */; };
|
||||
@@ -148,6 +150,7 @@
|
||||
21CD463209E0909393545D62 /* TrialBannerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrialBannerTests.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>"; };
|
||||
35AF32CC88B36CDFCB338F2C /* TrialExpirationTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TrialExpirationTests.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>"; };
|
||||
5354C23DD5FC67C1C97482F2 /* WaitHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitHelpers.swift; sourceTree = "<group>"; };
|
||||
@@ -192,6 +195,7 @@
|
||||
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>"; };
|
||||
F9A9583BE84E8DC13DB61F2B /* DeepLinkTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DeepLinkTests.swift; sourceTree = "<group>"; };
|
||||
FF66666666666666FFFFFFFF /* SecondaryTabTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondaryTabTests.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@@ -427,6 +431,8 @@
|
||||
0246E9F406F872E5DEEB7269 /* YearViewDisplayTests.swift */,
|
||||
542B1A71F9990806CD88B285 /* YearViewCollapseTests.swift */,
|
||||
BE4D06D4E7188339DE8BC040 /* SettingsLegalLinksTests.swift */,
|
||||
F9A9583BE84E8DC13DB61F2B /* DeepLinkTests.swift */,
|
||||
35AF32CC88B36CDFCB338F2C /* TrialExpirationTests.swift */,
|
||||
);
|
||||
path = "Tests iOS";
|
||||
sourceTree = "<group>";
|
||||
@@ -831,6 +837,8 @@
|
||||
D1AD0A0469EADFB1446E9B09 /* YearViewDisplayTests.swift in Sources */,
|
||||
C0137E33A722F405FDC457B5 /* YearViewCollapseTests.swift in Sources */,
|
||||
E78F98C41E83B81DAF43139E /* SettingsLegalLinksTests.swift in Sources */,
|
||||
C1A23F0250FD3380F0A0F999 /* DeepLinkTests.swift in Sources */,
|
||||
CFB41ED5D294B8997DB694E2 /* TrialExpirationTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
73
Tests iOS/DeepLinkTests.swift
Normal file
73
Tests iOS/DeepLinkTests.swift
Normal file
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// DeepLinkTests.swift
|
||||
// Tests iOS
|
||||
//
|
||||
// TC-125, TC-126: Deep link handling.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class DeepLinkTests: BaseUITestCase {
|
||||
override var seedFixture: String? { "empty" }
|
||||
override var bypassSubscription: Bool { false }
|
||||
override var expireTrial: Bool { true }
|
||||
|
||||
/// TC-126: Opening a malformed deep link does not crash the app.
|
||||
func testDeepLink_MalformedURL_NoCrash() {
|
||||
// Verify app launched and is on Day tab
|
||||
let tabBar = TabBarScreen(app: app)
|
||||
XCTAssertTrue(
|
||||
tabBar.dayTab.waitForExistence(timeout: 5),
|
||||
"App should launch to Day tab"
|
||||
)
|
||||
|
||||
// Send a malformed deep link
|
||||
let malformedURL = URL(string: "feels://invalidpath")!
|
||||
app.open(malformedURL)
|
||||
|
||||
// App should still be running and responsive — verify Day tab still exists
|
||||
XCTAssertTrue(
|
||||
tabBar.dayTab.waitForExistence(timeout: 5),
|
||||
"App should remain functional after malformed deep link"
|
||||
)
|
||||
|
||||
// Navigate to another tab to verify full responsiveness
|
||||
tabBar.tapYear()
|
||||
XCTAssertTrue(
|
||||
tabBar.yearTab.waitForExistence(timeout: 3),
|
||||
"App should be fully navigable after malformed deep link"
|
||||
)
|
||||
|
||||
captureScreenshot(name: "deeplink_malformed_no_crash")
|
||||
}
|
||||
|
||||
/// TC-125: feels://subscribe opens subscription view.
|
||||
func testDeepLink_Subscribe_OpensPaywall() {
|
||||
// Verify app launched
|
||||
let tabBar = TabBarScreen(app: app)
|
||||
XCTAssertTrue(
|
||||
tabBar.dayTab.waitForExistence(timeout: 5),
|
||||
"App should launch to Day tab"
|
||||
)
|
||||
|
||||
captureScreenshot(name: "deeplink_before_subscribe")
|
||||
|
||||
// Send subscribe deep link
|
||||
let subscribeURL = URL(string: "feels://subscribe")!
|
||||
app.open(subscribeURL)
|
||||
|
||||
// Subscription view should appear as a sheet.
|
||||
// Look for common subscription UI elements.
|
||||
let subscribeText = app.staticTexts.matching(
|
||||
NSPredicate(format: "label CONTAINS[c] 'subscribe' OR label CONTAINS[c] 'premium' OR label CONTAINS[c] 'upgrade' OR label CONTAINS[c] 'Feels+'")
|
||||
).firstMatch
|
||||
|
||||
let found = subscribeText.waitForExistence(timeout: 8)
|
||||
|
||||
captureScreenshot(name: "deeplink_subscribe_result")
|
||||
|
||||
XCTAssertTrue(found,
|
||||
"Subscription view should appear after feels://subscribe deep link"
|
||||
)
|
||||
}
|
||||
}
|
||||
34
Tests iOS/TrialExpirationTests.swift
Normal file
34
Tests iOS/TrialExpirationTests.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// TrialExpirationTests.swift
|
||||
// Tests iOS
|
||||
//
|
||||
// TC-078: Trial expiration state is reflected in the UI.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class TrialExpirationTests: BaseUITestCase {
|
||||
override var seedFixture: String? { "empty" }
|
||||
override var bypassSubscription: Bool { false }
|
||||
override var expireTrial: Bool { true }
|
||||
|
||||
/// TC-078: When trial is expired, Settings shows "Trial expired" text
|
||||
/// and the upgrade banner is visible.
|
||||
func testTrialExpired_ShowsExpiredBanner() {
|
||||
let tabBar = TabBarScreen(app: app)
|
||||
let settingsScreen = tabBar.tapSettings()
|
||||
settingsScreen.assertVisible()
|
||||
|
||||
// Verify upgrade banner is visible (trial expired, not subscribed)
|
||||
settingsScreen.assertUpgradeBannerVisible()
|
||||
|
||||
// Check for "Trial expired" text in the banner
|
||||
let expiredText = app.staticTexts["Trial expired"]
|
||||
XCTAssertTrue(
|
||||
expiredText.waitForExistence(timeout: 5),
|
||||
"Settings should show 'Trial expired' text when trial has expired"
|
||||
)
|
||||
|
||||
captureScreenshot(name: "trial_expired_banner")
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user