blob: fc9a9f41a257bd0a531182b78dcdc75272a845fb [file] [log] [blame]
// 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 <UIKit/UIKit.h>
#include "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h"
#import "ios/chrome/browser/find_in_page/find_in_page_model.h"
#import "ios/chrome/browser/find_in_page/js_findinpage_manager.h"
#import "ios/chrome/browser/web/chrome_web_test.h"
#import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
#import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
#import "ios/web/public/web_state/web_state.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// Unit tests for the find_in_page.js JavaScript file.
namespace {
// JavaScript invocation to search for 'foo' (for 1000 milliseconds).
NSString* kJavaScriptToSearchForFoo =
@"__gCrWeb.findInPage.highlightWord('foo', false, 1000)";
// Other JavaScript functions invoked by the tests.
NSString* kJavaScriptIncrementIndex = @"__gCrWeb.findInPage.incrementIndex()";
NSString* kJavaScriptDecrementIndex = @"__gCrWeb.findInPage.decrementIndex()";
NSString* kJavaScriptGoNext = @"__gCrWeb.findInPage.goNext()";
NSString* kJavaScriptGoPrev = @"__gCrWeb.findInPage.goPrev()";
// JavaScript variables accessed by the tests.
NSString* kJavaScriptIndex = @"__gCrWeb.findInPage.index";
NSString* kJavaScriptSpansLength = @"__gCrWeb.findInPage.spans.length";
// HTML that contains several occurences of the string 'foo', some visible and
// some not visible (the first 'foo' is hidden, the next is visible, the next is
// hidden and so on until the final 'foo' which is hidden.
NSString* kHtmlWithFoos = @"<html><body>"
" <span style='display:none'>foo</span>"
" <span>foo</span>"
" <span style='display:none'>foo</span>"
" <span>foo</span>"
" <span style='display:none'>foo</span>"
" <span>foo</span>"
" <span style='display:none'>foo</span>"
"</body></html>";
// The number of times 'foo' occurs in |kHtmlWithFoos| (hidden and visible).
const int kNumberOfFoosInHtml = 7;
// HTML that contains several occurences of the string 'foo', none visible.
NSString* kHtmlWithNoVisibleFoos = @"<html><body>"
" <span style='display:none'>foo</span>"
" <span style='display:none'>foo</span>"
" <span style='display:none'>foo</span>"
" <span style='display:none'>foo</span>"
" <span style='display:none'>foo</span>"
" <span style='display:none'>foo</span>"
"</body></html>";
// Test fixture to test Find In Page JS.
class FindInPageJsTest : public ChromeWebTest {
public:
// Loads the given HTML, then loads the |findInPage| JavaScript.
void LoadHtml(NSString* html) {
ChromeWebTest::LoadHtml(html);
// Inject and initialize the find in page javascript.
[findInPageJsManager_ inject];
CGRect frame = [web_state()->GetWebViewProxy() bounds];
[findInPageJsManager_ setWidth:frame.size.width height:frame.size.height];
}
// Runs the given JavaScript and asserts that the result matches the given
// |expected_value|.
void AssertJavaScriptValue(NSString* script, int expected_value) {
id result = ExecuteJavaScript(script);
EXPECT_TRUE(result) << " in script: " << base::SysNSStringToUTF8(script);
EXPECT_EQ(expected_value, [result intValue])
<< " in script: " << base::SysNSStringToUTF8(script);
}
// Loads the test HTML containing 'foo' strings and invokes the JavaScript
// necessary to search for and highlight any matches. Note that the JavaScript
// sets the current index to the first visible occurence of 'foo'.
void SearchForFoo() {
LoadHtml(kHtmlWithFoos);
// Assert the index and span count contain their initialized values
AssertJavaScriptValue(kJavaScriptIndex, -1);
AssertJavaScriptValue(kJavaScriptSpansLength, 0);
// Search for 'foo'. Performing the search sets the index to point to the
// first visible occurence of 'foo'.
ExecuteJavaScript(kJavaScriptToSearchForFoo);
AssertJavaScriptValue(kJavaScriptIndex, 1);
AssertJavaScriptValue(kJavaScriptSpansLength, kNumberOfFoosInHtml);
}
void SetUp() override {
ChromeWebTest::SetUp();
findInPageModel_ = [[FindInPageModel alloc] init];
findInPageJsManager_ = base::mac::ObjCCastStrict<JsFindinpageManager>(
[web_state()->GetJSInjectionReceiver()
instanceOfClass:[JsFindinpageManager class]]);
findInPageJsManager_.findInPageModel = findInPageModel_;
}
FindInPageModel* findInPageModel_;
JsFindinpageManager* findInPageJsManager_;
};
// Performs a search, then calls |incrementIndex| to loop through the
// matches, ensuring that when the end is reached the index wraps back to zero.
TEST_F(FindInPageJsTest, IncrementIndex) {
SearchForFoo();
// Increment index until it hits the max index.
for (int i = 2; i < kNumberOfFoosInHtml; i++) {
ExecuteJavaScript(kJavaScriptIncrementIndex);
AssertJavaScriptValue(kJavaScriptIndex, i);
}
// Increment index one more time and it should wrap back to zero.
ExecuteJavaScript(kJavaScriptIncrementIndex);
AssertJavaScriptValue(kJavaScriptIndex, 0);
};
// Performs a search, then calls |decrementIndex| to loop through the
// matches, ensuring that when the beginning is reached the index wraps back to
// the end of the page.
TEST_F(FindInPageJsTest, DecrementIndex) {
SearchForFoo();
// Since the first visible 'foo' is at index 1, decrement once to get to zero.
ExecuteJavaScript(kJavaScriptDecrementIndex);
AssertJavaScriptValue(kJavaScriptIndex, 0);
// Decrement index until it hits zero again. Note that the first time
// |decrementIndex| is called the index wraps from zero to the max index.
for (int i = kNumberOfFoosInHtml - 1; i >= 0; i--) {
ExecuteJavaScript(kJavaScriptDecrementIndex);
AssertJavaScriptValue(kJavaScriptIndex, i);
}
};
// Performs a search, then calls |goNext| to loop through the visible matches,
// ensuring that hidden matches are skipped and that when the end is reached the
// index wraps back to the beginning of the page.
TEST_F(FindInPageJsTest, GoNext) {
SearchForFoo();
// Since the first visible 'foo' is at index 1, and every other 'foo' is
// hidden, after calling goNext the index should be at 3.
ExecuteJavaScript(kJavaScriptGoNext);
AssertJavaScriptValue(kJavaScriptIndex, 3);
// The next visible 'foo' is at index 5.
ExecuteJavaScript(kJavaScriptGoNext);
AssertJavaScriptValue(kJavaScriptIndex, 5);
// Calling |goNext| again wraps around to the first visible foo.
ExecuteJavaScript(kJavaScriptGoNext);
AssertJavaScriptValue(kJavaScriptIndex, 1);
};
// Performs a search, then calls |goPrev| to loop through the visible matches,
// ensuring that hidden matches are skipped and that when the beginning is
// reached the index wraps back to the end of the page.
TEST_F(FindInPageJsTest, GoPrev) {
SearchForFoo();
// Calling |goPrev| will wrap around to the end of the page, and since the
// last 'foo' is hidden, we want |kNumberOfFoosInHtml| - 2.
ExecuteJavaScript(kJavaScriptGoPrev);
AssertJavaScriptValue(kJavaScriptIndex, 5);
// Since every other 'foo' is hidden, the prior visible 'foo' is at index 3.
ExecuteJavaScript(kJavaScriptGoPrev);
AssertJavaScriptValue(kJavaScriptIndex, 3);
};
TEST_F(FindInPageJsTest, NoneVisible) {
LoadHtml(kHtmlWithNoVisibleFoos);
// Assert the index and span count contain their initialized values
AssertJavaScriptValue(kJavaScriptIndex, -1);
AssertJavaScriptValue(kJavaScriptSpansLength, 0);
// Search for 'foo'. Performing the search sets the index to point to 0 since
// there are no visible occurrences of 'foo'.
ExecuteJavaScript(kJavaScriptToSearchForFoo);
AssertJavaScriptValue(kJavaScriptIndex, 0);
AssertJavaScriptValue(kJavaScriptSpansLength, 6);
ExecuteJavaScript(kJavaScriptGoPrev);
AssertJavaScriptValue(kJavaScriptIndex, 0);
ExecuteJavaScript(kJavaScriptGoNext);
AssertJavaScriptValue(kJavaScriptIndex, 0);
}
TEST_F(FindInPageJsTest, SearchForNonAscii) {
NSString* const kNonAscii = @"รก";
NSString* const htmlFormat = @"<html>"
"<meta charset=\"UTF-8\">"
"<body>%@</body>"
"</html>";
LoadHtml([NSString stringWithFormat:htmlFormat, kNonAscii]);
// Assert the index and span count contain their initialized values.
AssertJavaScriptValue(kJavaScriptIndex, -1);
AssertJavaScriptValue(kJavaScriptSpansLength, 0);
// Search for the non-Ascii value. Performing the search sets the index to
// point to the first visible occurence of the non-Ascii.
NSString* result = ExecuteJavaScript([NSString
stringWithFormat:@"__gCrWeb.findInPage.highlightWord('%@', false, 1000)",
kNonAscii]);
DCHECK(result);
AssertJavaScriptValue(kJavaScriptIndex, 0);
AssertJavaScriptValue(kJavaScriptSpansLength, 1);
}
} // namespace