1 // Copyright (c) 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 "chrome/test/chromedriver/session_commands.h"
6
7 #include <memory>
8 #include <string>
9
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/json/json_reader.h"
15 #include "base/run_loop.h"
16 #include "base/system/sys_info.h"
17 #include "base/threading/thread.h"
18 #include "base/values.h"
19 #include "chrome/test/chromedriver/capabilities.h"
20 #include "chrome/test/chromedriver/chrome/status.h"
21 #include "chrome/test/chromedriver/chrome/stub_chrome.h"
22 #include "chrome/test/chromedriver/chrome/stub_web_view.h"
23 #include "chrome/test/chromedriver/commands.h"
24 #include "chrome/test/chromedriver/logging.h"
25 #include "chrome/test/chromedriver/session.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27
TEST(SessionCommandsTest,ExecuteGetTimeouts)28 TEST(SessionCommandsTest, ExecuteGetTimeouts) {
29 Session session("id");
30 base::DictionaryValue params;
31 std::unique_ptr<base::Value> value;
32
33 Status status = ExecuteGetTimeouts(&session, params, &value);
34 ASSERT_EQ(kOk, status.code());
35 base::DictionaryValue* response;
36 ASSERT_TRUE(value->GetAsDictionary(&response));
37
38 int script;
39 ASSERT_TRUE(response->GetInteger("script", &script));
40 ASSERT_EQ(script, 30000);
41 int page_load;
42 ASSERT_TRUE(response->GetInteger("pageLoad", &page_load));
43 ASSERT_EQ(page_load, 300000);
44 int implicit;
45 ASSERT_TRUE(response->GetInteger("implicit", &implicit));
46 ASSERT_EQ(implicit, 0);
47 }
48
TEST(SessionCommandsTest,ExecuteSetTimeouts)49 TEST(SessionCommandsTest, ExecuteSetTimeouts) {
50 Session session("id");
51 base::DictionaryValue params;
52 std::unique_ptr<base::Value> value;
53
54 // W3C spec doesn't forbid passing in an empty object, so we should get kOk.
55 Status status = ExecuteSetTimeouts(&session, params, &value);
56 ASSERT_EQ(kOk, status.code());
57
58 params.SetInteger("pageLoad", 5000);
59 status = ExecuteSetTimeouts(&session, params, &value);
60 ASSERT_EQ(kOk, status.code());
61
62 params.SetInteger("script", 5000);
63 params.SetInteger("implicit", 5000);
64 status = ExecuteSetTimeouts(&session, params, &value);
65 ASSERT_EQ(kOk, status.code());
66
67 params.SetInteger("implicit", -5000);
68 status = ExecuteSetTimeouts(&session, params, &value);
69 ASSERT_EQ(kInvalidArgument, status.code());
70
71 params.Clear();
72 params.SetInteger("unknown", 5000);
73 status = ExecuteSetTimeouts(&session, params, &value);
74 ASSERT_EQ(kOk, status.code());
75
76 // Old pre-W3C format.
77 params.Clear();
78 params.SetDouble("ms", 5000.0);
79 params.SetString("type", "page load");
80 status = ExecuteSetTimeouts(&session, params, &value);
81 ASSERT_EQ(kOk, status.code());
82 }
83
TEST(SessionCommandsTest,MergeCapabilities)84 TEST(SessionCommandsTest, MergeCapabilities) {
85 base::DictionaryValue primary;
86 primary.SetString("strawberry", "velociraptor");
87 primary.SetString("pear", "unicorn");
88
89 base::DictionaryValue secondary;
90 secondary.SetString("broccoli", "giraffe");
91 secondary.SetString("celery", "hippo");
92 secondary.SetString("eggplant", "elephant");
93
94 base::DictionaryValue merged;
95
96 // key collision should return false
97 ASSERT_FALSE(MergeCapabilities(&primary, &primary, &merged));
98 // non key collision should return true
99 ASSERT_TRUE(MergeCapabilities(&primary, &secondary, &merged));
100
101 merged.Clear();
102 MergeCapabilities(&primary, &secondary, &merged);
103 primary.MergeDictionary(&secondary);
104
105 ASSERT_EQ(primary, merged);
106 }
107
TEST(SessionCommandsTest,ProcessCapabilities_Empty)108 TEST(SessionCommandsTest, ProcessCapabilities_Empty) {
109 // "capabilities" is required
110 base::DictionaryValue params;
111 base::DictionaryValue result;
112 Status status = ProcessCapabilities(params, &result);
113 ASSERT_EQ(kInvalidArgument, status.code());
114
115 // "capabilities" must be a JSON object
116 params.SetList("capabilities", std::make_unique<base::ListValue>());
117 status = ProcessCapabilities(params, &result);
118 ASSERT_EQ(kInvalidArgument, status.code());
119
120 // Empty "capabilities" is OK
121 params.SetDictionary("capabilities",
122 std::make_unique<base::DictionaryValue>());
123 status = ProcessCapabilities(params, &result);
124 ASSERT_EQ(kOk, status.code()) << status.message();
125 ASSERT_TRUE(result.empty());
126 }
127
TEST(SessionCommandsTest,ProcessCapabilities_AlwaysMatch)128 TEST(SessionCommandsTest, ProcessCapabilities_AlwaysMatch) {
129 base::DictionaryValue params;
130 base::DictionaryValue result;
131
132 // "alwaysMatch" must be a JSON object
133 params.SetList("capabilities.alwaysMatch",
134 std::make_unique<base::ListValue>());
135 Status status = ProcessCapabilities(params, &result);
136 ASSERT_EQ(kInvalidArgument, status.code());
137
138 // Empty "alwaysMatch" is OK
139 params.SetDictionary("capabilities.alwaysMatch",
140 std::make_unique<base::DictionaryValue>());
141 status = ProcessCapabilities(params, &result);
142 ASSERT_EQ(kOk, status.code()) << status.message();
143 ASSERT_TRUE(result.empty());
144
145 // Invalid "alwaysMatch"
146 params.SetInteger("capabilities.alwaysMatch.browserName", 10);
147 status = ProcessCapabilities(params, &result);
148 ASSERT_EQ(kInvalidArgument, status.code());
149
150 // Valid "alwaysMatch"
151 params.SetString("capabilities.alwaysMatch.browserName", "chrome");
152 status = ProcessCapabilities(params, &result);
153 ASSERT_EQ(kOk, status.code()) << status.message();
154 ASSERT_EQ(result.size(), 1u);
155 std::string result_string;
156 ASSERT_TRUE(result.GetString("browserName", &result_string));
157 ASSERT_EQ(result_string, "chrome");
158
159 // Null "browserName" treated as not specifying "browserName"
160 params.SetPath({"capabilities", "alwaysMatch", "browserName"}, base::Value());
161 status = ProcessCapabilities(params, &result);
162 ASSERT_EQ(kOk, status.code()) << status.message();
163 ASSERT_FALSE(result.GetString("browserName", &result_string));
164 }
165
TEST(SessionCommandsTest,ProcessCapabilities_FirstMatch)166 TEST(SessionCommandsTest, ProcessCapabilities_FirstMatch) {
167 base::DictionaryValue params;
168 base::DictionaryValue result;
169
170 // "firstMatch" must be a JSON list
171 params.SetDictionary("capabilities.firstMatch",
172 std::make_unique<base::DictionaryValue>());
173 Status status = ProcessCapabilities(params, &result);
174 ASSERT_EQ(kInvalidArgument, status.code());
175
176 // "firstMatch" must have at least one entry
177 params.SetList("capabilities.firstMatch",
178 std::make_unique<base::ListValue>());
179 status = ProcessCapabilities(params, &result);
180 ASSERT_EQ(kInvalidArgument, status.code());
181
182 // Each entry must be a JSON object
183 base::ListValue* list_ptr;
184 ASSERT_TRUE(params.GetList("capabilities.firstMatch", &list_ptr));
185 list_ptr->Set(0, std::make_unique<base::ListValue>());
186 status = ProcessCapabilities(params, &result);
187 ASSERT_EQ(kInvalidArgument, status.code());
188
189 // Empty JSON object allowed as an entry
190 list_ptr->Set(0, std::make_unique<base::DictionaryValue>());
191 status = ProcessCapabilities(params, &result);
192 ASSERT_EQ(kOk, status.code()) << status.message();
193 ASSERT_TRUE(result.empty());
194
195 // Invalid entry
196 base::DictionaryValue* entry_ptr;
197 ASSERT_TRUE(list_ptr->GetDictionary(0, &entry_ptr));
198 entry_ptr->SetString("pageLoadStrategy", "invalid");
199 status = ProcessCapabilities(params, &result);
200 ASSERT_EQ(kInvalidArgument, status.code());
201
202 // Valid entry
203 entry_ptr->SetString("pageLoadStrategy", "eager");
204 status = ProcessCapabilities(params, &result);
205 ASSERT_EQ(kOk, status.code()) << status.message();
206 ASSERT_EQ(result.size(), 1u);
207 std::string result_string;
208 ASSERT_TRUE(result.GetString("pageLoadStrategy", &result_string));
209 ASSERT_EQ(result_string, "eager");
210
211 // Multiple entries, the first one should be selected.
212 list_ptr->Set(1, std::make_unique<base::DictionaryValue>());
213 ASSERT_TRUE(list_ptr->GetDictionary(1, &entry_ptr));
214 entry_ptr->SetString("pageLoadStrategy", "normal");
215 entry_ptr->SetString("browserName", "chrome");
216 status = ProcessCapabilities(params, &result);
217 ASSERT_EQ(kOk, status.code()) << status.message();
218 ASSERT_EQ(result.size(), 1u);
219 ASSERT_TRUE(result.GetString("pageLoadStrategy", &result_string));
220 ASSERT_EQ(result_string, "eager");
221 }
222
223 namespace {
224
ProcessCapabilitiesJson(const std::string & paramsJson,base::DictionaryValue * result_capabilities)225 Status ProcessCapabilitiesJson(const std::string& paramsJson,
226 base::DictionaryValue* result_capabilities) {
227 std::unique_ptr<base::Value> params =
228 base::JSONReader::ReadDeprecated(paramsJson);
229 if (!params || !params->is_dict())
230 return Status(kUnknownError);
231 return ProcessCapabilities(
232 *static_cast<const base::DictionaryValue*>(params.get()),
233 result_capabilities);
234 }
235
236 } // namespace
237
TEST(SessionCommandsTest,ProcessCapabilities_Merge)238 TEST(SessionCommandsTest, ProcessCapabilities_Merge) {
239 base::DictionaryValue result;
240 Status status(kOk);
241
242 // Disallow setting same capability in alwaysMatch and firstMatch
243 status = ProcessCapabilitiesJson(
244 R"({
245 "capabilities": {
246 "alwaysMatch": { "pageLoadStrategy": "normal" },
247 "firstMatch": [
248 { "unhandledPromptBehavior": "accept" },
249 { "pageLoadStrategy": "normal" }
250 ]
251 }
252 })",
253 &result);
254 ASSERT_EQ(kInvalidArgument, status.code());
255
256 // No conflicts between alwaysMatch and firstMatch, select first firstMatch
257 status = ProcessCapabilitiesJson(
258 R"({
259 "capabilities": {
260 "alwaysMatch": { "timeouts": { } },
261 "firstMatch": [
262 { "unhandledPromptBehavior": "accept" },
263 { "pageLoadStrategy": "normal" }
264 ]
265 }
266 })",
267 &result);
268 ASSERT_EQ(kOk, status.code()) << status.message();
269 ASSERT_EQ(result.size(), 2u);
270 ASSERT_TRUE(result.HasKey("timeouts"));
271 ASSERT_TRUE(result.HasKey("unhandledPromptBehavior"));
272 ASSERT_FALSE(result.HasKey("pageLoadStrategy"));
273
274 // Selection by platformName
275 std::string platform_name =
276 base::ToLowerASCII(base::SysInfo::OperatingSystemName());
277 status = ProcessCapabilitiesJson(
278 R"({
279 "capabilities": {
280 "alwaysMatch": { "timeouts": { "script": 10 } },
281 "firstMatch": [
282 { "platformName": "LINUX", "pageLoadStrategy": "none" },
283 { "platformName": ")" +
284 platform_name + R"(", "pageLoadStrategy": "eager" }
285 ]
286 }
287 })",
288 &result);
289 printf("THIS IS PLATFORM: %s", platform_name.c_str());
290 ASSERT_EQ(kOk, status.code()) << status.message();
291 ASSERT_EQ(result.FindKey("platformName")->GetString(), platform_name);
292 ASSERT_EQ(result.FindKey("pageLoadStrategy")->GetString(), "eager");
293
294 // Selection by browserName
295 status = ProcessCapabilitiesJson(
296 R"({
297 "capabilities": {
298 "alwaysMatch": { "timeouts": { } },
299 "firstMatch": [
300 { "browserName": "firefox", "unhandledPromptBehavior": "accept" },
301 { "browserName": "chrome", "pageLoadStrategy": "normal" }
302 ]
303 }
304 })",
305 &result);
306 ASSERT_EQ(kOk, status.code()) << status.message();
307 ASSERT_EQ(result.size(), 3u);
308 ASSERT_TRUE(result.HasKey("timeouts"));
309 ASSERT_EQ(result.FindKey("browserName")->GetString(), "chrome");
310 ASSERT_FALSE(result.HasKey("unhandledPromptBehavior"));
311 ASSERT_TRUE(result.HasKey("pageLoadStrategy"));
312
313 // No acceptable firstMatch
314 status = ProcessCapabilitiesJson(
315 R"({
316 "capabilities": {
317 "alwaysMatch": { "timeouts": { } },
318 "firstMatch": [
319 { "browserName": "firefox", "unhandledPromptBehavior": "accept" },
320 { "browserName": "edge", "pageLoadStrategy": "normal" }
321 ]
322 }
323 })",
324 &result);
325 ASSERT_EQ(kSessionNotCreated, status.code());
326 }
327
TEST(SessionCommandsTest,FileUpload)328 TEST(SessionCommandsTest, FileUpload) {
329 Session session("id");
330 base::DictionaryValue params;
331 std::unique_ptr<base::Value> value;
332 // Zip file entry that contains a single file with contents 'COW\n', base64
333 // encoded following RFC 1521.
334 const char kBase64ZipEntry[] =
335 "UEsDBBQAAAAAAMROi0K/wAzGBAAAAAQAAAADAAAAbW9vQ09XClBLAQIUAxQAAAAAAMROi0K/"
336 "wAzG\nBAAAAAQAAAADAAAAAAAAAAAAAACggQAAAABtb29QSwUGAAAAAAEAAQAxAAAAJQAAAA"
337 "AA\n";
338 params.SetString("file", kBase64ZipEntry);
339 Status status = ExecuteUploadFile(&session, params, &value);
340 ASSERT_EQ(kOk, status.code()) << status.message();
341 base::FilePath::StringType path;
342 ASSERT_TRUE(value->GetAsString(&path));
343 ASSERT_TRUE(base::PathExists(base::FilePath(path)));
344 std::string data;
345 ASSERT_TRUE(base::ReadFileToString(base::FilePath(path), &data));
346 ASSERT_STREQ("COW\n", data.c_str());
347 }
348
349 namespace {
350
351 class DetachChrome : public StubChrome {
352 public:
DetachChrome()353 DetachChrome() : quit_called_(false) {}
~DetachChrome()354 ~DetachChrome() override {}
355
356 // Overridden from Chrome:
Quit()357 Status Quit() override {
358 quit_called_ = true;
359 return Status(kOk);
360 }
361
362 bool quit_called_;
363 };
364
365 } // namespace
366
TEST(SessionCommandsTest,MatchCapabilities)367 TEST(SessionCommandsTest, MatchCapabilities) {
368 base::DictionaryValue merged;
369 merged.SetString("browserName", "not chrome");
370
371 ASSERT_FALSE(MatchCapabilities(&merged));
372
373 merged.Clear();
374 merged.SetString("browserName", "chrome");
375
376 ASSERT_TRUE(MatchCapabilities(&merged));
377 }
378
TEST(SessionCommandsTest,MatchCapabilitiesVirtualAuthenticators)379 TEST(SessionCommandsTest, MatchCapabilitiesVirtualAuthenticators) {
380 // Match webauthn:virtualAuthenticators on desktop.
381 base::DictionaryValue merged;
382 merged.SetBoolPath("webauthn:virtualAuthenticators", true);
383 EXPECT_TRUE(MatchCapabilities(&merged));
384
385 // Don't match webauthn:virtualAuthenticators on android.
386 merged.SetStringPath("goog:chromeOptions.androidPackage", "packageName");
387 EXPECT_FALSE(MatchCapabilities(&merged));
388
389 // Don't match values other than bools.
390 merged.Clear();
391 merged.SetStringPath("webauthn:virtualAuthenticators", "not a bool");
392 EXPECT_FALSE(MatchCapabilities(&merged));
393 }
394
TEST(SessionCommandsTest,MatchCapabilitiesVirtualAuthenticatorsLargeBlob)395 TEST(SessionCommandsTest, MatchCapabilitiesVirtualAuthenticatorsLargeBlob) {
396 // Match webauthn:extension:largeBlob on desktop.
397 base::DictionaryValue merged;
398 merged.SetBoolPath("webauthn:extension:largeBlob", true);
399 EXPECT_TRUE(MatchCapabilities(&merged));
400
401 // Don't match webauthn:extension:largeBlob on android.
402 merged.SetStringPath("goog:chromeOptions.androidPackage", "packageName");
403 EXPECT_FALSE(MatchCapabilities(&merged));
404
405 // Don't match values other than bools.
406 merged.Clear();
407 merged.SetStringPath("webauthn:extension:largeBlob", "not a bool");
408 EXPECT_FALSE(MatchCapabilities(&merged));
409 }
410
TEST(SessionCommandsTest,Quit)411 TEST(SessionCommandsTest, Quit) {
412 DetachChrome* chrome = new DetachChrome();
413 Session session("id", std::unique_ptr<Chrome>(chrome));
414
415 base::DictionaryValue params;
416 std::unique_ptr<base::Value> value;
417
418 ASSERT_EQ(kOk, ExecuteQuit(false, &session, params, &value).code());
419 ASSERT_TRUE(chrome->quit_called_);
420
421 chrome->quit_called_ = false;
422 ASSERT_EQ(kOk, ExecuteQuit(true, &session, params, &value).code());
423 ASSERT_TRUE(chrome->quit_called_);
424 }
425
TEST(SessionCommandsTest,QuitWithDetach)426 TEST(SessionCommandsTest, QuitWithDetach) {
427 DetachChrome* chrome = new DetachChrome();
428 Session session("id", std::unique_ptr<Chrome>(chrome));
429 session.detach = true;
430
431 base::DictionaryValue params;
432 std::unique_ptr<base::Value> value;
433
434 ASSERT_EQ(kOk, ExecuteQuit(true, &session, params, &value).code());
435 ASSERT_FALSE(chrome->quit_called_);
436
437 ASSERT_EQ(kOk, ExecuteQuit(false, &session, params, &value).code());
438 ASSERT_TRUE(chrome->quit_called_);
439 }
440
441 namespace {
442
443 class FailsToQuitChrome : public StubChrome {
444 public:
FailsToQuitChrome()445 FailsToQuitChrome() {}
~FailsToQuitChrome()446 ~FailsToQuitChrome() override {}
447
448 // Overridden from Chrome:
Quit()449 Status Quit() override { return Status(kUnknownError); }
450 };
451
452 } // namespace
453
TEST(SessionCommandsTest,QuitFails)454 TEST(SessionCommandsTest, QuitFails) {
455 Session session("id", std::unique_ptr<Chrome>(new FailsToQuitChrome()));
456 base::DictionaryValue params;
457 std::unique_ptr<base::Value> value;
458 ASSERT_EQ(kUnknownError, ExecuteQuit(false, &session, params, &value).code());
459 }
460
461 namespace {
462
463 class MockChrome : public StubChrome {
464 public:
MockChrome(BrowserInfo & binfo)465 explicit MockChrome(BrowserInfo& binfo) : web_view_("1") {
466 browser_info_ = binfo;
467 }
~MockChrome()468 ~MockChrome() override {}
469
GetBrowserInfo() const470 const BrowserInfo* GetBrowserInfo() const override { return &browser_info_; }
471
GetWebViewById(const std::string & id,WebView ** web_view)472 Status GetWebViewById(const std::string& id, WebView** web_view) override {
473 *web_view = &web_view_;
474 return Status(kOk);
475 }
476
477 private:
478 BrowserInfo browser_info_;
479 StubWebView web_view_;
480 };
481
482 } // namespace
483
TEST(SessionCommandsTest,ConfigureHeadlessSession_dotNotation)484 TEST(SessionCommandsTest, ConfigureHeadlessSession_dotNotation) {
485 Capabilities capabilities;
486 base::DictionaryValue caps;
487 base::Value::ListStorage args;
488 args.emplace_back("headless");
489 caps.SetPath({"goog:chromeOptions", "args"}, base::Value(args));
490
491 base::DictionaryValue prefs;
492 prefs.SetKey("download.default_directory",
493 base::Value("/examples/python/downloads"));
494 caps.SetPath({"goog:chromeOptions", "prefs"}, prefs.Clone());
495
496 Status status = capabilities.Parse(caps);
497 BrowserInfo binfo;
498 binfo.is_headless = true;
499 MockChrome* chrome = new MockChrome(binfo);
500 Session session("id", std::unique_ptr<Chrome>(chrome));
501
502 status = internal::ConfigureHeadlessSession(&session, capabilities);
503 ASSERT_EQ(kOk, status.code()) << status.message();
504 ASSERT_TRUE(session.chrome->GetBrowserInfo()->is_headless);
505 ASSERT_STREQ("/examples/python/downloads",
506 session.headless_download_directory->c_str());
507 }
508
TEST(SessionCommandsTest,ConfigureHeadlessSession_nestedMap)509 TEST(SessionCommandsTest, ConfigureHeadlessSession_nestedMap) {
510 Capabilities capabilities;
511 base::DictionaryValue caps;
512 base::Value::ListStorage args;
513 args.emplace_back("headless");
514 caps.SetPath({"goog:chromeOptions", "args"}, base::Value(args));
515
516 base::DictionaryValue prefs;
517 std::unique_ptr<base::DictionaryValue> download(new base::DictionaryValue());
518 download->SetStringPath("default_directory", "/examples/python/downloads");
519 prefs.SetDictionary("download", std::move(download));
520 caps.SetPath({"goog:chromeOptions", "prefs"}, prefs.Clone());
521
522 Status status = capabilities.Parse(caps);
523 BrowserInfo binfo;
524 binfo.is_headless = true;
525 MockChrome* chrome = new MockChrome(binfo);
526 Session session("id", std::unique_ptr<Chrome>(chrome));
527
528 status = internal::ConfigureHeadlessSession(&session, capabilities);
529 ASSERT_EQ(kOk, status.code()) << status.message();
530 ASSERT_TRUE(session.chrome->GetBrowserInfo()->is_headless);
531 ASSERT_STREQ("/examples/python/downloads",
532 session.headless_download_directory->c_str());
533 }
534
TEST(SessionCommandsTest,ConfigureHeadlessSession_noDownloadDir)535 TEST(SessionCommandsTest, ConfigureHeadlessSession_noDownloadDir) {
536 Capabilities capabilities;
537 base::DictionaryValue caps;
538 base::Value::ListStorage args;
539 args.emplace_back("headless");
540 caps.SetPath({"goog:chromeOptions", "args"}, base::Value(args));
541
542 Status status = capabilities.Parse(caps);
543 BrowserInfo binfo;
544 binfo.is_headless = true;
545 MockChrome* chrome = new MockChrome(binfo);
546 Session session("id", std::unique_ptr<Chrome>(chrome));
547
548 status = internal::ConfigureHeadlessSession(&session, capabilities);
549 ASSERT_EQ(kOk, status.code()) << status.message();
550 ASSERT_TRUE(session.chrome->GetBrowserInfo()->is_headless);
551 ASSERT_STREQ(".", session.headless_download_directory->c_str());
552 }
553
TEST(SessionCommandsTest,ConfigureHeadlessSession_notHeadless)554 TEST(SessionCommandsTest, ConfigureHeadlessSession_notHeadless) {
555 Capabilities capabilities;
556 base::DictionaryValue caps;
557 base::DictionaryValue prefs;
558 std::unique_ptr<base::DictionaryValue> download(new base::DictionaryValue());
559 download->SetStringPath("default_directory", "/examples/python/downloads");
560 prefs.SetDictionary("download", std::move(download));
561 caps.SetPath({"goog:chromeOptions", "prefs"}, prefs.Clone());
562
563 Status status = capabilities.Parse(caps);
564 BrowserInfo binfo;
565 MockChrome* chrome = new MockChrome(binfo);
566 Session session("id", std::unique_ptr<Chrome>(chrome));
567
568 status = internal::ConfigureHeadlessSession(&session, capabilities);
569 ASSERT_EQ(kOk, status.code()) << status.message();
570 ASSERT_FALSE(session.chrome->GetBrowserInfo()->is_headless);
571 ASSERT_FALSE(session.headless_download_directory);
572 }
573
TEST(SessionCommandsTest,ConfigureSession_allSet)574 TEST(SessionCommandsTest, ConfigureSession_allSet) {
575 BrowserInfo binfo;
576 MockChrome* chrome = new MockChrome(binfo);
577 Session session("id", std::unique_ptr<Chrome>(chrome));
578
579 const base::DictionaryValue* params_in = nullptr;
580 base::Value value = base::JSONReader::Read(
581 R"({
582 "capabilities": {
583 "alwaysMatch": { },
584 "firstMatch": [ {
585 "acceptInsecureCerts": false,
586 "browserName": "chrome",
587 "goog:chromeOptions": {
588 },
589 "goog:loggingPrefs": {
590 "driver": "DEBUG"
591 },
592 "pageLoadStrategy": "normal",
593 "timeouts": {
594 "implicit": 57000,
595 "pageLoad": 29000,
596 "script": 21000
597 },
598 "strictFileInteractability": true,
599 "unhandledPromptBehavior": "accept"
600 } ]
601 }
602 })")
603 .value();
604 ASSERT_TRUE(value.GetAsDictionary(¶ms_in));
605
606 const base::DictionaryValue* desired_caps_out;
607 base::DictionaryValue merged_out;
608 Capabilities capabilities_out;
609 Status status = internal::ConfigureSession(
610 &session, *params_in, &desired_caps_out, &merged_out, &capabilities_out);
611 ASSERT_EQ(kOk, status.code()) << status.message();
612 // Verify out parameters have been set
613 ASSERT_TRUE(desired_caps_out->is_dict());
614 ASSERT_TRUE(merged_out.is_dict());
615 ASSERT_TRUE(capabilities_out.logging_prefs["driver"]);
616 // Verify session settings are correct
617 ASSERT_EQ(kAccept, session.unhandled_prompt_behavior);
618 ASSERT_EQ(base::TimeDelta::FromSeconds(57), session.implicit_wait);
619 ASSERT_EQ(base::TimeDelta::FromSeconds(29), session.page_load_timeout);
620 ASSERT_EQ(base::TimeDelta::FromSeconds(21), session.script_timeout);
621 ASSERT_TRUE(session.strict_file_interactability);
622 ASSERT_EQ(Log::Level::kDebug, session.driver_log.get()->min_level());
623 }
624
TEST(SessionCommandsTest,ConfigureSession_defaults)625 TEST(SessionCommandsTest, ConfigureSession_defaults) {
626 BrowserInfo binfo;
627 MockChrome* chrome = new MockChrome(binfo);
628 Session session("id", std::unique_ptr<Chrome>(chrome));
629
630 const base::DictionaryValue* params_in = nullptr;
631 base::Value value = base::JSONReader::Read(
632 R"({
633 "capabilities": {
634 "alwaysMatch": { },
635 "firstMatch": [ { } ]
636 }
637 })")
638 .value();
639 ASSERT_TRUE(value.GetAsDictionary(¶ms_in));
640 const base::DictionaryValue* desired_caps_out;
641 base::DictionaryValue merged_out;
642 Capabilities capabilities_out;
643
644 Status status = internal::ConfigureSession(
645 &session, *params_in, &desired_caps_out, &merged_out, &capabilities_out);
646 ASSERT_EQ(kOk, status.code()) << status.message();
647 ASSERT_TRUE(desired_caps_out->is_dict());
648 ASSERT_TRUE(merged_out.is_dict());
649 // Testing specific values could be fragile, but want to verify they are set
650 ASSERT_EQ(base::TimeDelta::FromSeconds(0), session.implicit_wait);
651 ASSERT_EQ(base::TimeDelta::FromSeconds(300), session.page_load_timeout);
652 ASSERT_EQ(base::TimeDelta::FromSeconds(30), session.script_timeout);
653 ASSERT_FALSE(session.strict_file_interactability);
654 ASSERT_EQ(Log::Level::kWarning, session.driver_log.get()->min_level());
655 // w3c values:
656 ASSERT_EQ(kDismissAndNotify, session.unhandled_prompt_behavior);
657 }
658
TEST(SessionCommandsTest,ConfigureSession_legacyDefault)659 TEST(SessionCommandsTest, ConfigureSession_legacyDefault) {
660 BrowserInfo binfo;
661 MockChrome* chrome = new MockChrome(binfo);
662 Session session("id", std::unique_ptr<Chrome>(chrome));
663
664 const base::DictionaryValue* params_in = nullptr;
665 base::Value value = base::JSONReader::Read(
666 R"({
667 "desiredCapabilities": {
668 "browserName": "chrome",
669 "goog:chromeOptions": {
670 "w3c": false
671 }
672 }
673 })")
674 .value();
675 ASSERT_TRUE(value.GetAsDictionary(¶ms_in));
676 const base::DictionaryValue* desired_caps_out;
677 base::DictionaryValue merged_out;
678 Capabilities capabilities_out;
679
680 Status status = internal::ConfigureSession(
681 &session, *params_in, &desired_caps_out, &merged_out, &capabilities_out);
682 ASSERT_EQ(kOk, status.code()) << status.message();
683 ASSERT_TRUE(desired_caps_out->is_dict());
684 // legacy values:
685 ASSERT_EQ(kIgnore, session.unhandled_prompt_behavior);
686 }
687