blob: 50344ec7d2104fc735c6099f76445008db4c1c20 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#include <functional>
#include <memory>
#include "base/bind.h"
#include "base/ios/ios_util.h"
#include "base/mac/foundation_util.h"
#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#import "base/test/ios/wait_util.h"
#import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_app_interface.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_constants.h"
#import "ios/chrome/browser/ui/table_view/table_view_constants.h"
#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/earl_grey/chrome_actions_app_interface.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.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"
#include "ios/testing/earl_grey/app_launch_configuration.h"
#import "ios/testing/earl_grey/app_launch_manager.h"
#import "ios/testing/earl_grey/earl_grey_test.h"
#include "ios/web/common/features.h"
#import "ios/web/public/navigation/navigation_manager.h"
#import "ios/web/public/navigation/reload_type.h"
#include "net/base/network_change_notifier.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#import "ui/base/device_form_factor.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/test/ios/ui_image_test_utils.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using base::test::ios::kWaitForUIElementTimeout;
using chrome_test_util::DeleteButton;
using chrome_test_util::ReadingListMarkAsReadButton;
using chrome_test_util::ReadingListMarkAsUnreadButton;
namespace {
const char kContentToRemove[] = "Text that distillation should remove.";
const char kContentToKeep[] = "Text that distillation should keep.";
NSString* const kDistillableTitle = @"Tomato";
const char kDistillableURL[] = "/potato";
const char kNonDistillableURL[] = "/beans";
const char kRedImageURL[] = "/redimage";
const char kGreenImageURL[] = "/greenimage";
NSString* const kReadTitle = @"foobar";
NSString* const kReadURL = @"http://readfoobar.com";
NSString* const kUnreadTitle = @"I am an unread entry";
NSString* const kUnreadURL = @"http://unreadfoobar.com";
NSString* const kReadURL2 = @"http://kReadURL2.com";
NSString* const kReadTitle2 = @"read item 2";
NSString* const kUnreadTitle2 = @"I am another unread entry";
NSString* const kUnreadURL2 = @"http://unreadfoobar2.com";
const size_t kNumberReadEntries = 2;
const size_t kNumberUnreadEntries = 2;
const CFTimeInterval kSnackbarAppearanceTimeout = 5;
// kSnackbarDisappearanceTimeout = MDCSnackbarMessageDurationMax + 1
const CFTimeInterval kSnackbarDisappearanceTimeout = 10 + 1;
const CFTimeInterval kDelayForSlowWebServer = 4;
const CFTimeInterval kLongPressDuration = 1.0;
const CFTimeInterval kDistillationTimeout = 5;
const CFTimeInterval kServerOperationDelay = 1;
NSString* const kReadHeader = @"Read";
NSString* const kUnreadHeader = @"Unread";
NSString* const kCheckImagesJS =
@"function checkImages() {"
@" for (img of document.getElementsByTagName('img')) {"
@" s = img.src;"
@" data = s.startsWith('data:');"
@" loaded = img.complete && (img.naturalWidth > 0);"
@" if (data != loaded) return false;"
@" }"
@" return true;"
@"}"
@"checkImages();";
// Returns the string concatenated `n` times.
std::string operator*(const std::string& s, unsigned int n) {
std::ostringstream out;
for (unsigned int i = 0; i < n; i++)
out << s;
return out.str();
}
// Scroll to the top of the Reading List.
void ScrollToTop() {
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(kReadingListViewID)]
performAction:[ChromeActionsAppInterface scrollToTop]];
}
// Asserts that the "mark" toolbar button is visible and has the a11y label of
// `a11y_label_id`.
void AssertToolbarMarkButtonText(int a11y_label_id) {
[[EarlGrey
selectElementWithMatcher:
grey_allOf(
grey_accessibilityID(kReadingListToolbarMarkButtonID),
grey_ancestor(grey_kindOfClassName(@"UIToolbar")),
chrome_test_util::ButtonWithAccessibilityLabelId(a11y_label_id),
nil)] assertWithMatcher:grey_sufficientlyVisible()];
}
// Asserts the `button_id` toolbar button is not visible.
void AssertToolbarButtonNotVisibleWithID(NSString* button_id) {
[[EarlGrey
selectElementWithMatcher:grey_allOf(grey_accessibilityID(button_id),
grey_ancestor(grey_kindOfClassName(
@"UIToolbar")),
nil)]
assertWithMatcher:grey_notVisible()];
}
// Assert the `button_id` toolbar button is visible.
void AssertToolbarButtonVisibleWithID(NSString* button_id) {
[[EarlGrey
selectElementWithMatcher:grey_allOf(grey_accessibilityID(button_id),
grey_ancestor(grey_kindOfClassName(
@"UIToolbar")),
nil)]
assertWithMatcher:grey_sufficientlyVisible()];
}
// Taps the `button_id` toolbar button.
void TapToolbarButtonWithID(NSString* button_id) {
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(button_id)]
performAction:grey_tap()];
}
// Taps the context menu button with the a11y label of `a11y_label_id`.
void TapContextMenuButtonWithA11yLabelID(int a11y_label_id) {
[[EarlGrey
selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
a11y_label_id)] performAction:grey_tap()];
}
// Performs `action` on the entry with the title `entryTitle`. The view can be
// scrolled down to find the entry.
void PerformActionOnEntry(NSString* entryTitle, id<GREYAction> action) {
ScrollToTop();
id<GREYMatcher> matcher =
grey_allOf(chrome_test_util::StaticTextWithAccessibilityLabel(entryTitle),
grey_ancestor(grey_kindOfClassName(@"TableViewURLCell")),
grey_sufficientlyVisible(), nil);
[[[EarlGrey selectElementWithMatcher:matcher]
usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 100)
onElementWithMatcher:grey_accessibilityID(kReadingListViewID)]
performAction:action];
}
// Taps the entry with the title `entryTitle`.
void TapEntry(NSString* entryTitle) {
PerformActionOnEntry(entryTitle, grey_tap());
}
// Long-presses the entry with the title `entryTitle`.
void LongPressEntry(NSString* entryTitle) {
PerformActionOnEntry(entryTitle,
grey_longPressWithDuration(kLongPressDuration));
}
// Asserts that the entry with the title `entryTitle` is visible.
void AssertEntryVisible(NSString* entryTitle) {
ScrollToTop();
[[[EarlGrey
selectElementWithMatcher:
grey_allOf(
chrome_test_util::StaticTextWithAccessibilityLabel(entryTitle),
grey_ancestor(grey_kindOfClassName(@"TableViewURLCell")),
grey_sufficientlyVisible(), nil)]
usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 100)
onElementWithMatcher:grey_accessibilityID(kReadingListViewID)]
assertWithMatcher:grey_notNil()];
}
// Asserts that all the entries are visible.
void AssertAllEntriesVisible() {
AssertEntryVisible(kReadTitle);
AssertEntryVisible(kReadTitle2);
AssertEntryVisible(kUnreadTitle);
AssertEntryVisible(kUnreadTitle2);
// If the number of entries changes, make sure this assert gets updated.
GREYAssertEqual((size_t)2, kNumberReadEntries,
@"The number of entries have changed");
GREYAssertEqual((size_t)2, kNumberUnreadEntries,
@"The number of entries have changed");
}
// Asserts that the entry `title` is not visible.
void AssertEntryNotVisible(NSString* title) {
[ChromeEarlGreyUI waitForAppToIdle];
ScrollToTop();
NSError* error;
[[[EarlGrey
selectElementWithMatcher:
grey_allOf(chrome_test_util::StaticTextWithAccessibilityLabel(title),
grey_ancestor(grey_kindOfClassName(@"TableViewURLCell")),
grey_sufficientlyVisible(), nil)]
usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 100)
onElementWithMatcher:grey_accessibilityID(kReadingListViewID)]
assertWithMatcher:grey_notNil()
error:&error];
GREYAssertNotNil(error, @"Entry is visible");
}
// Asserts `header` is visible.
void AssertHeaderNotVisible(NSString* header) {
[ChromeEarlGreyUI waitForAppToIdle];
ScrollToTop();
[[EarlGrey selectElementWithMatcher:
chrome_test_util::StaticTextWithAccessibilityLabel(header)]
assertWithMatcher:grey_notVisible()];
}
// Opens the reading list menu.
void OpenReadingList() {
[ChromeEarlGreyUI openToolsMenu];
[ChromeEarlGreyUI
tapToolsMenuButton:chrome_test_util::ReadingListDestinationButton()];
// It seems that sometimes there is a delay before the ReadingList is
// displayed. See https://crbug.com/1109202 .
GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
kWaitForUIElementTimeout,
^BOOL {
NSError* error = nil;
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(
kReadingListViewID)]
assertWithMatcher:grey_sufficientlyVisible()
error:&error];
return error == nil;
}),
@"Reading List didn't appear.");
}
// Adds 20 read and 20 unread entries to the model, opens the reading list menu
// and enter edit mode.
void AddLotOfEntriesAndEnterEdit() {
for (NSInteger index = 0; index < 10; index++) {
NSString* url_to_be_added =
[kReadURL stringByAppendingPathComponent:[@(index) stringValue]];
GREYAssertNil([ReadingListAppInterface
addEntryWithURL:[NSURL URLWithString:url_to_be_added]
title:kReadTitle
read:YES],
@"Unable to add Reading List item");
}
for (NSInteger index = 0; index < 10; index++) {
NSString* url_to_be_added =
[kUnreadURL stringByAppendingPathComponent:[@(index) stringValue]];
GREYAssertNil([ReadingListAppInterface
addEntryWithURL:[NSURL URLWithString:url_to_be_added]
title:kReadTitle
read:NO],
@"Unable to add Reading List item");
}
OpenReadingList();
TapToolbarButtonWithID(kReadingListToolbarEditButtonID);
}
// Adds 2 read and 2 unread entries to the model, opens the reading list menu.
void AddEntriesAndOpenReadingList() {
GREYAssertNil(
[ReadingListAppInterface addEntryWithURL:[NSURL URLWithString:kReadURL]
title:kReadTitle
read:YES],
@"Unable to add Reading List item");
GREYAssertNil(
[ReadingListAppInterface addEntryWithURL:[NSURL URLWithString:kReadURL2]
title:kReadTitle2
read:YES],
@"Unable to add Reading List item");
GREYAssertNil(
[ReadingListAppInterface addEntryWithURL:[NSURL URLWithString:kUnreadURL]
title:kUnreadTitle
read:NO],
@"Unable to add Reading List item");
GREYAssertNil(
[ReadingListAppInterface addEntryWithURL:[NSURL URLWithString:kUnreadURL2]
title:kUnreadTitle2
read:NO],
@"Unable to add Reading List item");
OpenReadingList();
}
void AddEntriesAndEnterEdit() {
AddEntriesAndOpenReadingList();
TapToolbarButtonWithID(kReadingListToolbarEditButtonID);
}
// Adds the current page to the Reading List.
void AddCurrentPageToReadingList() {
NSString* snackBarLabel =
l10n_util::GetNSStringWithFixup(IDS_IOS_READING_LIST_SNACKBAR_MESSAGE);
// Add the page to the reading list.
[ChromeEarlGreyUI openToolsMenu];
// Start custom monitor, because there's a chance the snackbar is
// already gone by the time we wait for it (and it was like that sometimes).
[ChromeEarlGrey watchForButtonsWithLabels:@[ snackBarLabel ]
timeout:kSnackbarAppearanceTimeout];
[ChromeEarlGreyUI
tapToolsMenuAction:chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_SHARE_MENU_READING_LIST_ACTION)];
// Wait for the snackbar to appear.
id<GREYMatcher> snackbar_matcher =
chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_READING_LIST_SNACKBAR_MESSAGE);
ConditionBlock wait_for_appearance = ^{
return [ChromeEarlGrey watcherDetectedButtonWithLabel:snackBarLabel];
};
GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
kSnackbarAppearanceTimeout, wait_for_appearance),
@"Snackbar did not appear.");
// Wait for the snackbar to disappear.
ConditionBlock wait_for_disappearance = ^{
NSError* error = nil;
[[EarlGrey selectElementWithMatcher:snackbar_matcher]
assertWithMatcher:grey_nil()
error:&error];
return error == nil;
};
GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
kSnackbarDisappearanceTimeout, wait_for_disappearance),
@"Snackbar did not disappear.");
[ReadingListAppInterface notifyWifiConnection];
}
// Wait until one element is distilled.
void WaitForDistillation() {
ConditionBlock wait_for_distillation_date = ^{
NSError* error = nil;
[[EarlGrey
selectElementWithMatcher:grey_allOf(
grey_accessibilityID(
kTableViewURLCellFaviconBadgeViewID),
grey_sufficientlyVisible(), nil)]
assertWithMatcher:grey_notNil()
error:&error];
return error == nil;
};
GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
kDistillationTimeout, wait_for_distillation_date),
@"Item was not distilled.");
}
// Serves URLs. Response can be delayed by `delay` second or return an error if
// `responds_with_content` is false.
// If `distillable`, result is can be distilled for offline display.
std::unique_ptr<net::test_server::HttpResponse> HandleQueryOrCloseSocket(
const bool& responds_with_content,
const int& delay,
bool distillable,
const net::test_server::HttpRequest& request) {
if (!responds_with_content) {
return std::make_unique<net::test_server::RawHttpResponse>(
/*headers=*/"", /*contents=*/"");
}
auto response = std::make_unique<net::test_server::DelayedHttpResponse>(
base::Seconds(delay));
if (base::StartsWith(request.relative_url, kDistillableURL)) {
response->set_content_type("text/html");
std::string page_title = "Tomato";
std::string content_to_remove(kContentToRemove);
std::string content_to_keep(kContentToKeep);
std::string green_image_url(kGreenImageURL);
std::string red_image_url(kRedImageURL);
response->set_content("<html><head><title>" + page_title +
"</title></head>" + content_to_remove * 20 +
"<article>" + content_to_keep * 20 + "<img src='" +
green_image_url +
"'/>"
"<img src='" +
red_image_url +
"'/>"
"</article>" +
content_to_remove * 20 + "</html>");
return std::move(response);
}
if (base::StartsWith(request.relative_url, kNonDistillableURL)) {
response->set_content_type("text/html");
response->set_content("<html><head><title>greens</title></head></html>");
return std::move(response);
}
NOTREACHED();
return std::move(response);
}
// Serves image URLs.
// If `serve_red_image` is false, 404 error is returned when red image is
// requested.
// `served_red_image` will be set to true whenever red image is requested.
std::unique_ptr<net::test_server::HttpResponse> HandleImageQueryOrCloseSocket(
const bool& serve_red_image,
bool& served_red_image,
const net::test_server::HttpRequest& request) {
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
if (base::StartsWith(request.relative_url, kGreenImageURL)) {
response->set_content_type("image/png");
UIImage* image = ui::test::uiimage_utils::UIImageWithSizeAndSolidColor(
CGSizeMake(10, 10), [UIColor greenColor]);
NSData* image_data = UIImagePNGRepresentation(image);
response->set_content(std::string(
static_cast<const char*>(image_data.bytes), image_data.length));
return std::move(response);
}
if (base::StartsWith(request.relative_url, kRedImageURL)) {
served_red_image = true;
if (!serve_red_image) {
response->set_code(net::HTTP_NOT_FOUND);
return std::move(response);
}
response->set_content_type("image/png");
UIImage* image = ui::test::uiimage_utils::UIImageWithSizeAndSolidColor(
CGSizeMake(10, 10), [UIColor redColor]);
NSData* image_data = UIImagePNGRepresentation(image);
response->set_content(std::string(
static_cast<const char*>(image_data.bytes), image_data.length));
return std::move(response);
}
NOTREACHED();
return std::move(response);
}
// Opens the page security info bubble.
void OpenPageSecurityInfoBubble() {
// In UI Refresh, the security info is accessed through the tools menu.
[ChromeEarlGreyUI openToolsMenu];
// Tap on the Page Info button.
[ChromeEarlGreyUI
tapToolsMenuButton:chrome_test_util::SiteInfoDestinationButton()];
}
// Tests that the correct version of kDistillableURL is displayed.
void AssertIsShowingDistillablePage(bool online, const GURL& distillable_url) {
[ChromeEarlGrey waitForWebStateContainingText:kContentToKeep];
[[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText(
distillable_url.GetContent())]
assertWithMatcher:grey_notNil()];
// Test that the offline and online pages are properly displayed.
if (online) {
[ChromeEarlGrey waitForWebStateContainingText:kContentToRemove];
[ChromeEarlGrey waitForWebStateContainingText:kContentToKeep];
} else {
[ChromeEarlGrey waitForWebStateNotContainingText:kContentToRemove];
[ChromeEarlGrey waitForWebStateContainingText:kContentToKeep];
}
// Test the presence of the omnibox offline chip.
[[EarlGrey selectElementWithMatcher:
grey_allOf(chrome_test_util::PageSecurityInfoIndicator(),
chrome_test_util::ImageViewWithImageNamed(
@"location_bar_connection_offline"),
nil)]
assertWithMatcher:online ? grey_nil() : grey_notNil()];
}
} // namespace
// Test class for the Reading List menu.
@interface ReadingListTestCase : ChromeTestCase
// YES if test server is replying with valid HTML content (URL query). NO if
// test server closes the socket.
@property(nonatomic, assign) bool serverRespondsWithContent;
// YES if test server is replying with valid read image. NO if it responds with
// 404 error.
@property(nonatomic, assign) bool serverServesRedImage;
// Server sets this to true when it is requested the red image.
@property(nonatomic, assign) bool serverServedRedImage;
// The delay after which self.testServer will send a response.
@property(nonatomic, assign) NSTimeInterval serverResponseDelay;
@end
@implementation ReadingListTestCase
@synthesize serverRespondsWithContent = _serverRespondsWithContent;
@synthesize serverResponseDelay = _serverResponseDelay;
- (void)setUp {
[super setUp];
GREYAssertNil([ReadingListAppInterface clearEntries],
@"Unable to clear Reading List entries");
self.testServer->RegisterRequestHandler(base::BindRepeating(
&net::test_server::HandlePrefixedRequest, kDistillableURL,
base::BindRepeating(&HandleQueryOrCloseSocket,
std::cref(_serverRespondsWithContent),
std::cref(_serverResponseDelay), true)));
self.testServer->RegisterRequestHandler(base::BindRepeating(
&net::test_server::HandlePrefixedRequest, kNonDistillableURL,
base::BindRepeating(&HandleQueryOrCloseSocket,
std::cref(_serverRespondsWithContent),
std::cref(_serverResponseDelay), false)));
self.testServer->RegisterRequestHandler(base::BindRepeating(
&net::test_server::HandlePrefixedRequest, kGreenImageURL,
base::BindRepeating(&HandleImageQueryOrCloseSocket,
std::cref(_serverServesRedImage),
std::ref(_serverServedRedImage))));
self.testServer->RegisterRequestHandler(base::BindRepeating(
&net::test_server::HandlePrefixedRequest, kRedImageURL,
base::BindRepeating(&HandleImageQueryOrCloseSocket,
std::cref(_serverServesRedImage),
std::ref(_serverServedRedImage))));
self.serverRespondsWithContent = true;
self.serverServesRedImage = true;
GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
[ChromeEarlGrey stopWatcher];
}
- (void)tearDown {
[ChromeEarlGrey stopWatcher];
[super tearDown];
[ReadingListAppInterface resetConnectionType];
}
// Tests that the Reading List view is accessible.
- (void)testAccessibility {
AddEntriesAndEnterEdit();
// In edit mode.
[ChromeEarlGrey verifyAccessibilityForCurrentScreen];
TapToolbarButtonWithID(kReadingListToolbarCancelButtonID);
[ChromeEarlGrey verifyAccessibilityForCurrentScreen];
}
// Tests that navigating back to an offline page is still displaying the error
// page and don't mess the navigation stack.
- (void)testNavigateBackToDistilledPage {
[ReadingListAppInterface forceConnectionToWifi];
GURL distillablePageURL(self.testServer->GetURL(kDistillableURL));
GURL nonDistillablePageURL(self.testServer->GetURL(kNonDistillableURL));
// Open http://potato
[ChromeEarlGrey loadURL:distillablePageURL];
[ChromeEarlGrey waitForPageToFinishLoading];
AddCurrentPageToReadingList();
// Verify that an entry with the correct title is present in the reading list.
OpenReadingList();
AssertEntryVisible(kDistillableTitle);
WaitForDistillation();
// Long press the entry, and open it offline.
LongPressEntry(kDistillableTitle);
int offlineStringId = IDS_IOS_READING_LIST_OPEN_OFFLINE_BUTTON;
TapContextMenuButtonWithA11yLabelID(offlineStringId);
[ChromeEarlGrey waitForPageToFinishLoading];
base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(1));
AssertIsShowingDistillablePage(false, distillablePageURL);
// Navigate to http://beans
[ChromeEarlGrey loadURL:nonDistillablePageURL];
[ChromeEarlGrey waitForPageToFinishLoading];
[ChromeEarlGrey goBack];
[ChromeEarlGrey waitForPageToFinishLoading];
base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(1));
if ([ChromeEarlGrey isLoadSimulatedRequestAPIEnabled]) {
// Check that the online version is now displayed.
AssertIsShowingDistillablePage(true, distillablePageURL);
GREYAssertEqual(1, [ChromeEarlGrey navigationBackListItemsCount],
@"The NTP page should be the first committed URL.");
} else {
// Check that the offline version is still displayed.
AssertIsShowingDistillablePage(false, distillablePageURL);
// Check that a new navigation wasn't created.
GREYAssertEqual(0, [ChromeEarlGrey navigationBackListItemsCount],
@"The offline page should be the first committed URL.");
}
// Check that navigating forward navigates to the correct page.
[[EarlGrey selectElementWithMatcher:chrome_test_util::ForwardButton()]
performAction:grey_tap()];
[[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText(
nonDistillablePageURL.GetContent())]
assertWithMatcher:grey_notNil()];
}
// Tests that sharing a web page to the Reading List results in a snackbar
// appearing, and that the Reading List entry is present in the Reading List.
// Loads offline version via context menu.
- (void)testSavingToReadingListAndLoadDistilled {
[ReadingListAppInterface forceConnectionToWifi];
GURL distillablePageURL(self.testServer->GetURL(kDistillableURL));
GURL nonDistillablePageURL(self.testServer->GetURL(kNonDistillableURL));
// Open http://potato
[ChromeEarlGrey loadURL:distillablePageURL];
[ChromeEarlGrey waitForPageToFinishLoading];
AddCurrentPageToReadingList();
// Navigate to http://beans
[ChromeEarlGrey loadURL:nonDistillablePageURL];
[ChromeEarlGrey waitForPageToFinishLoading];
// Verify that an entry with the correct title is present in the reading list.
OpenReadingList();
AssertEntryVisible(kDistillableTitle);
WaitForDistillation();
// Long press the entry, and open it offline.
LongPressEntry(kDistillableTitle);
int offlineStringId = IDS_IOS_READING_LIST_OPEN_OFFLINE_BUTTON;
TapContextMenuButtonWithA11yLabelID(offlineStringId);
[ChromeEarlGrey waitForPageToFinishLoading];
base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(1));
AssertIsShowingDistillablePage(false, distillablePageURL);
// Tap the Omnibox' Info Bubble to open the Page Info.
OpenPageSecurityInfoBubble();
// Verify that the Page Info is about offline pages.
id<GREYMatcher> pageInfoTitleMatcher =
chrome_test_util::StaticTextWithAccessibilityLabelId(
IDS_IOS_PAGE_INFO_OFFLINE_PAGE_LABEL);
[[EarlGrey selectElementWithMatcher:pageInfoTitleMatcher]
assertWithMatcher:grey_notNil()];
// Verify that the webState's title is correct.
GREYAssertEqualObjects([ChromeEarlGreyAppInterface currentTabTitle],
kDistillableTitle, @"Wrong page name");
}
// Tests that offline page does not request online resources.
- (void)testSavingToReadingListAndLoadDistilledNoOnlineResource {
self.serverServesRedImage = false;
[ReadingListAppInterface forceConnectionToWifi];
GURL distillablePageURL(self.testServer->GetURL(kDistillableURL));
GURL nonDistillablePageURL(self.testServer->GetURL(kNonDistillableURL));
// Open http://potato
[ChromeEarlGrey loadURL:distillablePageURL];
[ChromeEarlGrey waitForPageToFinishLoading];
AddCurrentPageToReadingList();
// Navigate to http://beans
[ChromeEarlGrey loadURL:nonDistillablePageURL];
[ChromeEarlGrey waitForPageToFinishLoading];
// Verify that an entry with the correct title is present in the reading list.
OpenReadingList();
AssertEntryVisible(kDistillableTitle);
WaitForDistillation();
self.serverServesRedImage = true;
self.serverServedRedImage = false;
// Long press the entry, and open it offline.
LongPressEntry(kDistillableTitle);
int offlineStringId = IDS_IOS_READING_LIST_OPEN_OFFLINE_BUTTON;
TapContextMenuButtonWithA11yLabelID(offlineStringId);
[ChromeEarlGrey waitForPageToFinishLoading];
AssertIsShowingDistillablePage(false, distillablePageURL);
GREYAssertFalse(self.serverServedRedImage,
@"Offline page accessed online resource.");
auto checkImage = [ChromeEarlGrey evaluateJavaScript:kCheckImagesJS];
GREYAssertTrue(checkImage.is_bool(), @"CheckImage is not a boolean.");
GREYAssert(checkImage.GetBool(), @"Incorrect image loading.");
// Verify that the webState's title is correct.
GREYAssertEqualObjects([ChromeEarlGreyAppInterface currentTabTitle],
kDistillableTitle, @"Wrong page name");
}
// Tests that sharing a web page to the Reading List results in a snackbar
// appearing, and that the Reading List entry is present in the Reading List.
// Loads online version by tapping on entry.
- (void)testSavingToReadingListAndLoadNormal {
[ReadingListAppInterface forceConnectionToWifi];
GURL distillableURL = self.testServer->GetURL(kDistillableURL);
// Open http://potato
[ChromeEarlGrey loadURL:distillableURL];
AddCurrentPageToReadingList();
// Navigate to http://beans
[ChromeEarlGrey loadURL:self.testServer->GetURL(kNonDistillableURL)];
[ChromeEarlGrey waitForPageToFinishLoading];
// Verify that an entry with the correct title is present in the reading list.
OpenReadingList();
AssertEntryVisible(kDistillableTitle);
WaitForDistillation();
// Press the entry, and open it online.
TapEntry(kDistillableTitle);
AssertIsShowingDistillablePage(true, distillableURL);
// Stop server to reload offline.
self.serverRespondsWithContent = NO;
base::test::ios::SpinRunLoopWithMinDelay(
base::Seconds(kServerOperationDelay));
[ChromeEarlGreyAppInterface startReloading];
AssertIsShowingDistillablePage(false, distillableURL);
}
// Tests that sharing a web page to the Reading List results in a snackbar
// appearing, and that the Reading List entry is present in the Reading List.
// Loads offline version by tapping on entry without web server.
// TODO(crbug.com/1326627): Fix flakiness.
- (void)DISABLED_testSavingToReadingListAndLoadNoNetwork {
[ReadingListAppInterface forceConnectionToWifi];
GURL distillableURL = self.testServer->GetURL(kDistillableURL);
// Open http://potato
[ChromeEarlGrey loadURL:distillableURL];
AddCurrentPageToReadingList();
// Navigate to http://beans
[ChromeEarlGrey loadURL:self.testServer->GetURL(kNonDistillableURL)];
[ChromeEarlGrey waitForPageToFinishLoading];
// Verify that an entry with the correct title is present in the reading list.
OpenReadingList();
AssertEntryVisible(kDistillableTitle);
WaitForDistillation();
// Stop server to generate error.
self.serverRespondsWithContent = NO;
base::test::ios::SpinRunLoopWithMinDelay(
base::Seconds(kServerOperationDelay));
// Long press the entry, and open it offline.
TapEntry(kDistillableTitle);
AssertIsShowingDistillablePage(false, distillableURL);
// Reload. As server is still down, the offline page should show again.
[ChromeEarlGreyAppInterface startReloading];
AssertIsShowingDistillablePage(false, distillableURL);
[ChromeEarlGrey goBack];
[ChromeEarlGrey goForward];
AssertIsShowingDistillablePage(false, distillableURL);
// Start server to reload online error.
self.serverRespondsWithContent = YES;
base::test::ios::SpinRunLoopWithMinDelay(
base::Seconds(kServerOperationDelay));
[ChromeEarlGreyAppInterface startReloading];
AssertIsShowingDistillablePage(true, distillableURL);
}
// Tests that sharing a web page to the Reading List results in a snackbar
// appearing, and that the Reading List entry is present in the Reading List.
// Loads offline version by tapping on entry with delayed web server.
- (void)testSavingToReadingListAndLoadBadNetwork {
// TODO(crbug.com/1350732): Re-enable when flake fixed.
if (@available(iOS 16, *)) {
EARL_GREY_TEST_DISABLED(@"Test consistently failing on iOS16 iPhone 8.");
}
[ReadingListAppInterface forceConnectionToWifi];
GURL distillableURL = self.testServer->GetURL(kDistillableURL);
// Open http://potato
[ChromeEarlGrey loadURL:distillableURL];
AddCurrentPageToReadingList();
// Navigate to http://beans
[ChromeEarlGrey loadURL:self.testServer->GetURL(kNonDistillableURL)];
[ChromeEarlGrey waitForPageToFinishLoading];
// Verify that an entry with the correct title is present in the reading
OpenReadingList();
AssertEntryVisible(kDistillableTitle);
WaitForDistillation();
self.serverResponseDelay = kDelayForSlowWebServer;
// Open the entry.
TapEntry(kDistillableTitle);
AssertIsShowingDistillablePage(false, distillableURL);
[ChromeEarlGrey goBack];
[ChromeEarlGrey goForward];
base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(1));
AssertIsShowingDistillablePage(false, distillableURL);
// Reload should load online page.
[ChromeEarlGreyAppInterface startReloading];
AssertIsShowingDistillablePage(true, distillableURL);
// Reload should load offline page.
[ChromeEarlGreyAppInterface startReloading];
AssertIsShowingDistillablePage(false, distillableURL);
}
// Tests that only the "Edit" button is showing when not editing.
- (void)testVisibleButtonsNonEditingMode {
GREYAssertNil(
[ReadingListAppInterface addEntryWithURL:[NSURL URLWithString:kUnreadURL]
title:kUnreadTitle
read:NO],
@"Unable to add Reading List entry.");
OpenReadingList();
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarDeleteButtonID);
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarDeleteAllReadButtonID);
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarMarkButtonID);
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarCancelButtonID);
AssertToolbarButtonVisibleWithID(kReadingListToolbarEditButtonID);
}
// Tests that only the "Cancel", "Delete All Read" and "Mark All…" buttons are
// showing when not editing.
- (void)testVisibleButtonsEditingModeEmptySelection {
AddEntriesAndEnterEdit();
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarDeleteButtonID);
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarEditButtonID);
AssertToolbarButtonVisibleWithID(kReadingListToolbarDeleteAllReadButtonID);
AssertToolbarButtonVisibleWithID(kReadingListToolbarCancelButtonID);
AssertToolbarMarkButtonText(IDS_IOS_READING_LIST_MARK_ALL_BUTTON);
}
// Tests that only the "Cancel", "Delete" and "Mark Unread" buttons are showing
// when not editing.
- (void)testVisibleButtonsOnlyReadEntrySelected {
AddEntriesAndEnterEdit();
TapEntry(kReadTitle);
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarDeleteAllReadButtonID);
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarEditButtonID);
AssertToolbarButtonVisibleWithID(kReadingListToolbarDeleteButtonID);
AssertToolbarButtonVisibleWithID(kReadingListToolbarCancelButtonID);
AssertToolbarMarkButtonText(IDS_IOS_READING_LIST_MARK_UNREAD_BUTTON);
}
// Tests that the "Cancel", "Edit" and "Mark Unread" buttons are not visible
// after delete (using swipe).
- (void)testVisibleButtonsAfterSwipeDeletion {
// TODO(crbug.com/1046978): Test fails on iOS 13.3 iPad
if ([ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_SKIPPED(@"Test disabled on iOS 13.3 iPad and later.");
}
AddEntriesAndOpenReadingList();
[[[EarlGrey
selectElementWithMatcher:
grey_allOf(
chrome_test_util::StaticTextWithAccessibilityLabel(kReadTitle),
grey_ancestor(grey_kindOfClassName(@"TableViewURLCell")),
grey_sufficientlyVisible(), nil)]
usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 100)
onElementWithMatcher:grey_accessibilityID(kReadingListViewID)]
performAction:grey_swipeFastInDirection(kGREYDirectionLeft)];
[[EarlGrey
selectElementWithMatcher:grey_allOf(grey_text(@"Delete"),
grey_ancestor(grey_kindOfClassName(
@"UISwipeActionPullView")),
nil)] performAction:grey_tap()];
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarMarkButtonID);
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarCancelButtonID);
AssertToolbarButtonVisibleWithID(kReadingListToolbarEditButtonID);
}
// Tests that only the "Cancel", "Delete" and "Mark Read" buttons are showing
// when not editing.
- (void)testVisibleButtonsOnlyUnreadEntrySelected {
AddEntriesAndEnterEdit();
TapEntry(kUnreadTitle);
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarDeleteAllReadButtonID);
AssertToolbarButtonVisibleWithID(kReadingListToolbarDeleteButtonID);
AssertToolbarButtonVisibleWithID(kReadingListToolbarCancelButtonID);
AssertToolbarMarkButtonText(IDS_IOS_READING_LIST_MARK_READ_BUTTON);
}
// Tests that only the "Cancel", "Delete" and "Mark…" buttons are showing when
// not editing.
- (void)testVisibleButtonsMixedEntriesSelected {
AddEntriesAndEnterEdit();
TapEntry(kReadTitle);
TapEntry(kUnreadTitle);
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarDeleteAllReadButtonID);
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarEditButtonID);
AssertToolbarButtonVisibleWithID(kReadingListToolbarDeleteButtonID);
AssertToolbarButtonVisibleWithID(kReadingListToolbarCancelButtonID);
AssertToolbarMarkButtonText(IDS_IOS_READING_LIST_MARK_BUTTON);
}
// Tests the deletion of selected entries.
- (void)testDeleteEntries {
AddEntriesAndEnterEdit();
TapEntry(kReadTitle2);
AssertToolbarButtonVisibleWithID(kReadingListToolbarDeleteButtonID);
AssertToolbarButtonVisibleWithID(kReadingListToolbarCancelButtonID);
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarEditButtonID);
TapToolbarButtonWithID(kReadingListToolbarDeleteButtonID);
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarMarkButtonID);
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarDeleteButtonID);
AssertToolbarButtonNotVisibleWithID(kReadingListToolbarCancelButtonID);
AssertToolbarButtonVisibleWithID(kReadingListToolbarEditButtonID);
AssertEntryVisible(kReadTitle);
AssertEntryNotVisible(kReadTitle2);
AssertEntryVisible(kUnreadTitle);
AssertEntryVisible(kUnreadTitle2);
XCTAssertEqual([ReadingListAppInterface readEntriesCount],
static_cast<long>(kNumberReadEntries - 1));
XCTAssertEqual([ReadingListAppInterface unreadEntriesCount],
kNumberUnreadEntries);
TapToolbarButtonWithID(kReadingListToolbarEditButtonID);
TapEntry(kReadTitle);
TapToolbarButtonWithID(kReadingListToolbarDeleteButtonID);
[[EarlGrey
selectElementWithMatcher:grey_allOf(
grey_text(@"Read"),
grey_ancestor(grey_kindOfClassName(
@"_UITableViewHeaderFooterContentView")),
nil)] assertWithMatcher:grey_nil()];
TapToolbarButtonWithID(kReadingListToolbarEditButtonID);
TapEntry(kUnreadTitle);
TapEntry(kUnreadTitle2);
TapToolbarButtonWithID(kReadingListToolbarDeleteButtonID);
[[EarlGrey
selectElementWithMatcher:grey_allOf(
grey_text(@"Unread"),
grey_ancestor(grey_kindOfClassName(
@"_UITableViewHeaderFooterContentView")),
nil)] assertWithMatcher:grey_nil()];
}
// Tests the deletion of all read entries.
- (void)testDeleteAllReadEntries {
AddEntriesAndEnterEdit();
TapToolbarButtonWithID(kReadingListToolbarDeleteAllReadButtonID);
AssertEntryNotVisible(kReadTitle);
AssertEntryNotVisible(kReadTitle2);
AssertHeaderNotVisible(kReadHeader);
AssertEntryVisible(kUnreadTitle);
AssertEntryVisible(kUnreadTitle2);
XCTAssertEqual(0l, [ReadingListAppInterface readEntriesCount]);
XCTAssertEqual(kNumberUnreadEntries,
[ReadingListAppInterface unreadEntriesCount]);
}
// Marks all unread entries as read.
- (void)testMarkAllRead {
AddEntriesAndEnterEdit();
AssertToolbarMarkButtonText(IDS_IOS_READING_LIST_MARK_ALL_BUTTON);
TapToolbarButtonWithID(kReadingListToolbarMarkButtonID);
// Tap the action sheet.
TapContextMenuButtonWithA11yLabelID(
IDS_IOS_READING_LIST_MARK_ALL_READ_ACTION);
AssertHeaderNotVisible(kUnreadHeader);
AssertAllEntriesVisible();
XCTAssertEqual(static_cast<long>(kNumberUnreadEntries + kNumberReadEntries),
[ReadingListAppInterface readEntriesCount]);
XCTAssertEqual(0l, [ReadingListAppInterface unreadEntriesCount]);
}
// Marks all read entries as unread.
- (void)testMarkAllUnread {
AddEntriesAndEnterEdit();
AssertToolbarMarkButtonText(IDS_IOS_READING_LIST_MARK_ALL_BUTTON);
TapToolbarButtonWithID(kReadingListToolbarMarkButtonID);
// Tap the action sheet.
TapContextMenuButtonWithA11yLabelID(
IDS_IOS_READING_LIST_MARK_ALL_UNREAD_ACTION);
AssertHeaderNotVisible(kReadHeader);
AssertAllEntriesVisible();
XCTAssertEqual(static_cast<long>(kNumberUnreadEntries + kNumberReadEntries),
[ReadingListAppInterface unreadEntriesCount]);
XCTAssertEqual(0l, [ReadingListAppInterface readEntriesCount]);
}
// Marks all read entries as unread, when there is a lot of entries. This is to
// prevent crbug.com/1013708 from regressing.
- (void)testMarkAllUnreadLotOfEntry {
AddLotOfEntriesAndEnterEdit();
AssertToolbarMarkButtonText(IDS_IOS_READING_LIST_MARK_ALL_BUTTON);
TapToolbarButtonWithID(kReadingListToolbarMarkButtonID);
// Tap the action sheet.
TapContextMenuButtonWithA11yLabelID(
IDS_IOS_READING_LIST_MARK_ALL_UNREAD_ACTION);
AssertHeaderNotVisible(kReadHeader);
}
// Selects an unread entry and mark it as read.
- (void)testMarkEntriesRead {
AddEntriesAndEnterEdit();
TapEntry(kUnreadTitle);
AssertToolbarMarkButtonText(IDS_IOS_READING_LIST_MARK_READ_BUTTON);
TapToolbarButtonWithID(kReadingListToolbarMarkButtonID);
AssertAllEntriesVisible();
XCTAssertEqual(static_cast<long>(kNumberReadEntries + 1),
[ReadingListAppInterface readEntriesCount]);
XCTAssertEqual(static_cast<long>(kNumberUnreadEntries - 1),
[ReadingListAppInterface unreadEntriesCount]);
}
// Selects an read entry and mark it as unread.
- (void)testMarkEntriesUnread {
AddEntriesAndEnterEdit();
TapEntry(kReadTitle);
AssertToolbarMarkButtonText(IDS_IOS_READING_LIST_MARK_UNREAD_BUTTON);
TapToolbarButtonWithID(kReadingListToolbarMarkButtonID);
AssertAllEntriesVisible();
XCTAssertEqual(static_cast<long>(kNumberReadEntries - 1),
[ReadingListAppInterface readEntriesCount]);
XCTAssertEqual(static_cast<long>(kNumberUnreadEntries + 1),
[ReadingListAppInterface unreadEntriesCount]);
}
// Selects read and unread entries and mark them as unread.
- (void)testMarkMixedEntriesUnread {
AddEntriesAndEnterEdit();
TapEntry(kReadTitle);
TapEntry(kUnreadTitle);
AssertToolbarMarkButtonText(IDS_IOS_READING_LIST_MARK_BUTTON);
TapToolbarButtonWithID(kReadingListToolbarMarkButtonID);
// Tap the action sheet.
TapContextMenuButtonWithA11yLabelID(IDS_IOS_READING_LIST_MARK_UNREAD_BUTTON);
AssertAllEntriesVisible();
XCTAssertEqual(static_cast<long>(kNumberReadEntries - 1),
[ReadingListAppInterface readEntriesCount]);
XCTAssertEqual(static_cast<long>(kNumberUnreadEntries + 1),
[ReadingListAppInterface unreadEntriesCount]);
}
// Selects read and unread entries and mark them as read.
- (void)testMarkMixedEntriesRead {
AddEntriesAndEnterEdit();
TapEntry(kReadTitle);
TapEntry(kUnreadTitle);
AssertToolbarMarkButtonText(IDS_IOS_READING_LIST_MARK_BUTTON);
TapToolbarButtonWithID(kReadingListToolbarMarkButtonID);
// Tap the action sheet.
TapContextMenuButtonWithA11yLabelID(IDS_IOS_READING_LIST_MARK_READ_BUTTON);
AssertAllEntriesVisible();
XCTAssertEqual(static_cast<long>(kNumberReadEntries + 1),
[ReadingListAppInterface readEntriesCount]);
XCTAssertEqual(static_cast<long>(kNumberUnreadEntries - 1),
[ReadingListAppInterface unreadEntriesCount]);
}
// Tests that you can delete multiple read items in the Reading List without
// creating a crash (crbug.com/701956).
- (void)testDeleteMultipleItems {
// Add entries.
for (int i = 0; i < 11; i++) {
NSURL* url =
[NSURL URLWithString:[kReadURL stringByAppendingFormat:@"%d", i]];
NSString* title = [kReadURL stringByAppendingFormat:@"%d", i];
GREYAssertNil([ReadingListAppInterface addEntryWithURL:url
title:title
read:YES],
@"Unable to add Reading List entry.");
}
OpenReadingList();
// Make sure the Reading List view is not empty. Therefore, the illustration,
// title and subtitles shoud not be present.
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(
kTableViewIllustratedEmptyViewID)]
assertWithMatcher:grey_nil()];
id<GREYMatcher> noReadingListTitleMatcher = grey_allOf(
grey_text(l10n_util::GetNSString(IDS_IOS_READING_LIST_NO_ENTRIES_TITLE)),
grey_sufficientlyVisible(), nil);
[[EarlGrey selectElementWithMatcher:noReadingListTitleMatcher]
assertWithMatcher:grey_nil()];
id<GREYMatcher> noReadingListMessageMatcher = grey_allOf(
grey_text(
l10n_util::GetNSString(IDS_IOS_READING_LIST_NO_ENTRIES_MESSAGE)),
grey_sufficientlyVisible(), nil);
[[EarlGrey selectElementWithMatcher:noReadingListMessageMatcher]
assertWithMatcher:grey_nil()];
// Delete them from the Reading List view.
TapToolbarButtonWithID(kReadingListToolbarEditButtonID);
TapToolbarButtonWithID(kReadingListToolbarDeleteAllReadButtonID);
// Verify the background string is displayed.
[self verifyReadingListIsEmpty];
}
// Tests that the VC can be dismissed by swiping down.
- (void)testSwipeDownDismiss {
// TODO(crbug.com/1129589): Test disabled on iOS14 iPhones.
if (![ChromeEarlGrey isIPadIdiom]) {
EARL_GREY_TEST_DISABLED(@"Fails on iOS14 iPhones.");
}
GREYAssertNil(
[ReadingListAppInterface addEntryWithURL:[NSURL URLWithString:kUnreadURL]
title:kUnreadTitle
read:NO],
@"Unable to add Reading List entry.");
OpenReadingList();
// Check that the TableView is presented.
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(kReadingListViewID)]
assertWithMatcher:grey_notNil()];
// Swipe TableView down.
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(kReadingListViewID)]
performAction:grey_swipeFastInDirection(kGREYDirectionDown)];
// Check that the TableView has been dismissed.
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(kReadingListViewID)]
assertWithMatcher:grey_nil()];
}
// Tests the Copy Link context menu action for a reading list entry.
- (void)testContextMenuCopyLink {
AddEntriesAndOpenReadingList();
LongPressEntry(kReadTitle);
// Tap "Copy URL" and wait for the URL to be copied to the pasteboard.
[ChromeEarlGrey verifyCopyLinkActionWithText:kReadURL];
}
// Tests the Open in New Tab context menu action for a reading list entry.
- (void)testContextMenuOpenInNewTab {
GURL distillablePageURL(self.testServer->GetURL(kDistillableURL));
[self addURLToReadingList:distillablePageURL];
LongPressEntry(kDistillableTitle);
// Select "Open in New Tab" and confirm that new tab is opened with selected
// URL.
[ChromeEarlGrey
verifyOpenInNewTabActionWithURL:distillablePageURL.GetContent()];
}
// Tests display and selection of 'Open in New Incognito Tab' in a context menu
// on a history entry.
- (void)testContextMenuOpenInIncognito {
GURL distillablePageURL(self.testServer->GetURL(kDistillableURL));
[self addURLToReadingList:distillablePageURL];
LongPressEntry(kDistillableTitle);
// Select "Open in Incognito" and confirm that new tab is opened with selected
// URL.
[ChromeEarlGrey
verifyOpenInIncognitoActionWithURL:distillablePageURL.GetContent()];
}
// Tests the Mark as Read/Unread context menu action for a reading list entry.
- (void)testContextMenuMarkAsReadAndBack {
// TODO(crbug.com/1350732): Re-enable when flake fixed.
if (@available(iOS 16, *)) {
EARL_GREY_TEST_DISABLED(@"Test consistently failing on iOS16 iPhone 8.");
}
AddEntriesAndOpenReadingList();
AssertAllEntriesVisible();
XCTAssertEqual(static_cast<long>(kNumberReadEntries),
[ReadingListAppInterface readEntriesCount]);
XCTAssertEqual(static_cast<long>(kNumberUnreadEntries),
[ReadingListAppInterface unreadEntriesCount]);
// Mark an unread entry as read.
LongPressEntry(kUnreadTitle);
[[EarlGrey selectElementWithMatcher:ReadingListMarkAsReadButton()]
performAction:grey_tap()];
AssertAllEntriesVisible();
XCTAssertEqual(static_cast<long>(kNumberReadEntries + 1),
[ReadingListAppInterface readEntriesCount]);
XCTAssertEqual(static_cast<long>(kNumberUnreadEntries - 1),
[ReadingListAppInterface unreadEntriesCount]);
// Now mark it back as unread.
LongPressEntry(kUnreadTitle);
[[EarlGrey selectElementWithMatcher:ReadingListMarkAsUnreadButton()]
performAction:grey_tap()];
AssertAllEntriesVisible();
XCTAssertEqual(static_cast<long>(kNumberReadEntries),
[ReadingListAppInterface readEntriesCount]);
XCTAssertEqual(static_cast<long>(kNumberUnreadEntries),
[ReadingListAppInterface unreadEntriesCount]);
}
// Tests the Share context menu action for a reading list entry.
- (void)testContextMenuShare {
GURL distillablePageURL(self.testServer->GetURL(kDistillableURL));
[self addURLToReadingList:distillablePageURL];
LongPressEntry(kDistillableTitle);
[ChromeEarlGrey verifyShareActionWithURL:distillablePageURL
pageTitle:kDistillableTitle];
}
// Tests the Delete context menu action for a reading list entry.
- (void)testContextMenuDelete {
GURL distillablePageURL(self.testServer->GetURL(kDistillableURL));
[self addURLToReadingList:distillablePageURL];
LongPressEntry(kDistillableTitle);
[[EarlGrey selectElementWithMatcher:DeleteButton()] performAction:grey_tap()];
[self verifyReadingListIsEmpty];
}
#pragma mark - Multiwindow
// Tests the Open in New Window context menu action for a reading list entry.
// The test is flaky. https://crbug.com/1274099
- (void)DISABLED_testContextMenuOpenInNewWindow {
if (![ChromeEarlGrey areMultipleWindowsSupported])
EARL_GREY_TEST_DISABLED(@"Multiple windows can't be opened.");
GURL distillablePageURL(self.testServer->GetURL(kDistillableURL));
[self addURLToReadingList:distillablePageURL];
LongPressEntry(kDistillableTitle);
[ChromeEarlGrey verifyOpenInNewWindowActionWithContent:kContentToKeep];
}
#pragma mark - Helper Methods
- (void)verifyReadingListIsEmpty {
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(
kTableViewIllustratedEmptyViewID)]
assertWithMatcher:grey_notNil()];
// The dimiss animation takes 2 steps, and without the two waits below this
// test will flake.
[ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher:
grey_text(l10n_util::GetNSString(
IDS_IOS_READING_LIST_NO_ENTRIES_TITLE))];
[ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher:
grey_text(l10n_util::GetNSString(
IDS_IOS_READING_LIST_NO_ENTRIES_MESSAGE))];
}
- (void)addURLToReadingList:(const GURL&)URL {
[ReadingListAppInterface forceConnectionToWifi];
// Open http://potato
[ChromeEarlGrey loadURL:URL];
[ChromeEarlGrey waitForPageToFinishLoading];
AddCurrentPageToReadingList();
[ChromeEarlGrey closeCurrentTab];
[ChromeEarlGrey openNewTab];
OpenReadingList();
}
@end