| // <copyright file="EventFiringWebDriver.cs" company="Selenium Committers"> |
| // Licensed to the Software Freedom Conservancy (SFC) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The SFC licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // </copyright> |
| |
| using System; |
| using System.Collections.Generic; |
| using System.Collections.ObjectModel; |
| using System.Diagnostics.CodeAnalysis; |
| using System.Drawing; |
| using System.Threading.Tasks; |
| using OpenQA.Selenium.Internal; |
| |
| namespace OpenQA.Selenium.Support.Events; |
| |
| /// <summary> |
| /// A wrapper around an arbitrary WebDriver instance which supports registering for |
| /// events, e.g. for logging purposes. |
| /// </summary> |
| public class EventFiringWebDriver : IWebDriver, IJavaScriptExecutor, ITakesScreenshot, IWrapsDriver |
| { |
| /// <summary> |
| /// Initializes a new instance of the <see cref="EventFiringWebDriver"/> class. |
| /// </summary> |
| /// <param name="parentDriver">The driver to register events for.</param> |
| /// <exception cref="ArgumentNullException">If <paramref name="parentDriver"/> is <see langword="null"/>.</exception> |
| public EventFiringWebDriver(IWebDriver parentDriver) |
| { |
| this.WrappedDriver = parentDriver ?? throw new ArgumentNullException(nameof(parentDriver)); |
| } |
| |
| /// <summary> |
| /// Fires before the driver begins navigation. |
| /// </summary> |
| public event EventHandler<WebDriverNavigationEventArgs>? Navigating; |
| |
| /// <summary> |
| /// Fires after the driver completes navigation |
| /// </summary> |
| public event EventHandler<WebDriverNavigationEventArgs>? Navigated; |
| |
| /// <summary> |
| /// Fires before the driver begins navigation back one entry in the browser history list. |
| /// </summary> |
| public event EventHandler<WebDriverNavigationEventArgs>? NavigatingBack; |
| |
| /// <summary> |
| /// Fires after the driver completes navigation back one entry in the browser history list. |
| /// </summary> |
| public event EventHandler<WebDriverNavigationEventArgs>? NavigatedBack; |
| |
| /// <summary> |
| /// Fires before the driver begins navigation forward one entry in the browser history list. |
| /// </summary> |
| public event EventHandler<WebDriverNavigationEventArgs>? NavigatingForward; |
| |
| /// <summary> |
| /// Fires after the driver completes navigation forward one entry in the browser history list. |
| /// </summary> |
| public event EventHandler<WebDriverNavigationEventArgs>? NavigatedForward; |
| |
| /// <summary> |
| /// Fires before the driver clicks on an element. |
| /// </summary> |
| public event EventHandler<WebElementEventArgs>? ElementClicking; |
| |
| /// <summary> |
| /// Fires after the driver has clicked on an element. |
| /// </summary> |
| public event EventHandler<WebElementEventArgs>? ElementClicked; |
| |
| /// <summary> |
| /// Fires before the driver changes the value of an element via Clear(), SendKeys() or Toggle(). |
| /// </summary> |
| public event EventHandler<WebElementValueEventArgs>? ElementValueChanging; |
| |
| /// <summary> |
| /// Fires after the driver has changed the value of an element via Clear(), SendKeys() or Toggle(). |
| /// </summary> |
| public event EventHandler<WebElementValueEventArgs>? ElementValueChanged; |
| |
| /// <summary> |
| /// Fires before the driver starts to find an element. |
| /// </summary> |
| public event EventHandler<FindElementEventArgs>? FindingElement; |
| |
| /// <summary> |
| /// Fires after the driver completes finding an element. |
| /// </summary> |
| public event EventHandler<FindElementEventArgs>? FindElementCompleted; |
| |
| /// <summary> |
| /// Fires before the driver starts to get a shadow root. |
| /// </summary> |
| public event EventHandler<GetShadowRootEventArgs>? GettingShadowRoot; |
| |
| /// <summary> |
| /// Fires after the driver completes getting a shadow root. |
| /// </summary> |
| public event EventHandler<GetShadowRootEventArgs>? GetShadowRootCompleted; |
| |
| /// <summary> |
| /// Fires before a script is executed. |
| /// </summary> |
| public event EventHandler<WebDriverScriptEventArgs>? ScriptExecuting; |
| |
| /// <summary> |
| /// Fires after a script is executed. |
| /// </summary> |
| public event EventHandler<WebDriverScriptEventArgs>? ScriptExecuted; |
| |
| /// <summary> |
| /// Fires when an exception is thrown. |
| /// </summary> |
| public event EventHandler<WebDriverExceptionEventArgs>? ExceptionThrown; |
| |
| /// <summary> |
| /// Gets the <see cref="IWebDriver"/> wrapped by this EventsFiringWebDriver instance. |
| /// </summary> |
| public IWebDriver WrappedDriver { get; } |
| |
| /// <summary> |
| /// Gets or sets the URL the browser is currently displaying. |
| /// </summary> |
| /// <remarks> |
| /// Setting the <see cref="Url"/> property will load a new web page in the current browser window. |
| /// This is done using an HTTP GET operation, and the method will block until the |
| /// load is complete. This will follow redirects issued either by the server or |
| /// as a meta-redirect from within the returned HTML. Should a meta-redirect "rest" |
| /// for any duration of time, it is best to wait until this timeout is over, since |
| /// should the underlying page change while your test is executing the results of |
| /// future calls against this interface will be against the freshly loaded page. |
| /// </remarks> |
| /// <seealso cref="INavigation.GoToUrl(string)"/> |
| /// <seealso cref="INavigation.GoToUrl(System.Uri)"/> |
| public string Url |
| { |
| get |
| { |
| string url; |
| try |
| { |
| url = this.WrappedDriver.Url; |
| } |
| catch (Exception ex) |
| { |
| this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); |
| throw; |
| } |
| |
| return url; |
| } |
| |
| set |
| { |
| try |
| { |
| WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.WrappedDriver, value); |
| this.OnNavigating(e); |
| this.WrappedDriver.Url = value; |
| this.OnNavigated(e); |
| } |
| catch (Exception ex) |
| { |
| this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); |
| throw; |
| } |
| } |
| } |
| |
| /// <summary> |
| /// Gets the title of the current browser window. |
| /// </summary> |
| public string Title |
| { |
| get |
| { |
| string title; |
| try |
| { |
| title = this.WrappedDriver.Title; |
| } |
| catch (Exception ex) |
| { |
| this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); |
| throw; |
| } |
| |
| return title; |
| } |
| } |
| |
| /// <summary> |
| /// Gets the source of the page last loaded by the browser. |
| /// </summary> |
| /// <remarks> |
| /// If the page has been modified after loading (for example, by JavaScript) |
| /// there is no guarantee that the returned text is that of the modified page. |
| /// Please consult the documentation of the particular driver being used to |
| /// determine whether the returned text reflects the current state of the page |
| /// or the text last sent by the web server. The page source returned is a |
| /// representation of the underlying DOM: do not expect it to be formatted |
| /// or escaped in the same way as the response sent from the web server. |
| /// </remarks> |
| public string PageSource |
| { |
| get |
| { |
| string source; |
| try |
| { |
| source = this.WrappedDriver.PageSource; |
| } |
| catch (Exception ex) |
| { |
| this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); |
| throw; |
| } |
| |
| return source; |
| } |
| } |
| |
| /// <summary> |
| /// Gets the current window handle, which is an opaque handle to this |
| /// window that uniquely identifies it within this driver instance. |
| /// </summary> |
| public string CurrentWindowHandle |
| { |
| get |
| { |
| string handle; |
| try |
| { |
| handle = this.WrappedDriver.CurrentWindowHandle; |
| } |
| catch (Exception ex) |
| { |
| this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); |
| throw; |
| } |
| |
| return handle; |
| } |
| } |
| |
| /// <summary> |
| /// Gets the window handles of open browser windows. |
| /// </summary> |
| public ReadOnlyCollection<string> WindowHandles |
| { |
| get |
| { |
| ReadOnlyCollection<string> handles; |
| try |
| { |
| handles = this.WrappedDriver.WindowHandles; |
| } |
| catch (Exception ex) |
| { |
| this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); |
| throw; |
| } |
| |
| return handles; |
| } |
| } |
| |
| /// <summary> |
| /// Close the current window, quitting the browser if it is the last window currently open. |
| /// </summary> |
| public void Close() |
| { |
| try |
| { |
| this.WrappedDriver.Close(); |
| } |
| catch (Exception ex) |
| { |
| this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Quits this driver, closing every associated window. |
| /// </summary> |
| public void Quit() |
| { |
| try |
| { |
| this.WrappedDriver.Quit(); |
| } |
| catch (Exception ex) |
| { |
| this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Instructs the driver to change its settings. |
| /// </summary> |
| /// <returns>An <see cref="IOptions"/> object allowing the user to change |
| /// the settings of the driver.</returns> |
| public IOptions Manage() |
| { |
| return new EventFiringOptions(this); |
| } |
| |
| /// <summary> |
| /// Instructs the driver to navigate the browser to another location. |
| /// </summary> |
| /// <returns>An <see cref="INavigation"/> object allowing the user to access |
| /// the browser's history and to navigate to a given URL.</returns> |
| public INavigation Navigate() |
| { |
| return new EventFiringNavigation(this); |
| } |
| |
| /// <summary> |
| /// Instructs the driver to send future commands to a different frame or window. |
| /// </summary> |
| /// <returns>An <see cref="ITargetLocator"/> object which can be used to select |
| /// a frame or window.</returns> |
| public ITargetLocator SwitchTo() |
| { |
| return new EventFiringTargetLocator(this); |
| } |
| |
| /// <summary> |
| /// Find the first <see cref="IWebElement"/> using the given method. |
| /// </summary> |
| /// <param name="by">The locating mechanism to use.</param> |
| /// <returns>The first matching <see cref="IWebElement"/> on the current context.</returns> |
| /// <exception cref="NoSuchElementException">If no element matches the criteria.</exception> |
| public IWebElement FindElement(By by) |
| { |
| IWebElement wrappedElement; |
| try |
| { |
| FindElementEventArgs e = new FindElementEventArgs(this.WrappedDriver, by); |
| this.OnFindingElement(e); |
| IWebElement element = this.WrappedDriver.FindElement(by); |
| this.OnFindElementCompleted(e); |
| wrappedElement = this.WrapElement(element); |
| } |
| catch (Exception ex) |
| { |
| this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); |
| throw; |
| } |
| |
| return wrappedElement; |
| } |
| |
| /// <summary> |
| /// Find all <see cref="IWebElement">IWebElements</see> within the current context |
| /// using the given mechanism. |
| /// </summary> |
| /// <param name="by">The locating mechanism to use.</param> |
| /// <returns>A <see cref="ReadOnlyCollection{T}"/> of all <see cref="IWebElement">WebElements</see> |
| /// matching the current criteria, or an empty list if nothing matches.</returns> |
| public ReadOnlyCollection<IWebElement> FindElements(By by) |
| { |
| try |
| { |
| FindElementEventArgs e = new FindElementEventArgs(this.WrappedDriver, by); |
| this.OnFindingElement(e); |
| ReadOnlyCollection<IWebElement> elements = this.WrappedDriver.FindElements(by); |
| this.OnFindElementCompleted(e); |
| |
| List<IWebElement> wrappedElementList = new List<IWebElement>(elements.Count); |
| foreach (IWebElement element in elements) |
| { |
| IWebElement wrappedElement = this.WrapElement(element); |
| wrappedElementList.Add(wrappedElement); |
| } |
| return wrappedElementList.AsReadOnly(); |
| } |
| catch (Exception ex) |
| { |
| this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); |
| throw; |
| } |
| |
| } |
| |
| /// <summary> |
| /// Frees all managed and unmanaged resources used by this instance. |
| /// </summary> |
| public void Dispose() |
| { |
| this.Dispose(true); |
| GC.SuppressFinalize(this); |
| } |
| |
| /// <summary> |
| /// Executes JavaScript in the context of the currently selected frame or window. |
| /// </summary> |
| /// <param name="script">The JavaScript code to execute.</param> |
| /// <param name="args">The arguments to the script.</param> |
| /// <returns>The value returned by the script.</returns> |
| /// <remarks> |
| /// <para> |
| /// The ExecuteScript method executes JavaScript in the context of |
| /// the currently selected frame or window. This means that "document" will refer |
| /// to the current document. If the script has a return value, then the following |
| /// steps will be taken: |
| /// </para> |
| /// <para> |
| /// <list type="bullet"> |
| /// <item><description>For an HTML element, this method returns a <see cref="IWebElement"/></description></item> |
| /// <item><description>For a number, a <see cref="long"/> is returned</description></item> |
| /// <item><description>For a boolean, a <see cref="bool"/> is returned</description></item> |
| /// <item><description>For all other cases a <see cref="string"/> is returned.</description></item> |
| /// <item><description>For an array,we check the first element, and attempt to return a |
| /// <see cref="List{T}"/> of that type, following the rules above. Nested lists are not |
| /// supported.</description></item> |
| /// <item><description>If the value is null or there is no return value, |
| /// <see langword="null"/> is returned.</description></item> |
| /// </list> |
| /// </para> |
| /// <para> |
| /// Arguments must be a number (which will be converted to a <see cref="long"/>), |
| /// a <see cref="bool"/>, a <see cref="string"/> or a <see cref="IWebElement"/>, |
| /// or a <see cref="IWrapsElement"/>. |
| /// An exception will be thrown if the arguments do not meet these criteria. |
| /// The arguments will be made available to the JavaScript via the "arguments" magic |
| /// variable, as if the function were called via "Function.apply" |
| /// </para> |
| /// </remarks> |
| public object? ExecuteScript([StringSyntax(StringSyntaxConstants.JavaScript)] string script, params object?[] args) |
| { |
| if (this.WrappedDriver is not IJavaScriptExecutor javascriptDriver) |
| { |
| throw new NotSupportedException("Underlying driver instance does not support executing JavaScript"); |
| } |
| |
| object? scriptResult; |
| try |
| { |
| object?[] unwrappedArgs = UnwrapElementArguments(args); |
| |
| WebDriverScriptEventArgs e = new WebDriverScriptEventArgs(this.WrappedDriver, script); |
| this.OnScriptExecuting(e); |
| scriptResult = javascriptDriver.ExecuteScript(script, unwrappedArgs); |
| this.OnScriptExecuted(e); |
| } |
| catch (Exception ex) |
| { |
| this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); |
| throw; |
| } |
| |
| return scriptResult; |
| } |
| |
| |
| /// <summary> |
| /// Executes JavaScript in the context of the currently selected frame or window. |
| /// </summary> |
| /// <param name="script">A <see cref="PinnedScript"/> object containing the code to execute.</param> |
| /// <param name="args">The arguments to the script.</param> |
| /// <returns>The value returned by the script.</returns> |
| /// <exception cref="ArgumentNullException">If <paramref name="script"/> is <see langword="null"/>.</exception> |
| /// <remarks> |
| /// <para> |
| /// The ExecuteScript method executes JavaScript in the context of |
| /// the currently selected frame or window. This means that "document" will refer |
| /// to the current document. If the script has a return value, then the following |
| /// steps will be taken: |
| /// </para> |
| /// <para> |
| /// <list type="bullet"> |
| /// <item><description>For an HTML element, this method returns a <see cref="IWebElement"/></description></item> |
| /// <item><description>For a number, a <see cref="long"/> is returned</description></item> |
| /// <item><description>For a boolean, a <see cref="bool"/> is returned</description></item> |
| /// <item><description>For all other cases a <see cref="string"/> is returned.</description></item> |
| /// <item><description>For an array,we check the first element, and attempt to return a |
| /// <see cref="List{T}"/> of that type, following the rules above. Nested lists are not |
| /// supported.</description></item> |
| /// <item><description>If the value is null or there is no return value, |
| /// <see langword="null"/> is returned.</description></item> |
| /// </list> |
| /// </para> |
| /// <para> |
| /// Arguments must be a number (which will be converted to a <see cref="long"/>), |
| /// a <see cref="bool"/>, a <see cref="string"/> or a <see cref="IWebElement"/>, |
| /// or a <see cref="IWrapsElement"/>. |
| /// An exception will be thrown if the arguments do not meet these criteria. |
| /// The arguments will be made available to the JavaScript via the "arguments" magic |
| /// variable, as if the function were called via "Function.apply" |
| /// </para> |
| /// </remarks> |
| public object? ExecuteScript(PinnedScript script, params object?[] args) |
| { |
| if (script == null) |
| { |
| throw new ArgumentNullException(nameof(script)); |
| } |
| |
| if (this.WrappedDriver is not IJavaScriptExecutor javascriptDriver) |
| { |
| throw new NotSupportedException("Underlying driver instance does not support executing JavaScript"); |
| } |
| |
| object? scriptResult; |
| try |
| { |
| object?[] unwrappedArgs = UnwrapElementArguments(args); |
| |
| WebDriverScriptEventArgs e = new WebDriverScriptEventArgs(this.WrappedDriver, script.Source); |
| this.OnScriptExecuting(e); |
| scriptResult = javascriptDriver.ExecuteScript(script, unwrappedArgs); |
| this.OnScriptExecuted(e); |
| } |
| catch (Exception ex) |
| { |
| this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); |
| throw; |
| } |
| |
| return scriptResult; |
| } |
| |
| /// <summary> |
| /// Executes JavaScript asynchronously in the context of the currently selected frame or window. |
| /// </summary> |
| /// <param name="script">The JavaScript code to execute.</param> |
| /// <param name="args">The arguments to the script.</param> |
| /// <returns>The value returned by the script.</returns> |
| public object? ExecuteAsyncScript([StringSyntax(StringSyntaxConstants.JavaScript)] string script, params object?[] args) |
| { |
| if (this.WrappedDriver is not IJavaScriptExecutor javascriptDriver) |
| { |
| throw new NotSupportedException("Underlying driver instance does not support executing JavaScript"); |
| } |
| |
| object? scriptResult; |
| try |
| { |
| object?[] unwrappedArgs = UnwrapElementArguments(args); |
| |
| WebDriverScriptEventArgs e = new WebDriverScriptEventArgs(this.WrappedDriver, script); |
| this.OnScriptExecuting(e); |
| scriptResult = javascriptDriver.ExecuteAsyncScript(script, unwrappedArgs); |
| this.OnScriptExecuted(e); |
| } |
| catch (Exception ex) |
| { |
| this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); |
| throw; |
| } |
| |
| return scriptResult; |
| } |
| |
| /// <summary> |
| /// Gets a <see cref="Screenshot"/> object representing the image of the page on the screen. |
| /// </summary> |
| /// <returns>A <see cref="Screenshot"/> object containing the image.</returns> |
| public Screenshot GetScreenshot() |
| { |
| if (this.WrappedDriver is not ITakesScreenshot screenshotDriver) |
| { |
| throw new NotSupportedException("Underlying driver instance does not support taking screenshots"); |
| } |
| |
| return screenshotDriver.GetScreenshot(); |
| } |
| |
| /// <summary> |
| /// Frees all managed and, optionally, unmanaged resources used by this instance. |
| /// </summary> |
| /// <param name="disposing"><see langword="true"/> to dispose of only managed resources; |
| /// <see langword="false"/> to dispose of managed and unmanaged resources.</param> |
| protected virtual void Dispose(bool disposing) |
| { |
| if (disposing) |
| { |
| this.WrappedDriver.Dispose(); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="Navigating"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="WebDriverNavigationEventArgs"/> that contains the event data.</param> |
| protected virtual void OnNavigating(WebDriverNavigationEventArgs e) |
| { |
| if (this.Navigating != null) |
| { |
| this.Navigating(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="Navigated"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="WebDriverNavigationEventArgs"/> that contains the event data.</param> |
| protected virtual void OnNavigated(WebDriverNavigationEventArgs e) |
| { |
| if (this.Navigated != null) |
| { |
| this.Navigated(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="NavigatingBack"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="WebDriverNavigationEventArgs"/> that contains the event data.</param> |
| protected virtual void OnNavigatingBack(WebDriverNavigationEventArgs e) |
| { |
| if (this.NavigatingBack != null) |
| { |
| this.NavigatingBack(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="NavigatedBack"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="WebDriverNavigationEventArgs"/> that contains the event data.</param> |
| protected virtual void OnNavigatedBack(WebDriverNavigationEventArgs e) |
| { |
| if (this.NavigatedBack != null) |
| { |
| this.NavigatedBack(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="NavigatingForward"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="WebDriverNavigationEventArgs"/> that contains the event data.</param> |
| protected virtual void OnNavigatingForward(WebDriverNavigationEventArgs e) |
| { |
| if (this.NavigatingForward != null) |
| { |
| this.NavigatingForward(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="NavigatedForward"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="WebDriverNavigationEventArgs"/> that contains the event data.</param> |
| protected virtual void OnNavigatedForward(WebDriverNavigationEventArgs e) |
| { |
| if (this.NavigatedForward != null) |
| { |
| this.NavigatedForward(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="ElementClicking"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="WebElementEventArgs"/> that contains the event data.</param> |
| protected virtual void OnElementClicking(WebElementEventArgs e) |
| { |
| if (this.ElementClicking != null) |
| { |
| this.ElementClicking(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="ElementClicked"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="WebElementEventArgs"/> that contains the event data.</param> |
| protected virtual void OnElementClicked(WebElementEventArgs e) |
| { |
| if (this.ElementClicked != null) |
| { |
| this.ElementClicked(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="ElementValueChanging"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="WebElementValueEventArgs"/> that contains the event data.</param> |
| protected virtual void OnElementValueChanging(WebElementValueEventArgs e) |
| { |
| if (this.ElementValueChanging != null) |
| { |
| this.ElementValueChanging(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="ElementValueChanged"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="WebElementValueEventArgs"/> that contains the event data.</param> |
| protected virtual void OnElementValueChanged(WebElementValueEventArgs e) |
| { |
| if (this.ElementValueChanged != null) |
| { |
| this.ElementValueChanged(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="FindingElement"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="FindElementEventArgs"/> that contains the event data.</param> |
| protected virtual void OnFindingElement(FindElementEventArgs e) |
| { |
| if (this.FindingElement != null) |
| { |
| this.FindingElement(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="FindElementCompleted"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="FindElementEventArgs"/> that contains the event data.</param> |
| protected virtual void OnFindElementCompleted(FindElementEventArgs e) |
| { |
| if (this.FindElementCompleted != null) |
| { |
| this.FindElementCompleted(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="OnGettingShadowRoot"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="GetShadowRootEventArgs"/> that contains the event data.</param> |
| protected virtual void OnGettingShadowRoot(GetShadowRootEventArgs e) |
| { |
| if (this.GettingShadowRoot != null) |
| { |
| this.GettingShadowRoot(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="OnGetShadowRootCompleted"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="GetShadowRootEventArgs"/> that contains the event data.</param> |
| protected virtual void OnGetShadowRootCompleted(GetShadowRootEventArgs e) |
| { |
| if (this.GetShadowRootCompleted != null) |
| { |
| this.GetShadowRootCompleted(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="ScriptExecuting"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="WebDriverScriptEventArgs"/> that contains the event data.</param> |
| protected virtual void OnScriptExecuting(WebDriverScriptEventArgs e) |
| { |
| if (this.ScriptExecuting != null) |
| { |
| this.ScriptExecuting(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="ScriptExecuted"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="WebDriverScriptEventArgs"/> that contains the event data.</param> |
| protected virtual void OnScriptExecuted(WebDriverScriptEventArgs e) |
| { |
| if (this.ScriptExecuted != null) |
| { |
| this.ScriptExecuted(this, e); |
| } |
| } |
| |
| /// <summary> |
| /// Raises the <see cref="ExceptionThrown"/> event. |
| /// </summary> |
| /// <param name="e">A <see cref="WebDriverExceptionEventArgs"/> that contains the event data.</param> |
| protected virtual void OnException(WebDriverExceptionEventArgs e) |
| { |
| if (this.ExceptionThrown != null) |
| { |
| this.ExceptionThrown(this, e); |
| } |
| } |
| |
| private static object?[] UnwrapElementArguments(object?[] args) |
| { |
| if (args is null) |
| { |
| throw new InvalidOperationException("Cannot unwrap null args"); |
| } |
| |
| // Walk the args: the various drivers expect unwrapped versions of the elements |
| List<object?> unwrappedArgs = new List<object?>(args.Length); |
| foreach (object? arg in args) |
| { |
| if (arg is IWrapsElement eventElementArg) |
| { |
| unwrappedArgs.Add(eventElementArg.WrappedElement); |
| } |
| else |
| { |
| unwrappedArgs.Add(arg); |
| } |
| } |
| |
| return unwrappedArgs.ToArray(); |
| } |
| |
| private IWebElement WrapElement(IWebElement underlyingElement) |
| { |
| return new EventFiringWebElement(this, underlyingElement); |
| } |
| |
| /// <summary> |
| /// Provides a mechanism for Navigating with the driver. |
| /// </summary> |
| private class EventFiringNavigation : INavigation |
| { |
| private readonly EventFiringWebDriver parentDriver; |
| private readonly INavigation wrappedNavigation; |
| |
| /// <summary> |
| /// Initializes a new instance of the <see cref="EventFiringNavigation"/> class |
| /// </summary> |
| /// <param name="driver">Driver in use</param> |
| public EventFiringNavigation(EventFiringWebDriver driver) |
| { |
| this.parentDriver = driver ?? throw new ArgumentNullException(nameof(driver)); |
| this.wrappedNavigation = this.parentDriver.WrappedDriver.Navigate(); |
| } |
| |
| /// <summary> |
| /// Move the browser back |
| /// </summary> |
| public void Back() |
| { |
| Task.Run(async delegate |
| { |
| await this.BackAsync(); |
| }).GetAwaiter().GetResult(); |
| } |
| |
| /// <summary> |
| /// Move the browser back as an asynchronous task. |
| /// </summary> |
| /// <returns>A task object representing the asynchronous operation</returns> |
| public async Task BackAsync() |
| { |
| try |
| { |
| WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.parentDriver); |
| |
| this.parentDriver.OnNavigatingBack(e); |
| await this.wrappedNavigation.BackAsync().ConfigureAwait(false); |
| this.parentDriver.OnNavigatedBack(e); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Move a single "item" forward in the browser's history. |
| /// </summary> |
| public void Forward() |
| { |
| Task.Run(async delegate |
| { |
| await this.ForwardAsync(); |
| }).GetAwaiter().GetResult(); |
| } |
| |
| /// <summary> |
| /// Move a single "item" forward in the browser's history as an asynchronous task. |
| /// </summary> |
| /// <returns>A task object representing the asynchronous operation.</returns> |
| public async Task ForwardAsync() |
| { |
| try |
| { |
| WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.parentDriver); |
| this.parentDriver.OnNavigatingForward(e); |
| await this.wrappedNavigation.ForwardAsync().ConfigureAwait(false); |
| this.parentDriver.OnNavigatedForward(e); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Navigate to a url. |
| /// </summary> |
| /// <param name="url">String of where you want the browser to go to</param> |
| public void GoToUrl(string url) |
| { |
| Task.Run(async delegate |
| { |
| await this.GoToUrlAsync(url); |
| }).GetAwaiter().GetResult(); |
| } |
| |
| /// <summary> |
| /// Navigate to a url as an asynchronous task. |
| /// </summary> |
| /// <param name="url">String of where you want the browser to go.</param> |
| /// <returns>A task object representing the asynchronous operation.</returns> |
| public async Task GoToUrlAsync(string url) |
| { |
| if (url == null) |
| { |
| throw new ArgumentNullException(nameof(url), "url cannot be null"); |
| } |
| |
| try |
| { |
| WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.parentDriver, url); |
| this.parentDriver.OnNavigating(e); |
| await this.wrappedNavigation.GoToUrlAsync(url).ConfigureAwait(false); |
| this.parentDriver.OnNavigated(e); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Navigate to a url. |
| /// </summary> |
| /// <param name="url">Uri object of where you want the browser to go to</param> |
| public void GoToUrl(Uri url) |
| { |
| Task.Run(async delegate |
| { |
| await this.GoToUrlAsync(url); |
| }).GetAwaiter().GetResult(); |
| } |
| |
| /// <summary> |
| /// Navigate to a url as an asynchronous task. |
| /// </summary> |
| /// <param name="url">Uri object of where you want the browser to go.</param> |
| /// <returns>A task object representing the asynchronous operation.</returns> |
| public async Task GoToUrlAsync(Uri url) |
| { |
| if (url == null) |
| { |
| throw new ArgumentNullException(nameof(url), "url cannot be null"); |
| } |
| |
| await this.GoToUrlAsync(url.ToString()).ConfigureAwait(false); |
| } |
| |
| /// <summary> |
| /// Reload the current page. |
| /// </summary> |
| public void Refresh() |
| { |
| Task.Run(async delegate |
| { |
| await this.RefreshAsync(); |
| }).GetAwaiter().GetResult(); |
| } |
| |
| /// <summary> |
| /// Reload the current page as an asynchronous task. |
| /// </summary> |
| /// <returns>A task object representing the asynchronous operation.</returns> |
| public async Task RefreshAsync() |
| { |
| try |
| { |
| await this.wrappedNavigation.RefreshAsync().ConfigureAwait(false); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| } |
| } |
| |
| /// <summary> |
| /// Provides a mechanism for setting options needed for the driver during the test. |
| /// </summary> |
| private class EventFiringOptions : IOptions |
| { |
| private readonly IOptions wrappedOptions; |
| |
| /// <summary> |
| /// Initializes a new instance of the <see cref="EventFiringOptions"/> class |
| /// </summary> |
| /// <param name="driver">Instance of the driver currently in use</param> |
| public EventFiringOptions(EventFiringWebDriver driver) |
| { |
| this.wrappedOptions = driver.WrappedDriver.Manage(); |
| } |
| |
| /// <summary> |
| /// Gets an object allowing the user to manipulate cookies on the page. |
| /// </summary> |
| public ICookieJar Cookies => this.wrappedOptions.Cookies; |
| |
| /// <summary> |
| /// Gets an object allowing the user to manipulate the currently-focused browser window. |
| /// </summary> |
| /// <remarks>"Currently-focused" is defined as the browser window having the window handle |
| /// returned when IWebDriver.CurrentWindowHandle is called.</remarks> |
| public IWindow Window => this.wrappedOptions.Window; |
| |
| public ILogs Logs => this.wrappedOptions.Logs; |
| |
| public INetwork Network => this.wrappedOptions.Network; |
| |
| /// <summary> |
| /// Provides access to the timeouts defined for this driver. |
| /// </summary> |
| /// <returns>An object implementing the <see cref="ITimeouts"/> interface.</returns> |
| public ITimeouts Timeouts() |
| { |
| return new EventFiringTimeouts(this.wrappedOptions); |
| } |
| } |
| |
| /// <summary> |
| /// Provides a mechanism for finding elements on the page with locators. |
| /// </summary> |
| private class EventFiringTargetLocator : ITargetLocator |
| { |
| private readonly ITargetLocator wrappedLocator; |
| private readonly EventFiringWebDriver parentDriver; |
| |
| /// <summary> |
| /// Initializes a new instance of the <see cref="EventFiringTargetLocator"/> class |
| /// </summary> |
| /// <param name="driver">The driver that is currently in use</param> |
| public EventFiringTargetLocator(EventFiringWebDriver driver) |
| { |
| this.parentDriver = driver ?? throw new ArgumentNullException(nameof(driver)); |
| this.wrappedLocator = this.parentDriver.WrappedDriver.SwitchTo(); |
| } |
| |
| /// <summary> |
| /// Move to a different frame using its index |
| /// </summary> |
| /// <param name="frameIndex">The index of the </param> |
| /// <returns>A WebDriver instance that is currently in use</returns> |
| public IWebDriver Frame(int frameIndex) |
| { |
| IWebDriver driver; |
| try |
| { |
| driver = this.wrappedLocator.Frame(frameIndex); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return driver; |
| } |
| |
| /// <summary> |
| /// Move to different frame using its name |
| /// </summary> |
| /// <param name="frameName">name of the frame</param> |
| /// <returns>A WebDriver instance that is currently in use</returns> |
| public IWebDriver Frame(string frameName) |
| { |
| IWebDriver driver; |
| try |
| { |
| driver = this.wrappedLocator.Frame(frameName); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return driver; |
| } |
| |
| /// <summary> |
| /// Move to a frame element. |
| /// </summary> |
| /// <param name="frameElement">a previously found FRAME or IFRAME element.</param> |
| /// <returns>A WebDriver instance that is currently in use.</returns> |
| public IWebDriver Frame(IWebElement frameElement) |
| { |
| IWebDriver driver; |
| try |
| { |
| IWrapsElement wrapper = (IWrapsElement)frameElement; |
| driver = this.wrappedLocator.Frame(wrapper.WrappedElement); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return driver; |
| } |
| |
| /// <summary> |
| /// Select the parent frame of the currently selected frame. |
| /// </summary> |
| /// <returns>An <see cref="IWebDriver"/> instance focused on the specified frame.</returns> |
| public IWebDriver ParentFrame() |
| { |
| IWebDriver driver; |
| try |
| { |
| driver = this.wrappedLocator.ParentFrame(); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return driver; |
| } |
| |
| /// <summary> |
| /// Change to the Window by passing in the name |
| /// </summary> |
| /// <param name="windowName">name of the window that you wish to move to</param> |
| /// <returns>A WebDriver instance that is currently in use</returns> |
| public IWebDriver Window(string windowName) |
| { |
| IWebDriver driver; |
| try |
| { |
| driver = this.wrappedLocator.Window(windowName); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return driver; |
| } |
| |
| /// <summary> |
| /// Creates a new browser window and switches the focus for future commands |
| /// of this driver to the new window. |
| /// </summary> |
| /// <param name="typeHint">The type of new browser window to be created. |
| /// The created window is not guaranteed to be of the requested type; if |
| /// the driver does not support the requested type, a new browser window |
| /// will be created of whatever type the driver does support.</param> |
| /// <returns>An <see cref="IWebDriver"/> instance focused on the new browser.</returns> |
| public IWebDriver NewWindow(WindowType typeHint) |
| { |
| IWebDriver driver; |
| try |
| { |
| driver = this.wrappedLocator.NewWindow(typeHint); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return driver; |
| } |
| |
| /// <summary> |
| /// Change the active frame to the default |
| /// </summary> |
| /// <returns>Element of the default</returns> |
| public IWebDriver DefaultContent() |
| { |
| IWebDriver driver; |
| try |
| { |
| driver = this.wrappedLocator.DefaultContent(); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return driver; |
| } |
| |
| /// <summary> |
| /// Finds the active element on the page and returns it |
| /// </summary> |
| /// <returns>Element that is active</returns> |
| public IWebElement ActiveElement() |
| { |
| IWebElement element; |
| try |
| { |
| element = this.wrappedLocator.ActiveElement(); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return element; |
| } |
| |
| /// <summary> |
| /// Switches to the currently active modal dialog for this particular driver instance. |
| /// </summary> |
| /// <returns>A handle to the dialog.</returns> |
| public IAlert Alert() |
| { |
| IAlert alert; |
| try |
| { |
| alert = this.wrappedLocator.Alert(); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return alert; |
| } |
| } |
| |
| /// <summary> |
| /// Defines the interface through which the user can define timeouts. |
| /// </summary> |
| private class EventFiringTimeouts : ITimeouts |
| { |
| private readonly ITimeouts wrappedTimeouts; |
| |
| /// <summary> |
| /// Initializes a new instance of the <see cref="EventFiringTimeouts"/> class |
| /// </summary> |
| /// <param name="options">The <see cref="IOptions"/> object to wrap.</param> |
| public EventFiringTimeouts(IOptions options) |
| { |
| this.wrappedTimeouts = options.Timeouts(); |
| } |
| |
| /// <summary> |
| /// Gets or sets the implicit wait timeout, which is the amount of time the |
| /// driver should wait when searching for an element if it is not immediately |
| /// present. |
| /// </summary> |
| /// <remarks> |
| /// When searching for a single element, the driver should poll the page |
| /// until the element has been found, or this timeout expires before throwing |
| /// a <see cref="NoSuchElementException"/>. When searching for multiple elements, |
| /// the driver should poll the page until at least one element has been found |
| /// or this timeout has expired. |
| /// <para> |
| /// Increasing the implicit wait timeout should be used judiciously as it |
| /// will have an adverse effect on test run time, especially when used with |
| /// slower location strategies like XPath. |
| /// </para> |
| /// </remarks> |
| public TimeSpan ImplicitWait |
| { |
| get => this.wrappedTimeouts.ImplicitWait; |
| set => this.wrappedTimeouts.ImplicitWait = value; |
| } |
| |
| /// <summary> |
| /// Gets or sets the asynchronous script timeout, which is the amount |
| /// of time the driver should wait when executing JavaScript asynchronously. |
| /// This timeout only affects the <see cref="IJavaScriptExecutor.ExecuteAsyncScript(string, object[])"/> |
| /// method. |
| /// </summary> |
| public TimeSpan AsynchronousJavaScript |
| { |
| get => this.wrappedTimeouts.AsynchronousJavaScript; |
| set => this.wrappedTimeouts.AsynchronousJavaScript = value; |
| } |
| |
| /// <summary> |
| /// Gets or sets the page load timeout, which is the amount of time the driver |
| /// should wait for a page to load when setting the <see cref="IWebDriver.Url"/> |
| /// property. |
| /// </summary> |
| public TimeSpan PageLoad |
| { |
| get => this.wrappedTimeouts.PageLoad; |
| set => this.wrappedTimeouts.PageLoad = value; |
| } |
| } |
| |
| /// <summary> |
| /// EventFiringWebElement allows you to have access to specific items that are found on the page |
| /// </summary> |
| private class EventFiringWebElement : ITakesScreenshot, IWebElement, IWrapsElement, IWrapsDriver, IEquatable<IWebElement> |
| { |
| private readonly EventFiringWebDriver parentDriver; |
| |
| /// <summary> |
| /// Initializes a new instance of the <see cref="EventFiringWebElement"/> class. |
| /// </summary> |
| /// <param name="driver">The <see cref="EventFiringWebDriver"/> instance hosting this element.</param> |
| /// <param name="element">The <see cref="IWebElement"/> to wrap for event firing.</param> |
| public EventFiringWebElement(EventFiringWebDriver driver, IWebElement element) |
| { |
| this.WrappedElement = element ?? throw new ArgumentNullException(nameof(element)); |
| this.parentDriver = driver ?? throw new ArgumentNullException(nameof(driver)); |
| } |
| |
| /// <summary> |
| /// Gets the underlying wrapped <see cref="IWebElement"/>. |
| /// </summary> |
| public IWebElement WrappedElement { get; } |
| |
| /// <summary> |
| /// Gets the underlying parent wrapped <see cref="IWebDriver"/> |
| /// </summary> |
| public IWebDriver WrappedDriver => this.parentDriver; |
| |
| /// <summary> |
| /// Gets the DOM Tag of element |
| /// </summary> |
| public string TagName |
| { |
| get |
| { |
| string tagName; |
| try |
| { |
| tagName = this.WrappedElement.TagName; |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return tagName; |
| } |
| } |
| |
| /// <summary> |
| /// Gets the text from the element |
| /// </summary> |
| public string Text |
| { |
| get |
| { |
| string text; |
| try |
| { |
| text = this.WrappedElement.Text; |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return text; |
| } |
| } |
| |
| /// <summary> |
| /// Gets a value indicating whether an element is currently enabled |
| /// </summary> |
| public bool Enabled |
| { |
| get |
| { |
| bool enabled; |
| try |
| { |
| enabled = this.WrappedElement.Enabled; |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return enabled; |
| } |
| } |
| |
| /// <summary> |
| /// Gets a value indicating whether this element is selected or not. This operation only applies to input elements such as checkboxes, options in a select and radio buttons. |
| /// </summary> |
| public bool Selected |
| { |
| get |
| { |
| bool selected; |
| try |
| { |
| selected = this.WrappedElement.Selected; |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return selected; |
| } |
| } |
| |
| /// <summary> |
| /// Gets the Location of an element and returns a Point object |
| /// </summary> |
| public Point Location |
| { |
| get |
| { |
| Point location; |
| try |
| { |
| location = this.WrappedElement.Location; |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return location; |
| } |
| } |
| |
| /// <summary> |
| /// Gets the <see cref="Size"/> of the element on the page |
| /// </summary> |
| public Size Size |
| { |
| get |
| { |
| Size size; |
| try |
| { |
| size = this.WrappedElement.Size; |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return size; |
| } |
| } |
| |
| /// <summary> |
| /// Gets a value indicating whether the element is currently being displayed |
| /// </summary> |
| public bool Displayed |
| { |
| get |
| { |
| bool displayed; |
| try |
| { |
| displayed = this.WrappedElement.Displayed; |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return displayed; |
| } |
| } |
| |
| /// <summary> |
| /// Gets the underlying EventFiringWebDriver for this element. |
| /// </summary> |
| protected EventFiringWebDriver ParentDriver => this.parentDriver; |
| |
| /// <summary> |
| /// Method to clear the text out of an Input element |
| /// </summary> |
| public void Clear() |
| { |
| try |
| { |
| WebElementValueEventArgs e = new WebElementValueEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement, null); |
| this.parentDriver.OnElementValueChanging(e); |
| this.WrappedElement.Clear(); |
| this.parentDriver.OnElementValueChanged(e); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Method for sending native key strokes to the browser |
| /// </summary> |
| /// <param name="text">String containing what you would like to type onto the screen</param> |
| public void SendKeys(string text) |
| { |
| try |
| { |
| WebElementValueEventArgs e = new WebElementValueEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement, text); |
| this.parentDriver.OnElementValueChanging(e); |
| this.WrappedElement.SendKeys(text); |
| this.parentDriver.OnElementValueChanged(e); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// If this current element is a form, or an element within a form, then this will be submitted to the remote server. |
| /// If this causes the current page to change, then this method will block until the new page is loaded. |
| /// </summary> |
| public void Submit() |
| { |
| try |
| { |
| this.WrappedElement.Submit(); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Click this element. If this causes a new page to load, this method will block until |
| /// the page has loaded. At this point, you should discard all references to this element |
| /// and any further operations performed on this element will have undefined behavior unless |
| /// you know that the element and the page will still be present. If this element is not |
| /// clickable, then this operation is a no-op since it's pretty common for someone to |
| /// accidentally miss the target when clicking in Real Life |
| /// </summary> |
| public void Click() |
| { |
| try |
| { |
| WebElementEventArgs e = new WebElementEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement); |
| this.parentDriver.OnElementClicking(e); |
| this.WrappedElement.Click(); |
| this.parentDriver.OnElementClicked(e); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// If this current element is a form, or an element within a form, then this will be submitted to the remote server. If this causes the current page to change, then this method will block until the new page is loaded. |
| /// </summary> |
| /// <param name="attributeName">Attribute you wish to get details of</param> |
| /// <returns>The attribute's current value or null if the value is not set.</returns> |
| public string? GetAttribute(string attributeName) |
| { |
| try |
| { |
| return this.WrappedElement.GetAttribute(attributeName); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Gets the value of a declared HTML attribute of this element. |
| /// </summary> |
| /// <param name="attributeName">The name of the HTML attribute to get the value of.</param> |
| /// <returns>The HTML attribute's current value. Returns a <see langword="null"/> if the |
| /// value is not set or the declared attribute does not exist.</returns> |
| /// <remarks> |
| /// As opposed to the <see cref="GetAttribute(string)"/> method, this method |
| /// only returns attributes declared in the element's HTML markup. To access the value |
| /// of an IDL property of the element, either use the <see cref="GetAttribute(string)"/> |
| /// method or the <see cref="GetDomProperty(string)"/> method. |
| /// </remarks> |
| public string? GetDomAttribute(string attributeName) |
| { |
| try |
| { |
| return this.WrappedElement.GetDomAttribute(attributeName); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Gets the value of a JavaScript property of this element. |
| /// </summary> |
| /// <param name="propertyName">The name of the JavaScript property to get the value of.</param> |
| /// <returns>The JavaScript property's current value. Returns a <see langword="null"/> if the |
| /// value is not set or the property does not exist.</returns> |
| public string? GetDomProperty(string propertyName) |
| { |
| try |
| { |
| return this.WrappedElement.GetDomProperty(propertyName); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Method to return the value of a CSS Property |
| /// </summary> |
| /// <param name="propertyName">CSS property key</param> |
| /// <returns>string value of the CSS property</returns> |
| public string GetCssValue(string propertyName) |
| { |
| string cssValue; |
| try |
| { |
| cssValue = this.WrappedElement.GetCssValue(propertyName); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return cssValue; |
| } |
| |
| /// <summary> |
| /// Gets the representation of an element's shadow root for accessing the shadow DOM of a web component. |
| /// </summary> |
| /// <exception cref="NoSuchShadowRootException">Thrown when this element does not have a shadow root.</exception> |
| /// <returns>A shadow root representation.</returns> |
| public ISearchContext GetShadowRoot() |
| { |
| try |
| { |
| GetShadowRootEventArgs e = new GetShadowRootEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement); |
| this.parentDriver.OnGettingShadowRoot(e); |
| ISearchContext shadowRoot = this.WrappedElement.GetShadowRoot(); |
| this.parentDriver.OnGetShadowRootCompleted(e); |
| return new EventFiringShadowRoot(this.parentDriver, shadowRoot); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Finds the first element in the page that matches the <see cref="By"/> object |
| /// </summary> |
| /// <param name="by">By mechanism to find the element</param> |
| /// <returns>IWebElement object so that you can interaction that object</returns> |
| public IWebElement FindElement(By by) |
| { |
| IWebElement wrappedElement; |
| try |
| { |
| FindElementEventArgs e = new FindElementEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement, by); |
| this.parentDriver.OnFindingElement(e); |
| IWebElement element = this.WrappedElement.FindElement(by); |
| this.parentDriver.OnFindElementCompleted(e); |
| wrappedElement = this.parentDriver.WrapElement(element); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return wrappedElement; |
| } |
| |
| /// <summary> |
| /// Finds the elements on the page by using the <see cref="By"/> object and returns a ReadOnlyCollection of the Elements on the page |
| /// </summary> |
| /// <param name="by">By mechanism to find the element</param> |
| /// <returns>ReadOnlyCollection of IWebElement</returns> |
| public ReadOnlyCollection<IWebElement> FindElements(By by) |
| { |
| try |
| { |
| FindElementEventArgs e = new FindElementEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement, by); |
| this.parentDriver.OnFindingElement(e); |
| ReadOnlyCollection<IWebElement> elements = this.WrappedElement.FindElements(by); |
| this.parentDriver.OnFindElementCompleted(e); |
| |
| List<IWebElement> wrappedElementList = new List<IWebElement>(elements.Count); |
| foreach (IWebElement element in elements) |
| { |
| IWebElement wrappedElement = this.parentDriver.WrapElement(element); |
| wrappedElementList.Add(wrappedElement); |
| } |
| |
| return wrappedElementList.AsReadOnly(); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Gets a <see cref="Screenshot"/> object representing the image of the page on the screen. |
| /// </summary> |
| /// <returns>A <see cref="Screenshot"/> object containing the image.</returns> |
| public Screenshot GetScreenshot() |
| { |
| if (this.WrappedElement is not ITakesScreenshot screenshotDriver) |
| { |
| throw new NotSupportedException("Underlying element instance does not support taking screenshots"); |
| } |
| |
| return screenshotDriver.GetScreenshot(); |
| } |
| |
| /// <summary> |
| /// Determines whether the specified <see cref="object"/> is equal to the current <see cref="EventFiringWebElement"/>. |
| /// </summary> |
| /// <param name="obj">The <see cref="object"/> to compare to the current <see cref="EventFiringWebElement"/>.</param> |
| /// <returns><see langword="true"/> if the current <see cref="EventFiringWebElement"/> is equal to the other parameter; otherwise, <see langword="false"/>.</returns> |
| public override bool Equals(object? obj) |
| { |
| return Equals(obj as IWebElement); |
| } |
| |
| /// <summary> |
| /// Indicates whether the current <see cref="EventFiringWebElement"/> is equal to another <see cref="IWebElement"/>. |
| /// </summary> |
| /// <param name="other">An <see cref="IWebElement"/> to compare with this <see cref="EventFiringWebElement"/>.</param> |
| /// <returns><see langword="true"/> if the current <see cref="EventFiringWebElement"/> is equal to the <paramref name="other"/> parameter; otherwise, <see langword="false"/>.</returns> |
| public bool Equals(IWebElement? other) |
| { |
| if (other is IWrapsElement otherWrapper) |
| { |
| other = otherWrapper.WrappedElement; |
| } |
| |
| return WrappedElement.Equals(other); |
| } |
| |
| /// <summary> |
| /// Return the hash code for this <see cref="EventFiringWebElement"/>. |
| /// </summary> |
| /// <returns>A 32-bit signed integer hash code.</returns> |
| public override int GetHashCode() |
| { |
| return this.WrappedElement.GetHashCode(); |
| } |
| } |
| |
| /// <summary> |
| /// EventFiringShadowElement allows you to have access to specific shadow elements |
| /// </summary> |
| private class EventFiringShadowRoot : ISearchContext, IWrapsDriver, IEquatable<ISearchContext> |
| { |
| private readonly EventFiringWebDriver parentDriver; |
| |
| /// <summary> |
| /// Initializes a new instance of the <see cref="EventFiringShadowRoot"/> class. |
| /// </summary> |
| /// <param name="driver">The <see cref="EventFiringWebDriver"/> instance hosting this element.</param> |
| /// <param name="searchContext">The <see cref="ISearchContext"/> to wrap for event firing.</param> |
| public EventFiringShadowRoot(EventFiringWebDriver driver, ISearchContext searchContext) |
| { |
| this.WrappedSearchContext = searchContext ?? throw new ArgumentNullException(nameof(searchContext)); |
| this.parentDriver = driver; |
| } |
| |
| /// <summary> |
| /// Gets the underlying wrapped <see cref="ISearchContext"/>. |
| /// </summary> |
| public ISearchContext WrappedSearchContext { get; } |
| |
| /// <summary> |
| /// Gets the underlying parent wrapped <see cref="IWebDriver"/> |
| /// </summary> |
| public IWebDriver WrappedDriver => this.parentDriver; |
| |
| /// <summary> |
| /// Finds the first element in the page that matches the <see cref="By"/> object |
| /// </summary> |
| /// <param name="by">By mechanism to find the element</param> |
| /// <returns>IWebElement object so that you can interaction that object</returns> |
| public IWebElement FindElement(By by) |
| { |
| IWebElement wrappedElement; |
| try |
| { |
| GetShadowRootEventArgs e = new GetShadowRootEventArgs(this.parentDriver.WrappedDriver, this.WrappedSearchContext); |
| this.parentDriver.OnGettingShadowRoot(e); |
| IWebElement element = this.WrappedSearchContext.FindElement(by); |
| this.parentDriver.OnGetShadowRootCompleted(e); |
| wrappedElement = new EventFiringWebElement(this.parentDriver, element); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| |
| return wrappedElement; |
| } |
| |
| /// <summary> |
| /// Finds the elements on the page by using the <see cref="By"/> object and returns a ReadOnlyCollection of the Elements on the page |
| /// </summary> |
| /// <param name="by">By mechanism to find the element</param> |
| /// <returns>ReadOnlyCollection of IWebElement</returns> |
| public ReadOnlyCollection<IWebElement> FindElements(By by) |
| { |
| try |
| { |
| GetShadowRootEventArgs e = new GetShadowRootEventArgs(this.parentDriver.WrappedDriver, this.WrappedSearchContext); |
| this.parentDriver.OnGettingShadowRoot(e); |
| ReadOnlyCollection<IWebElement> elements = this.WrappedSearchContext.FindElements(by); |
| this.parentDriver.OnGetShadowRootCompleted(e); |
| |
| List<IWebElement> wrappedElementList = new List<IWebElement>(elements.Count); |
| foreach (IWebElement element in elements) |
| { |
| IWebElement wrappedElement = this.parentDriver.WrapElement(element); |
| wrappedElementList.Add(wrappedElement); |
| } |
| |
| return wrappedElementList.AsReadOnly(); |
| } |
| catch (Exception ex) |
| { |
| this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Determines whether the specified <see cref="object"/> is equal to the current <see cref="EventFiringShadowRoot"/>. |
| /// </summary> |
| /// <param name="obj">The <see cref="object"/> to compare to the current <see cref="EventFiringShadowRoot"/>.</param> |
| /// <returns><see langword="true"/> if the current <see cref="EventFiringShadowRoot"/> is equal to the other parameter; otherwise, <see langword="false"/>.</returns> |
| public override bool Equals(object? obj) |
| { |
| return Equals(obj as ISearchContext); |
| } |
| |
| /// <summary> |
| /// Determines whether the specified <see cref="EventFiringShadowRoot"/> is equal to the current <see cref="EventFiringShadowRoot"/>. |
| /// </summary> |
| /// <param name="other">The <see cref="ISearchContext"/> to compare to the current <see cref="EventFiringShadowRoot"/>.</param> |
| /// <returns><see langword="true"/> if the current <see cref="EventFiringShadowRoot"/> is equal to the <paramref name="other"/> parameter; otherwise, <see langword="false"/>.</returns> |
| public bool Equals(ISearchContext? other) |
| { |
| return WrappedSearchContext.Equals(other); |
| } |
| |
| /// <summary> |
| /// Return the hash code for this <see cref="EventFiringWebElement"/>. |
| /// </summary> |
| /// <returns>A 32-bit signed integer hash code.</returns> |
| public override int GetHashCode() |
| { |
| return this.WrappedSearchContext.GetHashCode(); |
| } |
| } |
| } |