diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index 2c2d75243..7557c7556 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -1070,7 +1070,6 @@ isa = PBXGroup; children = ( D2F18469244DB8F6000CC5D1 /* SnapshotHelper.swift */, - D2F18467244B0F35000CC5D1 /* AppTestHelper.swift */, 9FDF3651235A1EDE00614596 /* XCUIApplicationBuilder.swift */, 9FDF364F235A1D3F00614596 /* UITestHelper.swift */, 9F228BA523C673AD005D2CB6 /* Springboard.swift */, @@ -1417,6 +1416,7 @@ 9FDF364C235A1CCD00614596 /* SignInImapTest.swift */, D2F18463244B0C63000CC5D1 /* SignInGoogleTest.swift */, D216D05D242D32EA0083EDD6 /* TestCredentials.swift */, + D2F18467244B0F35000CC5D1 /* AppTestHelper.swift */, 9FDF3645235A1B0100614596 /* Info.plist */, 9F2F212A26B305420044E144 /* test-ci-secrets.json */, 9F228BA723C673C2005D2CB6 /* Resources */, diff --git a/FlowCrypt.xcodeproj/xcshareddata/xcschemes/FlowCryptUITests.xcscheme b/FlowCrypt.xcodeproj/xcshareddata/xcschemes/FlowCryptUITests.xcscheme index b1302a697..0f443212d 100644 --- a/FlowCrypt.xcodeproj/xcshareddata/xcschemes/FlowCryptUITests.xcscheme +++ b/FlowCrypt.xcodeproj/xcshareddata/xcschemes/FlowCryptUITests.xcscheme @@ -44,6 +44,9 @@ reference = "container:FlowCryptUITests/Gmail.xctestplan" default = "YES"> + + [AttContext] in + let attachmentContext = messageContexts.flatMap { msgContext -> [AttachmentContext] in guard let parts = msgContext.msg.attachments() as? [MCOIMAPPart] else { assertionFailure(); return [] } - return parts.map { part in AttContext(path: msgContext.path, msg: msgContext.msg, part: part) } + return parts.map { part in AttachmentContext(path: msgContext.path, msg: msgContext.msg, part: part) } } - guard attContext.isNotEmpty else { - throw BackupError.missedAttributes + // in case there are no attachments return empty data + guard attachmentContext.isNotEmpty else { + return Data() } - let dataArr = try attContext.map { attContext -> Data in - try awaitPromise(self.fetchMsgAttribute( + let dataArr = try attachmentContext.map { attContext -> Data in + try awaitPromise(self.fetchMsgAttachment( in: attContext.path, msgUid: attContext.msg.uid, part: attContext.part @@ -77,7 +80,7 @@ extension Imap: BackupProvider { } } - private func fetchMsgAttribute(in folder: String, msgUid: UInt32, part: MCOIMAPPart) -> Promise { + private func fetchMsgAttachment(in folder: String, msgUid: UInt32, part: MCOIMAPPart) -> Promise { Promise { [weak self] resolve, reject in guard let self = self else { return reject(AppErr.nilSelf) } self.imapSess? @@ -88,7 +91,7 @@ extension Imap: BackupProvider { encoding: part.encoding ) .start(self.finalize("fetchMsgAtt", resolve, reject, retry: { - self.fetchMsgAttribute(in: folder, msgUid: msgUid, part: part) + self.fetchMsgAttachment(in: folder, msgUid: msgUid, part: part) })) } } @@ -122,7 +125,7 @@ private struct MsgContext { let msg: MCOIMAPMessage } -private struct AttContext { +private struct AttachmentContext { let path: String let msg: MCOIMAPMessage let part: MCOIMAPPart diff --git a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+session.swift b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+session.swift index 8f75d74cb..cf5814959 100644 --- a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+session.swift +++ b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+session.swift @@ -17,6 +17,7 @@ extension Imap { let imapSession = dataService.imapSession(), let smtpSession = dataService.smtpSession() else { return } + logger.logInfo("Create new imap and smtp session") createNewConnection( imapSession: imapSession, diff --git a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/MessageLabel.swift b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/MessageLabel.swift index 6d803dc8c..5b4b83bdc 100644 --- a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/MessageLabel.swift +++ b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/MessageLabel.swift @@ -83,7 +83,7 @@ extension MessageLabelType { case .label("submited"): return MCOMessageFlag.submitted.rawValue case .none: return 0 default: - assertionFailure("This label \(self) is not supported by this provider") + debugPrint("This label \(self) is not supported by this provider") return 0 } } diff --git a/FlowCryptUI/Cell Nodes/ButtonCellNode.swift b/FlowCryptUI/Cell Nodes/ButtonCellNode.swift index 346bceba1..beafd7a64 100644 --- a/FlowCryptUI/Cell Nodes/ButtonCellNode.swift +++ b/FlowCryptUI/Cell Nodes/ButtonCellNode.swift @@ -13,7 +13,7 @@ public final class ButtonCellNode: CellNode { let title: NSAttributedString let insets: UIEdgeInsets let color: UIColor? - + public init( title: NSAttributedString, insets: UIEdgeInsets, @@ -24,7 +24,7 @@ public final class ButtonCellNode: CellNode { self.color = color } } - + private var onTap: (() -> Void)? public lazy var button = ButtonNode { [weak self] in self?.onTap?() @@ -41,7 +41,7 @@ public final class ButtonCellNode: CellNode { .withAlphaComponent(alpha) } } - + public init(input: Input, action: (() -> Void)?) { onTap = action self.insets = input.insets diff --git a/FlowCryptUI/Cell Nodes/TextCellNode.swift b/FlowCryptUI/Cell Nodes/TextCellNode.swift index 40c752611..e8574c6fc 100644 --- a/FlowCryptUI/Cell Nodes/TextCellNode.swift +++ b/FlowCryptUI/Cell Nodes/TextCellNode.swift @@ -56,7 +56,7 @@ public final class TextCellNode: CellNode { ? [textNode, spinner] : [textNode] ) - + return spec } } diff --git a/FlowCryptUI/Nodes/AttachmentNode.swift b/FlowCryptUI/Nodes/AttachmentNode.swift index 2b99d4cc8..4492019c6 100644 --- a/FlowCryptUI/Nodes/AttachmentNode.swift +++ b/FlowCryptUI/Nodes/AttachmentNode.swift @@ -15,15 +15,15 @@ public final class AttachmentNode: CellNode { self.size = size } } - + private let titleNode = ASTextNode() private let subtitleNode = ASTextNode2() private let imageNode = ASImageNode() private let buttonNode = ASButtonNode() private let borderNode = ASDisplayNode() - + private var onDownloadTap: (() -> Void)? - + public init( input: Input, onDownloadTap: (() -> Void)? @@ -38,20 +38,19 @@ public final class AttachmentNode: CellNode { imageNode.tintColor = .gray buttonNode.tintColor = .gray - + imageNode.image = UIImage(named: "paperclip")?.tinted(.gray) buttonNode.setImage(UIImage(named: "download")?.tinted(.gray), for: .normal) titleNode.attributedText = input.name subtitleNode.attributedText = input.size - + buttonNode.addTarget(self, action: #selector(onDownloadButtonTap), forControlEvents: .touchUpInside) - } - + @objc private func onDownloadButtonTap() { onDownloadTap?() } - + public override func layoutSpecThatFits(_: ASSizeRange) -> ASLayoutSpec { let verticalStack = ASStackLayoutSpec.vertical() verticalStack.spacing = 3 @@ -59,7 +58,7 @@ public final class AttachmentNode: CellNode { verticalStack.style.flexGrow = 1.0 verticalStack.children = [titleNode, subtitleNode] - + let finalSpec = ASStackLayoutSpec( direction: .horizontal, spacing: 10, @@ -69,7 +68,7 @@ public final class AttachmentNode: CellNode { ) let borderInset = UIEdgeInsets.side(8) - + let resultSpec = ASInsetLayoutSpec( insets: UIEdgeInsets( top: 8 + borderInset.top, @@ -79,7 +78,7 @@ public final class AttachmentNode: CellNode { ), child: finalSpec ) - + return ASOverlayLayoutSpec( child: resultSpec, overlay: ASInsetLayoutSpec( diff --git a/FlowCryptUI/Nodes/TableNode.swift b/FlowCryptUI/Nodes/TableNode.swift index 9c46f56c9..51ec6894d 100644 --- a/FlowCryptUI/Nodes/TableNode.swift +++ b/FlowCryptUI/Nodes/TableNode.swift @@ -57,7 +57,7 @@ public extension UIViewController { width: tableNode.frame.size.width, height: max(height, 0) ) - + return size } } diff --git a/FlowCryptUI/Nodes/TextFieldNode.swift b/FlowCryptUI/Nodes/TextFieldNode.swift index c678e2896..2d1b99a71 100644 --- a/FlowCryptUI/Nodes/TextFieldNode.swift +++ b/FlowCryptUI/Nodes/TextFieldNode.swift @@ -121,7 +121,7 @@ public final class TextFieldNode: ASDisplayNode { private var textFiledAction: TextFieldAction? private var onToolbarDoneAction: (() -> Void)? - + public init(preferredHeight: CGFloat?, action: TextFieldAction? = nil, accessibilityIdentifier: String?) { super.init() addSubnode(node) diff --git a/FlowCryptUI/Views/NavigationBarActionButton.swift b/FlowCryptUI/Views/NavigationBarActionButton.swift index 6e8bdc959..5e67af80a 100644 --- a/FlowCryptUI/Views/NavigationBarActionButton.swift +++ b/FlowCryptUI/Views/NavigationBarActionButton.swift @@ -15,13 +15,14 @@ public final class NavigationBarActionButton: UIBarButtonItem { private var onAction: (() -> Void)? - public convenience init(_ image: UIImage?, action: (() -> Void)?) { + public convenience init(_ image: UIImage?, action: (() -> Void)?, accessibilityIdentifier: String? = nil) { self.init() onAction = action customView = LeftAlignedIconButton(type: .system).with { $0.setImage(image, for: .normal) $0.frame.size = Constants.buttonSize $0.addTarget(self, action: #selector(tap), for: .touchUpInside) + $0.accessibilityIdentifier = accessibilityIdentifier } } diff --git a/FlowCryptUI/Views/NavigationBarItemsView.swift b/FlowCryptUI/Views/NavigationBarItemsView.swift index 6e50300c4..d2bf89a77 100644 --- a/FlowCryptUI/Views/NavigationBarItemsView.swift +++ b/FlowCryptUI/Views/NavigationBarItemsView.swift @@ -56,7 +56,7 @@ public final class NavigationBarItemsView: UIBarButtonItem { required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } - + public override var isEnabled: Bool { didSet { customView?.alpha = isEnabled ? 1 : 0.5 diff --git a/FlowCryptUITests/Resources/AppTestHelper.swift b/FlowCryptUITests/AppTestHelper.swift similarity index 86% rename from FlowCryptUITests/Resources/AppTestHelper.swift rename to FlowCryptUITests/AppTestHelper.swift index ce5852c73..e5d749e4d 100644 --- a/FlowCryptUITests/Resources/AppTestHelper.swift +++ b/FlowCryptUITests/AppTestHelper.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import XCTest import FlowCryptCommon +import XCTest protocol AppTest { var app: XCUIApplication! { get set } @@ -44,6 +44,14 @@ extension AppTest { var passPhraseTextField: XCUIElement { app.tables.secureTextFields["Enter your pass phrase"] } + + var navigationBackButton: XCUIElement { + app.navigationBars.buttons["arrow left c"] + } + + var setupUseAnotherAccount: XCUIElement { + app.tables.buttons["Use Another Account"] + } } // MARK: - Actions @@ -64,15 +72,12 @@ extension AppTest { } func tapOnCompose() { - let plusButton = app.buttons["tap"] + let plusButton = app.buttons["+"] if plusButton.exists { plusButton.tap() } else { - // for iPhone X - XCUIDevice.shared.orientation = .landscapeLeft - app.buttons["+"].tap() - XCUIDevice.shared.orientation = .portrait + logger.logError("+ does not exist") } wait(0.2) } @@ -95,21 +100,17 @@ extension AppTest { _ = app.keys["\n"] } } - + func logOutIfNeeded() { logger.logInfo("Log out if needed") - - // Check which screen we are now - guard menuButton.exists else { - logger.logInfo("Already logged out") - return - } - + if menuButton.exists { logger.logInfo("User is logged in. Try to log out") menuButton.tap() tapOnMenu(folder: "Log out") } else { + logger.logInfo("No menu button") + let otherAccountButton = app.tables.buttons["Use Another Account"] if otherAccountButton.exists { logger.logInfo("Try to use another account") diff --git a/FlowCryptUITests/Resources/UserCredentials.swift b/FlowCryptUITests/Resources/UserCredentials.swift index 0d04bd6e2..1ca2985ce 100644 --- a/FlowCryptUITests/Resources/UserCredentials.swift +++ b/FlowCryptUITests/Resources/UserCredentials.swift @@ -18,11 +18,23 @@ struct UserCredentials: Codable, Equatable { static var empty = UserCredentials(email: "", password: "", pass: "", recovery: "", privateKey: "") /// ci.tests.gmail@flowcrypt.dev + /// default Gmail account static var gmailDev: UserCredentials = .user(with: "ci.tests.gmail@flowcrypt.dev") - + /// default@flowcrypt.test - static var imapDev: UserCredentials = .user(with: "default@flowcrypt.test") - + /// default IMAP/SMTP account + static let imapDev = UserCredentials.user(with: "default@flowcrypt.test") + + /// den@flowcrypt.test + /// user without messages + static let imapDen = UserCredentials.user(with: "den@flowcrypt.test") + + /// has_msgs_no_backups@flowcrypt.test + /// user with messages but without any backups + static let imapHasMessagesNoBackups = UserCredentials.user(with: "has_msgs_no_backups@flowcrypt.test") +} + +extension UserCredentials { static func user(with email: String) -> UserCredentials { Credentials.default .users diff --git a/FlowCryptUITests/SignInGoogleTest.swift b/FlowCryptUITests/SignInGoogleTest.swift index 8887eb4ee..f99cbecf0 100644 --- a/FlowCryptUITests/SignInGoogleTest.swift +++ b/FlowCryptUITests/SignInGoogleTest.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import XCTest import FlowCryptCommon +import XCTest class SignInGoogleTest: XCTestCase, AppTest { var app: XCUIApplication! @@ -19,7 +19,7 @@ class SignInGoogleTest: XCTestCase, AppTest { .setupRegion() .build() .launched() - + logger.logInfo("Wait for launch") wait(10) } @@ -27,47 +27,47 @@ class SignInGoogleTest: XCTestCase, AppTest { private var gmailAlert: XCUIElement { Springboard.springboard.alerts.element } - + private var gmailLoginButton: XCUIElement { app.tables.buttons["Continue with Gmail"] } - + private var findTextFieldForGmailWebView: XCUIElement? { logger.logInfo("Try to find text field for gmail web view") return app.webViews.textFields.firstMatch } - + func test_1_successful_login() { logOutIfNeeded() wait(2) startGmailLoginFlow() wait(5) - + let user = UserCredentials.gmailDev - + enterUserCredentials(for: user) wait(5) enterUserDevCredentials(for: user) wait(5) } - + private func startGmailLoginFlow() { // Tap on Gmail login gmailLoginButton.tap() wait(5) - + // Wait for user alert and continue guard gmailAlert.exists else { assertionFailure("Gmail alert is missing") return } - + logger.logInfo("Gmail alert is on the screen") - + gmailAlert.buttons["Continue"].tap() wait(3) } - + private func enterUserCredentials(for user: UserCredentials) { // Try to find first text field in gmail web view logger.logInfo("Try to find text field for gmail web view") @@ -78,21 +78,21 @@ class SignInGoogleTest: XCTestCase, AppTest { } textField.tap() wait(0.2) - + textField.typeText(user.email) goKeyboardButton.tap() } - + private func enterUserDevCredentials(for user: UserCredentials) { let mainWebView = app.webViews.otherElements["main"] - + let userNameTextField = mainWebView.children(matching: .textField).element userNameTextField.tap() userNameTextField.typeText(user.email) - + app.toolbars.matching(identifier: "Toolbar").buttons["Next"].tap() app.typeText(user.password) - + wait(2) goKeyboardButton.tap() wait(3) diff --git a/FlowCryptUITests/SignInImapTest.swift b/FlowCryptUITests/SignInImapTest.swift index 11b8f8a64..adbd2829f 100644 --- a/FlowCryptUITests/SignInImapTest.swift +++ b/FlowCryptUITests/SignInImapTest.swift @@ -6,43 +6,313 @@ // Copyright © 2019 FlowCrypt Limited. All rights reserved. // -import XCTest import FlowCryptCommon +import XCTest /// make ui_tests_imap class SignInImapTest: XCTestCase, AppTest { var app: XCUIApplication! - + override func setUp() { continueAfterFailure = false - + logger.logInfo("Start App") - + app = XCUIApplicationBuilder() .setupRegion() .build() .addSnapshot() .launched() - + logger.logInfo("Wait for launch") wait(10) } -} -// MARK: - Tests -extension SignInImapTest { // login -> approve -> backups found -> enter pass phrase -> main flow func test_1_successful_login_imap() { let user = UserCredentials.imapDev loginWithImap(user) - + passPhraseTextField.tap() passPhraseTextField.typeText(user.pass) goKeyboardButton.tap() - + wait(4) XCTAssert(app.buttons["+"].exists) } + + // restart app -> loads inbox + func test_2_restart_load_inbox() { + let application = XCUIApplication() + wait(2) + application.buttons["+"].tap() + + wait(0.3) + + application.typeText("test@test.com") + application.tables.textFields["Subject"].tap() + wait(3) + application.tables.textFields["Subject"].tap() + + application.typeText("ios") + + snapshot("compose") + navigationBackButton.tap() + wait(1) + + tapOnCell() + snapshot("message") + navigationBackButton.tap() + + wait(1) + + menuButton.tap() + snapshot("menu") + + tapOnMenu(folder: "Settings") + snapshot("settings") + } + + // restart app -> loads inbox -> verify messages + func test_3_restart_contains_emails() { + let app = XCUIApplication() + + let tablesQuery = app.tables + let cellsQuery = tablesQuery.cells + + // open first message + cellsQuery.otherElements.containing(.staticText, identifier:"Jun 07").staticTexts["denbond7@flowcrypt.test"].tap() + navigationBackButton.tap() + + // message > 5mb + cellsQuery.otherElements.containing(.staticText, identifier:"...").staticTexts["denbond7@flowcrypt.test"].tap() + wait(0.5) + + cellsQuery.otherElements.containing(.staticText, identifier:"encrypted message with missing key error").staticTexts["denbond7@flowcrypt.test"].tap() + tablesQuery.textViews.textViews["Could not decrypt:"].tap() + navigationBackButton.tap() + + // open 3d message + cellsQuery.otherElements.containing(.staticText, identifier:"Simple encrypted message").staticTexts["denbond7@flowcrypt.test"].tap() + let textView = tablesQuery.children(matching: .cell) + .element(boundBy: 2) + .children(matching: .other) + .element + .children(matching: .other) + .element + .children(matching: .textView) + .element + textView.children(matching: .textView)["Simple encrypted text"].tap() + navigationBackButton.tap() + + // open 4th message + cellsQuery.otherElements.containing(.staticText, identifier:"Simple encrypted message + pub key").staticTexts["denbond7@flowcrypt.test"].tap() + textView.children(matching: .textView)["It's an encrypted message with my pub key"].tap() + navigationBackButton.tap() + + // open message with attachment + cellsQuery.otherElements.containing(.staticText, identifier:"Simple encrypted message + attachment").staticTexts["denbond7@flowcrypt.test"].tap() + tablesQuery.staticTexts["android.png.pgp"].tap() + wait(1) + cellsQuery.otherElements.containing(.staticText, identifier:"denbond7@flowcrypt.test").children(matching: .button).element.tap() + navigationBackButton.tap() + + tablesQuery.staticTexts["Simple encrypted message + attachment"].tap() + textView.children(matching: .textView)["It's an encrypted message with one encrypted attachment."].tap() + } + + // restart app -> search functionality + func test_4_restart_search() { + // search + let inboxNavigationBar = app.navigationBars["Inbox"] + let searchButton = inboxNavigationBar.buttons["search icn"] + let tablesQuery = app.tables + + searchButton.tap() + + let searchNavigationBar = app.navigationBars["Search"] + let searchTextField = searchNavigationBar.searchFields["Search"] + + searchTextField.tap() + wait(1) + searchTextField.typeText("search") + + // Verify result with "search" + let denBondMessage = tablesQuery.staticTexts["denbond7@flowcrypt.test"] + denBondMessage.tap() + // Search in subject + tablesQuery.staticTexts["Search"].tap() + let textView = tablesQuery.children(matching: .cell).element(boundBy: 2).children(matching: .other).element.children(matching: .other).element.children(matching: .textView).element + // body + textView.children(matching: .textView)["Search in the body"].tap() + // email in recipient + denBondMessage.tap() + // go back to search controller + navigationBackButton.tap() + searchTextField.tap() + + // clear previous result + let clearTextButton = searchTextField.buttons["Clear text"] + clearTextButton.tap() + + // ESPRESSO + searchTextField.tap() + searchTextField.typeText("espresso") + + let espresso = tablesQuery.staticTexts["'espresso' in a subject"] + espresso.tap() + espresso.tap() + textView.children(matching: .textView)["Some text"].tap() + navigationBackButton.tap() + + let cellsQuery = tablesQuery.cells + cellsQuery.otherElements.containing(.staticText, identifier:"Message").staticTexts["denbond7@flowcrypt.test"].tap() + tablesQuery.staticTexts["Message"].tap() + textView.children(matching: .textView)["The message with 'espresso' in a body"].tap() + navigationBackButton.tap() + clearTextButton.tap() + + // ANDROID + searchTextField.tap() + searchTextField.typeText("android") + cellsQuery.otherElements.containing(.staticText, identifier:"Standard message + one attachment").staticTexts["denbond7@flowcrypt.test"].tap() + navigationBackButton.tap() + cellsQuery.otherElements.containing(.staticText, identifier:"With android in subject").staticTexts["denbond7@flowcrypt.test"].tap() + navigationBackButton.tap() + cellsQuery.otherElements.containing(.staticText, identifier:"with that text in body").staticTexts["denbond7@flowcrypt.test"].tap() + navigationBackButton.tap() + cellsQuery.otherElements.containing(.staticText, identifier:"Honor reply-to address").staticTexts["denbond7@flowcrypt.test"].tap() + navigationBackButton.tap() + cellsQuery.otherElements.containing(.staticText, identifier:"Simple encrypted message + attachment").staticTexts["denbond7@flowcrypt.test"].tap() + navigationBackButton.tap() + app.children(matching: .window).element(boundBy: 0).children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element(boundBy: 0).children(matching: .other).element.children(matching: .table).element.tap() + navigationBackButton.tap() + } + + func test_5_restart_folders() { + let app = XCUIApplication() + let tablesQuery = app.tables + + let menuButton = app.navigationBars["Inbox"].buttons["menu icn"] + menuButton.tap() + wait(1) + tablesQuery.cells.otherElements.containing(.staticText, identifier:"...").staticTexts["denbond7@flowcrypt.test"].tap() + menuButton.tap() + + tablesQuery.staticTexts["Junk"].tap() + tablesQuery.staticTexts["Junk is empty"].tap() + app.navigationBars["Junk"].buttons["menu icn"].tap() + + tablesQuery.staticTexts["Drafts"].tap() + tablesQuery.staticTexts["Drafts is empty"].tap() + app.navigationBars["Drafts"].buttons["menu icn"].tap() + + tablesQuery.staticTexts["Trash"].tap() + tablesQuery.staticTexts["Standard message - plaintext"].tap() + navigationBackButton.tap() + app.navigationBars["Trash"].buttons["menu icn"].tap() + + tablesQuery.staticTexts["Sent"].tap() + tablesQuery.staticTexts["Sent is empty"].tap() + app.navigationBars["Sent"].buttons["menu icn"].tap() + + tablesQuery.staticTexts["Inbox"].tap() + menuButton.tap() + } + + func test_6_restart_settings() { + let app = XCUIApplication() + let tablesQuery = app.tables + + menuButton.tap() + wait(1) + + tablesQuery.staticTexts["Settings"].tap() + tablesQuery.staticTexts["Backups"].tap() + tablesQuery.staticTexts["Security and Privacy"].tap() + tablesQuery.staticTexts["Contacts"].tap() + + app.navigationBars["Contacts"].staticTexts["Contacts"].tap() + navigationBackButton.tap() + + tablesQuery.staticTexts["Keys"].tap() + wait(1) + tablesQuery.cells.firstMatch.tap() + tablesQuery.buttons["Show public key"].tap() + + // part of public key + let searchText = "nxjMEYIq7phYJKwYBBAHaRw8BAQdAat45rrh" + let predicate = NSPredicate(format: "label CONTAINS[c] %@", searchText) + tablesQuery.containing(predicate) + + navigationBackButton.tap() + + tablesQuery.buttons["Show key details"].tap() + tablesQuery.staticTexts["Longid: 225F8023C20D0957"].tap() + tablesQuery.staticTexts["Longid: 4F1458BD22B7BB53"].tap() + + navigationBackButton.tap() + tablesQuery.buttons["Copy to clipboard"].tap() + tablesQuery.buttons["Share"].tap() + app.navigationBars["UIActivityContentView"].buttons["Close"].tap() + tablesQuery.buttons["Show private key"].tap() + + navigationBackButton.tap() + + app.navigationBars["Keys"].buttons["Add"].tap() + XCTAssert(tablesQuery.buttons["Load From File"].exists) + navigationBackButton.tap() + navigationBackButton.tap() + + tablesQuery.staticTexts["Notifications"].tap() + tablesQuery.staticTexts["Legal"].tap() + + let collectionViewsQuery = app.collectionViews + collectionViewsQuery.staticTexts["Terms"].tap() + collectionViewsQuery.staticTexts["License"].tap() + collectionViewsQuery.staticTexts["Sources"].tap() + app.navigationBars["Legal"].buttons["arrow left c"].tap() + tablesQuery.staticTexts["Experimental"].tap() + } + + // login -> cancel + func test_7_login_cancel() { + let user = UserCredentials.imapDev + loginWithImap(user) + + passPhraseTextField.swipeUp() + tapUseAnotherAccountAndVerify() + } + + // login with user without key backups and emails + // login -> no messages + func test_8_login_no_messages() { + verifyFlowWithNoBackups(for: .imapDen) + } + + func test_9_has_msgs_no_backups() { + verifyFlowWithNoBackups(for: .imapHasMessagesNoBackups) + } + + // login with wrong pass phrase + func test_10_login_bad_pass_phrase() { + let user = UserCredentials.imapDev + loginWithImap(user) + + let tablesQuery = app.tables + XCTAssert(tablesQuery.staticTexts["Remember pass phrase temporarily"].exists) + + passPhraseTextField.typeText(user.pass + "wrong") + tapOnGoButton() + wait(2) + + let errorAlert = app.alerts["Error"] + XCTAssert(errorAlert.exists, "Error alert is missing after entering wrong pass phrase") + XCTAssert(errorAlert.scrollViews.otherElements.staticTexts["Wrong pass phrase, please try again"].exists) + errorAlert.scrollViews.otherElements.buttons["OK"].tap() + wait(0.2) + app.tables.buttons["Use Another Account"].tap() + } } // MARK: - Convenience @@ -50,78 +320,102 @@ extension SignInImapTest { private var toolbarDoneButton: XCUIElement { app.toolbars["Toolbar"].buttons["Done"] } - + private func loginWithImap(_ user: UserCredentials) { logger.logInfo("Login with \(user.email)") - + // other account logOutIfNeeded() wait(0.3) - + logger.logInfo("Use other email provider") let otherEmailButton = app.tables.buttons["Other email provider"] otherEmailButton.tap() - + logger.logInfo("Fill all user credentials") - + // email let emailTextField = app.tables.textFields["Email"] emailTextField.tap() emailTextField.typeText(user.email) wait(1) - + // move focus to username goKeyboardButton.tap() wait(1) - + // move focus to password goKeyboardButton.tap() app.typeText(user.password) - + // move focus to imap server (filled) goKeyboardButton.tap() - + // move focus to imap port goKeyboardButton.tap() - + // move focus to imap security type. Set none toolbarDoneButton.tap() app.pickerWheels["none"].tap() toolbarDoneButton.tap() - + // move to smtp type let smtpType = app.tables.textFields["SMTP type"] smtpType.tap() app.pickerWheels["none"].tap() toolbarDoneButton.tap() - + app.tables.buttons["Connect"].tap() - + logger.logInfo("Try to connect") wait(10) } + + private func verifyFlowWithNoBackups(for user: UserCredentials) { + loginWithImap(user) + + let tablesQuery = app.tables + + let noBackupsLabel = tablesQuery.staticTexts["No backups found on account: \n\(user.email)"] + let importMyKeyButton = tablesQuery.buttons["Import my key"] + let createNewKeyButton = tablesQuery.buttons["Create a new key"] + + XCTAssert(noBackupsLabel.exists) + XCTAssert(importMyKeyButton.exists) + XCTAssert(createNewKeyButton.exists) + XCTAssert(setupUseAnotherAccount.exists) + + importMyKeyButton.tap() + navigationBackButton.tap() + + createNewKeyButton.tap() + navigationBackButton.tap() + + importMyKeyButton.tap() + + let loadFromFileButton = tablesQuery.buttons["Load From File"] + XCTAssert(loadFromFileButton.exists) + + let loadFromClipboard = tablesQuery.buttons["Load From Clipboard"] + XCTAssert(loadFromClipboard.exists) + navigationBackButton.tap() + + XCTAssert(noBackupsLabel.exists) + + tapUseAnotherAccountAndVerify() + } + + private func tapUseAnotherAccountAndVerify() { + setupUseAnotherAccount.tap() + + wait(1) + XCTAssert(app.tables.buttons["Other email provider"].exists) + } } -//extension SignInImapTest { -// // log in -> approve -> no backups -> switch email -// func test_1_login_no_backups() { -// // login with user without key backup -// -// login(UserCredentials.noKeyBackUp) -// -// // retry -// let buttons = app.alerts.scrollViews.otherElements.buttons -// buttons["Retry"].tap() -// wait(1) -// -// // switch to a new account -// buttons["Use other account"].tap() -// wait(2) -// -// // login -// test_6_login_good_pass() -// } -// +// Currently disabled tests +// UI tests which can make changes on remote server are currently disabled + // func test_2_login_no_backups_generate() { // // log in -> approve -> no backups -> generate pubkey -> weak pass phrase // login(UserCredentials.noKeyBackUp) @@ -188,90 +482,7 @@ extension SignInImapTest { // } // } // -// // log in -> cancel for gmail -// func test_3_login_cancel_gmail() { -// logOutIfNeeded() -// snapshot("splash") -// -// app.tables.buttons["gmail"].tap() -// wait(1) -// -// snapshot("auth") -// } -// -// // log in -> cancel -// func test_4_login_cancel() { -// login(user) -// -// let useAnotherAccountButton = app.tables.buttons["Use Another Account"] -// useAnotherAccountButton.tap() -// -// wait(1) -// XCTAssert(app.tables.buttons["Other email provider"].exists) -// } -// -// // log in -> approve -> bad pass phrase -// func test_5_login_bad_pass() { -// login(user) -// -// passPhraseTextField.tap() -// passPhraseTextField.typeText(user.pass + "wrong") -// tapOnGoButton() -// -// wait(0.2) -// let errorAlert = app.alerts["Error"] -// XCTAssert(errorAlert.exists, "Error alert is missing after entering wrong pass phrase") -// errorAlert.scrollViews.otherElements.buttons["OK"].tap() -// wait(0.2) -// -// app.tables.buttons["Use Another Account"].tap() -// } -// -// // log in -> approve -> loaded 1 backup -> good pass phrase -> inbox -// func test_6_login_good_pass() { -// login(user) -// -// passPhraseTextField.tap() -// passPhraseTextField.typeText(user.pass) -// -// snapshot("recover") -// tapOnGoButton() -// -// wait(1) -// XCTAssert(app.navigationBars["Inbox"].exists, "Could not login") -// } -// -// // restart app -> loads inbox -// func test_7_restart_app_load_inbox() { -// wait(1) -// XCTAssert(app.navigationBars["Inbox"].exists, "Inbox is not found after restarting the app") -// snapshot("inbox") -// -// tapOnCompose() -// wait(0.3) -// -// app.typeText("ElonMusk@gmail.com") -// app.tables.textFields["Subject"].tap() -// -// app.tables.textFields["Subject"].tap() -// app.typeText("SpaceX") -// -// snapshot("compose") -// app.navigationBars.buttons["arrow left c"].tap() -// wait(1) -// -// tapOnCell() -// snapshot("message") -// app.navigationBars.buttons["arrow left c"].tap() -// -// wait(1) -// -// menuButton.tap() -// snapshot("menu") -// -// tapOnMenu(folder: "Settings") -// snapshot("settings") -// } + // // // send new msg -> inbox -> switch to sent -> open sent msg and verify content, recipient, subject // func test_8_send_message() { @@ -315,7 +526,7 @@ extension SignInImapTest { // // tapOnCell() // let buttons = app.navigationBars.buttons -// let backButton = buttons["arrow left c"] +// let backButton = navigationBackButton // // wait(3) // // Verify buttons in Trash folder @@ -358,7 +569,3 @@ extension SignInImapTest { // XCTAssert(errorAlert.exists) // } //} - -/* - log in -> approve -> no backups -> generate pubkey -> switch accounts - */ diff --git a/FlowCryptUITests/TestCredentials.swift b/FlowCryptUITests/TestCredentials.swift index 9a716b012..ed340114c 100644 --- a/FlowCryptUITests/TestCredentials.swift +++ b/FlowCryptUITests/TestCredentials.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import XCTest import FlowCryptCommon +import XCTest public let logger = Logger.nested("UI Tests") diff --git a/Scripts/format.sh b/Scripts/format.sh index 6c64824c0..70d38b478 100755 --- a/Scripts/format.sh +++ b/Scripts/format.sh @@ -13,7 +13,7 @@ fi if which swiftformat >/dev/null; then echo "Start formatting" # swiftlint autocorrect --path . - swiftformat "FlowCrypt" \ + swiftformat "FlowCrypt", "FlowCryptUITests" \ --rules trailingSpace \ --rules blankLinesAtEndOfScope \ --rules consecutiveBlankLines \