| Index: ios/chrome/browser/translate/js_language_detection_manager_unittest.mm
|
| diff --git a/ios/chrome/browser/translate/js_language_detection_manager_unittest.mm b/ios/chrome/browser/translate/js_language_detection_manager_unittest.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c506eb8ea4983e34168d8db544af3da161d3520c
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/translate/js_language_detection_manager_unittest.mm
|
| @@ -0,0 +1,351 @@
|
| +// Copyright 2013 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 "components/translate/ios/browser/js_language_detection_manager.h"
|
| +
|
| +#include <string.h>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/mac/scoped_nsobject.h"
|
| +#include "base/memory/scoped_vector.h"
|
| +#import "base/test/ios/wait_util.h"
|
| +#include "base/values.h"
|
| +#import "ios/chrome/browser/web/chrome_web_test.h"
|
| +#include "ios/chrome/common/string_util.h"
|
| +#import "ios/web/public/test/js_test_util.h"
|
| +#import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
|
| +#import "ios/web/public/web_state/web_state.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "testing/gtest_mac.h"
|
| +
|
| +namespace {
|
| +
|
| +const char kExpectedLanguage[] = "Foo";
|
| +
|
| +// Returns an NSString filled with the char 'a' of length |length|.
|
| +NSString* GetLongString(NSUInteger length) {
|
| + base::scoped_nsobject<NSMutableData> data(
|
| + [[NSMutableData alloc] initWithLength:length]);
|
| + memset([data mutableBytes], 'a', length);
|
| + NSString* long_string =
|
| + [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
|
| + return [long_string autorelease];
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// TODO(shreyasv): Moved this to the translate component.
|
| +// Test fixture to test language detection.
|
| +class JsLanguageDetectionManagerTest : public ChromeWebTest {
|
| + protected:
|
| + void SetUp() override {
|
| + ChromeWebTest::SetUp();
|
| + manager_.reset(static_cast<JsLanguageDetectionManager*>(
|
| + [[web_state()->GetJSInjectionReceiver()
|
| + instanceOfClass:[JsLanguageDetectionManager class]] retain]));
|
| + ASSERT_TRUE(manager_);
|
| + }
|
| +
|
| + void LoadHtmlAndInject(NSString* html) {
|
| + ChromeWebTest::LoadHtml(html);
|
| + [manager_ inject];
|
| + ASSERT_TRUE([manager_ hasBeenInjected]);
|
| + }
|
| +
|
| + // Injects JS, waits for the completion handler and verifies if the result
|
| + // was what was expected.
|
| + void InjectJsAndVerify(NSString* js, id expected_result) {
|
| + EXPECT_NSEQ(expected_result, web::ExecuteJavaScript(manager_, js));
|
| + }
|
| +
|
| + // Injects JS, and spins the run loop until |condition| block returns true
|
| + void InjectJSAndWaitUntilCondition(NSString* js, ConditionBlock condition) {
|
| + [manager_ executeJavaScript:js completionHandler:nil];
|
| + base::test::ios::WaitUntilCondition(^bool() {
|
| + return condition();
|
| + });
|
| + }
|
| +
|
| + // Verifies if translation was allowed or not on the page based on
|
| + // |expected_value|.
|
| + void ExpectTranslationAllowed(BOOL expected_value) {
|
| + InjectJsAndVerify(@"__gCrWeb.languageDetection.translationAllowed();",
|
| + @(expected_value));
|
| + }
|
| +
|
| + // Verifies if |lang| attribute of the HTML tag is the |expected_html_lang|,
|
| + void ExpectHtmlLang(NSString* expected_html_lang) {
|
| + InjectJsAndVerify(@"document.documentElement.lang;", expected_html_lang);
|
| + }
|
| +
|
| + // Verifies if the value of the |Content-Language| meta tag is the same as
|
| + // |expected_http_content_language|.
|
| + void ExpectHttpContentLanguage(NSString* expected_http_content_language) {
|
| + NSString* const kMetaTagContentJS =
|
| + @"__gCrWeb.languageDetection.getMetaContentByHttpEquiv("
|
| + @"'content-language');";
|
| + InjectJsAndVerify(kMetaTagContentJS, expected_http_content_language);
|
| + }
|
| +
|
| + // Verifies if |__gCrWeb.languageDetection.getTextContent| correctly extracts
|
| + // the text content from an HTML page.
|
| + void ExpectTextContent(NSString* expected_text_content) {
|
| + base::scoped_nsobject<NSString> script([[NSString alloc]
|
| + initWithFormat:
|
| + @"__gCrWeb.languageDetection.getTextContent(document.body, %lu);",
|
| + language_detection::kMaxIndexChars]);
|
| + InjectJsAndVerify(script, expected_text_content);
|
| + }
|
| +
|
| + base::scoped_nsobject<JsLanguageDetectionManager> manager_;
|
| +};
|
| +
|
| +// Tests that |hasBeenInjected| returns YES after |inject| call.
|
| +TEST_F(JsLanguageDetectionManagerTest, InitAndInject) {
|
| + LoadHtmlAndInject(@"<html></html>");
|
| +}
|
| +
|
| +// Tests |__gCrWeb.languageDetection.translationAllowed| JS call.
|
| +TEST_F(JsLanguageDetectionManagerTest, IsTranslationAllowed) {
|
| + LoadHtmlAndInject(@"<html></html>");
|
| + ExpectTranslationAllowed(YES);
|
| +
|
| + LoadHtmlAndInject(@"<html><head>"
|
| + "<meta name='google' content='notranslate'>"
|
| + "</head></html>");
|
| + ExpectTranslationAllowed(NO);
|
| +
|
| + LoadHtmlAndInject(@"<html><head>"
|
| + "<meta name='google' value='notranslate'>"
|
| + "</head></html>");
|
| + ExpectTranslationAllowed(NO);
|
| +}
|
| +
|
| +// Tests correctness of |document.documentElement.lang| attribute.
|
| +TEST_F(JsLanguageDetectionManagerTest, HtmlLang) {
|
| + base::scoped_nsobject<NSString> html;
|
| + // Non-empty attribute.
|
| + html.reset([[NSString alloc]
|
| + initWithFormat:@"<html lang='%s'></html>", kExpectedLanguage]);
|
| + LoadHtmlAndInject(html);
|
| + ExpectHtmlLang(@(kExpectedLanguage));
|
| +
|
| + // Empty attribute.
|
| + LoadHtmlAndInject(@"<html></html>");
|
| + ExpectHtmlLang(@"");
|
| +
|
| + // Test with mixed case.
|
| + html.reset([[NSString alloc]
|
| + initWithFormat:@"<html lAnG='%s'></html>", kExpectedLanguage]);
|
| + LoadHtmlAndInject(html);
|
| + ExpectHtmlLang(@(kExpectedLanguage));
|
| +}
|
| +
|
| +// Tests |__gCrWeb.languageDetection.getMetaContentByHttpEquiv| JS call.
|
| +TEST_F(JsLanguageDetectionManagerTest, HttpContentLanguage) {
|
| + // No content language.
|
| + LoadHtmlAndInject(@"<html></html>");
|
| + ExpectHttpContentLanguage(@"");
|
| + base::scoped_nsobject<NSString> html;
|
| +
|
| + // Some content language.
|
| + html.reset(([[NSString alloc]
|
| + initWithFormat:@"<html><head>"
|
| + "<meta http-equiv='content-language' content='%s'>"
|
| + "</head></html>",
|
| + kExpectedLanguage]));
|
| + LoadHtmlAndInject(html);
|
| + ExpectHttpContentLanguage(@(kExpectedLanguage));
|
| +
|
| + // Test with mixed case.
|
| + html.reset(([[NSString alloc]
|
| + initWithFormat:@"<html><head>"
|
| + "<meta http-equiv='cOnTenT-lAngUAge' content='%s'>"
|
| + "</head></html>",
|
| + kExpectedLanguage]));
|
| + LoadHtmlAndInject(html);
|
| + ExpectHttpContentLanguage(@(kExpectedLanguage));
|
| +}
|
| +
|
| +// Tests |__gCrWeb.languageDetection.getTextContent| JS call.
|
| +TEST_F(JsLanguageDetectionManagerTest, ExtractTextContent) {
|
| + LoadHtmlAndInject(@"<html><body>"
|
| + "<script>var text = 'No scripts!'</script>"
|
| + "<p style='display: none;'>Not displayed!</p>"
|
| + "<p style='visibility: hidden;'>Hidden!</p>"
|
| + "<div>Some <span>text here <b>and</b></span> there.</div>"
|
| + "</body></html>");
|
| +
|
| + ExpectTextContent(@"\nSome text here and there.");
|
| +}
|
| +
|
| +// Tests that |__gCrWeb.languageDetection.getTextContent| correctly truncates
|
| +// text.
|
| +TEST_F(JsLanguageDetectionManagerTest, Truncation) {
|
| + LoadHtmlAndInject(@"<html><body>"
|
| + "<script>var text = 'No scripts!'</script>"
|
| + "<p style='display: none;'>Not displayed!</p>"
|
| + "<p style='visibility: hidden;'>Hidden!</p>"
|
| + "<div>Some <span>text here <b>and</b></span> there.</div>"
|
| + "</body></html>");
|
| + NSString* const kTextContentJS =
|
| + @"__gCrWeb.languageDetection.getTextContent(document.body, 13)";
|
| + InjectJsAndVerify(kTextContentJS, @"\nSome text he");
|
| +}
|
| +
|
| +// HTML elements introduce a line break, except inline ones.
|
| +TEST_F(JsLanguageDetectionManagerTest, ExtractWhitespace) {
|
| + // |b| and |span| do not break lines.
|
| + // |br| and |div| do.
|
| + LoadHtmlAndInject(@"<html><body>"
|
| + "O<b>n</b>e<br>Two\tT<span>hr</span>ee<div>Four</div>"
|
| + "</body></html>");
|
| + ExpectTextContent(@"One\nTwo\tThree\nFour");
|
| +
|
| + // |a| does not break lines.
|
| + // |li|, |p| and |ul| do.
|
| + LoadHtmlAndInject(
|
| + @"<html><body>"
|
| + "<ul><li>One</li><li>T<a href='foo'>wo</a></li></ul><p>Three</p>"
|
| + "</body></html>");
|
| + ExpectTextContent(@"\n\nOne\nTwo\nThree");
|
| +}
|
| +
|
| +// Tests that |__gCrWeb.languageDetection.getTextContent| returns only until the
|
| +// kMaxIndexChars number of characters even if the text content is very large.
|
| +TEST_F(JsLanguageDetectionManagerTest, LongTextContent) {
|
| + // Very long string.
|
| + NSUInteger kLongStringLength = language_detection::kMaxIndexChars - 5;
|
| + base::scoped_nsobject<NSMutableString> long_string(
|
| + [GetLongString(kLongStringLength) mutableCopy]);
|
| + [long_string appendString:@" b cdefghijklmnopqrstuvwxyz"];
|
| +
|
| + // The string should be cut at the last whitespace, after the 'b' character.
|
| + base::scoped_nsobject<NSString> html([[NSString alloc]
|
| + initWithFormat:@"<html><body>%@</html></body>", long_string.get()]);
|
| + LoadHtmlAndInject(html);
|
| +
|
| + base::scoped_nsobject<NSString> script([[NSString alloc]
|
| + initWithFormat:
|
| + @"__gCrWeb.languageDetection.getTextContent(document.body, %lu);",
|
| + language_detection::kMaxIndexChars]);
|
| + NSString* result = web::ExecuteJavaScript(manager_, script);
|
| + EXPECT_EQ(language_detection::kMaxIndexChars, [result length]);
|
| +}
|
| +
|
| +// Tests if |__gCrWeb.languageDetection.retrieveBufferedTextContent| correctly
|
| +// retrieves the cache and then purges it.
|
| +TEST_F(JsLanguageDetectionManagerTest, RetrieveBufferedTextContent) {
|
| + LoadHtmlAndInject(@"<html></html>");
|
| + // Set some cached text content.
|
| + [manager_ executeJavaScript:
|
| + @"__gCrWeb.languageDetection.bufferedTextContent = 'foo'"
|
| + completionHandler:nil];
|
| + [manager_ executeJavaScript:@"__gCrWeb.languageDetection.activeRequests = 1"
|
| + completionHandler:nil];
|
| + NSString* const kRetrieveBufferedTextContentJS =
|
| + @"__gCrWeb.languageDetection.retrieveBufferedTextContent()";
|
| + InjectJsAndVerify(kRetrieveBufferedTextContentJS, @"foo");
|
| +
|
| + // Verify cache is purged.
|
| + InjectJsAndVerify(@"__gCrWeb.languageDetection.bufferedTextContent",
|
| + [NSNull null]);
|
| +}
|
| +
|
| +// Test fixture to test |__gCrWeb.languageDetection.detectLanguage|.
|
| +class JsLanguageDetectionManagerDetectLanguageTest
|
| + : public JsLanguageDetectionManagerTest {
|
| + public:
|
| + void SetUp() override {
|
| + JsLanguageDetectionManagerTest::SetUp();
|
| + auto callback = base::Bind(
|
| + &JsLanguageDetectionManagerDetectLanguageTest::CommandReceived,
|
| + base::Unretained(this));
|
| + web_state()->AddScriptCommandCallback(callback, "languageDetection");
|
| + }
|
| + void TearDown() override {
|
| + web_state()->RemoveScriptCommandCallback("languageDetection");
|
| + JsLanguageDetectionManagerTest::TearDown();
|
| + }
|
| + // Called when "languageDetection" command is received.
|
| + bool CommandReceived(const base::DictionaryValue& command,
|
| + const GURL&,
|
| + bool) {
|
| + commands_received_.push_back(command.DeepCopy());
|
| + return true;
|
| + }
|
| +
|
| + protected:
|
| + // Received "languageDetection" commands.
|
| + ScopedVector<base::DictionaryValue> commands_received_;
|
| +};
|
| +
|
| +// Tests if |__gCrWeb.languageDetection.detectLanguage| correctly informs the
|
| +// native side when translation is not allowed.
|
| +TEST_F(JsLanguageDetectionManagerDetectLanguageTest,
|
| + DetectLanguageTranslationNotAllowed) {
|
| + LoadHtmlAndInject(@"<html></html>");
|
| + [manager_ startLanguageDetection];
|
| + // Wait until the original injection has recived a command.
|
| + base::test::ios::WaitUntilCondition(^bool() {
|
| + return !commands_received_.empty();
|
| + });
|
| + ASSERT_EQ(1U, commands_received_.size());
|
| +
|
| + commands_received_.clear();
|
| +
|
| + // Stub out translationAllowed.
|
| + NSString* const kTranslationAllowedJS =
|
| + @"__gCrWeb.languageDetection.translationAllowed = function() {"
|
| + @" return false;"
|
| + @"}";
|
| + [manager_ executeJavaScript:kTranslationAllowedJS completionHandler:nil];
|
| + ConditionBlock commands_recieved_block = ^bool {
|
| + return commands_received_.size();
|
| + };
|
| + InjectJSAndWaitUntilCondition(@"__gCrWeb.languageDetection.detectLanguage()",
|
| + commands_recieved_block);
|
| + ASSERT_EQ(1U, commands_received_.size());
|
| + base::DictionaryValue* value = commands_received_[0];
|
| + EXPECT_TRUE(value->HasKey("translationAllowed"));
|
| + bool translation_allowed = true;
|
| + value->GetBoolean("translationAllowed", &translation_allowed);
|
| + EXPECT_FALSE(translation_allowed);
|
| +}
|
| +
|
| +// Tests if |__gCrWeb.languageDetection.detectLanguage| correctly informs the
|
| +// native side when translation is allowed with the right parameters.
|
| +TEST_F(JsLanguageDetectionManagerDetectLanguageTest,
|
| + DetectLanguageTranslationAllowed) {
|
| + // A simple page that allows translation.
|
| + NSString* html = @"<html><head>"
|
| + @"<meta http-equiv='content-language' content='en'>"
|
| + @"</head></html>";
|
| + LoadHtmlAndInject(html);
|
| + [manager_ startLanguageDetection];
|
| + // Wait until the original injection has recived a command.
|
| + base::test::ios::WaitUntilCondition(^bool() {
|
| + return !commands_received_.empty();
|
| + });
|
| + ASSERT_EQ(1U, commands_received_.size());
|
| +
|
| + commands_received_.clear();
|
| +
|
| + [manager_ executeJavaScript:@"__gCrWeb.languageDetection.detectLanguage()"
|
| + completionHandler:nil];
|
| + base::test::ios::WaitUntilCondition(^bool() {
|
| + return !commands_received_.empty();
|
| + });
|
| + ASSERT_EQ(1U, commands_received_.size());
|
| + base::DictionaryValue* value = commands_received_[0];
|
| +
|
| + EXPECT_TRUE(value->HasKey("translationAllowed"));
|
| + EXPECT_TRUE(value->HasKey("captureTextTime"));
|
| + EXPECT_TRUE(value->HasKey("htmlLang"));
|
| + EXPECT_TRUE(value->HasKey("httpContentLanguage"));
|
| +
|
| + bool translation_allowed = false;
|
| + value->GetBoolean("translationAllowed", &translation_allowed);
|
| + EXPECT_TRUE(translation_allowed);
|
| +}
|
|
|