diff --git a/Sources/XCTestApp/TestAppTestsView.swift b/Sources/XCTestApp/TestAppTestsView.swift index 3537971..432eff7 100644 --- a/Sources/XCTestApp/TestAppTestsView.swift +++ b/Sources/XCTestApp/TestAppTestsView.swift @@ -24,7 +24,12 @@ public struct TestAppTestsView: View { NavigationLink(test.rawValue, value: test) } .navigationDestination(for: Tests.self) { test in - test.view(withNavigationPath: $path) + test + .view(withNavigationPath: $path) + .navigationTitle(test.rawValue) +#if !os(macOS) && !os(tvOS) + .navigationBarTitleDisplayMode(.inline) +#endif } .navigationTitle(String(describing: Tests.self)) .toolbar { diff --git a/Sources/XCTestApp/TestAppView.swift b/Sources/XCTestApp/TestAppView.swift index 27dfd5d..3637626 100644 --- a/Sources/XCTestApp/TestAppView.swift +++ b/Sources/XCTestApp/TestAppView.swift @@ -14,7 +14,7 @@ public struct TestAppView: View { @State private var testState = "Running ..." private let testCase: any TestAppTestCase - + public var body: some View { Text(testState) .task { diff --git a/Sources/XCTestExtensions/XCTestCase+DisablePasswordAutofill.swift b/Sources/XCTestExtensions/XCTestCase+DisablePasswordAutofill.swift deleted file mode 100644 index c748552..0000000 --- a/Sources/XCTestExtensions/XCTestCase+DisablePasswordAutofill.swift +++ /dev/null @@ -1,97 +0,0 @@ -// -// This source file is part of the Stanford XCTestExtensions open-source project -// -// SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import OSLog -import XCTest - - -extension XCTestCase { - /// Navigate to the iOS settings app and turn off the password autofill functionality. - /// - /// The iOS Simulator has the password autofill feature enabled by default, which makes it challenging to write automated UI tests for password fields: - /// The iOS Simulator does not properly display the password autofill UI, and the UI test can no longer enter text in text fields that contain the autogenerated password with a yellow background. - /// - /// Use this function used to to disable password autofill by navigating to the iOS settings app and turning off the password autofill functionality in the settings UI. - /// - /// > Warning: While this workaround worked well until 17.2, we experienced a crash of the passwords section in the IOS 17.2 passwords app on the iOS simulator, which no longer allows us to use this workaround. - /// We recommend using a custom setup script to skip password-related functionality in your UI tests until there is a better workaround. Please inspect the logic to setup simulators in the [xcodebuild-or-fastlane.yml](https://github.com/StanfordBDHG/.github/blob/main/.github/workflows/xcodebuild-or-fastlane.yml) workflow and be sure to `setupSimulators: true` if you use the GitHub action as a reusable workflow. - @available( - iOS, - deprecated: 17.2, - message: """ - To avoid having the password autofill interfere with your UI test, \ - avoid specifying the password text content type for simulator builds. - This method will be removed in a future version. - """ - ) - @available(watchOS, unavailable) - @available(macOS, unavailable) - @available(tvOS, unavailable) - @available(visionOS, unavailable) - @MainActor - public func disablePasswordAutofill() throws { - let settingsApp = XCUIApplication(bundleIdentifier: "com.apple.Preferences") - settingsApp.terminate() - settingsApp.launch() - - XCTAssert(settingsApp.staticTexts["PASSWORDS"].waitForExistence(timeout: 5.0)) - settingsApp.staticTexts["PASSWORDS"].tap() - - if #available(iOS 17.2, *) { - sleep(2) - - guard settingsApp.navigationBars.staticTexts["Passwords"].waitForExistence(timeout: 2.0) else { - os_log("Could not open the passwords section in the iOS 17.2 simulator due to a bug that immediately closed the passwords section.") - return - } - } else { - XCTAssert(settingsApp.navigationBars.staticTexts["Passwords"].waitForExistence(timeout: 2.0)) - } - - let springboard = XCUIApplication(bundleIdentifier: XCUIApplication.homeScreenBundle) - - sleep(1) - if springboard.secureTextFields["Passcode field"].exists { - let passcodeInput = springboard.secureTextFields["Passcode field"] - passcodeInput.tap() - - sleep(2) - - passcodeInput.typeText("1234\r") - - sleep(2) - } else if #unavailable(iOS 17.4) { - // other versions just don't need a passcode anymore - os_log("Could not enter the passcode in the device to enter the password section in the settings app.") - throw XCTestError(.failureWhileWaiting) - } - - var counter = 0 - let passwordOptionsCell = settingsApp.tables.cells["PasswordOptionsCell"] - while !passwordOptionsCell.isHittable || counter > 10 { - _ = settingsApp.tables.cells["PasswordOptionsCell"].waitForExistence(timeout: 2.0) - counter += 1 - } - - sleep(3) - - settingsApp.tables.cells["PasswordOptionsCell"].tap() - - let autoFillPasswords: String - if #available(iOS 17.0, *) { - autoFillPasswords = "AutoFill Passwords and Passkeys" - } else { - autoFillPasswords = "AutoFill Passwords" - } - - XCTAssert(settingsApp.switches[autoFillPasswords].waitForExistence(timeout: 5)) - if settingsApp.switches[autoFillPasswords].value as? String == "1" { - settingsApp.switches[autoFillPasswords].tap() - } - } -} diff --git a/Sources/XCTestExtensions/XCTestExtensions.docc/XCTestExtensions.md b/Sources/XCTestExtensions/XCTestExtensions.docc/XCTestExtensions.md index bfc9b46..015d5c1 100644 --- a/Sources/XCTestExtensions/XCTestExtensions.docc/XCTestExtensions.md +++ b/Sources/XCTestExtensions/XCTestExtensions.docc/XCTestExtensions.md @@ -51,12 +51,13 @@ The `enter(value:)` and `delete(count:)` methods provide the `checkIfTextWasEnte ### Assertions -- ``XCTAssertThrowsErrorAsync`` +- ``XCTAssertThrowsErrorAsync(_:_:file:line:_:)`` ### Text Entry -- ``XCTest/XCUIElement/enter(value:checkIfTextWasEnteredCorrectly:dismissKeyboard:)`` -- ``XCTest/XCUIElement/delete(count:checkIfTextWasDeletedCorrectly:dismissKeyboard:)`` +- ``TextInputOptions`` +- ``XCTest/XCUIElement/enter(value:options:)`` +- ``XCTest/XCUIElement/delete(count:options:)`` ### App Interaction diff --git a/Sources/XCTestExtensions/XCUIElement+TextEntry.swift b/Sources/XCTestExtensions/XCUIElement+TextEntry.swift index 1a515fd..08d2ecb 100644 --- a/Sources/XCTestExtensions/XCUIElement+TextEntry.swift +++ b/Sources/XCTestExtensions/XCUIElement+TextEntry.swift @@ -16,6 +16,51 @@ import XCTest @MainActor var simulateFlakySimulatorTextEntry = false +/// Modify the behavior of text entry and text deletion. +public struct TextInputOptions: OptionSet, Sendable { + /// Disable the automatic dismiss of the keyboard at the end of text entry. + /// + /// By default the keyboard will be automatically dismissed after text entry. If you want to disable this behavior, e.g., entering input into the text field + /// in multiple steps, you can disable that behavior by specifying this option. + public static let disableKeyboardDismiss = TextInputOptions(rawValue: 1 << 0) + // on visionOS the keyboard app running state is always foreground and the keyboards query doesn't do anything. + + /// Place the cursor on the rightmost position in the text field. + /// + /// By default, the the textfield is selected by performing standard [`tap()`](https://developer.apple.com/documentation/xctest/xcuielement/1618666-tap) + /// on the text field which puts the cursor in the middle of the text field. + /// This might be an issue when working with text fields that have existing input, as the cursor will be place in the middle of the text. + /// When using this option, the text field is selected by continuously tapping from the rightmost edge of the text field until the keyboard exists. + /// + /// - Note: This method doesn't work on visionOS or macOS. For macOS you can manually call [`coordinate(withNormalizedOffset:)`](https://developer.apple.com/documentation/xctest/xcuielement/1500960-coordinate) + /// to tap on a fitting coordinate and then use the ``skipTextFieldSelection`` option. For visionOS `coordinate(withNormalizedOffset:)` doesn't work. Aim to not + /// test with longer text entry on visionOS. + @available(visionOS, unavailable, message: "Tapping from the far right is unsupported on visionOS") + @available(macOS, unavailable, message: "Tapping from the far right is unsupported on macOS") + public static let tapFromRight = TextInputOptions(rawValue: 1 << 1) + /// Do not verify if text was enter correctly. + /// + /// Unfortunately, the iOS simulator sometimes has flaky behavior when entering text in a simulator with low computation resources. + /// By default, it will be verified if the characteristic were enter/deleted correctly. + /// Use this option to disable this behavior. + public static let skipTextInputValidation = TextInputOptions(rawValue: 1 << 2) + /// Do not automatically select the text field before typing. + /// + /// By default the text field is automatically selected before typing text. Use this option to disable this behavior, if you + /// know the keyboard to already be present. + public static let skipTextFieldSelection = TextInputOptions(rawValue: 1 << 3) + + /// Same option as ``tapFromRight``, but accessible for us internally without compiler conditions. + fileprivate static let _tapFromRight = TextInputOptions(rawValue: 1 << 1) + + public let rawValue: UInt16 + + public init(rawValue: UInt16) { + self.rawValue = rawValue + } +} + + extension XCUIElement { /// Get the current value so we can assert if the text entry was correct. private var currentValue: String { @@ -29,64 +74,51 @@ extension XCUIElement { /// Delete a fixed number of characters in a text field or secure text field. - /// - Parameter count: The number of characters that should be deleted. - /// - Parameter checkIfTextWasDeletedCorrectly: Check if the text was deleted correctly. - /// - Parameter dismissKeyboard: Press the return key after deleting the text. - /// - Throws: Throws an `XCTestError` of the number of characters could not be deleted. - /// - /// Unfortunately, the iOS simulator sometimes has flaky behavior when entering text in a simulator with low computation resources. - /// The method provides the `checkIfTextWasDeletedCorrectly` parameter that is set to true by default to check if the characters were deleted correctly. - /// If your text entry does fail to do so, e.g., a deletion in a secure text field, set the `checkIfTextWasDeletedCorrectly` parameter to `false`. - public func delete( - count: Int, - checkIfTextWasDeletedCorrectly: Bool = true, - dismissKeyboard: Bool = true - ) throws { - // Select the textfield - selectField(dismissKeyboard: dismissKeyboard) - - try performDelete(count: count, checkIfTextWasDeletedCorrectly: checkIfTextWasDeletedCorrectly, recursiveDepth: 0) - - if dismissKeyboard { + /// + /// ```swift + /// try app.textFields["enter first name"].delete(4) + /// ``` + /// + /// - Parameters: + /// - count: The number of characters that should be deleted. + /// - options: Control additional behavior how text deletion should be performed. + /// - Throws: Throws an `XCTestError`, if the number of characters could not be deleted. + public func delete(count: Int, options: TextInputOptions = []) throws { + if !options.contains(.skipTextFieldSelection) { + selectField(options: options) + } + + try performDelete(count: count, options: options, recursiveDepth: 0) + + if !options.contains(.disableKeyboardDismiss) { XCUIApplication().dismissKeyboard() } } /// Type a text in a text field or secure text field. - /// - Parameter newValue: The text that should be typed. - /// - Parameter checkIfTextWasEnteredCorrectly: Check if the text was entered correctly. - /// - Parameter dismissKeyboard: Press the return key after entering the text. - /// - Throws: Throws an `XCTestError` of the text could not be entered in the text field. /// - /// Unfortunately, the iOS simulator sometimes has flaky behavior when entering text in a simulator with low computation resources. - /// The method provides the `checkIfTextWasEnteredCorrectly` parameter that is set to true by default to check if the characters were entered correctly. - /// If your text entry does fail to do so, e.g., an entry in a secure text field, set the `checkIfTextWasEnteredCorrectly` parameter to `false`. - public func enter( - value newValue: String, - checkIfTextWasEnteredCorrectly: Bool = true, - dismissKeyboard: Bool = true - ) throws { - // Select the textfield - selectField(dismissKeyboard: dismissKeyboard) - - try performEnter( - value: newValue, - checkIfTextWasEnteredCorrectly: checkIfTextWasEnteredCorrectly, - dismissKeyboard: dismissKeyboard, - recursiveDepth: 0 - ) - - if dismissKeyboard { + /// ```swift + /// try app.textFields["enter first name"].enter("Hello World") + /// ``` + /// + /// - Parameters: + /// - newValue: The text that should be typed. + /// - options: Control additional behavior how text entry should be performed. + /// - Throws: Throws an `XCTestError`, if the text could not be entered in the text field. + public func enter(value newValue: String, options: TextInputOptions = []) throws { + if !options.contains(.skipTextFieldSelection) { + selectField(options: options) + } + + try performEnter(value: newValue, options: options, recursiveDepth: 0) + + if !options.contains(.disableKeyboardDismiss) { XCUIApplication().dismissKeyboard() } } - private func performDelete( - count: Int, - checkIfTextWasDeletedCorrectly: Bool, - recursiveDepth: Int - ) throws { + private func performDelete(count: Int, options: TextInputOptions, recursiveDepth: Int) throws { guard recursiveDepth <= 2 else { os_log("Could not successfully delete \(count) characters in the textfield \(self.debugDescription)") throw XCTestError(.failureWhileWaiting) @@ -103,25 +135,17 @@ extension XCUIElement { } // Check of the text was deleted correctly: - if checkIfTextWasDeletedCorrectly { + if !options.contains(.skipTextInputValidation) { let countAfterDeletion = currentValue.count if max(currentValueCount - count, 0) < countAfterDeletion { - try performDelete( - count: countAfterDeletion - ( currentValueCount - count), - checkIfTextWasDeletedCorrectly: true, - recursiveDepth: recursiveDepth + 1 - ) + tap() // cursor might not be placed at the rightmost position of the text field + try performDelete(count: countAfterDeletion - ( currentValueCount - count), options: options, recursiveDepth: recursiveDepth + 1) } } } - private func performEnter( - value textToEnter: String, - checkIfTextWasEnteredCorrectly: Bool, - dismissKeyboard: Bool, - recursiveDepth: Int - ) throws { + private func performEnter(value textToEnter: String, options: TextInputOptions, recursiveDepth: Int) throws { guard recursiveDepth <= 2 else { os_log("Could not successfully verify entering \"\(textToEnter)\" in the textfield \(self.debugDescription)") throw XCTestError(.failureWhileWaiting) @@ -138,22 +162,16 @@ extension XCUIElement { } // Check if the text was entered correctly - if checkIfTextWasEnteredCorrectly { + if !options.contains(.skipTextInputValidation) { let valueAfterTextEntry = currentValue if self.elementType == .secureTextField { if previousValue.isEmpty && textToEnter.count != valueAfterTextEntry.count { - // We delete the text twice to ensure that we have an empty text field even if the textfield might go beyond the current scope - try performDelete(count: valueAfterTextEntry.count, checkIfTextWasDeletedCorrectly: false, recursiveDepth: 0) - XCUIApplication().dismissKeyboard() - try delete(count: valueAfterTextEntry.count, checkIfTextWasDeletedCorrectly: false, dismissKeyboard: false) - - try performEnter( - value: previousValue + textToEnter, - checkIfTextWasEnteredCorrectly: true, - dismissKeyboard: dismissKeyboard, - recursiveDepth: recursiveDepth + 1 - ) + try performDelete(count: valueAfterTextEntry.count, options: [], recursiveDepth: 0) + tap() // cursor might not be placed at the rightmost position of the text field + try performDelete(count: valueAfterTextEntry.count, options: [], recursiveDepth: 0) + + try performEnter(value: previousValue + textToEnter, options: options, recursiveDepth: recursiveDepth + 1) } else if previousValue.count + textToEnter.count != valueAfterTextEntry.count { os_log( """ @@ -165,17 +183,11 @@ extension XCUIElement { } } else { if previousValue + textToEnter != valueAfterTextEntry { - // We delete the text twice to ensure that we have an empty text field even if the textfield might go beyond the current scope - try performDelete(count: valueAfterTextEntry.count, checkIfTextWasDeletedCorrectly: false, recursiveDepth: 0) - XCUIApplication().dismissKeyboard() - try delete(count: valueAfterTextEntry.count, checkIfTextWasDeletedCorrectly: false, dismissKeyboard: false) - - try performEnter( - value: previousValue + textToEnter, - checkIfTextWasEnteredCorrectly: true, - dismissKeyboard: dismissKeyboard, - recursiveDepth: recursiveDepth + 1 - ) + try performDelete(count: valueAfterTextEntry.count, options: [], recursiveDepth: 0) + tap() // cursor might not be placed at the rightmost position of the text field + try performDelete(count: valueAfterTextEntry.count, options: [], recursiveDepth: 0) + + try performEnter(value: previousValue + textToEnter, options: options, recursiveDepth: recursiveDepth + 1) } } } @@ -187,34 +199,30 @@ extension XCUIElement { /// - Note: This will not necessarily bring up the keyboard in the simulator. Don't expect buttons to show there. /// If the user interacted with the Simulator (e.g. Mouse clicks) the keyboard won't show as the simulator expects input via the Mac Keyboard. /// This is controlled via I/O -> Keyboard -> Connect Hardware Keyboard / Toggle Software Keyboard - func selectField(dismissKeyboard: Bool) { + func selectField(options: TextInputOptions = []) { let app = XCUIApplication() - // With visionOS we can't detect if the keyboard is currently shown. Interacting with a keyboard that is not shown will fail though. - // So we have to build around the assumption that the keyboard is not shown when we select a text field. - #if !os(visionOS) - // Press the return button if the keyboard is currently active. - if dismissKeyboard { - app.dismissKeyboard() + if options.contains(._tapFromRight) { + // Select the text field, see https://stackoverflow.com/questions/38523125/place-cursor-at-the-end-of-uitextview-under-uitest + + XCTAssertFalse(app.keyboards.firstMatch.exists, "Keyboard must not exist when selecting text field from the right.") + + var offset = 0.99 + repeat { + coordinate(withNormalizedOffset: CGVector(dx: offset, dy: 0.5)).tap() + offset -= 0.05 + } while offset >= 0 && !app.keyboards.firstMatch.waitForExistence(timeout: 2.0) + + XCTAssertTrue(app.keyboards.firstMatch.waitForExistence(timeout: 2.0), "Keyboard does not exist.") + } else { + tap() + + #if os(visionOS) + XCTAssertTrue(app.visionOSKeyboard.wait(for: .runningForeground, timeout: 2.0)) + #elseif !os(macOS) && !targetEnvironment(macCatalyst) + XCTAssertTrue(app.keyboards.firstMatch.waitForExistence(timeout: 2.0)) + #endif } - #endif - - #if os(visionOS) - tap() - _ = app.visionOSKeyboard.waitForExistence(timeout: 2.0) // this will always succeed - #elseif os(macOS) || targetEnvironment(macCatalyst) - // this should hit the keyboard most likely. The `.keyboard` query doesn't exist on macOS. - coordinate(withNormalizedOffset: CGVector(dx: 0.95, dy: 0.5)).tap() - #else - let keyboard = app.keyboards.firstMatch - - // Select the text field, see https://stackoverflow.com/questions/38523125/place-cursor-at-the-end-of-uitextview-under-uitest - var offset = 0.99 - repeat { - coordinate(withNormalizedOffset: CGVector(dx: offset, dy: 0.5)).tap() - offset -= 0.05 - } while !keyboard.waitForExistence(timeout: 2.0) && offset > 0 - #endif // With latest simulator releases it seems like the "swift to type" tutorial isn't popping up anymore. // For more information see https://developer.apple.com/forums/thread/650826. diff --git a/Tests/UITests/TestAppUITests/XCTestAppTests.swift b/Tests/UITests/TestAppUITests/XCTestAppTests.swift index 5e16b4c..bc3753b 100644 --- a/Tests/UITests/TestAppUITests/XCTestAppTests.swift +++ b/Tests/UITests/TestAppUITests/XCTestAppTests.swift @@ -13,10 +13,13 @@ class XCTestAppTests: XCTestCase { func testTestAppTestCaseTest() throws { let app = XCUIApplication() app.launch() - - XCTAssert(app.buttons["XCTestApp"].waitForExistence(timeout: 5.0)) + + XCTAssert(app.wait(for: .runningForeground, timeout: 2.0)) + + XCTAssert(app.buttons["XCTestApp"].waitForExistence(timeout: 2.0)) app.buttons["XCTestApp"].tap() - - XCTAssert(app.staticTexts["Passed"].waitForExistence(timeout: 5)) + + XCTAssert(app.navigationBars.staticTexts["XCTestApp"].waitForExistence(timeout: 2.0)) + XCTAssert(app.staticTexts["Passed"].waitForExistence(timeout: 5.0)) } } diff --git a/Tests/UITests/TestAppUITests/XCTestExtensionsTests.swift b/Tests/UITests/TestAppUITests/XCTestExtensionsTests.swift index 47e5dbb..4aedb8e 100644 --- a/Tests/UITests/TestAppUITests/XCTestExtensionsTests.swift +++ b/Tests/UITests/TestAppUITests/XCTestExtensionsTests.swift @@ -25,14 +25,13 @@ class XCTestExtensionsTests: XCTestCase { #endif let app = XCUIApplication() - app.deleteAndLaunch(withSpringboardAppName: "TestApp") XCTAssert(app.buttons["XCTestExtensions"].waitForExistence(timeout: 5.0)) app.buttons["XCTestExtensions"].tap() XCTAssert(app.staticTexts["No text set ..."].waitForExistence(timeout: 5)) - XCTAssert(app.staticTexts["No secure text set ..."].waitForExistence(timeout: 5)) + XCTAssert(app.staticTexts["No secure text set ..."].exists) } @available(macOS, unavailable) @@ -56,14 +55,7 @@ class XCTestExtensionsTests: XCTestCase { app.buttons["XCTestExtensions"].tap() XCTAssert(app.staticTexts["No text set ..."].waitForExistence(timeout: 5)) - XCTAssert(app.staticTexts["No secure text set ..."].waitForExistence(timeout: 5)) - } - - @MainActor - func testDisablePasswordAutofill() throws { - #if os(iOS) - try disablePasswordAutofill() - #endif + XCTAssert(app.staticTexts["No secure text set ..."].exists) } func testTextEntry() throws { @@ -100,10 +92,17 @@ class XCTestExtensionsTests: XCTestCase { let textField = app.textFields["TextField"] let message = "This is a very long text and some more" - try textField.enter(value: message, checkIfTextWasEnteredCorrectly: false) + try textField.enter(value: message, options: .skipTextInputValidation) XCTAssert(app.staticTexts[message].waitForExistence(timeout: 5)) - try textField.enter(value: " ...", checkIfTextWasEnteredCorrectly: false) +#if os(visionOS) + try textField.enter(value: " ...", options: [.skipTextInputValidation]) +#else + try textField.enter(value: " ...", options: [.skipTextInputValidation, .tapFromRight]) +#endif XCTAssert(app.staticTexts["\(message) ..."].waitForExistence(timeout: 5)) + + // Test text field deletion with longer text input + try textField.delete(count: message.count + 4) } func testKeyboardBehavior() throws { @@ -115,21 +114,22 @@ class XCTestExtensionsTests: XCTestCase { app.buttons["DismissKeyboard"].tap() let checkLabel = { (label: String) in XCTAssert(app.textFields[label].exists) - app.textFields[label].selectField(dismissKeyboard: false) + app.textFields[label].selectField() #if os(visionOS) - XCTAssert(app.visionOSKeyboard.exists) + XCTAssert(app.visionOSKeyboard.wait(for: .runningForeground, timeout: 2.0)) #elseif !os(macOS) - print(app.textFields[label].isSelected) - XCTAssertTrue(app.keyboards.firstMatch.exists) + XCTAssertTrue(app.keyboards.firstMatch.waitForExistence(timeout: 2.0)) #endif - sleep(1) app.dismissKeyboard() - sleep(1) #if !os(visionOS) && !os(macOS) - // we currently can't check if the keyboard app is not launched +#if compiler(<6) + sleep(1) XCTAssertFalse(app.keyboards.firstMatch.exists) -#endif +#else // compiler(<6) + XCTAssertTrue(app.keyboards.firstMatch.waitForNonExistence(timeout: 2.0)) +#endif // compiler(<6) +#endif // !os(visionOS) && !os(macOS) } // this way we know exactly which button failed by line numbers. @@ -173,6 +173,6 @@ extension XCUIApplication { try secureTextField.delete(count: 42) XCTAssert(staticTexts["No secure text set ..."].waitForExistence(timeout: 5)) - XCTAssertFalse(staticTexts["Button was pressed!"].waitForExistence(timeout: 5.0)) + XCTAssertFalse(staticTexts["Button was pressed!"].exists) } }