OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 // Tests for CppBoundClass, in conjunction with CppBindingExample. Binds | |
6 // a CppBindingExample class into JavaScript in a custom test shell and tests | |
7 // the binding from the outside by loading JS into the shell. | |
8 | |
9 #include <vector> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/bind_helpers.h" | |
13 #include "base/message_loop.h" | |
14 #include "base/string_util.h" | |
15 #include "testing/gtest/include/gtest/gtest.h" | |
16 #include "third_party/WebKit/Source/Platform/chromium/public/Platform.h" | |
17 #include "third_party/WebKit/Source/Platform/chromium/public/WebData.h" | |
18 #include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h" | |
19 #include "third_party/WebKit/Source/Platform/chromium/public/WebString.h" | |
20 #include "third_party/WebKit/Source/Platform/chromium/public/WebURL.h" | |
21 #include "third_party/WebKit/Source/Platform/chromium/public/WebURLRequest.h" | |
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | |
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrameClient.h" | |
24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h" | |
25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSettings.h" | |
26 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | |
27 #include "third_party/WebKit/Source/WebKit/chromium/public/WebViewClient.h" | |
28 #include "webkit/glue/cpp_binding_example.h" | |
29 #include "webkit/glue/webkit_glue.h" | |
30 #include "webkit/user_agent/user_agent.h" | |
31 #include "webkit/user_agent/user_agent_util.h" | |
32 | |
33 using WebKit::WebFrame; | |
34 using WebKit::WebView; | |
35 using webkit_glue::CppArgumentList; | |
36 using webkit_glue::CppBindingExample; | |
37 using webkit_glue::CppVariant; | |
38 | |
39 namespace { | |
40 | |
41 class CppBindingExampleSubObject : public CppBindingExample { | |
42 public: | |
43 CppBindingExampleSubObject() { | |
44 sub_value_.Set("sub!"); | |
45 BindProperty("sub_value", &sub_value_); | |
46 } | |
47 private: | |
48 CppVariant sub_value_; | |
49 }; | |
50 | |
51 | |
52 class CppBindingExampleWithOptionalFallback : public CppBindingExample { | |
53 public: | |
54 CppBindingExampleWithOptionalFallback() { | |
55 BindProperty("sub_object", sub_object_.GetAsCppVariant()); | |
56 } | |
57 | |
58 void set_fallback_method_enabled(bool state) { | |
59 BindFallbackCallback(state ? | |
60 base::Bind(&CppBindingExampleWithOptionalFallback::fallbackMethod, | |
61 base::Unretained(this)) | |
62 : CppBoundClass::Callback()); | |
63 } | |
64 | |
65 // The fallback method does nothing, but because of it the JavaScript keeps | |
66 // running when a nonexistent method is called on an object. | |
67 void fallbackMethod(const CppArgumentList& args, CppVariant* result) { | |
68 } | |
69 | |
70 private: | |
71 CppBindingExampleSubObject sub_object_; | |
72 }; | |
73 | |
74 class TestWebFrameClient : public WebKit::WebFrameClient { | |
75 public: | |
76 virtual void didClearWindowObject(WebKit::WebFrame* frame) OVERRIDE { | |
77 example_bound_class_.BindToJavascript(frame, "example"); | |
78 } | |
79 void set_fallback_method_enabled(bool use_fallback) { | |
80 example_bound_class_.set_fallback_method_enabled(use_fallback); | |
81 } | |
82 private: | |
83 CppBindingExampleWithOptionalFallback example_bound_class_; | |
84 }; | |
85 | |
86 class TestWebViewClient : public WebKit::WebViewClient { | |
87 }; | |
88 | |
89 class CppBoundClassTest : public testing::Test, public WebKit::WebFrameClient { | |
90 public: | |
91 CppBoundClassTest() : webview_(NULL) { } | |
92 | |
93 virtual void SetUp() OVERRIDE { | |
94 webview_ = WebView::create(&webview_client_); | |
95 webview_->settings()->setJavaScriptEnabled(true); | |
96 webview_->initializeMainFrame(&webframe_client_); | |
97 webframe_client_.set_fallback_method_enabled(useFallback()); | |
98 webkit_glue::SetUserAgent(webkit_glue::BuildUserAgentFromProduct( | |
99 "TestShell/0.0.0.0"), false); | |
100 | |
101 WebKit::WebURLRequest urlRequest; | |
102 urlRequest.initialize(); | |
103 urlRequest.setURL(GURL("about:blank")); | |
104 webframe()->loadRequest(urlRequest); | |
105 } | |
106 | |
107 virtual void TearDown() OVERRIDE { | |
108 if (webview_) | |
109 webview_->close(); | |
110 } | |
111 | |
112 WebFrame* webframe() { | |
113 return webview_->mainFrame(); | |
114 } | |
115 | |
116 // Wraps the given JavaScript snippet in <html><body><script> tags, then | |
117 // loads it into a webframe so it is executed. | |
118 void ExecuteJavaScript(const std::string& javascript) { | |
119 std::string html = "<html><body>"; | |
120 html.append("<script>"); | |
121 html.append(javascript); | |
122 html.append("</script></body></html>"); | |
123 webframe()->loadHTMLString(html, GURL("about:blank")); | |
124 MessageLoop::current()->RunUntilIdle(); | |
125 } | |
126 | |
127 // Executes the specified JavaScript and checks to be sure that the resulting | |
128 // document text is exactly "SUCCESS". | |
129 void CheckJavaScriptSuccess(const std::string& javascript) { | |
130 ExecuteJavaScript(javascript); | |
131 EXPECT_EQ("SUCCESS", | |
132 UTF16ToASCII(webkit_glue::DumpDocumentText(webframe()))); | |
133 } | |
134 | |
135 // Executes the specified JavaScript and checks that the resulting document | |
136 // text is empty. | |
137 void CheckJavaScriptFailure(const std::string& javascript) { | |
138 ExecuteJavaScript(javascript); | |
139 EXPECT_EQ("", UTF16ToASCII(webkit_glue::DumpDocumentText(webframe()))); | |
140 } | |
141 | |
142 // Constructs a JavaScript snippet that evaluates and compares the left and | |
143 // right expressions, printing "SUCCESS" to the page if they are equal and | |
144 // printing their actual values if they are not. Any strings in the | |
145 // expressions should be enclosed in single quotes, and no double quotes | |
146 // should appear in either expression (even if escaped). (If a test case | |
147 // is added that needs fancier quoting, Json::valueToQuotedString could be | |
148 // used here. For now, it's not worth adding the dependency.) | |
149 std::string BuildJSCondition(std::string left, std::string right) { | |
150 return "var leftval = " + left + ";" + | |
151 "var rightval = " + right + ";" + | |
152 "if (leftval == rightval) {" + | |
153 " document.writeln('SUCCESS');" + | |
154 "} else {" + | |
155 " document.writeln(\"" + | |
156 left + " [\" + leftval + \"] != " + | |
157 right + " [\" + rightval + \"]\");" + | |
158 "}"; | |
159 } | |
160 | |
161 protected: | |
162 virtual bool useFallback() { | |
163 return false; | |
164 } | |
165 | |
166 private: | |
167 WebView* webview_; | |
168 TestWebFrameClient webframe_client_; | |
169 TestWebViewClient webview_client_; | |
170 }; | |
171 | |
172 class CppBoundClassWithFallbackMethodTest : public CppBoundClassTest { | |
173 protected: | |
174 virtual bool useFallback() OVERRIDE { | |
175 return true; | |
176 } | |
177 }; | |
178 | |
179 // Ensures that the example object has been bound to JS. | |
180 TEST_F(CppBoundClassTest, ObjectExists) { | |
181 std::string js = BuildJSCondition("typeof window.example", "'object'"); | |
182 CheckJavaScriptSuccess(js); | |
183 | |
184 // An additional check to test our test. | |
185 js = BuildJSCondition("typeof window.invalid_object", "'undefined'"); | |
186 CheckJavaScriptSuccess(js); | |
187 } | |
188 | |
189 TEST_F(CppBoundClassTest, PropertiesAreInitialized) { | |
190 std::string js = BuildJSCondition("example.my_value", "10"); | |
191 CheckJavaScriptSuccess(js); | |
192 | |
193 js = BuildJSCondition("example.my_other_value", "'Reinitialized!'"); | |
194 CheckJavaScriptSuccess(js); | |
195 } | |
196 | |
197 TEST_F(CppBoundClassTest, SubOject) { | |
198 std::string js = BuildJSCondition("typeof window.example.sub_object", | |
199 "'object'"); | |
200 CheckJavaScriptSuccess(js); | |
201 | |
202 js = BuildJSCondition("example.sub_object.sub_value", "'sub!'"); | |
203 CheckJavaScriptSuccess(js); | |
204 } | |
205 | |
206 TEST_F(CppBoundClassTest, SetAndGetProperties) { | |
207 // The property on the left will be set to the value on the right, then | |
208 // checked to make sure it holds that same value. | |
209 static const std::string tests[] = { | |
210 "example.my_value", "7", | |
211 "example.my_value", "'test'", | |
212 "example.my_other_value", "3.14", | |
213 "example.my_other_value", "false", | |
214 "" // Array end marker: insert additional test pairs before this. | |
215 }; | |
216 | |
217 for (int i = 0; tests[i] != ""; i += 2) { | |
218 std::string left = tests[i]; | |
219 std::string right = tests[i + 1]; | |
220 // left = right; | |
221 std::string js = left; | |
222 js.append(" = "); | |
223 js.append(right); | |
224 js.append(";"); | |
225 js.append(BuildJSCondition(left, right)); | |
226 CheckJavaScriptSuccess(js); | |
227 } | |
228 } | |
229 | |
230 TEST_F(CppBoundClassTest, SetAndGetPropertiesWithCallbacks) { | |
231 // TODO(dglazkov): fix NPObject issues around failing property setters and | |
232 // getters and add tests for situations when GetProperty or SetProperty fail. | |
233 std::string js = "var result = 'SUCCESS';\n" | |
234 "example.my_value_with_callback = 10;\n" | |
235 "if (example.my_value_with_callback != 10)\n" | |
236 " result = 'FAIL: unable to set property.';\n" | |
237 "example.my_value_with_callback = 11;\n" | |
238 "if (example.my_value_with_callback != 11)\n" | |
239 " result = 'FAIL: unable to set property again';\n" | |
240 "if (example.same != 42)\n" | |
241 " result = 'FAIL: same property should always be 42';\n" | |
242 "example.same = 24;\n" | |
243 "if (example.same != 42)\n" | |
244 " result = 'FAIL: same property should always be 42';\n" | |
245 "document.writeln(result);\n"; | |
246 CheckJavaScriptSuccess(js); | |
247 } | |
248 | |
249 TEST_F(CppBoundClassTest, InvokeMethods) { | |
250 // The expression on the left is expected to return the value on the right. | |
251 static const std::string tests[] = { | |
252 "example.echoValue(true)", "true", | |
253 "example.echoValue(13)", "13", | |
254 "example.echoValue(2.718)", "2.718", | |
255 "example.echoValue('yes')", "'yes'", | |
256 "example.echoValue()", "null", // Too few arguments | |
257 | |
258 "example.echoType(false)", "true", | |
259 "example.echoType(19)", "3.14159", | |
260 "example.echoType(9.876)", "3.14159", | |
261 "example.echoType('test string')", "'Success!'", | |
262 "example.echoType()", "null", // Too few arguments | |
263 | |
264 // Comparing floats that aren't integer-valued is usually problematic due | |
265 // to rounding, but exact powers of 2 should also be safe. | |
266 "example.plus(2.5, 18.0)", "20.5", | |
267 "example.plus(2, 3.25)", "5.25", | |
268 "example.plus(2, 3)", "5", | |
269 "example.plus()", "null", // Too few arguments | |
270 "example.plus(1)", "null", // Too few arguments | |
271 "example.plus(1, 'test')", "null", // Wrong argument type | |
272 "example.plus('test', 2)", "null", // Wrong argument type | |
273 "example.plus('one', 'two')", "null", // Wrong argument type | |
274 "" // Array end marker: insert additional test pairs before this. | |
275 }; | |
276 | |
277 for (int i = 0; tests[i] != ""; i+= 2) { | |
278 std::string left = tests[i]; | |
279 std::string right = tests[i + 1]; | |
280 std::string js = BuildJSCondition(left, right); | |
281 CheckJavaScriptSuccess(js); | |
282 } | |
283 | |
284 std::string js = "example.my_value = 3.25; example.my_other_value = 1.25;"; | |
285 js.append(BuildJSCondition( | |
286 "example.plus(example.my_value, example.my_other_value)", "4.5")); | |
287 CheckJavaScriptSuccess(js); | |
288 } | |
289 | |
290 // Tests that invoking a nonexistent method with no fallback method stops the | |
291 // script's execution | |
292 TEST_F(CppBoundClassTest, | |
293 InvokeNonexistentMethodNoFallback) { | |
294 std::string js = "example.nonExistentMethod();document.writeln('SUCCESS');"; | |
295 CheckJavaScriptFailure(js); | |
296 } | |
297 | |
298 // Ensures existent methods can be invoked successfully when the fallback method | |
299 // is used | |
300 TEST_F(CppBoundClassWithFallbackMethodTest, | |
301 InvokeExistentMethodsWithFallback) { | |
302 std::string js = BuildJSCondition("example.echoValue(34)", "34"); | |
303 CheckJavaScriptSuccess(js); | |
304 } | |
305 | |
306 } // namespace | |
OLD | NEW |