PageRenderTime 51ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/dotnet/src/WebDriver/Remote/RemoteWebDriver.cs

https://bitbucket.org/abahdanovich/selenium
C# | 1152 lines | 612 code | 117 blank | 423 comment | 69 complexity | b1e912bbab5abcb9e140e3dd00431bda MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, AGPL-1.0, MIT, Apache-2.0, BSD-3-Clause, GPL-2.0
  1. // <copyright file="RemoteWebDriver.cs" company="WebDriver Committers">
  2. // Copyright 2007-2011 WebDriver committers
  3. // Copyright 2007-2011 Google Inc.
  4. // Portions copyright 2011 Software Freedom Conservancy
  5. //
  6. // Licensed under the Apache License, Version 2.0 (the "License");
  7. // you may not use this file except in compliance with the License.
  8. // You may obtain a copy of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. // </copyright>
  18. using System;
  19. using System.Collections;
  20. using System.Collections.Generic;
  21. using System.Collections.ObjectModel;
  22. using System.Globalization;
  23. using OpenQA.Selenium.Interactions;
  24. using OpenQA.Selenium.Interactions.Internal;
  25. using OpenQA.Selenium.Internal;
  26. namespace OpenQA.Selenium.Remote
  27. {
  28. /// <summary>
  29. /// Provides a way to use the driver through
  30. /// </summary>
  31. /// /// <example>
  32. /// <code>
  33. /// [TestFixture]
  34. /// public class Testing
  35. /// {
  36. /// private IWebDriver driver;
  37. /// <para></para>
  38. /// [SetUp]
  39. /// public void SetUp()
  40. /// {
  41. /// driver = new RemoteWebDriver(new Uri("http://127.0.0.1:4444/wd/hub"),DesiredCapabilities.InternetExplorer());
  42. /// }
  43. /// <para></para>
  44. /// [Test]
  45. /// public void TestGoogle()
  46. /// {
  47. /// driver.Navigate().GoToUrl("http://www.google.co.uk");
  48. /// /*
  49. /// * Rest of the test
  50. /// */
  51. /// }
  52. /// <para></para>
  53. /// [TearDown]
  54. /// public void TearDown()
  55. /// {
  56. /// driver.Quit();
  57. /// }
  58. /// }
  59. /// </code>
  60. /// </example>
  61. public class RemoteWebDriver : IWebDriver, ISearchContext, IJavaScriptExecutor, IFindsById, IFindsByClassName, IFindsByLinkText, IFindsByName, IFindsByTagName, IFindsByXPath, IFindsByPartialLinkText, IFindsByCssSelector, IHasInputDevices, IHasCapabilities, IAllowsFileDetection
  62. {
  63. /// <summary>
  64. /// The default command timeout for HTTP requests in a RemoteWebDriver instance.
  65. /// </summary>
  66. protected static readonly TimeSpan DefaultCommandTimeout = TimeSpan.FromSeconds(60);
  67. #region Private members
  68. private ICommandExecutor executor;
  69. private ICapabilities capabilities;
  70. private IMouse mouse;
  71. private IKeyboard keyboard;
  72. private SessionId sessionId;
  73. private IFileDetector fileDetector = new DefaultFileDetector();
  74. #endregion
  75. #region Constructors
  76. /// <summary>
  77. /// Initializes a new instance of the RemoteWebDriver class
  78. /// </summary>
  79. /// <param name="commandExecutor">An <see cref="ICommandExecutor"/> object which executes commands for the driver.</param>
  80. /// <param name="desiredCapabilities">An <see cref="ICapabilities"/> object containing the desired capabilities of the browser.</param>
  81. public RemoteWebDriver(ICommandExecutor commandExecutor, ICapabilities desiredCapabilities)
  82. {
  83. this.executor = commandExecutor;
  84. this.StartClient();
  85. this.StartSession(desiredCapabilities);
  86. this.mouse = new RemoteMouse(this);
  87. this.keyboard = new RemoteKeyboard(this);
  88. }
  89. /// <summary>
  90. /// Initializes a new instance of the RemoteWebDriver class. This constructor defaults proxy to http://127.0.0.1:4444/wd/hub
  91. /// </summary>
  92. /// <param name="desiredCapabilities">An <see cref="ICapabilities"/> object containing the desired capabilities of the browser.</param>
  93. public RemoteWebDriver(ICapabilities desiredCapabilities)
  94. : this(new Uri("http://127.0.0.1:4444/wd/hub"), desiredCapabilities)
  95. {
  96. }
  97. /// <summary>
  98. /// Initializes a new instance of the RemoteWebDriver class
  99. /// </summary>
  100. /// <param name="remoteAddress">URI containing the address of the WebDriver remote server (e.g. http://127.0.0.1:4444/wd/hub).</param>
  101. /// <param name="desiredCapabilities">An <see cref="ICapabilities"/> object containing the desired capabilities of the browser.</param>
  102. public RemoteWebDriver(Uri remoteAddress, ICapabilities desiredCapabilities)
  103. : this(remoteAddress, desiredCapabilities, RemoteWebDriver.DefaultCommandTimeout)
  104. {
  105. }
  106. /// <summary>
  107. /// Initializes a new instance of the RemoteWebDriver class using the specified remote address, desired capabilities, and command timeout.
  108. /// </summary>
  109. /// <param name="remoteAddress">URI containing the address of the WebDriver remote server (e.g. http://127.0.0.1:4444/wd/hub).</param>
  110. /// <param name="desiredCapabilities">An <see cref="ICapabilities"/> object containing the desired capabilities of the browser.</param>
  111. /// <param name="commandTimeout">The maximum amount of time to wait for each command.</param>
  112. public RemoteWebDriver(Uri remoteAddress, ICapabilities desiredCapabilities, TimeSpan commandTimeout)
  113. : this(new HttpCommandExecutor(remoteAddress, commandTimeout), desiredCapabilities)
  114. {
  115. }
  116. #endregion
  117. #region IWebDriver Properties
  118. /// <summary>
  119. /// Gets or sets the URL the browser is currently displaying.
  120. /// </summary>
  121. /// <seealso cref="IWebDriver.Url"/>
  122. /// <seealso cref="INavigation.GoToUrl(System.String)"/>
  123. /// <seealso cref="INavigation.GoToUrl(System.Uri)"/>
  124. public string Url
  125. {
  126. get
  127. {
  128. Response commandResponse = this.Execute(DriverCommand.GetCurrentUrl, null);
  129. return commandResponse.Value.ToString();
  130. }
  131. set
  132. {
  133. if (value == null)
  134. {
  135. throw new ArgumentNullException("value", "Argument 'url' cannot be null.");
  136. }
  137. Dictionary<string, object> parameters = new Dictionary<string, object>();
  138. parameters.Add("url", value);
  139. try
  140. {
  141. this.Execute(DriverCommand.Get, parameters);
  142. }
  143. catch (WebDriverTimeoutException)
  144. {
  145. // WebDriverTimeoutException is a subclass of WebDriverException,
  146. // and should be rethrown instead of caught by the catch block
  147. // for WebDriverExceptions.
  148. throw;
  149. }
  150. catch (WebDriverException)
  151. {
  152. // Catch the exeception, if any. This is consistent with other
  153. // drivers, in that no exeception is thrown when going to an
  154. // invalid URL.
  155. }
  156. catch (InvalidOperationException)
  157. {
  158. // Catch the exeception, if any. This is consistent with other
  159. // drivers, in that no exeception is thrown when going to an
  160. // invalid URL.
  161. }
  162. catch (NotImplementedException)
  163. {
  164. // Chrome throws NotImplementedException if the URL is invalid.
  165. // Catch the exeception, if any. This is consistent with other
  166. // drivers, in that no exeception is thrown when going to an
  167. // invalid URL.
  168. }
  169. }
  170. }
  171. /// <summary>
  172. /// Gets the title of the current browser window.
  173. /// </summary>
  174. public string Title
  175. {
  176. get
  177. {
  178. Response commandResponse = this.Execute(DriverCommand.GetTitle, null);
  179. object returnedTitle = commandResponse != null ? commandResponse.Value : string.Empty;
  180. return returnedTitle.ToString();
  181. }
  182. }
  183. /// <summary>
  184. /// Gets the source of the page last loaded by the browser.
  185. /// </summary>
  186. public string PageSource
  187. {
  188. get
  189. {
  190. Response commandResponse = this.Execute(DriverCommand.GetPageSource, null);
  191. return commandResponse.Value.ToString();
  192. }
  193. }
  194. /// <summary>
  195. /// Gets the current window handle, which is an opaque handle to this
  196. /// window that uniquely identifies it within this driver instance.
  197. /// </summary>
  198. public string CurrentWindowHandle
  199. {
  200. get
  201. {
  202. Response commandResponse = this.Execute(DriverCommand.GetCurrentWindowHandle, null);
  203. return commandResponse.Value.ToString();
  204. }
  205. }
  206. /// <summary>
  207. /// Gets the window handles of open browser windows.
  208. /// </summary>
  209. public ReadOnlyCollection<string> WindowHandles
  210. {
  211. get
  212. {
  213. Response commandResponse = this.Execute(DriverCommand.GetWindowHandles, null);
  214. object[] handles = (object[])commandResponse.Value;
  215. List<string> handleList = new List<string>();
  216. foreach (object handle in handles)
  217. {
  218. handleList.Add(handle.ToString());
  219. }
  220. return handleList.AsReadOnly();
  221. }
  222. }
  223. #endregion
  224. #region IHasInputDevices Members
  225. /// <summary>
  226. /// Gets an <see cref="IKeyboard"/> object for sending keystrokes to the browser.
  227. /// </summary>
  228. public IKeyboard Keyboard
  229. {
  230. get { return this.keyboard; }
  231. }
  232. /// <summary>
  233. /// Gets an <see cref="IMouse"/> object for sending mouse commands to the browser.
  234. /// </summary>
  235. public IMouse Mouse
  236. {
  237. get { return this.mouse; }
  238. }
  239. #endregion
  240. #region IHasCapabilities properties
  241. /// <summary>
  242. /// Gets the capabilities that the RemoteWebDriver instance is currently using
  243. /// </summary>
  244. public ICapabilities Capabilities
  245. {
  246. get { return this.capabilities; }
  247. }
  248. #endregion
  249. #region IAllowsFileDetection Members
  250. /// <summary>
  251. /// Gets or sets the <see cref="IFileDetector"/> responsible for detecting
  252. /// sequences of keystrokes representing file paths and names.
  253. /// </summary>
  254. public virtual IFileDetector FileDetector
  255. {
  256. get
  257. {
  258. return this.fileDetector;
  259. }
  260. set
  261. {
  262. if (value == null)
  263. {
  264. throw new ArgumentNullException("value", "FileDetector cannot be null");
  265. }
  266. this.fileDetector = value;
  267. }
  268. }
  269. #endregion
  270. #region Protected properties
  271. /// <summary>
  272. /// Gets the <see cref="ICommandExecutor"/> which executes commands for this driver.
  273. /// </summary>
  274. protected ICommandExecutor CommandExecutor
  275. {
  276. get { return this.executor; }
  277. }
  278. /// <summary>
  279. /// Gets the <see cref="SessionId"/> for the current session of this driver.
  280. /// </summary>
  281. protected SessionId SessionId
  282. {
  283. get { return this.sessionId; }
  284. }
  285. #endregion
  286. #region IWebDriver methods
  287. /// <summary>
  288. /// Finds the first element in the page that matches the <see cref="By"/> object
  289. /// </summary>
  290. /// <param name="by">By mechanism to find the object</param>
  291. /// <returns>IWebElement object so that you can interact with that object</returns>
  292. /// <example>
  293. /// <code>
  294. /// IWebDriver driver = new InternetExplorerDriver();
  295. /// IWebElement elem = driver.FindElement(By.Name("q"));
  296. /// </code>
  297. /// </example>
  298. public IWebElement FindElement(By by)
  299. {
  300. if (by == null)
  301. {
  302. throw new ArgumentNullException("by", "by cannot be null");
  303. }
  304. return by.FindElement(this);
  305. }
  306. /// <summary>
  307. /// Finds the elements on the page by using the <see cref="By"/> object and returns a ReadOnlyCollection of the Elements on the page
  308. /// </summary>
  309. /// <param name="by">By mechanism to find the element</param>
  310. /// <returns>ReadOnlyCollection of IWebElement</returns>
  311. /// <example>
  312. /// <code>
  313. /// IWebDriver driver = new InternetExplorerDriver();
  314. /// ReadOnlyCollection<![CDATA[<IWebElement>]]> classList = driver.FindElements(By.ClassName("class"));
  315. /// </code>
  316. /// </example>
  317. public ReadOnlyCollection<IWebElement> FindElements(By by)
  318. {
  319. if (by == null)
  320. {
  321. throw new ArgumentNullException("by", "by cannot be null");
  322. }
  323. return by.FindElements(this);
  324. }
  325. /// <summary>
  326. /// Closes the Browser
  327. /// </summary>
  328. public void Close()
  329. {
  330. this.Execute(DriverCommand.Close, null);
  331. }
  332. /// <summary>
  333. /// Close the Browser and Dispose of WebDriver
  334. /// </summary>
  335. public void Quit()
  336. {
  337. this.Dispose();
  338. }
  339. /// <summary>
  340. /// Method For getting an object to set the Speed
  341. /// </summary>
  342. /// <returns>Returns an IOptions object that allows the driver to set the speed and cookies and getting cookies</returns>
  343. /// <seealso cref="IOptions"/>
  344. /// <example>
  345. /// <code>
  346. /// IWebDriver driver = new InternetExplorerDriver();
  347. /// driver.Manage().GetCookies();
  348. /// </code>
  349. /// </example>
  350. public IOptions Manage()
  351. {
  352. return new RemoteOptions(this);
  353. }
  354. /// <summary>
  355. /// Method to allow you to Navigate with WebDriver
  356. /// </summary>
  357. /// <returns>Returns an INavigation Object that allows the driver to navigate in the browser</returns>
  358. /// <example>
  359. /// <code>
  360. /// IWebDriver driver = new InternetExplorerDriver();
  361. /// driver.Navigate().GoToUrl("http://www.google.co.uk");
  362. /// </code>
  363. /// </example>
  364. public INavigation Navigate()
  365. {
  366. return new RemoteNavigator(this);
  367. }
  368. /// <summary>
  369. /// Method to give you access to switch frames and windows
  370. /// </summary>
  371. /// <returns>Returns an Object that allows you to Switch Frames and Windows</returns>
  372. /// <example>
  373. /// <code>
  374. /// IWebDriver driver = new InternetExplorerDriver();
  375. /// driver.SwitchTo().Frame("FrameName");
  376. /// </code>
  377. /// </example>
  378. public ITargetLocator SwitchTo()
  379. {
  380. return new RemoteTargetLocator(this);
  381. }
  382. #endregion
  383. #region IJavaScriptExecutor Members
  384. /// <summary>
  385. /// Executes JavaScript in the context of the currently selected frame or window
  386. /// </summary>
  387. /// <param name="script">The JavaScript code to execute.</param>
  388. /// <param name="args">The arguments to the script.</param>
  389. /// <returns>The value returned by the script.</returns>
  390. public object ExecuteScript(string script, params object[] args)
  391. {
  392. return this.ExecuteScriptInternal(script, false, args);
  393. }
  394. /// <summary>
  395. /// Executes JavaScript asynchronously in the context of the currently selected frame or window.
  396. /// </summary>
  397. /// <param name="script">The JavaScript code to execute.</param>
  398. /// <param name="args">The arguments to the script.</param>
  399. /// <returns>The value returned by the script.</returns>
  400. public object ExecuteAsyncScript(string script, params object[] args)
  401. {
  402. return this.ExecuteScriptInternal(script, true, args);
  403. }
  404. #endregion
  405. #region IFindsById Members
  406. /// <summary>
  407. /// Finds the first element in the page that matches the ID supplied
  408. /// </summary>
  409. /// <param name="id">ID of the element</param>
  410. /// <returns>IWebElement object so that you can interact with that object</returns>
  411. /// <example>
  412. /// <code>
  413. /// IWebDriver driver = new RemoteWebDriver(DesiredCapabilities.Firefox());
  414. /// IWebElement elem = driver.FindElementById("id")
  415. /// </code>
  416. /// </example>
  417. public IWebElement FindElementById(string id)
  418. {
  419. return this.FindElement("id", id);
  420. }
  421. /// <summary>
  422. /// Finds the first element in the page that matches the ID supplied
  423. /// </summary>
  424. /// <param name="id">ID of the Element</param>
  425. /// <returns>ReadOnlyCollection of Elements that match the object so that you can interact that object</returns>
  426. /// <example>
  427. /// <code>
  428. /// IWebDriver driver = new RemoteWebDriver(DesiredCapabilities.Firefox());
  429. /// ReadOnlyCollection<![CDATA[<IWebElement>]]> elem = driver.FindElementsById("id")
  430. /// </code>
  431. /// </example>
  432. public ReadOnlyCollection<IWebElement> FindElementsById(string id)
  433. {
  434. return this.FindElements("id", id);
  435. }
  436. #endregion
  437. #region IFindsByClassName Members
  438. /// <summary>
  439. /// Finds the first element in the page that matches the CSS Class supplied
  440. /// </summary>
  441. /// <param name="className">className of the</param>
  442. /// <returns>IWebElement object so that you can interact that object</returns>
  443. /// <example>
  444. /// <code>
  445. /// IWebDriver driver = new RemoteWebDriver(DesiredCapabilities.Firefox());
  446. /// IWebElement elem = driver.FindElementByClassName("classname")
  447. /// </code>
  448. /// </example>
  449. public IWebElement FindElementByClassName(string className)
  450. {
  451. return this.FindElement("class name", className);
  452. }
  453. /// <summary>
  454. /// Finds a list of elements that match the class name supplied
  455. /// </summary>
  456. /// <param name="className">CSS class Name on the element</param>
  457. /// <returns>ReadOnlyCollection of IWebElement object so that you can interact with those objects</returns>
  458. /// <example>
  459. /// <code>
  460. /// IWebDriver driver = new RemoteWebDriver(DesiredCapabilities.Firefox());
  461. /// ReadOnlyCollection<![CDATA[<IWebElement>]]> elem = driver.FindElementsByClassName("classname")
  462. /// </code>
  463. /// </example>
  464. public ReadOnlyCollection<IWebElement> FindElementsByClassName(string className)
  465. {
  466. return this.FindElements("class name", className);
  467. }
  468. #endregion
  469. #region IFindsByLinkText Members
  470. /// <summary>
  471. /// Finds the first of elements that match the link text supplied
  472. /// </summary>
  473. /// <param name="linkText">Link text of element </param>
  474. /// <returns>IWebElement object so that you can interact that object</returns>
  475. /// <example>
  476. /// <code>
  477. /// IWebDriver driver = new RemoteWebDriver(DesiredCapabilities.Firefox());
  478. /// IWebElement elem = driver.FindElementsByLinkText("linktext")
  479. /// </code>
  480. /// </example>
  481. public IWebElement FindElementByLinkText(string linkText)
  482. {
  483. return this.FindElement("link text", linkText);
  484. }
  485. /// <summary>
  486. /// Finds a list of elements that match the link text supplied
  487. /// </summary>
  488. /// <param name="linkText">Link text of element</param>
  489. /// <returns>ReadOnlyCollection<![CDATA[<IWebElement>]]> object so that you can interact with those objects</returns>
  490. /// <example>
  491. /// <code>
  492. /// IWebDriver driver = new RemoteWebDriver(DesiredCapabilities.Firefox());
  493. /// ReadOnlyCollection<![CDATA[<IWebElement>]]> elem = driver.FindElementsByClassName("classname")
  494. /// </code>
  495. /// </example>
  496. public ReadOnlyCollection<IWebElement> FindElementsByLinkText(string linkText)
  497. {
  498. return this.FindElements("link text", linkText);
  499. }
  500. #endregion
  501. #region IFindsByPartialLinkText Members
  502. /// <summary>
  503. /// Finds the first of elements that match the part of the link text supplied
  504. /// </summary>
  505. /// <param name="partialLinkText">part of the link text</param>
  506. /// <returns>IWebElement object so that you can interact that object</returns>
  507. /// <example>
  508. /// <code>
  509. /// IWebDriver driver = new RemoteWebDriver(DesiredCapabilities.Firefox());
  510. /// IWebElement elem = driver.FindElementsByPartialLinkText("partOfLink")
  511. /// </code>
  512. /// </example>
  513. public IWebElement FindElementByPartialLinkText(string partialLinkText)
  514. {
  515. return this.FindElement("partial link text", partialLinkText);
  516. }
  517. /// <summary>
  518. /// Finds a list of elements that match the class name supplied
  519. /// </summary>
  520. /// <param name="partialLinkText">part of the link text</param>
  521. /// <returns>ReadOnlyCollection<![CDATA[<IWebElement>]]> objects so that you can interact that object</returns>
  522. /// <example>
  523. /// <code>
  524. /// IWebDriver driver = new RemoteWebDriver(DesiredCapabilities.Firefox());
  525. /// ReadOnlyCollection<![CDATA[<IWebElement>]]> elem = driver.FindElementsByPartialLinkText("partOfTheLink")
  526. /// </code>
  527. /// </example>
  528. public ReadOnlyCollection<IWebElement> FindElementsByPartialLinkText(string partialLinkText)
  529. {
  530. return this.FindElements("partial link text", partialLinkText);
  531. }
  532. #endregion
  533. #region IFindsByName Members
  534. /// <summary>
  535. /// Finds the first of elements that match the name supplied
  536. /// </summary>
  537. /// <param name="name">Name of the element on the page</param>
  538. /// <returns>IWebElement object so that you can interact that object</returns>
  539. /// <example>
  540. /// <code>
  541. /// IWebDriver driver = new RemoteWebDriver(DesiredCapabilities.Firefox());
  542. /// elem = driver.FindElementsByName("name")
  543. /// </code>
  544. /// </example>
  545. public IWebElement FindElementByName(string name)
  546. {
  547. return this.FindElement("name", name);
  548. }
  549. /// <summary>
  550. /// Finds a list of elements that match the name supplied
  551. /// </summary>
  552. /// <param name="name">Name of element</param>
  553. /// <returns>ReadOnlyCollect of IWebElement objects so that you can interact that object</returns>
  554. /// <example>
  555. /// <code>
  556. /// IWebDriver driver = new RemoteWebDriver(DesiredCapabilities.Firefox());
  557. /// ReadOnlyCollection<![CDATA[<IWebElement>]]> elem = driver.FindElementsByName("name")
  558. /// </code>
  559. /// </example>
  560. public ReadOnlyCollection<IWebElement> FindElementsByName(string name)
  561. {
  562. return this.FindElements("name", name);
  563. }
  564. #endregion
  565. #region IFindsByTagName Members
  566. /// <summary>
  567. /// Finds the first of elements that match the DOM Tag supplied
  568. /// </summary>
  569. /// <param name="tagName">DOM tag Name of the element being searched</param>
  570. /// <returns>IWebElement object so that you can interact that object</returns>
  571. /// <example>
  572. /// <code>
  573. /// IWebDriver driver = new RemoteWebDriver(DesiredCapabilities.Firefox());
  574. /// IWebElement elem = driver.FindElementsByTagName("tag")
  575. /// </code>
  576. /// </example>
  577. public IWebElement FindElementByTagName(string tagName)
  578. {
  579. return this.FindElement("tag name", tagName);
  580. }
  581. /// <summary>
  582. /// Finds a list of elements that match the DOM Tag supplied
  583. /// </summary>
  584. /// <param name="tagName">DOM tag Name of element being searched</param>
  585. /// <returns>IWebElement object so that you can interact that object</returns>
  586. /// <example>
  587. /// <code>
  588. /// IWebDriver driver = new RemoteWebDriver(DesiredCapabilities.Firefox());
  589. /// ReadOnlyCollection<![CDATA[<IWebElement>]]> elem = driver.FindElementsByTagName("tag")
  590. /// </code>
  591. /// </example>
  592. public ReadOnlyCollection<IWebElement> FindElementsByTagName(string tagName)
  593. {
  594. return this.FindElements("tag name", tagName);
  595. }
  596. #endregion
  597. #region IFindsByXPath Members
  598. /// <summary>
  599. /// Finds the first of elements that match the XPath supplied
  600. /// </summary>
  601. /// <param name="xpath">xpath to the element</param>
  602. /// <returns>IWebElement object so that you can interact that object</returns>
  603. /// <example>
  604. /// <code>
  605. /// IWebDriver driver = new RemoteWebDriver(DesiredCapabilities.Firefox());
  606. /// IWebElement elem = driver.FindElementsByXPath("//table/tbody/tr/td/a");
  607. /// </code>
  608. /// </example>
  609. public IWebElement FindElementByXPath(string xpath)
  610. {
  611. return this.FindElement("xpath", xpath);
  612. }
  613. /// <summary>
  614. /// Finds a list of elements that match the XPath supplied
  615. /// </summary>
  616. /// <param name="xpath">xpath to the element</param>
  617. /// <returns>ReadOnlyCollection of IWebElement objects so that you can interact that object</returns>
  618. /// <example>
  619. /// <code>
  620. /// IWebDriver driver = new RemoteWebDriver(DesiredCapabilities.Firefox());
  621. /// ReadOnlyCollection<![CDATA[<IWebElement>]]> elem = driver.FindElementsByXpath("//tr/td/a")
  622. /// </code>
  623. /// </example>
  624. public ReadOnlyCollection<IWebElement> FindElementsByXPath(string xpath)
  625. {
  626. return this.FindElements("xpath", xpath);
  627. }
  628. #endregion
  629. #region IFindsByCssSelector Members
  630. /// <summary>
  631. /// Finds the first element matching the specified CSS selector.
  632. /// </summary>
  633. /// <param name="cssSelector">The CSS selector to match.</param>
  634. /// <returns>The first <see cref="IWebElement"/> matching the criteria.</returns>
  635. public IWebElement FindElementByCssSelector(string cssSelector)
  636. {
  637. return this.FindElement("css selector", cssSelector);
  638. }
  639. /// <summary>
  640. /// Finds all elements matching the specified CSS selector.
  641. /// </summary>
  642. /// <param name="cssSelector">The CSS selector to match.</param>
  643. /// <returns>A <see cref="ReadOnlyCollection{T}"/> containing all
  644. /// <see cref="IWebElement">IWebElements</see> matching the criteria.</returns>
  645. public ReadOnlyCollection<IWebElement> FindElementsByCssSelector(string cssSelector)
  646. {
  647. return this.FindElements("css selector", cssSelector);
  648. }
  649. #endregion
  650. #region IDisposable Members
  651. /// <summary>
  652. /// Dispose the RemoteWebDriver Instance
  653. /// </summary>
  654. public void Dispose()
  655. {
  656. this.Dispose(true);
  657. GC.SuppressFinalize(this);
  658. }
  659. #endregion
  660. #region Internal Methods
  661. /// <summary>
  662. /// Executes commands with the driver
  663. /// </summary>
  664. /// <param name="driverCommandToExecute">Command that needs executing</param>
  665. /// <param name="parameters">Parameters needed for the command</param>
  666. /// <returns>WebDriver Response</returns>
  667. internal Response InternalExecute(string driverCommandToExecute, Dictionary<string, object> parameters)
  668. {
  669. return this.Execute(driverCommandToExecute, parameters);
  670. }
  671. /// <summary>
  672. /// Find the element in the response
  673. /// </summary>
  674. /// <param name="response">Response from the browser</param>
  675. /// <returns>Element from the page</returns>
  676. internal IWebElement GetElementFromResponse(Response response)
  677. {
  678. if (response == null)
  679. {
  680. throw new NoSuchElementException();
  681. }
  682. RemoteWebElement element = null;
  683. Dictionary<string, object> elementDictionary = response.Value as Dictionary<string, object>;
  684. if (elementDictionary != null)
  685. {
  686. string id = (string)elementDictionary["ELEMENT"];
  687. element = this.CreateElement(id);
  688. }
  689. return element;
  690. }
  691. /// <summary>
  692. /// Finds the elements that are in the response
  693. /// </summary>
  694. /// <param name="response">Response from the browser</param>
  695. /// <returns>Collection of elements</returns>
  696. internal ReadOnlyCollection<IWebElement> GetElementsFromResponse(Response response)
  697. {
  698. List<IWebElement> toReturn = new List<IWebElement>();
  699. object[] elements = response.Value as object[];
  700. foreach (object elementObject in elements)
  701. {
  702. Dictionary<string, object> elementDictionary = elementObject as Dictionary<string, object>;
  703. if (elementDictionary != null)
  704. {
  705. string id = (string)elementDictionary["ELEMENT"];
  706. RemoteWebElement element = this.CreateElement(id);
  707. toReturn.Add(element);
  708. }
  709. }
  710. return toReturn.AsReadOnly();
  711. }
  712. #endregion
  713. #region Protected Members
  714. /// <summary>
  715. /// Stops the client from running
  716. /// </summary>
  717. /// <param name="disposing">if its in the process of disposing</param>
  718. protected virtual void Dispose(bool disposing)
  719. {
  720. try
  721. {
  722. this.Execute(DriverCommand.Quit, null);
  723. }
  724. catch (NotImplementedException)
  725. {
  726. }
  727. catch (InvalidOperationException)
  728. {
  729. }
  730. catch (WebDriverException)
  731. {
  732. }
  733. finally
  734. {
  735. this.StopClient();
  736. this.sessionId = null;
  737. }
  738. }
  739. /// <summary>
  740. /// Starts a session with the driver
  741. /// </summary>
  742. /// <param name="desiredCapabilities">Capabilities of the browser</param>
  743. protected void StartSession(ICapabilities desiredCapabilities)
  744. {
  745. Dictionary<string, object> parameters = new Dictionary<string, object>();
  746. parameters.Add("desiredCapabilities", desiredCapabilities);
  747. Response response = this.Execute(DriverCommand.NewSession, parameters);
  748. Dictionary<string, object> rawCapabilities = (Dictionary<string, object>)response.Value;
  749. DesiredCapabilities returnedCapabilities = new DesiredCapabilities(rawCapabilities);
  750. this.capabilities = returnedCapabilities;
  751. this.sessionId = new SessionId(response.SessionId);
  752. }
  753. /// <summary>
  754. /// Executes a command with this driver .
  755. /// </summary>
  756. /// <param name="driverCommandToExecute">A <see cref="DriverCommand"/> value representing the command to execute.</param>
  757. /// <param name="parameters">A <see cref="Dictionary{K, V}"/> containing the names and values of the parameters of the command.</param>
  758. /// <returns>A <see cref="Response"/> containing information about the success or failure of the command and any data returned by the command.</returns>
  759. protected virtual Response Execute(string driverCommandToExecute, Dictionary<string, object> parameters)
  760. {
  761. Command commandToExecute = new Command(this.sessionId, driverCommandToExecute, parameters);
  762. Response commandResponse = new Response();
  763. try
  764. {
  765. commandResponse = this.executor.Execute(commandToExecute);
  766. }
  767. catch (System.Net.WebException e)
  768. {
  769. commandResponse.Status = WebDriverResult.UnhandledError;
  770. commandResponse.Value = e;
  771. }
  772. if (commandResponse.Status != WebDriverResult.Success)
  773. {
  774. UnpackAndThrowOnError(commandResponse);
  775. }
  776. return commandResponse;
  777. }
  778. /// <summary>
  779. /// Starts the command executor, enabling communication with the browser.
  780. /// </summary>
  781. protected virtual void StartClient()
  782. {
  783. }
  784. /// <summary>
  785. /// Stops the command executor, ending further communication with the browser.
  786. /// </summary>
  787. protected virtual void StopClient()
  788. {
  789. }
  790. /// <summary>
  791. /// Finds an element matching the given mechanism and value.
  792. /// </summary>
  793. /// <param name="mechanism">The mechanism by which to find the element.</param>
  794. /// <param name="value">The value to use to search for the element.</param>
  795. /// <returns>The first <see cref="IWebElement"/> matching the given criteria.</returns>
  796. protected IWebElement FindElement(string mechanism, string value)
  797. {
  798. Dictionary<string, object> parameters = new Dictionary<string, object>();
  799. parameters.Add("using", mechanism);
  800. parameters.Add("value", value);
  801. Response commandResponse = this.Execute(DriverCommand.FindElement, parameters);
  802. return this.GetElementFromResponse(commandResponse);
  803. }
  804. /// <summary>
  805. /// Finds all elements matching the given mechanism and value.
  806. /// </summary>
  807. /// <param name="mechanism">The mechanism by which to find the elements.</param>
  808. /// <param name="value">The value to use to search for the elements.</param>
  809. /// <returns>A collection of all of the <see cref="IWebElement">IWebElements</see> matching the given criteria.</returns>
  810. protected ReadOnlyCollection<IWebElement> FindElements(string mechanism, string value)
  811. {
  812. Dictionary<string, object> parameters = new Dictionary<string, object>();
  813. parameters.Add("using", mechanism);
  814. parameters.Add("value", value);
  815. Response commandResponse = this.Execute(DriverCommand.FindElements, parameters);
  816. return this.GetElementsFromResponse(commandResponse);
  817. }
  818. /// <summary>
  819. /// Creates a <see cref="RemoteWebElement"/> with the specified ID.
  820. /// </summary>
  821. /// <param name="elementId">The ID of this element.</param>
  822. /// <returns>A <see cref="RemoteWebElement"/> with the specified ID.</returns>
  823. protected virtual RemoteWebElement CreateElement(string elementId)
  824. {
  825. RemoteWebElement toReturn = new RemoteWebElement(this, elementId);
  826. return toReturn;
  827. }
  828. #endregion
  829. #region Private methods
  830. private static object ConvertObjectToJavaScriptObject(object arg)
  831. {
  832. IWrapsElement argAsWrapsElement = arg as IWrapsElement;
  833. RemoteWebElement argAsElement = arg as RemoteWebElement;
  834. IEnumerable argAsEnumerable = arg as IEnumerable;
  835. if (argAsElement == null && argAsWrapsElement != null)
  836. {
  837. argAsElement = argAsWrapsElement.WrappedElement as RemoteWebElement;
  838. }
  839. object converted = null;
  840. if (arg is string || arg is float || arg is double || arg is int || arg is long || arg is bool || arg == null)
  841. {
  842. converted = arg;
  843. }
  844. else if (argAsElement != null)
  845. {
  846. Dictionary<string, object> elementDictionary = new Dictionary<string, object>();
  847. elementDictionary.Add("ELEMENT", argAsElement.InternalElementId);
  848. converted = elementDictionary;
  849. }
  850. else if (argAsEnumerable != null)
  851. {
  852. List<object> objectList = new List<object>();
  853. foreach (object item in argAsEnumerable)
  854. {
  855. if (item is string || item is float || item is double || item is int || item is long || item is bool || item == null)
  856. {
  857. objectList.Add(item);
  858. }
  859. else
  860. {
  861. throw new ArgumentException("Only primitives may be used as elements in collections used as arguments for JavaScript functions.");
  862. }
  863. }
  864. converted = objectList.ToArray();
  865. }
  866. else
  867. {
  868. throw new ArgumentException("Argument is of an illegal type" + arg.ToString(), "arg");
  869. }
  870. return converted;
  871. }
  872. private static object[] ConvertArgumentsToJavaScriptObjects(object[] args)
  873. {
  874. if (args == null)
  875. {
  876. return new object[] { null };
  877. }
  878. for (int i = 0; i < args.Length; i++)
  879. {
  880. args[i] = ConvertObjectToJavaScriptObject(args[i]);
  881. }
  882. return args;
  883. }
  884. private static void UnpackAndThrowOnError(Response errorResponse)
  885. {
  886. // Check the status code of the error, and only handle if not success.
  887. if (errorResponse.Status != WebDriverResult.Success)
  888. {
  889. Dictionary<string, object> errorAsDictionary = errorResponse.Value as Dictionary<string, object>;
  890. if (errorAsDictionary != null)
  891. {
  892. ErrorResponse errorResponseObject = new ErrorResponse(errorAsDictionary);
  893. string errorMessage = errorResponseObject.Message;
  894. switch (errorResponse.Status)
  895. {
  896. case WebDriverResult.NoSuchElement:
  897. throw new NoSuchElementException(errorMessage);
  898. case WebDriverResult.NoSuchFrame:
  899. throw new NoSuchFrameException(errorMessage);
  900. case WebDriverResult.UnknownCommand:
  901. throw new NotImplementedException(errorMessage);
  902. case WebDriverResult.ObsoleteElement:
  903. throw new StaleElementReferenceException(errorMessage);
  904. case WebDriverResult.ElementNotDisplayed:
  905. throw new ElementNotVisibleException(errorMessage);
  906. case WebDriverResult.InvalidElementState:
  907. case WebDriverResult.ElementNotSelectable:
  908. throw new InvalidElementStateException(errorMessage);
  909. case WebDriverResult.UnhandledError:
  910. throw new InvalidOperationException(errorMessage);
  911. case WebDriverResult.NoSuchDocument:
  912. throw new NoSuchElementException(errorMessage);
  913. case WebDriverResult.Timeout:
  914. throw new WebDriverTimeoutException(errorMessage);
  915. case WebDriverResult.NoSuchWindow:
  916. throw new NoSuchWindowException(errorMessage);
  917. case WebDriverResult.InvalidCookieDomain:
  918. case WebDriverResult.UnableToSetCookie:
  919. throw new WebDriverException(errorMessage);
  920. case WebDriverResult.AsyncScriptTimeout:
  921. throw new WebDriverTimeoutException(errorMessage);
  922. case WebDriverResult.UnexpectedAlertOpen:
  923. // TODO(JimEvans): Handle the case where the unexpected alert setting
  924. // has been set to "ignore", so there is still a valid alert to be
  925. // handled.
  926. string alertText = string.Empty;
  927. if (errorAsDictionary.ContainsKey("alert"))
  928. {
  929. Dictionary<string, object> alertDescription = errorAsDictionary["alert"] as Dictionary<string, object>;
  930. if (alertDescription != null && alertDescription.ContainsKey("text"))
  931. {
  932. alertText = alertDescription["text"].ToString();
  933. }
  934. }
  935. throw new UnhandledAlertException(errorMessage, alertText);
  936. case WebDriverResult.NoAlertPresent:
  937. throw new NoAlertPresentException(errorMessage);
  938. case WebDriverResult.InvalidSelector:
  939. throw new InvalidSelectorException(errorMessage);
  940. default:
  941. throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "{0} ({1})", errorMessage, errorResponse.Status));
  942. }
  943. }
  944. else
  945. {
  946. throw new WebDriverException("Unexpected error. " + errorResponse.Value.ToString());
  947. }
  948. }
  949. }
  950. private object ExecuteScriptInternal(string script, bool async, params object[] args)
  951. {
  952. if (!this.Capabilities.IsJavaScriptEnabled)
  953. {
  954. throw new NotSupportedException("You must be using an underlying instance of WebDriver that supports executing javascript");
  955. }
  956. // Escape the quote marks
  957. // script = script.Replace("\"", "\\\"");
  958. object[] convertedArgs = ConvertArgumentsToJavaScriptObjects(args);
  959. Dictionary<string, object> parameters = new Dictionary<string, object>();
  960. parameters.Add("script", script);
  961. if (convertedArgs != null && convertedArgs.Length > 0)
  962. {
  963. parameters.Add("args", convertedArgs);
  964. }
  965. else
  966. {
  967. parameters.Add("args", new object[] { });
  968. }
  969. string command = async ? DriverCommand.ExecuteAsyncScript : DriverCommand.ExecuteScript;
  970. Response commandResponse = this.Execute(command, parameters);
  971. return this.ParseJavaScriptReturnValue(commandResponse.Value);
  972. }
  973. private object ParseJavaScriptReturnValue(object responseValue)
  974. {
  975. object returnValue = null;
  976. Dictionary<string, object> resultAsDictionary = responseValue as Dictionary<string, object>;
  977. object[] resultAsArray = responseValue as object[];
  978. if (resultAsDictionary != null)
  979. {
  980. if (resultAsDictionary.ContainsKey("ELEMENT"))
  981. {
  982. string id = (string)resultAsDictionary["ELEMENT"];
  983. RemoteWebElement element = this.CreateElement(id);
  984. returnValue = element;
  985. }
  986. else
  987. {
  988. // Recurse through the dictionary, re-parsing each value.
  989. string[] keyCopy = new string[resultAsDictionary.Keys.Count];
  990. resultAsDictionary.Keys.CopyTo(keyCopy, 0);
  991. foreach (string key in keyCopy)
  992. {
  993. resultAsDictionary[key] = this.ParseJavaScriptReturnValue(resultAsDictionary[key]);
  994. }
  995. returnValue = resultAsDictionary;
  996. }
  997. }
  998. else if (resultAsArray != null)
  999. {
  1000. bool allElementsAreWebElements = true;
  1001. List<object> toReturn = new List<object>();
  1002. foreach (object item in resultAsArray)
  1003. {
  1004. object parsedItem = this.ParseJavaScriptReturnValue(item);
  1005. IWebElement parsedItemAsElement = parsedItem as IWebElement;
  1006. if (parsedItemAsElement == null)
  1007. {
  1008. allElementsAreWebElements = false;
  1009. }
  1010. toReturn.Add(parsedItem);
  1011. }
  1012. if (toReturn.Count > 0 && allElementsAreWebElements)
  1013. {
  1014. List<IWebElement> elementList = new List<IWebElement>();
  1015. foreach (object listItem in toReturn)
  1016. {
  1017. IWebElement itemAsElement = listItem as IWebElement;
  1018. elementList.Add(itemAsElement);
  1019. }
  1020. returnValue = elementList.AsReadOnly();
  1021. }
  1022. else
  1023. {
  1024. returnValue = toReturn.AsReadOnly();
  1025. }
  1026. }
  1027. else
  1028. {
  1029. returnValue = responseValue;
  1030. }
  1031. return returnValue;
  1032. }
  1033. #endregion
  1034. }
  1035. }