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