diff --git a/docs/demo/shadondom.html b/docs/demo/shadondom.html new file mode 100644 index 0000000..ca4a6da --- /dev/null +++ b/docs/demo/shadondom.html @@ -0,0 +1,34 @@ + + + + + + + +
This is styled by the main document
+
+ + + \ No newline at end of file diff --git a/docs/demo/shadowdom.html b/docs/demo/shadowdom.html new file mode 100644 index 0000000..3d89c57 --- /dev/null +++ b/docs/demo/shadowdom.html @@ -0,0 +1,80 @@ + + + + + + + +

Shadow Dom Test page

+
This is a div called shadow-box but not in Shadow Dom
+ +

Shadow Dom host 1:

+
+ +

Shadow Dom host 2:

+
+ + + +

We have also shadowdom in an iFrame

+ + + \ No newline at end of file diff --git a/docs/demo/shadowdom2.html b/docs/demo/shadowdom2.html new file mode 100644 index 0000000..28d66d4 --- /dev/null +++ b/docs/demo/shadowdom2.html @@ -0,0 +1,78 @@ + + + + + + + +

Shadow Dom in iFrame

+
This is a div in iFrame
+ +

Shadow Dom in Frame:

+
+ +

Shadow Dom in Frame 22:

+
+ + + + + + \ No newline at end of file diff --git a/docs/manifest.json b/docs/manifest.json index b9b503f..98dc9b5 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -4781,6 +4781,26 @@ }, "version": "" }, + { + "type": "Resource", + "source_relative_path": "demo/shadowdom.html", + "output": { + "resource": { + "relative_path": "demo/shadowdom.html" + } + }, + "version": "" + }, + { + "type": "Resource", + "source_relative_path": "demo/shadowdom2.html", + "output": { + "resource": { + "relative_path": "demo/shadowdom2.html" + } + }, + "version": "" + }, { "type": "Resource", "source_relative_path": "files/Data-HomeInsurance.xlsx", diff --git a/src/AxaFrance.WebEngine.Doc/demo/shadowdom.html b/src/AxaFrance.WebEngine.Doc/demo/shadowdom.html new file mode 100644 index 0000000..3d89c57 --- /dev/null +++ b/src/AxaFrance.WebEngine.Doc/demo/shadowdom.html @@ -0,0 +1,80 @@ + + + + + + + +

Shadow Dom Test page

+
This is a div called shadow-box but not in Shadow Dom
+ +

Shadow Dom host 1:

+
+ +

Shadow Dom host 2:

+
+ + + +

We have also shadowdom in an iFrame

+ + + \ No newline at end of file diff --git a/src/AxaFrance.WebEngine.Doc/demo/shadowdom2.html b/src/AxaFrance.WebEngine.Doc/demo/shadowdom2.html new file mode 100644 index 0000000..28d66d4 --- /dev/null +++ b/src/AxaFrance.WebEngine.Doc/demo/shadowdom2.html @@ -0,0 +1,78 @@ + + + + + + + +

Shadow Dom in iFrame

+
This is a div in iFrame
+ +

Shadow Dom in Frame:

+
+ +

Shadow Dom in Frame 22:

+
+ + + + + + \ No newline at end of file diff --git a/src/AxaFrance.WebEngine.Web/WebElementDescription.cs b/src/AxaFrance.WebEngine.Web/WebElementDescription.cs index 8a29b2e..1d2ea1a 100644 --- a/src/AxaFrance.WebEngine.Web/WebElementDescription.cs +++ b/src/AxaFrance.WebEngine.Web/WebElementDescription.cs @@ -19,6 +19,8 @@ namespace AxaFrance.WebEngine.Web /// public class WebElementDescription : ElementDescription { + private bool isInFrame; + /// /// Initialize the Element. If the element is not created within a , you should use before us the element. /// @@ -170,14 +172,32 @@ protected override IWebElement InternalFindElement() protected override IReadOnlyCollection InternalFindElements() { IEnumerable elements = null; - if (this.Id != null) + ISearchContext context = driver; + if (this.ShadowRoot != null) + { + ShadowRoot.UseDriver(driver); + var root = ShadowRoot.InternalFindElements(); + if (root.Count > 1) + { + throw new InvalidSelectorException("Multiple element has found with the given selection criteria for ShadowRoot"); + } + else if (root.Count == 0) + { + throw new NoSuchElementException("No such Shadow Root found:" + ShadowRoot.ToString()); + } + else + { + context = root.First().GetShadowRoot(); + } + } + if (this.Id != null) { - elements = driver.FindElements(By.Id(this.Id)); + elements = context.FindElements(By.Id(this.Id)); } if (this.Name != null) { - var names = driver.FindElements(By.Name(this.Name)); + var names = context.FindElements(By.Name(this.Name)); if (elements == null) { elements = names; @@ -191,7 +211,7 @@ protected override IReadOnlyCollection InternalFindElements() if (this.ClassName != null) { string xpath = $"//*[@class='{ClassName}']"; - var cssnames = driver.FindElements(By.XPath(xpath)); + var cssnames = context.FindElements(By.XPath(xpath)); if (elements == null) { elements = cssnames; @@ -204,7 +224,7 @@ protected override IReadOnlyCollection InternalFindElements() if (this.LinkText != null) { - var links = driver.FindElements(By.LinkText(this.LinkText)); + var links = context.FindElements(By.LinkText(this.LinkText)); if (elements == null) { elements = links; @@ -217,7 +237,7 @@ protected override IReadOnlyCollection InternalFindElements() if (this.TagName != null) { - var tagNames = driver.FindElements(By.TagName(TagName.ToUpper())); + var tagNames = context.FindElements(By.TagName(TagName.ToUpper())); if (elements == null) { elements = tagNames; @@ -230,7 +250,7 @@ protected override IReadOnlyCollection InternalFindElements() if (this.CssSelector != null) { - var classes = driver.FindElements(By.CssSelector(CssSelector)); + var classes = context.FindElements(By.CssSelector(CssSelector)); if (elements == null) { elements = classes; @@ -244,7 +264,7 @@ protected override IReadOnlyCollection InternalFindElements() if (this.XPath != null) { - var xpaths = driver.FindElements(By.XPath(this.XPath)); + var xpaths = context.FindElements(By.XPath(this.XPath)); if (elements == null) { elements = xpaths; @@ -263,7 +283,7 @@ protected override IReadOnlyCollection InternalFindElements() } else { - elements = driver.FindElements(By.XPath($"//*[text()='{InnerText}']")); + elements = context.FindElements(By.XPath($"//*[text()='{InnerText}']")); } } @@ -275,7 +295,7 @@ protected override IReadOnlyCollection InternalFindElements() attributes.Add($"[{a.Name}=\"{a.Value}\"]"); } string cssSelector = $"{string.Join("", attributes)}"; - var attr = driver.FindElements(By.CssSelector(cssSelector)); + var attr = context.FindElements(By.CssSelector(cssSelector)); if (elements == null) { elements = attr; @@ -293,7 +313,6 @@ protected override IReadOnlyCollection InternalFindElements() else { return new ReadOnlyCollection(elements.ToList()); - } } @@ -367,6 +386,15 @@ public bool IsVisibleInViewPort } } + /// + /// Describes the ShadowRoot if the element is placed in a shadow DOM. + /// The description of the current element wil be based in the scope of the ShadowRoot. + /// + /// + /// Warning: When searching an element in the ShadowRoot: You can only use CssSelctor to describe the element. + /// + public WebElementDescription ShadowRoot { get; set; } + private bool InternalInViewPort() { //https://stackoverflow.com/questions/45243992/verification-of-element-in-viewport-in-selenium/45244889#45244889 diff --git a/src/WebEngine.Test/UnitTests/FramesShadowDoms.cs b/src/WebEngine.Test/UnitTests/FramesShadowDoms.cs new file mode 100644 index 0000000..053a2e1 --- /dev/null +++ b/src/WebEngine.Test/UnitTests/FramesShadowDoms.cs @@ -0,0 +1,128 @@ +using AxaFrance.WebEngine; +using AxaFrance.WebEngine.Web; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WebEngine.Test.UnitTests +{ + [TestClass] + public class FramesShadowDoms + { + static WebDriver driver = null; + + [ClassCleanup] + public static void Cleanup() + { + try + { + driver?.Quit(); + } + catch { } + try + { + driver?.Close(); + } + catch { } + try + { + driver?.Dispose(); + } + catch { } + } + + [ClassInitialize] + public static void Initialize(TestContext context) + { + if (driver == null) + { + driver = BrowserFactory.GetDriver(AxaFrance.WebEngine.Platform.Windows, BrowserType.ChromiumEdge); + } + + driver.Navigate().GoToUrl("https://axafrance.github.io/webengine-dotnet/demo/shadowdom.html"); + } + + [TestMethod] + public void ShadowDom() + { + WebElementDescription wed = new WebElementDescription(driver) + { + CssSelector = ".shadow-box", + ShadowRoot = new WebElementDescription() + { + Id = "host" + } + }; + var element = wed.FindElement(); + var text = element.GetDomProperty("innerText"); + Assert.AreEqual("Hello, Shadow DOM!", text); + } + + [TestMethod] + public void MultipleShadowDom() + { + WebElementDescription wed = new WebElementDescription(driver) + { + CssSelector = ".shadow-box", + ShadowRoot = new WebElementDescription() + { + Id = "host2" + } + }; + var element = wed.FindElement(); + var text = element.GetDomProperty("innerText"); + Assert.AreEqual("Hello, Shadow DOM in the second div!", text); + } + + [TestMethod] + public void NestedShadowDom() + { + WebElementDescription wed = new WebElementDescription(driver) + { + CssSelector = ".shadow-box", + ShadowRoot = new WebElementDescription() + { + Id = "host3", + ShadowRoot = new WebElementDescription() + { + Id = "host2", + } + } + }; + var element = wed.FindElement(); + var text = element.GetDomProperty("innerText"); + Assert.AreEqual("Hello, Shadow DOM in a Shadow DOM!", text); + } + + [TestMethod] + public void ShadowDomInFrame() + { + var Frame = new WebElementDescription(driver) + { + Id = "Frame1" + }; + + WebElementDescription wed = new WebElementDescription(driver) + { + CssSelector = ".shadow-box", + ShadowRoot = new WebElementDescription() + { + Id = "host" + } + }; + + //goto Frame1 + driver.SwitchTo().Frame(Frame.FindElement()); + var element = wed.FindElement(); + var text = element.GetDomProperty("innerText"); + driver.SwitchTo().DefaultContent(); + Assert.AreEqual("Hello, Shadow DOM in Frame!", text); + + + } + } +} diff --git a/src/WebEngine.Test/UnitTests/WebTest.cs b/src/WebEngine.Test/UnitTests/WebTest.cs index 7a42afc..a85c21b 100644 --- a/src/WebEngine.Test/UnitTests/WebTest.cs +++ b/src/WebEngine.Test/UnitTests/WebTest.cs @@ -46,6 +46,7 @@ public static void Initialize(TestContext context) driver.Navigate().GoToUrl("https://axafrance.github.io/webengine-dotnet/demo/Test.html"); } + [TestMethod] public void ElementTypeing() {