Fix integration tests following the search tab introduction

Tabbar tabs now can take accessibility identifiers and the integration test uses the newly expected flow.
This commit is contained in:
Stefan Ceriu
2026-10-14 09:44:53 +03:00
parent 7752ae3f6a
commit 27e59a81e4
6 changed files with 32 additions and 13 deletions
@@ -26,18 +26,20 @@ import SwiftUI
/// When `true` the tab is given the `.search` role, which on iOS 26 detaches it /// When `true` the tab is given the `.search` role, which on iOS 26 detaches it
/// into the trailing search button in the tab bar. /// into the trailing search button in the tab bar.
let isSearch: Bool let isSearch: Bool
let accessibilityIdentifier: String?
var badgeCount = 0 var badgeCount = 0
/// Provide the tab's split coordinator in here to have the tab bar automatically hidden /// Provide the tab's split coordinator in here to have the tab bar automatically hidden
/// when pushing a child into the split view's details on iPhone/compact iPad. /// when pushing a child into the split view's details on iPhone/compact iPad.
weak var navigationSplitCoordinator: NavigationSplitCoordinator? weak var navigationSplitCoordinator: NavigationSplitCoordinator?
init(tag: Tag, title: String, icon: KeyPath<CompoundIcons, Image>, selectedIcon: KeyPath<CompoundIcons, Image>, isSearch: Bool = false) { init(tag: Tag, title: String, icon: KeyPath<CompoundIcons, Image>, selectedIcon: KeyPath<CompoundIcons, Image>, isSearch: Bool = false, accessibilityIdentifier: String? = nil) {
self.tag = tag self.tag = tag
self.title = title self.title = title
self.icon = icon self.icon = icon
self.selectedIcon = selectedIcon self.selectedIcon = selectedIcon
self.isSearch = isSearch self.isSearch = isSearch
self.accessibilityIdentifier = accessibilityIdentifier
} }
func barVisibility(in horizontalSizeClass: UserInterfaceSizeClass?) -> Visibility { func barVisibility(in horizontalSizeClass: UserInterfaceSizeClass?) -> Visibility {
@@ -406,6 +408,7 @@ private struct NavigationTabCoordinatorView<Tag: Hashable>: View {
} }
} }
.badge(module.details.badgeCount) .badge(module.details.badgeCount)
.accessibilityIdentifier(module.details.accessibilityIdentifier ?? "")
} }
} }
.backportTabBarMinimizeBehaviorOnScrollDown() .backportTabBarMinimizeBehaviorOnScrollDown()
@@ -90,13 +90,13 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
let chatsSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator(hideBrandChrome: flowParameters.userSession.userSettings.app.hideBrandChrome)) let chatsSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator(hideBrandChrome: flowParameters.userSession.userSettings.app.hideBrandChrome))
chatsTabFlowCoordinator = ChatsTabFlowCoordinator(navigationSplitCoordinator: chatsSplitCoordinator, chatsTabFlowCoordinator = ChatsTabFlowCoordinator(navigationSplitCoordinator: chatsSplitCoordinator,
flowParameters: flowParameters) flowParameters: flowParameters)
chatsTabDetails = .init(tag: HomeTab.chats, title: L10n.screenHomeTabChats, icon: \.chat, selectedIcon: \.chatSolid) chatsTabDetails = .init(tag: HomeTab.chats, title: L10n.screenHomeTabChats, icon: \.chat, selectedIcon: \.chatSolid, accessibilityIdentifier: A11yIdentifiers.homeTabs.chats)
chatsTabDetails.navigationSplitCoordinator = chatsSplitCoordinator chatsTabDetails.navigationSplitCoordinator = chatsSplitCoordinator
let spacesSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator(hideBrandChrome: flowParameters.userSession.userSettings.app.hideBrandChrome)) let spacesSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator(hideBrandChrome: flowParameters.userSession.userSettings.app.hideBrandChrome))
spacesTabFlowCoordinator = SpacesTabFlowCoordinator(navigationSplitCoordinator: spacesSplitCoordinator, spacesTabFlowCoordinator = SpacesTabFlowCoordinator(navigationSplitCoordinator: spacesSplitCoordinator,
flowParameters: flowParameters) flowParameters: flowParameters)
spacesTabDetails = .init(tag: HomeTab.spaces, title: L10n.screenHomeTabSpaces, icon: \.space, selectedIcon: \.spaceSolid) spacesTabDetails = .init(tag: HomeTab.spaces, title: L10n.screenHomeTabSpaces, icon: \.space, selectedIcon: \.spaceSolid, accessibilityIdentifier: A11yIdentifiers.homeTabs.spaces)
spacesTabDetails.navigationSplitCoordinator = spacesSplitCoordinator spacesTabDetails.navigationSplitCoordinator = spacesSplitCoordinator
if flowParameters.userSession.userSettings.app.globalSearchEnabled { if flowParameters.userSession.userSettings.app.globalSearchEnabled {
@@ -108,7 +108,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
searchScreenCoordinator = searchCoordinator searchScreenCoordinator = searchCoordinator
searchTabNavigationStackCoordinator = searchStackCoordinator searchTabNavigationStackCoordinator = searchStackCoordinator
searchTabDetails = .init(tag: HomeTab.search, title: UntranslatedL10n.screenHomeTabSearch, icon: \.search, selectedIcon: \.search, isSearch: true) searchTabDetails = .init(tag: HomeTab.search, title: UntranslatedL10n.screenHomeTabSearch, icon: \.search, selectedIcon: \.search, isSearch: true, accessibilityIdentifier: A11yIdentifiers.homeTabs.search)
} else { } else {
searchScreenCoordinator = nil searchScreenCoordinator = nil
searchTabNavigationStackCoordinator = nil searchTabNavigationStackCoordinator = nil
@@ -20,6 +20,7 @@ enum A11yIdentifiers {
static let encryptionResetScreen = EncryptionResetScreen() static let encryptionResetScreen = EncryptionResetScreen()
static let encryptionResetPasswordScreen = EncryptionResetPasswordScreen() static let encryptionResetPasswordScreen = EncryptionResetPasswordScreen()
static let homeScreen = HomeScreen() static let homeScreen = HomeScreen()
static let homeTabs = HomeTabs()
static let loginScreen = LoginScreen() static let loginScreen = LoginScreen()
static let authenticationStartScreen = AuthenticationStartScreen() static let authenticationStartScreen = AuthenticationStartScreen()
static let reportContent = ReportContent() static let reportContent = ReportContent()
@@ -48,6 +49,7 @@ enum A11yIdentifiers {
static let spacesScreen = SpacesScreen() static let spacesScreen = SpacesScreen()
static let spaceScreen = SpaceScreen() static let spaceScreen = SpaceScreen()
static let spaceAddRoomsScreen = SpaceAddRoomsScreen() static let spaceAddRoomsScreen = SpaceAddRoomsScreen()
static let searchScreen = SearchScreen()
static let spaceSettingsScreen = SpaceSettingsScreen() static let spaceSettingsScreen = SpaceSettingsScreen()
static let linkNewDeviceScreen = LinkNewDeviceScreen() static let linkNewDeviceScreen = LinkNewDeviceScreen()
@@ -120,6 +122,12 @@ enum A11yIdentifiers {
} }
} }
struct HomeTabs {
let chats = "home_tabs-chats"
let spaces = "home_tabs-spaces"
let search = "home_tabs-search"
}
struct InviteUsersScreen { struct InviteUsersScreen {
let proceed = "invite_users-proceed" let proceed = "invite_users-proceed"
let userProfile = "invite_users-user_profile" let userProfile = "invite_users-user_profile"
@@ -353,6 +361,13 @@ enum A11yIdentifiers {
let cancel = "space_add_rooms_screen-cancel" let cancel = "space_add_rooms_screen-cancel"
} }
struct SearchScreen {
let roomNamePrefix = "search_screen-room_name"
func roomName(_ name: String) -> String {
"\(roomNamePrefix):\(name)"
}
}
struct SpaceSettingsScreen { struct SpaceSettingsScreen {
let editBaseInfo = "space_settings_screen-edit_space_info" let editBaseInfo = "space_settings_screen-edit_space_info"
} }
@@ -283,7 +283,7 @@ struct SearchScreen: View {
private extension View { private extension View {
/// Searchable makes the preview contents randomly appear lower /// Searchable makes the preview contents randomly appear lower
@ViewBuilder func conditionalSearchable(searchQuery: Binding<String>) -> some View { @ViewBuilder func conditionalSearchable(searchQuery: Binding<String>) -> some View {
if !ProcessInfo.isXcodePreview, !ProcessInfo.isRunningTests { if !ProcessInfo.isXcodePreview, !ProcessInfo.isRunningTests || ProcessInfo.isRunningIntegrationTests {
searchable(text: searchQuery, placement: .toolbarPrincipal) searchable(text: searchQuery, placement: .toolbarPrincipal)
} else { } else {
self self
@@ -334,6 +334,7 @@ private struct SearchScreenRoomCell: View {
.padding(.vertical, 12) .padding(.vertical, 12)
} }
.buttonStyle(SearchScreenRoomCellButtonStyle(isSelected: isSelected)) .buttonStyle(SearchScreenRoomCellButtonStyle(isSelected: isSelected))
.accessibilityIdentifier(A11yIdentifiers.searchScreen.roomName(room.title))
.listRowInsets(.init()) .listRowInsets(.init())
.listRowSeparator(.hidden) .listRowSeparator(.hidden)
.rowDivider() .rowDivider()
+7 -7
View File
@@ -31,12 +31,17 @@ class UserFlowTests: XCTestCase {
/// Assumes app is on the home screen /// Assumes app is on the home screen
private func checkRoomFlows() { private func checkRoomFlows() {
// Search for the special test room // Search for the special test room
let searchField = app.searchFields.firstMatch let searchTab = app.buttons[A11yIdentifiers.homeTabs.search].firstMatch
XCTAssertTrue(searchTab.waitForExistence(timeout: 30))
searchTab.tap(.center)
// The search tab hosts its field in the tab bar, which exposes it as a text field.
let searchField = app.textFields.firstMatch
XCTAssertTrue(searchField.waitForExistence(timeout: 30)) XCTAssertTrue(searchField.waitForExistence(timeout: 30))
searchField.clearAndTypeText(Self.integrationTestsRoomName, app: app) searchField.clearAndTypeText(Self.integrationTestsRoomName, app: app)
// And open it // And open it
let firstRoom = app.buttons.matching(NSPredicate(format: "identifier CONTAINS %@", Self.integrationTestsRoomName)).firstMatch let firstRoom = app.buttons[A11yIdentifiers.searchScreen.roomName(Self.integrationTestsRoomName)].firstMatch
// The backend is sometimes really slow and having a longer timeout // The backend is sometimes really slow and having a longer timeout
// beats having to rerun the whole suite again. // beats having to rerun the whole suite again.
@@ -50,11 +55,6 @@ class UserFlowTests: XCTestCase {
// Go back to the room list // Go back to the room list
tapOnBackButton("Chats") tapOnBackButton("Chats")
// Cancel initial the room search
let searchCancelButton = app.buttons["Close"].firstMatch
XCTAssertTrue(searchCancelButton.waitForExistence(timeout: 10.0))
searchCancelButton.tap(.center)
} }
private func sendMessages() { private func sendMessages() {
+1 -1
View File
@@ -197,7 +197,7 @@ class UserSessionScreenTests: XCTestCase {
try await app.assertScreenshot(step: Step.homeScreenWithTabBar) try await app.assertScreenshot(step: Step.homeScreenWithTabBar)
// app.tabBars doesn't work on iPadOS 18 😐 // app.tabBars doesn't work on iPadOS 18 😐
app.buttons["Spaces"].firstMatch.tap(.center) app.buttons[A11yIdentifiers.homeTabs.spaces].firstMatch.tap(.center)
try await app.assertScreenshot(step: Step.spacesScreen) try await app.assertScreenshot(step: Step.spacesScreen)