Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,19 @@
// under the License.
// </copyright>

using OpenQA.Selenium.BiDi.Json.Converters;
using System.Text.Json.Serialization;

namespace OpenQA.Selenium.BiDi.BrowsingContext;

internal sealed class SetViewportCommand(SetViewportParameters @params)
: Command<SetViewportParameters, SetViewportResult>(@params, "browsingContext.setViewport");

internal sealed record SetViewportParameters(BrowsingContext Context, Viewport? Viewport, double? DevicePixelRatio) : Parameters;
internal sealed record SetViewportParameters(BrowsingContext Context, [property: JsonConverter(typeof(OptionalConverter<Viewport?>))] Optional<Viewport?>? Viewport, double? DevicePixelRatio) : Parameters;

public sealed class SetViewportOptions : CommandOptions
{
public Viewport? Viewport { get; set; }
public Optional<Viewport?>? Viewport { get; set; }

public double? DevicePixelRatio { get; set; }
}
Expand Down
51 changes: 51 additions & 0 deletions dotnet/src/webdriver/BiDi/Json/Converters/OptionalConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// <copyright file="OptionalConverter.cs" company="Selenium Committers">
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// </copyright>

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace OpenQA.Selenium.BiDi.Json.Converters;

public sealed class OptionalConverter<T> : JsonConverter<Optional<T>>
{
public override Optional<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
{
reader.Read(); // consume null
return new Optional<T>(default!);
}

T value = JsonSerializer.Deserialize<T>(ref reader, options)!;
return new Optional<T>(value);
}

public override void Write(Utf8JsonWriter writer, Optional<T> value, JsonSerializerOptions options)
{
if (value.TryGetValue(out var optionalValue))
{
JsonSerializer.Serialize(writer, optionalValue, options);
}
else
{
writer.WriteNullValue();
}
}
}
47 changes: 47 additions & 0 deletions dotnet/src/webdriver/BiDi/Optional.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// <copyright file="Optional.cs" company="Selenium Committers">
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// </copyright>

using System;

namespace OpenQA.Selenium.BiDi;

public readonly record struct Optional<T>
Copy link
Member Author

Choose a reason for hiding this comment

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

readonly struct, do we see issues? There are limitations of its use. I propose to be safe and don't use it.

{
private readonly T _value;
public bool HasValue { get; }

public T Value => HasValue
? _value
: throw new InvalidOperationException("Optional has no value. Check IsSet first.");

public Optional(T value)
{
_value = value;
HasValue = true;
}

public bool TryGetValue(out T value)
{
value = _value;
return HasValue;
}

// implicit conversion from T -> Optional<T>
public static implicit operator Optional<T>(T value) => new(value);
}
31 changes: 29 additions & 2 deletions dotnet/test/common/BiDi/BrowsingContext/BrowsingContextTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,13 +298,40 @@ public async Task CanCaptureScreenshotOfElement()
[Test]
public async Task CanSetViewport()
{
await context.SetViewportAsync(new() { Viewport = new(250, 300) });
Task<int> GetWidthAsync() => context.Script.EvaluateAsync<int>("window.innerWidth", false);
Task<int> GetHeightAsync() => context.Script.EvaluateAsync<int>("window.innerHeight", false);

var defaultWidth = await GetWidthAsync();
var defaultHeight = await GetHeightAsync();

await context.SetViewportAsync(new() { Viewport = new Viewport(250, 300) });

Assert.That(await GetWidthAsync(), Is.EqualTo(250));
Assert.That(await GetHeightAsync(), Is.EqualTo(300));

await context.SetViewportAsync(new() { Viewport = new Viewport(250, 300) });
await context.SetViewportAsync(); // Sends nothing

Assert.That(await GetWidthAsync(), Is.EqualTo(250));
Assert.That(await GetHeightAsync(), Is.EqualTo(300));

await context.SetViewportAsync(new() { Viewport = new Viewport(250, 300) });
await context.SetViewportAsync(new() { Viewport = default }); // Sends nothing

Assert.That(await GetWidthAsync(), Is.EqualTo(250));
Assert.That(await GetHeightAsync(), Is.EqualTo(300));

await context.SetViewportAsync(new() { Viewport = new Viewport(250, 300) });
await context.SetViewportAsync(new() { Viewport = default(Viewport?) }); // Explicitly sends "null", resetting to default

Assert.That(await GetWidthAsync(), Is.EqualTo(defaultWidth));
Assert.That(await GetHeightAsync(), Is.EqualTo(defaultHeight));
}

[Test]
public async Task CanSetViewportWithDevicePixelRatio()
{
await context.SetViewportAsync(new() { Viewport = new(250, 300), DevicePixelRatio = 5 });
await context.SetViewportAsync(new() { Viewport = new Viewport(250, 300), DevicePixelRatio = 5 });
}

[Test]
Expand Down