1// Copyright 2013 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#include "base/strings/stringprintf.h"
6#include "base/strings/utf_string_conversions.h"
7#include "chrome/test/base/chrome_render_view_test.h"
8#include "components/translate/core/common/translate_errors.h"
9#include "grit/components_resources.h"
10#include "third_party/WebKit/public/web/WebLocalFrame.h"
11#include "third_party/WebKit/public/web/WebScriptSource.h"
12#include "ui/base/resource/resource_bundle.h"
13#include "v8/include/v8.h"
14
15using blink::WebScriptSource;
16
17namespace {
18
19// JavaScript code to set runtime test flags.
20const char kThrowInitializationError[] = "throwInitializationError = true";
21const char kThrowUnexpectedScriptError[] = "throwUnexpectedScriptError = true";
22const char kCallbackReturnBooleanError[] = "callbackReturnBooleanError = true";
23const char kCallbackReturnNumberError[] = "callbackReturnNumberError = true";
24const char kSetCallbackErrorCode[] = "callbackErrorCode = ";
25
26// JavaScript code to check if any error happens.
27const char kError[] = "cr.googleTranslate.error";
28
29// JavaScript code to get error code.
30const char kErrorCode[] = "cr.googleTranslate.errorCode";
31
32// JavaScript code to check if the library is ready.
33const char kLibReady[] = "cr.googleTranslate.libReady";
34
35// JavaScript code to perform translation.
36const char kTranslate[] = "cr.googleTranslate.translate('auto', 'en')";
37
38// JavaScript code to mimic element.js provided by a translate server.
39const char kElementJs[] =
40    "translateApiKey = '';"
41    "google = {};"
42    "google.translate = {};"
43    "google.translate.TranslateService = function() {"
44    "  if (window['throwInitializationError']) {"
45    "    throw 'API initialization error';"
46    "  }"
47    "  return {"
48    "    isAvailable: function() { return true; },"
49    "    restore: function() {},"
50    "    translatePage: function(originalLang, targetLang, cb) {"
51    "      if (window['throwUnexpectedScriptError']) {"
52    "        throw 'all your base are belong to us';"
53    "      }"
54    "      if (window['callbackReturnBooleanError']) {"
55    "        cb(0, false, true);"
56    "      }"
57    "      if (window['callbackReturnNumberError']) {"
58    "        cb(0, false, callbackErrorCode);"
59    "      }"
60    "    }"
61    "  };"
62    "};"
63    "cr.googleTranslate.onTranslateElementLoad();";
64
65std::string GenerateSetCallbackErrorCodeScript(int code) {
66  return base::StringPrintf("%s%d", kSetCallbackErrorCode, code);
67}
68
69};  // namespace
70
71// This class is for testing resource/translate.js works and reports errors
72// correctly.
73class TranslateScriptBrowserTest : public ChromeRenderViewTest {
74 public:
75  TranslateScriptBrowserTest() {}
76
77 protected:
78  void InjectElementLibrary() {
79    std::string script;
80    base::StringPiece translate_js = ResourceBundle::GetSharedInstance().
81        GetRawDataResource(IDR_TRANSLATE_JS);
82    translate_js.CopyToString(&script);
83    script += kElementJs;
84    ExecuteScript(script);
85  }
86
87  void ExecuteScript(const std::string& script) {
88    WebScriptSource source = WebScriptSource(base::ASCIIToUTF16(script));
89    GetMainFrame()->executeScript(source);
90  }
91
92  bool GetError() {
93    return ExecuteScriptAndGetBoolResult(kError);
94  }
95
96  double GetErrorCode() {
97    return ExecuteScriptAndGetNumberResult(kErrorCode);
98  }
99
100  bool IsLibReady() {
101    return ExecuteScriptAndGetBoolResult(kLibReady);
102  }
103
104 private:
105  virtual void SetUp() OVERRIDE {
106    ChromeRenderViewTest::SetUp();
107  }
108
109  virtual void TearDown() OVERRIDE {
110    ChromeRenderViewTest::TearDown();
111  }
112
113  double ExecuteScriptAndGetNumberResult(const std::string& script) {
114    WebScriptSource source = WebScriptSource(base::ASCIIToUTF16(script));
115    v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
116    v8::Handle<v8::Value> result =
117        GetMainFrame()->executeScriptAndReturnValue(source);
118    if (result.IsEmpty() || !result->IsNumber()) {
119      NOTREACHED();
120      // TODO(toyoshim): Return NaN here and the real implementation in
121      // TranslateHelper::ExecuteScriptAndGetDoubleResult().
122      return 0.0;
123    }
124    return result->NumberValue();
125  }
126
127  bool ExecuteScriptAndGetBoolResult(const std::string& script) {
128    WebScriptSource source = WebScriptSource(base::ASCIIToUTF16(script));
129    v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
130    v8::Handle<v8::Value> result =
131        GetMainFrame()->executeScriptAndReturnValue(source);
132    if (result.IsEmpty() || !result->IsBoolean()) {
133      NOTREACHED();
134      return false;
135    }
136    return result->BooleanValue();
137  }
138
139  DISALLOW_COPY_AND_ASSIGN(TranslateScriptBrowserTest);
140};
141
142// Test if onTranslateElementLoad() succeeds to initialize the element library.
143TEST_F(TranslateScriptBrowserTest, ElementLoadSuccess) {
144  InjectElementLibrary();
145  EXPECT_TRUE(IsLibReady());
146  EXPECT_FALSE(GetError());
147  EXPECT_EQ(translate::TranslateErrors::NONE, GetErrorCode());
148}
149
150// Test if onTranslateElementLoad() fails to initialize the element library and
151// report the right error code.
152TEST_F(TranslateScriptBrowserTest, ElementLoadFailure) {
153  ExecuteScript(kThrowInitializationError);
154
155  InjectElementLibrary();
156  EXPECT_FALSE(IsLibReady());
157  EXPECT_TRUE(GetError());
158  EXPECT_EQ(translate::TranslateErrors::INITIALIZATION_ERROR, GetErrorCode());
159}
160
161// Test if cr.googleTranslate.translate() works.
162TEST_F(TranslateScriptBrowserTest, TranslateSuccess) {
163  InjectElementLibrary();
164  EXPECT_TRUE(IsLibReady());
165  EXPECT_FALSE(GetError());
166  EXPECT_EQ(translate::TranslateErrors::NONE, GetErrorCode());
167
168  ExecuteScript(kTranslate);
169
170  EXPECT_FALSE(GetError());
171  EXPECT_EQ(translate::TranslateErrors::NONE, GetErrorCode());
172}
173
174// Test if cr.googleTranslate.translate() handles library exception correctly.
175TEST_F(TranslateScriptBrowserTest, TranslateFail) {
176  ExecuteScript(kThrowUnexpectedScriptError);
177
178  InjectElementLibrary();
179  EXPECT_TRUE(IsLibReady());
180  EXPECT_FALSE(GetError());
181  EXPECT_EQ(translate::TranslateErrors::NONE, GetErrorCode());
182
183  ExecuteScript(kTranslate);
184
185  EXPECT_TRUE(GetError());
186  EXPECT_EQ(translate::TranslateErrors::UNEXPECTED_SCRIPT_ERROR,
187            GetErrorCode());
188}
189
190// Test if onTranslateProgress callback handles boolean type error correctly.
191// Remove this test once server side changes the API to return a number.
192TEST_F(TranslateScriptBrowserTest, CallbackGetBooleanError) {
193  ExecuteScript(kCallbackReturnBooleanError);
194
195  InjectElementLibrary();
196  EXPECT_TRUE(IsLibReady());
197  EXPECT_FALSE(GetError());
198  EXPECT_EQ(translate::TranslateErrors::NONE, GetErrorCode());
199
200  ExecuteScript(kTranslate);
201
202  EXPECT_TRUE(GetError());
203  EXPECT_EQ(translate::TranslateErrors::TRANSLATION_ERROR, GetErrorCode());
204}
205
206// Test if onTranslateProgress callback handles number type error correctly and
207// converts it to the proper error code.
208TEST_F(TranslateScriptBrowserTest, CallbackGetNumberError1) {
209  ExecuteScript(kCallbackReturnNumberError);
210  ExecuteScript(GenerateSetCallbackErrorCodeScript(1));
211
212  InjectElementLibrary();
213  EXPECT_TRUE(IsLibReady());
214  EXPECT_FALSE(GetError());
215  EXPECT_EQ(translate::TranslateErrors::NONE, GetErrorCode());
216
217  ExecuteScript(kTranslate);
218
219  EXPECT_TRUE(GetError());
220  EXPECT_EQ(translate::TranslateErrors::TRANSLATION_ERROR, GetErrorCode());
221}
222
223// Test if onTranslateProgress callback handles number type error correctly and
224// converts it to the proper error code.
225TEST_F(TranslateScriptBrowserTest, CallbackGetNumberError2) {
226  ExecuteScript(kCallbackReturnNumberError);
227  ExecuteScript(GenerateSetCallbackErrorCodeScript(2));
228
229  InjectElementLibrary();
230  EXPECT_TRUE(IsLibReady());
231  EXPECT_FALSE(GetError());
232  EXPECT_EQ(translate::TranslateErrors::NONE, GetErrorCode());
233
234  ExecuteScript(kTranslate);
235
236  EXPECT_TRUE(GetError());
237  EXPECT_EQ(translate::TranslateErrors::UNSUPPORTED_LANGUAGE, GetErrorCode());
238}
239
240// TODO(toyoshim): Add test for onLoadJavaScript.
241