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#include <string>
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/compiler_specific.h"
10#include "base/files/file_path.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/message_loop/message_loop.h"
13#include "base/run_loop.h"
14#include "base/synchronization/lock.h"
15#include "base/threading/thread.h"
16#include "base/values.h"
17#include "chrome/test/chromedriver/chrome/status.h"
18#include "chrome/test/chromedriver/chrome/stub_chrome.h"
19#include "chrome/test/chromedriver/chrome/stub_web_view.h"
20#include "chrome/test/chromedriver/chrome/web_view.h"
21#include "chrome/test/chromedriver/commands.h"
22#include "chrome/test/chromedriver/element_commands.h"
23#include "chrome/test/chromedriver/session.h"
24#include "chrome/test/chromedriver/session_commands.h"
25#include "chrome/test/chromedriver/window_commands.h"
26#include "testing/gtest/include/gtest/gtest.h"
27#include "third_party/webdriver/atoms.h"
28
29namespace {
30
31void OnGetStatus(const Status& status,
32                 scoped_ptr<base::Value> value,
33                 const std::string& session_id) {
34  ASSERT_EQ(kOk, status.code());
35  base::DictionaryValue* dict;
36  ASSERT_TRUE(value->GetAsDictionary(&dict));
37  base::Value* unused;
38  ASSERT_TRUE(dict->Get("os.name", &unused));
39  ASSERT_TRUE(dict->Get("os.version", &unused));
40  ASSERT_TRUE(dict->Get("os.arch", &unused));
41  ASSERT_TRUE(dict->Get("build.version", &unused));
42}
43
44}  // namespace
45
46TEST(CommandsTest, GetStatus) {
47  base::DictionaryValue params;
48  ExecuteGetStatus(params, std::string(), base::Bind(&OnGetStatus));
49}
50
51namespace {
52
53void ExecuteStubQuit(
54    int* count,
55    const base::DictionaryValue& params,
56    const std::string& session_id,
57    const CommandCallback& callback) {
58  if (*count == 0) {
59    EXPECT_STREQ("id", session_id.c_str());
60  } else {
61    EXPECT_STREQ("id2", session_id.c_str());
62  }
63  (*count)++;
64  callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
65}
66
67void OnQuitAll(const Status& status,
68               scoped_ptr<base::Value> value,
69               const std::string& session_id) {
70  ASSERT_EQ(kOk, status.code());
71  ASSERT_FALSE(value.get());
72}
73
74}  // namespace
75
76TEST(CommandsTest, QuitAll) {
77  SessionThreadMap map;
78  Session session("id");
79  Session session2("id2");
80  map[session.id] = make_linked_ptr(new base::Thread("1"));
81  map[session2.id] = make_linked_ptr(new base::Thread("2"));
82
83  int count = 0;
84  Command cmd = base::Bind(&ExecuteStubQuit, &count);
85  base::DictionaryValue params;
86  base::MessageLoop loop;
87  ExecuteQuitAll(cmd, &map, params, std::string(), base::Bind(&OnQuitAll));
88  ASSERT_EQ(2, count);
89}
90
91namespace {
92
93Status ExecuteSimpleCommand(
94    const std::string& expected_id,
95    base::DictionaryValue* expected_params,
96    base::Value* value,
97    Session* session,
98    const base::DictionaryValue& params,
99    scoped_ptr<base::Value>* return_value) {
100  EXPECT_EQ(expected_id, session->id);
101  EXPECT_TRUE(expected_params->Equals(&params));
102  return_value->reset(value->DeepCopy());
103  session->quit = true;
104  return Status(kOk);
105}
106
107void OnSimpleCommand(base::RunLoop* run_loop,
108                     const std::string& expected_session_id,
109                     base::Value* expected_value,
110                     const Status& status,
111                     scoped_ptr<base::Value> value,
112                     const std::string& session_id) {
113  ASSERT_EQ(kOk, status.code());
114  ASSERT_TRUE(expected_value->Equals(value.get()));
115  ASSERT_EQ(expected_session_id, session_id);
116  run_loop->Quit();
117}
118
119}  // namespace
120
121TEST(CommandsTest, ExecuteSessionCommand) {
122  SessionThreadMap map;
123  linked_ptr<base::Thread> thread(new base::Thread("1"));
124  ASSERT_TRUE(thread->Start());
125  std::string id("id");
126  thread->message_loop()->PostTask(
127      FROM_HERE,
128      base::Bind(&internal::CreateSessionOnSessionThreadForTesting, id));
129  map[id] = thread;
130
131  base::DictionaryValue params;
132  params.SetInteger("param", 5);
133  base::FundamentalValue expected_value(6);
134  SessionCommand cmd = base::Bind(
135      &ExecuteSimpleCommand, id, &params, &expected_value);
136
137  base::MessageLoop loop;
138  base::RunLoop run_loop;
139  ExecuteSessionCommand(
140      &map,
141      cmd,
142      false,
143      params,
144      id,
145      base::Bind(&OnSimpleCommand, &run_loop, id, &expected_value));
146  run_loop.Run();
147}
148
149namespace {
150
151Status ShouldNotBeCalled(
152    Session* session,
153    const base::DictionaryValue& params,
154    scoped_ptr<base::Value>* value) {
155  EXPECT_TRUE(false);
156  return Status(kOk);
157}
158
159void OnNoSuchSession(const Status& status,
160                     scoped_ptr<base::Value> value,
161                     const std::string& session_id) {
162  EXPECT_EQ(kNoSuchSession, status.code());
163  EXPECT_FALSE(value.get());
164}
165
166void OnNoSuchSessionIsOk(const Status& status,
167                         scoped_ptr<base::Value> value,
168                         const std::string& session_id) {
169  EXPECT_EQ(kOk, status.code());
170  EXPECT_FALSE(value.get());
171}
172
173}  // namespace
174
175TEST(CommandsTest, ExecuteSessionCommandOnNoSuchSession) {
176  SessionThreadMap map;
177  base::DictionaryValue params;
178  ExecuteSessionCommand(&map,
179                        base::Bind(&ShouldNotBeCalled),
180                        false,
181                        params,
182                        "session",
183                        base::Bind(&OnNoSuchSession));
184}
185
186TEST(CommandsTest, ExecuteSessionCommandOnNoSuchSessionWhenItExpectsOk) {
187  SessionThreadMap map;
188  base::DictionaryValue params;
189  ExecuteSessionCommand(&map,
190                        base::Bind(&ShouldNotBeCalled),
191                        true,
192                        params,
193                        "session",
194                        base::Bind(&OnNoSuchSessionIsOk));
195}
196
197namespace {
198
199void OnNoSuchSessionAndQuit(base::RunLoop* run_loop,
200                            const Status& status,
201                            scoped_ptr<base::Value> value,
202                            const std::string& session_id) {
203  run_loop->Quit();
204  EXPECT_EQ(kNoSuchSession, status.code());
205  EXPECT_FALSE(value.get());
206}
207
208}  // namespace
209
210TEST(CommandsTest, ExecuteSessionCommandOnJustDeletedSession) {
211  SessionThreadMap map;
212  linked_ptr<base::Thread> thread(new base::Thread("1"));
213  ASSERT_TRUE(thread->Start());
214  std::string id("id");
215  map[id] = thread;
216
217  base::MessageLoop loop;
218  base::RunLoop run_loop;
219  ExecuteSessionCommand(&map,
220                        base::Bind(&ShouldNotBeCalled),
221                        false,
222                        base::DictionaryValue(),
223                        "session",
224                        base::Bind(&OnNoSuchSessionAndQuit, &run_loop));
225  run_loop.Run();
226}
227
228namespace {
229
230enum TestScenario {
231  kElementExistsQueryOnce = 0,
232  kElementExistsQueryTwice,
233  kElementNotExistsQueryOnce,
234  kElementExistsTimeout
235};
236
237class FindElementWebView : public StubWebView {
238 public:
239  FindElementWebView(bool only_one, TestScenario scenario)
240      : StubWebView("1"), only_one_(only_one), scenario_(scenario),
241        current_count_(0) {
242    switch (scenario_) {
243      case kElementExistsQueryOnce:
244      case kElementExistsQueryTwice:
245      case kElementExistsTimeout: {
246        if (only_one_) {
247          base::DictionaryValue element;
248          element.SetString("ELEMENT", "1");
249          result_.reset(element.DeepCopy());
250        } else {
251          base::DictionaryValue element1;
252          element1.SetString("ELEMENT", "1");
253          base::DictionaryValue element2;
254          element2.SetString("ELEMENT", "2");
255          base::ListValue list;
256          list.Append(element1.DeepCopy());
257          list.Append(element2.DeepCopy());
258          result_.reset(list.DeepCopy());
259        }
260        break;
261      }
262      case kElementNotExistsQueryOnce: {
263        if (only_one_)
264          result_.reset(base::Value::CreateNullValue());
265        else
266          result_.reset(new base::ListValue());
267        break;
268      }
269    }
270  }
271  virtual ~FindElementWebView() {}
272
273  void Verify(const std::string& expected_frame,
274              const base::ListValue* expected_args,
275              const base::Value* actrual_result) {
276    EXPECT_EQ(expected_frame, frame_);
277    std::string function;
278    if (only_one_)
279      function = webdriver::atoms::asString(webdriver::atoms::FIND_ELEMENT);
280    else
281      function = webdriver::atoms::asString(webdriver::atoms::FIND_ELEMENTS);
282    EXPECT_EQ(function, function_);
283    ASSERT_TRUE(args_.get());
284    EXPECT_TRUE(expected_args->Equals(args_.get()));
285    ASSERT_TRUE(actrual_result);
286    EXPECT_TRUE(result_->Equals(actrual_result));
287  }
288
289  // Overridden from WebView:
290  virtual Status CallFunction(const std::string& frame,
291                              const std::string& function,
292                              const base::ListValue& args,
293                              scoped_ptr<base::Value>* result) OVERRIDE {
294    ++current_count_;
295    if (scenario_ == kElementExistsTimeout ||
296        (scenario_ == kElementExistsQueryTwice && current_count_ == 1)) {
297        // Always return empty result when testing timeout.
298        if (only_one_)
299          result->reset(base::Value::CreateNullValue());
300        else
301          result->reset(new base::ListValue());
302    } else {
303      switch (scenario_) {
304        case kElementExistsQueryOnce:
305        case kElementNotExistsQueryOnce: {
306          EXPECT_EQ(1, current_count_);
307          break;
308        }
309        case kElementExistsQueryTwice: {
310          EXPECT_EQ(2, current_count_);
311          break;
312        }
313        default: {
314          break;
315        }
316      }
317
318      result->reset(result_->DeepCopy());
319      frame_ = frame;
320      function_ = function;
321      args_.reset(args.DeepCopy());
322    }
323    return Status(kOk);
324  }
325
326 private:
327  bool only_one_;
328  TestScenario scenario_;
329  int current_count_;
330  std::string frame_;
331  std::string function_;
332  scoped_ptr<base::ListValue> args_;
333  scoped_ptr<base::Value> result_;
334};
335
336}  // namespace
337
338TEST(CommandsTest, SuccessfulFindElement) {
339  FindElementWebView web_view(true, kElementExistsQueryTwice);
340  Session session("id");
341  session.implicit_wait = 1000;
342  session.SwitchToSubFrame("frame_id1", std::string());
343  base::DictionaryValue params;
344  params.SetString("using", "id");
345  params.SetString("value", "a");
346  scoped_ptr<base::Value> result;
347  ASSERT_EQ(kOk,
348            ExecuteFindElement(1, &session, &web_view, params, &result).code());
349  base::DictionaryValue param;
350  param.SetString("id", "a");
351  base::ListValue expected_args;
352  expected_args.Append(param.DeepCopy());
353  web_view.Verify("frame_id1", &expected_args, result.get());
354}
355
356TEST(CommandsTest, FailedFindElement) {
357  FindElementWebView web_view(true, kElementNotExistsQueryOnce);
358  Session session("id");
359  base::DictionaryValue params;
360  params.SetString("using", "id");
361  params.SetString("value", "a");
362  scoped_ptr<base::Value> result;
363  ASSERT_EQ(kNoSuchElement,
364            ExecuteFindElement(1, &session, &web_view, params, &result).code());
365}
366
367TEST(CommandsTest, SuccessfulFindElements) {
368  FindElementWebView web_view(false, kElementExistsQueryTwice);
369  Session session("id");
370  session.implicit_wait = 1000;
371  session.SwitchToSubFrame("frame_id2", std::string());
372  base::DictionaryValue params;
373  params.SetString("using", "name");
374  params.SetString("value", "b");
375  scoped_ptr<base::Value> result;
376  ASSERT_EQ(
377      kOk,
378      ExecuteFindElements(1, &session, &web_view, params, &result).code());
379  base::DictionaryValue param;
380  param.SetString("name", "b");
381  base::ListValue expected_args;
382  expected_args.Append(param.DeepCopy());
383  web_view.Verify("frame_id2", &expected_args, result.get());
384}
385
386TEST(CommandsTest, FailedFindElements) {
387  Session session("id");
388  FindElementWebView web_view(false, kElementNotExistsQueryOnce);
389  base::DictionaryValue params;
390  params.SetString("using", "id");
391  params.SetString("value", "a");
392  scoped_ptr<base::Value> result;
393  ASSERT_EQ(
394      kOk,
395      ExecuteFindElements(1, &session, &web_view, params, &result).code());
396  base::ListValue* list;
397  ASSERT_TRUE(result->GetAsList(&list));
398  ASSERT_EQ(0U, list->GetSize());
399}
400
401TEST(CommandsTest, SuccessfulFindChildElement) {
402  FindElementWebView web_view(true, kElementExistsQueryTwice);
403  Session session("id");
404  session.implicit_wait = 1000;
405  session.SwitchToSubFrame("frame_id3", std::string());
406  base::DictionaryValue params;
407  params.SetString("using", "tag name");
408  params.SetString("value", "div");
409  std::string element_id = "1";
410  scoped_ptr<base::Value> result;
411  ASSERT_EQ(
412      kOk,
413      ExecuteFindChildElement(
414          1, &session, &web_view, element_id, params, &result).code());
415  base::DictionaryValue locator_param;
416  locator_param.SetString("tag name", "div");
417  base::DictionaryValue root_element_param;
418  root_element_param.SetString("ELEMENT", element_id);
419  base::ListValue expected_args;
420  expected_args.Append(locator_param.DeepCopy());
421  expected_args.Append(root_element_param.DeepCopy());
422  web_view.Verify("frame_id3", &expected_args, result.get());
423}
424
425TEST(CommandsTest, FailedFindChildElement) {
426  Session session("id");
427  FindElementWebView web_view(true, kElementNotExistsQueryOnce);
428  base::DictionaryValue params;
429  params.SetString("using", "id");
430  params.SetString("value", "a");
431  std::string element_id = "1";
432  scoped_ptr<base::Value> result;
433  ASSERT_EQ(
434      kNoSuchElement,
435      ExecuteFindChildElement(
436          1, &session, &web_view, element_id, params, &result).code());
437}
438
439TEST(CommandsTest, SuccessfulFindChildElements) {
440  FindElementWebView web_view(false, kElementExistsQueryTwice);
441  Session session("id");
442  session.implicit_wait = 1000;
443  session.SwitchToSubFrame("frame_id4", std::string());
444  base::DictionaryValue params;
445  params.SetString("using", "class name");
446  params.SetString("value", "c");
447  std::string element_id = "1";
448  scoped_ptr<base::Value> result;
449  ASSERT_EQ(
450      kOk,
451      ExecuteFindChildElements(
452          1, &session, &web_view, element_id, params, &result).code());
453  base::DictionaryValue locator_param;
454  locator_param.SetString("class name", "c");
455  base::DictionaryValue root_element_param;
456  root_element_param.SetString("ELEMENT", element_id);
457  base::ListValue expected_args;
458  expected_args.Append(locator_param.DeepCopy());
459  expected_args.Append(root_element_param.DeepCopy());
460  web_view.Verify("frame_id4", &expected_args, result.get());
461}
462
463TEST(CommandsTest, FailedFindChildElements) {
464  Session session("id");
465  FindElementWebView web_view(false, kElementNotExistsQueryOnce);
466  base::DictionaryValue params;
467  params.SetString("using", "id");
468  params.SetString("value", "a");
469  std::string element_id = "1";
470  scoped_ptr<base::Value> result;
471  ASSERT_EQ(
472      kOk,
473      ExecuteFindChildElements(
474          1, &session, &web_view, element_id, params, &result).code());
475  base::ListValue* list;
476  ASSERT_TRUE(result->GetAsList(&list));
477  ASSERT_EQ(0U, list->GetSize());
478}
479
480TEST(CommandsTest, TimeoutInFindElement) {
481  Session session("id");
482  FindElementWebView web_view(true, kElementExistsTimeout);
483  session.implicit_wait = 2;
484  base::DictionaryValue params;
485  params.SetString("using", "id");
486  params.SetString("value", "a");
487  params.SetString("id", "1");
488  scoped_ptr<base::Value> result;
489  ASSERT_EQ(kNoSuchElement,
490            ExecuteFindElement(1, &session, &web_view, params, &result).code());
491}
492
493namespace {
494
495class ErrorCallFunctionWebView : public StubWebView {
496 public:
497  explicit ErrorCallFunctionWebView(StatusCode code)
498      : StubWebView("1"), code_(code) {}
499  virtual ~ErrorCallFunctionWebView() {}
500
501  // Overridden from WebView:
502  virtual Status CallFunction(const std::string& frame,
503                              const std::string& function,
504                              const base::ListValue& args,
505                              scoped_ptr<base::Value>* result) OVERRIDE {
506    return Status(code_);
507  }
508
509 private:
510  StatusCode code_;
511};
512
513}  // namespace
514
515TEST(CommandsTest, ErrorFindElement) {
516  Session session("id");
517  ErrorCallFunctionWebView web_view(kUnknownError);
518  base::DictionaryValue params;
519  params.SetString("using", "id");
520  params.SetString("value", "a");
521  scoped_ptr<base::Value> value;
522  ASSERT_EQ(kUnknownError,
523            ExecuteFindElement(1, &session, &web_view, params, &value).code());
524  ASSERT_EQ(kUnknownError,
525            ExecuteFindElements(1, &session, &web_view, params, &value).code());
526}
527
528TEST(CommandsTest, ErrorFindChildElement) {
529  Session session("id");
530  ErrorCallFunctionWebView web_view(kStaleElementReference);
531  base::DictionaryValue params;
532  params.SetString("using", "id");
533  params.SetString("value", "a");
534  std::string element_id = "1";
535  scoped_ptr<base::Value> result;
536  ASSERT_EQ(
537      kStaleElementReference,
538      ExecuteFindChildElement(
539          1, &session, &web_view, element_id, params, &result).code());
540  ASSERT_EQ(
541      kStaleElementReference,
542      ExecuteFindChildElements(
543          1, &session, &web_view, element_id, params, &result).code());
544}
545