From 9204580dc4a577b03a006fa8f526dc66e193967e Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Mon, 4 Apr 2022 20:15:35 +0300 Subject: [PATCH] Some UI testing code --- AutoCat2.xcodeproj/project.pbxproj | 38 +++++++++----- .../xcschemes/xcschememanagement.plist | 14 +++--- AutoCat2/Components/Extensions/UIView.swift | 5 ++ AutoCat2/Controllers/AuthController.swift | 16 +++--- AutoCat2UITests/AutoCat2UITests.swift | 34 +++++++------ .../Api/Lib/ApiMethodMockProtocol.swift | 0 .../Testing}/Api/Lib/MockURLProtocol.swift | 0 .../Testing}/Api/Mocks/ApiMethodMock.swift | 1 - .../Testing}/Api/Mocks/LoginMethodMock.swift | 5 +- .../Testing}/Api/Responses/login_success.json | 0 AutoCatCore/Testing/TestError.swift | 20 ++++++++ AutoCatCore/Testing/Testing.swift | 49 +++++++++++++++++++ AutoCatCore/Utils/Api.swift | 9 ++++ AutoCatCoreTests/ApiTests.swift | 2 +- 14 files changed, 148 insertions(+), 45 deletions(-) rename {AutoCatCoreTests => AutoCatCore/Testing}/Api/Lib/ApiMethodMockProtocol.swift (100%) rename {AutoCatCoreTests => AutoCatCore/Testing}/Api/Lib/MockURLProtocol.swift (100%) rename {AutoCatCoreTests => AutoCatCore/Testing}/Api/Mocks/ApiMethodMock.swift (98%) rename {AutoCatCoreTests => AutoCatCore/Testing}/Api/Mocks/LoginMethodMock.swift (84%) rename {AutoCatCoreTests => AutoCatCore/Testing}/Api/Responses/login_success.json (100%) create mode 100644 AutoCatCore/Testing/TestError.swift create mode 100644 AutoCatCore/Testing/Testing.swift diff --git a/AutoCat2.xcodeproj/project.pbxproj b/AutoCat2.xcodeproj/project.pbxproj index 33c7abb..1b5d0fb 100644 --- a/AutoCat2.xcodeproj/project.pbxproj +++ b/AutoCat2.xcodeproj/project.pbxproj @@ -31,6 +31,13 @@ 7A28283327E7263B0049BDBF /* UIStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A28283227E7263B0049BDBF /* UIStackView.swift */; }; 7A28283627E74C110049BDBF /* SwiftEntryKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7A28283527E74C110049BDBF /* SwiftEntryKit */; }; 7A28283827E74D930049BDBF /* CheckController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A28283727E74D930049BDBF /* CheckController.swift */; }; + 7A36E55C27FB55570025AACB /* Testing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A36E55B27FB55570025AACB /* Testing.swift */; }; + 7A36E55D27FB5A220025AACB /* ApiMethodMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AAA27FA3CCF001A18EE /* ApiMethodMock.swift */; }; + 7A36E55E27FB5A260025AACB /* LoginMethodMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AA927FA3CCF001A18EE /* LoginMethodMock.swift */; }; + 7A36E55F27FB5A2C0025AACB /* login_success.json in Resources */ = {isa = PBXBuildFile; fileRef = 7A558AAC27FA3CCF001A18EE /* login_success.json */; }; + 7A36E56027FB5A2F0025AACB /* MockURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AAE27FA3CCF001A18EE /* MockURLProtocol.swift */; }; + 7A36E56127FB5A330025AACB /* ApiMethodMockProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AAF27FA3CCF001A18EE /* ApiMethodMockProtocol.swift */; }; + 7A36E56327FB5BEB0025AACB /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A36E56227FB5BEB0025AACB /* TestError.swift */; }; 7A48B26727D9442A004D1A4B /* PKHUD in Frameworks */ = {isa = PBXBuildFile; productRef = 7A48B26627D9442A004D1A4B /* PKHUD */; }; 7A49F4A327D4061900AEAAE0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F4A227D4061900AEAAE0 /* AppDelegate.swift */; }; 7A49F4A527D4061900AEAAE0 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F4A427D4061900AEAAE0 /* SceneDelegate.swift */; }; @@ -62,11 +69,6 @@ 7A49F51527D40C6100AEAAE0 /* AutoCat2.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F51327D40C6100AEAAE0 /* AutoCat2.xcdatamodeld */; }; 7A558AB027FA3CCF001A18EE /* SettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AA527FA3CCF001A18EE /* SettingsTests.swift */; }; 7A558AB127FA3CCF001A18EE /* ApiTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AA627FA3CCF001A18EE /* ApiTests.swift */; }; - 7A558AB227FA3CCF001A18EE /* LoginMethodMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AA927FA3CCF001A18EE /* LoginMethodMock.swift */; }; - 7A558AB327FA3CCF001A18EE /* ApiMethodMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AAA27FA3CCF001A18EE /* ApiMethodMock.swift */; }; - 7A558AB427FA3CCF001A18EE /* login_success.json in Resources */ = {isa = PBXBuildFile; fileRef = 7A558AAC27FA3CCF001A18EE /* login_success.json */; }; - 7A558AB527FA3CCF001A18EE /* MockURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AAE27FA3CCF001A18EE /* MockURLProtocol.swift */; }; - 7A558AB627FA3CCF001A18EE /* ApiMethodMockProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AAF27FA3CCF001A18EE /* ApiMethodMockProtocol.swift */; }; 7A9F2AC327E71531006492A9 /* ACTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F2AC227E71531006492A9 /* ACTabBarController.swift */; }; 7AE32D6427F05F89004EF6E0 /* VehicleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE32D6327F05F89004EF6E0 /* VehicleCell.swift */; }; 7AE32D6627F063A1004EF6E0 /* UIEdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE32D6527F063A1004EF6E0 /* UIEdgeInsets.swift */; }; @@ -158,6 +160,8 @@ 7A28283027E721A70049BDBF /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; 7A28283227E7263B0049BDBF /* UIStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackView.swift; sourceTree = ""; }; 7A28283727E74D930049BDBF /* CheckController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckController.swift; sourceTree = ""; }; + 7A36E55B27FB55570025AACB /* Testing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Testing.swift; sourceTree = ""; }; + 7A36E56227FB5BEB0025AACB /* TestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = ""; }; 7A49F49F27D4061900AEAAE0 /* AutoCat2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AutoCat2.app; sourceTree = BUILT_PRODUCTS_DIR; }; 7A49F4A227D4061900AEAAE0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7A49F4A427D4061900AEAAE0 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -333,6 +337,16 @@ path = ACTabBar; sourceTree = ""; }; + 7A36E55A27FB54610025AACB /* Testing */ = { + isa = PBXGroup; + children = ( + 7A558AA727FA3CCF001A18EE /* Api */, + 7A36E55B27FB55570025AACB /* Testing.swift */, + 7A36E56227FB5BEB0025AACB /* TestError.swift */, + ); + path = Testing; + sourceTree = ""; + }; 7A49F49627D4061900AEAAE0 = { isa = PBXGroup; children = ( @@ -396,6 +410,7 @@ 7A49F4D827D4064500AEAAE0 /* AutoCatCore */ = { isa = PBXGroup; children = ( + 7A36E55A27FB54610025AACB /* Testing */, 7AE32D6F27F06D87004EF6E0 /* DataSource */, 7A49F51327D40C6100AEAAE0 /* AutoCat2.xcdatamodeld */, 7A49F50427D406CB00AEAAE0 /* Models */, @@ -411,7 +426,6 @@ 7A49F4E627D4064500AEAAE0 /* AutoCatCoreTests */ = { isa = PBXGroup; children = ( - 7A558AA727FA3CCF001A18EE /* Api */, 7A558AA627FA3CCF001A18EE /* ApiTests.swift */, 7A558AA527FA3CCF001A18EE /* SettingsTests.swift */, ); @@ -742,6 +756,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7A36E55F27FB5A2C0025AACB /* login_success.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -749,7 +764,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7A558AB427FA3CCF001A18EE /* login_success.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -820,12 +834,16 @@ 7A49F51227D406CB00AEAAE0 /* VName.swift in Sources */, 7A1D80E827F30399007BD64F /* VModel.swift in Sources */, 7A49F51127D406CB00AEAAE0 /* PlateNumber.swift in Sources */, + 7A36E55E27FB5A260025AACB /* LoginMethodMock.swift in Sources */, 7A49F50C27D406CB00AEAAE0 /* VBrand.swift in Sources */, + 7A36E55C27FB55570025AACB /* Testing.swift in Sources */, 7A49F51027D406CB00AEAAE0 /* Settings.swift in Sources */, 929EDE9027F8F76300E55F65 /* DebugInfo.swift in Sources */, + 7A36E55D27FB5A220025AACB /* ApiMethodMock.swift in Sources */, 929EDE8927F8E65500E55F65 /* VAd.swift in Sources */, 7A49F4F927D406B200AEAAE0 /* Constants.swift in Sources */, 929EDE8527F8CB0600E55F65 /* VEvent.swift in Sources */, + 7A36E56327FB5BEB0025AACB /* TestError.swift in Sources */, 7A49F50F27D406CB00AEAAE0 /* Response.swift in Sources */, 929EDE8127F8A75E00E55F65 /* VPhoto.swift in Sources */, 7A49F4FB27D406B200AEAAE0 /* Api.swift in Sources */, @@ -833,6 +851,8 @@ 929EDE7F27F89C3000E55F65 /* VEngine.swift in Sources */, 929EDE8727F8E11E00E55F65 /* VOsago.swift in Sources */, 7A49F50E27D406CB00AEAAE0 /* User.swift in Sources */, + 7A36E56127FB5A330025AACB /* ApiMethodMockProtocol.swift in Sources */, + 7A36E56027FB5A2F0025AACB /* MockURLProtocol.swift in Sources */, 7A49F4FA27D406B200AEAAE0 /* ApiError.swift in Sources */, 7AE32D7127F06DA4004EF6E0 /* DateSection.swift in Sources */, ); @@ -842,12 +862,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7A558AB227FA3CCF001A18EE /* LoginMethodMock.swift in Sources */, 7A558AB027FA3CCF001A18EE /* SettingsTests.swift in Sources */, 7A558AB127FA3CCF001A18EE /* ApiTests.swift in Sources */, - 7A558AB627FA3CCF001A18EE /* ApiMethodMockProtocol.swift in Sources */, - 7A558AB527FA3CCF001A18EE /* MockURLProtocol.swift in Sources */, - 7A558AB327FA3CCF001A18EE /* ApiMethodMock.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/AutoCat2.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist b/AutoCat2.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist index b1921f4..6e1ef2e 100644 --- a/AutoCat2.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/AutoCat2.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist @@ -17,49 +17,49 @@ AutoCatCore.xcscheme_^#shared#^_ orderHint - 0 + 7 DifferenceKit (Playground) 1.xcscheme isShown orderHint - 3 + 2 DifferenceKit (Playground) 2.xcscheme isShown orderHint - 4 + 3 DifferenceKit (Playground).xcscheme isShown orderHint - 2 + 1 SwiftDate (Playground) 1.xcscheme isShown orderHint - 4 + 5 SwiftDate (Playground) 2.xcscheme isShown orderHint - 5 + 6 SwiftDate (Playground).xcscheme isShown orderHint - 3 + 4 SuppressBuildableAutocreation diff --git a/AutoCat2/Components/Extensions/UIView.swift b/AutoCat2/Components/Extensions/UIView.swift index e02ae0c..7c24533 100644 --- a/AutoCat2/Components/Extensions/UIView.swift +++ b/AutoCat2/Components/Extensions/UIView.swift @@ -22,4 +22,9 @@ extension UIView { bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -insets.bottom) ]) } + + func accessibilityId(_ id: String) -> Self { + accessibilityIdentifier = id + return self + } } diff --git a/AutoCat2/Controllers/AuthController.swift b/AutoCat2/Controllers/AuthController.swift index 918862e..92a7ba2 100644 --- a/AutoCat2/Controllers/AuthController.swift +++ b/AutoCat2/Controllers/AuthController.swift @@ -9,18 +9,22 @@ import AutoCatCore class AuthController: UIViewController { private lazy var emailField = ACTextField(placeholder: "Email") - .onTextChanged(textChanged) - .keyboardType(.emailAddress) + .onTextChanged(textChanged) + .keyboardType(.emailAddress) + .accessibilityId("emailTextField") private lazy var passwordField = ACTextField(placeholder: "Password") - .onTextChanged(textChanged) - .secure(true) + .onTextChanged(textChanged) + .secure(true) + .accessibilityId("passwordTextField") private lazy var loginButton = ACButton(title: "Log in", onTapAsync: loginTapped) - .enable(false) + .accessibilityId("loginButton") + .enable(false) private lazy var signupButton = ACButton(title: "Sign up", onTapAsync: signupTapped) - .enable(false) + .accessibilityId("signupButton") + .enable(false) private lazy var stackView: UIStackView = { let stack = UIStackView(arrangedSubviews: [ diff --git a/AutoCat2UITests/AutoCat2UITests.swift b/AutoCat2UITests/AutoCat2UITests.swift index 4b40741..4176a50 100644 --- a/AutoCat2UITests/AutoCat2UITests.swift +++ b/AutoCat2UITests/AutoCat2UITests.swift @@ -6,16 +6,16 @@ // import XCTest +import AutoCatCore class AutoCat2UITests: XCTestCase { + + private let testLogin = "test@gmail.com" + private let testPassword = "12345" override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - // In UI tests it is usually best to stop immediately when a failure occurs. continueAfterFailure = false - - // In UI tests it’s 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. } override func tearDownWithError() throws { @@ -23,20 +23,22 @@ class AutoCat2UITests: XCTestCase { } func testExample() throws { - // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launchArguments += ["UI-TESTING"] + app.launchEnvironment["testKind"] = Testing.TestKind.addNumberSuccess.rawValue app.launch() - - // Use recording to get started writing UI tests. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - 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() - } + + let loginButton = app.buttons["loginButton"] + guard loginButton.waitForExistence(timeout: 5) else { + XCTFail("Login button not found") + return } + + app.textFields["emailTextField"].tap() + app.textFields["emailTextField"].typeText(testLogin) + app.secureTextFields["passwordTextField"].tap() + app.secureTextFields["passwordTextField"].typeText(testPassword) + loginButton.tap() } } diff --git a/AutoCatCoreTests/Api/Lib/ApiMethodMockProtocol.swift b/AutoCatCore/Testing/Api/Lib/ApiMethodMockProtocol.swift similarity index 100% rename from AutoCatCoreTests/Api/Lib/ApiMethodMockProtocol.swift rename to AutoCatCore/Testing/Api/Lib/ApiMethodMockProtocol.swift diff --git a/AutoCatCoreTests/Api/Lib/MockURLProtocol.swift b/AutoCatCore/Testing/Api/Lib/MockURLProtocol.swift similarity index 100% rename from AutoCatCoreTests/Api/Lib/MockURLProtocol.swift rename to AutoCatCore/Testing/Api/Lib/MockURLProtocol.swift diff --git a/AutoCatCoreTests/Api/Mocks/ApiMethodMock.swift b/AutoCatCore/Testing/Api/Mocks/ApiMethodMock.swift similarity index 98% rename from AutoCatCoreTests/Api/Mocks/ApiMethodMock.swift rename to AutoCatCore/Testing/Api/Mocks/ApiMethodMock.swift index f6d3016..2d4a02b 100644 --- a/AutoCatCoreTests/Api/Mocks/ApiMethodMock.swift +++ b/AutoCatCore/Testing/Api/Mocks/ApiMethodMock.swift @@ -1,5 +1,4 @@ import Foundation -import AutoCatCore open class ApiMethodMock: ApiMethodMockProtocol { diff --git a/AutoCatCoreTests/Api/Mocks/LoginMethodMock.swift b/AutoCatCore/Testing/Api/Mocks/LoginMethodMock.swift similarity index 84% rename from AutoCatCoreTests/Api/Mocks/LoginMethodMock.swift rename to AutoCatCore/Testing/Api/Mocks/LoginMethodMock.swift index 0f00711..d40392f 100644 --- a/AutoCatCoreTests/Api/Mocks/LoginMethodMock.swift +++ b/AutoCatCore/Testing/Api/Mocks/LoginMethodMock.swift @@ -1,14 +1,13 @@ import Foundation -import AutoCatCore class LoginMethodMock: ApiMethodMock { private var login: String private var password: String - init(httpMethod: String, path: String, login: String, password: String) { + init(login: String, password: String) { self.login = login self.password = password - super.init(httpMethod: httpMethod, path: path) + super.init(httpMethod: "POST", path: "user/login") } override func response(headers: [String : String], params: [String : Any]) -> (status: Int, data: Data?) { diff --git a/AutoCatCoreTests/Api/Responses/login_success.json b/AutoCatCore/Testing/Api/Responses/login_success.json similarity index 100% rename from AutoCatCoreTests/Api/Responses/login_success.json rename to AutoCatCore/Testing/Api/Responses/login_success.json diff --git a/AutoCatCore/Testing/TestError.swift b/AutoCatCore/Testing/TestError.swift new file mode 100644 index 0000000..33c4e14 --- /dev/null +++ b/AutoCatCore/Testing/TestError.swift @@ -0,0 +1,20 @@ +// +// TestError.swift +// AutoCatCore +// +// Created by Selim Mustafaev on 04.04.2022. +// + +import Foundation + +enum TestError: LocalizedError { + + case testKindNotFound + + public var errorDescription: String? { + switch self { + case .testKindNotFound: + return "Test kind not found" + } + } +} diff --git a/AutoCatCore/Testing/Testing.swift b/AutoCatCore/Testing/Testing.swift new file mode 100644 index 0000000..3159613 --- /dev/null +++ b/AutoCatCore/Testing/Testing.swift @@ -0,0 +1,49 @@ +// +// Testing.swift +// AutoCatCore +// +// Created by Selim Mustafaev on 04.04.2022. +// + +import Foundation + +public struct Testing { + + typealias ApiMockMap = [TestKind: [ApiMethodMockProtocol]] + + public static let testEmail = "test@gmail.com" + public static let testPassword = "12345" + + public enum TestKind: String { + + case addNumberSuccess + } + + public static var isUITesting: Bool { + ProcessInfo.processInfo.arguments.contains("UI-TESTING") + } + + public static var testUrlSession: URLSession? { + guard let kindStr = ProcessInfo.processInfo.environment["testKind"], + let kind = TestKind(rawValue: kindStr) else { + return nil + } + + MockURLProtocol.baseUrl = Constants.baseUrl + MockURLProtocol.apiMethodMocks = apiMockSets[kind] ?? [] + + let sessionConfig = URLSessionConfiguration.default + sessionConfig.protocolClasses = [MockURLProtocol.self] + return URLSession(configuration: sessionConfig) + } + + private static let apiMockSets: ApiMockMap = { + var map = ApiMockMap() + + map[.addNumberSuccess] = [ + LoginMethodMock(login: testEmail, password: testPassword) + ] + + return map + }() +} diff --git a/AutoCatCore/Utils/Api.swift b/AutoCatCore/Utils/Api.swift index 3042f79..e87bb0a 100644 --- a/AutoCatCore/Utils/Api.swift +++ b/AutoCatCore/Utils/Api.swift @@ -18,6 +18,15 @@ public class Api: ApiProtocol { self.settings = settings + if Testing.isUITesting { + if let testSession = Testing.testUrlSession { + self.session = testSession + } else { + fatalError("Error creating test session") + } + return + } + if let session = session { self.session = session } else { diff --git a/AutoCatCoreTests/ApiTests.swift b/AutoCatCoreTests/ApiTests.swift index be3185b..85587d5 100644 --- a/AutoCatCoreTests/ApiTests.swift +++ b/AutoCatCoreTests/ApiTests.swift @@ -11,7 +11,7 @@ class ApiTests: XCTestCase { override func setUpWithError() throws { MockURLProtocol.baseUrl = Constants.baseUrl MockURLProtocol.apiMethodMocks = [ - LoginMethodMock(httpMethod: "POST", path: "user/login", login: self.testLogin, password: self.testPassword) + LoginMethodMock(login: self.testLogin, password: self.testPassword) ] let sessionConfig = URLSessionConfiguration.default