Skip to content

Commit

Permalink
[dotnet] Handle returning shadow roots from JavaScript
Browse files Browse the repository at this point in the history
  • Loading branch information
jimevans committed Nov 19, 2021
1 parent 9cd6c45 commit 201fad3
Show file tree
Hide file tree
Showing 19 changed files with 277 additions and 157 deletions.
25 changes: 25 additions & 0 deletions common/src/web/shadowRootPage.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<title>Shadow Root Page</title>
</head>
<body>
<style>
custom-checkbox-element {
display:block; width:20px; height:20px;
}
</style>
<div><input id="noShadowRoot" /></div>
<div><custom-checkbox-element></custom-checkbox-element></div>
<script>
customElements.define('custom-checkbox-element',
class extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'}).innerHTML = '<div><input type="checkbox"/></div>';
}
}
);
</script>
</body>
</html>
4 changes: 2 additions & 2 deletions dotnet/src/webdriver/ElementCoordinates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public object AuxiliaryLocator
{
get
{
IWebElementReference elementReference = this.element as IWebElementReference;
IWebDriverObjectReference elementReference = this.element as IWebDriverObjectReference;
if (elementReference == null)
{
return null;
Expand All @@ -80,7 +80,7 @@ public object AuxiliaryLocator
// uses the raw ID of the element, not an element reference. To use this,
// extract the ID using the well-known key to the dictionary for element
// references.
return elementReference.ElementReferenceId;
return elementReference.ObjectReferenceId;
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions dotnet/src/webdriver/Interactions/PointerInputDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -521,13 +521,13 @@ public override string ToString()

private Dictionary<string, object> ConvertElement()
{
IWebElementReference elementReference = this.target as IWebElementReference;
IWebDriverObjectReference elementReference = this.target as IWebDriverObjectReference;
if (elementReference == null)
{
IWrapsElement elementWrapper = this.target as IWrapsElement;
if (elementWrapper != null)
{
elementReference = elementWrapper.WrappedElement as IWebElementReference;
elementReference = elementWrapper.WrappedElement as IWebDriverObjectReference;
}
}

Expand Down
4 changes: 2 additions & 2 deletions dotnet/src/webdriver/Interactions/WheelInputDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,13 @@ public override Dictionary<string, object> ToDictionary()

private Dictionary<string, object> ConvertElement()
{
IWebElementReference elementReference = this.target as IWebElementReference;
IWebDriverObjectReference elementReference = this.target as IWebDriverObjectReference;
if (elementReference == null)
{
IWrapsElement elementWrapper = this.target as IWrapsElement;
if (elementWrapper != null)
{
elementReference = elementWrapper.WrappedElement as IWebElementReference;
elementReference = elementWrapper.WrappedElement as IWebDriverObjectReference;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="IWebElementReference.cs" company="WebDriver Committers">
// <copyright file="IWebElementReference.cs" company="WebDriver 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
Expand Down Expand Up @@ -26,12 +26,12 @@ namespace OpenQA.Selenium.Internal
/// <summary>
/// Defines the interface through which the framework can serialize an element to the wire protocol.
/// </summary>
internal interface IWebElementReference
internal interface IWebDriverObjectReference
{
/// <summary>
/// Gets the internal ID of the element.
/// Gets the internal ID of the WebDriver object.
/// </summary>
string ElementReferenceId { get; }
string ObjectReferenceId { get; }

/// <summary>
/// Converts an object into an object that represents an element for the wire protocol.
Expand Down
33 changes: 32 additions & 1 deletion dotnet/src/webdriver/ShadowRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using OpenQA.Selenium.Internal;

namespace OpenQA.Selenium
{
/// <summary>
/// Provides a representation of an element's shadow root.
/// </summary>
public class ShadowRoot : ISearchContext, IWrapsDriver
public class ShadowRoot : ISearchContext, IWrapsDriver, IWebDriverObjectReference
{
/// <summary>
/// The property name that represents an element shadow root in the wire protocol.
Expand Down Expand Up @@ -54,6 +55,29 @@ public IWebDriver WrappedDriver
get { return this.driver; }
}

/// <summary>
/// Gets the internal ID for this ShadowRoot.
/// </summary>
string IWebDriverObjectReference.ObjectReferenceId
{
get { return this.shadowRootId; }
}

internal static bool ContainsShadowRootReference(Dictionary<string, object> shadowRootDictionary)
{
if (shadowRootDictionary == null)
{
throw new ArgumentNullException(nameof(shadowRootDictionary), "The dictionary containing the shadow root reference cannot be null");
}

return shadowRootDictionary.ContainsKey(ShadowRootReferencePropertyName);
}

internal static ShadowRoot FromDictionary(WebDriver driver, Dictionary<string, object> shadowRootDictionary)
{
return new ShadowRoot(driver, shadowRootDictionary[ShadowRoot.ShadowRootReferencePropertyName].ToString());
}

/// <summary>
/// Finds the first <see cref="IWebElement"/> using the given method.
/// </summary>
Expand Down Expand Up @@ -96,5 +120,12 @@ public ReadOnlyCollection<IWebElement> FindElements(By by)
Response commandResponse = this.driver.InternalExecute(DriverCommand.FindShadowChildElements, parameters);
return this.driver.GetElementsFromResponse(commandResponse);
}

Dictionary<string, object> IWebDriverObjectReference.ToDictionary()
{
Dictionary<string, object> shadowRootDictionary = new Dictionary<string, object>();
shadowRootDictionary.Add(ShadowRootReferencePropertyName, this.shadowRootId);
return shadowRootDictionary;
}
}
}
6 changes: 3 additions & 3 deletions dotnet/src/webdriver/TargetLocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,13 @@ public IWebDriver Frame(IWebElement frameElement)
throw new ArgumentNullException(nameof(frameElement), "Frame element cannot be null");
}

IWebElementReference elementReference = frameElement as IWebElementReference;
IWebDriverObjectReference elementReference = frameElement as IWebDriverObjectReference;
if (elementReference == null)
{
IWrapsElement elementWrapper = frameElement as IWrapsElement;
if (elementWrapper != null)
{
elementReference = elementWrapper.WrappedElement as IWebElementReference;
elementReference = elementWrapper.WrappedElement as IWebDriverObjectReference;
}
}

Expand All @@ -108,7 +108,7 @@ public IWebDriver Frame(IWebElement frameElement)

// TODO: Remove "ELEMENT" addition when all remote ends are spec-compliant.
Dictionary<string, object> elementDictionary = elementReference.ToDictionary();
elementDictionary.Add("ELEMENT", elementReference.ElementReferenceId);
elementDictionary.Add("ELEMENT", elementReference.ObjectReferenceId);

Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("id", elementDictionary);
Expand Down
16 changes: 10 additions & 6 deletions dotnet/src/webdriver/WebDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -940,13 +940,13 @@ protected object ExecuteScriptCommand(string script, string commandName, params
private static object ConvertObjectToJavaScriptObject(object arg)
{
IWrapsElement argAsWrapsElement = arg as IWrapsElement;
IWebElementReference argAsElementReference = arg as IWebElementReference;
IWebDriverObjectReference argAsObjectReference = arg as IWebDriverObjectReference;
IEnumerable argAsEnumerable = arg as IEnumerable;
IDictionary argAsDictionary = arg as IDictionary;

if (argAsElementReference == null && argAsWrapsElement != null)
if (argAsObjectReference == null && argAsWrapsElement != null)
{
argAsElementReference = argAsWrapsElement.WrappedElement as IWebElementReference;
argAsObjectReference = argAsWrapsElement.WrappedElement as IWebDriverObjectReference;
}

object converted = null;
Expand All @@ -955,11 +955,11 @@ private static object ConvertObjectToJavaScriptObject(object arg)
{
converted = arg;
}
else if (argAsElementReference != null)
else if (argAsObjectReference != null)
{
// TODO: Remove "ELEMENT" addition when all remote ends are spec-compliant.
Dictionary<string, object> elementDictionary = argAsElementReference.ToDictionary();
converted = elementDictionary;
Dictionary<string, object> webDriverObjectReferenceDictionary = argAsObjectReference.ToDictionary();
converted = webDriverObjectReferenceDictionary;
}
else if (argAsDictionary != null)
{
Expand Down Expand Up @@ -1026,6 +1026,10 @@ private object ParseJavaScriptReturnValue(object responseValue)
{
returnValue = this.elementFactory.CreateElement(resultAsDictionary);
}
else if (ShadowRoot.ContainsShadowRootReference(resultAsDictionary))
{
returnValue = ShadowRoot.FromDictionary(this, resultAsDictionary);
}
else
{
// Recurse through the dictionary, re-parsing each value.
Expand Down
11 changes: 6 additions & 5 deletions dotnet/src/webdriver/WebElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

namespace OpenQA.Selenium
{
public class WebElement : IWebElement, IFindsElement, IWrapsDriver, ILocatable, ITakesScreenshot, IWebElementReference
public class WebElement : IWebElement, IFindsElement, IWrapsDriver, ILocatable, ITakesScreenshot, IWebDriverObjectReference
{
/// <summary>
/// The property name that represents a web element in the wire protocol.
Expand Down Expand Up @@ -250,7 +250,7 @@ public virtual ICoordinates Coordinates
/// <summary>
/// Gets the internal ID of the element.
/// </summary>
string IWebElementReference.ElementReferenceId
string IWebDriverObjectReference.ObjectReferenceId
{
get { return this.elementId; }
}
Expand Down Expand Up @@ -695,7 +695,7 @@ public override bool Equals(object obj)
return false;
}

Dictionary<string, object> IWebElementReference.ToDictionary()
Dictionary<string, object> IWebDriverObjectReference.ToDictionary()
{
Dictionary<string, object> elementDictionary = new Dictionary<string, object>();
elementDictionary.Add(ElementReferencePropertyName, this.elementId);
Expand Down Expand Up @@ -753,9 +753,10 @@ private string UploadFile(string localFile)
throw new WebDriverException("Cannot upload " + localFile, e);
}
}
private IWebElementReference ToElementReference()

private IWebDriverObjectReference ToElementReference()
{
return this as IWebElementReference;
return this as IWebDriverObjectReference;
}
}
}
4 changes: 2 additions & 2 deletions dotnet/test/common/DevTools/DevToolsConsoleTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ public class DevToolsConsoleTest : DevToolsTestFixture
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")]
public async Task VerifyMessageAdded()
{
var domains = session.GetVersionSpecificDomains<V93.DevToolsSessionDomains>();
var domains = session.GetVersionSpecificDomains<V96.DevToolsSessionDomains>();
string consoleMessage = "Hello Selenium";

ManualResetEventSlim sync = new ManualResetEventSlim(false);
EventHandler<V93.Console.MessageAddedEventArgs> messageAddedHandler = (sender, e) =>
EventHandler<V96.Console.MessageAddedEventArgs> messageAddedHandler = (sender, e) =>
{
Assert.That(e.Message.Text, Is.EqualTo(consoleMessage));
sync.Set();
Expand Down
6 changes: 3 additions & 3 deletions dotnet/test/common/DevTools/DevToolsLogTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ public class DevToolsLogTest : DevToolsTestFixture
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")]
public async Task VerifyEntryAddedAndClearLog()
{
var domains = session.GetVersionSpecificDomains<V93.DevToolsSessionDomains>();
var domains = session.GetVersionSpecificDomains<V96.DevToolsSessionDomains>();
ManualResetEventSlim sync = new ManualResetEventSlim(false);
EventHandler<V93.Log.EntryAddedEventArgs> entryAddedHandler = (sender, e) =>
EventHandler<V96.Log.EntryAddedEventArgs> entryAddedHandler = (sender, e) =>
{
Assert.That(e.Entry.Text.Contains("404"));
Assert.That(e.Entry.Level == V93.Log.LogEntryLevelValues.Error);
Assert.That(e.Entry.Level == V96.Log.LogEntryLevelValues.Error);
sync.Set();
};

Expand Down
Loading

0 comments on commit 201fad3

Please sign in to comment.