diff --git a/dotnet/src/support/Events/EventFiringWebDriver.cs b/dotnet/src/support/Events/EventFiringWebDriver.cs index 05b5b3dcdd22d..80ca5b925e5a1 100644 --- a/dotnet/src/support/Events/EventFiringWebDriver.cs +++ b/dotnet/src/support/Events/EventFiringWebDriver.cs @@ -20,7 +20,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; -using OpenQA.Selenium.Internal; namespace OpenQA.Selenium.Support.Events { @@ -101,6 +100,16 @@ public EventFiringWebDriver(IWebDriver parentDriver) /// public event EventHandler FindElementCompleted; + /// + /// Fires before the driver starts to get a shadow root. + /// + public event EventHandler GettingShadowRoot; + + /// + /// Fires after the driver completes getting a shadow root. + /// + public event EventHandler GetShadowRootCompleted; + /// /// Fires before a script is executed. /// @@ -728,6 +737,30 @@ protected virtual void OnFindElementCompleted(FindElementEventArgs e) } } + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnGettingShadowRoot(GetShadowRootEventArgs e) + { + if (this.GettingShadowRoot != null) + { + this.GettingShadowRoot(this, e); + } + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnGetShadowRootCompleted(GetShadowRootEventArgs e) + { + if (this.GetShadowRootCompleted != null) + { + this.GetShadowRootCompleted(this, e); + } + } + /// /// Raises the event. /// @@ -1613,7 +1646,11 @@ public ISearchContext GetShadowRoot() ISearchContext shadowRoot = null; try { + GetShadowRootEventArgs e = new GetShadowRootEventArgs(this.parentDriver.WrappedDriver, this.underlyingElement); + this.parentDriver.OnGettingShadowRoot(e); shadowRoot = this.underlyingElement.GetShadowRoot(); + this.parentDriver.OnGetShadowRootCompleted(e); + shadowRoot = new EventFiringShadowRoot(this.parentDriver, shadowRoot); } catch (Exception ex) { @@ -1726,5 +1763,121 @@ public override int GetHashCode() return this.underlyingElement.GetHashCode(); } } + + /// + /// EventFiringShadowElement allows you to have access to specific shadow elements + /// + private class EventFiringShadowRoot : ISearchContext, IWrapsDriver + { + private ISearchContext underlyingSearchContext; + private EventFiringWebDriver parentDriver; + + /// + /// Initializes a new instance of the class. + /// + /// The instance hosting this element. + /// The to wrap for event firing. + public EventFiringShadowRoot(EventFiringWebDriver driver, ISearchContext searchContext) + { + this.underlyingSearchContext = searchContext; + this.parentDriver = driver; + } + + /// + /// Gets the underlying wrapped . + /// + public ISearchContext WrappedSearchContext + { + get { return this.underlyingSearchContext; } + } + + /// + /// Gets the underlying parent wrapped + /// + public IWebDriver WrappedDriver + { + get { return this.parentDriver; } + } + + /// + /// Finds the first element in the page that matches the object + /// + /// By mechanism to find the element + /// IWebElement object so that you can interaction that object + public IWebElement FindElement(By by) + { + IWebElement wrappedElement = null; + try + { + GetShadowRootEventArgs e = new GetShadowRootEventArgs(this.parentDriver.WrappedDriver, this.underlyingSearchContext); + this.parentDriver.OnGettingShadowRoot(e); + IWebElement element = this.underlyingSearchContext.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; + } + + /// + /// Finds the elements on the page by using the object and returns a ReadOnlyCollection of the Elements on the page + /// + /// By mechanism to find the element + /// ReadOnlyCollection of IWebElement + public ReadOnlyCollection FindElements(By by) + { + List wrappedElementList = new List(); + try + { + GetShadowRootEventArgs e = new GetShadowRootEventArgs(this.parentDriver.WrappedDriver, this.underlyingSearchContext); + this.parentDriver.OnGettingShadowRoot(e); + ReadOnlyCollection elements = this.underlyingSearchContext.FindElements(by); + this.parentDriver.OnGetShadowRootCompleted(e); + foreach (IWebElement element in elements) + { + IWebElement wrappedElement = this.parentDriver.WrapElement(element); + wrappedElementList.Add(wrappedElement); + } + } + catch (Exception ex) + { + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; + } + + return wrappedElementList.AsReadOnly(); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare to the current . + /// if the specified is equal to the current ; otherwise, . + public override bool Equals(object obj) + { + ISearchContext other = obj as ISearchContext; + + if (other == null) + { + return false; + } + + return underlyingSearchContext.Equals(other); + } + + /// + /// Return the hash code for this . + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return this.underlyingSearchContext.GetHashCode(); + } + } } } diff --git a/dotnet/src/support/Events/GetShadowRootEventArgs.cs b/dotnet/src/support/Events/GetShadowRootEventArgs.cs new file mode 100644 index 0000000000000..5601a0becef65 --- /dev/null +++ b/dotnet/src/support/Events/GetShadowRootEventArgs.cs @@ -0,0 +1,58 @@ +// +// 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. +// + +using System; + +namespace OpenQA.Selenium.Support.Events +{ + /// + /// Provides data for events related to getting shadow root of the web element. + /// + public class GetShadowRootEventArgs : EventArgs + { + private IWebDriver driver; + private ISearchContext searchContext; + + /// + /// Initializes a new instance of the class. + /// + /// The WebDriver instance used in the current context. + /// The parent searc context used as the context for getting shadow root. + public GetShadowRootEventArgs(IWebDriver driver, ISearchContext searchContext) + { + this.driver = driver; + this.searchContext = searchContext; + } + + /// + /// Gets the WebDriver instance used in the current context. + /// + public IWebDriver Driver + { + get { return this.driver; } + } + + /// + /// Gets the parent search context used as the context for getting shadow root. + /// + public ISearchContext SearchContext + { + get { return this.searchContext; } + } + } +} diff --git a/dotnet/test/support/Events/EventFiringWebDriverTest.cs b/dotnet/test/support/Events/EventFiringWebDriverTest.cs index b9f6bbbefe865..f9b14a19813d6 100644 --- a/dotnet/test/support/Events/EventFiringWebDriverTest.cs +++ b/dotnet/test/support/Events/EventFiringWebDriverTest.cs @@ -13,6 +13,7 @@ public class EventFiringWebDriverTest { private Mock mockDriver; private Mock mockElement; + private Mock mockShadowRoot; private Mock mockNavigation; private IWebDriver stubDriver; private StringBuilder log; @@ -22,6 +23,7 @@ public void Setup() { mockDriver = new Mock(); mockElement = new Mock(); + mockShadowRoot = new Mock(); mockNavigation = new Mock(); log = new StringBuilder(); } @@ -214,6 +216,59 @@ public void ShouldBeAbleToAccessWrappedInstanceFromEventCalls() testDriver.Url = "http://example.org"; } + [Test] + public void ShouldFireGetShadowRootEvents() + { + mockDriver.Setup(d => d.FindElement(It.IsAny())).Returns(mockElement.Object); + EventFiringWebDriver testDriver = new EventFiringWebDriver(mockDriver.Object); + + GetShadowRootEventArgs gettingShadowRootArgs = null; + GetShadowRootEventArgs getShadowRootCompletedArgs = null; + testDriver.GettingShadowRoot += (d, e) => gettingShadowRootArgs = e; + testDriver.GetShadowRootCompleted += (d, e) => getShadowRootCompletedArgs = e; + + var abcElement = testDriver.FindElement(By.CssSelector(".abc")); + + // act + abcElement.GetShadowRoot(); + + Assert.IsNotNull(gettingShadowRootArgs); + Assert.AreEqual(mockDriver.Object, gettingShadowRootArgs.Driver); + Assert.AreEqual(mockElement.Object, gettingShadowRootArgs.SearchContext); + + Assert.IsNotNull(getShadowRootCompletedArgs); + Assert.AreEqual(mockDriver.Object, getShadowRootCompletedArgs.Driver); + Assert.AreEqual(mockElement.Object, getShadowRootCompletedArgs.SearchContext); + } + + [Test] + public void ShouldFireFindEventsInShadowRoot() + { + mockElement.Setup(e => e.GetShadowRoot()).Returns(mockShadowRoot.Object); + mockElement.Setup(e => e.FindElement(It.IsAny())).Returns(mockElement.Object); + mockDriver.Setup(d => d.FindElement(It.IsAny())).Returns(mockElement.Object); + EventFiringWebDriver testDriver = new EventFiringWebDriver(mockDriver.Object); + + FindElementEventArgs findingElementArgs = null; + FindElementEventArgs findElementCompletedArgs = null; + testDriver.FindingElement += (d, e) => findingElementArgs = e; + testDriver.FindElementCompleted += (d, e) => findElementCompletedArgs = e; + + var abcElement = testDriver.FindElement(By.CssSelector(".abc")); + var shadowRoot = abcElement.GetShadowRoot(); + + // act + var element = shadowRoot.FindElement(By.CssSelector(".abc")); + + Assert.IsNotNull(findingElementArgs); + Assert.AreEqual(mockDriver.Object, findingElementArgs.Driver); + Assert.AreEqual(null, findingElementArgs.Element); + + Assert.IsNotNull(findElementCompletedArgs); + Assert.AreEqual(mockDriver.Object, findElementCompletedArgs.Driver); + Assert.AreEqual(null, findElementCompletedArgs.Element); + } + void testDriver_Navigating(object sender, WebDriverNavigationEventArgs e) { Assert.AreEqual(e.Driver, stubDriver);