diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h index 9f53ff944938fa..8b0d7f7a7f440a 100644 --- a/ios/chrome/test/earl_grey/chrome_earl_grey.h +++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h @@ -95,6 +95,14 @@ id ExecuteJavaScript(NSString* javascript, // Waits for there to be |count| number of incognito tabs. If the condition is // not met within a timeout, a GREYAssert is induced. + (void)waitForIncognitoTabCount:(NSUInteger)count; + +// Waits for there to be a web view containing a blocked |image_id|. When +// blocked, the image element will be smaller than the actual image size. ++ (void)waitForWebViewContainingBlockedImageElementWithID:(std::string)imageID; + +// Waits for there to be a web view containing loaded image with |image_id|. +// When loaded, the image element will have the same size as actual image. ++ (void)waitForWebViewContainingLoadedImageElementWithID:(std::string)imageID; @end #endif // IOS_CHROME_TEST_EARL_GREY_CHROME_EARL_GREY_H_ diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm index 5e46264b10ee4f..d57746ef2afb3b 100644 --- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm +++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm @@ -227,4 +227,18 @@ + (void)waitForIncognitoTabCount:(NSUInteger)count { @"Failed waiting for incognito tab count to become %" PRIuNS, count); } + ++ (void)waitForWebViewContainingBlockedImageElementWithID:(std::string)imageID { + GREYAssert(web::test::WaitForWebViewContainingImage( + imageID, chrome_test_util::GetCurrentWebState(), + web::test::IMAGE_STATE_BLOCKED), + @"Failed waiting for web view blocked image %s", imageID.c_str()); +} + ++ (void)waitForWebViewContainingLoadedImageElementWithID:(std::string)imageID { + GREYAssert(web::test::WaitForWebViewContainingImage( + imageID, chrome_test_util::GetCurrentWebState(), + web::test::IMAGE_STATE_LOADED), + @"Failed waiting for web view loaded image %s", imageID.c_str()); +} @end diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm index d6490834c2b042..29f360ce5b562b 100644 --- a/ios/chrome/test/earl_grey/chrome_matchers.mm +++ b/ios/chrome/test/earl_grey/chrome_matchers.mm @@ -10,8 +10,6 @@ #include "base/mac/foundation_util.h" #include "base/strings/sys_string_conversions.h" -#include "base/strings/utf_string_conversions.h" -#import "base/test/ios/wait_util.h" #include "components/strings/grit/components_strings.h" #import "ios/chrome/browser/ui/authentication/signin_promo_view.h" #import "ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.h" @@ -29,7 +27,6 @@ #import "ios/chrome/browser/ui/uikit_ui_util.h" #include "ios/chrome/grit/ios_strings.h" #import "ios/chrome/test/app/chrome_test_util.h" -#import "ios/testing/wait_util.h" #import "ios/web/public/block_types.h" #import "ios/web/public/test/earl_grey/web_view_matchers.h" #include "ui/base/l10n/l10n_util.h" diff --git a/ios/web/public/test/earl_grey/web_view_matchers.mm b/ios/web/public/test/earl_grey/web_view_matchers.mm index 75458a5cf6d0fc..5c53385fd3d3de 100644 --- a/ios/web/public/test/earl_grey/web_view_matchers.mm +++ b/ios/web/public/test/earl_grey/web_view_matchers.mm @@ -26,12 +26,13 @@ using testing::kWaitForDownloadTimeout; using testing::WaitUntilConditionOrTimeout; +// TODO(crbug.com/757982): Remove this class, after LoadImage() is removed. // A helper delegate class that allows downloading responses with invalid // SSL certs. -@interface TestURLSessionDelegate : NSObject +@interface TestURLSessionDelegateDeprecated : NSObject @end -@implementation TestURLSessionDelegate +@implementation TestURLSessionDelegateDeprecated - (void)URLSession:(NSURLSession*)session didReceiveChallenge:(NSURLAuthenticationChallenge*)challenge @@ -64,8 +65,8 @@ - (void)URLSession:(NSURLSession*)session UIImage* LoadImage(const GURL& image_url) { __block UIImage* image; __block NSError* error; - TestURLSessionDelegate* session_delegate = - [[TestURLSessionDelegate alloc] init]; + TestURLSessionDelegateDeprecated* session_delegate = + [[TestURLSessionDelegateDeprecated alloc] init]; NSURLSessionConfiguration* session_config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession* session = diff --git a/ios/web/public/test/web_view_content_test_util.h b/ios/web/public/test/web_view_content_test_util.h index d6bed17970af2e..160abbaf15f52a 100644 --- a/ios/web/public/test/web_view_content_test_util.h +++ b/ios/web/public/test/web_view_content_test_util.h @@ -10,6 +10,14 @@ namespace web { namespace test { +// Enum describing loaded/blocked state of an image html element. +enum ImageStateElement { + // Element was not loaded by WebState. + IMAGE_STATE_BLOCKED = 1, + // Element was fullt loaded by WebState. + IMAGE_STATE_LOADED, +}; + // Returns true if there is a web view for |web_state| that contains |text|. // Otherwise, returns false. bool IsWebViewContainingText(web::WebState* web_state, const std::string& text); @@ -19,6 +27,11 @@ bool IsWebViewContainingText(web::WebState* web_state, const std::string& text); bool WaitForWebViewContainingText(web::WebState* web_state, std::string text) WARN_UNUSED_RESULT; +// Waits for a web view with the corresponding |image_id| and |image_state|, in +// the given |web_state|. +bool WaitForWebViewContainingImage(std::string image_id, + web::WebState* web_state, + ImageStateElement image_state); } // namespace test } // namespace web diff --git a/ios/web/public/test/web_view_content_test_util.mm b/ios/web/public/test/web_view_content_test_util.mm index 83b8a7be718f49..3a6863d59a45d9 100644 --- a/ios/web/public/test/web_view_content_test_util.mm +++ b/ios/web/public/test/web_view_content_test_util.mm @@ -5,14 +5,71 @@ #import "ios/web/public/test/web_view_content_test_util.h" #import +#import +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" #import "ios/testing/wait_util.h" #import "ios/web/public/test/web_view_interaction_test_util.h" +#import "net/base/mac/url_conversions.h" + +using testing::kWaitForDownloadTimeout; +using testing::WaitUntilConditionOrTimeout; + +// A helper delegate class that allows downloading responses with invalid +// SSL certs. +@interface TestURLSessionDelegate : NSObject +@end + +@implementation TestURLSessionDelegate + +- (void)URLSession:(NSURLSession*)session + didReceiveChallenge:(NSURLAuthenticationChallenge*)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, + NSURLCredential*))completionHandler { + SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; + completionHandler(NSURLSessionAuthChallengeUseCredential, + [NSURLCredential credentialForTrust:serverTrust]); +} + +@end namespace { // Script that returns document.body as a string. char kGetDocumentBodyJavaScript[] = "document.body ? document.body.textContent : null"; + +// Fetches the image from |image_url|. +UIImage* LoadImage(const GURL& image_url) { + __block UIImage* image; + __block NSError* error; + TestURLSessionDelegate* session_delegate = + [[TestURLSessionDelegate alloc] init]; + NSURLSessionConfiguration* session_config = + [NSURLSessionConfiguration ephemeralSessionConfiguration]; + NSURLSession* session = + [NSURLSession sessionWithConfiguration:session_config + delegate:session_delegate + delegateQueue:nil]; + id completion_handler = ^(NSData* data, NSURLResponse*, NSError* task_error) { + error = task_error; + image = [[UIImage alloc] initWithData:data]; + }; + + NSURLSessionDataTask* task = + [session dataTaskWithURL:net::NSURLWithGURL(image_url) + completionHandler:completion_handler]; + [task resume]; + + bool task_completed = WaitUntilConditionOrTimeout(kWaitForDownloadTimeout, ^{ + return image || error; + }); + + if (!task_completed) { + return nil; + } + return image; +} } using testing::WaitUntilConditionOrTimeout; @@ -38,5 +95,57 @@ bool WaitForWebViewContainingText(web::WebState* web_state, std::string text) { }); } +bool WaitForWebViewContainingImage(std::string image_id, + web::WebState* web_state, + ImageStateElement image_state) { + std::string get_url_script = + base::StringPrintf("document.getElementById('%s').src", image_id.c_str()); + std::unique_ptr url_as_value = + web::test::ExecuteJavaScript(web_state, get_url_script); + std::string url_as_string; + if (!url_as_value->GetAsString(&url_as_string)) + return false; + + UIImage* image = LoadImage(GURL(url_as_string)); + if (!image) + return false; + + CGSize expected_size = image.size; + + return WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^{ + NSString* const kGetElementAttributesScript = + [NSString stringWithFormat: + @"var image = document.getElementById('%@');" + @"var imageHeight = image.height;" + @"var imageWidth = image.width;" + @"JSON.stringify({" + @" height:imageHeight," + @" width:imageWidth" + @"});", + base::SysUTF8ToNSString(image_id)]; + std::unique_ptr value = web::test::ExecuteJavaScript( + web_state, base::SysNSStringToUTF8(kGetElementAttributesScript)); + std::string result; + if (value && value->GetAsString(&result)) { + NSString* evaluation_result = base::SysUTF8ToNSString(result); + NSData* image_attributes_as_data = + [evaluation_result dataUsingEncoding:NSUTF8StringEncoding]; + NSDictionary* image_attributes = + [NSJSONSerialization JSONObjectWithData:image_attributes_as_data + options:0 + error:nil]; + CGFloat height = [image_attributes[@"height"] floatValue]; + CGFloat width = [image_attributes[@"width"] floatValue]; + switch (image_state) { + case IMAGE_STATE_BLOCKED: + return height < expected_size.height && width < expected_size.width; + case IMAGE_STATE_LOADED: + return height == expected_size.height && width == expected_size.width; + } + } + return false; + }); +} + } // namespace test } // namespace web