Skip to content

Commit 997503c

Browse files
committed
Refactoring creation of RemoteWebElement to use a factory
Previously, the RemoteWebDriver class had a protected method called CreateElement that took the element ID as a parameter and returned a RemoteWebElement object. That approach is not flexible enough for some downstream projects that implement their own subclasses of RemoteWebDriver and RemoteWebElement. To rectify that, this commit replaces the calling of CreateElement with the use of a RemoteWebElementFactory class that handles the creation of RemoteWebElements. Note carefully that the CreateElement method still exists, so as to prevent compile-time errors for downstream consumers, that method is no longer called. If you are a user who has overridden that method as part of your project, you'll need to create an implementation of RemoteWebElementFactory that creates your custom subclass of RemoteWebElement.
1 parent 7fa5135 commit 997503c

File tree

3 files changed

+118
-37
lines changed

3 files changed

+118
-37
lines changed

dotnet/src/webdriver/Firefox/FirefoxDriver.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -252,16 +252,6 @@ protected virtual void PrepareEnvironment()
252252
// Does nothing, but provides a hook for subclasses to do "stuff"
253253
}
254254

255-
/// <summary>
256-
/// Creates a <see cref="RemoteWebElement"/> with the specified ID.
257-
/// </summary>
258-
/// <param name="elementId">The ID of this element.</param>
259-
/// <returns>A <see cref="RemoteWebElement"/> with the specified ID. For the FirefoxDriver this will be a <see cref="FirefoxWebElement"/>.</returns>
260-
protected override RemoteWebElement CreateElement(string elementId)
261-
{
262-
return new FirefoxWebElement(this, elementId);
263-
}
264-
265255
private static ICommandExecutor CreateExecutor(FirefoxDriverService service, FirefoxOptions options, TimeSpan commandTimeout)
266256
{
267257
ICommandExecutor executor = null;

dotnet/src/webdriver/Remote/RemoteWebDriver.cs

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// <copyright file="RemoteWebDriver.cs" company="WebDriver Committers">
1+
// <copyright file="RemoteWebDriver.cs" company="WebDriver Committers">
22
// Licensed to the Software Freedom Conservancy (SFC) under one
33
// or more contributor license agreements. See the NOTICE file
44
// distributed with this work for additional information
@@ -79,6 +79,7 @@ public class RemoteWebDriver : IWebDriver, ISearchContext, IJavaScriptExecutor,
7979
private IApplicationCache appCache;
8080
private ILocationContext locationContext;
8181
private IFileDetector fileDetector = new DefaultFileDetector();
82+
private RemoteWebElementFactory elementFactory;
8283

8384
/// <summary>
8485
/// Initializes a new instance of the <see cref="RemoteWebDriver"/> class. This constructor defaults proxy to http://127.0.0.1:4444/wd/hub
@@ -141,6 +142,7 @@ public RemoteWebDriver(ICommandExecutor commandExecutor, ICapabilities desiredCa
141142
this.StartSession(desiredCapabilities);
142143
this.mouse = new RemoteMouse(this);
143144
this.keyboard = new RemoteKeyboard(this);
145+
this.elementFactory = new RemoteWebElementFactory(this);
144146

145147
if (this.capabilities.HasCapability(CapabilityType.SupportsApplicationCache))
146148
{
@@ -436,6 +438,16 @@ protected ICommandExecutor CommandExecutor
436438
get { return this.executor; }
437439
}
438440

441+
/// <summary>
442+
/// Gets or sets the factory object used to create instances of <see cref="RemoteWebElement"/>
443+
/// or its subclasses.
444+
/// </summary>
445+
protected RemoteWebElementFactory ElementFactory
446+
{
447+
get { return this.elementFactory; }
448+
set { this.elementFactory = value; }
449+
}
450+
439451
/// <summary>
440452
/// Finds the first element in the page that matches the <see cref="By"/> object
441453
/// </summary>
@@ -1015,19 +1027,7 @@ internal IWebElement GetElementFromResponse(Response response)
10151027
Dictionary<string, object> elementDictionary = response.Value as Dictionary<string, object>;
10161028
if (elementDictionary != null)
10171029
{
1018-
// TODO: Remove this "if" logic once the spec is properly updated
1019-
// and remote-end implementations comply.
1020-
string id = string.Empty;
1021-
if (elementDictionary.ContainsKey(RemoteWebElement.ElementReferencePropertyName))
1022-
{
1023-
id = (string)elementDictionary[RemoteWebElement.ElementReferencePropertyName];
1024-
}
1025-
else if (elementDictionary.ContainsKey(RemoteWebElement.LegacyElementReferencePropertyName))
1026-
{
1027-
id = (string)elementDictionary[RemoteWebElement.LegacyElementReferencePropertyName];
1028-
}
1029-
1030-
element = this.CreateElement(id);
1030+
element = this.elementFactory.CreateElement(elementDictionary);
10311031
}
10321032

10331033
return element;
@@ -1049,19 +1049,7 @@ internal ReadOnlyCollection<IWebElement> GetElementsFromResponse(Response respon
10491049
Dictionary<string, object> elementDictionary = elementObject as Dictionary<string, object>;
10501050
if (elementDictionary != null)
10511051
{
1052-
// TODO: Remove this "if" logic once the spec is properly updated
1053-
// and remote-end implementations comply.
1054-
string id = string.Empty;
1055-
if (elementDictionary.ContainsKey(RemoteWebElement.ElementReferencePropertyName))
1056-
{
1057-
id = (string)elementDictionary[RemoteWebElement.ElementReferencePropertyName];
1058-
}
1059-
else if (elementDictionary.ContainsKey(RemoteWebElement.LegacyElementReferencePropertyName))
1060-
{
1061-
id = (string)elementDictionary[RemoteWebElement.LegacyElementReferencePropertyName];
1062-
}
1063-
1064-
RemoteWebElement element = this.CreateElement(id);
1052+
RemoteWebElement element = this.elementFactory.CreateElement(elementDictionary);
10651053
toReturn.Add(element);
10661054
}
10671055
}
@@ -1242,6 +1230,7 @@ protected ReadOnlyCollection<IWebElement> FindElements(string mechanism, string
12421230
/// </summary>
12431231
/// <param name="elementId">The ID of this element.</param>
12441232
/// <returns>A <see cref="RemoteWebElement"/> with the specified ID.</returns>
1233+
[Obsolete("This method is no longer called to create RemoteWebElement instances. Implement a subclass of RemoteWebElementFactory and set the ElementFactory property to create instances of custom RemoteWebElement subclasses.")]
12451234
protected virtual RemoteWebElement CreateElement(string elementId)
12461235
{
12471236
RemoteWebElement toReturn = new RemoteWebElement(this, elementId);
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// <copyright file="RemoteWebElementFactory.cs" company="WebDriver Committers">
2+
// Licensed to the Software Freedom Conservancy (SFC) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The SFC licenses this file
6+
// to you under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
// </copyright>
18+
19+
using System;
20+
using System.Collections.Generic;
21+
using System.Linq;
22+
using System.Text;
23+
24+
namespace OpenQA.Selenium.Remote
25+
{
26+
public class RemoteWebElementFactory
27+
{
28+
private RemoteWebDriver driver;
29+
30+
public RemoteWebElementFactory(RemoteWebDriver parentDriver)
31+
{
32+
this.driver = parentDriver;
33+
}
34+
35+
protected RemoteWebDriver ParentDriver
36+
{
37+
get { return this.driver; }
38+
}
39+
40+
/// <summary>
41+
/// Creates a <see cref="RemoteWebElement"/> from a dictionary containing a reference to an element.
42+
/// </summary>
43+
/// <param name="elementDictionary">The dictionary containing the element reference.</param>
44+
/// <returns>A <see cref="RemoteWebElement"/> containing the information from the specified dictionary.</returns>
45+
public virtual RemoteWebElement CreateElement(Dictionary<string, object> elementDictionary)
46+
{
47+
string elementId = this.GetElementId(elementDictionary);
48+
return new RemoteWebElement(this.ParentDriver, elementId);
49+
}
50+
51+
/// <summary>
52+
/// Gets a value indicating wether the specified dictionary represents a reference to a web element.
53+
/// </summary>
54+
/// <param name="elementDictionary">The dictionary to check.</param>
55+
/// <returns><see langword="true"/> if the dictionary contains an element reference; otherwise, <see langword="false"/>.</returns>
56+
public bool ContainsElementReference(Dictionary<string, object> elementDictionary)
57+
{
58+
string elementPropertyName = string.Empty;
59+
return this.TryGetElementPropertyName(elementDictionary, out elementPropertyName);
60+
}
61+
62+
public string GetElementId(Dictionary<string, object> elementDictionary)
63+
{
64+
string elementPropertyName = string.Empty;
65+
if (!this.TryGetElementPropertyName(elementDictionary, out elementPropertyName))
66+
{
67+
throw new ArgumentException("elementDictionary", "The specified dictionary does not contain an element reference");
68+
}
69+
70+
string elementId = elementDictionary[elementPropertyName].ToString();
71+
if (string.IsNullOrEmpty(elementId))
72+
{
73+
throw new InvalidOperationException("The specified element ID is either null or the empty string.");
74+
}
75+
76+
return elementId;
77+
}
78+
79+
private bool TryGetElementPropertyName(Dictionary<string, object> elementDictionary, out string elementPropertyName)
80+
{
81+
if (elementDictionary == null)
82+
{
83+
throw new ArgumentNullException("elementDictionary", "The dictionary containing the element reference cannot be null");
84+
}
85+
86+
if (elementDictionary.ContainsKey(RemoteWebElement.ElementReferencePropertyName))
87+
{
88+
elementPropertyName = RemoteWebElement.ElementReferencePropertyName;
89+
return true;
90+
}
91+
92+
if (elementDictionary.ContainsKey(RemoteWebElement.LegacyElementReferencePropertyName))
93+
{
94+
elementPropertyName = RemoteWebElement.LegacyElementReferencePropertyName;
95+
return true;
96+
}
97+
98+
elementPropertyName = string.Empty;
99+
return false;
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)