// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_view.h"
#import <Foundation/Foundation.h>
#import "base/test/task_environment.h"
#import "ios/chrome/browser/safety_check/model/ios_chrome_safety_check_manager_constants.h"
#import "ios/chrome/browser/ui/content_suggestions/safety_check/constants.h"
#import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_item_view.h"
#import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_state.h"
#import "testing/platform_test.h"
// Tests the SafetyCheckView and subviews.
class SafetyCheckViewTest : public PlatformTest {
public:
SafetyCheckViewTest() {
_superview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
_window = [[UIWindow alloc] init];
[_window addSubview:_superview];
UIView.animationsEnabled = NO;
}
// Iterates a view's subviews recursively, calling the block with each one.
void IterateSubviews(UIView* view, bool (^block)(UIView* subview)) {
for (UIView* subview in view.subviews) {
bool should_break = block(subview);
if (should_break) {
break;
}
IterateSubviews(subview, block);
}
}
// Searches recursively through subviews to find one with the given
// `accessibility_id`.
UIView* FindSubview(NSString* accessibility_id) {
__block UIView* found = nil;
IterateSubviews(_superview, ^bool(UIView* subview) {
if (subview.accessibilityIdentifier == accessibility_id) {
found = subview;
return true;
}
return false;
});
return found;
}
// Expects a subview with the given `accessibility_id` to either exist or
// or not.
void ExpectSubview(NSString* accessibility_id, bool exists) {
UIView* subview = FindSubview(accessibility_id);
if (exists) {
EXPECT_NE(subview, nil);
} else {
EXPECT_EQ(subview, nil);
}
}
// Returns a count of subviews of the given `klass`.
int CountSubviewsWithClass(UIView* view, Class klass) {
__block int count = 0;
IterateSubviews(view, ^bool(UIView* subview) {
if ([subview class] == klass) {
count++;
}
return false;
});
return count;
}
// Expects `count` subviews of the given `klass` to exist.
void ExpectSubviewCount(int count, Class klass) {
int actual_count = CountSubviewsWithClass(_superview, klass);
EXPECT_EQ(actual_count, count);
}
protected:
base::test::SingleThreadTaskEnvironment _task_environment;
UIWindow* _window;
UIView* _superview;
};
// Tests that the module can be initialized, create subviews, and that the
// default module state is displayed.
TEST_F(SafetyCheckViewTest, DisplaysModuleWithDefaultState) {
SafetyCheckState* state = [[SafetyCheckState alloc]
initWithUpdateChromeState:UpdateChromeSafetyCheckState::kDefault
passwordState:PasswordSafetyCheckState::kDefault
safeBrowsingState:SafeBrowsingSafetyCheckState::kDefault
runningState:RunningSafetyCheckState::kDefault];
SafetyCheckView* view = [[SafetyCheckView alloc] initWithState:state
contentViewDelegate:nil];
[_superview addSubview:view];
ExpectSubviewCount(1, [SafetyCheckView class]);
// It should initially display one item, i.e. the hero-cell default layout
// item.
ExpectSubviewCount(1, [SafetyCheckItemView class]);
ExpectSubview(safety_check::kSafetyCheckViewID, true);
ExpectSubview(safety_check::kDefaultItemID, true);
ExpectSubview(safety_check::kAllSafeItemID, false);
ExpectSubview(safety_check::kRunningItemID, false);
ExpectSubview(safety_check::kUpdateChromeItemID, false);
ExpectSubview(safety_check::kPasswordItemID, false);
ExpectSubview(safety_check::kSafeBrowsingItemID, false);
}
// Tests that the module can be initialized, create subviews, and that the
// running module state is displayed.
TEST_F(SafetyCheckViewTest, DisplaysModuleWithRunningState) {
SafetyCheckState* state = [[SafetyCheckState alloc]
initWithUpdateChromeState:UpdateChromeSafetyCheckState::kDefault
passwordState:PasswordSafetyCheckState::kDefault
safeBrowsingState:SafeBrowsingSafetyCheckState::kDefault
runningState:RunningSafetyCheckState::kRunning];
SafetyCheckView* view = [[SafetyCheckView alloc] initWithState:state
contentViewDelegate:nil];
[_superview addSubview:view];
ExpectSubviewCount(1, [SafetyCheckView class]);
// It should initially display one item, i.e. the hero-cell default layout
// item.
ExpectSubviewCount(1, [SafetyCheckItemView class]);
ExpectSubview(safety_check::kSafetyCheckViewID, true);
ExpectSubview(safety_check::kRunningItemID, true);
ExpectSubview(safety_check::kDefaultItemID, false);
ExpectSubview(safety_check::kAllSafeItemID, false);
ExpectSubview(safety_check::kUpdateChromeItemID, false);
ExpectSubview(safety_check::kPasswordItemID, false);
ExpectSubview(safety_check::kSafeBrowsingItemID, false);
}
// Tests that the module can be initialized, create subviews, and that the
// module state for a single check issue (passwords) is displayed.
TEST_F(SafetyCheckViewTest, DisplaysModuleWithSinglePasswordsIssue) {
SafetyCheckState* state = [[SafetyCheckState alloc]
initWithUpdateChromeState:UpdateChromeSafetyCheckState::kDefault
passwordState:PasswordSafetyCheckState::
kUnmutedCompromisedPasswords
safeBrowsingState:SafeBrowsingSafetyCheckState::kDefault
runningState:RunningSafetyCheckState::kDefault];
SafetyCheckView* view = [[SafetyCheckView alloc] initWithState:state
contentViewDelegate:nil];
[_superview addSubview:view];
ExpectSubviewCount(1, [SafetyCheckView class]);
// It should initially display one item, i.e. the hero-cell default layout
// item.
ExpectSubviewCount(1, [SafetyCheckItemView class]);
ExpectSubview(safety_check::kSafetyCheckViewID, true);
ExpectSubview(safety_check::kPasswordItemID, true);
ExpectSubview(safety_check::kRunningItemID, false);
ExpectSubview(safety_check::kDefaultItemID, false);
ExpectSubview(safety_check::kAllSafeItemID, false);
ExpectSubview(safety_check::kUpdateChromeItemID, false);
ExpectSubview(safety_check::kSafeBrowsingItemID, false);
}
// Tests that the module can be initialized, create subviews, and that the
// module state for a single check issue (safe browsing) is displayed.
TEST_F(SafetyCheckViewTest, DisplaysModuleWithSingleSafeBrowsingIssue) {
SafetyCheckState* state = [[SafetyCheckState alloc]
initWithUpdateChromeState:UpdateChromeSafetyCheckState::kDefault
passwordState:PasswordSafetyCheckState::kDefault
safeBrowsingState:SafeBrowsingSafetyCheckState::kUnsafe
runningState:RunningSafetyCheckState::kDefault];
SafetyCheckView* view = [[SafetyCheckView alloc] initWithState:state
contentViewDelegate:nil];
[_superview addSubview:view];
ExpectSubviewCount(1, [SafetyCheckView class]);
// It should initially display one item, i.e. the hero-cell default layout
// item.
ExpectSubviewCount(1, [SafetyCheckItemView class]);
ExpectSubview(safety_check::kSafetyCheckViewID, true);
ExpectSubview(safety_check::kSafeBrowsingItemID, true);
ExpectSubview(safety_check::kRunningItemID, false);
ExpectSubview(safety_check::kDefaultItemID, false);
ExpectSubview(safety_check::kAllSafeItemID, false);
ExpectSubview(safety_check::kUpdateChromeItemID, false);
ExpectSubview(safety_check::kPasswordItemID, false);
}
// Tests that the module can be initialized, create subviews, and that the
// module state for a single check issue (update chrome) is displayed.
TEST_F(SafetyCheckViewTest, DisplaysModuleWithSingleUpdateChromeIssue) {
SafetyCheckState* state = [[SafetyCheckState alloc]
initWithUpdateChromeState:UpdateChromeSafetyCheckState::kOutOfDate
passwordState:PasswordSafetyCheckState::kDefault
safeBrowsingState:SafeBrowsingSafetyCheckState::kDefault
runningState:RunningSafetyCheckState::kDefault];
SafetyCheckView* view = [[SafetyCheckView alloc] initWithState:state
contentViewDelegate:nil];
[_superview addSubview:view];
ExpectSubviewCount(1, [SafetyCheckView class]);
// It should initially display one item, i.e. the hero-cell default layout
// item.
ExpectSubviewCount(1, [SafetyCheckItemView class]);
ExpectSubview(safety_check::kSafetyCheckViewID, true);
ExpectSubview(safety_check::kUpdateChromeItemID, true);
ExpectSubview(safety_check::kRunningItemID, false);
ExpectSubview(safety_check::kDefaultItemID, false);
ExpectSubview(safety_check::kAllSafeItemID, false);
ExpectSubview(safety_check::kSafeBrowsingItemID, false);
ExpectSubview(safety_check::kPasswordItemID, false);
}
// Tests that the module can be initialized, create subviews, and that the
// module state for a multiple check issues (update chrome & passwords) are
// displayed.
TEST_F(SafetyCheckViewTest, DisplaysModuleWithPasswordAndUpdateChromeIssues) {
SafetyCheckState* state = [[SafetyCheckState alloc]
initWithUpdateChromeState:UpdateChromeSafetyCheckState::kOutOfDate
passwordState:PasswordSafetyCheckState::
kUnmutedCompromisedPasswords
safeBrowsingState:SafeBrowsingSafetyCheckState::kDefault
runningState:RunningSafetyCheckState::kDefault];
SafetyCheckView* view = [[SafetyCheckView alloc] initWithState:state
contentViewDelegate:nil];
[_superview addSubview:view];
ExpectSubviewCount(1, [SafetyCheckView class]);
// It should initially display two items, i.e. the multi-row layout.
ExpectSubviewCount(2, [SafetyCheckItemView class]);
ExpectSubview(safety_check::kSafetyCheckViewID, true);
ExpectSubview(safety_check::kUpdateChromeItemID, true);
ExpectSubview(safety_check::kPasswordItemID, true);
ExpectSubview(safety_check::kRunningItemID, false);
ExpectSubview(safety_check::kDefaultItemID, false);
ExpectSubview(safety_check::kAllSafeItemID, false);
ExpectSubview(safety_check::kSafeBrowsingItemID, false);
}
// Tests that the module can be initialized, create subviews, and that the
// module state for a multiple check issues (passwords & safe browsing) are
// displayed.
TEST_F(SafetyCheckViewTest, DisplaysModuleWithPasswordAndSafeBrowsingIssues) {
SafetyCheckState* state = [[SafetyCheckState alloc]
initWithUpdateChromeState:UpdateChromeSafetyCheckState::kDefault
passwordState:PasswordSafetyCheckState::
kUnmutedCompromisedPasswords
safeBrowsingState:SafeBrowsingSafetyCheckState::kUnsafe
runningState:RunningSafetyCheckState::kDefault];
SafetyCheckView* view = [[SafetyCheckView alloc] initWithState:state
contentViewDelegate:nil];
[_superview addSubview:view];
ExpectSubviewCount(1, [SafetyCheckView class]);
// It should initially display two items, i.e. the multi-row layout.
ExpectSubviewCount(2, [SafetyCheckItemView class]);
ExpectSubview(safety_check::kSafetyCheckViewID, true);
ExpectSubview(safety_check::kPasswordItemID, true);
ExpectSubview(safety_check::kSafeBrowsingItemID, true);
ExpectSubview(safety_check::kUpdateChromeItemID, false);
ExpectSubview(safety_check::kRunningItemID, false);
ExpectSubview(safety_check::kDefaultItemID, false);
ExpectSubview(safety_check::kAllSafeItemID, false);
}
// Tests that the module can be initialized, create subviews, and that the
// module state for a multiple check issues (update chrome & safe browsing) are
// displayed.
TEST_F(SafetyCheckViewTest,
DisplaysModuleWithUpdateChromeAndSafeBrowsingIssues) {
SafetyCheckState* state = [[SafetyCheckState alloc]
initWithUpdateChromeState:UpdateChromeSafetyCheckState::kOutOfDate
passwordState:PasswordSafetyCheckState::kDefault
safeBrowsingState:SafeBrowsingSafetyCheckState::kUnsafe
runningState:RunningSafetyCheckState::kDefault];
SafetyCheckView* view = [[SafetyCheckView alloc] initWithState:state
contentViewDelegate:nil];
[_superview addSubview:view];
ExpectSubviewCount(1, [SafetyCheckView class]);
// It should initially display two items, i.e. the multi-row layout.
ExpectSubviewCount(2, [SafetyCheckItemView class]);
ExpectSubview(safety_check::kSafetyCheckViewID, true);
ExpectSubview(safety_check::kUpdateChromeItemID, true);
ExpectSubview(safety_check::kSafeBrowsingItemID, true);
ExpectSubview(safety_check::kPasswordItemID, false);
ExpectSubview(safety_check::kRunningItemID, false);
ExpectSubview(safety_check::kDefaultItemID, false);
ExpectSubview(safety_check::kAllSafeItemID, false);
}