diff --git a/xcuitest-sample-proj.xcodeproj/project.pbxproj b/xcuitest-sample-proj.xcodeproj/project.pbxproj index b9c0b03..0cf6b55 100644 --- a/xcuitest-sample-proj.xcodeproj/project.pbxproj +++ b/xcuitest-sample-proj.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ FFF034912A6CFA8B00A33E97 /* BasePage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF034902A6CFA8B00A33E97 /* BasePage.swift */; }; FFF034932A6CFEA200A33E97 /* SecondPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF034922A6CFEA200A33E97 /* SecondPage.swift */; }; FFF034952A6D027B00A33E97 /* TabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF034942A6D027B00A33E97 /* TabBar.swift */; }; + FFF034992A6D219000A33E97 /* TextFieldWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF034982A6D219000A33E97 /* TextFieldWrapper.swift */; }; + FFF0349B2A6D227100A33E97 /* XCUIApplication+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF0349A2A6D227100A33E97 /* XCUIApplication+Extensions.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -60,6 +62,8 @@ FFF034902A6CFA8B00A33E97 /* BasePage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasePage.swift; sourceTree = ""; }; FFF034922A6CFEA200A33E97 /* SecondPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondPage.swift; sourceTree = ""; }; FFF034942A6D027B00A33E97 /* TabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBar.swift; sourceTree = ""; }; + FFF034982A6D219000A33E97 /* TextFieldWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldWrapper.swift; sourceTree = ""; }; + FFF0349A2A6D227100A33E97 /* XCUIApplication+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCUIApplication+Extensions.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -153,6 +157,7 @@ children = ( FF79C27E2A66F3A000BBC60A /* XCUIElement+Extensions.swift */, FF79C2802A66F45200BBC60A /* XCUIElementQuery+Extensions.swift */, + FFF0349A2A6D227100A33E97 /* XCUIApplication+Extensions.swift */, ); path = Extensions; sourceTree = ""; @@ -178,6 +183,7 @@ isa = PBXGroup; children = ( FF86799D2A696BDC00C4A1A7 /* CarouselItemView.swift */, + FFF034982A6D219000A33E97 /* TextFieldWrapper.swift */, ); path = Views; sourceTree = ""; @@ -321,6 +327,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FFF034992A6D219000A33E97 /* TextFieldWrapper.swift in Sources */, FF86799B2A69699D00C4A1A7 /* AppDelegate.swift in Sources */, FF86799E2A696BDC00C4A1A7 /* CarouselItemView.swift in Sources */, ); @@ -343,6 +350,7 @@ FFF034952A6D027B00A33E97 /* TabBar.swift in Sources */, FF79C2892A66F9F500BBC60A /* InteractionHelper.swift in Sources */, FF4D5CB02A68033C00B52624 /* ElementsHelper.swift in Sources */, + FFF0349B2A6D227100A33E97 /* XCUIApplication+Extensions.swift in Sources */, FFF0348F2A6CFA6500A33E97 /* FirstPage.swift in Sources */, FF79C2862A66F72200BBC60A /* TestConstants.swift in Sources */, FF79C27F2A66F3A000BBC60A /* XCUIElement+Extensions.swift in Sources */, diff --git a/xcuitest-sample-proj.xcodeproj/project.xcworkspace/xcuserdata/shamanec.xcuserdatad/UserInterfaceState.xcuserstate b/xcuitest-sample-proj.xcodeproj/project.xcworkspace/xcuserdata/shamanec.xcuserdatad/UserInterfaceState.xcuserstate index 0d85096..bb9b263 100644 Binary files a/xcuitest-sample-proj.xcodeproj/project.xcworkspace/xcuserdata/shamanec.xcuserdatad/UserInterfaceState.xcuserstate and b/xcuitest-sample-proj.xcodeproj/project.xcworkspace/xcuserdata/shamanec.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/xcuitest-sample-proj/AppDelegate.swift b/xcuitest-sample-proj/AppDelegate.swift index aac8aab..ff6d81c 100644 --- a/xcuitest-sample-proj/AppDelegate.swift +++ b/xcuitest-sample-proj/AppDelegate.swift @@ -34,6 +34,7 @@ struct TabBarView: View { struct CarouselView: View { @State private var isButtonVisible = true + @State private var text: String = "" var body: some View { VStack { @@ -63,6 +64,12 @@ struct CarouselView: View { .accessibilityIdentifier("disappearing-button") } } + + TextFieldWrapper(text: $text) + .padding() + .frame(width: 200, height: 50) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .accessibilityIdentifier("text-field") } } diff --git a/xcuitest-sample-proj/Views/TextFieldWrapper.swift b/xcuitest-sample-proj/Views/TextFieldWrapper.swift new file mode 100644 index 0000000..dcf724d --- /dev/null +++ b/xcuitest-sample-proj/Views/TextFieldWrapper.swift @@ -0,0 +1,43 @@ +// +// TextFieldWrapper.swift +// xcuitest-sample-proj +// +// Created by Nikola Shabanov on 23.07.23. +// + +import SwiftUI + +struct TextFieldWrapper: UIViewRepresentable { + @Binding var text: String + + func makeUIView(context: Context) -> UITextField { + let textField = UITextField() + textField.delegate = context.coordinator + + // Apply the black border to the UITextField + textField.layer.borderColor = UIColor.black.cgColor + textField.layer.borderWidth = 1.0 + + return textField + } + + func updateUIView(_ uiView: UITextField, context: Context) { + uiView.text = text + } + + func makeCoordinator() -> Coordinator { + Coordinator(text: $text) + } + + class Coordinator: NSObject, UITextFieldDelegate { + @Binding var text: String + + init(text: Binding) { + _text = text + } + + func textFieldDidChangeSelection(_ textField: UITextField) { + text = textField.text ?? "" + } + } +} diff --git a/xcuitest-sample-projUITests/Extensions/XCUIApplication+Extensions.swift b/xcuitest-sample-projUITests/Extensions/XCUIApplication+Extensions.swift new file mode 100644 index 0000000..27f6f47 --- /dev/null +++ b/xcuitest-sample-projUITests/Extensions/XCUIApplication+Extensions.swift @@ -0,0 +1,14 @@ +// +// XCUIApplication+Extensions.swift +// xcuitest-sample-projUITests +// +// Created by Nikola Shabanov on 23.07.23. +// + +import XCTest + +extension XCUIApplication { + + /// Tap the zero coordinate of the app, useful for closing keyboards or triggering UIApplicationMonitor + func tapZero() { coordinate(withNormalizedOffset: .zero).tap() } +} diff --git a/xcuitest-sample-projUITests/Extensions/XCUIElementQuery+Extensions.swift b/xcuitest-sample-projUITests/Extensions/XCUIElementQuery+Extensions.swift index f898101..9864f13 100644 --- a/xcuitest-sample-projUITests/Extensions/XCUIElementQuery+Extensions.swift +++ b/xcuitest-sample-projUITests/Extensions/XCUIElementQuery+Extensions.swift @@ -15,22 +15,27 @@ extension XCUIElementQuery { var isEmpty: Bool { count == 0 } // MARK: Return element by predicate methods + /// Get element where the label exactly matches the provided string func element(withLabelMatching text: String) -> XCUIElement { return element(matching: NSPredicate(format: "label == %@", text)) } + /// Get element where the label contains the provided string func element(withLabelContaining text: String) -> XCUIElement { return element(matching: NSPredicate(format: "label CONTAINS %@", text)) } + /// Get element where the label ends with the provided string func element(withLabelSuffixed text: String) -> XCUIElement { return element(matching: NSPredicate(format: "label ENDSWITH %@", text)) } + /// Get element where the label begins with the provided string func element(withLabelPrefixed text: String) -> XCUIElement { return element(matching: NSPredicate(format: "label BEGINSWITH %@", text)) } + /// Get element where the label func element(withLabelLike text: String) -> XCUIElement { return element(matching: NSPredicate(format: "label LIKE %@", text)) } diff --git a/xcuitest-sample-projUITests/Foundations/BaseTest.swift b/xcuitest-sample-projUITests/Foundations/BaseTest.swift index 96cfc0c..20aaa9b 100644 --- a/xcuitest-sample-projUITests/Foundations/BaseTest.swift +++ b/xcuitest-sample-projUITests/Foundations/BaseTest.swift @@ -11,6 +11,7 @@ class BaseTest: XCTestCase { private let defaultLoadingTime = 30.0 static var isReflectionIdleEnabled = false private let app = XCUIApplication() + private let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") override func setUp() { // Fail-fast tests diff --git a/xcuitest-sample-projUITests/Helpers/ElementsHelper.swift b/xcuitest-sample-projUITests/Helpers/ElementsHelper.swift index 9b85859..aa40b7c 100644 --- a/xcuitest-sample-projUITests/Helpers/ElementsHelper.swift +++ b/xcuitest-sample-projUITests/Helpers/ElementsHelper.swift @@ -87,4 +87,8 @@ class ElementsHelper: XCTest { } XCTAssertTrue(result, "\(firstElement) is not in `\(relativePosition)` relative position to \(secondElement)") } + + class func waitForElement(_ element: XCUIElement, _ timeoutValue: Double) -> Bool { + return element.waitForExistence(timeout: timeoutValue) + } } diff --git a/xcuitest-sample-projUITests/Pages/FirstPage.swift b/xcuitest-sample-projUITests/Pages/FirstPage.swift index b9b46af..44fa8c4 100644 --- a/xcuitest-sample-projUITests/Pages/FirstPage.swift +++ b/xcuitest-sample-projUITests/Pages/FirstPage.swift @@ -11,6 +11,6 @@ class FirstPage: BasePage { var carousel: XCUIElement { app.scrollViews.matching(identifier: "carousel-view").firstMatch } var carouselItems: XCUIElementQuery { carousel.staticTexts } var disappearingButton: XCUIElement { app.buttons["disappearing-button"] } - + var textField: XCUIElement { app.textFields["text-field"] } } diff --git a/xcuitest-sample-projUITests/xcuitest_sample_projUITests.swift b/xcuitest-sample-projUITests/xcuitest_sample_projUITests.swift index 2a6b6fe..21a1838 100644 --- a/xcuitest-sample-projUITests/xcuitest_sample_projUITests.swift +++ b/xcuitest-sample-projUITests/xcuitest_sample_projUITests.swift @@ -57,4 +57,12 @@ final class xcuitest_sample_projUITests: BaseTest { // Wait 5 more seconds to have 6 elements, should fail because only 5 in total will be loaded ElementsHelper.waitUntilTableFilled(elements, 6, TestConstants.Timeout.short) } + + func testTypeText() { + let textToType = "typed-text" + firstPage.textField.tap() + firstPage.textField.typeText(textToType) + let typedText = firstPage.textField.textFromValue + XCTAssertEqual(textToType, typedText) + } }