();
+
+ if(browseTheWeb.WebDriver.HasQuirk(BrowserQuirks.CannotGetShadowRoot))
+ throw new NotSupportedException("The current web browser is not capable of getting Shadow Roots via any known technique");
+
+ if(browseTheWeb.WebDriver.HasQuirk(BrowserQuirks.NeedsJavaScriptToGetShadowRoot))
+ return actor.PerformAsync(GetTheShadowRootWithJavaScriptFrom(element), cancellationToken);
+
+ return actor.PerformAsync(GetTheShadowRootNativelyFrom(element), cancellationToken);
+ }
+
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// The Shadow Host element
+ /// If is
+ public GetShadowRoot(ITarget element)
+ {
+ this.element = element ?? throw new ArgumentNullException(nameof(element));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Selenium.TestWebapp/Controllers/DelayedOpeningController.cs b/Tests/CSF.Screenplay.Selenium.TestWebapp/Controllers/DelayedOpeningController.cs
index f877f04c..f62e0a09 100644
--- a/Tests/CSF.Screenplay.Selenium.TestWebapp/Controllers/DelayedOpeningController.cs
+++ b/Tests/CSF.Screenplay.Selenium.TestWebapp/Controllers/DelayedOpeningController.cs
@@ -2,6 +2,8 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
+namespace CSF.Screenplay.Selenium.TestWebApp;
+
public class DelayedOpeningController : Controller
{
[HttpGet, Route("DelayedOpening")]
diff --git a/Tests/CSF.Screenplay.Selenium.TestWebapp/wwwroot/GetShadowRoot.html b/Tests/CSF.Screenplay.Selenium.TestWebapp/wwwroot/GetShadowRoot.html
new file mode 100644
index 00000000..adfe6594
--- /dev/null
+++ b/Tests/CSF.Screenplay.Selenium.TestWebapp/wwwroot/GetShadowRoot.html
@@ -0,0 +1,23 @@
+
+
+ Selenium Shadow Root tests
+
+
+ Selenium Shadow Root tests
+
+ This page includes a Shadow DOM,
+ which means that its elements cannot be selected by Selenium natively.
+
+
+
+
+
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Selenium.Tests/Elements/ShadowRootAdapterTests.cs b/Tests/CSF.Screenplay.Selenium.Tests/Elements/ShadowRootAdapterTests.cs
new file mode 100644
index 00000000..921a29f2
--- /dev/null
+++ b/Tests/CSF.Screenplay.Selenium.Tests/Elements/ShadowRootAdapterTests.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Collections.ObjectModel;
+using Moq;
+using OpenQA.Selenium;
+
+namespace CSF.Screenplay.Selenium.Elements;
+
+[TestFixture, Parallelizable]
+public class ShadowRootAdapterTests
+{
+ [Test, AutoMoqData]
+ public void FindElementShouldExerciseWrappedImpl([Frozen] ISearchContext wrapped, ShadowRootAdapter sut)
+ {
+ var by = By.Id("foo");
+ sut.FindElement(by);
+ Mock.Get(wrapped).Verify(x => x.FindElement(by));
+ }
+
+ [Test, AutoMoqData]
+ public void FindElementsShouldExerciseWrappedImpl([Frozen] ISearchContext wrapped, ShadowRootAdapter sut)
+ {
+ var by = By.Id("foo");
+ Mock.Get(wrapped).Setup(x => x.FindElements(by)).Returns(new ReadOnlyCollection([]));
+ sut.FindElements(by);
+ Mock.Get(wrapped).Verify(x => x.FindElements(by));
+ }
+
+ [Test, AutoMoqData]
+ public void TagNameShouldReturnHardcodedResult(ShadowRootAdapter sut)
+ {
+ Assert.That(sut.TagName, Is.EqualTo("#shadow-root"));
+ }
+
+ [Test, AutoMoqData]
+ public void TextShouldThrow(ShadowRootAdapter sut)
+ {
+ Assert.That(() => sut.Text, Throws.InstanceOf());
+ }
+
+
+ [Test, AutoMoqData]
+ public void EnabledShouldThrow(ShadowRootAdapter sut)
+ {
+ Assert.That(() => sut.Enabled, Throws.InstanceOf());
+ }
+
+ [Test, AutoMoqData]
+ public void SelectedShouldThrow(ShadowRootAdapter sut)
+ {
+ Assert.That(() => sut.Selected, Throws.InstanceOf());
+ }
+
+ [Test, AutoMoqData]
+ public void LocationShouldThrow(ShadowRootAdapter sut)
+ {
+ Assert.That(() => sut.Location, Throws.InstanceOf());
+ }
+
+ [Test, AutoMoqData]
+ public void SizeShouldThrow(ShadowRootAdapter sut)
+ {
+ Assert.That(() => sut.Size, Throws.InstanceOf());
+ }
+
+ [Test, AutoMoqData]
+ public void DisplayedShouldThrow(ShadowRootAdapter sut)
+ {
+ Assert.That(() => sut.Displayed, Throws.InstanceOf());
+ }
+
+ [Test, AutoMoqData]
+ public void ClearShouldThrow(ShadowRootAdapter sut)
+ {
+ Assert.That(sut.Clear, Throws.InstanceOf());
+ }
+
+ [Test, AutoMoqData]
+ public void ClickShouldThrow(ShadowRootAdapter sut)
+ {
+ Assert.That(sut.Click, Throws.InstanceOf());
+ }
+
+ [Test, AutoMoqData]
+ public void GetAttributeShouldThrow(ShadowRootAdapter sut, string attributeName)
+ {
+ Assert.That(() => sut.GetAttribute(attributeName), Throws.InstanceOf());
+ }
+
+ [Test, AutoMoqData]
+ public void GetCssValueShouldThrow(ShadowRootAdapter sut, string propertyName)
+ {
+ Assert.That(() => sut.GetCssValue(propertyName), Throws.InstanceOf());
+ }
+
+ [Test, AutoMoqData]
+ public void GetDomAttributeShouldThrow(ShadowRootAdapter sut, string attributeName)
+ {
+ Assert.That(() => sut.GetDomAttribute(attributeName), Throws.InstanceOf());
+ }
+
+ [Test, AutoMoqData]
+ public void GetDomPropertyShouldThrow(ShadowRootAdapter sut, string propertyName)
+ {
+ Assert.That(() => sut.GetDomProperty(propertyName), Throws.InstanceOf());
+ }
+
+ [Test, AutoMoqData]
+ public void GetPropertyShouldThrow(ShadowRootAdapter sut, string propertyName)
+ {
+ Assert.That(() => sut.GetProperty(propertyName), Throws.InstanceOf());
+ }
+
+ [Test, AutoMoqData]
+ public void GetShadowRootShouldThrow(ShadowRootAdapter sut)
+ {
+ Assert.That(sut.GetShadowRoot, Throws.InstanceOf());
+ }
+
+ [Test, AutoMoqData]
+ public void SendKeysShouldThrow(ShadowRootAdapter sut, string text)
+ {
+ Assert.That(() => sut.SendKeys(text), Throws.InstanceOf());
+ }
+
+ [Test, AutoMoqData]
+ public void SubmitShouldThrow(ShadowRootAdapter sut)
+ {
+ Assert.That(sut.Submit, Throws.InstanceOf());
+ }
+}
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Selenium.Tests/Questions/GetShadowRootNativelyTests.cs b/Tests/CSF.Screenplay.Selenium.Tests/Questions/GetShadowRootNativelyTests.cs
new file mode 100644
index 00000000..a140cd1f
--- /dev/null
+++ b/Tests/CSF.Screenplay.Selenium.Tests/Questions/GetShadowRootNativelyTests.cs
@@ -0,0 +1,34 @@
+using CSF.Screenplay.Selenium.Elements;
+using OpenQA.Selenium;
+using static CSF.Screenplay.PerformanceStarter;
+using static CSF.Screenplay.Selenium.PerformableBuilder;
+
+namespace CSF.Screenplay.Selenium.Questions;
+
+[TestFixture, Parallelizable]
+public class GetShadowRootNativelyTests
+{
+ static readonly Locator
+ host = new ElementId("shadowHost", "The shadow host"),
+ content = new CssSelector("p.content", "The content inside the Shadow DOM");
+
+ static readonly NamedUri testPage = new NamedUri("GetShadowRoot.html", "the test page");
+
+ [Test, Screenplay]
+ public async Task GetShadowRootNativelyShouldResultInBeingAbleToReadTheShadowDomContent(IStage stage)
+ {
+ var webster = stage.Spotlight();
+ var browseTheWeb = webster.GetAbility();
+
+ if (browseTheWeb.WebDriver.HasQuirk(BrowserQuirks.CannotGetShadowRoot)
+ || browseTheWeb.WebDriver.HasQuirk(BrowserQuirks.NeedsJavaScriptToGetShadowRoot))
+ Assert.Pass("This test cannot be run on the current web browser");
+
+ await Given(webster).WasAbleTo(OpenTheUrl(testPage));
+ var shadowRoot = await When(webster).AttemptsTo(GetTheShadowRootNativelyFrom(host));
+ var shadowContent = await Then(webster).Should(FindAnElementWithin(shadowRoot).WhichMatches(content));
+ var text = await Then(webster).Should(ReadFromTheElement(shadowContent).TheText());
+
+ Assert.That(text, Is.EqualTo("I am an element inside the Shadow DOM"));
+ }
+}
diff --git a/Tests/CSF.Screenplay.Selenium.Tests/Questions/GetShadowRootWithJavaScriptTests.cs b/Tests/CSF.Screenplay.Selenium.Tests/Questions/GetShadowRootWithJavaScriptTests.cs
new file mode 100644
index 00000000..14b10026
--- /dev/null
+++ b/Tests/CSF.Screenplay.Selenium.Tests/Questions/GetShadowRootWithJavaScriptTests.cs
@@ -0,0 +1,33 @@
+using CSF.Screenplay.Selenium.Elements;
+using OpenQA.Selenium;
+using static CSF.Screenplay.PerformanceStarter;
+using static CSF.Screenplay.Selenium.PerformableBuilder;
+
+namespace CSF.Screenplay.Selenium.Questions;
+
+[TestFixture, Parallelizable]
+public class GetShadowRootWithJavaScriptTests
+{
+ static readonly Locator
+ host = new ElementId("shadowHost", "The shadow host"),
+ content = new CssSelector("p.content", "The content inside the Shadow DOM");
+
+ static readonly NamedUri testPage = new NamedUri("GetShadowRoot.html", "the test page");
+
+ [Test, Screenplay]
+ public async Task GetShadowRootWithJavaScriptShouldResultInBeingAbleToReadTheShadowDomContent(IStage stage)
+ {
+ var webster = stage.Spotlight();
+ var browseTheWeb = webster.GetAbility();
+
+ if (browseTheWeb.WebDriver.HasQuirk(BrowserQuirks.CannotGetShadowRoot))
+ Assert.Pass("This test cannot be run on the current web browser");
+
+ await Given(webster).WasAbleTo(OpenTheUrl(testPage));
+ var shadowRoot = await When(webster).AttemptsTo(GetTheShadowRootWithJavaScriptFrom(host));
+ var shadowContent = await Then(webster).Should(FindAnElementWithin(shadowRoot).WhichMatches(content));
+ var text = await Then(webster).Should(ReadFromTheElement(shadowContent).TheText());
+
+ Assert.That(text, Is.EqualTo("I am an element inside the Shadow DOM"));
+ }
+}
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Selenium.Tests/Tasks/GetShadowRootNativelyTests.cs b/Tests/CSF.Screenplay.Selenium.Tests/Tasks/GetShadowRootNativelyTests.cs
new file mode 100644
index 00000000..d5a216d9
--- /dev/null
+++ b/Tests/CSF.Screenplay.Selenium.Tests/Tasks/GetShadowRootNativelyTests.cs
@@ -0,0 +1,33 @@
+using CSF.Screenplay.Selenium.Elements;
+using OpenQA.Selenium;
+using static CSF.Screenplay.PerformanceStarter;
+using static CSF.Screenplay.Selenium.PerformableBuilder;
+
+namespace CSF.Screenplay.Selenium.Tasks;
+
+[TestFixture, Parallelizable]
+public class GetShadowRootTests
+{
+ static readonly Locator
+ host = new ElementId("shadowHost", "The shadow host"),
+ content = new CssSelector("p.content", "The content inside the Shadow DOM");
+
+ static readonly NamedUri testPage = new NamedUri("GetShadowRoot.html", "the test page");
+
+ [Test, Screenplay]
+ public async Task GetShadowRootShouldResultInBeingAbleToReadTheShadowDomContent(IStage stage)
+ {
+ var webster = stage.Spotlight();
+ var browseTheWeb = webster.GetAbility();
+
+ if (browseTheWeb.WebDriver.HasQuirk(BrowserQuirks.CannotGetShadowRoot))
+ Assert.Pass("This test cannot be run on the current web browser");
+
+ await Given(webster).WasAbleTo(OpenTheUrl(testPage));
+ var shadowRoot = await When(webster).AttemptsTo(GetTheShadowRootFrom(host));
+ var shadowContent = await Then(webster).Should(FindAnElementWithin(shadowRoot).WhichMatches(content));
+ var text = await Then(webster).Should(ReadFromTheElement(shadowContent).TheText());
+
+ Assert.That(text, Is.EqualTo("I am an element inside the Shadow DOM"));
+ }
+}