Include the Members and Add Rooms screens in the spaces UI tests. (#4981)

* Expand the space flow UI tests to include the add rooms screen.

Also fixes some failures in these tests due to the room/space title now being a button.

* Expand the space flow UI tests to include the space members screen.

* Reset AppSettings before running preview tests.
This commit is contained in:
Doug
2026-01-21 15:24:54 +00:00
committed by GitHub
parent a77a41dbe7
commit e6efdf0e82
14 changed files with 286 additions and 16 deletions

View File

@@ -203,3 +203,14 @@ private extension RoomHero {
avatarUrl: memberProxy.avatarURL?.absoluteString) avatarUrl: memberProxy.avatarURL?.absoluteString)
} }
} }
@MainActor
extension Array where Element == JoinedRoomProxyProtocol {
static var mockRooms: [JoinedRoomProxyProtocol] {
[
JoinedRoomProxyMock(.init(id: "1", name: "Room Name", canonicalAlias: "#room-name:example.com")),
JoinedRoomProxyMock(.init(id: "2", name: "Room Name", canonicalAlias: "#room-name:example.com")),
JoinedRoomProxyMock(.init(id: "3", name: "Room Name", canonicalAlias: "#room-name:example.com"))
]
}
}

View File

@@ -43,8 +43,11 @@ enum A11yIdentifiers {
static let notificationSettingsEditScreen = NotificationSettingsEditScreen() static let notificationSettingsEditScreen = NotificationSettingsEditScreen()
static let pollFormScreen = PollFormScreen() static let pollFormScreen = PollFormScreen()
static let roomPollsHistoryScreen = RoomPollsHistoryScreen() static let roomPollsHistoryScreen = RoomPollsHistoryScreen()
static let roomMembersListScreen = RoomMembersListScreen()
static let manageRoomMemberSheet = ManageRoomMemberSheet() static let manageRoomMemberSheet = ManageRoomMemberSheet()
static let spacesScreen = SpacesScreen() static let spacesScreen = SpacesScreen()
static let spaceScreen = SpaceScreen()
static let spaceAddRoomsScreen = SpaceAddRoomsScreen()
static let linkNewDeviceScreen = LinkNewDeviceScreen() static let linkNewDeviceScreen = LinkNewDeviceScreen()
struct AlertInfo { struct AlertInfo {
@@ -303,6 +306,10 @@ enum A11yIdentifiers {
let loadMore = "room_polls_history_screen-load_more" let loadMore = "room_polls_history_screen-load_more"
} }
struct RoomMembersListScreen {
let invite = "room_members_list_screen-invite"
}
struct ManageRoomMemberSheet { struct ManageRoomMemberSheet {
let viewProfile = "manage_room_member_sheet-view_profile" let viewProfile = "manage_room_member_sheet-view_profile"
} }
@@ -316,6 +323,16 @@ enum A11yIdentifiers {
} }
} }
struct SpaceScreen {
let moreMenu = "space_screen-more_menu"
let addExistingRooms = "space_screen-add_existing_rooms"
let viewMembers = "space_screen-view_members"
}
struct SpaceAddRoomsScreen {
let cancel = "space_add_rooms_screen-cancel"
}
struct LinkNewDeviceScreen { struct LinkNewDeviceScreen {
let cancel = "link_new_device_screen-cancel" let cancel = "link_new_device_screen-cancel"
let mobileDevice = "link_new_device_screen-mobile_device" let mobileDevice = "link_new_device_screen-mobile_device"

View File

@@ -102,6 +102,7 @@ struct RoomMembersListScreen: View {
Button(L10n.actionInvite) { Button(L10n.actionInvite) {
context.send(viewAction: .invite) context.send(viewAction: .invite)
} }
.accessibilityIdentifier(A11yIdentifiers.roomMembersListScreen.invite)
} }
} }
} }

View File

@@ -226,10 +226,6 @@ struct RoomScreen_Previews: PreviewProvider, TestablePreview {
} }
static func makeViewModels(canSendMessage: Bool = true, hasSuccessor: Bool = false) -> ViewModels { static func makeViewModels(canSendMessage: Bool = true, hasSuccessor: Bool = false) -> ViewModels {
// TimelineItemBubbledStylerView sets enableKeyShareOnInvite to true which if run
// before these tests, ends up adding a banner to the snapshots.
AppSettings.resetAllSettings()
let roomProxyMock = JoinedRoomProxyMock(.init(id: "stable_id", let roomProxyMock = JoinedRoomProxyMock(.init(id: "stable_id",
name: "Preview room", name: "Preview room",
hasOngoingCall: true, hasOngoingCall: true,

View File

@@ -115,6 +115,7 @@ struct SpaceAddRoomsScreen: View {
ToolbarButton(role: .cancel) { ToolbarButton(role: .cancel) {
context.send(viewAction: .cancel) context.send(viewAction: .cancel)
} }
.accessibilityIdentifier(A11yIdentifiers.spaceAddRoomsScreen.cancel)
} }
ToolbarItem(placement: .confirmationAction) { ToolbarItem(placement: .confirmationAction) {
@@ -186,11 +187,7 @@ struct SpaceAddRoomsScreen_Previews: PreviewProvider, TestablePreview {
let spaceRoomListProxy = SpaceRoomListProxyMock(.init(spaceServiceRoom: SpaceServiceRoomMock(.init(isSpace: true)))) let spaceRoomListProxy = SpaceRoomListProxyMock(.init(spaceServiceRoom: SpaceServiceRoomMock(.init(isSpace: true))))
let clientProxy = ClientProxyMock(.init()) let clientProxy = ClientProxyMock(.init())
clientProxy.recentlyVisitedRoomsFilterReturnValue = [ clientProxy.recentlyVisitedRoomsFilterReturnValue = .mockRooms
JoinedRoomProxyMock(.init(id: "1", name: "Room Name", canonicalAlias: "#room-name:example.com")),
JoinedRoomProxyMock(.init(id: "2", name: "Room Name", canonicalAlias: "#room-name:example.com")),
JoinedRoomProxyMock(.init(id: "3", name: "Room Name", canonicalAlias: "#room-name:example.com"))
]
let viewModel = SpaceAddRoomsScreenViewModel(spaceRoomListProxy: spaceRoomListProxy, let viewModel = SpaceAddRoomsScreenViewModel(spaceRoomListProxy: spaceRoomListProxy,
userSession: UserSessionMock(.init(clientProxy: clientProxy)), userSession: UserSessionMock(.init(clientProxy: clientProxy)),

View File

@@ -100,6 +100,8 @@ struct SpaceScreen: View {
Button { context.send(viewAction: .addExistingRooms) } label: { Button { context.send(viewAction: .addExistingRooms) } label: {
Label(L10n.actionAddExistingRooms, icon: \.room) Label(L10n.actionAddExistingRooms, icon: \.room)
} }
.accessibilityIdentifier(A11yIdentifiers.spaceScreen.addExistingRooms)
Button { context.send(viewAction: .manageChildren) } label: { Button { context.send(viewAction: .manageChildren) } label: {
Label(L10n.actionManageRooms, icon: \.edit) Label(L10n.actionManageRooms, icon: \.edit)
} }
@@ -111,7 +113,9 @@ struct SpaceScreen: View {
Button { context.send(viewAction: .displayMembers(roomProxy: roomProxy)) } label: { Button { context.send(viewAction: .displayMembers(roomProxy: roomProxy)) } label: {
Label(L10n.screenSpaceMenuActionMembers, icon: \.user) Label(L10n.screenSpaceMenuActionMembers, icon: \.user)
} }
.accessibilityIdentifier(A11yIdentifiers.spaceScreen.viewMembers)
} }
if let permalink = context.viewState.permalink { if let permalink = context.viewState.permalink {
ShareLink(item: permalink) { ShareLink(item: permalink) {
Label(L10n.actionShare, icon: \.shareIos) Label(L10n.actionShare, icon: \.shareIos)
@@ -135,6 +139,7 @@ struct SpaceScreen: View {
// Use an SF Symbol to match what ToolbarItemGroup(placement: .secondaryAction) would give us. // Use an SF Symbol to match what ToolbarItemGroup(placement: .secondaryAction) would give us.
Image(systemSymbol: .ellipsis) Image(systemSymbol: .ellipsis)
} }
.accessibilityIdentifier(A11yIdentifiers.spaceScreen.moreMenu)
} }
} }
} }

View File

@@ -596,8 +596,9 @@ class MockScreen: Identifiable {
let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com", let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com",
deviceID: "MOCKCLIENT", deviceID: "MOCKCLIENT",
roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(roomSummaries))), roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(roomSummaries))),
spaceServiceConfiguration: .init(topLevelSpaces: .mockSingleRoom), spaceServiceConfiguration: .init(topLevelSpaces: .mockSpaceList.filter(\.isSpace) + .mockSingleRoom),
roomPreviews: [SpaceServiceRoomProtocol].mockSpaceList.map(RoomPreviewProxyMock.init))) roomPreviews: [SpaceServiceRoomProtocol].mockSpaceList.map(RoomPreviewProxyMock.init)))
clientProxy.recentlyVisitedRoomsFilterReturnValue = .mockRooms
// The tab bar remains hidden for the non-spaces tests as we don't supply any mock spaces. // The tab bar remains hidden for the non-spaces tests as we don't supply any mock spaces.
let spaceServiceProxy = SpaceServiceProxyMock(id == .userSessionSpacesFlow ? .populated : .init()) let spaceServiceProxy = SpaceServiceProxyMock(id == .userSessionSpacesFlow ? .populated : .init())

File diff suppressed because it is too large Load Diff

View File

@@ -27,6 +27,7 @@ extension PreviewTests {
{% for type in types.types where (type.implements.TestablePreview or type.based.TestablePreview or type|annotated:"TestablePreview") and type.name != "TestablePreview" %} {% for type in types.types where (type.implements.TestablePreview or type.based.TestablePreview or type|annotated:"TestablePreview") and type.name != "TestablePreview" %}
func test{{ type.name|replace:"_Previews", "" }}() async throws { func test{{ type.name|replace:"_Previews", "" }}() async throws {
AppSettings.resetAllSettings() // Ensure this test's previews start with fresh settings.
for (index, preview) in {{ type.name }}._allPreviews.enumerated() { for (index, preview) in {{ type.name }}._allPreviews.enumerated() {
try await assertSnapshots(matching: preview, step: index) try await assertSnapshots(matching: preview, step: index)
} }

View File

@@ -27,6 +27,8 @@ class UserSessionScreenTests: XCTestCase {
static let subspaceScreen = 7 static let subspaceScreen = 7
static let subspaceRoomScreen = 8 static let subspaceRoomScreen = 8
static let spaceJoinRoomScreen = 9 static let spaceJoinRoomScreen = 9
static let spaceAddRoomsScreen = 10
static let spaceMembersListScreen = 11
} }
func testUserSessionFlows() async throws { func testUserSessionFlows() async throws {
@@ -37,7 +39,7 @@ class UserSessionScreenTests: XCTestCase {
try await app.assertScreenshot(step: Step.homeScreen) try await app.assertScreenshot(step: Step.homeScreen)
app.buttons[A11yIdentifiers.homeScreen.roomName(firstRoomName)].tap() app.buttons[A11yIdentifiers.homeScreen.roomName(firstRoomName)].tap()
XCTAssert(app.staticTexts[firstRoomName].waitForExistence(timeout: 5.0)) XCTAssert(app.buttons[firstRoomName].waitForExistence(timeout: 5.0))
try await Task.sleep(for: .seconds(1)) try await Task.sleep(for: .seconds(1))
try await app.assertScreenshot(step: Step.roomScreen) try await app.assertScreenshot(step: Step.roomScreen)
@@ -48,7 +50,7 @@ class UserSessionScreenTests: XCTestCase {
func testUserSessionReply() async throws { func testUserSessionReply() async throws {
let app = Application.launch(.userSessionScreenReply, disableTimelineAccessibility: false) let app = Application.launch(.userSessionScreenReply, disableTimelineAccessibility: false)
app.buttons[A11yIdentifiers.homeScreen.roomName(firstRoomName)].tap() app.buttons[A11yIdentifiers.homeScreen.roomName(firstRoomName)].tap()
XCTAssert(app.staticTexts[firstRoomName].waitForExistence(timeout: 5.0)) XCTAssert(app.buttons[firstRoomName].waitForExistence(timeout: 5.0))
try await Task.sleep(for: .seconds(1)) try await Task.sleep(for: .seconds(1))
let cell = app.cells.element(boundBy: 1) // Skip the typing indicator cell let cell = app.cells.element(boundBy: 1) // Skip the typing indicator cell
@@ -61,7 +63,7 @@ class UserSessionScreenTests: XCTestCase {
let app = Application.launch(.userSessionScreen) let app = Application.launch(.userSessionScreen)
app.buttons[A11yIdentifiers.homeScreen.roomName(firstRoomName)].tap() app.buttons[A11yIdentifiers.homeScreen.roomName(firstRoomName)].tap()
XCTAssert(app.staticTexts[firstRoomName].waitForExistence(timeout: 5.0)) XCTAssert(app.buttons[firstRoomName].waitForExistence(timeout: 5.0))
app.buttons[A11yIdentifiers.roomScreen.joinCall].tap() app.buttons[A11yIdentifiers.roomScreen.joinCall].tap()
@@ -94,8 +96,26 @@ class UserSessionScreenTests: XCTestCase {
try await Task.sleep(for: .seconds(1)) try await Task.sleep(for: .seconds(1))
try await app.assertScreenshot(step: Step.subspaceScreen) try await app.assertScreenshot(step: Step.subspaceScreen)
app.buttons[A11yIdentifiers.spaceScreen.moreMenu].tap()
app.buttons[A11yIdentifiers.spaceScreen.addExistingRooms].tap()
XCTAssert(app.buttons[A11yIdentifiers.spaceAddRoomsScreen.cancel].waitForExistence(timeout: 5.0))
try await Task.sleep(for: .seconds(1))
try await app.assertScreenshot(step: Step.spaceAddRoomsScreen)
app.buttons[A11yIdentifiers.spaceAddRoomsScreen.cancel].tap()
XCTAssert(app.staticTexts[joinedSubspaceName].waitForExistence(timeout: 5.0))
app.buttons[A11yIdentifiers.spaceScreen.moreMenu].tap()
app.buttons[A11yIdentifiers.spaceScreen.viewMembers].tap()
XCTAssert(app.buttons[A11yIdentifiers.roomMembersListScreen.invite].waitForExistence(timeout: 5.0))
try await Task.sleep(for: .seconds(1))
try await app.assertScreenshot(step: Step.spaceMembersListScreen)
app.navigationBars.buttons[joinedSubspaceName].firstMatch.tap(.center)
XCTAssert(app.staticTexts[joinedSubspaceName].waitForExistence(timeout: 5.0))
app.buttons[A11yIdentifiers.spacesScreen.spaceRoomName(joinedSubspaceRoomName)].tap() app.buttons[A11yIdentifiers.spacesScreen.spaceRoomName(joinedSubspaceRoomName)].tap()
XCTAssert(app.staticTexts[joinedSubspaceRoomName].waitForExistence(timeout: 5.0)) XCTAssert(app.buttons[joinedSubspaceRoomName].waitForExistence(timeout: 5.0))
try await Task.sleep(for: .seconds(1)) try await Task.sleep(for: .seconds(1))
try await app.assertScreenshot(step: Step.subspaceRoomScreen) try await app.assertScreenshot(step: Step.subspaceRoomScreen)
@@ -126,7 +146,7 @@ class UserSessionScreenTests: XCTestCase {
// Tap join on the join room screen. // Tap join on the join room screen.
app.buttons[A11yIdentifiers.joinRoomScreen.join].tap() app.buttons[A11yIdentifiers.joinRoomScreen.join].tap()
XCTAssert(app.staticTexts[A11yIdentifiers.roomScreen.name].waitForExistence(timeout: 5.0)) // The space screen reuses the room screen header XCTAssert(app.buttons[A11yIdentifiers.roomScreen.name].waitForExistence(timeout: 5.0)) // The space screen reuses the room screen header
try await Task.sleep(for: .seconds(1)) try await Task.sleep(for: .seconds(1))
try await app.assertScreenshot(step: Step.spaceScreen) try await app.assertScreenshot(step: Step.spaceScreen)
@@ -144,7 +164,7 @@ class UserSessionScreenTests: XCTestCase {
app.buttons.matching(NSPredicate(format: "identifier == %@ && label == %@", app.buttons.matching(NSPredicate(format: "identifier == %@ && label == %@",
A11yIdentifiers.homeScreen.roomName(spaceInviteName), A11yIdentifiers.homeScreen.roomName(spaceInviteName),
"Accept")).firstMatch.tap() "Accept")).firstMatch.tap()
XCTAssert(app.staticTexts[A11yIdentifiers.roomScreen.name].waitForExistence(timeout: 5.0)) // The space screen reuses the room screen header XCTAssert(app.buttons[A11yIdentifiers.roomScreen.name].waitForExistence(timeout: 5.0)) // The space screen reuses the room screen header
try await Task.sleep(for: .seconds(1)) try await Task.sleep(for: .seconds(1))
try await app.assertScreenshot(step: Step.spaceScreen) try await app.assertScreenshot(step: Step.spaceScreen)
} }

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:67e8f04eb61e42989683d1d5e8667474928123902d4980a470b481589c84ac37
size 312442

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:06d692328ca4bd472d38dc907a22e3ca7400caac504493012a9cb950ffa6c711
size 331112

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6361faefaeff3d18121fe975c6c472a5f33b08cd4818c6e6192dcf5c940ffeae
size 171619

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0f9e70f683eb594c733961f8263a492845adef03ac965ee6e8b2bb86b3e2adc7
size 229161