Index: ios/chrome/browser/ui/omnibox/omnibox_text_field_ios_unittest.mm |
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios_unittest.mm b/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios_unittest.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4b0eddd49942ef1dcfa4388d09ca5e786695a504 |
--- /dev/null |
+++ b/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios_unittest.mm |
@@ -0,0 +1,274 @@ |
+// Copyright 2012 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 "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h" |
+ |
+#include "base/command_line.h" |
+#include "base/files/file_path.h" |
+#include "base/files/file_util.h" |
+#include "base/mac/scoped_nsobject.h" |
+#include "base/path_service.h" |
+#include "base/strings/string_split.h" |
+#include "base/strings/sys_string_conversions.h" |
+#include "ios/chrome/browser/chrome_paths.h" |
+#include "ios/chrome/grit/ios_strings.h" |
+#include "testing/gtest_mac.h" |
+#include "testing/platform_test.h" |
+#include "ui/base/l10n/l10n_util_mac.h" |
+ |
+// A category for making existing methods visible for use in these tests. |
+@interface OmniboxTextFieldIOS (VisibleForTesting) |
+- (CGRect)rectForDrawTextInRect:(CGRect)rect; |
+@end |
+ |
+namespace { |
+ |
+class OmniboxTextFieldIOSTest : public PlatformTest { |
+ protected: |
+ void SetUp() override { |
+ PlatformTest::SetUp(); |
+ // This rect is fairly arbitrary. The text field just needs a non-zero width |
+ // so that the pre-edit label's text alignment can be tested. |
+ CGRect rect = CGRectMake(0, 0, 100, 20); |
+ textfield_.reset([[OmniboxTextFieldIOS alloc] initWithFrame:rect]); |
+ [[[UIApplication sharedApplication] keyWindow] addSubview:textfield_]; |
+ }; |
+ |
+ void TearDown() override { [textfield_ removeFromSuperview]; } |
+ |
+ BOOL IsCopyUrlInMenu() { |
+ UIMenuController* menuController = [UIMenuController sharedMenuController]; |
+ NSString* const kTitle = l10n_util::GetNSString(IDS_IOS_COPY_URL); |
+ for (UIMenuItem* item in menuController.menuItems) { |
+ if ([item.title isEqual:kTitle]) |
+ return YES; |
+ } |
+ return NO; |
+ }; |
+ |
+ void ExpectRectEqual(CGRect expectedRect, CGRect actualRect) { |
+ EXPECT_EQ(expectedRect.origin.x, actualRect.origin.x); |
+ EXPECT_EQ(expectedRect.origin.y, actualRect.origin.y); |
+ EXPECT_EQ(expectedRect.size.width, actualRect.size.width); |
+ EXPECT_EQ(expectedRect.size.height, actualRect.size.height); |
+ } |
+ |
+ // Verifies that the |selectedNSRange| function properly converts from opaque |
+ // UITextRanges to NSRanges. This function selects blocks of text in the text |
+ // field and compares the field's actual selected text to the converted |
+ // NSRange. |
+ void VerifySelectedNSRanges(NSString* text) { |
+ // The NSRange conversion mechanism only works when the field is first |
+ // responder. |
+ [textfield_ setText:text]; |
+ [textfield_ becomeFirstResponder]; |
+ ASSERT_TRUE([textfield_ isFirstResponder]); |
+ |
+ // |i| and |j| hold the start and end offsets of the range that is currently |
+ // being tested. This function iterates through all possible combinations |
+ // of |i| and |j|. |
+ NSInteger i = 0; |
+ NSInteger j = i + 1; |
+ UITextPosition* beginning = [textfield_ beginningOfDocument]; |
+ UITextPosition* start = |
+ [textfield_ positionFromPosition:[textfield_ beginningOfDocument] |
+ offset:i]; |
+ |
+ // In order to avoid making any assumptions about the length of the text in |
+ // the field, this test operates by incrementing the |i| and |j| offsets and |
+ // converting them to opaque UITextPositions. If either |i| or |j| are |
+ // invalid offsets for the current field text, |
+ // |positionFromPosition:offset:| is documented to return nil. This is used |
+ // as a signal to stop incrementing that offset and reset (or end the test). |
+ while (start) { |
+ UITextPosition* end = |
+ [textfield_ positionFromPosition:beginning offset:j]; |
+ while (end) { |
+ [textfield_ |
+ setSelectedTextRange:[textfield_ textRangeFromPosition:start |
+ toPosition:end]]; |
+ |
+ // There are two ways to get the selected text: |
+ // 1) Ask the field for it directly. |
+ // 2) Compute the selected NSRange and use that to extract a substring |
+ // from the field's text. |
+ // This block of code ensures that the two methods give identical text. |
+ NSRange nsrange = [textfield_ selectedNSRange]; |
+ NSString* nstext = [[textfield_ text] substringWithRange:nsrange]; |
+ UITextRange* uirange = [textfield_ selectedTextRange]; |
+ NSString* uitext = [textfield_ textInRange:uirange]; |
+ EXPECT_NSEQ(nstext, uitext); |
+ |
+ // Increment |j| and |end| for the next iteration of the inner while |
+ // loop. |
+ ++j; |
+ end = [textfield_ positionFromPosition:beginning offset:j]; |
+ } |
+ |
+ // Increment |i| and |start| for the next iteration of the outer while |
+ // loop. This also requires |j| to be reset. |
+ ++i; |
+ j = i + 1; |
+ start = [textfield_ positionFromPosition:beginning offset:i]; |
+ } |
+ |
+ [textfield_ resignFirstResponder]; |
+ } |
+ |
+ base::scoped_nsobject<OmniboxTextFieldIOS> textfield_; |
+}; |
+ |
+TEST_F(OmniboxTextFieldIOSTest, BecomeFirstResponderAddsCopyURLMenuItem) { |
+ // The 'Copy URL' menu item should not be present before this test runs. |
+ EXPECT_FALSE(IsCopyUrlInMenu()); |
+ |
+ // Call |becomeFirstResponder| and verify the Copy URL menu item was added. |
+ UIMenuController* menuController = [UIMenuController sharedMenuController]; |
+ NSUInteger expectedItems = [menuController.menuItems count] + 1; |
+ [textfield_ becomeFirstResponder]; |
+ EXPECT_EQ(expectedItems, [menuController.menuItems count]); |
+ EXPECT_TRUE(IsCopyUrlInMenu()); |
+ |
+ // Call |becomeFirstResponder| again and verify the Copy URL menu item is not |
+ // added again. |
+ [textfield_ becomeFirstResponder]; |
+ EXPECT_EQ(expectedItems, [menuController.menuItems count]); |
+ EXPECT_TRUE(IsCopyUrlInMenu()); |
+} |
+ |
+TEST_F(OmniboxTextFieldIOSTest, ResignFirstResponderRemovesCopyURLMenuItem) { |
+ // Call |becomeFirstResponder| to add the 'Copy URL' menu item so this test |
+ // can remove it. |
+ [textfield_ becomeFirstResponder]; |
+ |
+ UIMenuController* menuController = [UIMenuController sharedMenuController]; |
+ NSUInteger expectedItems = [menuController.menuItems count] - 1; |
+ [textfield_ resignFirstResponder]; |
+ EXPECT_EQ(expectedItems, [menuController.menuItems count]); |
+ EXPECT_FALSE(IsCopyUrlInMenu()); |
+} |
+ |
+TEST_F(OmniboxTextFieldIOSTest, enterPreEditState_preEditTextAlignment_short) { |
+ [textfield_ setText:@"s"]; |
+ [textfield_ becomeFirstResponder]; |
+ [textfield_ enterPreEditState]; |
+ UILabel* preEditLabel = [textfield_ preEditStaticLabel]; |
+ EXPECT_EQ(NSTextAlignmentLeft, preEditLabel.textAlignment); |
+ [textfield_ resignFirstResponder]; |
+} |
+ |
+TEST_F(OmniboxTextFieldIOSTest, enterPreEditState_preEditTextAlignment_long) { |
+ [textfield_ setText:@"some really long text that is wider than the omnibox"]; |
+ [textfield_ becomeFirstResponder]; |
+ [textfield_ enterPreEditState]; |
+ UILabel* preEditLabel = [textfield_ preEditStaticLabel]; |
+ EXPECT_EQ(NSTextAlignmentRight, preEditLabel.textAlignment); |
+ [textfield_ resignFirstResponder]; |
+} |
+ |
+TEST_F(OmniboxTextFieldIOSTest, enterPreEditState_preEditTextAlignment_change) { |
+ [textfield_ setText:@"s"]; |
+ [textfield_ becomeFirstResponder]; |
+ [textfield_ enterPreEditState]; |
+ // Simulate changing the omnibox text while in pre-edit state. |
+ [textfield_ setText:@"some really long text that is wider than the omnibox"]; |
+ [textfield_ layoutSubviews]; |
+ UILabel* preEditLabel = [textfield_ preEditStaticLabel]; |
+ EXPECT_EQ(NSTextAlignmentLeft, preEditLabel.textAlignment); |
+ [textfield_ resignFirstResponder]; |
+} |
+ |
+TEST_F(OmniboxTextFieldIOSTest, rectForDrawTextInRect_entireURLFits) { |
+ NSString* text = @"http://www.google.com"; |
+ [textfield_ setText:text]; |
+ CGSize textSize = [[textfield_ attributedText] size]; |
+ CGFloat widthForEntireURL = ceil(textSize.width) + 10; |
+ |
+ CGRect inputRect = CGRectMake(0, 0, widthForEntireURL, textSize.height); |
+ CGRect actualRect = [textfield_ rectForDrawTextInRect:inputRect]; |
+ ExpectRectEqual(inputRect, actualRect); |
+} |
+ |
+TEST_F(OmniboxTextFieldIOSTest, rectForDrawTextInRect_clippedPrefix) { |
+ NSString* text = @"http://www.google.com"; |
+ [textfield_ setText:text]; |
+ CGSize textSize = [[textfield_ attributedText] size]; |
+ CGFloat clippedWidth = 10; |
+ CGFloat widthForPartOfHost = ceil(textSize.width) - clippedWidth; |
+ |
+ CGRect inputRect = CGRectMake(0, 0, widthForPartOfHost, textSize.height); |
+ CGRect actualRect = [textfield_ rectForDrawTextInRect:inputRect]; |
+ CGRect expectedRect = |
+ CGRectMake(-1 * clippedWidth, 0, ceil(textSize.width), textSize.height); |
+ ExpectRectEqual(expectedRect, actualRect); |
+} |
+ |
+TEST_F(OmniboxTextFieldIOSTest, rectForDrawTextInRect_clippedSuffix) { |
+ NSString* text = @"http://www.google.com/somelongpath"; |
+ [textfield_ setText:text]; |
+ CGSize textSize = [[textfield_ attributedText] size]; |
+ CGFloat widthForPartOfPath = ceil(textSize.width) - 10; |
+ |
+ CGRect inputRect = CGRectMake(0, 0, widthForPartOfPath, textSize.height); |
+ CGRect actualRect = [textfield_ rectForDrawTextInRect:inputRect]; |
+ CGRect expectedRect = CGRectMake(0, 0, ceil(textSize.width), textSize.height); |
+ ExpectRectEqual(expectedRect, actualRect); |
+} |
+ |
+TEST_F(OmniboxTextFieldIOSTest, rectForDrawTextInRect_noScheme) { |
+ NSString* text = @"www.google.com"; |
+ [textfield_ setText:text]; |
+ CGSize textSize = [[textfield_ attributedText] size]; |
+ |
+ CGRect inputRect = CGRectMake(0, 0, ceil(textSize.width), textSize.height); |
+ CGRect actualRect = [textfield_ rectForDrawTextInRect:inputRect]; |
+ ExpectRectEqual(inputRect, actualRect); |
+} |
+ |
+// When the text doesn't contain a host the method bails early and returns |
+// the |rect| passed in. |
+TEST_F(OmniboxTextFieldIOSTest, rectForDrawTextInRect_noHost) { |
+ NSString* text = @"http://"; |
+ [textfield_ setText:text]; |
+ CGSize textSize = [[textfield_ attributedText] size]; |
+ |
+ CGRect inputRect = CGRectMake(0, 0, ceil(textSize.width), textSize.height); |
+ CGRect actualRect = [textfield_ rectForDrawTextInRect:inputRect]; |
+ ExpectRectEqual(inputRect, actualRect); |
+} |
+ |
+TEST_F(OmniboxTextFieldIOSTest, SelectedRanges) { |
+ base::FilePath test_data_directory; |
+ ASSERT_TRUE(PathService::Get(ios::DIR_TEST_DATA, &test_data_directory)); |
+ base::FilePath test_file = test_data_directory.Append( |
+ FILE_PATH_LITERAL("omnibox/selected_ranges.txt")); |
+ ASSERT_TRUE(base::PathExists(test_file)); |
+ |
+ std::string contents; |
+ ASSERT_TRUE(base::ReadFileToString(test_file, &contents)); |
+ std::vector<std::string> test_strings = base::SplitString( |
+ contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
+ |
+ for (size_t i = 0; i < test_strings.size(); ++i) { |
+ if (test_strings[i].size() > 0) { |
+ VerifySelectedNSRanges(base::SysUTF8ToNSString(test_strings[i])); |
+ } |
+ } |
+} |
+ |
+TEST_F(OmniboxTextFieldIOSTest, SelectExitsPreEditState) { |
+ [textfield_ enterPreEditState]; |
+ EXPECT_TRUE([textfield_ isPreEditing]); |
+ [textfield_ select:nil]; |
+ EXPECT_FALSE([textfield_ isPreEditing]); |
+} |
+ |
+TEST_F(OmniboxTextFieldIOSTest, SelectAllExitsPreEditState) { |
+ [textfield_ enterPreEditState]; |
+ EXPECT_TRUE([textfield_ isPreEditing]); |
+ [textfield_ selectAll:nil]; |
+ EXPECT_FALSE([textfield_ isPreEditing]); |
+} |
+ |
+} // namespace |