From cf8dd124e45e4b1e78206e47f746fa0a4303793e Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 27 Apr 2023 13:47:51 -0700 Subject: [PATCH] [auick_action_ios] Retries multiple times to not fail ci when there is a flake (#3823) The only way that we found to test quick action menu is to long press for a x second. Previously, we pressed it for 2 seconds but it is sometimes too long and the quick action menu is disappeared. In this PR, we: 1. Reduce the press time to 1.5 seconds. 2. Retry 3 times. fixes https://github.com/flutter/flutter/issues/125509 --- .../ios/RunnerUITests/RunnerUITests.swift | 74 +++++++++++++------ 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.swift b/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.swift index a59692e7639d..2ac4fdbc46a9 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.swift +++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.swift @@ -5,6 +5,20 @@ import XCTest private let elementWaitingTime: TimeInterval = 30 +// The duration in when pressing the app icon to open the +// quick action menu. This duration is undocumented by Apple. +// The duration will be adjusted with `pressDurationRetryAdjustment` if +// this duration does not result in the quick action menu opened. +private let quickActionPressDuration: TimeInterval = 1.5 +// If the previous try to open quick action menu did not work, +// a new try with adjust the press time by this value. +// The adjusment could be + or - depends on the result of the previous try. +private let pressDurationRetryAdjustment: TimeInterval = 0.2 +// Max number of tries to open the quick action menu if failed. +// This is to deflake a situation where the quick action menu is not present after +// the long press. +// See: https://github.com/flutter/flutter/issues/125509 +private let quickActionMaxRetries: Int = 4; class RunnerUITests: XCTestCase { @@ -25,22 +39,8 @@ class RunnerUITests: XCTestCase { func testQuickActionWithFreshStart() { let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") let quickActionsAppIcon = springboard.icons["quick_actions_example"] - if !quickActionsAppIcon.waitForExistence(timeout: elementWaitingTime) { - XCTFail( - "Failed due to not able to find the example app from springboard with \(elementWaitingTime) seconds. Springboard debug description: \(springboard.debugDescription)" - ) - } - - quickActionsAppIcon.press(forDuration: 2) - - let actionTwo = springboard.buttons["Action two"] - if !actionTwo.waitForExistence(timeout: elementWaitingTime) { - XCTFail( - "Failed due to not able to find the actionTwo button from springboard with \(elementWaitingTime) seconds. Springboard debug description: \(springboard.debugDescription)" - ) - } - actionTwo.tap() + findAndTapQuickActionButton(buttonName: "Action two", quickActionsAppIcon: quickActionsAppIcon, springboard: springboard); let actionTwoConfirmation = exampleApp.otherElements["action_two"] if !actionTwoConfirmation.waitForExistence(timeout: elementWaitingTime) { @@ -73,24 +73,50 @@ class RunnerUITests: XCTestCase { ) } - quickActionsAppIcon.press(forDuration: 2) + findAndTapQuickActionButton(buttonName: "Action one", quickActionsAppIcon: quickActionsAppIcon, springboard: springboard); - let actionOne = springboard.buttons["Action one"] - if !actionOne.waitForExistence(timeout: elementWaitingTime) { + let actionOneConfirmation = exampleApp.otherElements["action_one"] + if !actionOneConfirmation.waitForExistence(timeout: elementWaitingTime) { XCTFail( - "Failed due to not able to find the actionOne button from springboard with \(elementWaitingTime) seconds. Springboard debug description: \(springboard.debugDescription)" + "Failed due to not able to find the actionOneConfirmation in the app with \(elementWaitingTime) seconds. Springboard debug description: \(springboard.debugDescription)" ) } - actionOne.tap() + XCTAssert(actionOneConfirmation.exists) + } - let actionOneConfirmation = exampleApp.otherElements["action_one"] - if !actionOneConfirmation.waitForExistence(timeout: elementWaitingTime) { + private func findAndTapQuickActionButton(buttonName: String, quickActionsAppIcon: XCUIElement, springboard: XCUIElement) { + var actionButton: XCUIElement? + var pressDuration = quickActionPressDuration + for _ in 1...quickActionMaxRetries { + if !quickActionsAppIcon.waitForExistence(timeout: elementWaitingTime) { + XCTFail( + "Failed due to not able to find the example app from springboard with \(elementWaitingTime) seconds. Springboard debug description: \(springboard.debugDescription)" + ) + } + quickActionsAppIcon.press(forDuration: pressDuration) + actionButton = springboard.buttons[buttonName] + if actionButton!.waitForExistence(timeout: elementWaitingTime) { + // find the button, exit the retry loop. + break + } + let deleteButton = springboard.buttons["DeleteButton"] + if deleteButton.waitForExistence(timeout: elementWaitingTime) { + // Found delete button instead, we pressed too long, reduce the press time. + pressDuration -= pressDurationRetryAdjustment + } else { + // Neither action button nor delete button was found, we need a longer press. + pressDuration += pressDurationRetryAdjustment + } + // Reset to previous state. + XCUIDevice.shared.press(XCUIDevice.Button.home) + } + if (!actionButton!.exists) { XCTFail( - "Failed due to not able to find the actionOneConfirmation in the app with \(elementWaitingTime) seconds. Springboard debug description: \(springboard.debugDescription)" + "Failed due to not able to find the \(buttonName) button from springboard with \(elementWaitingTime) seconds. Springboard debug description: \(springboard.debugDescription)" ) } - XCTAssert(actionOneConfirmation.exists) + actionButton!.tap(); } }