Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dotnet] Annotate nullability on more of WebElement #15230

Open
wants to merge 10 commits into
base: trunk
Choose a base branch
from

Conversation

RenderMichael
Copy link
Contributor

@RenderMichael RenderMichael commented Feb 4, 2025

User description

Thanks for contributing to Selenium!
A PR well described will help maintainers to quickly review and merge it

Before submitting your PR, please check our contributing guidelines.
Avoid large PRs, help reviewers by making them as simple and short as possible.

Annotate nullability on more of WebElement

Motivation and Context

Contributes to #14640

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • I have read the contributing document.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

PR Type

Enhancement, Bug fix


Description

  • Added nullability annotations to WebElement class for improved type safety.

  • Enhanced error handling for null or unexpected responses in various methods.

  • Updated method signatures to reflect nullable return types where applicable.

  • Improved code readability and maintainability with structured nullability directives.


Changes walkthrough 📝

Relevant files
Enhancement
WebElement.cs
Add nullability annotations and enhance error handling     

dotnet/src/webdriver/WebElement.cs

  • Enabled nullability annotations to improve type safety.
  • Added error handling for null or unexpected response values.
  • Updated method signatures to reflect nullable return types.
  • Improved code structure with nullability directives and comments.
  • +68/-22 

    Need help?
  • Type /help how to ... in the comments thread for any questions about Qodo Merge usage.
  • Check out the documentation for more information.
  • Copy link
    Contributor

    qodo-merge-pro bot commented Feb 4, 2025

    PR Reviewer Guide 🔍

    (Review updated until commit d895b6b)

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
    🧪 No relevant tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Null Check

    The GetDomProperty method returns string? but doesn't validate if commandResponse.Value is null before calling ToString() on it

    public virtual string? GetDomProperty(string propertyName)
    {
        Dictionary<string, object> parameters = new Dictionary<string, object>();
        parameters.Add("id", this.Id);
        parameters.Add("name", propertyName);
    Type Safety

    The GetShadowRoot method has nested if blocks that could be combined into a single validation using pattern matching to improve readability and safety

    if (commandResponse.Value is not Dictionary<string, object?> shadowRootDictionary)
    {
        throw new WebDriverException("Get shadow root command succeeded, but response value does not represent a shadow root.");
    }

    Copy link
    Contributor

    qodo-merge-pro bot commented Feb 4, 2025

    PR Code Suggestions ✨

    Latest suggestions up to d895b6b
    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Score
    Possible issue
    Add null check for dictionary cast

    Add null check for rawLocation dictionary before accessing its values to prevent
    potential NullReferenceException when the script execution fails or returns
    unexpected format.

    dotnet/src/webdriver/WebElement.cs [229-233]

     object scriptResponse = this.driver.ExecuteScript("var rect = arguments[0].getBoundingClientRect(); return {'x': rect.left, 'y': rect.top};", this)!;
     
    -Dictionary<string, object> rawLocation = (Dictionary<string, object>)scriptResponse;
    +if (scriptResponse is not Dictionary<string, object> rawLocation)
    +{
    +    throw new WebDriverException($"Script execution returned unexpected format: {scriptResponse}");
    +}
     
     int x = Convert.ToInt32(rawLocation["x"], CultureInfo.InvariantCulture);
    • Apply this suggestion
    Suggestion importance[1-10]: 8

    Why: The suggestion adds important null checking and type validation for the script response, which could prevent runtime errors if the JavaScript execution returns unexpected data format.

    8
    Add null check for created object

    Add null check for shadowRoot before returning it to prevent potential
    NullReferenceException in case TryCreate fails but doesn't throw.

    dotnet/src/webdriver/WebElement.cs [535-538]

    -if (!ShadowRoot.TryCreate(this.driver, shadowRootDictionary, out ShadowRoot? shadowRoot))
    +if (!ShadowRoot.TryCreate(this.driver, shadowRootDictionary, out ShadowRoot? shadowRoot) || shadowRoot == null)
     {
         throw new WebDriverException("Get shadow root command succeeded, but response value does not have a shadow root key value.");
     }
    • Apply this suggestion
    Suggestion importance[1-10]: 7

    Why: The suggestion adds an additional null check for the shadowRoot object after TryCreate, providing better protection against potential null reference exceptions in edge cases.

    7
    Learned
    best practice
    Add null validation checks at the start of methods that take required string parameters to prevent NullReferenceExceptions

    The GetAttribute(), GetDomAttribute(), GetDomProperty(), and GetCssValue()
    methods should validate their string parameters for null at the start of the
    method. Add ArgumentNullException.ThrowIfNull() checks for these required
    parameters.

    dotnet/src/webdriver/WebElement.cs [457-462]

     public virtual string? GetAttribute(string attributeName)
     {
    +    ArgumentNullException.ThrowIfNull(attributeName, nameof(attributeName));
    +    
         Dictionary<string, object> parameters = new Dictionary<string, object>();
         string atom = GetAtom("get-attribute.js");
         parameters.Add("script", atom);
         parameters.Add("args", new object[] { ((IWebDriverObjectReference)this).ToDictionary(), attributeName });
    • Apply this suggestion
    6

    Previous suggestions

    ✅ Suggestions up to commit 2ea99a3
    CategorySuggestion                                                                                                                                    Score
    Possible issue
    Validate coordinate values exist

    Add null check for rawPoint dictionary values before accessing them to prevent
    NullReferenceException if coordinates are missing

    dotnet/src/webdriver/WebElement.cs [165-167]

    -int x = Convert.ToInt32(rawPoint["x"], CultureInfo.InvariantCulture);
    -int y = Convert.ToInt32(rawPoint["y"], CultureInfo.InvariantCulture);
    +if (!rawPoint.TryGetValue("x", out object? xValue) || !rawPoint.TryGetValue("y", out object? yValue))
    +{
    +    throw new WebDriverException("GetElementRect response missing x or y coordinates");
    +}
    +int x = Convert.ToInt32(xValue, CultureInfo.InvariantCulture);
    +int y = Convert.ToInt32(yValue, CultureInfo.InvariantCulture);
     return new Point(x, y);
    Suggestion importance[1-10]: 9

    Why: The suggestion adds crucial validation for coordinate values using TryGetValue, preventing potential NullReferenceException and providing clear error messages when coordinates are missing.

    9
    Validate dimension values exist

    Add null check for rawSize dictionary values before accessing them to prevent
    NullReferenceException if dimensions are missing

    dotnet/src/webdriver/WebElement.cs [189-191]

    -int width = Convert.ToInt32(rawSize["width"], CultureInfo.InvariantCulture);
    -int height = Convert.ToInt32(rawSize["height"], CultureInfo.InvariantCulture);
    +if (!rawSize.TryGetValue("width", out object? widthValue) || !rawSize.TryGetValue("height", out object? heightValue))
    +{
    +    throw new WebDriverException("GetElementRect response missing width or height dimensions");
    +}
    +int width = Convert.ToInt32(widthValue, CultureInfo.InvariantCulture);
    +int height = Convert.ToInt32(heightValue, CultureInfo.InvariantCulture);
     return new Size(width, height);
    Suggestion importance[1-10]: 9

    Why: The suggestion implements important validation for dimension values using TryGetValue, preventing potential NullReferenceException and providing clear error messages when dimensions are missing.

    9
    ✅ Handle null response values
    Suggestion Impact:The commit implements null checking for commandResponse.Value, but throws an exception instead of returning empty string

    code diff:

    +            if (commandResponse.Value is null)
    +            {
    +                throw new WebDriverException("GetElementValueOfCssProperty command returned a successful result, but contained no data");
    +            }
    +
    +            return commandResponse.Value.ToString()!;

    Add null check for commandResponse.Value before calling ToString() since the
    response value could be null, which would cause a NullReferenceException

    dotnet/src/webdriver/WebElement.cs [100-102]

     Response commandResponse = this.Execute(DriverCommand.GetElementText, parameters);
    -return commandResponse.Value.ToString();
    +return commandResponse.Value?.ToString() ?? string.Empty;
    Suggestion importance[1-10]: 8

    Why: The suggestion addresses a potential NullReferenceException by safely handling null response values, which is critical for robust error handling and preventing runtime crashes.

    8
    Learned
    best practice
    Add null validation checks for nullable parameters at the start of methods to prevent NullReferenceExceptions
    Suggestion Impact:The commit adds null checks for command response values and throws WebDriverException when null, which addresses the same goal of preventing NullReferenceExceptions, though implemented differently than suggested

    code diff:

    -                return commandResponse.Value.ToString();
    +                if (commandResponse.Value is null)
    +                {
    +                    throw new WebDriverException("GetElementTagName command returned a successful result, but contained no data");
    +                }
    +                return commandResponse.Value.ToString()!;

    Add ArgumentNullException.ThrowIfNull() checks for nullable parameters
    'attributeName', 'propertyName', and 'text' at the start of GetAttribute(),
    GetDomAttribute(), GetDomProperty(), GetCssValue() and SendKeys() methods to
    prevent potential NullReferenceExceptions.

    dotnet/src/webdriver/WebElement.cs [445-452]

     public virtual string? GetAttribute(string attributeName)
     {
    +    ArgumentNullException.ThrowIfNull(attributeName, nameof(attributeName));
    +    
         Dictionary<string, object> parameters = new Dictionary<string, object>();
         string atom = GetAtom("get-attribute.js");
         parameters.Add("script", atom);
         parameters.Add("args", new object[] { ((IWebDriverObjectReference)this).ToDictionary(), attributeName });
    6

    @RenderMichael RenderMichael marked this pull request as draft February 4, 2025 07:40
    @RenderMichael RenderMichael marked this pull request as ready for review February 4, 2025 19:30
    }
    if (commandResponse.Value is null)
    {
    throw new WebDriverException("GetElementValueOfCssProperty command returned a successful result, but contained no data");
    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    We should test it carefully.

    Copy link
    Contributor Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    I tried to be very careful with this, to change no behavior. I read the spec, it says this will always be a string.

    The only reason I am comfortable with this change, is because it would previously be a NullReferenceException if this were null. This way, users have something better to report if there is a bug here.

    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    This is very good insight. Please give me a chance to revisit it.

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    2 participants