OLD | NEW |
(Empty) | |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h" |
| 6 |
| 7 #include "base/command_line.h" |
| 8 #include "base/files/file_path.h" |
| 9 #include "base/files/file_util.h" |
| 10 #include "base/mac/scoped_nsobject.h" |
| 11 #include "base/path_service.h" |
| 12 #include "base/strings/string_split.h" |
| 13 #include "base/strings/sys_string_conversions.h" |
| 14 #include "ios/chrome/browser/chrome_paths.h" |
| 15 #include "ios/chrome/grit/ios_strings.h" |
| 16 #include "testing/gtest_mac.h" |
| 17 #include "testing/platform_test.h" |
| 18 #include "ui/base/l10n/l10n_util_mac.h" |
| 19 |
| 20 // A category for making existing methods visible for use in these tests. |
| 21 @interface OmniboxTextFieldIOS (VisibleForTesting) |
| 22 - (CGRect)rectForDrawTextInRect:(CGRect)rect; |
| 23 @end |
| 24 |
| 25 namespace { |
| 26 |
| 27 class OmniboxTextFieldIOSTest : public PlatformTest { |
| 28 protected: |
| 29 void SetUp() override { |
| 30 PlatformTest::SetUp(); |
| 31 // This rect is fairly arbitrary. The text field just needs a non-zero width |
| 32 // so that the pre-edit label's text alignment can be tested. |
| 33 CGRect rect = CGRectMake(0, 0, 100, 20); |
| 34 textfield_.reset([[OmniboxTextFieldIOS alloc] initWithFrame:rect]); |
| 35 [[[UIApplication sharedApplication] keyWindow] addSubview:textfield_]; |
| 36 }; |
| 37 |
| 38 void TearDown() override { [textfield_ removeFromSuperview]; } |
| 39 |
| 40 BOOL IsCopyUrlInMenu() { |
| 41 UIMenuController* menuController = [UIMenuController sharedMenuController]; |
| 42 NSString* const kTitle = l10n_util::GetNSString(IDS_IOS_COPY_URL); |
| 43 for (UIMenuItem* item in menuController.menuItems) { |
| 44 if ([item.title isEqual:kTitle]) |
| 45 return YES; |
| 46 } |
| 47 return NO; |
| 48 }; |
| 49 |
| 50 void ExpectRectEqual(CGRect expectedRect, CGRect actualRect) { |
| 51 EXPECT_EQ(expectedRect.origin.x, actualRect.origin.x); |
| 52 EXPECT_EQ(expectedRect.origin.y, actualRect.origin.y); |
| 53 EXPECT_EQ(expectedRect.size.width, actualRect.size.width); |
| 54 EXPECT_EQ(expectedRect.size.height, actualRect.size.height); |
| 55 } |
| 56 |
| 57 // Verifies that the |selectedNSRange| function properly converts from opaque |
| 58 // UITextRanges to NSRanges. This function selects blocks of text in the text |
| 59 // field and compares the field's actual selected text to the converted |
| 60 // NSRange. |
| 61 void VerifySelectedNSRanges(NSString* text) { |
| 62 // The NSRange conversion mechanism only works when the field is first |
| 63 // responder. |
| 64 [textfield_ setText:text]; |
| 65 [textfield_ becomeFirstResponder]; |
| 66 ASSERT_TRUE([textfield_ isFirstResponder]); |
| 67 |
| 68 // |i| and |j| hold the start and end offsets of the range that is currently |
| 69 // being tested. This function iterates through all possible combinations |
| 70 // of |i| and |j|. |
| 71 NSInteger i = 0; |
| 72 NSInteger j = i + 1; |
| 73 UITextPosition* beginning = [textfield_ beginningOfDocument]; |
| 74 UITextPosition* start = |
| 75 [textfield_ positionFromPosition:[textfield_ beginningOfDocument] |
| 76 offset:i]; |
| 77 |
| 78 // In order to avoid making any assumptions about the length of the text in |
| 79 // the field, this test operates by incrementing the |i| and |j| offsets and |
| 80 // converting them to opaque UITextPositions. If either |i| or |j| are |
| 81 // invalid offsets for the current field text, |
| 82 // |positionFromPosition:offset:| is documented to return nil. This is used |
| 83 // as a signal to stop incrementing that offset and reset (or end the test). |
| 84 while (start) { |
| 85 UITextPosition* end = |
| 86 [textfield_ positionFromPosition:beginning offset:j]; |
| 87 while (end) { |
| 88 [textfield_ |
| 89 setSelectedTextRange:[textfield_ textRangeFromPosition:start |
| 90 toPosition:end]]; |
| 91 |
| 92 // There are two ways to get the selected text: |
| 93 // 1) Ask the field for it directly. |
| 94 // 2) Compute the selected NSRange and use that to extract a substring |
| 95 // from the field's text. |
| 96 // This block of code ensures that the two methods give identical text. |
| 97 NSRange nsrange = [textfield_ selectedNSRange]; |
| 98 NSString* nstext = [[textfield_ text] substringWithRange:nsrange]; |
| 99 UITextRange* uirange = [textfield_ selectedTextRange]; |
| 100 NSString* uitext = [textfield_ textInRange:uirange]; |
| 101 EXPECT_NSEQ(nstext, uitext); |
| 102 |
| 103 // Increment |j| and |end| for the next iteration of the inner while |
| 104 // loop. |
| 105 ++j; |
| 106 end = [textfield_ positionFromPosition:beginning offset:j]; |
| 107 } |
| 108 |
| 109 // Increment |i| and |start| for the next iteration of the outer while |
| 110 // loop. This also requires |j| to be reset. |
| 111 ++i; |
| 112 j = i + 1; |
| 113 start = [textfield_ positionFromPosition:beginning offset:i]; |
| 114 } |
| 115 |
| 116 [textfield_ resignFirstResponder]; |
| 117 } |
| 118 |
| 119 base::scoped_nsobject<OmniboxTextFieldIOS> textfield_; |
| 120 }; |
| 121 |
| 122 TEST_F(OmniboxTextFieldIOSTest, BecomeFirstResponderAddsCopyURLMenuItem) { |
| 123 // The 'Copy URL' menu item should not be present before this test runs. |
| 124 EXPECT_FALSE(IsCopyUrlInMenu()); |
| 125 |
| 126 // Call |becomeFirstResponder| and verify the Copy URL menu item was added. |
| 127 UIMenuController* menuController = [UIMenuController sharedMenuController]; |
| 128 NSUInteger expectedItems = [menuController.menuItems count] + 1; |
| 129 [textfield_ becomeFirstResponder]; |
| 130 EXPECT_EQ(expectedItems, [menuController.menuItems count]); |
| 131 EXPECT_TRUE(IsCopyUrlInMenu()); |
| 132 |
| 133 // Call |becomeFirstResponder| again and verify the Copy URL menu item is not |
| 134 // added again. |
| 135 [textfield_ becomeFirstResponder]; |
| 136 EXPECT_EQ(expectedItems, [menuController.menuItems count]); |
| 137 EXPECT_TRUE(IsCopyUrlInMenu()); |
| 138 } |
| 139 |
| 140 TEST_F(OmniboxTextFieldIOSTest, ResignFirstResponderRemovesCopyURLMenuItem) { |
| 141 // Call |becomeFirstResponder| to add the 'Copy URL' menu item so this test |
| 142 // can remove it. |
| 143 [textfield_ becomeFirstResponder]; |
| 144 |
| 145 UIMenuController* menuController = [UIMenuController sharedMenuController]; |
| 146 NSUInteger expectedItems = [menuController.menuItems count] - 1; |
| 147 [textfield_ resignFirstResponder]; |
| 148 EXPECT_EQ(expectedItems, [menuController.menuItems count]); |
| 149 EXPECT_FALSE(IsCopyUrlInMenu()); |
| 150 } |
| 151 |
| 152 TEST_F(OmniboxTextFieldIOSTest, enterPreEditState_preEditTextAlignment_short) { |
| 153 [textfield_ setText:@"s"]; |
| 154 [textfield_ becomeFirstResponder]; |
| 155 [textfield_ enterPreEditState]; |
| 156 UILabel* preEditLabel = [textfield_ preEditStaticLabel]; |
| 157 EXPECT_EQ(NSTextAlignmentLeft, preEditLabel.textAlignment); |
| 158 [textfield_ resignFirstResponder]; |
| 159 } |
| 160 |
| 161 TEST_F(OmniboxTextFieldIOSTest, enterPreEditState_preEditTextAlignment_long) { |
| 162 [textfield_ setText:@"some really long text that is wider than the omnibox"]; |
| 163 [textfield_ becomeFirstResponder]; |
| 164 [textfield_ enterPreEditState]; |
| 165 UILabel* preEditLabel = [textfield_ preEditStaticLabel]; |
| 166 EXPECT_EQ(NSTextAlignmentRight, preEditLabel.textAlignment); |
| 167 [textfield_ resignFirstResponder]; |
| 168 } |
| 169 |
| 170 TEST_F(OmniboxTextFieldIOSTest, enterPreEditState_preEditTextAlignment_change) { |
| 171 [textfield_ setText:@"s"]; |
| 172 [textfield_ becomeFirstResponder]; |
| 173 [textfield_ enterPreEditState]; |
| 174 // Simulate changing the omnibox text while in pre-edit state. |
| 175 [textfield_ setText:@"some really long text that is wider than the omnibox"]; |
| 176 [textfield_ layoutSubviews]; |
| 177 UILabel* preEditLabel = [textfield_ preEditStaticLabel]; |
| 178 EXPECT_EQ(NSTextAlignmentLeft, preEditLabel.textAlignment); |
| 179 [textfield_ resignFirstResponder]; |
| 180 } |
| 181 |
| 182 TEST_F(OmniboxTextFieldIOSTest, rectForDrawTextInRect_entireURLFits) { |
| 183 NSString* text = @"http://www.google.com"; |
| 184 [textfield_ setText:text]; |
| 185 CGSize textSize = [[textfield_ attributedText] size]; |
| 186 CGFloat widthForEntireURL = ceil(textSize.width) + 10; |
| 187 |
| 188 CGRect inputRect = CGRectMake(0, 0, widthForEntireURL, textSize.height); |
| 189 CGRect actualRect = [textfield_ rectForDrawTextInRect:inputRect]; |
| 190 ExpectRectEqual(inputRect, actualRect); |
| 191 } |
| 192 |
| 193 TEST_F(OmniboxTextFieldIOSTest, rectForDrawTextInRect_clippedPrefix) { |
| 194 NSString* text = @"http://www.google.com"; |
| 195 [textfield_ setText:text]; |
| 196 CGSize textSize = [[textfield_ attributedText] size]; |
| 197 CGFloat clippedWidth = 10; |
| 198 CGFloat widthForPartOfHost = ceil(textSize.width) - clippedWidth; |
| 199 |
| 200 CGRect inputRect = CGRectMake(0, 0, widthForPartOfHost, textSize.height); |
| 201 CGRect actualRect = [textfield_ rectForDrawTextInRect:inputRect]; |
| 202 CGRect expectedRect = |
| 203 CGRectMake(-1 * clippedWidth, 0, ceil(textSize.width), textSize.height); |
| 204 ExpectRectEqual(expectedRect, actualRect); |
| 205 } |
| 206 |
| 207 TEST_F(OmniboxTextFieldIOSTest, rectForDrawTextInRect_clippedSuffix) { |
| 208 NSString* text = @"http://www.google.com/somelongpath"; |
| 209 [textfield_ setText:text]; |
| 210 CGSize textSize = [[textfield_ attributedText] size]; |
| 211 CGFloat widthForPartOfPath = ceil(textSize.width) - 10; |
| 212 |
| 213 CGRect inputRect = CGRectMake(0, 0, widthForPartOfPath, textSize.height); |
| 214 CGRect actualRect = [textfield_ rectForDrawTextInRect:inputRect]; |
| 215 CGRect expectedRect = CGRectMake(0, 0, ceil(textSize.width), textSize.height); |
| 216 ExpectRectEqual(expectedRect, actualRect); |
| 217 } |
| 218 |
| 219 TEST_F(OmniboxTextFieldIOSTest, rectForDrawTextInRect_noScheme) { |
| 220 NSString* text = @"www.google.com"; |
| 221 [textfield_ setText:text]; |
| 222 CGSize textSize = [[textfield_ attributedText] size]; |
| 223 |
| 224 CGRect inputRect = CGRectMake(0, 0, ceil(textSize.width), textSize.height); |
| 225 CGRect actualRect = [textfield_ rectForDrawTextInRect:inputRect]; |
| 226 ExpectRectEqual(inputRect, actualRect); |
| 227 } |
| 228 |
| 229 // When the text doesn't contain a host the method bails early and returns |
| 230 // the |rect| passed in. |
| 231 TEST_F(OmniboxTextFieldIOSTest, rectForDrawTextInRect_noHost) { |
| 232 NSString* text = @"http://"; |
| 233 [textfield_ setText:text]; |
| 234 CGSize textSize = [[textfield_ attributedText] size]; |
| 235 |
| 236 CGRect inputRect = CGRectMake(0, 0, ceil(textSize.width), textSize.height); |
| 237 CGRect actualRect = [textfield_ rectForDrawTextInRect:inputRect]; |
| 238 ExpectRectEqual(inputRect, actualRect); |
| 239 } |
| 240 |
| 241 TEST_F(OmniboxTextFieldIOSTest, SelectedRanges) { |
| 242 base::FilePath test_data_directory; |
| 243 ASSERT_TRUE(PathService::Get(ios::DIR_TEST_DATA, &test_data_directory)); |
| 244 base::FilePath test_file = test_data_directory.Append( |
| 245 FILE_PATH_LITERAL("omnibox/selected_ranges.txt")); |
| 246 ASSERT_TRUE(base::PathExists(test_file)); |
| 247 |
| 248 std::string contents; |
| 249 ASSERT_TRUE(base::ReadFileToString(test_file, &contents)); |
| 250 std::vector<std::string> test_strings = base::SplitString( |
| 251 contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| 252 |
| 253 for (size_t i = 0; i < test_strings.size(); ++i) { |
| 254 if (test_strings[i].size() > 0) { |
| 255 VerifySelectedNSRanges(base::SysUTF8ToNSString(test_strings[i])); |
| 256 } |
| 257 } |
| 258 } |
| 259 |
| 260 TEST_F(OmniboxTextFieldIOSTest, SelectExitsPreEditState) { |
| 261 [textfield_ enterPreEditState]; |
| 262 EXPECT_TRUE([textfield_ isPreEditing]); |
| 263 [textfield_ select:nil]; |
| 264 EXPECT_FALSE([textfield_ isPreEditing]); |
| 265 } |
| 266 |
| 267 TEST_F(OmniboxTextFieldIOSTest, SelectAllExitsPreEditState) { |
| 268 [textfield_ enterPreEditState]; |
| 269 EXPECT_TRUE([textfield_ isPreEditing]); |
| 270 [textfield_ selectAll:nil]; |
| 271 EXPECT_FALSE([textfield_ isPreEditing]); |
| 272 } |
| 273 |
| 274 } // namespace |
OLD | NEW |