blob: 09dcb12934243649a1d594430383915246fa69b5 [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 <EarlGrey/EarlGrey.h>
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#include "base/strings/sys_string_conversions.h"
#include "components/reading_list/ios/reading_list_model.h"
#include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
#import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
#include "ios/chrome/browser/ui/commands/ios_command_ids.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/app/chrome_test_util.h"
#import "ios/chrome/test/earl_grey/accessibility_util.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/testing/wait_util.h"
#import "ios/third_party/material_components_ios/src/components/Snackbar/src/MaterialSnackbar.h"
#import "ios/web/public/test/http_server.h"
#import "ios/web/public/test/http_server_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
const char kReadTitle[] = "foobar";
const char kReadURL[] = "http://readfoobar.com";
const char kUnreadTitle[] = "I am an unread entry";
const char kUnreadURL[] = "http://unreadfoobar.com";
const char kReadURL2[] = "http://kReadURL2.com";
const char kReadTitle2[] = "read item 2";
const char kUnreadTitle2[] = "I am another unread entry";
const char kUnreadURL2[] = "http://unreadfoobar2.com";
const size_t kNumberReadEntries = 2;
const size_t kNumberUnreadEntries = 2;
const CFTimeInterval kSnackbarAppearanceTimeout = 5;
const CFTimeInterval kSnackbarDisappearanceTimeout =
MDCSnackbarMessageDurationMax + 1;
const char kReadHeader[] = "Read";
const char kUnreadHeader[] = "Unread";
// Returns the reading list model.
ReadingListModel* GetReadingListModel() {
ReadingListModel* model =
ReadingListModelFactory::GetInstance()->GetForBrowserState(
chrome_test_util::GetOriginalBrowserState());
GREYAssert(testing::WaitUntilConditionOrTimeout(2,
^{
return model->loaded();
}),
@"Reading List model did not load");
return model;
}
// Asserts the |button_id| button is not visible.
void AssertButtonNotVisibleWithID(int button_id) {
[[EarlGrey
selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
button_id)]
assertWithMatcher:grey_notVisible()];
}
// Assert the |button_id| button is visible.
void AssertButtonVisibleWithID(int button_id) {
[[EarlGrey
selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
button_id)]
assertWithMatcher:grey_sufficientlyVisible()];
}
// Taps the |button_id| button.
void TapButtonWithID(int button_id) {
[[EarlGrey
selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
button_id)] performAction:grey_tap()];
}
// Taps the entry |title|.
void TapEntry(std::string title) {
[[EarlGrey selectElementWithMatcher:
grey_allOf(chrome_test_util::StaticTextWithAccessibilityLabel(
base::SysUTF8ToNSString(title)),
grey_sufficientlyVisible(), nil)]
performAction:grey_tap()];
}
// Asserts that the entry |title| is visible.
void AssertEntryVisible(std::string title) {
[[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
[[EarlGrey
selectElementWithMatcher:
grey_allOf(chrome_test_util::StaticTextWithAccessibilityLabel(
base::SysUTF8ToNSString(title)),
grey_ancestor(grey_kindOfClass([ReadingListCell class])),
nil)] assertWithMatcher:grey_sufficientlyVisible()];
}
// 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(std::string title) {
[[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
[[EarlGrey
selectElementWithMatcher:
grey_allOf(chrome_test_util::StaticTextWithAccessibilityLabel(
base::SysUTF8ToNSString(title)),
grey_ancestor(grey_kindOfClass([ReadingListCell class])),
nil)] assertWithMatcher:grey_notVisible()];
}
// Asserts |header| is visible.
void AssertHeaderNotVisible(std::string header) {
[[EarlGrey selectElementWithMatcher:chrome_test_util::
StaticTextWithAccessibilityLabel(
base::SysUTF8ToNSString(header))]
assertWithMatcher:grey_notVisible()];
}
// Opens the reading list menu using command line.
void OpenReadingList() {
GenericChromeCommand* command =
[[GenericChromeCommand alloc] initWithTag:IDC_SHOW_READING_LIST];
chrome_test_util::RunCommandWithActiveViewController(command);
}
// Adds a read and an unread entry to the model, opens the reading list menu and
// enter edit mode.
void AddEntriesAndEnterEdit() {
ReadingListModel* model = GetReadingListModel();
model->AddEntry(GURL(kReadURL), std::string(kReadTitle),
reading_list::ADDED_VIA_CURRENT_APP);
model->SetReadStatus(GURL(kReadURL), true);
model->AddEntry(GURL(kReadURL2), std::string(kReadTitle2),
reading_list::ADDED_VIA_CURRENT_APP);
model->SetReadStatus(GURL(kReadURL2), true);
model->AddEntry(GURL(kUnreadURL), std::string(kUnreadTitle),
reading_list::ADDED_VIA_CURRENT_APP);
model->AddEntry(GURL(kUnreadURL2), std::string(kUnreadTitle2),
reading_list::ADDED_VIA_CURRENT_APP);
OpenReadingList();
TapButtonWithID(IDS_IOS_READING_LIST_EDIT_BUTTON);
}
// Computes the number of read entries in the model.
size_t ModelReadSize(ReadingListModel* model) {
size_t size = 0;
for (const auto& url : model->Keys()) {
if (model->GetEntryByURL(url)->IsRead()) {
size++;
}
}
return size;
}
} // namespace
// Test class for the Reading List menu.
@interface ReadingListTestCase : ChromeTestCase
@end
@implementation ReadingListTestCase
- (void)setUp {
[super setUp];
ReadingListModel* model = GetReadingListModel();
for (const GURL& url : model->Keys())
model->RemoveEntryByURL(url);
}
// Tests that the Reading List view is accessible.
- (void)testAccessibility {
AddEntriesAndEnterEdit();
// In edit mode.
chrome_test_util::VerifyAccessibilityForCurrentScreen();
TapButtonWithID(IDS_IOS_READING_LIST_CANCEL_BUTTON);
chrome_test_util::VerifyAccessibilityForCurrentScreen();
}
// 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.
- (void)testSavingToReadingList {
// Setup a server serving a page at http://potato with the title "tomato".
std::map<GURL, std::string> responses;
const GURL regularPageURL = web::test::HttpServer::MakeUrl("http://potato");
responses[regularPageURL] = "<html><head><title>tomato</title></head></html>";
web::test::SetUpSimpleHttpServer(responses);
// Open http://potato
[ChromeEarlGrey loadURL:regularPageURL];
// Add the page to the reading list.
[ChromeEarlGreyUI openShareMenu];
TapButtonWithID(IDS_IOS_SHARE_MENU_READING_LIST_ACTION);
// Wait for the snackbar to appear.
id<GREYMatcher> snackbarMatcher =
chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_READING_LIST_SNACKBAR_MESSAGE);
ConditionBlock waitForAppearance = ^{
NSError* error = nil;
[[EarlGrey selectElementWithMatcher:snackbarMatcher]
assertWithMatcher:grey_notNil()
error:&error];
return error == nil;
};
// Wait for the snackbar to disappear.
GREYAssert(testing::WaitUntilConditionOrTimeout(kSnackbarAppearanceTimeout,
waitForAppearance),
@"Snackbar did not appear.");
ConditionBlock waitForDisappearance = ^{
NSError* error = nil;
[[EarlGrey selectElementWithMatcher:snackbarMatcher]
assertWithMatcher:grey_nil()
error:&error];
return error == nil;
};
GREYAssert(testing::WaitUntilConditionOrTimeout(kSnackbarDisappearanceTimeout,
waitForDisappearance),
@"Snackbar did not disappear.");
// Verify that a page with the title "tomato" is present in the reading list.
OpenReadingList();
AssertEntryVisible("tomato");
}
// Tests that only the "Edit" button is showing when not editing.
- (void)testVisibleButtonsNonEditingMode {
GetReadingListModel()->AddEntry(GURL(kUnreadURL), std::string(kUnreadTitle),
reading_list::ADDED_VIA_CURRENT_APP);
OpenReadingList();
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_DELETE_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_DELETE_ALL_READ_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_READ_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_UNREAD_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_ALL_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_CANCEL_BUTTON);
AssertButtonVisibleWithID(IDS_IOS_READING_LIST_EDIT_BUTTON);
}
// Tests that only the "Cancel", "Delete All Read" and "Mark All…" buttons are
// showing when not editing.
- (void)testVisibleButtonsEditingModeEmptySelection {
AddEntriesAndEnterEdit();
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_DELETE_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_READ_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_UNREAD_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_EDIT_BUTTON);
AssertButtonVisibleWithID(IDS_IOS_READING_LIST_DELETE_ALL_READ_BUTTON);
AssertButtonVisibleWithID(IDS_IOS_READING_LIST_MARK_ALL_BUTTON);
AssertButtonVisibleWithID(IDS_IOS_READING_LIST_CANCEL_BUTTON);
}
// Tests that only the "Cancel", "Delete" and "Mark Unread" buttons are showing
// when not editing.
- (void)testVisibleButtonsOnlyReadEntrySelected {
AddEntriesAndEnterEdit();
TapEntry(kReadTitle);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_DELETE_ALL_READ_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_READ_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_EDIT_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_ALL_BUTTON);
AssertButtonVisibleWithID(IDS_IOS_READING_LIST_MARK_UNREAD_BUTTON);
AssertButtonVisibleWithID(IDS_IOS_READING_LIST_DELETE_BUTTON);
AssertButtonVisibleWithID(IDS_IOS_READING_LIST_CANCEL_BUTTON);
}
// Tests that only the "Cancel", "Delete" and "Mark Read" buttons are showing
// when not editing.
- (void)testVisibleButtonsOnlyUnreadEntrySelected {
AddEntriesAndEnterEdit();
TapEntry(kUnreadTitle);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_DELETE_ALL_READ_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_UNREAD_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_EDIT_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_ALL_BUTTON);
AssertButtonVisibleWithID(IDS_IOS_READING_LIST_MARK_READ_BUTTON);
AssertButtonVisibleWithID(IDS_IOS_READING_LIST_DELETE_BUTTON);
AssertButtonVisibleWithID(IDS_IOS_READING_LIST_CANCEL_BUTTON);
}
// Tests that only the "Cancel", "Delete" and "Mark…" buttons are showing when
// not editing.
- (void)testVisibleButtonsMixedEntriesSelected {
AddEntriesAndEnterEdit();
TapEntry(kReadTitle);
TapEntry(kUnreadTitle);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_DELETE_ALL_READ_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_UNREAD_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_READ_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_EDIT_BUTTON);
AssertButtonNotVisibleWithID(IDS_IOS_READING_LIST_MARK_ALL_BUTTON);
AssertButtonVisibleWithID(IDS_IOS_READING_LIST_MARK_BUTTON);
AssertButtonVisibleWithID(IDS_IOS_READING_LIST_DELETE_BUTTON);
AssertButtonVisibleWithID(IDS_IOS_READING_LIST_CANCEL_BUTTON);
}
// Tests the deletion of selected entries.
- (void)testDeleteEntries {
AddEntriesAndEnterEdit();
TapEntry(kReadTitle2);
TapButtonWithID(IDS_IOS_READING_LIST_DELETE_BUTTON);
AssertEntryVisible(kReadTitle);
AssertEntryNotVisible(kReadTitle2);
AssertEntryVisible(kUnreadTitle);
AssertEntryVisible(kUnreadTitle2);
XCTAssertEqual(kNumberReadEntries - 1, ModelReadSize(GetReadingListModel()));
XCTAssertEqual(kNumberUnreadEntries, GetReadingListModel()->unread_size());
}
// Tests the deletion of all read entries.
- (void)testDeleteAllReadEntries {
AddEntriesAndEnterEdit();
TapButtonWithID(IDS_IOS_READING_LIST_DELETE_ALL_READ_BUTTON);
AssertEntryNotVisible(kReadTitle);
AssertEntryNotVisible(kReadTitle2);
AssertHeaderNotVisible(kReadHeader);
AssertEntryVisible(kUnreadTitle);
AssertEntryVisible(kUnreadTitle2);
XCTAssertEqual((size_t)0, ModelReadSize(GetReadingListModel()));
XCTAssertEqual(kNumberUnreadEntries, GetReadingListModel()->unread_size());
}
// Marks all unread entries as read.
- (void)testMarkAllRead {
AddEntriesAndEnterEdit();
TapButtonWithID(IDS_IOS_READING_LIST_MARK_ALL_BUTTON);
// Tap the action sheet.
TapButtonWithID(IDS_IOS_READING_LIST_MARK_ALL_READ_ACTION);
AssertHeaderNotVisible(kUnreadHeader);
AssertAllEntriesVisible();
XCTAssertEqual(kNumberUnreadEntries + kNumberReadEntries,
ModelReadSize(GetReadingListModel()));
XCTAssertEqual((size_t)0, GetReadingListModel()->unread_size());
}
// Marks all read entries as unread.
- (void)testMarkAllUnread {
AddEntriesAndEnterEdit();
TapButtonWithID(IDS_IOS_READING_LIST_MARK_ALL_BUTTON);
// Tap the action sheet.
TapButtonWithID(IDS_IOS_READING_LIST_MARK_ALL_UNREAD_ACTION);
AssertHeaderNotVisible(kReadHeader);
AssertAllEntriesVisible();
XCTAssertEqual(kNumberUnreadEntries + kNumberReadEntries,
GetReadingListModel()->unread_size());
XCTAssertEqual((size_t)0, ModelReadSize(GetReadingListModel()));
}
// Selects an unread entry and mark it as read.
- (void)testMarkEntriesRead {
AddEntriesAndEnterEdit();
TapEntry(kUnreadTitle);
TapButtonWithID(IDS_IOS_READING_LIST_MARK_READ_BUTTON);
AssertAllEntriesVisible();
XCTAssertEqual(kNumberReadEntries + 1, ModelReadSize(GetReadingListModel()));
XCTAssertEqual(kNumberUnreadEntries - 1,
GetReadingListModel()->unread_size());
}
// Selects an read entry and mark it as unread.
- (void)testMarkEntriesUnread {
AddEntriesAndEnterEdit();
TapEntry(kReadTitle);
TapButtonWithID(IDS_IOS_READING_LIST_MARK_UNREAD_BUTTON);
AssertAllEntriesVisible();
XCTAssertEqual(kNumberReadEntries - 1, ModelReadSize(GetReadingListModel()));
XCTAssertEqual(kNumberUnreadEntries + 1,
GetReadingListModel()->unread_size());
}
// Selects read and unread entries and mark them as unread.
- (void)testMarkMixedEntriesUnread {
AddEntriesAndEnterEdit();
TapEntry(kReadTitle);
TapEntry(kUnreadTitle);
TapButtonWithID(IDS_IOS_READING_LIST_MARK_BUTTON);
// Tap the action sheet.
TapButtonWithID(IDS_IOS_READING_LIST_MARK_UNREAD_BUTTON);
AssertAllEntriesVisible();
XCTAssertEqual(kNumberReadEntries - 1, ModelReadSize(GetReadingListModel()));
XCTAssertEqual(kNumberUnreadEntries + 1,
GetReadingListModel()->unread_size());
}
// Selects read and unread entries and mark them as read.
- (void)testMarkMixedEntriesRead {
AddEntriesAndEnterEdit();
TapEntry(kReadTitle);
TapEntry(kUnreadTitle);
TapButtonWithID(IDS_IOS_READING_LIST_MARK_BUTTON);
// Tap the action sheet.
TapButtonWithID(IDS_IOS_READING_LIST_MARK_READ_BUTTON);
AssertAllEntriesVisible();
XCTAssertEqual(kNumberReadEntries + 1, ModelReadSize(GetReadingListModel()));
XCTAssertEqual(kNumberUnreadEntries - 1,
GetReadingListModel()->unread_size());
}
@end