selenium icon indicating copy to clipboard operation
selenium copied to clipboard

[dotnet] Enable Option<T> to work when no value is provided

Open RenderMichael opened this issue 1 month ago • 2 comments

User description

Not sure how CI was passing, CanSetViewport was failing locally.

When the value was supposed to be unset, we were sending null anyways. We remove that here.

🔗 Related Issues

💥 What does this PR do?

🔧 Implementation Notes

💡 Additional Considerations

🔄 Types of changes

  • Bug fix (backwards compatible)

PR Type

Bug fix


Description

  • Fix Optional<T> serialization to properly handle unset values

  • Replace custom converter with factory-based approach for better handling

  • Use JsonIgnore attribute to omit unset optional properties from JSON

  • Add Optional<T>.None static property for explicit unset values


Diagram Walkthrough

flowchart LR
  A["Optional&lt;T&gt; struct"] -->|"Add JsonConverter attribute"| B["OptionalConverterFactory"]
  B -->|"Create converter for each T"| C["OptionalConverter&lt;T&gt;"]
  D["SetViewportOptions properties"] -->|"Add JsonIgnore attribute"| E["Omit when default"]
  C -->|"Throw on unset write"| F["Enforce JsonIgnore usage"]
  G["SetViewportParameters"] -->|"Use Optional.None explicitly"| H["Proper unset handling"]

File Walkthrough

Relevant files
Enhancement
Optional.cs
Add JsonConverter and None property to Optional                   

dotnet/src/webdriver/BiDi/Optional.cs

  • Add JsonConverter attribute to Optional struct for automatic
    serialization
  • Add static None property to represent unset optional values
  • Change _value field to nullable T? to support null values
  • Update Value property to return nullable T?
  • Update implicit conversion operator to accept nullable T?
+10/-5   
OptionalConverterFactory.cs
Implement factory-based Optional converter                             

dotnet/src/webdriver/BiDi/Json/Converters/OptionalConverterFactory.cs

  • Create new factory-based converter to handle Optional types
    dynamically
  • Implement OptionalConverterFactory that creates converters for any
    Optional
  • Add nested OptionalConverter with HandleNull support
  • Throw exception when writing unset values to enforce JsonIgnore usage
+66/-0   
Refactoring
OptionalConverter.cs
Remove standalone converter class                                               

dotnet/src/webdriver/BiDi/Json/Converters/OptionalConverter.cs

  • Remove standalone OptionalConverter class (replaced by factory
    pattern)
  • Functionality moved to OptionalConverterFactory.OptionalConverter
+0/-51   
Bug fix
SetViewportCommand.cs
Use JsonIgnore instead of custom converter                             

dotnet/src/webdriver/BiDi/BrowsingContext/SetViewportCommand.cs

  • Replace OptionalConverter attribute with JsonIgnore(Condition =
    JsonIgnoreCondition.WhenWritingDefault)
  • Change property types from nullable Optional? to non-nullable Optional
  • Remove unused using OpenQA.Selenium.BiDi.Json.Converters statement
  • Apply formatting improvements with multi-line record declaration
+8/-4     
BrowsingContextModule.cs
Explicitly use Optional.None for unset values                       

dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs

  • Update SetViewportAsync to explicitly use Optional.None for unset
    values
  • Pass options?.Viewport ?? Optional.None to ensure proper unset
    handling
  • Pass options?.DevicePixelRatio ?? Optional.None for device pixel ratio
+1/-1     
Configuration changes
BiDiJsonSerializerContext.cs
Register nullable Viewport type                                                   

dotnet/src/webdriver/BiDi/Json/BiDiJsonSerializerContext.cs

  • Add [JsonSerializable(typeof(BrowsingContext.Viewport?))] attribute
  • Ensure nullable Viewport? type is properly serialized in the context
+1/-0     
Tests
BrowsingContextTest.cs
Update test to use Optional.None                                                 

dotnet/test/common/BiDi/BrowsingContext/BrowsingContextTest.cs

  • Update test to use Optional.None instead of default for clarity
  • Ensures test explicitly demonstrates unset optional value handling
+1/-1     

RenderMichael avatar Nov 28 '25 06:11 RenderMichael

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🟡
🎫 #5678
🔴 Investigate and resolve "Error: ConnectFailure (Connection refused)" occurring on
subsequent ChromeDriver instantiations on Ubuntu 16.04.4 with Chrome 65/Chromedriver 2.35
using Selenium 3.9.0.
Provide steps or code changes that prevent the error after the first ChromeDriver
instance.
Ensure stable behavior across multiple ChromeDriver instantiations.
🟡
🎫 #1234
🔴 Clicking links whose href contains JavaScript should trigger the JavaScript in Selenium
2.48.x with Firefox 42.0, consistent with 2.47.1 behavior.
Provide fix or regression handling specifically for Firefox driver interaction with
javascript: links.
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
No auditing: The new handling of optional viewport parameters introduces behavior changes without
adding any logging or audit trail of the action or outcome.

Referred Code
public async Task<SetViewportResult> SetViewportAsync(BrowsingContext context, SetViewportOptions? options = null)
{
    var @params = new SetViewportParameters(context, options?.Viewport ?? Optional<Viewport?>.None, options?.DevicePixelRatio ?? Optional<double?>.None);

    return await Broker.ExecuteCommandAsync(new SetViewportCommand(@params), options, JsonContext.SetViewportCommand, JsonContext.SetViewportResult).ConfigureAwait(false);
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Exception policy: Writing an unset Optional throws a generic JsonException without contextual details, which
may be appropriate but lacks actionable context or fallback handling.

Referred Code
public override void Write(Utf8JsonWriter writer, Optional<T> value, JsonSerializerOptions options)
{
    if (value.TryGetValue(out var optionalValue))
    {
        JsonSerializer.Serialize(writer, optionalValue, options);
    }
    else
    {
        throw new JsonException("This property should be annotated with [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]");
    }
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Input validation: There is no explicit validation for Viewport dimensions or DevicePixelRatio when provided,
which may rely on upstream validation not visible in this diff.

Referred Code
internal sealed record SetViewportParameters(
    BrowsingContext Context,
    [property: JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] Optional<Viewport?> Viewport,
    [property: JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] Optional<double?> DevicePixelRatio) : Parameters;

public sealed class SetViewportOptions : CommandOptions
{
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public Optional<Viewport?> Viewport { get; set; }

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public Optional<double?> DevicePixelRatio { get; set; }
}

public readonly record struct Viewport(long Width, long Height);

Learn more about managing compliance generic rules or creating your own custom rules

  • [ ] Update <!-- /compliance --update_compliance=true -->
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

qodo-code-review[bot] avatar Nov 28 '25 06:11 qodo-code-review[bot]

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Improve nullability annotation for type safety
Suggestion Impact:The commit changed the signature to use a nullable out parameter (out T?), addressing the nullability concern, though it did not use the MaybeNullWhen(false) attribute as suggested.

code diff:

-    public bool TryGetValue(out T value)
+    public bool TryGetValue(out T? value)
     {
         value = _value;
         return HasValue;

Add the [MaybeNullWhen(false)] attribute to the out T value parameter in the
TryGetValue method to improve nullability correctness and type safety.

dotnet/src/webdriver/BiDi/Optional.cs [42-46]

-public bool TryGetValue(out T value)
+public bool TryGetValue([System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out T value)
 {
     value = _value;
     return HasValue;
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies a nullability issue in TryGetValue and proposes using the [MaybeNullWhen(false)] attribute, which is the standard C# pattern to improve static analysis and type safety.

Low
Learned
best practice
Improve exception clarity for unset Optional

Include the full property path in the exception to make diagnostics actionable,
and clarify the unset Optional write case. Use the writer.CurrentDepth/path if
available or a parameter name hint.

dotnet/src/webdriver/BiDi/Json/Converters/OptionalConverterFactory.cs [59-63]

 else
 {
-    throw new JsonException("This property should be annotated with [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]");
+    var message = "Attempted to serialize an unset Optional value. Annotate the property with [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] so unset optionals are omitted.";
+    throw new JsonException(message);
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: Relevant best practice - Guard external API/serialization behavior with clear validation and actionable error messages to avoid crashes and guide correct usage.

Low
  • [ ] Update <!-- /improve_multi --more_suggestions=true -->

qodo-code-review[bot] avatar Nov 28 '25 06:11 qodo-code-review[bot]