Skip to content

Commit c9f18c1

Browse files
RenderMichaelsandeepsuryaprasad
authored andcommitted
[dotnet] Annotate nullability on more of WebElement (SeleniumHQ#15230)
* [dotnet] Annotate nullability on more of `WebElement` * Handle nullability of GetCssValue * Implement nullability on more of `WebElement` * fix typo * improve error message * Use new `EnsureValueIsNotNull` hlper
1 parent 6607d9f commit c9f18c1

File tree

1 file changed

+56
-23
lines changed

1 file changed

+56
-23
lines changed

dotnet/src/webdriver/WebElement.cs

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public class WebElement : IWebElement, IFindsElement, IWrapsDriver, ILocatable,
4040
/// </summary>
4141
public const string ElementReferencePropertyName = "element-6066-11e4-a52e-4f735466cecf";
4242

43+
#nullable enable
44+
4345
private readonly WebDriver driver;
4446

4547
/// <summary>
@@ -77,7 +79,8 @@ public virtual string TagName
7779

7880
Response commandResponse = this.Execute(DriverCommand.GetElementTagName, parameters);
7981

80-
return commandResponse.Value.ToString();
82+
commandResponse.EnsureValueIsNotNull();
83+
return commandResponse.Value.ToString()!;
8184
}
8285
}
8386

@@ -95,7 +98,8 @@ public virtual string Text
9598

9699
Response commandResponse = this.Execute(DriverCommand.GetElementText, parameters);
97100

98-
return commandResponse.Value.ToString();
101+
commandResponse.EnsureValueIsNotNull();
102+
return commandResponse.Value.ToString()!;
99103
}
100104
}
101105

@@ -151,7 +155,11 @@ public virtual Point Location
151155

152156
Response commandResponse = this.Execute(DriverCommand.GetElementRect, parameters);
153157

154-
Dictionary<string, object> rawPoint = (Dictionary<string, object>)commandResponse.Value;
158+
if (commandResponse.Value is not Dictionary<string, object?> rawPoint)
159+
{
160+
throw new WebDriverException($"GetElementRect command was successful, but response was not an object: {commandResponse.Value}");
161+
}
162+
155163
int x = Convert.ToInt32(rawPoint["x"], CultureInfo.InvariantCulture);
156164
int y = Convert.ToInt32(rawPoint["y"], CultureInfo.InvariantCulture);
157165
return new Point(x, y);
@@ -171,7 +179,11 @@ public virtual Size Size
171179

172180
Response commandResponse = this.Execute(DriverCommand.GetElementRect, parameters);
173181

174-
Dictionary<string, object> rawSize = (Dictionary<string, object>)commandResponse.Value;
182+
if (commandResponse.Value is not Dictionary<string, object?> rawSize)
183+
{
184+
throw new WebDriverException($"GetElementRect command was successful, but response was not an object: {commandResponse.Value}");
185+
}
186+
175187
int width = Convert.ToInt32(rawSize["width"], CultureInfo.InvariantCulture);
176188
int height = Convert.ToInt32(rawSize["height"], CultureInfo.InvariantCulture);
177189
return new Size(width, height);
@@ -207,7 +219,7 @@ public virtual Point LocationOnScreenOnceScrolledIntoView
207219
{
208220
get
209221
{
210-
object scriptResponse = this.driver.ExecuteScript("var rect = arguments[0].getBoundingClientRect(); return {'x': rect.left, 'y': rect.top};", this);
222+
object scriptResponse = this.driver.ExecuteScript("var rect = arguments[0].getBoundingClientRect(); return {'x': rect.left, 'y': rect.top};", this)!;
211223

212224
Dictionary<string, object> rawLocation = (Dictionary<string, object>)scriptResponse;
213225

@@ -229,7 +241,8 @@ public virtual string ComputedAccessibleLabel
229241

230242
Response commandResponse = this.Execute(DriverCommand.GetComputedAccessibleLabel, parameters);
231243

232-
return commandResponse.Value.ToString();
244+
commandResponse.EnsureValueIsNotNull();
245+
return commandResponse.Value.ToString()!;
233246
}
234247
}
235248

@@ -240,16 +253,18 @@ public virtual string ComputedAccessibleRole
240253
{
241254
get
242255
{
243-
// TODO: Returning this as a string is incorrect. The W3C WebDriver Specification
244-
// needs to be updated to more thoroughly document the structure of what is returned
245-
// by this command. Once that is done, a type-safe class will be created, and will
246-
// be returned by this property.
247256
Dictionary<string, object> parameters = new Dictionary<string, object>();
248257
parameters.Add("id", this.Id);
249258

250259
Response commandResponse = this.Execute(DriverCommand.GetComputedAccessibleRole, parameters);
251260

261+
#nullable disable
262+
// TODO: Returning this as a string is incorrect. The W3C WebDriver Specification
263+
// needs to be updated to more thoroughly document the structure of what is returned
264+
// by this command. Once that is done, a type-safe class will be created, and will
265+
// be returned by this property.
252266
return commandResponse.Value.ToString();
267+
#nullable enable
253268
}
254269
}
255270

@@ -312,11 +327,14 @@ public virtual void Click()
312327
this.Execute(DriverCommand.ClickElement, parameters);
313328
}
314329

330+
#nullable restore
331+
315332
/// <summary>
316333
/// Finds the first <see cref="IWebElement"/> using the given method.
317334
/// </summary>
318335
/// <param name="by">The locating mechanism to use.</param>
319336
/// <returns>The first matching <see cref="IWebElement"/> on the current context.</returns>
337+
/// <exception cref="ArgumentNullException">If <paramref name="by"/> is <see langword="null"/>.</exception>
320338
/// <exception cref="NoSuchElementException">If no element matches the criteria.</exception>
321339
public virtual IWebElement FindElement(By by)
322340
{
@@ -328,6 +346,8 @@ public virtual IWebElement FindElement(By by)
328346
return by.FindElement(this);
329347
}
330348

349+
#nullable enable
350+
331351
/// <summary>
332352
/// Finds a child element matching the given mechanism and value.
333353
/// </summary>
@@ -346,6 +366,8 @@ public virtual IWebElement FindElement(string mechanism, string value)
346366
return this.driver.GetElementFromResponse(commandResponse);
347367
}
348368

369+
#nullable restore
370+
349371
/// <summary>
350372
/// Finds all <see cref="IWebElement">IWebElements</see> within the current context
351373
/// using the given mechanism.
@@ -363,6 +385,8 @@ public virtual ReadOnlyCollection<IWebElement> FindElements(By by)
363385
return by.FindElements(this);
364386
}
365387

388+
#nullable enable
389+
366390
/// <summary>
367391
/// Finds all child elements matching the given mechanism and value.
368392
/// </summary>
@@ -418,15 +442,14 @@ public virtual ReadOnlyCollection<IWebElement> FindElements(string mechanism, st
418442
/// via JavaScript.
419443
/// </remarks>
420444
/// <exception cref="StaleElementReferenceException">Thrown when the target element is no longer valid in the document DOM.</exception>
421-
public virtual string GetAttribute(string attributeName)
445+
public virtual string? GetAttribute(string attributeName)
422446
{
423-
Response commandResponse = null;
424-
string attributeValue = string.Empty;
425447
Dictionary<string, object> parameters = new Dictionary<string, object>();
426448
string atom = GetAtom("get-attribute.js");
427449
parameters.Add("script", atom);
428450
parameters.Add("args", new object[] { ((IWebDriverObjectReference)this).ToDictionary(), attributeName });
429-
commandResponse = this.Execute(DriverCommand.ExecuteScript, parameters);
451+
452+
Response commandResponse = Execute(DriverCommand.ExecuteScript, parameters);
430453

431454

432455
// Normalize string values of boolean results as lowercase.
@@ -451,7 +474,7 @@ public virtual string GetAttribute(string attributeName)
451474
/// of an IDL property of the element, either use the <see cref="GetAttribute(string)"/>
452475
/// method or the <see cref="GetDomProperty(string)"/> method.
453476
/// </remarks>
454-
public virtual string GetDomAttribute(string attributeName)
477+
public virtual string? GetDomAttribute(string attributeName)
455478
{
456479
Dictionary<string, object> parameters = new Dictionary<string, object>();
457480
parameters.Add("id", this.Id);
@@ -469,7 +492,7 @@ public virtual string GetDomAttribute(string attributeName)
469492
/// <returns>The JavaScript property's current value. Returns a <see langword="null"/> if the
470493
/// value is not set or the property does not exist.</returns>
471494
/// <exception cref="StaleElementReferenceException">Thrown when the target element is no longer valid in the document DOM.</exception>
472-
public virtual string GetDomProperty(string propertyName)
495+
public virtual string? GetDomProperty(string propertyName)
473496
{
474497
Dictionary<string, object> parameters = new Dictionary<string, object>();
475498
parameters.Add("id", this.Id);
@@ -492,12 +515,12 @@ public virtual ISearchContext GetShadowRoot()
492515
parameters.Add("id", this.Id);
493516

494517
Response commandResponse = this.Execute(DriverCommand.GetElementShadowRoot, parameters);
495-
if (commandResponse.Value is not Dictionary<string, object> shadowRootDictionary)
518+
if (commandResponse.Value is not Dictionary<string, object?> shadowRootDictionary)
496519
{
497520
throw new WebDriverException("Get shadow root command succeeded, but response value does not represent a shadow root.");
498521
}
499522

500-
if (!ShadowRoot.TryCreate(this.driver, shadowRootDictionary, out ShadowRoot shadowRoot))
523+
if (!ShadowRoot.TryCreate(this.driver, shadowRootDictionary, out ShadowRoot? shadowRoot))
501524
{
502525
throw new WebDriverException("Get shadow root command succeeded, but response value does not have a shadow root key value.");
503526
}
@@ -523,7 +546,9 @@ public virtual string GetCssValue(string propertyName)
523546
parameters.Add("name", propertyName);
524547

525548
Response commandResponse = this.Execute(DriverCommand.GetElementValueOfCssProperty, parameters);
526-
return commandResponse.Value.ToString();
549+
550+
commandResponse.EnsureValueIsNotNull();
551+
return commandResponse.Value.ToString()!;
527552
}
528553

529554
/// <summary>
@@ -537,7 +562,9 @@ public virtual Screenshot GetScreenshot()
537562

538563
// Get the screenshot as base64.
539564
Response screenshotResponse = this.Execute(DriverCommand.ElementScreenshot, parameters);
540-
string base64 = screenshotResponse.Value.ToString();
565+
566+
screenshotResponse.EnsureValueIsNotNull();
567+
string base64 = screenshotResponse.Value.ToString()!;
541568

542569
// ... and convert it.
543570
return new Screenshot(base64);
@@ -595,7 +622,7 @@ public virtual void SendKeys(string text)
595622
/// <exception cref="StaleElementReferenceException">Thrown when the target element is no longer valid in the document DOM.</exception>
596623
public virtual void Submit()
597624
{
598-
string elementType = this.GetAttribute("type");
625+
string? elementType = this.GetAttribute("type");
599626
if (elementType != null && elementType == "submit")
600627
{
601628
this.Click();
@@ -639,7 +666,7 @@ public override int GetHashCode()
639666
/// </summary>
640667
/// <param name="obj">Object to compare against</param>
641668
/// <returns>A boolean if it is equal or not</returns>
642-
public override bool Equals(object obj)
669+
public override bool Equals(object? obj)
643670
{
644671
if (obj is not IWebElement other)
645672
{
@@ -674,6 +701,8 @@ Dictionary<string, object> IWebDriverObjectReference.ToDictionary()
674701
return elementDictionary;
675702
}
676703

704+
#nullable restore
705+
677706
/// <summary>
678707
/// Executes a command on this element using the specified parameters.
679708
/// </summary>
@@ -685,6 +714,8 @@ protected virtual Response Execute(string commandToExecute, Dictionary<string, o
685714
return this.driver.InternalExecute(commandToExecute, parameters);
686715
}
687716

717+
#nullable enable
718+
688719
private static string GetAtom(string atomResourceName)
689720
{
690721
string atom = string.Empty;
@@ -719,7 +750,9 @@ private string UploadFile(string localFile)
719750
Dictionary<string, object> parameters = new Dictionary<string, object>();
720751
parameters.Add("file", base64zip);
721752
Response response = this.Execute(DriverCommand.UploadFile, parameters);
722-
return response.Value.ToString();
753+
754+
response.EnsureValueIsNotNull();
755+
return response.Value.ToString()!;
723756
}
724757
catch (IOException e)
725758
{

0 commit comments

Comments
 (0)