Path: blob/trunk/dotnet/test/webdriver/BiDi/SessionUnitTests.cs
11817 views
// <copyright file="SessionUnitTests.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.Text.Json;
using System.Text.Json.Nodes;
using OpenQA.Selenium.BiDi;
namespace OpenQA.Selenium.Tests.BiDi;
[Parallelizable(ParallelScope.All)]
[FixtureLifeCycle(LifeCycle.InstancePerTestCase)]
class SessionUnitTests
{
private IBiDi _bidi;
private FakeTransport _transport;
[SetUp]
public async Task SetUp()
{
_transport = new FakeTransport();
_bidi = await Selenium.BiDi.BiDi.ConnectAsync(new Uri("ws://fake"), opts => opts.UseTransport(() => _transport));
}
[TearDown]
public async Task TearDown()
{
await _bidi.DisposeAsync();
}
[Test]
public void ShouldRespectCommandTimeout()
{
Assert.That(
() => _bidi.StatusAsync(new() { Timeout = TimeSpan.FromMilliseconds(1) }),
Throws.InstanceOf<TaskCanceledException>());
}
[Test]
public void ShouldRespectCancellationToken()
{
using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(1));
Assert.That(
() => _bidi.StatusAsync(cancellationToken: cts.Token),
Throws.InstanceOf<TaskCanceledException>());
}
[Test]
public async Task ShouldHaveIdempotentDisposal()
{
await _bidi.DisposeAsync();
await _bidi.DisposeAsync();
}
[Test]
public async Task StatusCommandSendsCorrectMethod()
{
var status = await _bidi.StatusAsync()
.WithResponse(_transport, """{"ready":true,"message":"running"}""");
Assert.That(status.Message, Is.EqualTo("running"));
Assert.That(_transport.SentMessages, Has.Count.EqualTo(1));
using var doc = JsonDocument.Parse(_transport.SentMessages[0]);
Assert.That(doc.RootElement.GetProperty("method").GetString(), Is.EqualTo("session.status"));
}
[Test]
public async Task ErrorResponseThrowsBiDiException()
{
Assert.That(
() => _bidi.StatusAsync()
.WithErrorResponse(_transport, "invalid argument", "missing required field"),
Throws.InstanceOf<BiDiException>()
.With.Message.Contains("invalid argument"));
}
[Test]
public async Task CommandIdsAreSequential()
{
await _bidi.StatusAsync()
.WithResponse(_transport, """{"ready":true,"message":"first"}""");
await _bidi.StatusAsync()
.WithResponse(_transport, """{"ready":true,"message":"second"}""");
using var doc1 = JsonDocument.Parse(_transport.SentMessages[0]);
using var doc2 = JsonDocument.Parse(_transport.SentMessages[1]);
var id1 = doc1.RootElement.GetProperty("id").GetInt64();
var id2 = doc2.RootElement.GetProperty("id").GetInt64();
Assert.That(id2, Is.EqualTo(id1 + 1));
}
[Test]
public async Task StatusResultExposesAdditionalData()
{
var status = await _bidi.StatusAsync()
.WithResponse(_transport, """{"ready":true,"message":"running","foo":"value"}""");
Assert.That(status.AdditionalData["foo"].GetString(), Is.EqualTo("value"));
}
[Test]
public async Task StatusResultExposesAdditionalMessageData()
{
var status = await _bidi.StatusAsync()
.WithRawResponse(_transport, """{"foo":"topLevel","id":1,"type":"success","result":{"ready":true,"message":"running"}}""");
Assert.That(status.AdditionalMessageData["foo"].GetString(), Is.EqualTo("topLevel"));
}
[Test]
public async Task CommandAdditionalDataIsSerializedIntoParams()
{
await _bidi.StatusAsync(new()
{
AdditionalData = new JsonObject { ["foo"] = "bar" }
}).WithResponse(_transport, """{"ready":true,"message":"running"}""");
using var doc = JsonDocument.Parse(_transport.SentMessages[0]);
Assert.That(doc.RootElement.GetProperty("params").GetProperty("foo").GetString(), Is.EqualTo("bar"));
}
[Test]
public async Task CommandAdditionalMessageDataIsSerializedAsTopLevelFields()
{
await _bidi.StatusAsync(new()
{
AdditionalMessageData = """{"baz": "qux"}"""
}).WithResponse(_transport, """{"ready":true,"message":"running"}""");
using var doc = JsonDocument.Parse(_transport.SentMessages[0]);
Assert.That(doc.RootElement.GetProperty("baz").GetString(), Is.EqualTo("qux"));
Assert.That(doc.RootElement.GetProperty("params").EnumerateObject().Any(p => p.Name == "baz"), Is.False,
"AdditionalMessageData fields must be top-level, not inside params");
}
[Test]
public void CommandAdditionalDataMustBeJsonObject()
{
Assert.That(
() => new AdditionalData("""42"""),
Throws.InstanceOf<ArgumentException>()
.With.Message.Contains("Additional data must be a JSON object."));
}
[Test]
public async Task EventArgsExposesAdditionalData()
{
var stream = await _bidi.Script.RealmDestroyed.StreamAsync()
.WithResponse(_transport, """{"subscription":"sub-1"}""");
_transport.EnqueueEvent("script.realmDestroyed", """{"realm":"r-1","foo":"extra"}""");
var received = await stream.FirstAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(5));
Assert.That(received.AdditionalData["foo"].GetString(), Is.EqualTo("extra"));
await stream.DisposeAsync().WithResponse(_transport);
}
[Test]
public async Task EventArgsExposesAdditionalMessageData()
{
var stream = await _bidi.Script.RealmDestroyed.StreamAsync()
.WithResponse(_transport, """{"subscription":"sub-1"}""");
_transport.Enqueue("""{"type":"event","method":"script.realmDestroyed","params":{"realm":"r-1"},"bar":"topLevel"}""");
var received = await stream.FirstAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(5));
Assert.That(received.AdditionalMessageData["bar"].GetString(), Is.EqualTo("topLevel"));
await stream.DisposeAsync().WithResponse(_transport);
}
}