// 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.
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/test/base/chrome_render_view_test.h"
#include "components/translate/core/common/translate_errors.h"
#include "grit/component_resources.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebScriptSource.h"
#include "ui/base/resource/resource_bundle.h"
#include "v8/include/v8.h"
using blink::WebScriptSource;
namespace {
// JavaScript code to set runtime test flags.
const char kThrowInitializationError[] = "throwInitializationError = true";
const char kThrowUnexpectedScriptError[] = "throwUnexpectedScriptError = true";
const char kCallbackReturnBooleanError[] = "callbackReturnBooleanError = true";
const char kCallbackReturnNumberError[] = "callbackReturnNumberError = true";
const char kSetCallbackErrorCode[] = "callbackErrorCode = ";
// JavaScript code to check if any error happens.
const char kError[] = "cr.googleTranslate.error";
// JavaScript code to get error code.
const char kErrorCode[] = "cr.googleTranslate.errorCode";
// JavaScript code to check if the library is ready.
const char kLibReady[] = "cr.googleTranslate.libReady";
// JavaScript code to perform translation.
const char kTranslate[] = "cr.googleTranslate.translate('auto', 'en')";
// JavaScript code to mimic element.js provided by a translate server.
const char kElementJs[] =
"translateApiKey = '';"
"google = {};"
"google.translate = {};"
"google.translate.TranslateService = function() {"
" if (window['throwInitializationError']) {"
" throw 'API initialization error';"
" }"
" return {"
" isAvailable: function() { return true; },"
" restore: function() {},"
" translatePage: function(originalLang, targetLang, cb) {"
" if (window['throwUnexpectedScriptError']) {"
" throw 'all your base are belong to us';"
" }"
" if (window['callbackReturnBooleanError']) {"
" cb(0, false, true);"
" }"
" if (window['callbackReturnNumberError']) {"
" cb(0, false, callbackErrorCode);"
" }"
" }"
" };"
"};"
"cr.googleTranslate.onTranslateElementLoad();";
std::string GenerateSetCallbackErrorCodeScript(int code) {
return base::StringPrintf("%s%d", kSetCallbackErrorCode, code);
}
}; // namespace
// This class is for testing resource/translate.js works and reports errors
// correctly.
class TranslateScriptBrowserTest : public ChromeRenderViewTest {
public:
TranslateScriptBrowserTest() {}
protected:
void InjectElementLibrary() {
std::string script;
base::StringPiece translate_js = ResourceBundle::GetSharedInstance().
GetRawDataResource(IDR_TRANSLATE_JS);
translate_js.CopyToString(&script);
script += kElementJs;
ExecuteScript(script);
}
void ExecuteScript(const std::string& script) {
WebScriptSource source = WebScriptSource(base::ASCIIToUTF16(script));
GetMainFrame()->executeScript(source);
}
bool GetError() {
return ExecuteScriptAndGetBoolResult(kError);
}
double GetErrorCode() {
return ExecuteScriptAndGetNumberResult(kErrorCode);
}
bool IsLibReady() {
return ExecuteScriptAndGetBoolResult(kLibReady);
}
private:
virtual void SetUp() OVERRIDE {
ChromeRenderViewTest::SetUp();
}
virtual void TearDown() OVERRIDE {
ChromeRenderViewTest::TearDown();
}
double ExecuteScriptAndGetNumberResult(const std::string& script) {
WebScriptSource source = WebScriptSource(base::ASCIIToUTF16(script));
v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
v8::Handle<v8::Value> result =
GetMainFrame()->executeScriptAndReturnValue(source);
if (result.IsEmpty() || !result->IsNumber()) {
NOTREACHED();
// TODO(toyoshim): Return NaN here and the real implementation in
// TranslateHelper::ExecuteScriptAndGetDoubleResult().
return 0.0;
}
return result->NumberValue();
}
bool ExecuteScriptAndGetBoolResult(const std::string& script) {
WebScriptSource source = WebScriptSource(base::ASCIIToUTF16(script));
v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
v8::Handle<v8::Value> result =
GetMainFrame()->executeScriptAndReturnValue(source);
if (result.IsEmpty() || !result->IsBoolean()) {
NOTREACHED();
return false;
}
return result->BooleanValue();
}
DISALLOW_COPY_AND_ASSIGN(TranslateScriptBrowserTest);
};
// Test if onTranslateElementLoad() succeeds to initialize the element library.
TEST_F(TranslateScriptBrowserTest, ElementLoadSuccess) {
InjectElementLibrary();
EXPECT_TRUE(IsLibReady());
EXPECT_FALSE(GetError());
EXPECT_EQ(TranslateErrors::NONE, GetErrorCode());
}
// Test if onTranslateElementLoad() fails to initialize the element library and
// report the right error code.
TEST_F(TranslateScriptBrowserTest, ElementLoadFailure) {
ExecuteScript(kThrowInitializationError);
InjectElementLibrary();
EXPECT_FALSE(IsLibReady());
EXPECT_TRUE(GetError());
EXPECT_EQ(TranslateErrors::INITIALIZATION_ERROR, GetErrorCode());
}
// Test if cr.googleTranslate.translate() works.
TEST_F(TranslateScriptBrowserTest, TranslateSuccess) {
InjectElementLibrary();
EXPECT_TRUE(IsLibReady());
EXPECT_FALSE(GetError());
EXPECT_EQ(TranslateErrors::NONE, GetErrorCode());
ExecuteScript(kTranslate);
EXPECT_FALSE(GetError());
EXPECT_EQ(TranslateErrors::NONE, GetErrorCode());
}
// Test if cr.googleTranslate.translate() handles library exception correctly.
TEST_F(TranslateScriptBrowserTest, TranslateFail) {
ExecuteScript(kThrowUnexpectedScriptError);
InjectElementLibrary();
EXPECT_TRUE(IsLibReady());
EXPECT_FALSE(GetError());
EXPECT_EQ(TranslateErrors::NONE, GetErrorCode());
ExecuteScript(kTranslate);
EXPECT_TRUE(GetError());
EXPECT_EQ(TranslateErrors::UNEXPECTED_SCRIPT_ERROR, GetErrorCode());
}
// Test if onTranslateProgress callback handles boolean type error correctly.
// Remove this test once server side changes the API to return a number.
TEST_F(TranslateScriptBrowserTest, CallbackGetBooleanError) {
ExecuteScript(kCallbackReturnBooleanError);
InjectElementLibrary();
EXPECT_TRUE(IsLibReady());
EXPECT_FALSE(GetError());
EXPECT_EQ(TranslateErrors::NONE, GetErrorCode());
ExecuteScript(kTranslate);
EXPECT_TRUE(GetError());
EXPECT_EQ(TranslateErrors::TRANSLATION_ERROR, GetErrorCode());
}
// Test if onTranslateProgress callback handles number type error correctly and
// converts it to the proper error code.
TEST_F(TranslateScriptBrowserTest, CallbackGetNumberError1) {
ExecuteScript(kCallbackReturnNumberError);
ExecuteScript(GenerateSetCallbackErrorCodeScript(1));
InjectElementLibrary();
EXPECT_TRUE(IsLibReady());
EXPECT_FALSE(GetError());
EXPECT_EQ(TranslateErrors::NONE, GetErrorCode());
ExecuteScript(kTranslate);
EXPECT_TRUE(GetError());
EXPECT_EQ(TranslateErrors::TRANSLATION_ERROR, GetErrorCode());
}
// Test if onTranslateProgress callback handles number type error correctly and
// converts it to the proper error code.
TEST_F(TranslateScriptBrowserTest, CallbackGetNumberError2) {
ExecuteScript(kCallbackReturnNumberError);
ExecuteScript(GenerateSetCallbackErrorCodeScript(2));
InjectElementLibrary();
EXPECT_TRUE(IsLibReady());
EXPECT_FALSE(GetError());
EXPECT_EQ(TranslateErrors::NONE, GetErrorCode());
ExecuteScript(kTranslate);
EXPECT_TRUE(GetError());
EXPECT_EQ(TranslateErrors::UNSUPPORTED_LANGUAGE, GetErrorCode());
}
// TODO(toyoshim): Add test for onLoadJavaScript.