Add Tests iOS/Screens/ page objects and fix gitignore
The screens/ gitignore rule was matching Tests iOS/Screens/ on case-insensitive macOS. Anchored to /screens/ (repo root only) so the 7 UI test page object files are no longer ignored. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -76,5 +76,5 @@ Secrets.swift
|
|||||||
**/Secrets.swift
|
**/Secrets.swift
|
||||||
|
|
||||||
# Screenshots and promo assets
|
# Screenshots and promo assets
|
||||||
screens/
|
/screens/
|
||||||
feels-promo/
|
feels-promo/
|
||||||
|
|||||||
65
Tests iOS/Screens/CustomizeScreen.swift
Normal file
65
Tests iOS/Screens/CustomizeScreen.swift
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
//
|
||||||
|
// CustomizeScreen.swift
|
||||||
|
// Tests iOS
|
||||||
|
//
|
||||||
|
// Screen object for the Customize sub-tab — theme, voting layout, and day view style pickers.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
struct CustomizeScreen {
|
||||||
|
let app: XCUIApplication
|
||||||
|
|
||||||
|
// MARK: - Theme Mode Buttons
|
||||||
|
|
||||||
|
func themeButton(named name: String) -> XCUIElement {
|
||||||
|
app.buttons["customize_theme_\(name.lowercased())"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Voting Layout Buttons
|
||||||
|
|
||||||
|
func votingLayoutButton(named name: String) -> XCUIElement {
|
||||||
|
app.buttons["customize_voting_\(name.lowercased())"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Day View Style Buttons
|
||||||
|
|
||||||
|
func dayViewStyleButton(named name: String) -> XCUIElement {
|
||||||
|
app.buttons["customize_daystyle_\(name.lowercased())"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Actions
|
||||||
|
|
||||||
|
func selectTheme(_ name: String) {
|
||||||
|
let button = themeButton(named: name)
|
||||||
|
button.tapWhenReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectVotingLayout(_ name: String) {
|
||||||
|
let button = votingLayoutButton(named: name)
|
||||||
|
// May need to scroll horizontally to find it
|
||||||
|
if !button.isHittable {
|
||||||
|
app.swipeLeft()
|
||||||
|
}
|
||||||
|
button.tapWhenReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectDayViewStyle(_ name: String) {
|
||||||
|
let button = dayViewStyleButton(named: name)
|
||||||
|
// May need to scroll horizontally to find it
|
||||||
|
if !button.isHittable {
|
||||||
|
app.swipeLeft()
|
||||||
|
}
|
||||||
|
button.tapWhenReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Assertions
|
||||||
|
|
||||||
|
func assertThemeButtonExists(_ name: String, file: StaticString = #file, line: UInt = #line) {
|
||||||
|
XCTAssertTrue(
|
||||||
|
themeButton(named: name).waitForExistence(timeout: 5),
|
||||||
|
"Theme button '\(name)' should exist",
|
||||||
|
file: file, line: line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
89
Tests iOS/Screens/DayScreen.swift
Normal file
89
Tests iOS/Screens/DayScreen.swift
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
//
|
||||||
|
// DayScreen.swift
|
||||||
|
// Tests iOS
|
||||||
|
//
|
||||||
|
// Screen object for the Day (main) view — mood logging and entry list.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
struct DayScreen {
|
||||||
|
let app: XCUIApplication
|
||||||
|
|
||||||
|
// MARK: - Mood Buttons (via accessibilityIdentifier)
|
||||||
|
|
||||||
|
var greatButton: XCUIElement { app.buttons["mood_button_great"] }
|
||||||
|
var goodButton: XCUIElement { app.buttons["mood_button_good"] }
|
||||||
|
var averageButton: XCUIElement { app.buttons["mood_button_average"] }
|
||||||
|
var badButton: XCUIElement { app.buttons["mood_button_bad"] }
|
||||||
|
var horribleButton: XCUIElement { app.buttons["mood_button_horrible"] }
|
||||||
|
|
||||||
|
/// The mood header container
|
||||||
|
var moodHeader: XCUIElement { app.otherElements["mood_header"] }
|
||||||
|
|
||||||
|
// MARK: - Entry List
|
||||||
|
|
||||||
|
/// Find an entry row by its date string (format: "M/d/yyyy")
|
||||||
|
func entryRow(dateString: String) -> XCUIElement {
|
||||||
|
app.descendants(matching: .any).matching(identifier: "entry_row_\(dateString)").firstMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Actions
|
||||||
|
|
||||||
|
/// Tap a mood button by mood name. Waits for the celebration animation to complete.
|
||||||
|
func logMood(_ mood: MoodChoice, file: StaticString = #file, line: UInt = #line) {
|
||||||
|
let button = moodButton(for: mood)
|
||||||
|
guard button.waitUntilHittable(timeout: 5) else {
|
||||||
|
XCTFail("Mood button '\(mood.rawValue)' not hittable", file: file, line: line)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
button.tap()
|
||||||
|
|
||||||
|
// Wait for the celebration animation to finish and entry to appear.
|
||||||
|
// The mood header disappears after logging today's mood.
|
||||||
|
// Give extra time for animation + data save.
|
||||||
|
_ = moodHeader.waitForDisappearance(timeout: 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Assertions
|
||||||
|
|
||||||
|
func assertMoodHeaderVisible(file: StaticString = #file, line: UInt = #line) {
|
||||||
|
XCTAssertTrue(
|
||||||
|
moodHeader.waitForExistence(timeout: 5),
|
||||||
|
"Mood voting header should be visible",
|
||||||
|
file: file, line: line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertMoodHeaderHidden(file: StaticString = #file, line: UInt = #line) {
|
||||||
|
// After logging, the header should either disappear or the buttons should not be hittable
|
||||||
|
let hidden = moodHeader.waitForDisappearance(timeout: 8)
|
||||||
|
XCTAssertTrue(hidden, "Mood header should be hidden after logging today's mood", file: file, line: line)
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertEntryExists(dateString: String, file: StaticString = #file, line: UInt = #line) {
|
||||||
|
let row = entryRow(dateString: dateString)
|
||||||
|
XCTAssertTrue(
|
||||||
|
row.waitForExistence(timeout: 5),
|
||||||
|
"Entry row for \(dateString) should exist",
|
||||||
|
file: file, line: line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func moodButton(for mood: MoodChoice) -> XCUIElement {
|
||||||
|
switch mood {
|
||||||
|
case .great: return greatButton
|
||||||
|
case .good: return goodButton
|
||||||
|
case .average: return averageButton
|
||||||
|
case .bad: return badButton
|
||||||
|
case .horrible: return horribleButton
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the 5 selectable mood values for test code.
|
||||||
|
enum MoodChoice: String {
|
||||||
|
case great, good, average, bad, horrible
|
||||||
|
}
|
||||||
62
Tests iOS/Screens/EntryDetailScreen.swift
Normal file
62
Tests iOS/Screens/EntryDetailScreen.swift
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
//
|
||||||
|
// EntryDetailScreen.swift
|
||||||
|
// Tests iOS
|
||||||
|
//
|
||||||
|
// Screen object for the Entry Detail sheet (edit mood, notes, delete).
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
struct EntryDetailScreen {
|
||||||
|
let app: XCUIApplication
|
||||||
|
|
||||||
|
// MARK: - Elements
|
||||||
|
|
||||||
|
var navigationTitle: XCUIElement { app.navigationBars["Entry Details"] }
|
||||||
|
var doneButton: XCUIElement { app.buttons["entry_detail_done"] }
|
||||||
|
var deleteButton: XCUIElement { app.buttons["entry_detail_delete"] }
|
||||||
|
var moodGrid: XCUIElement { app.otherElements["entry_detail_mood_grid"] }
|
||||||
|
|
||||||
|
/// Mood buttons inside the detail sheet's mood grid.
|
||||||
|
/// These use accessibilityLabel (the mood name text), not identifiers.
|
||||||
|
func moodButton(label: String) -> XCUIElement {
|
||||||
|
app.buttons.matching(NSPredicate(format: "label CONTAINS[cd] %@", label)).firstMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Actions
|
||||||
|
|
||||||
|
func dismiss() {
|
||||||
|
doneButton.tapWhenReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectMood(_ mood: MoodChoice) {
|
||||||
|
let button = moodButton(label: mood.rawValue.capitalized)
|
||||||
|
button.tapWhenReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteEntry() {
|
||||||
|
deleteButton.tapWhenReady()
|
||||||
|
// Confirm the delete alert
|
||||||
|
let deleteAlert = app.alerts["Delete Entry"]
|
||||||
|
let confirmButton = deleteAlert.buttons["Delete"]
|
||||||
|
confirmButton.tapWhenReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Assertions
|
||||||
|
|
||||||
|
func assertVisible(file: StaticString = #file, line: UInt = #line) {
|
||||||
|
XCTAssertTrue(
|
||||||
|
navigationTitle.waitForExistence(timeout: 5),
|
||||||
|
"Entry Detail sheet should be visible",
|
||||||
|
file: file, line: line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertDismissed(file: StaticString = #file, line: UInt = #line) {
|
||||||
|
XCTAssertTrue(
|
||||||
|
navigationTitle.waitForDisappearance(timeout: 5),
|
||||||
|
"Entry Detail sheet should be dismissed",
|
||||||
|
file: file, line: line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
63
Tests iOS/Screens/NoteEditorScreen.swift
Normal file
63
Tests iOS/Screens/NoteEditorScreen.swift
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
//
|
||||||
|
// NoteEditorScreen.swift
|
||||||
|
// Tests iOS
|
||||||
|
//
|
||||||
|
// Screen object for the Journal Note editor sheet.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
struct NoteEditorScreen {
|
||||||
|
let app: XCUIApplication
|
||||||
|
|
||||||
|
// MARK: - Elements
|
||||||
|
|
||||||
|
var navigationTitle: XCUIElement { app.navigationBars["Journal Note"] }
|
||||||
|
var textEditor: XCUIElement { app.textViews["note_editor_text"] }
|
||||||
|
var saveButton: XCUIElement { app.buttons["note_editor_save"] }
|
||||||
|
var cancelButton: XCUIElement { app.buttons["note_editor_cancel"] }
|
||||||
|
|
||||||
|
// MARK: - Actions
|
||||||
|
|
||||||
|
func typeNote(_ text: String) {
|
||||||
|
textEditor.tapWhenReady()
|
||||||
|
textEditor.typeText(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearAndTypeNote(_ text: String) {
|
||||||
|
textEditor.tapWhenReady()
|
||||||
|
// Select all and replace
|
||||||
|
textEditor.press(forDuration: 1.0)
|
||||||
|
let selectAll = app.menuItems["Select All"]
|
||||||
|
if selectAll.waitForExistence(timeout: 2) {
|
||||||
|
selectAll.tap()
|
||||||
|
}
|
||||||
|
textEditor.typeText(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func save() {
|
||||||
|
saveButton.tapWhenReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
func cancel() {
|
||||||
|
cancelButton.tapWhenReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Assertions
|
||||||
|
|
||||||
|
func assertVisible(file: StaticString = #file, line: UInt = #line) {
|
||||||
|
XCTAssertTrue(
|
||||||
|
navigationTitle.waitForExistence(timeout: 5),
|
||||||
|
"Note editor should be visible",
|
||||||
|
file: file, line: line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertDismissed(file: StaticString = #file, line: UInt = #line) {
|
||||||
|
XCTAssertTrue(
|
||||||
|
navigationTitle.waitForDisappearance(timeout: 5),
|
||||||
|
"Note editor should be dismissed",
|
||||||
|
file: file, line: line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
76
Tests iOS/Screens/OnboardingScreen.swift
Normal file
76
Tests iOS/Screens/OnboardingScreen.swift
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
//
|
||||||
|
// OnboardingScreen.swift
|
||||||
|
// Tests iOS
|
||||||
|
//
|
||||||
|
// Screen object for the onboarding flow — welcome, time, day, style, and subscription screens.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
struct OnboardingScreen {
|
||||||
|
let app: XCUIApplication
|
||||||
|
|
||||||
|
// MARK: - Screen Elements
|
||||||
|
|
||||||
|
var welcomeScreen: XCUIElement { app.otherElements["onboarding_welcome"] }
|
||||||
|
var dayScreen: XCUIElement { app.otherElements["onboarding_day"] }
|
||||||
|
var subscriptionScreen: XCUIElement { app.otherElements["onboarding_subscription"] }
|
||||||
|
|
||||||
|
var dayTodayButton: XCUIElement { app.buttons.matching(NSPredicate(format: "identifier == %@", "onboarding_day_today")).firstMatch }
|
||||||
|
var dayYesterdayButton: XCUIElement { app.buttons.matching(NSPredicate(format: "identifier == %@", "onboarding_day_yesterday")).firstMatch }
|
||||||
|
var subscribeButton: XCUIElement { app.buttons.matching(NSPredicate(format: "identifier == %@", "onboarding_subscribe_button")).firstMatch }
|
||||||
|
var skipButton: XCUIElement { app.buttons.matching(NSPredicate(format: "identifier == %@", "onboarding_skip_button")).firstMatch }
|
||||||
|
|
||||||
|
// MARK: - Actions
|
||||||
|
|
||||||
|
/// Swipe left to advance to the next onboarding page.
|
||||||
|
func swipeToNext() {
|
||||||
|
app.swipeLeft()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Complete the full onboarding flow by swiping through all screens and tapping "Maybe Later".
|
||||||
|
func completeOnboarding() {
|
||||||
|
// Welcome -> swipe
|
||||||
|
if welcomeScreen.waitForExistence(timeout: 5) {
|
||||||
|
swipeToNext()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time -> swipe
|
||||||
|
// Time screen doesn't have a unique identifier, just swipe
|
||||||
|
swipeToNext()
|
||||||
|
|
||||||
|
// Day -> select Today, then swipe
|
||||||
|
if dayTodayButton.waitForExistence(timeout: 3) {
|
||||||
|
dayTodayButton.tap()
|
||||||
|
}
|
||||||
|
swipeToNext()
|
||||||
|
|
||||||
|
// Style -> swipe
|
||||||
|
swipeToNext()
|
||||||
|
|
||||||
|
// Subscription -> tap "Maybe Later"
|
||||||
|
if skipButton.waitForExistence(timeout: 5) {
|
||||||
|
skipButton.tap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Assertions
|
||||||
|
|
||||||
|
func assertVisible(file: StaticString = #file, line: UInt = #line) {
|
||||||
|
XCTAssertTrue(
|
||||||
|
welcomeScreen.waitForExistence(timeout: 5),
|
||||||
|
"Onboarding welcome screen should be visible",
|
||||||
|
file: file, line: line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertDismissed(file: StaticString = #file, line: UInt = #line) {
|
||||||
|
// After onboarding, the tab bar should be visible
|
||||||
|
let tabBar = app.tabBars.firstMatch
|
||||||
|
XCTAssertTrue(
|
||||||
|
tabBar.waitForExistence(timeout: 10),
|
||||||
|
"Tab bar should be visible after onboarding completes",
|
||||||
|
file: file, line: line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
74
Tests iOS/Screens/SettingsScreen.swift
Normal file
74
Tests iOS/Screens/SettingsScreen.swift
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
//
|
||||||
|
// SettingsScreen.swift
|
||||||
|
// Tests iOS
|
||||||
|
//
|
||||||
|
// Screen object for the Settings tab (Customize + Settings sub-tabs).
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
struct SettingsScreen {
|
||||||
|
let app: XCUIApplication
|
||||||
|
|
||||||
|
// MARK: - Elements
|
||||||
|
|
||||||
|
var settingsHeader: XCUIElement { app.staticTexts["settings_header"] }
|
||||||
|
var customizeSegment: XCUIElement { app.buttons["Customize"] }
|
||||||
|
var settingsSegment: XCUIElement { app.buttons["Settings"] }
|
||||||
|
var upgradeBanner: XCUIElement { app.otherElements["upgrade_banner"] }
|
||||||
|
var subscribeButton: XCUIElement { app.buttons["subscribe_button"] }
|
||||||
|
var whyUpgradeButton: XCUIElement { app.buttons["why_upgrade_button"] }
|
||||||
|
var browseThemesButton: XCUIElement { app.buttons["browse_themes_button"] }
|
||||||
|
var clearDataButton: XCUIElement { app.buttons["settings_clear_data"].firstMatch }
|
||||||
|
var analyticsToggle: XCUIElement { app.descendants(matching: .any).matching(identifier: "settings_analytics_toggle").firstMatch }
|
||||||
|
var showOnboardingButton: XCUIElement { app.buttons["settings_show_onboarding"].firstMatch }
|
||||||
|
|
||||||
|
// MARK: - Actions
|
||||||
|
|
||||||
|
func tapCustomizeTab() {
|
||||||
|
customizeSegment.tapWhenReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
func tapSettingsTab() {
|
||||||
|
settingsSegment.tapWhenReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
func tapClearData() {
|
||||||
|
clearDataButton.tapWhenReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
func tapAnalyticsToggle() {
|
||||||
|
// Scroll down to find the toggle if needed
|
||||||
|
let toggle = app.descendants(matching: .any).matching(identifier: "settings_analytics_toggle").firstMatch
|
||||||
|
if !toggle.isHittable {
|
||||||
|
app.swipeUp()
|
||||||
|
}
|
||||||
|
toggle.tapWhenReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Assertions
|
||||||
|
|
||||||
|
func assertVisible(file: StaticString = #file, line: UInt = #line) {
|
||||||
|
XCTAssertTrue(
|
||||||
|
settingsHeader.waitForExistence(timeout: 5),
|
||||||
|
"Settings header should be visible",
|
||||||
|
file: file, line: line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertUpgradeBannerVisible(file: StaticString = #file, line: UInt = #line) {
|
||||||
|
XCTAssertTrue(
|
||||||
|
upgradeBanner.waitForExistence(timeout: 5),
|
||||||
|
"Upgrade banner should be visible",
|
||||||
|
file: file, line: line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertUpgradeBannerHidden(file: StaticString = #file, line: UInt = #line) {
|
||||||
|
XCTAssertTrue(
|
||||||
|
upgradeBanner.waitForDisappearance(timeout: 5),
|
||||||
|
"Upgrade banner should be hidden (subscribed)",
|
||||||
|
file: file, line: line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
62
Tests iOS/Screens/TabBarScreen.swift
Normal file
62
Tests iOS/Screens/TabBarScreen.swift
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
//
|
||||||
|
// TabBarScreen.swift
|
||||||
|
// Tests iOS
|
||||||
|
//
|
||||||
|
// Screen object for the main tab bar navigation.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
struct TabBarScreen {
|
||||||
|
let app: XCUIApplication
|
||||||
|
|
||||||
|
// MARK: - Tab Buttons (using localized labels)
|
||||||
|
|
||||||
|
var dayTab: XCUIElement { app.tabBars.buttons["Day"] }
|
||||||
|
var monthTab: XCUIElement { app.tabBars.buttons["Month"] }
|
||||||
|
var yearTab: XCUIElement { app.tabBars.buttons["Year"] }
|
||||||
|
var insightsTab: XCUIElement { app.tabBars.buttons["Insights"] }
|
||||||
|
var settingsTab: XCUIElement { app.tabBars.buttons["Settings"] }
|
||||||
|
|
||||||
|
// MARK: - Actions
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func tapDay() -> DayScreen {
|
||||||
|
dayTab.tapWhenReady()
|
||||||
|
return DayScreen(app: app)
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func tapMonth() -> TabBarScreen {
|
||||||
|
monthTab.tapWhenReady()
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func tapYear() -> TabBarScreen {
|
||||||
|
yearTab.tapWhenReady()
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func tapInsights() -> TabBarScreen {
|
||||||
|
insightsTab.tapWhenReady()
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func tapSettings() -> SettingsScreen {
|
||||||
|
settingsTab.tapWhenReady()
|
||||||
|
return SettingsScreen(app: app)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Assertions
|
||||||
|
|
||||||
|
func assertDayTabSelected() {
|
||||||
|
XCTAssertTrue(dayTab.isSelected, "Day tab should be selected")
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertTabBarVisible() {
|
||||||
|
XCTAssertTrue(dayTab.waitForExistence(timeout: 5), "Tab bar should be visible")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user