| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "ios/testing/earl_grey/system_alert_handler.h" |
| |
| #import "base/logging.h" |
| #import "base/strings/sys_string_conversions.h" |
| #import "ios/testing/earl_grey/earl_grey_test.h" |
| #import "ios/testing/system_alert_handler.h" |
| |
| @implementation SystemAlertHandlerImpl |
| |
| - (void)handleSystemAlertIfVisible { |
| NSError* systemAlertFoundError = nil; |
| [[EarlGrey selectElementWithMatcher:grey_systemAlertViewShown()] |
| assertWithMatcher:grey_nil() |
| error:&systemAlertFoundError]; |
| |
| if (systemAlertFoundError) { |
| NSError* alertGetTextError = nil; |
| NSString* alertText = |
| [EarlGrey SystemAlertTextWithError:&alertGetTextError]; |
| GREYAssertNil(alertGetTextError, @"Error getting alert text.\n%@", |
| alertGetTextError); |
| |
| @try { |
| // Calling this method has for side-effect to throw an |
| // NSInternalInconsistencyException if the system alert is unknown to the |
| // EG2 framework. |
| // If it does throw, handle the system alert in @catch. |
| // If it doesn’t throw, accept it here. |
| // TODO(crbug.com/40127610): Style guide does not allow throwing |
| // exceptions. |
| [EarlGrey SystemAlertType]; |
| |
| DLOG(WARNING) << "Accepting iOS system alert: " |
| << base::SysNSStringToUTF8(alertText); |
| |
| NSError* acceptAlertError = nil; |
| [EarlGrey AcceptSystemDialogWithError:&acceptAlertError]; |
| GREYAssertNil(acceptAlertError, @"Error accepting system alert.\n%@", |
| acceptAlertError); |
| |
| } @catch (NSException* exception) { |
| GREYAssert((exception.name == NSInternalInconsistencyException && |
| [exception.reason containsString:@"Invalid System Alert"]), |
| @"Unknown error caught when handling unknown system alert: %@", |
| exception.reason); |
| |
| // Manually handle EG2-unsupported alerts for some known cases, otherwise |
| // fail the test. |
| [self handleSystemAlertUnsupportedByEG2:alertText]; |
| } |
| } |
| // Ensures no visible alert after handling. |
| [EarlGrey WaitForAlertVisibility:NO |
| withTimeout:kSystemAlertVisibilityTimeout]; |
| } |
| |
| #pragma mark - Private |
| |
| // Tries to dismiss a system alert if it matches a list of known system alerts. |
| // alertText (NSString*) is the text of the system alert currently being shown. |
| // If the button cannot be tapped (system alert cannot be dismissed), or the |
| // text does not match a known text/button pair, the test will fail. |
| - (void)handleSystemAlertUnsupportedByEG2:(NSString*)alertText { |
| NSDictionary<NSString*, NSArray<NSString*>*>* textToButtons = |
| TextToButtonsOfKnownSystemAlerts(); |
| for (NSString* text in textToButtons) { |
| if ([alertText containsString:text]) { |
| DLOG(WARNING) << "Dismissing iOS system alert with label: " << text; |
| NSError* error; |
| |
| // Try every wanted button label possibility for that text before checking |
| // for an error. Some button labels share an alert text. |
| for (NSString* button in textToButtons[text]) { |
| error = nil; |
| [self tapAlertButtonWithText:button error:&error]; |
| |
| // Break out of button labels loop if the current button worked. |
| if (error == nil) { |
| break; |
| } |
| } |
| |
| GREYAssertNil(error, @"Error dismissing iOS alert with label: %@\n%@", |
| text, error); |
| |
| // For the case where a second alert will appear in succession, wait for |
| // it, and then handle it. |
| if ([alertText isEqualToString:@"Software Update"]) { |
| [EarlGrey WaitForAlertVisibility:YES |
| withTimeout:kSystemAlertVisibilityTimeout]; |
| [self handleSystemAlertUnsupportedByEG2:alertText]; |
| } |
| |
| return; |
| } |
| } |
| |
| XCTFail("An unsupported system alert is present on device. Failing the test. " |
| "Alert label: %@", |
| alertText); |
| } |
| |
| // Taps button with `text` in the system alert on screen. If an alert or the |
| // button doesn't exist, note it in `error` accordingly. |
| - (void)tapAlertButtonWithText:(NSString*)text error:(NSError**)error { |
| XCUIApplication* springboardApp = [[XCUIApplication alloc] |
| initWithBundleIdentifier:@"com.apple.springboard"]; |
| XCUIElement* alert = springboardApp.alerts.firstMatch; |
| if (![alert waitForExistenceWithTimeout:kSystemAlertVisibilityTimeout]) { |
| *error = [NSError errorWithDomain:kGREYSystemAlertDismissalErrorDomain |
| code:GREYSystemAlertNotPresent |
| userInfo:nil]; |
| return; |
| } |
| XCUIElement* button = alert.buttons[text]; |
| if (![alert.buttons[text] exists]) { |
| *error = [NSError errorWithDomain:kGREYSystemAlertDismissalErrorDomain |
| code:GREYSystemAlertCustomButtonNotFound |
| userInfo:nil]; |
| return; |
| } |
| |
| [button tap]; |
| } |
| |
| @end |