Add edit banner button UI automated test + accessibility improvements

This commit adds an automated UI test to check if the edit banner button
UI is clickable and not hidden behind a top bar or another invisible
element.

It also improves accessibility support for some elements on login and
top bar.

Changelog-Changed: Improved accessibility support on some elements
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
Daniel D’Aquino
2024-11-17 13:13:52 -08:00
parent e3105a90c5
commit 5c6e5ca2de
16 changed files with 218 additions and 94 deletions

View File

@@ -338,7 +338,6 @@
4CE6DEEE27F7A08200C66700 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4CE6DEED27F7A08200C66700 /* Preview Assets.xcassets */; };
4CE6DEF827F7A08200C66700 /* damusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DEF727F7A08200C66700 /* damusTests.swift */; };
4CE6DF0227F7A08200C66700 /* damusUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF0127F7A08200C66700 /* damusUITests.swift */; };
4CE6DF0427F7A08200C66700 /* damusUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */; };
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */; };
4CE8794829941DA700F758CC /* RelayFilters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794729941DA700F758CC /* RelayFilters.swift */; };
4CE8794E2996B16A00F758CC /* RelayToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794D2996B16A00F758CC /* RelayToggle.swift */; };
@@ -1057,6 +1056,11 @@
D7100C5C2B77016700C59298 /* IAPProductStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7100C5B2B77016700C59298 /* IAPProductStateView.swift */; };
D7100C5E2B7709ED00C59298 /* PurpleStoreKitManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7100C5D2B7709ED00C59298 /* PurpleStoreKitManager.swift */; };
D71AC4CC2BA8E3480076268E /* VisibilityTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71AC4CB2BA8E3480076268E /* VisibilityTracker.swift */; };
D71AD8FD2CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71AD8FC2CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift */; };
D71AD8FE2CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71AD8FC2CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift */; };
D71AD8FF2CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71AD8FC2CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift */; };
D71AD9002CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71AD8FC2CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift */; };
D71AD9012CEC2398002E2C3C /* AppAccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71AD8FC2CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift */; };
D71DC1EC2A9129C3006E207C /* PostViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71DC1EB2A9129C3006E207C /* PostViewTests.swift */; };
D72341192B6864F200E1E135 /* DamusPurpleEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72341182B6864F200E1E135 /* DamusPurpleEnvironment.swift */; };
D723411A2B6864F200E1E135 /* DamusPurpleEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72341182B6864F200E1E135 /* DamusPurpleEnvironment.swift */; };
@@ -2266,7 +2270,6 @@
4CE6DEF727F7A08200C66700 /* damusTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusTests.swift; sourceTree = "<group>"; };
4CE6DEFD27F7A08200C66700 /* damusUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = damusUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
4CE6DF0127F7A08200C66700 /* damusUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusUITests.swift; sourceTree = "<group>"; };
4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusUITestsLaunchTests.swift; sourceTree = "<group>"; };
4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayConnection.swift; sourceTree = "<group>"; };
4CE8794729941DA700F758CC /* RelayFilters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilters.swift; sourceTree = "<group>"; };
4CE8794D2996B16A00F758CC /* RelayToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayToggle.swift; sourceTree = "<group>"; };
@@ -2397,6 +2400,7 @@
D7100C5B2B77016700C59298 /* IAPProductStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IAPProductStateView.swift; sourceTree = "<group>"; };
D7100C5D2B7709ED00C59298 /* PurpleStoreKitManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurpleStoreKitManager.swift; sourceTree = "<group>"; };
D71AC4CB2BA8E3480076268E /* VisibilityTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisibilityTracker.swift; sourceTree = "<group>"; };
D71AD8FC2CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAccessibilityIdentifiers.swift; sourceTree = "<group>"; };
D71DC1EB2A9129C3006E207C /* PostViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostViewTests.swift; sourceTree = "<group>"; };
D72341182B6864F200E1E135 /* DamusPurpleEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleEnvironment.swift; sourceTree = "<group>"; };
D723C38D2AB8D83400065664 /* ContentFilters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentFilters.swift; sourceTree = "<group>"; };
@@ -3125,6 +3129,7 @@
643EA5C7296B764E005081BB /* RelayFilterView.swift */,
D783A63E2AD4E53D00658DDA /* SuggestedHashtagsView.swift */,
D77BFA0A2AE3051200621634 /* ProfileActionSheetView.swift */,
D71AD8FC2CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift */,
);
path = Views;
sourceTree = "<group>";
@@ -3634,7 +3639,6 @@
isa = PBXGroup;
children = (
4CE6DF0127F7A08200C66700 /* damusUITests.swift */,
4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */,
);
path = damusUITests;
sourceTree = "<group>";
@@ -4669,6 +4673,7 @@
5CC852A62BE00F180039FFC5 /* HighlightEventRef.swift in Sources */,
4CE8794E2996B16A00F758CC /* RelayToggle.swift in Sources */,
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */,
D71AD8FF2CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift in Sources */,
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */,
4C9BB83129C0ED4F00FC4E37 /* DisplayName.swift in Sources */,
7CFF6317299FEFE5005D382A /* SelectableText.swift in Sources */,
@@ -4805,8 +4810,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D71AD9012CEC2398002E2C3C /* AppAccessibilityIdentifiers.swift in Sources */,
4CE6DF0227F7A08200C66700 /* damusUITests.swift in Sources */,
4CE6DF0427F7A08200C66700 /* damusUITestsLaunchTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -5165,6 +5170,7 @@
82D6FC062CD99F7900C925F4 /* ZapTypePicker.swift in Sources */,
82D6FC072CD99F7900C925F4 /* ZapUserView.swift in Sources */,
82D6FC082CD99F7900C925F4 /* ProfileZapLinkView.swift in Sources */,
D71AD8FE2CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift in Sources */,
82D6FC092CD99F7900C925F4 /* AboutView.swift in Sources */,
82D6FC0A2CD99F7900C925F4 /* ProfileName.swift in Sources */,
82D6FC0B2CD99F7900C925F4 /* ProfilePictureSelector.swift in Sources */,
@@ -5443,6 +5449,7 @@
D73E5EB42C6A97F4007EB227 /* NoteContent.swift in Sources */,
D73E5EB52C6A97F4007EB227 /* LongformEvent.swift in Sources */,
D73E5EB62C6A97F4007EB227 /* PushNotificationClient.swift in Sources */,
D71AD8FD2CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift in Sources */,
D73E5EB72C6A97F4007EB227 /* HighlightEvent.swift in Sources */,
D73E5EB82C6A97F4007EB227 /* RelayConnection.swift in Sources */,
D73E5EB92C6A97F4007EB227 /* RelayLog.swift in Sources */,
@@ -5771,6 +5778,7 @@
4C8FA7242BED58A900798A6A /* ThreadReply.swift in Sources */,
D798D21F2B0858D600234419 /* MigratedTypes.swift in Sources */,
D7CE1B472B0BE719002EDAD4 /* NativeObject.swift in Sources */,
D71AD9002CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift in Sources */,
D7CB5D552B11758A00AD4105 /* UnmuteThreadNotify.swift in Sources */,
D7CCFC192B058A3F00323D86 /* Block.swift in Sources */,
D7CCFC112B05884E00323D86 /* AsciiCharacter.swift in Sources */,

View File

@@ -40,7 +40,7 @@
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "YES">
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "4CE6DEFC27F7A08200C66700"

View File

@@ -20,6 +20,7 @@ struct DamusBackground: View {
.resizable()
.frame(maxWidth: .infinity, maxHeight: maxHeight, alignment: .center)
.ignoresSafeArea()
.accessibilityHidden(true)
}
}

View File

@@ -239,14 +239,7 @@ struct ContentView: View {
MainContent(damus: damus)
.toolbar() {
ToolbarItem(placement: .navigationBarLeading) {
Button {
isSideBarOpened.toggle()
} label: {
ProfilePicView(pubkey: damus_state!.pubkey, size: 32, highlight: .none, profiles: damus_state!.profiles, disable_animation: damus_state!.settings.disable_animation)
.opacity(isSideBarOpened ? 0 : 1)
.animation(isSideBarOpened ? .none : .default, value: isSideBarOpened)
}
.disabled(isSideBarOpened)
TopbarSideMenuButton(damus_state: damus, isSideBarOpened: $isSideBarOpened)
}
ToolbarItem(placement: .navigationBarTrailing) {
@@ -792,6 +785,25 @@ struct ContentView: View {
}
}
struct TopbarSideMenuButton: View {
let damus_state: DamusState
@Binding var isSideBarOpened: Bool
var body: some View {
Button {
isSideBarOpened.toggle()
} label: {
ProfilePicView(pubkey: damus_state.pubkey, size: 32, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation)
.opacity(isSideBarOpened ? 0 : 1)
.animation(isSideBarOpened ? .none : .default, value: isSideBarOpened)
.accessibilityHidden(true) // Knowing there is a profile picture here leads to no actionable outcome to VoiceOver users, so it is best not to show it
}
.accessibilityIdentifier(AppAccessibilityIdentifiers.main_side_menu_button.rawValue)
.accessibilityLabel(NSLocalizedString("Side menu", comment: "Accessibility label for the side menu button at the topbar"))
.disabled(isSideBarOpened)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(keypair: Keypair(pubkey: test_pubkey, privkey: nil), appDelegate: nil)

View File

@@ -0,0 +1,66 @@
//
// AppAccessibilityIdentifiers.swift
// damus
//
// Created by Daniel DAquino on 2024-11-18.
//
import Foundation
/// A collection of app-wide identifier constants used to facilitate UI tests to find the element they are looking for.
///
/// ## Implementation notes
///
/// - This is not an exhaustive list. Add more identifiers as needed.
/// - Organize this by separating each category with `MARK` comment markers and a unique prefix, each category separated by 2 empty lines
enum AppAccessibilityIdentifiers: String {
// MARK: Login
// Prefix: `sign_in`
/// Sign in button at the very start of the app
case sign_in_option_button
/// A secure text entry field where the user can put their private key when logging in
case sign_in_nsec_key_entry_field
/// Button to sign in after entering private key
case sign_in_confirm_button
// MARK: Onboarding
// Prefix: `onboarding`
/// The skip button on the onboarding sheet
case onboarding_sheet_skip_button
// MARK: Post composer
// Prefix: `post_composer`
/// The cancel post button
case post_composer_cancel_button
// MARK: Main interface layout
// Prefix: `main`
/// Profile picture item on the top toolbar, used to open the side menu
case main_side_menu_button
// MARK: Side menu
// Prefix: `side_menu`
/// The profile option in the side menu
case side_menu_profile_button
// MARK: Items specific to the user's own profile
// Prefix: `own_profile`
/// The edit profile button
case own_profile_edit_button
/// The button to edit the banner image on the profile
case own_profile_banner_image_edit_button
/// The button to pick the new banner image from URL
case own_profile_banner_image_edit_from_url
}

View File

@@ -32,15 +32,21 @@ struct EditBannerImageView: View {
.onFailureImage(defaultImage)
.kfClickable()
if #available(iOS 17.0, *) {
EditPictureControl(uploader: damus_state.settings.default_media_uploader, keypair: damus_state.keypair, pubkey: damus_state.pubkey, image_url: $banner_image, uploadObserver: viewModel, callback: callback)
.padding(10)
.safeAreaPadding(self.safeAreaInsets)
} else {
EditPictureControl(uploader: damus_state.settings.default_media_uploader, keypair: damus_state.keypair, pubkey: damus_state.pubkey, image_url: $banner_image, uploadObserver: viewModel, callback: callback)
.padding(10)
.padding(.top, self.safeAreaInsets.top)
}
EditPictureControl(uploader: damus_state.settings.default_media_uploader, keypair: damus_state.keypair, pubkey: damus_state.pubkey, image_url: $banner_image, uploadObserver: viewModel, callback: callback)
.padding(10)
.backwardsCompatibleSafeAreaPadding(self.safeAreaInsets)
.accessibilityLabel(NSLocalizedString("Edit banner image", comment: "Accessibility label for edit banner image button"))
.accessibilityIdentifier(AppAccessibilityIdentifiers.own_profile_banner_image_edit_button.rawValue)
}
}
}
extension View {
fileprivate func backwardsCompatibleSafeAreaPadding(_ insets: EdgeInsets) -> some View {
if #available(iOS 17.0, *) {
return self.safeAreaPadding(insets)
} else {
return self.padding(.top, insets.top)
}
}
}

View File

@@ -104,6 +104,7 @@ struct LoginView: View {
}
.frame(minWidth: 300, maxWidth: .infinity, maxHeight: 12, alignment: .center)
}
.accessibilityIdentifier(AppAccessibilityIdentifiers.sign_in_confirm_button.rawValue)
.buttonStyle(GradientButtonStyle())
.padding(.top, 10)
}
@@ -299,27 +300,35 @@ struct KeyInput: View {
var body: some View {
HStack {
Image(systemName: "doc.on.clipboard")
.foregroundColor(.gray)
.onTapGesture {
if let pastedkey = UIPasteboard.general.string {
self.key.wrappedValue = pastedkey
}
Button(action: {
if let pastedkey = UIPasteboard.general.string {
self.key.wrappedValue = pastedkey
}
}, label: {
Image(systemName: "doc.on.clipboard")
})
.foregroundColor(.gray)
.accessibilityLabel(NSLocalizedString("Paste private key", comment: "Accessibility label for the private key paste button"))
SignInScan(shouldSaveKey: shouldSaveKey, loginKey: key, privKeyFound: privKeyFound)
if is_secured {
SecureField("", text: key)
.nsecLoginStyle(key: key.wrappedValue, title: title)
} else {
TextField("", text: key)
.nsecLoginStyle(key: key.wrappedValue, title: title)
}
Image(systemName: "eye.slash")
.foregroundColor(.gray)
.onTapGesture {
is_secured.toggle()
}
SecureField("", text: key)
.nsecLoginStyle(key: key.wrappedValue, title: title)
.accessibilityLabel(NSLocalizedString("Account private key", comment: "Accessibility label for the private key input field"))
} else {
TextField("", text: key)
.nsecLoginStyle(key: key.wrappedValue, title: title)
.accessibilityLabel(NSLocalizedString("Account private key", comment: "Accessibility label for the private key input field"))
}
Button(action: {
is_secured.toggle()
}, label: {
Image(systemName: "eye.slash")
})
.foregroundColor(.gray)
.accessibilityLabel(NSLocalizedString("Toggle key visibility", comment: "Accessibility label for toggling the visibility of the private key input field"))
}
.padding(.vertical, 2)
.padding(.horizontal, 10)
@@ -342,6 +351,7 @@ struct SignInHeader: View {
.frame(width: 56, height: 56, alignment: .center)
.shadow(color: DamusColors.purple, radius: 2)
.padding(.bottom)
.accessibilityLabel(NSLocalizedString("Damus logo", comment: "Accessibility label for damus logo"))
Text("Sign in", comment: "Title of view to log into an account.")
.foregroundColor(DamusColors.neutral6)
@@ -365,10 +375,12 @@ struct SignInEntry: View {
.fontWeight(.medium)
.padding(.top, 30)
KeyInput(NSLocalizedString("nsec1...", comment: "Prompt for user to enter in an account key to login. This text shows the characters the key could start with if it was a private key."),
KeyInput(NSLocalizedString("nsec1", comment: "Prompt for user to enter in an account key to login. This text shows the characters the key could start with if it was a private key."),
key: key,
shouldSaveKey: shouldSaveKey,
privKeyFound: $privKeyFound)
.accessibilityIdentifier(AppAccessibilityIdentifiers.sign_in_nsec_key_entry_field.rawValue)
if privKeyFound {
Toggle(NSLocalizedString("Save Key in Secure Keychain", comment: "Toggle to save private key to the Apple secure keychain."), isOn: shouldSaveKey)
}
@@ -389,7 +401,7 @@ struct SignInScan: View {
Button(action: { showQR.toggle() }, label: {
Image(systemName: "qrcode.viewfinder")})
.foregroundColor(.gray)
.accessibilityLabel(NSLocalizedString("Scan QR code", comment: "Accessibility label for a button that scans a private key QR code"))
}
.sheet(isPresented: $showQR, onDismiss: {
if qrkey == nil { resetView() }}

View File

@@ -38,7 +38,9 @@ struct OnboardingSuggestionsView: View {
}, label: {
Text("Skip", comment: "Button to dismiss the suggested users screen")
.font(.subheadline.weight(.semibold))
}))
})
.accessibilityIdentifier(AppAccessibilityIdentifiers.onboarding_sheet_skip_button.rawValue)
)
.tag(0)
PostView(

View File

@@ -304,6 +304,7 @@ struct PostView: View {
.padding(10)
})
.buttonStyle(NeutralButtonStyle())
.accessibilityIdentifier(AppAccessibilityIdentifiers.post_composer_cancel_button.rawValue)
if let error {
Text(error)

View File

@@ -41,6 +41,7 @@ struct EditPictureControl: View {
}) {
Text("Image URL", comment: "Option to enter a url")
}
.accessibilityIdentifier(AppAccessibilityIdentifiers.own_profile_banner_image_edit_from_url.rawValue)
Button(action: {
self.show_library = true

View File

@@ -27,6 +27,7 @@ struct ProfileEditButton: View {
.minimumScaleFactor(0.5)
.lineLimit(1)
}
.accessibilityIdentifier(AppAccessibilityIdentifiers.own_profile_edit_button.rawValue)
}
func fillColor() -> Color {

View File

@@ -56,6 +56,7 @@ struct SetupView: View {
.frame(minWidth: 300, maxWidth: .infinity, maxHeight: 12, alignment: .center)
}
.buttonStyle(GradientButtonStyle())
.accessibilityIdentifier(AppAccessibilityIdentifiers.sign_in_option_button.rawValue)
.padding()
Button(action: {

View File

@@ -40,6 +40,7 @@ struct SideMenuView: View {
NavigationLink(value: Route.Profile(profile: profile_model, followers: followers)) {
navLabel(title: NSLocalizedString("Profile", comment: "Sidebar menu label for Profile view."), img: "user")
}
.accessibilityIdentifier(AppAccessibilityIdentifiers.side_menu_profile_button.rawValue)
NavigationLink(value: Route.Wallet(wallet: damus_state.wallet)) {
navLabel(title: NSLocalizedString("Wallet", comment: "Sidebar menu label for Wallet view."), img: "wallet")

View File

@@ -49,14 +49,7 @@ struct PostingTimelineView: View {
.frame(height: getSafeAreaTop())
HStack(alignment: .top) {
Button {
isSideBarOpened.toggle()
} label: {
ProfilePicView(pubkey: damus_state.pubkey, size: 32, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation)
.opacity(isSideBarOpened ? 0 : 1)
.animation(isSideBarOpened ? .none : .default, value: isSideBarOpened)
}
.disabled(isSideBarOpened)
TopbarSideMenuButton(damus_state: damus_state, isSideBarOpened: $isSideBarOpened)
Spacer()

View File

@@ -8,34 +8,85 @@
import XCTest
class damusUITests: XCTestCase {
var app = XCUIApplication()
typealias AID = AppAccessibilityIdentifiers
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
self.app = XCUIApplication()
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// In UI tests its important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
// Set app language to English
app.launchArguments += ["-AppleLanguages", "(en)"]
app.launchArguments += ["-AppleLocale", "en_US"]
// Force portrait orientation
XCUIDevice.shared.orientation = .portrait
// Optional: Reset the device's orientation before each test
addTeardownBlock {
XCUIDevice.shared.orientation = .portrait
}
app.launch()
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()
/// Tests if banner edit button is clickable.
/// Note: This is able to detect if the button is obscured by an invisible overlaying object.
/// See https://github.com/damus-io/damus/issues/2636 for the kind of issue this guards against.
func testEditBannerImage() throws {
// Use XCTAssert and related functions to verify your tests produce the correct results.
try self.loginIfNotAlready()
guard app.buttons[AID.main_side_menu_button.rawValue].tapIfExists(timeout: 5) else { throw DamusUITestError.timeout_waiting_for_element }
guard app.buttons[AID.side_menu_profile_button.rawValue].tapIfExists(timeout: 5) else { throw DamusUITestError.timeout_waiting_for_element }
guard app.buttons[AID.own_profile_edit_button.rawValue].tapIfExists(timeout: 5) else { throw DamusUITestError.timeout_waiting_for_element }
guard app.buttons[AID.own_profile_banner_image_edit_button.rawValue].waitForExistence(timeout: 5) else { throw DamusUITestError.timeout_waiting_for_element }
let bannerEditButtonCoordinates = app.buttons[AID.own_profile_banner_image_edit_button.rawValue].coordinate(withNormalizedOffset: CGVector.zero).withOffset(CGVector(dx: 15, dy: 15))
bannerEditButtonCoordinates.tap()
guard app.buttons[AID.own_profile_banner_image_edit_from_url.rawValue].waitForExistence(timeout: 5) else { throw DamusUITestError.timeout_waiting_for_element }
}
func testLaunchPerformance() throws {
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
// This measures how long it takes to launch your application.
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
func loginIfNotAlready() throws {
if app.buttons[AID.sign_in_option_button.rawValue].waitForExistence(timeout: 5) {
try self.login()
}
app.buttons[AID.onboarding_sheet_skip_button.rawValue].tapIfExists(timeout: 5)
app.buttons[AID.post_composer_cancel_button.rawValue].tapIfExists(timeout: 5)
}
func login() throws {
app.buttons[AID.sign_in_option_button.rawValue].tap()
guard app.secureTextFields[AID.sign_in_nsec_key_entry_field.rawValue].tapIfExists(timeout: 10) else { throw DamusUITestError.timeout_waiting_for_element }
app.typeText("nsec1vxvz8c7070d99njn0aqpcttljnzhfutt422l0r37yep7htesd0mq9p8fg2")
guard app.buttons[AID.sign_in_confirm_button.rawValue].tapIfExists(timeout: 5) else { throw DamusUITestError.timeout_waiting_for_element }
}
enum DamusUITestError: Error {
case timeout_waiting_for_element
}
}
extension XCUIElement {
@discardableResult
func tapIfExists(timeout: TimeInterval) -> Bool {
if self.waitForExistence(timeout: timeout) {
self.tap()
return true
}
return false
}
}

View File

@@ -1,32 +0,0 @@
//
// damusUITestsLaunchTests.swift
// damusUITests
//
// Created by William Casarin on 2022-04-01.
//
import XCTest
class damusUITestsLaunchTests: XCTestCase {
override class var runsForEachTargetApplicationUIConfiguration: Bool {
true
}
override func setUpWithError() throws {
continueAfterFailure = false
}
func testLaunch() throws {
let app = XCUIApplication()
app.launch()
// Insert steps here to perform after app launch but before taking a screenshot,
// such as logging into a test account or navigating somewhere in the app
let attachment = XCTAttachment(screenshot: app.screenshot())
attachment.name = "Launch Screen"
attachment.lifetime = .keepAlways
add(attachment)
}
}