| // Copyright 2017 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/bubble/ui_bundled/bubble_view.h" |
| |
| #import "base/apple/foundation_util.h" |
| #import "base/ios/ios_util.h" |
| #import "ios/chrome/browser/bubble/ui_bundled/bubble_constants.h" |
| #import "ios/chrome/browser/bubble/ui_bundled/bubble_unittest_util.h" |
| #import "testing/gtest/include/gtest/gtest.h" |
| #import "testing/gtest_mac.h" |
| #import "testing/platform_test.h" |
| |
| @interface BubbleViewDelegateTest : NSObject <BubbleViewDelegate> |
| |
| - (instancetype)init; |
| |
| @property(nonatomic) int tapCounter; |
| |
| @end |
| |
| @implementation BubbleViewDelegateTest |
| |
| - (instancetype)init { |
| self = [super init]; |
| if (self) { |
| _tapCounter = 0; |
| } |
| return self; |
| } |
| |
| - (void)didTapCloseButton { |
| _tapCounter += 1; |
| } |
| |
| - (void)didTapSnoozeButton { |
| _tapCounter += 1; |
| } |
| |
| - (void)didTapNextButton { |
| _tapCounter += 1; |
| } |
| |
| @end |
| |
| // Fixture to test BubbleView. |
| class BubbleViewTest : public PlatformTest { |
| public: |
| BubbleViewTest() |
| : max_size_(CGSizeMake(500.0f, 500.0f)), |
| arrow_direction_(BubbleArrowDirectionUp), |
| alignment_(BubbleAlignmentCenter), |
| alignment_offset_(35.0f), |
| short_text_(@"I"), |
| long_text_(@"Lorem ipsum dolor sit amet, consectetur adipiscing elit."), |
| text_alignment_(NSTextAlignmentNatural) {} |
| |
| protected: |
| // The maximum size of the bubble. |
| const CGSize max_size_; |
| // The direction that the bubble's arrow points. |
| const BubbleArrowDirection arrow_direction_; |
| // The alignment of the bubble's arrow relative to the rest of the bubble. |
| const BubbleAlignment alignment_; |
| // Distance between the arrow's centerX and the (leading or trailing) edge of |
| // the bubble, depending on the BubbleAlignment. If BubbleAlignment is center, |
| // then `alignmentOffset` is ignored. |
| const CGFloat alignment_offset_; |
| // Text that is shorter than the minimum line width. |
| NSString* short_text_; |
| // Text that is longer than the maximum line width. It should wrap onto |
| // multiple lines. |
| NSString* long_text_; |
| // Text alignment for bubble's title, text and snooze button. |
| NSTextAlignment text_alignment_; |
| }; |
| |
| // Test `sizeThatFits` given short text. |
| TEST_F(BubbleViewTest, BubbleSizeShortText) { |
| BubbleView* bubble = [[BubbleView alloc] initWithText:short_text_ |
| arrowDirection:arrow_direction_ |
| alignment:alignment_]; |
| CGSize bubble_size = [bubble sizeThatFits:max_size_]; |
| // Since the label is shorter than the minimum line width, expect the bubble |
| // to be the minimum width and accommodate one line of text. |
| EXPECT_NEAR(68.0f, bubble_size.width, 1.0f); |
| EXPECT_NEAR(67.0f, bubble_size.height, 1.0f); |
| } |
| |
| // Test that the accessibility label matches the display text. |
| TEST_F(BubbleViewTest, Accessibility) { |
| BubbleView* bubble = [[BubbleView alloc] initWithText:long_text_ |
| arrowDirection:arrow_direction_ |
| alignment:alignment_]; |
| UIView* superview = [[UIView alloc] initWithFrame:CGRectZero]; |
| // Add the bubble view to the view hierarchy. |
| [superview addSubview:bubble]; |
| EXPECT_NSEQ(long_text_, bubble.accessibilityValue); |
| } |
| |
| // Tests that the close button is not showed when the option is set to hidden. |
| TEST_F(BubbleViewTest, CloseButtonIsNotPresent) { |
| BubbleView* bubble = |
| [[BubbleView alloc] initWithText:long_text_ |
| arrowDirection:arrow_direction_ |
| alignment:alignment_ |
| showsCloseButton:NO |
| title:nil |
| showsSnoozeButton:NO |
| showsNextButton:NO |
| page:BubblePageControlPageNone |
| textAlignment:text_alignment_ |
| delegate:nil]; |
| UIView* superview = [[UIView alloc] initWithFrame:CGRectZero]; |
| [superview addSubview:bubble]; |
| UIButton* close_button = GetCloseButtonFromBubbleView(bubble); |
| ASSERT_FALSE(close_button); |
| } |
| |
| // Tests the close button action and its presence. |
| TEST_F(BubbleViewTest, CloseButtonActionAndPresent) { |
| BubbleViewDelegateTest* delegate = [[BubbleViewDelegateTest alloc] init]; |
| BubbleView* bubble = |
| [[BubbleView alloc] initWithText:long_text_ |
| arrowDirection:arrow_direction_ |
| alignment:alignment_ |
| showsCloseButton:YES |
| title:nil |
| showsSnoozeButton:NO |
| showsNextButton:NO |
| page:BubblePageControlPageNone |
| textAlignment:text_alignment_ |
| delegate:delegate]; |
| UIView* superview = [[UIView alloc] initWithFrame:CGRectZero]; |
| [superview addSubview:bubble]; |
| UIButton* close_button = GetCloseButtonFromBubbleView(bubble); |
| ASSERT_TRUE(close_button); |
| // Tests close button action. |
| [close_button sendActionsForControlEvents:UIControlEventTouchUpInside]; |
| EXPECT_EQ(delegate.tapCounter, 1); |
| } |
| |
| // Tests that the title is not showed when the option is set to hidden. |
| TEST_F(BubbleViewTest, TitleIsNotPresent) { |
| BubbleView* bubble = [[BubbleView alloc] initWithText:long_text_ |
| arrowDirection:arrow_direction_ |
| alignment:alignment_]; |
| UIView* superview = [[UIView alloc] initWithFrame:CGRectZero]; |
| [superview addSubview:bubble]; |
| UILabel* title_label = GetTitleLabelFromBubbleView(bubble); |
| ASSERT_FALSE(title_label); |
| } |
| |
| // Tests that the title is present and correct. |
| TEST_F(BubbleViewTest, TitleIsPresentAndCorrect) { |
| BubbleView* bubble = |
| [[BubbleView alloc] initWithText:long_text_ |
| arrowDirection:arrow_direction_ |
| alignment:alignment_ |
| showsCloseButton:NO |
| title:short_text_ |
| showsSnoozeButton:NO |
| showsNextButton:NO |
| page:BubblePageControlPageNone |
| textAlignment:text_alignment_ |
| delegate:nil]; |
| UIView* superview = [[UIView alloc] initWithFrame:CGRectZero]; |
| [superview addSubview:bubble]; |
| UILabel* title_label = GetTitleLabelFromBubbleView(bubble); |
| ASSERT_TRUE(title_label); |
| ASSERT_EQ(title_label.text, short_text_); |
| } |
| |
| // Tests that the title is aligned correctly. |
| TEST_F(BubbleViewTest, TitleIsAligned) { |
| BubbleView* bubble = |
| [[BubbleView alloc] initWithText:long_text_ |
| arrowDirection:arrow_direction_ |
| alignment:alignment_ |
| showsCloseButton:NO |
| title:short_text_ |
| showsSnoozeButton:NO |
| showsNextButton:NO |
| page:BubblePageControlPageNone |
| textAlignment:NSTextAlignmentNatural |
| delegate:nil]; |
| UIView* superview = [[UIView alloc] initWithFrame:CGRectZero]; |
| [superview addSubview:bubble]; |
| UILabel* title_label = GetTitleLabelFromBubbleView(bubble); |
| ASSERT_TRUE(title_label); |
| ASSERT_EQ(title_label.textAlignment, NSTextAlignmentNatural); |
| } |
| |
| // Tests that the snooze button is not showed when the option is set to hidden. |
| TEST_F(BubbleViewTest, SnoozeButtonIsNotPresent) { |
| BubbleView* bubble = |
| [[BubbleView alloc] initWithText:long_text_ |
| arrowDirection:arrow_direction_ |
| alignment:alignment_ |
| showsCloseButton:NO |
| title:nil |
| showsSnoozeButton:NO |
| showsNextButton:NO |
| page:BubblePageControlPageNone |
| textAlignment:text_alignment_ |
| delegate:nil]; |
| UIView* superview = [[UIView alloc] initWithFrame:CGRectZero]; |
| [superview addSubview:bubble]; |
| UIButton* snooze_button = GetSnoozeButtonFromBubbleView(bubble); |
| ASSERT_FALSE(snooze_button); |
| } |
| |
| // Tests the snooze button action and its presence. |
| TEST_F(BubbleViewTest, SnoozeButtonActionAndPresent) { |
| BubbleViewDelegateTest* delegate = [[BubbleViewDelegateTest alloc] init]; |
| BubbleView* bubble = |
| [[BubbleView alloc] initWithText:long_text_ |
| arrowDirection:arrow_direction_ |
| alignment:alignment_ |
| showsCloseButton:NO |
| title:nil |
| showsSnoozeButton:YES |
| showsNextButton:NO |
| page:BubblePageControlPageNone |
| textAlignment:text_alignment_ |
| delegate:delegate]; |
| UIView* superview = [[UIView alloc] initWithFrame:CGRectZero]; |
| [superview addSubview:bubble]; |
| UIButton* snooze_button = GetSnoozeButtonFromBubbleView(bubble); |
| ASSERT_TRUE(snooze_button); |
| // Tests snooze button action. |
| [snooze_button sendActionsForControlEvents:UIControlEventTouchUpInside]; |
| EXPECT_EQ(delegate.tapCounter, 1); |
| } |
| |
| // Tests the arrow view is aligned properly with BubbleAlignmentTopOrLeading. |
| TEST_F(BubbleViewTest, ArrowViewLeadingAligned) { |
| BubbleView* bubble = |
| [[BubbleView alloc] initWithText:long_text_ |
| arrowDirection:arrow_direction_ |
| alignment:BubbleAlignmentTopOrLeading]; |
| bubble.alignmentOffset = alignment_offset_; |
| UIView* superview = [[UIView alloc] initWithFrame:CGRectZero]; |
| [superview addSubview:bubble]; |
| [bubble layoutIfNeeded]; |
| UIView* arrow_view = GetArrowViewFromBubbleView(bubble); |
| ASSERT_TRUE(arrow_view); |
| // The center of the arrow must be at a distance of `alignment_offset_` to the |
| // leading edge of the bubble. |
| EXPECT_EQ(CGRectGetMidX(arrow_view.frame), alignment_offset_); |
| } |
| |
| // Tests the arrow view is aligned properly with BubbleAlignmentCenter. |
| TEST_F(BubbleViewTest, ArrowViewCenterAligned) { |
| BubbleView* bubble = [[BubbleView alloc] initWithText:long_text_ |
| arrowDirection:arrow_direction_ |
| alignment:BubbleAlignmentCenter]; |
| bubble.alignmentOffset = alignment_offset_; |
| UIView* superview = [[UIView alloc] initWithFrame:CGRectZero]; |
| [superview addSubview:bubble]; |
| [bubble layoutIfNeeded]; |
| UIView* arrow_view = GetArrowViewFromBubbleView(bubble); |
| ASSERT_TRUE(arrow_view); |
| // `alignmentOffset` should be ignored with BubbleAlignmentCenter. |
| EXPECT_EQ(CGRectGetMidX(arrow_view.frame), CGRectGetMidX(bubble.frame)); |
| } |
| |
| // Tests the arrow view is aligned properly with |
| // BubbleAlignmentBottomOrTrailing. |
| TEST_F(BubbleViewTest, ArrowViewTrailingAligned) { |
| BubbleView* bubble = |
| [[BubbleView alloc] initWithText:long_text_ |
| arrowDirection:arrow_direction_ |
| alignment:BubbleAlignmentBottomOrTrailing]; |
| bubble.alignmentOffset = alignment_offset_; |
| UIView* superview = [[UIView alloc] initWithFrame:CGRectZero]; |
| [superview addSubview:bubble]; |
| [bubble layoutIfNeeded]; |
| UIView* arrow_view = GetArrowViewFromBubbleView(bubble); |
| ASSERT_TRUE(arrow_view); |
| // The center of the arrow must be at a distance of `alignment_offset_` to the |
| // trailing edge of the bubble. |
| EXPECT_EQ(CGRectGetMidX(arrow_view.frame), |
| bubble.frame.size.width - alignment_offset_); |
| } |
| |
| // Tests the next button action and its presence. |
| TEST_F(BubbleViewTest, NextButtonActionAndPresent) { |
| BubbleViewDelegateTest* delegate = [[BubbleViewDelegateTest alloc] init]; |
| BubbleView* bubble = |
| [[BubbleView alloc] initWithText:long_text_ |
| arrowDirection:arrow_direction_ |
| alignment:alignment_ |
| showsCloseButton:NO |
| title:nil |
| showsSnoozeButton:NO |
| showsNextButton:YES |
| page:BubblePageControlPageNone |
| textAlignment:text_alignment_ |
| delegate:delegate]; |
| UIView* superview = [[UIView alloc] initWithFrame:CGRectZero]; |
| [superview addSubview:bubble]; |
| UIButton* next_button = GetNextButtonFromBubbleView(bubble); |
| ASSERT_TRUE(next_button); |
| // Tests snooze button action. |
| [next_button sendActionsForControlEvents:UIControlEventTouchUpInside]; |
| EXPECT_EQ(delegate.tapCounter, 1); |
| } |
| |
| // Tests the optional and its presence. |
| TEST_F(BubbleViewTest, HighlightedPagePresent) { |
| BubbleViewDelegateTest* delegate = [[BubbleViewDelegateTest alloc] init]; |
| BubbleView* bubble = |
| [[BubbleView alloc] initWithText:long_text_ |
| arrowDirection:arrow_direction_ |
| alignment:alignment_ |
| showsCloseButton:NO |
| title:nil |
| showsSnoozeButton:NO |
| showsNextButton:YES |
| page:BubblePageControlPageThird |
| textAlignment:text_alignment_ |
| delegate:delegate]; |
| UIView* superview = [[UIView alloc] initWithFrame:CGRectZero]; |
| [superview addSubview:bubble]; |
| UIStackView* highlighted_page = GetPageControlPageBubbleView(bubble); |
| ASSERT_TRUE(highlighted_page); |
| } |