| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "base/functional/bind.h" |
| #import "base/ios/ios_util.h" |
| #import "base/strings/stringprintf.h" |
| #import "base/strings/sys_string_conversions.h" |
| #import "base/strings/utf_string_conversions.h" |
| #import "base/test/ios/wait_util.h" |
| #import "components/strings/grit/components_strings.h" |
| #import "components/url_formatter/elide_url.h" |
| #import "ios/chrome/browser/overlays/model/public/web_content_area/alert_constants.h" |
| #import "ios/chrome/grit/ios_strings.h" |
| #import "ios/chrome/test/earl_grey/chrome_actions.h" |
| #import "ios/chrome/test/earl_grey/chrome_earl_grey.h" |
| #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h" |
| #import "ios/chrome/test/earl_grey/chrome_matchers.h" |
| #import "ios/chrome/test/earl_grey/chrome_test_case.h" |
| #import "ios/chrome/test/scoped_eg_synchronization_disabler.h" |
| #import "ios/testing/earl_grey/disabled_test_macros.h" |
| #import "ios/testing/earl_grey/earl_grey_test.h" |
| #import "ios/testing/earl_grey/matchers.h" |
| #import "ios/web/public/test/element_selector.h" |
| #import "net/test/embedded_test_server/http_request.h" |
| #import "net/test/embedded_test_server/http_response.h" |
| #import "net/test/embedded_test_server/request_handler_util.h" |
| #import "ui/base/l10n/l10n_util.h" |
| #import "ui/base/l10n/l10n_util_mac.h" |
| #import "url/gurl.h" |
| |
| using chrome_test_util::ButtonWithAccessibilityLabel; |
| using chrome_test_util::OKButton; |
| using chrome_test_util::SettingsDoneButton; |
| using chrome_test_util::WebViewMatcher; |
| |
| using base::test::ios::kWaitForJSCompletionTimeout; |
| using base::test::ios::kWaitForUIElementTimeout; |
| using base::test::ios::WaitUntilConditionOrTimeout; |
| |
| namespace { |
| |
| // Body script for test page that shows an alert with kAlertMessage and returns |
| // kAlertResult. |
| const char kAlertURLPath[] = "/alert"; |
| const char kAlertMessage[] = "This is a JavaScript alert."; |
| const char kAlertResult[] = "JAVASCRIPT ALERT WAS DISMISSED"; |
| const char kAlertScriptBodyFormat[] = "alert(\"%s\");" |
| "return \"%s\";"; |
| std::string GetAlertScriptBody() { |
| return base::StringPrintf(kAlertScriptBodyFormat, kAlertMessage, |
| kAlertResult); |
| } |
| |
| // Body script for test page that shows a confirmation with kConfirmationMessage |
| // and returns kConfirmationResultOK or kConfirmationResultCancelled, depending |
| // on whether the OK or Cancel button was tapped. |
| const char kConfirmationURLPath[] = "/confirm"; |
| const char kConfirmationMessage[] = "This is a JavaScript confirmation."; |
| const char kConfirmationResultOK[] = "Okay"; |
| const char kConfirmationResultCancelled[] = "Cancelled"; |
| const char kConfirmationScriptBodyFormat[] = "if (confirm(\"%s\") == true) {" |
| " return \"%s\";" |
| "} else {" |
| " return \"%s\";" |
| "}"; |
| std::string GetConfirmationScriptBody() { |
| return base::StringPrintf(kConfirmationScriptBodyFormat, kConfirmationMessage, |
| kConfirmationResultOK, |
| kConfirmationResultCancelled); |
| } |
| |
| // Body script for test page that shows a prompt with kPromptMessage |
| // and returns kPromptTestUserInput or kPromptResultCancelled, depending on |
| // whether the OK or Cancel button was tapped. |
| const char kPromptURLPath[] = "/prompt"; |
| const char kPromptMessage[] = "This is a JavaScript prompt."; |
| const char kPromptTestUserInput[] = "test"; |
| const char kPromptResultCancelled[] = "Cancelled"; |
| const char kPromptTestScriptBodyFormat[] = "var input = prompt(\"%s\");" |
| "if (input != null) {" |
| " return input;" |
| "} else {" |
| " return \"%s\";" |
| "}"; |
| std::string GetPromptScriptBody() { |
| return base::StringPrintf(kPromptTestScriptBodyFormat, kPromptMessage, |
| kPromptResultCancelled); |
| } |
| |
| // Script to inject that will show a JavaScript alert in a loop 20 times, then |
| // returns kAlertLoopFinishedText. |
| const char kAlertLoopURLPath[] = "/loop"; |
| const char kAlertLoopMessage[] = "This is a looped alert."; |
| const char kAlertLoopFinishedText[] = "Loop Finished"; |
| const char kAlertLoopScriptBodyFormat[] = "for (i = 0; i < 20; ++i) {" |
| " alert(\"%s\");" |
| "}" |
| "return \"%s\";"; |
| std::string GetAlertLoopScriptBody() { |
| return base::StringPrintf(kAlertLoopScriptBodyFormat, kAlertLoopMessage, |
| kAlertLoopFinishedText); |
| } |
| |
| // HTTP server constants. |
| |
| // The URL path for a test page which has a link to show JavaScript dialogs. The |
| // page calls the provided script with a timeout so that the JavaScript used to |
| // simulate the link tap can return while the dialogs are displayed. |
| const char kTestPageLinkID[] = "show-dialog"; |
| const char kTestPageContentsFormat[] = |
| "<!DOCTYPE html><html>" |
| " <body>" |
| " <script> function dialogScript() { %s } </script>" |
| " <script>" |
| " function runDialogScript() {" |
| " var result = dialogScript();" |
| " document.getElementById(\"dialog-result\").innerHTML = result;" |
| " }" |
| " </script>" |
| " <a onclick=\"setTimeout(runDialogScript, 0)\" id=\"%s\" " |
| " href=\"javascript:void(0);\">" |
| " Show Dialog." |
| " </a>" |
| " <p id=\"dialog-result\"></p>" |
| " </body>" |
| "</html>"; |
| std::string GetTestPageContents(std::string script_body) { |
| return base::StringPrintf(kTestPageContentsFormat, script_body.c_str(), |
| kTestPageLinkID); |
| } |
| // The URL path for a page that shows an alert onload. |
| const char kOnLoadURLPath[] = "/onload"; |
| const char kOnLoadAlertMessage[] = "onload Alert"; |
| const char kOnLoadContentsFormat[] = |
| "<!DOCTYPE html><html><body onload=\"alert('%s')\"></body></html>"; |
| std::string GetOnLoadPageContents() { |
| return base::StringPrintf(kOnLoadContentsFormat, kOnLoadAlertMessage); |
| } |
| // The URL path for a page with a link to kOnLoadURLPath. |
| const char kLinkPageURLPath[] = "/link"; |
| const char kLinkPageContentsFormat[] = |
| "<!DOCTYPE html><html><body><a id=\"%s\" href=\"%s\">%s</a></body></html>"; |
| const char kLinkPageLinkText[] = "LINK TO ONLOAD ALERT PAGE"; |
| const char kLinkID[] = "link-id"; |
| std::string GetLinkPageContents(const GURL& on_load_url) { |
| return base::StringPrintf(kLinkPageContentsFormat, kLinkID, |
| on_load_url.spec().c_str(), kLinkPageLinkText); |
| } |
| |
| // Helper function that returns a text/html HttpResponse using `content`. |
| std::unique_ptr<net::test_server::HttpResponse> GetHttpResponseWithContent( |
| const std::string& content) { |
| std::unique_ptr<net::test_server::BasicHttpResponse> http_response( |
| new net::test_server::BasicHttpResponse); |
| http_response->set_content_type("text/html"); |
| http_response->set_content(content); |
| return std::move(http_response); |
| } |
| |
| // net::EmbeddedTestServer handler for the test page. |
| std::unique_ptr<net::test_server::HttpResponse> LoadTestPage( |
| std::string script_body, |
| const net::test_server::HttpRequest& request) { |
| return GetHttpResponseWithContent(GetTestPageContents(script_body)); |
| } |
| // net::EmbeddedTestServer handler for kOnLoadURLPath. |
| std::unique_ptr<net::test_server::HttpResponse> LoadPageWithOnLoadAlert( |
| const net::test_server::HttpRequest& request) { |
| return GetHttpResponseWithContent(GetOnLoadPageContents()); |
| } |
| // net::EmbeddedTestServer handler for kLinkPageURLPath. |
| std::unique_ptr<net::test_server::HttpResponse> LoadPageWithLinkToOnLoadPage( |
| ChromeTestCase* test_case, |
| const net::test_server::HttpRequest& request) { |
| GURL on_load_page_url = test_case.testServer->GetURL(kOnLoadURLPath); |
| return GetHttpResponseWithContent(GetLinkPageContents(on_load_page_url)); |
| } |
| |
| // Waits for a JavaScript dialog from `url` with `message` to be shown or |
| // hidden. |
| void WaitForJavaScriptDialog(const GURL& url, |
| const char* message, |
| bool visible, |
| bool is_main_frame) { |
| // Wait for the JavaScript dialog identifier. |
| id<GREYMatcher> visibility_matcher = visible ? grey_notNil() : grey_nil(); |
| ConditionBlock condition = ^{ |
| NSError* error = nil; |
| id<GREYMatcher> dialog_matcher = |
| grey_accessibilityID(kJavaScriptDialogAccessibilityIdentifier); |
| [[EarlGrey selectElementWithMatcher:dialog_matcher] |
| assertWithMatcher:visibility_matcher |
| error:&error]; |
| return !error; |
| }; |
| NSString* error_text = visible ? @"JavaScript dialog was not shown." |
| : @"JavaScript dialog was not hidden."; |
| GREYAssert(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, condition), |
| error_text); |
| |
| // Check the title. Non-modal main-frame dialogs do not have a title label. |
| if (!is_main_frame) { |
| std::u16string url_string = url_formatter::FormatUrlForSecurityDisplay( |
| url, url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS); |
| NSString* expected_title = |
| l10n_util::GetNSStringF(IDS_JAVASCRIPT_MESSAGEBOX_TITLE, url_string); |
| id<GREYMatcher> title_matcher = |
| chrome_test_util::StaticTextWithAccessibilityLabel(expected_title); |
| [[EarlGrey selectElementWithMatcher:title_matcher] |
| assertWithMatcher:visibility_matcher]; |
| } |
| |
| // Check the message. |
| id<GREYMatcher> message_matcher = |
| chrome_test_util::StaticTextWithAccessibilityLabel(@(message)); |
| [[EarlGrey selectElementWithMatcher:message_matcher] |
| assertWithMatcher:visibility_matcher]; |
| } |
| |
| // Types `input` in the prompt. |
| void TypeInPrompt(NSString* input) { |
| id<GREYMatcher> text_field_matcher = grey_allOf( |
| grey_kindOfClass([UITextField class]), |
| grey_accessibilityID(kJavaScriptDialogTextFieldAccessibilityIdentifier), |
| nil); |
| [[EarlGrey selectElementWithMatcher:text_field_matcher] |
| performAction:grey_replaceText(input)]; |
| } |
| |
| void TapCancel() { |
| [[EarlGrey selectElementWithMatcher:chrome_test_util::CancelButton()] |
| performAction:grey_tap()]; |
| } |
| |
| void TapSuppressDialogsButton() { |
| id<GREYMatcher> suppress_dialogs_button = |
| chrome_test_util::ButtonWithAccessibilityLabelId( |
| IDS_IOS_JAVA_SCRIPT_DIALOG_BLOCKING_BUTTON_TEXT); |
| [[EarlGrey selectElementWithMatcher:suppress_dialogs_button] |
| performAction:grey_tap()]; |
| } |
| |
| } // namespace |
| |
| @interface JavaScriptDialogTestCase : ChromeTestCase |
| @end |
| |
| @implementation JavaScriptDialogTestCase |
| |
| #pragma mark - ChromeTestCase |
| |
| - (void)setUp { |
| [super setUp]; |
| |
| self.testServer->RegisterDefaultHandler(base::BindRepeating( |
| net::test_server::HandlePrefixedRequest, kAlertURLPath, |
| base::BindRepeating(&LoadTestPage, GetAlertScriptBody()))); |
| self.testServer->RegisterDefaultHandler(base::BindRepeating( |
| net::test_server::HandlePrefixedRequest, kConfirmationURLPath, |
| base::BindRepeating(&LoadTestPage, GetConfirmationScriptBody()))); |
| self.testServer->RegisterDefaultHandler(base::BindRepeating( |
| net::test_server::HandlePrefixedRequest, kPromptURLPath, |
| base::BindRepeating(&LoadTestPage, GetPromptScriptBody()))); |
| self.testServer->RegisterDefaultHandler(base::BindRepeating( |
| net::test_server::HandlePrefixedRequest, kAlertLoopURLPath, |
| base::BindRepeating(&LoadTestPage, GetAlertLoopScriptBody()))); |
| self.testServer->RegisterDefaultHandler(base::BindRepeating( |
| net::test_server::HandlePrefixedRequest, kOnLoadURLPath, |
| base::BindRepeating(&LoadPageWithOnLoadAlert))); |
| self.testServer->RegisterDefaultHandler(base::BindRepeating( |
| net::test_server::HandlePrefixedRequest, kLinkPageURLPath, |
| base::BindRepeating(&LoadPageWithLinkToOnLoadPage, self))); |
| GREYAssertTrue(self.testServer->Start(), @"Test server failed to start"); |
| } |
| |
| - (void)tearDownHelper { |
| NSError* errorOK = nil; |
| NSError* errorCancel = nil; |
| |
| // Dismiss JavaScript alert by tapping Cancel. |
| [[EarlGrey selectElementWithMatcher:chrome_test_util::CancelButton()] |
| performAction:grey_tap() |
| error:&errorCancel]; |
| // Dismiss JavaScript alert by tapping OK. |
| id<GREYMatcher> OKButton = |
| chrome_test_util::ButtonWithAccessibilityLabelId(IDS_OK); |
| [[EarlGrey selectElementWithMatcher:OKButton] performAction:grey_tap() |
| error:&errorOK]; |
| |
| if (!errorOK || !errorCancel) { |
| GREYFail(@"There are still alerts"); |
| } |
| [super tearDownHelper]; |
| } |
| |
| #pragma mark - Tests |
| |
| // Tests that an alert is shown, and that the completion block is called. |
| - (void)testShowJavaScriptAlert { |
| // Load the alert test page and tap on the link. |
| const GURL kURL = self.testServer->GetURL(kAlertURLPath); |
| [ChromeEarlGrey loadURL:kURL]; |
| [ChromeEarlGrey waitForWebStateContainingElement: |
| [ElementSelector selectorWithElementID:kTestPageLinkID]]; |
| [ChromeEarlGrey tapWebStateElementWithID:@(kTestPageLinkID)]; |
| WaitForJavaScriptDialog(kURL, kAlertMessage, /*visible=*/true, |
| /*is_main_frame=*/true); |
| |
| // Tap the OK button to close the alert. |
| [[EarlGrey selectElementWithMatcher:OKButton()] performAction:grey_tap()]; |
| WaitForJavaScriptDialog(kURL, kAlertMessage, /*visible=*/false, |
| /*is_main_frame=*/true); |
| |
| // Wait for the expected text to be added to the test page. |
| [ChromeEarlGrey waitForWebStateContainingText:kAlertResult]; |
| } |
| |
| // Tests that a confirmation dialog is shown, and that the completion block is |
| // called with the correct value when the OK buton is tapped. |
| - (void)testShowJavaScriptConfirmationOK { |
| // Load the confirmation test page and tap on the link. |
| const GURL kURL = self.testServer->GetURL(kConfirmationURLPath); |
| [ChromeEarlGrey loadURL:kURL]; |
| [ChromeEarlGrey waitForWebStateContainingElement: |
| [ElementSelector selectorWithElementID:kTestPageLinkID]]; |
| [ChromeEarlGrey tapWebStateElementWithID:@(kTestPageLinkID)]; |
| WaitForJavaScriptDialog(kURL, kConfirmationMessage, /*visible=*/true, |
| /*is_main_frame=*/true); |
| |
| // Tap the OK button to close the confirmation. |
| [[EarlGrey selectElementWithMatcher:OKButton()] performAction:grey_tap()]; |
| WaitForJavaScriptDialog(kURL, kConfirmationMessage, /*visible=*/false, |
| /*is_main_frame=*/true); |
| |
| // Wait for the expected text to be added to the test page. |
| [ChromeEarlGrey waitForWebStateContainingText:kConfirmationResultOK]; |
| } |
| |
| // Tests that a confirmation dialog is shown, and that the completion block is |
| // called with the correct value when the Cancel buton is tapped. |
| - (void)testShowJavaScriptConfirmationCancelled { |
| // Load the confirmation test page and tap on the link. |
| const GURL kURL = self.testServer->GetURL(kConfirmationURLPath); |
| [ChromeEarlGrey loadURL:kURL]; |
| [ChromeEarlGrey waitForWebStateContainingElement: |
| [ElementSelector selectorWithElementID:kTestPageLinkID]]; |
| [ChromeEarlGrey tapWebStateElementWithID:@(kTestPageLinkID)]; |
| WaitForJavaScriptDialog(kURL, kConfirmationMessage, /*visible=*/true, |
| /*is_main_frame=*/true); |
| |
| // Tap the OK button to close the confirmation. |
| [[EarlGrey selectElementWithMatcher:OKButton()] performAction:grey_tap()]; |
| |
| WaitForJavaScriptDialog(kURL, kConfirmationMessage, /*visible=*/false, |
| /*is_main_frame=*/true); |
| |
| // Wait for the expected text to be added to the test page. |
| [ChromeEarlGrey waitForWebStateContainingText:kConfirmationResultCancelled]; |
| } |
| |
| // Tests that a prompt dialog is shown, and that the completion block is called |
| // with the correct value when the OK buton is tapped. |
| - (void)testShowJavaScriptPromptOK { |
| // Load the prompt test page and tap on the link. |
| const GURL kURL = self.testServer->GetURL(kPromptURLPath); |
| [ChromeEarlGrey loadURL:kURL]; |
| [ChromeEarlGrey waitForWebStateContainingElement: |
| [ElementSelector selectorWithElementID:kTestPageLinkID]]; |
| [ChromeEarlGrey tapWebStateElementWithID:@(kTestPageLinkID)]; |
| WaitForJavaScriptDialog(kURL, kPromptMessage, /*visible=*/true, |
| /*is_main_frame=*/true); |
| |
| // Enter text into text field. |
| TypeInPrompt(@(kPromptTestUserInput)); |
| |
| // Tap the OK button to close the confirmation. |
| [[EarlGrey selectElementWithMatcher:OKButton()] performAction:grey_tap()]; |
| WaitForJavaScriptDialog(kURL, kPromptMessage, /*visible=*/false, |
| /*is_main_frame=*/true); |
| |
| // Wait for the html to be reset to the input text. |
| [ChromeEarlGrey waitForWebStateContainingText:kPromptTestUserInput]; |
| } |
| |
| // Tests that a prompt dialog is shown, and that the completion block is called |
| // with the correct value when the Cancel buton is tapped. |
| - (void)testShowJavaScriptPromptCancelled { |
| // Load the prompt test page and tap on the link. |
| const GURL kURL = self.testServer->GetURL(kPromptURLPath); |
| [ChromeEarlGrey loadURL:kURL]; |
| [ChromeEarlGrey waitForWebStateContainingElement: |
| [ElementSelector selectorWithElementID:kTestPageLinkID]]; |
| [ChromeEarlGrey tapWebStateElementWithID:@(kTestPageLinkID)]; |
| WaitForJavaScriptDialog(kURL, kPromptMessage, /*visible=*/true, |
| /*is_main_frame=*/true); |
| |
| // Enter text into text field. |
| TypeInPrompt(@(kPromptTestUserInput)); |
| |
| // Tap the Cancel button. |
| TapCancel(); |
| WaitForJavaScriptDialog(kURL, kPromptMessage, /*visible=*/false, |
| /*is_main_frame=*/true); |
| |
| // Wait for the html to be reset to the cancel text. |
| [ChromeEarlGrey waitForWebStateContainingText:kPromptResultCancelled]; |
| } |
| |
| // Tests that JavaScript alerts that are shown in a loop can be suppressed. |
| - (void)testShowJavaScriptAlertLoop { |
| // Load the alert test page and tap on the link. |
| const GURL kURL = self.testServer->GetURL(kAlertLoopURLPath); |
| [ChromeEarlGrey loadURL:kURL]; |
| [ChromeEarlGrey waitForWebStateContainingElement: |
| [ElementSelector selectorWithElementID:kTestPageLinkID]]; |
| [ChromeEarlGrey tapWebStateElementWithID:@(kTestPageLinkID)]; |
| WaitForJavaScriptDialog(kURL, kAlertLoopMessage, /*visible=*/true, |
| /*is_main_frame=*/true); |
| |
| // Tap the OK button to close the alert, then verify that the next alert in |
| // the loop is shown. |
| [[EarlGrey selectElementWithMatcher:OKButton()] performAction:grey_tap()]; |
| WaitForJavaScriptDialog(kURL, kAlertLoopMessage, /*visible=*/true, |
| /*is_main_frame=*/true); |
| |
| // Tap the suppress dialogs button. |
| TapSuppressDialogsButton(); |
| WaitForJavaScriptDialog(kURL, kAlertLoopMessage, /*visible=*/false, |
| /*is_main_frame=*/true); |
| |
| // Wait for the html to be reset to the loop finished text. |
| [ChromeEarlGrey waitForWebStateContainingText:kAlertLoopFinishedText]; |
| } |
| |
| // Tests to ensure crbug.com/658260 does not regress. |
| // Tests that if an alert should be called when settings are displays, the alert |
| // waits for the dismiss of the settings. |
| - (void)MAYBE_testShowJavaScriptBehindSettings { |
| // TODO(crbug.com/40182086): test failing on ipad device |
| #if !TARGET_OS_SIMULATOR |
| if ([ChromeEarlGrey isIPadIdiom]) { |
| EARL_GREY_TEST_SKIPPED(@"This test doesn't pass on iPad device."); |
| } |
| #endif |
| // Load the alert test page. |
| const GURL kURL = self.testServer->GetURL(kAlertURLPath); |
| [ChromeEarlGrey loadURL:kURL]; |
| [ChromeEarlGrey waitForWebStateContainingElement: |
| [ElementSelector selectorWithElementID:kTestPageLinkID]]; |
| |
| // Show settings. |
| [ChromeEarlGreyUI openSettingsMenu]; |
| [[EarlGrey |
| selectElementWithMatcher:grey_allOf(grey_accessibilityLabel( |
| l10n_util::GetNSString( |
| IDS_IOS_SETTINGS_TITLE)), |
| grey_accessibilityTrait( |
| UIAccessibilityTraitHeader), |
| nil)] |
| assertWithMatcher:grey_sufficientlyVisible()]; |
| |
| // Tap the link to trigger the dialog. |
| [ChromeEarlGrey tapWebStateElementWithID:@(kTestPageLinkID)]; |
| |
| // Close the settings. |
| [[EarlGrey selectElementWithMatcher:SettingsDoneButton()] |
| performAction:grey_tap()]; |
| |
| // Make sure the alert is present. |
| WaitForJavaScriptDialog(kURL, kAlertMessage, /*visible=*/true, |
| /*is_main_frame=*/true); |
| |
| [[EarlGrey selectElementWithMatcher:OKButton()] performAction:grey_tap()]; |
| WaitForJavaScriptDialog(kURL, kAlertMessage, /*visible=*/false, |
| /*is_main_frame=*/true); |
| |
| // Wait for the expected text to be added to the test page. |
| [ChromeEarlGrey waitForWebStateContainingText:kAlertResult]; |
| } |
| |
| // Tests that an alert is presented after displaying the share menu. |
| - (void)testShowJavaScriptAfterShareMenu { |
| // Load the blank test page. |
| const GURL kURL = self.testServer->GetURL(kAlertURLPath); |
| [ChromeEarlGrey loadURL:kURL]; |
| [ChromeEarlGrey waitForWebStateContainingElement: |
| [ElementSelector selectorWithElementID:kTestPageLinkID]]; |
| |
| [ChromeEarlGreyUI openShareMenu]; |
| |
| // Copy URL, dismissing the share menu. |
| [ChromeEarlGrey tapButtonInActivitySheetWithID:@"Copy"]; |
| |
| // Show an alert and assert it is present. |
| [ChromeEarlGrey tapWebStateElementWithID:@(kTestPageLinkID)]; |
| WaitForJavaScriptDialog(kURL, kAlertMessage, /*visible=*/true, |
| /*is_main_frame=*/true); |
| |
| [[EarlGrey selectElementWithMatcher:OKButton()] performAction:grey_tap()]; |
| WaitForJavaScriptDialog(kURL, kAlertMessage, /*visible=*/false, |
| /*is_main_frame=*/true); |
| |
| // Wait for the expected text to be added to the test page. |
| [ChromeEarlGrey waitForWebStateContainingText:kAlertResult]; |
| } |
| |
| @end |