From cfd06e2066fca0f734d43fc6a2d8eb99cc01c57b Mon Sep 17 00:00:00 2001 From: Philippe Weidmann Date: Fri, 31 Jan 2025 15:01:20 +0100 Subject: [PATCH 1/3] ci: Add more wait for existence delay --- MailUITests/MailUITests.swift | 60 ++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/MailUITests/MailUITests.swift b/MailUITests/MailUITests.swift index f5f4036262..6198c3f007 100644 --- a/MailUITests/MailUITests.swift +++ b/MailUITests/MailUITests.swift @@ -38,6 +38,12 @@ class MailUITests: XCTestCase { app.launch() } + func wait(delay: TimeInterval = 5) { + let delayExpectation = XCTestExpectation() + delayExpectation.isInverted = true + wait(for: [delayExpectation], timeout: delay) + } + func testLogin() throws { launchAppFromScratch() login() @@ -51,14 +57,6 @@ class MailUITests: XCTestCase { _ = app.webViews.firstMatch.waitForExistence(timeout: defaultTimeOut) } - func testNewMessage() throws { - launchAppFromScratch() - login() - - app.buttons[MailResourcesStrings.Localizable.buttonNewMessage].firstMatch.tap() - _ = app.webViews.firstMatch.waitForExistence(timeout: defaultTimeOut) - } - func testSendNewMessage() throws { launchAppFromScratch() login() @@ -76,18 +74,25 @@ class MailUITests: XCTestCase { app.navigationBars[MailResourcesStrings.Localizable.buttonNewMessage] .buttons[MailResourcesStrings.Localizable.buttonClose].firstMatch.tap() - _ = app.collectionViews.firstMatch.waitForExistence(timeout: defaultTimeOut) let deleteDraftButton = app.buttons[MailResourcesStrings.Localizable.actionDelete].firstMatch _ = deleteDraftButton.waitForExistence(timeout: defaultTimeOut) deleteDraftButton.tap() } + func tapMenuButton() { + let menuButton = app.navigationBars.firstMatch.buttons[MailResourcesStrings.Localizable.contentDescriptionButtonMenu] + .firstMatch + _ = menuButton.waitForExistence(timeout: defaultTimeOut) + menuButton.tap() + } + func testSwitchFolder() { launchAppFromScratch() login() - app.navigationBars.firstMatch.buttons[MailResourcesStrings.Localizable.contentDescriptionButtonMenu].firstMatch.tap() + tapMenuButton() + app.scrollViews.otherElements.staticTexts[MailResourcesStrings.Localizable.archiveFolder].firstMatch.tap() } @@ -95,7 +100,8 @@ class MailUITests: XCTestCase { launchAppFromScratch() login() - app.navigationBars.firstMatch.buttons[MailResourcesStrings.Localizable.contentDescriptionButtonMenu].tap() + tapMenuButton() + let newFolderButton = app.scrollViews.otherElements.buttons[MailResourcesStrings.Localizable.newFolderDialogTitle] _ = newFolderButton.waitForExistence(timeout: defaultTimeOut) newFolderButton.tap() @@ -112,7 +118,9 @@ class MailUITests: XCTestCase { login() swipeFirstCell() - app.collectionViews.buttons[Action.delete.accessibilityIdentifier].firstMatch.tap() + let deleteButton = app.collectionViews.buttons[Action.delete.accessibilityIdentifier].firstMatch + _ = deleteButton.waitForExistence(timeout: defaultTimeOut) + deleteButton.tap() undo() } @@ -122,7 +130,9 @@ class MailUITests: XCTestCase { login() swipeFirstCell() - app.collectionViews.buttons[Action.delete.accessibilityIdentifier].firstMatch.tap() + let deleteButton = app.collectionViews.buttons[Action.delete.accessibilityIdentifier].firstMatch + _ = deleteButton.waitForExistence(timeout: defaultTimeOut) + deleteButton.tap() undo(ignoreUndoFailure: false) } @@ -132,7 +142,7 @@ class MailUITests: XCTestCase { login() swipeFirstCell() - app.collectionViews.buttons[MailResourcesStrings.Localizable.settingsSwipeActionQuickActionsMenu].tap() + tapSwipeActionQuickActionsMenu() app.buttons[MailResourcesStrings.Localizable.actionMove].tap() let moveFolderViewTitle = app.navigationBars.staticTexts[MailResourcesStrings.Localizable.actionMove] @@ -147,26 +157,33 @@ class MailUITests: XCTestCase { undo() } + func tapSwipeActionQuickActionsMenu() { + let quickActionsMenuButton = app.collectionViews + .buttons[MailResourcesStrings.Localizable.settingsSwipeActionQuickActionsMenu].firstMatch + _ = quickActionsMenuButton.waitForExistence(timeout: defaultTimeOut) + quickActionsMenuButton.tap() + } + func testMoreSwipeAction() { launchAppFromScratch() login() swipeFirstCell() - app.collectionViews.buttons[MailResourcesStrings.Localizable.settingsSwipeActionQuickActionsMenu].firstMatch.tap() + tapSwipeActionQuickActionsMenu() app.buttons[Action.delete.accessibilityIdentifier].tap() undo() swipeFirstCell() - app.collectionViews.buttons[MailResourcesStrings.Localizable.settingsSwipeActionQuickActionsMenu].firstMatch.tap() + tapSwipeActionQuickActionsMenu() app.buttons[Action.archive.accessibilityIdentifier].firstMatch.tap() undo() swipeFirstCell() - app.collectionViews.buttons[MailResourcesStrings.Localizable.settingsSwipeActionQuickActionsMenu].firstMatch.tap() + tapSwipeActionQuickActionsMenu() if app.buttons[Action.markAsRead.accessibilityIdentifier].firstMatch.exists { app.buttons[Action.markAsRead.accessibilityIdentifier].firstMatch.tap() } else { @@ -175,7 +192,7 @@ class MailUITests: XCTestCase { swipeFirstCell() - app.collectionViews.buttons[MailResourcesStrings.Localizable.settingsSwipeActionQuickActionsMenu].firstMatch.tap() + tapSwipeActionQuickActionsMenu() if app.buttons[MailResourcesStrings.Localizable.actionMarkAsUnread].firstMatch.exists { app.buttons[MailResourcesStrings.Localizable.actionMarkAsUnread].firstMatch.tap() } else { @@ -202,7 +219,10 @@ class MailUITests: XCTestCase { } func writeTestMessage() { - app.buttons[MailResourcesStrings.Localizable.buttonNewMessage].firstMatch.tap() + let newMessageButton = app.buttons[MailResourcesStrings.Localizable.buttonNewMessage].firstMatch + _ = newMessageButton.waitForExistence(timeout: defaultTimeOut) + newMessageButton.tap() + let composeBodyView = app.webViews.firstMatch _ = composeBodyView.waitForExistence(timeout: defaultTimeOut) @@ -216,6 +236,8 @@ class MailUITests: XCTestCase { composeBodyView.tap() composeBodyView.typeText(MailResourcesStrings.Localizable.aiPromptExample1) + + wait(delay: 5) } func login() { From 19bf82e18c2466af35257b4a25e4f72b6bf8a4fb Mon Sep 17 00:00:00 2001 From: Philippe Weidmann Date: Mon, 24 Feb 2025 08:16:53 +0100 Subject: [PATCH 2/3] test: Wait for app to stabilize after login --- MailUITests/MailUITests.swift | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/MailUITests/MailUITests.swift b/MailUITests/MailUITests.swift index 6198c3f007..508f4ebcc2 100644 --- a/MailUITests/MailUITests.swift +++ b/MailUITests/MailUITests.swift @@ -226,9 +226,16 @@ class MailUITests: XCTestCase { let composeBodyView = app.webViews.firstMatch _ = composeBodyView.waitForExistence(timeout: defaultTimeOut) - app.textFields.firstMatch.tap() - app.textFields.firstMatch.typeText(Env.testAccountEmail) - app.textFields.firstMatch.typeText("\n") + let laterButton = app.buttons[MailResourcesStrings.Localizable.buttonLater].firstMatch + if laterButton.waitForExistence(timeout: defaultTimeOut) { + laterButton.tap() + } + + let toTextField = app.textFields.firstMatch + _ = toTextField.waitForExistence(timeout: defaultTimeOut) + toTextField.tap() + toTextField.typeText(Env.testAccountEmail) + toTextField.typeText("\n") let subjectTextField = app.textFields[MailResourcesStrings.Localizable.subjectTitle].firstMatch subjectTextField.tap() @@ -237,7 +244,7 @@ class MailUITests: XCTestCase { composeBodyView.tap() composeBodyView.typeText(MailResourcesStrings.Localizable.aiPromptExample1) - wait(delay: 5) + wait(delay: 15) } func login() { @@ -260,6 +267,8 @@ class MailUITests: XCTestCase { passwordField.typeText(Env.testAccountPassword) passwordField.typeText("\n") + wait(delay: 15) + let nowText = MailResourcesStrings.Localizable .threadListHeaderLastUpdate(Date().formatted(.relative(presentation: .named))) let refreshText = app.staticTexts[nowText].firstMatch @@ -292,5 +301,7 @@ class MailUITests: XCTestCase { let refreshText = app.staticTexts[nowText].firstMatch _ = refreshText.waitForExistence(timeout: defaultTimeOut) } + + wait(delay: 15) } } From 2b1b24150e2144efedc02a6fbe32f921c5761f5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthieu=20D=C3=A9glon?= Date: Wed, 5 Mar 2025 07:52:34 +0100 Subject: [PATCH 3/3] ci: Test UI --- MailUITests/MailUITests.swift | 221 ++++++++++++++++++---------------- 1 file changed, 114 insertions(+), 107 deletions(-) diff --git a/MailUITests/MailUITests.swift b/MailUITests/MailUITests.swift index 508f4ebcc2..a060db3bfe 100644 --- a/MailUITests/MailUITests.swift +++ b/MailUITests/MailUITests.swift @@ -44,19 +44,19 @@ class MailUITests: XCTestCase { wait(for: [delayExpectation], timeout: delay) } - func testLogin() throws { - launchAppFromScratch() - login() - } - - func testDisplayThread() throws { - launchAppFromScratch() - login() - - app.collectionViews.staticTexts.element(boundBy: 1).firstMatch.tap() - _ = app.webViews.firstMatch.waitForExistence(timeout: defaultTimeOut) - } - +// func testLogin() throws { +// launchAppFromScratch() +// login() +// } + +// func testDisplayThread() throws { +// launchAppFromScratch() +// login() +// +// app.collectionViews.staticTexts.element(boundBy: 1).firstMatch.tap() +// _ = app.webViews.firstMatch.waitForExistence(timeout: defaultTimeOut) +// } +// func testSendNewMessage() throws { launchAppFromScratch() login() @@ -66,96 +66,96 @@ class MailUITests: XCTestCase { .firstMatch.tap() _ = app.collectionViews.firstMatch.waitForExistence(timeout: defaultTimeOut) } - - func testSaveMessage() throws { - launchAppFromScratch() - login() - writeTestMessage() - - app.navigationBars[MailResourcesStrings.Localizable.buttonNewMessage] - .buttons[MailResourcesStrings.Localizable.buttonClose].firstMatch.tap() - - let deleteDraftButton = app.buttons[MailResourcesStrings.Localizable.actionDelete].firstMatch - _ = deleteDraftButton.waitForExistence(timeout: defaultTimeOut) - deleteDraftButton.tap() - } - - func tapMenuButton() { - let menuButton = app.navigationBars.firstMatch.buttons[MailResourcesStrings.Localizable.contentDescriptionButtonMenu] - .firstMatch - _ = menuButton.waitForExistence(timeout: defaultTimeOut) - menuButton.tap() - } - - func testSwitchFolder() { - launchAppFromScratch() - login() - - tapMenuButton() - - app.scrollViews.otherElements.staticTexts[MailResourcesStrings.Localizable.archiveFolder].firstMatch.tap() - } - - func testCreateFolder() { - launchAppFromScratch() - login() - - tapMenuButton() - - let newFolderButton = app.scrollViews.otherElements.buttons[MailResourcesStrings.Localizable.newFolderDialogTitle] - _ = newFolderButton.waitForExistence(timeout: defaultTimeOut) - newFolderButton.tap() - - let folderNameTextField = app.textFields[MailResourcesStrings.Localizable.createFolderName] - _ = folderNameTextField.waitForExistence(timeout: defaultTimeOut) - folderNameTextField.tap() - folderNameTextField.typeText("Test-\(Date().timeIntervalSince1970)") - app.staticTexts[MailResourcesStrings.Localizable.buttonCreate].tap() - } - - func testDeleteSwipeAction() { - launchAppFromScratch() - login() - swipeFirstCell() - - let deleteButton = app.collectionViews.buttons[Action.delete.accessibilityIdentifier].firstMatch - _ = deleteButton.waitForExistence(timeout: defaultTimeOut) - deleteButton.tap() - - undo() - } - - func testUndoDeleteAction() { - launchAppFromScratch() - login() - swipeFirstCell() - - let deleteButton = app.collectionViews.buttons[Action.delete.accessibilityIdentifier].firstMatch - _ = deleteButton.waitForExistence(timeout: defaultTimeOut) - deleteButton.tap() - - undo(ignoreUndoFailure: false) - } - - func testMoveAction() { - launchAppFromScratch() - login() - swipeFirstCell() - - tapSwipeActionQuickActionsMenu() - app.buttons[MailResourcesStrings.Localizable.actionMove].tap() - - let moveFolderViewTitle = app.navigationBars.staticTexts[MailResourcesStrings.Localizable.actionMove] - _ = moveFolderViewTitle.waitForExistence(timeout: defaultTimeOut) - - // Because the burger menu is in a ZStack "trash" folder appears twice, that's why we use element bound by - app.scrollViews.containing( - .staticText, - identifier: MailResourcesStrings.Localizable.trashFolder - ).element(boundBy: 1).tap() - - undo() - } +// +// func testSaveMessage() throws { +// launchAppFromScratch() +// login() +// writeTestMessage() +// +// app.navigationBars[MailResourcesStrings.Localizable.buttonNewMessage] +// .buttons[MailResourcesStrings.Localizable.buttonClose].firstMatch.tap() +// +// let deleteDraftButton = app.buttons[MailResourcesStrings.Localizable.actionDelete].firstMatch +// _ = deleteDraftButton.waitForExistence(timeout: defaultTimeOut) +// deleteDraftButton.tap() +// } +// +// func tapMenuButton() { +// let menuButton = app.navigationBars.firstMatch.buttons[MailResourcesStrings.Localizable.contentDescriptionButtonMenu] +// .firstMatch +// _ = menuButton.waitForExistence(timeout: defaultTimeOut) +// menuButton.tap() +// } +// +// func testSwitchFolder() { +// launchAppFromScratch() +// login() +// +// tapMenuButton() +// +// app.scrollViews.otherElements.staticTexts[MailResourcesStrings.Localizable.archiveFolder].firstMatch.tap() +// } +// +// func testCreateFolder() { +// launchAppFromScratch() +// login() +// +// tapMenuButton() +// +// let newFolderButton = app.scrollViews.otherElements.buttons[MailResourcesStrings.Localizable.newFolderDialogTitle] +// _ = newFolderButton.waitForExistence(timeout: defaultTimeOut) +// newFolderButton.tap() +// +// let folderNameTextField = app.textFields[MailResourcesStrings.Localizable.createFolderName] +// _ = folderNameTextField.waitForExistence(timeout: defaultTimeOut) +// folderNameTextField.tap() +// folderNameTextField.typeText("Test-\(Date().timeIntervalSince1970)") +// app.staticTexts[MailResourcesStrings.Localizable.buttonCreate].tap() +// } +// +// func testDeleteSwipeAction() { +// launchAppFromScratch() +// login() +// swipeFirstCell() +// +// let deleteButton = app.collectionViews.buttons[Action.delete.accessibilityIdentifier].firstMatch +// _ = deleteButton.waitForExistence(timeout: defaultTimeOut) +// deleteButton.tap() +// +// undo() +// } +// +// func testUndoDeleteAction() { +// launchAppFromScratch() +// login() +// swipeFirstCell() +// +// let deleteButton = app.collectionViews.buttons[Action.delete.accessibilityIdentifier].firstMatch +// _ = deleteButton.waitForExistence(timeout: defaultTimeOut) +// deleteButton.tap() +// +// undo(ignoreUndoFailure: false) +// } +// +// func testMoveAction() { +// launchAppFromScratch() +// login() +// swipeFirstCell() +// +// tapSwipeActionQuickActionsMenu() +// app.buttons[MailResourcesStrings.Localizable.actionMove].tap() +// +// let moveFolderViewTitle = app.navigationBars.staticTexts[MailResourcesStrings.Localizable.actionMove] +// _ = moveFolderViewTitle.waitForExistence(timeout: defaultTimeOut) +// +// // Because the burger menu is in a ZStack "trash" folder appears twice, that's why we use element bound by +// app.scrollViews.containing( +// .staticText, +// identifier: MailResourcesStrings.Localizable.trashFolder +// ).element(boundBy: 1).tap() +// +// undo() +// } func tapSwipeActionQuickActionsMenu() { let quickActionsMenuButton = app.collectionViews @@ -167,6 +167,7 @@ class MailUITests: XCTestCase { func testMoreSwipeAction() { launchAppFromScratch() login() + print(app.debugDescription) swipeFirstCell() tapSwipeActionQuickActionsMenu() @@ -214,7 +215,8 @@ class MailUITests: XCTestCase { func swipeFirstCell() { // First cell could be the loading indicator so we get the second one let testMailCell = app.collectionViews.containing(.button, identifier: "ThreadListCell").firstMatch - _ = testMailCell.waitForExistence(timeout: defaultTimeOut) + _ = testMailCell.exists + testMailCell.swipeLeft() } @@ -244,7 +246,6 @@ class MailUITests: XCTestCase { composeBodyView.tap() composeBodyView.typeText(MailResourcesStrings.Localizable.aiPromptExample1) - wait(delay: 15) } func login() { @@ -267,7 +268,6 @@ class MailUITests: XCTestCase { passwordField.typeText(Env.testAccountPassword) passwordField.typeText("\n") - wait(delay: 15) let nowText = MailResourcesStrings.Localizable .threadListHeaderLastUpdate(Date().formatted(.relative(presentation: .named))) @@ -302,6 +302,13 @@ class MailUITests: XCTestCase { _ = refreshText.waitForExistence(timeout: defaultTimeOut) } - wait(delay: 15) + } +} + +extension XCUIElement { + func scrollToElement() { + while !isHittable { + XCUIApplication().swipeUp() + } } }