Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(49)

Side by Side Diff: ios/chrome/browser/ui/contextual_search/contextual_search_js_unittest.mm

Issue 2588713002: Upstream Chrome on iOS source code [4/11]. (Closed)
Patch Set: Created 3 years, 12 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 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 <UIKit/UIKit.h>
6
7 #include <memory>
8
9 #include "base/ios/ios_util.h"
10 #include "base/json/json_reader.h"
11 #include "base/mac/scoped_nsobject.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #import "base/test/ios/wait_util.h"
15 #include "base/values.h"
16 #include "components/pref_registry/pref_registry_syncable.h"
17 #include "components/prefs/pref_registry_simple.h"
18 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
19 #import "ios/chrome/browser/ui/contextual_search/contextual_search_controller.h"
20 #import "ios/chrome/browser/ui/contextual_search/js_contextual_search_manager.h"
21 #import "ios/chrome/browser/ui/contextual_search/touch_to_search_permissions_med iator+testing.h"
22 #import "ios/chrome/browser/web/chrome_web_test.h"
23 #import "ios/web/public/web_state/js/crw_js_injection_manager.h"
24 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
25 #import "ios/web/public/web_state/web_state.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 #include "testing/gtest_mac.h"
28 #import "third_party/ocmock/OCMock/OCMock.h"
29
30 // Unit tests for the resources/contextualsearch.js JavaScript file.
31
32 struct ContextualSearchStruct {
33 std::string url;
34 std::string selectedText;
35 std::string surroundingText;
36 int offsetStart;
37 int offsetEnd;
38 };
39
40 @interface JsContextualSearchAdditionsManager : CRWJSInjectionManager
41 @end
42
43 @implementation JsContextualSearchAdditionsManager : CRWJSInjectionManager
44 - (NSString*)scriptPath {
45 return @"contextualsearch_unittest";
46 }
47
48 - (NSArray*)dependencies {
49 return @[ [JsContextualSearchManager class] ];
50 }
51
52 @end
53
54 namespace {
55
56 // HTML that contains script.
57 NSString* kHTMLSentenceWithScript =
58 @"<html><body>"
59 "This is <span id='taphere'>the</span> <script>function ignore() "
60 "{};</script>sentence to select."
61 "</body></html>";
62
63 // HTML that contains label to be tapped.
64 NSString* kHTMLSentenceWithLabel =
65 @"<html><body>"
66 "<label>Click <span id='taphere'>me</span> <input type='text'/></label>."
67 "A sentence to select."
68 "</body></html>";
69
70 // HTML that contains italic element.
71 NSString* kHTMLWithItalic =
72 @"<html><body>"
73 "This is <span id='taphere'>an</span> <i>italic</i> element."
74 "</body></html>";
75
76 // HTML that contains a table.
77 NSString* kHTMLWithTable =
78 @"<html><body>"
79 "<table><tr><td>Left <span id='taphere'>cell</span></td>"
80 "<td>right cell</td></tr></table>"
81 "</body></html>";
82
83 // HTML that will trigger an unrelated DOM mutation on tap.
84 NSString* kHTMLWithUnrelatedDOMMutation =
85 @"<html><body>"
86 "<span>This <span style='margin-left:50px' id='taphere'>is</span>"
87 " sentence</span> <span>with <span id='test' attr='before'>mutation</span>"
88 "</span>.</body></html>";
89
90 // HTML that will trigger a related DOM mutation on tap.
91 NSString* kHTMLWithRelatedDOMMutation =
92 @"<html><body>"
93 "This <span id='test' attr='before'>"
94 "<span style='margin-left:50px' id='taphere'>is</span></span>"
95 "a sentence with mutation."
96 "</body></html>";
97
98 // HTML that will trigger a related DOM mutation on tap.
99 NSString* kHTMLWithRelatedTextMutation =
100 @"<html><body>"
101 "This <span style='margin-left:50px' id='taphere'> text "
102 "<span id='test'>mutation is </span> inside </span>"
103 "a sentence with mutation."
104 "</body></html>";
105
106 // HTML that contain a div in the middle of a sentence.
107 NSString* kHTMLWithDiv =
108 @"<html><body>"
109 "This<div>is</div>a <span id='taphere'>sentence</span>."
110 "</body></html>";
111
112 // HTML that contain a div in the middle of a sentence.
113 NSString* kHTMLWithSpaceDiv =
114 @"<html><body>"
115 "This <div>is</div>a <span id='taphere'>sentence</span>."
116 "</body></html>";
117
118 // HTML that contains prevent default.
119 NSString* kHTMLWithPreventDefault = @"<html><body>"
120 "<div id='interceptor'>"
121 "<span id='taphere'>is</span>"
122 "</div></body></html>";
123
124 NSString* kStringWith50Chars =
125 @"Sentence $INDEX containing exactly 50 characters. ";
126
127 class ContextualSearchJsTest : public ChromeWebTest {
128 public:
129 // Loads the given HTML, then loads the |contextualSearch| JavaScript.
130 void LoadHtml(NSString* html) {
131 ChromeWebTest::LoadHtml(html);
132 [jsUnittestsAdditions_ inject];
133 }
134
135 bool GetContextFromId(NSString* elementID,
136 ContextualSearchStruct* searchContext) {
137 id javaScriptResult = ExecuteJavaScript([NSString
138 stringWithFormat:@"document.getElementById('%@').scrollIntoView();"
139 "__gCrWeb.contextualSearch.tapOnElement('%@');",
140 elementID, elementID]);
141 if (!javaScriptResult)
142 return false;
143 const std::string json = base::SysNSStringToUTF8(javaScriptResult);
144 std::unique_ptr<base::Value> parsedResult(
145 base::JSONReader::Read(json, false));
146 if (!parsedResult.get() ||
147 !parsedResult->IsType(base::Value::Type::DICTIONARY)) {
148 return false;
149 }
150
151 base::DictionaryValue* resultDict =
152 static_cast<base::DictionaryValue*>(parsedResult.get());
153 const base::DictionaryValue* context = nullptr;
154 if (!resultDict->GetDictionary("context", &context)) {
155 return false;
156 }
157
158 std::string error;
159 context->GetString("error", &error);
160 if (!error.empty()) {
161 LOG(ERROR) << "GetContext error: " << error;
162 return false;
163 }
164
165 std::string url, selectedText;
166 context->GetString("url", &searchContext->url);
167 context->GetString("selectedText", &searchContext->selectedText);
168 context->GetString("surroundingText", &searchContext->surroundingText);
169 context->GetInteger("offsetStart", &searchContext->offsetStart);
170 context->GetInteger("offsetEnd", &searchContext->offsetEnd);
171 return true;
172 }
173
174 id expandHighlight(int start, int end) {
175 return ExecuteJavaScript([NSString
176 stringWithFormat:@"__gCrWeb.contextualSearch.expandHighlight(%d, %d);"
177 "__gCrWeb.contextualSearch.retrieveHighlighted();",
178 start, end]);
179 }
180
181 id highlight() {
182 return ExecuteJavaScript(
183 @"__gCrWeb.contextualSearch.setHighlighting(true);"
184 "__gCrWeb.contextualSearch.retrieveHighlighted();");
185 }
186
187 NSInteger GetMutatedNodeCount() {
188 id output = ExecuteJavaScript(
189 @"__gCrWeb.contextualSearch.getMutatedElementCount();");
190 return [output integerValue];
191 }
192
193 void CheckContextOffsets(const ContextualSearchStruct& searchContext,
194 const std::string& surroundingText) {
195 EXPECT_EQ(searchContext.surroundingText, surroundingText);
196 EXPECT_EQ(searchContext.selectedText,
197 searchContext.surroundingText.substr(
198 searchContext.offsetStart,
199 searchContext.offsetEnd - searchContext.offsetStart));
200 }
201
202 NSString* BuildLongTestString(int startIndex,
203 int endIndex,
204 int tapOn,
205 bool newLine) {
206 NSString* longString = @"";
207 for (int i = startIndex; i < endIndex; i++) {
208 NSString* index = [NSString stringWithFormat:@"%06d", i];
209 if (i == tapOn) {
210 index =
211 [NSString stringWithFormat:@"<span id='taphere'>%@</span>", index];
212 }
213 NSString* sentence =
214 [kStringWith50Chars stringByReplacingOccurrencesOfString:@"$INDEX"
215 withString:index];
216 longString = [longString stringByAppendingString:sentence];
217 if (newLine) {
218 longString = [longString stringByAppendingString:@"<br/>"];
219 }
220 }
221 return longString;
222 }
223
224 void SetUp() override {
225 ChromeWebTest::SetUp();
226 mockDelegate_.reset([[OCMockObject
227 niceMockForProtocol:@protocol(ContextualSearchControllerDelegate)]
228 retain]);
229 jsUnittestsAdditions_ = static_cast<JsContextualSearchAdditionsManager*>(
230 [web_state()->GetJSInjectionReceiver()
231 instanceOfClass:[JsContextualSearchAdditionsManager class]]);
232 TestChromeBrowserState::Builder test_cbs_builder;
233 chrome_browser_state_ = test_cbs_builder.Build();
234 controller_.reset([[ContextualSearchController alloc]
235 initWithBrowserState:chrome_browser_state_.get()
236 delegate:mockDelegate_]);
237 [controller_
238 setPermissions:[[MockTouchToSearchPermissionsMediator alloc]
239 initWithBrowserState:chrome_browser_state_.get()]];
240 [controller_ setWebState:web_state()];
241 [controller_ enableContextualSearch:YES];
242 }
243
244 void TearDown() override {
245 [controller_ close];
246 // Need to tear down the controller so it deregisters its JS handlers
247 // before |webController_| is destroyed.
248 controller_.reset();
249 ChromeWebTest::TearDown();
250 }
251
252 std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
253 __unsafe_unretained JsContextualSearchAdditionsManager* jsUnittestsAdditions_;
254 base::scoped_nsobject<ContextualSearchController> controller_;
255 base::scoped_nsobject<id> mockDelegate_;
256 base::scoped_nsobject<id> mockToolbarDelegate_;
257 };
258
259 // Test that ignored elements do not trigger CS when tapped.
260 TEST_F(ContextualSearchJsTest, TestIgnoreTapsOnElements) {
261 LoadHtml(kHTMLSentenceWithLabel);
262 ContextualSearchStruct searchContext;
263 ASSERT_FALSE(GetContextFromId(@"taphere", &searchContext));
264 };
265
266 // Test that ingnored element are not included in highlight or surrounding text.
267 TEST_F(ContextualSearchJsTest, TestIgnoreScript) {
268 LoadHtml(kHTMLSentenceWithScript);
269 ContextualSearchStruct searchContext;
270 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
271
272 CheckContextOffsets(searchContext, "This is the sentence to select.");
273 id highlighted = expandHighlight(0, searchContext.surroundingText.size());
274 EXPECT_NSEQ(highlighted, @"This is the sentence to select.");
275 };
276
277 // Test that all span element are correctly highlighted.
278 TEST_F(ContextualSearchJsTest, TestHighlightThroughSpan) {
279 LoadHtml(kHTMLWithItalic);
280 ContextualSearchStruct searchContext;
281 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
282
283 CheckContextOffsets(searchContext, "This is an italic element.");
284 NSString* highlighted =
285 expandHighlight(0, searchContext.surroundingText.size());
286 EXPECT_NSEQ(highlighted, @"This is an italic element.");
287 };
288
289 // Test that all block element are highlighted. Spaces separating element must
290 // be ignored.
291 TEST_F(ContextualSearchJsTest, TestHighlightThroughBlock) {
292 LoadHtml(kHTMLWithTable);
293 ContextualSearchStruct searchContext;
294 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
295
296 CheckContextOffsets(searchContext, "Left cell right cell");
297 NSString* highlighted =
298 expandHighlight(0, searchContext.surroundingText.size());
299 EXPECT_NSEQ(highlighted, @"Left cell right cell");
300 };
301
302 // Test that blocks add spaces if there are not arround it.
303 TEST_F(ContextualSearchJsTest, TestHighlightBlockAddsSpace) {
304 LoadHtml(kHTMLWithDiv);
305 ContextualSearchStruct searchContext;
306 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
307 CheckContextOffsets(searchContext, "This is a sentence.");
308 };
309
310 // Test that blocks don't add spaces if there are around it.
311 TEST_F(ContextualSearchJsTest, TestHighlightBlockDontAddsSpace) {
312 LoadHtml(kHTMLWithSpaceDiv);
313 ContextualSearchStruct searchContext;
314 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
315 CheckContextOffsets(searchContext, "This is a sentence.");
316 };
317
318 // Test that related DOM mutation cancels contextual search.
319 TEST_F(ContextualSearchJsTest, TestHighlightRelatedDOMMutation) {
320 LoadHtml(kHTMLWithRelatedDOMMutation);
321 ExecuteJavaScript(
322 @"document.getElementById('test').setAttribute('attr', 'after');");
323 ASSERT_EQ(1, GetMutatedNodeCount());
324 ContextualSearchStruct searchContext;
325 ASSERT_FALSE(GetContextFromId(@"taphere", &searchContext));
326 };
327
328 // Test that unrelated DOM mutation does not cancel contextual search.
329 TEST_F(ContextualSearchJsTest, TestHighlightIgnoreUnrelatedDOMMutation) {
330 LoadHtml(kHTMLWithUnrelatedDOMMutation);
331 ExecuteJavaScript(
332 @"document.getElementById('test').setAttribute('attr', 'after');");
333 ASSERT_EQ(1, GetMutatedNodeCount());
334 ContextualSearchStruct searchContext;
335
336 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
337 };
338
339 // Test that DOM mutation with same value does not cancel contextual search.
340 // Mutation should not mark a node as mutated.
341 TEST_F(ContextualSearchJsTest, TestHighlightIgnoreDOMMutationSameAttribute) {
342 LoadHtml(kHTMLWithRelatedDOMMutation);
343 ExecuteJavaScript(
344 @"document.getElementById('test').setAttribute('attr', 'before');");
345 ASSERT_EQ(0, GetMutatedNodeCount());
346 ContextualSearchStruct searchContext;
347
348 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
349 };
350
351 // Test that DOM mutation between different false values does not cancel
352 // contextual search. Mutation should not mark a node as mutated.
353 TEST_F(ContextualSearchJsTest, TestHighlightIgnoreDOMMutationBothFalse) {
354 LoadHtml(kHTMLWithRelatedDOMMutation);
355 ExecuteJavaScript(
356 @"document.getElementById('test').setAttribute('non_attr', '');");
357 ASSERT_EQ(0, GetMutatedNodeCount());
358 ContextualSearchStruct searchContext;
359
360 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
361 };
362
363 // Test that DOM mutation with same text does not cancel contextual search.
364 // Mutation should not mark a node as mutated.
365 TEST_F(ContextualSearchJsTest, TestHighlightIgnoreDOMMutationSameText) {
366 LoadHtml(kHTMLWithRelatedTextMutation);
367 ExecuteJavaScript(
368 @"document.getElementById('test').innerText = 'mutation is ';");
369 ASSERT_EQ(0, GetMutatedNodeCount());
370 ContextualSearchStruct searchContext;
371
372 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
373 };
374
375 // Test that related text DOM mutation prevents contextual search.
376 TEST_F(ContextualSearchJsTest, TestHighlightRelatedDOMMutationText) {
377 LoadHtml(kHTMLWithRelatedTextMutation);
378 ExecuteJavaScript(@"document.getElementById('test').innerText = 'mutated';");
379 ASSERT_EQ(1, GetMutatedNodeCount());
380 ContextualSearchStruct searchContext;
381
382 ASSERT_FALSE(GetContextFromId(@"taphere", &searchContext));
383 };
384
385 // Test that unrelated text DOM mutation doesn't prevent contextual search.
386 TEST_F(ContextualSearchJsTest, TestHighlightUnrelatedDOMMutationTextIgnored) {
387 LoadHtml(kHTMLWithUnrelatedDOMMutation);
388 ExecuteJavaScript(@"document.getElementById('test').innerText = 'mutated';");
389 ASSERT_EQ(1, GetMutatedNodeCount());
390 ContextualSearchStruct searchContext;
391
392 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
393 };
394
395 // Test that two related DOM mutations prevent contextual search.
396 TEST_F(ContextualSearchJsTest, TestHighlightTwoDOMMutations) {
397 LoadHtml(kHTMLWithRelatedDOMMutation);
398 ASSERT_EQ(0, GetMutatedNodeCount());
399 ExecuteJavaScript(
400 @"document.getElementById('taphere').innerText = 'mutated';"
401 "document.getElementById('test').setAttribute('attr', 'after');");
402 ASSERT_EQ(2, GetMutatedNodeCount());
403 ContextualSearchStruct searchContext;
404
405 ASSERT_FALSE(GetContextFromId(@"taphere", &searchContext));
406 };
407
408 // Test that two related DOM mutations with only one change prevent contextual
409 // search.
410 TEST_F(ContextualSearchJsTest, TestHighlightTwoDOMMutationOneChanging) {
411 LoadHtml(kHTMLWithRelatedDOMMutation);
412 ExecuteJavaScript(
413 @"document.getElementById('taphere').innerText = 'mutated';"
414 "document.getElementById('test').setAttribute('attr', 'before');");
415 ASSERT_EQ(1, GetMutatedNodeCount());
416 ContextualSearchStruct searchContext;
417
418 ASSERT_FALSE(GetContextFromId(@"taphere", &searchContext));
419 };
420
421 // Test that two DOM mutations with same value does not cancel contextual
422 // search.
423 TEST_F(ContextualSearchJsTest, TestHighlightTwoDOMMutationNoChange) {
424 LoadHtml(kHTMLWithRelatedDOMMutation);
425 ExecuteJavaScript(
426 @"document.getElementById('taphere').innerText = 'is';"
427 "document.getElementById('test').setAttribute('attr', 'before');");
428 ASSERT_EQ(0, GetMutatedNodeCount());
429 ContextualSearchStruct searchContext;
430
431 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
432 };
433
434 // Test that non bubbling event does not trigger contextual search.
435 TEST_F(ContextualSearchJsTest, TestHighlightIgnorePreventDefault) {
436 LoadHtml(kHTMLWithPreventDefault);
437 // Enable touch delay.
438 ExecuteJavaScript(
439 @"__gCrWeb.contextualSearch.setBodyTouchListenerDelay(200);");
440 ContextualSearchStruct searchContext;
441 ExecuteJavaScript(@"document.getElementById('interceptor')."
442 @"addEventListener('touchend', function(event) "
443 @"{event.preventDefault();return false;}, false);");
444
445 ASSERT_FALSE(GetContextFromId(@"taphere", &searchContext));
446 };
447
448 TEST_F(ContextualSearchJsTest, Test1500CharactersCenter) {
449 // String should extend 15 sentences to the left and right.
450 NSString* stringHTML = BuildLongTestString(0, 50, 25, true);
451 NSString* expectedHTML = BuildLongTestString(10, 41, -1, false);
452 LoadHtml(stringHTML);
453 ContextualSearchStruct searchContext;
454 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
455 CheckContextOffsets(searchContext, base::SysNSStringToUTF8(expectedHTML));
456 };
457
458 TEST_F(ContextualSearchJsTest, Test1500CharactersRight) {
459 // There is not enough chars on the left, so string should extend on the
460 // right.
461 NSString* stringHTML = BuildLongTestString(0, 50, 5, true);
462 NSString* expectedHTML = BuildLongTestString(0, 30, -1, false);
463 LoadHtml(
464 [NSString stringWithFormat:@"<html><body>%@</body></html>", stringHTML]);
465 ContextualSearchStruct searchContext;
466 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
467 CheckContextOffsets(searchContext, base::SysNSStringToUTF8(expectedHTML));
468 };
469
470 TEST_F(ContextualSearchJsTest, Test1500CharactersLeft) {
471 // There is not enough chars on the right, so string should extend on the
472 // left.
473 NSString* stringHTML = BuildLongTestString(0, 50, 45, true);
474 NSString* expectedHTML = BuildLongTestString(20, 50, -1, false);
475 LoadHtml(
476 [NSString stringWithFormat:@"<html><body>%@</body></html>", stringHTML]);
477 ContextualSearchStruct searchContext;
478 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
479 CheckContextOffsets(searchContext, base::SysNSStringToUTF8(expectedHTML));
480 };
481
482 TEST_F(ContextualSearchJsTest, Test1500CharactersToShort) {
483 // String is too short so the whole string should be in the context.
484 NSString* stringHTML = BuildLongTestString(0, 10, 5, true);
485 NSString* expectedHTML = BuildLongTestString(0, 10, -1, false);
486 // LoadHtml will trim the last space.
487 LoadHtml(
488 [NSString stringWithFormat:@"<html><body>%@</body></html>", stringHTML]);
489 ContextualSearchStruct searchContext;
490 ASSERT_TRUE(GetContextFromId(@"taphere", &searchContext));
491 CheckContextOffsets(searchContext, base::SysNSStringToUTF8(expectedHTML));
492 };
493 } // namespace
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698