Skip to content

Commit 5b648f3

Browse files
[dotnet] Annotate nullability on Actions type (#15208)
* [dotnet] Annotate nullability on `Actions` type * Add `Keys` nullability
1 parent 01a96d9 commit 5b648f3

File tree

4 files changed

+56
-29
lines changed

4 files changed

+56
-29
lines changed

dotnet/src/webdriver/Interactions/Actions.cs

+48-26
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919

2020
using System;
2121
using System.Collections.Generic;
22+
using System.Diagnostics.CodeAnalysis;
23+
24+
#nullable enable
2225

2326
namespace OpenQA.Selenium.Interactions
2427
{
@@ -29,9 +32,9 @@ public class Actions : IAction
2932
{
3033
private readonly TimeSpan duration;
3134
private ActionBuilder actionBuilder = new ActionBuilder();
32-
private PointerInputDevice activePointer;
33-
private KeyInputDevice activeKeyboard;
34-
private WheelInputDevice activeWheel;
35+
private PointerInputDevice? activePointer;
36+
private KeyInputDevice? activeKeyboard;
37+
private WheelInputDevice? activeWheel;
3538

3639
/// <summary>
3740
/// Initializes a new instance of the <see cref="Actions"/> class.
@@ -51,14 +54,10 @@ public Actions(IWebDriver driver)
5154
/// <exception cref="ArgumentException">If <paramref name="driver"/> does not implement <see cref="IActionExecutor"/>.</exception>
5255
public Actions(IWebDriver driver, TimeSpan duration)
5356
{
54-
IActionExecutor actionExecutor = GetDriverAs<IActionExecutor>(driver);
55-
if (actionExecutor == null)
56-
{
57-
throw new ArgumentException("The IWebDriver object must implement or wrap a driver that implements IActionExecutor.", nameof(driver));
58-
}
57+
IActionExecutor actionExecutor = GetDriverAs<IActionExecutor>(driver)
58+
?? throw new ArgumentException("The IWebDriver object must implement or wrap a driver that implements IActionExecutor.", nameof(driver));
5959

6060
this.ActionExecutor = actionExecutor;
61-
6261
this.duration = duration;
6362
}
6463

@@ -74,9 +73,10 @@ public Actions(IWebDriver driver, TimeSpan duration)
7473
/// <param name="name">The name of the pointer device to set as active.</param>
7574
/// <returns>A self-reference to this Actions class.</returns>
7675
/// <exception cref="InvalidOperationException">If a device with this name exists but is not a pointer.</exception>
76+
[MemberNotNull(nameof(activePointer))]
7777
public Actions SetActivePointer(PointerKind kind, string name)
7878
{
79-
InputDevice device = FindDeviceById(name);
79+
InputDevice? device = FindDeviceById(name);
8080

8181
this.activePointer = device switch
8282
{
@@ -94,9 +94,10 @@ public Actions SetActivePointer(PointerKind kind, string name)
9494
/// <param name="name">The name of the keyboard device to set as active.</param>
9595
/// <returns>A self-reference to this Actions class.</returns>
9696
/// <exception cref="InvalidOperationException">If a device with this name exists but is not a keyboard.</exception>
97+
[MemberNotNull(nameof(activeKeyboard))]
9798
public Actions SetActiveKeyboard(string name)
9899
{
99-
InputDevice device = FindDeviceById(name);
100+
InputDevice? device = FindDeviceById(name);
100101

101102
this.activeKeyboard = device switch
102103
{
@@ -114,9 +115,10 @@ public Actions SetActiveKeyboard(string name)
114115
/// <param name="name">The name of the wheel device to set as active.</param>
115116
/// <returns>A self-reference to this Actions class.</returns>
116117
/// <exception cref="InvalidOperationException">If a device with this name exists but is not a wheel.</exception>
118+
[MemberNotNull(nameof(activeWheel))]
117119
public Actions SetActiveWheel(string name)
118120
{
119-
InputDevice device = FindDeviceById(name);
121+
InputDevice? device = FindDeviceById(name);
120122

121123
this.activeWheel = device switch
122124
{
@@ -128,7 +130,7 @@ public Actions SetActiveWheel(string name)
128130
return this;
129131
}
130132

131-
private InputDevice FindDeviceById(string name)
133+
private InputDevice? FindDeviceById(string? name)
132134
{
133135
foreach (var sequence in this.actionBuilder.ToActionSequenceList())
134136
{
@@ -211,14 +213,14 @@ public Actions KeyDown(string theKey)
211213
/// of <see cref="Keys.Shift"/>, <see cref="Keys.Control"/>, <see cref="Keys.Alt"/>,
212214
/// <see cref="Keys.Meta"/>, <see cref="Keys.Command"/>,<see cref="Keys.LeftAlt"/>,
213215
/// <see cref="Keys.LeftControl"/>,<see cref="Keys.LeftShift"/>.</exception>
214-
public Actions KeyDown(IWebElement element, string theKey)
216+
public Actions KeyDown(IWebElement? element, string theKey)
215217
{
216218
if (string.IsNullOrEmpty(theKey))
217219
{
218220
throw new ArgumentException("The key value must not be null or empty", nameof(theKey));
219221
}
220222

221-
ILocatable target = GetLocatableFromElement(element);
223+
ILocatable? target = GetLocatableFromElement(element);
222224
if (element != null)
223225
{
224226
this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(element, 0, 0, duration));
@@ -255,14 +257,14 @@ public Actions KeyUp(string theKey)
255257
/// of <see cref="Keys.Shift"/>, <see cref="Keys.Control"/>, <see cref="Keys.Alt"/>,
256258
/// <see cref="Keys.Meta"/>, <see cref="Keys.Command"/>,<see cref="Keys.LeftAlt"/>,
257259
/// <see cref="Keys.LeftControl"/>,<see cref="Keys.LeftShift"/>.</exception>
258-
public Actions KeyUp(IWebElement element, string theKey)
260+
public Actions KeyUp(IWebElement? element, string theKey)
259261
{
260262
if (string.IsNullOrEmpty(theKey))
261263
{
262264
throw new ArgumentException("The key value must not be null or empty", nameof(theKey));
263265
}
264266

265-
ILocatable target = GetLocatableFromElement(element);
267+
ILocatable? target = GetLocatableFromElement(element);
266268
if (element != null)
267269
{
268270
this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(element, 0, 0, duration));
@@ -279,6 +281,7 @@ public Actions KeyUp(IWebElement element, string theKey)
279281
/// </summary>
280282
/// <param name="keysToSend">The keystrokes to send to the browser.</param>
281283
/// <returns>A self-reference to this <see cref="Actions"/>.</returns>
284+
/// <exception cref="ArgumentException">If <paramref name="keysToSend"/> is <see langword="null"/> or <see cref="string.Empty"/>.</exception>
282285
public Actions SendKeys(string keysToSend)
283286
{
284287
return this.SendKeys(null, keysToSend);
@@ -290,14 +293,15 @@ public Actions SendKeys(string keysToSend)
290293
/// <param name="element">The element to which to send the keystrokes.</param>
291294
/// <param name="keysToSend">The keystrokes to send to the browser.</param>
292295
/// <returns>A self-reference to this <see cref="Actions"/>.</returns>
293-
public Actions SendKeys(IWebElement element, string keysToSend)
296+
/// <exception cref="ArgumentException">If <paramref name="keysToSend"/> is <see langword="null"/> or <see cref="string.Empty"/>.</exception>
297+
public Actions SendKeys(IWebElement? element, string keysToSend)
294298
{
295299
if (string.IsNullOrEmpty(keysToSend))
296300
{
297301
throw new ArgumentException("The key value must not be null or empty", nameof(keysToSend));
298302
}
299303

300-
ILocatable target = GetLocatableFromElement(element);
304+
ILocatable? target = GetLocatableFromElement(element);
301305
if (element != null)
302306
{
303307
this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(element, 0, 0, duration));
@@ -319,6 +323,7 @@ public Actions SendKeys(IWebElement element, string keysToSend)
319323
/// </summary>
320324
/// <param name="onElement">The element on which to click and hold.</param>
321325
/// <returns>A self-reference to this <see cref="Actions"/>.</returns>
326+
/// <exception cref="ArgumentNullException">If <paramref name="onElement"/> is null.</exception>
322327
public Actions ClickAndHold(IWebElement onElement)
323328
{
324329
this.MoveToElement(onElement).ClickAndHold();
@@ -340,6 +345,7 @@ public Actions ClickAndHold()
340345
/// </summary>
341346
/// <param name="onElement">The element on which to release the button.</param>
342347
/// <returns>A self-reference to this <see cref="Actions"/>.</returns>
348+
/// <exception cref="ArgumentNullException">If <paramref name="onElement"/> is null.</exception>
343349
public Actions Release(IWebElement onElement)
344350
{
345351
this.MoveToElement(onElement).Release();
@@ -361,6 +367,7 @@ public Actions Release()
361367
/// </summary>
362368
/// <param name="onElement">The element on which to click.</param>
363369
/// <returns>A self-reference to this <see cref="Actions"/>.</returns>
370+
/// <exception cref="ArgumentNullException">If <paramref name="onElement"/> is null.</exception>
364371
public Actions Click(IWebElement onElement)
365372
{
366373
this.MoveToElement(onElement).Click();
@@ -383,6 +390,7 @@ public Actions Click()
383390
/// </summary>
384391
/// <param name="onElement">The element on which to double-click.</param>
385392
/// <returns>A self-reference to this <see cref="Actions"/>.</returns>
393+
/// <exception cref="ArgumentNullException">If <paramref name="onElement"/> is null.</exception>
386394
public Actions DoubleClick(IWebElement onElement)
387395
{
388396
this.MoveToElement(onElement).DoubleClick();
@@ -407,6 +415,7 @@ public Actions DoubleClick()
407415
/// </summary>
408416
/// <param name="toElement">The element to which to move the mouse.</param>
409417
/// <returns>A self-reference to this <see cref="Actions"/>.</returns>
418+
/// <exception cref="ArgumentNullException">If <paramref name="toElement"/> is null.</exception>
410419
public Actions MoveToElement(IWebElement toElement)
411420
{
412421
if (toElement == null)
@@ -460,6 +469,7 @@ public Actions MoveToLocation(int offsetX, int offsetY)
460469
/// </summary>
461470
/// <param name="onElement">The element on which to right-click.</param>
462471
/// <returns>A self-reference to this <see cref="Actions"/>.</returns>
472+
/// <exception cref="ArgumentNullException">If <paramref name="onElement"/> is null.</exception>
463473
public Actions ContextClick(IWebElement onElement)
464474
{
465475
this.MoveToElement(onElement).ContextClick();
@@ -483,6 +493,7 @@ public Actions ContextClick()
483493
/// <param name="source">The element on which the drag operation is started.</param>
484494
/// <param name="target">The element on which the drop is performed.</param>
485495
/// <returns>A self-reference to this <see cref="Actions"/>.</returns>
496+
/// <exception cref="ArgumentNullException">If <paramref name="source"/> or <paramref name="target"/> are null.</exception>
486497
public Actions DragAndDrop(IWebElement source, IWebElement target)
487498
{
488499
this.ClickAndHold(source).MoveToElement(target).Release(target);
@@ -496,6 +507,7 @@ public Actions DragAndDrop(IWebElement source, IWebElement target)
496507
/// <param name="offsetX">The horizontal offset to which to move the mouse.</param>
497508
/// <param name="offsetY">The vertical offset to which to move the mouse.</param>
498509
/// <returns>A self-reference to this <see cref="Actions"/>.</returns>
510+
/// <exception cref="ArgumentNullException">If <paramref name="source"/> is null.</exception>
499511
public Actions DragAndDropToOffset(IWebElement source, int offsetX, int offsetY)
500512
{
501513
this.ClickAndHold(source).MoveByOffset(offsetX, offsetY).Release();
@@ -507,6 +519,7 @@ public Actions DragAndDropToOffset(IWebElement source, int offsetX, int offsetY)
507519
/// </summary>
508520
/// <param name="element">Which element to scroll into the viewport.</param>
509521
/// <returns>A self-reference to this <see cref="Actions"/>.</returns>
522+
/// <exception cref="ArgumentNullException">If <paramref name="element"/> is null.</exception>
510523
public Actions ScrollToElement(IWebElement element)
511524
{
512525
this.actionBuilder.AddAction(this.GetActiveWheel().CreateWheelScroll(element, 0, 0, 0, 0, duration));
@@ -540,8 +553,15 @@ public Actions ScrollByAmount(int deltaX, int deltaY)
540553
/// <param name="deltaY">Distance along Y axis to scroll using the wheel. A negative value scrolls up.</param>
541554
/// <returns>A self-reference to this <see cref="Actions"/>.</returns>
542555
/// <exception cref="MoveTargetOutOfBoundsException">If the origin with offset is outside the viewport.</exception>
556+
/// <exception cref="ArgumentNullException">If <paramref name="scrollOrigin"/> is null.</exception>
557+
/// <exception cref="ArgumentException">If both or either of Viewport and Element are set.</exception>
543558
public Actions ScrollFromOrigin(WheelInputDevice.ScrollOrigin scrollOrigin, int deltaX, int deltaY)
544559
{
560+
if (scrollOrigin is null)
561+
{
562+
throw new ArgumentNullException(nameof(scrollOrigin));
563+
}
564+
545565
if (scrollOrigin.Viewport && scrollOrigin.Element != null)
546566
{
547567
throw new ArgumentException("viewport can not be true if an element is defined.", nameof(scrollOrigin));
@@ -554,7 +574,7 @@ public Actions ScrollFromOrigin(WheelInputDevice.ScrollOrigin scrollOrigin, int
554574
}
555575
else
556576
{
557-
this.actionBuilder.AddAction(this.GetActiveWheel().CreateWheelScroll(scrollOrigin.Element,
577+
this.actionBuilder.AddAction(this.GetActiveWheel().CreateWheelScroll(scrollOrigin.Element!,
558578
scrollOrigin.XOffset, scrollOrigin.YOffset, deltaX, deltaY, duration));
559579
}
560580

@@ -566,6 +586,7 @@ public Actions ScrollFromOrigin(WheelInputDevice.ScrollOrigin scrollOrigin, int
566586
/// </summary>
567587
/// <param name="duration">How long to pause the action chain.</param>
568588
/// <returns>A self-reference to this <see cref="Actions"/>.</returns>
589+
/// <exception cref="ArgumentException">If <paramref name="duration"/> is negative.</exception>
569590
public Actions Pause(TimeSpan duration)
570591
{
571592
this.actionBuilder.AddAction(new PauseInteraction(this.GetActivePointer(), duration));
@@ -603,15 +624,16 @@ public void Reset()
603624
/// </summary>
604625
/// <param name="element">The <see cref="IWebElement"/> to get the location of.</param>
605626
/// <returns>The <see cref="ILocatable"/> of the <see cref="IWebElement"/>.</returns>
606-
protected static ILocatable GetLocatableFromElement(IWebElement element)
627+
[return: NotNullIfNotNull(nameof(element))]
628+
protected static ILocatable? GetLocatableFromElement(IWebElement? element)
607629
{
608630
if (element == null)
609631
{
610632
return null;
611633
}
612634

613-
ILocatable target = null;
614-
IWrapsElement wrapper = element as IWrapsElement;
635+
ILocatable? target = null;
636+
IWrapsElement? wrapper = element as IWrapsElement;
615637
while (wrapper != null)
616638
{
617639
target = wrapper.WrappedElement as ILocatable;
@@ -631,12 +653,12 @@ protected static ILocatable GetLocatableFromElement(IWebElement element)
631653
return target;
632654
}
633655

634-
private T GetDriverAs<T>(IWebDriver driver) where T : class
656+
private static T? GetDriverAs<T>(IWebDriver? driver) where T : class
635657
{
636-
T driverAsType = driver as T;
658+
T? driverAsType = driver as T;
637659
if (driverAsType == null)
638660
{
639-
IWrapsDriver wrapper = driver as IWrapsDriver;
661+
IWrapsDriver? wrapper = driver as IWrapsDriver;
640662
while (wrapper != null)
641663
{
642664
driverAsType = wrapper.WrappedDriver as T;

dotnet/src/webdriver/Interactions/InputDevice.cs

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public Interaction CreatePause()
7777
/// of the pause. Note that <see cref="TimeSpan.Zero"/> pauses to synchronize
7878
/// with other action sequences for other devices.</param>
7979
/// <returns>The <see cref="Interaction"/> representing the action.</returns>
80+
/// <exception cref="ArgumentException">If <paramref name="duration"/> is negative.</exception>
8081
public Interaction CreatePause(TimeSpan duration)
8182
{
8283
return new PauseInteraction(this, duration);

dotnet/src/webdriver/Interactions/PauseInteraction.cs

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public PauseInteraction(InputDevice sourceDevice)
4646
/// </summary>
4747
/// <param name="sourceDevice">The input device on which to execute the pause.</param>
4848
/// <param name="duration">The length of time to pause for.</param>
49+
/// <exception cref="ArgumentException">If <paramref name="duration"/> is negative.</exception>
4950
public PauseInteraction(InputDevice sourceDevice, TimeSpan duration)
5051
: base(sourceDevice)
5152
{

dotnet/src/webdriver/Keys.cs

+6-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
using System.Collections.Generic;
2222
using System.Globalization;
2323

24+
#nullable enable
25+
2426
namespace OpenQA.Selenium
2527
{
2628
/// <summary>
@@ -352,13 +354,14 @@ public static class Keys
352354
/// </summary>
353355
public static readonly string ZenkakuHankaku = Convert.ToString(Convert.ToChar(0xE040, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture);
354356

355-
private static Dictionary<string, string> descriptions;
357+
private static Dictionary<string, string>? descriptions;
356358

357359
/// <summary>
358360
/// Gets the description of a specific key.
359361
/// </summary>
360362
/// <param name="value">The key value for which to get the description.</param>
361363
/// <returns>The description of the key.</returns>
364+
/// <exception cref="ArgumentNullException">If <paramref name="value"/> is <see langword="null"/>.</exception>
362365
internal static object GetDescription(string value)
363366
{
364367
if (descriptions == null)
@@ -423,9 +426,9 @@ internal static object GetDescription(string value)
423426
descriptions.Add(ZenkakuHankaku, "Zenkaku Hankaku");
424427
}
425428

426-
if (descriptions.ContainsKey(value))
429+
if (descriptions.TryGetValue(value, out string? description))
427430
{
428-
return descriptions[value];
431+
return description;
429432
}
430433

431434
return value;

0 commit comments

Comments
 (0)