Skip to content
Merged
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
4 changes: 2 additions & 2 deletions CSF.Screenplay.Selenium/Actions/OpenUrl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ public class OpenUrl : IPerformable, ICanReport
public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default)
{
var ability = actor.GetAbility<BrowseTheWeb>();
ability.WebDriver.Url = uri.Uri.AbsoluteUri;
ability.WebDriver.Url = uri.Uri.ToString();
return default;
}

/// <inheritdoc/>
public ReportFragment GetReportFragment(Actor actor, IFormatsReportFragment formatter)
=> formatter.Format("{Actor} opens their browser at {UriName}: {Uri}", actor.Name, uri.Name, uri.Uri.AbsoluteUri);
=> formatter.Format("{Actor} opens their browser at {UriName}: {Uri}", actor.Name, uri.Name, uri.Uri.ToString());

/// <summary>
/// Initializes a new instance of the <see cref="OpenUrl"/> class with the specified URL.
Expand Down
28 changes: 27 additions & 1 deletion CSF.Screenplay.Selenium/NamedUri.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,32 @@ public sealed class NamedUri : IHasName
/// </summary>
public Uri Uri { get; }

/// <summary>
/// Gets a copy of the current named URI, except 'rebased' using the specified base URI.
/// </summary>
/// <remarks>
/// <para>
/// If the current <see cref="Uri"/> is <see cref="UriKind.Absolute"/> then this method has not effect and the named URI which is
/// returned is the unmodified current instance.
/// </para>
/// <para>
/// If the current Uri is not absolute, then the specified base URI is prepended to the current URI, serving as a base.
/// The new URI is then returned from this method. Note that this method will never result in the current instance being
/// mutated, at most it will only return a copy of the current instance, which has the newly-rebased URI.
/// </para>
/// </remarks>
/// <param name="baseUri">A new base URI</param>
/// <returns>A URI which might have been rebased onto the new base URI</returns>
public NamedUri RebaseTo(Uri baseUri)
{
if(baseUri == null) throw new ArgumentNullException(nameof(baseUri));

if (Uri.IsAbsoluteUri) return this;

var rebased = new Uri(baseUri, Uri);
return new NamedUri(rebased, Name);
}

/// <summary>
/// Initializes a new instance of the <see cref="NamedUri"/> class.
/// </summary>
Expand Down Expand Up @@ -63,6 +89,6 @@ public NamedUri(string uri, string name = null)
/// </summary>
/// <param name="uri">The URI to convert.</param>
/// <returns>A new <see cref="NamedUri"/> instance.</returns>
public static implicit operator NamedUri(string uri) => uri is null ? null : new NamedUri(new Uri(uri, UriKind.RelativeOrAbsolute));
public static implicit operator NamedUri(string uri) => uri is null ? null : new NamedUri(uri);
}
}
36 changes: 36 additions & 0 deletions CSF.Screenplay.Selenium/NamedUriExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

using System;

namespace CSF.Screenplay.Selenium
{
/// <summary>
/// Extension methods for <see cref="NamedUri"/>.
/// </summary>
public static class NamedUriExtensions
{
/// <summary>
/// Gets a copy of the current named URI, except 'rebased' using the specified base URI.
/// </summary>
/// <remarks>
/// <para>
/// If the current <see cref="Uri"/> is <see cref="UriKind.Absolute"/> then this method has not effect and the named URI which is
/// returned is the unmodified current instance.
/// </para>
/// <para>
/// If the current Uri is not absolute, then the specified base URI is prepended to the current URI, serving as a base.
/// The new URI is then returned from this method. Note that this method will never result in the current instance being
/// mutated, at most it will only return a copy of the current instance, which has the newly-rebased URI.
/// </para>
/// </remarks>
/// <param name="namedUri">The named URI to rebase</param>
/// <param name="baseUri">A new base URI</param>
/// <returns>A URI which might have been rebased onto the new base URI</returns>
public static NamedUri RebaseTo(this NamedUri namedUri, string baseUri)
{
if(namedUri == null) throw new ArgumentNullException(nameof(namedUri));
if(baseUri == null) throw new ArgumentNullException(nameof(baseUri));
return namedUri.RebaseTo(new Uri(baseUri, UriKind.Absolute));
}
}

}
9 changes: 3 additions & 6 deletions CSF.Screenplay.Selenium/Tasks/OpenUrlRespectingBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,11 @@ public class OpenUrlRespectingBase : IPerformable, ICanReport
/// <inheritdoc/>
public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default)
{
if(uri.Uri.IsAbsoluteUri)
return actor.PerformAsync(new OpenUrl(uri.Uri), cancellationToken);

if(!actor.TryGetAbility<UseABaseUri>(out var ability))
return actor.PerformAsync(new OpenUrl(uri.Uri), cancellationToken);
return actor.PerformAsync(new OpenUrl(uri), cancellationToken);

var absoluteUri = new Uri(ability.BaseUri, uri.Uri);
return actor.PerformAsync(new OpenUrl(absoluteUri), cancellationToken);
var rebased = uri.RebaseTo(ability.BaseUri);
return actor.PerformAsync(new OpenUrl(rebased), cancellationToken);
}

/// <inheritdoc/>
Expand Down
60 changes: 60 additions & 0 deletions Tests/CSF.Screenplay.Selenium.Tests/NamedUriTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;
using CSF.Screenplay.Selenium;

namespace CSF.Screenplay.Selenium;

[TestFixture, Parallelizable]
public class NamedUriTests
{
[Test]
public void RebaseToShouldReturnARebasedUriIfItIsNotAbsolute()
{
var sut = new NamedUri("test.html", "name");
var rebased = sut.RebaseTo("https://example.com");
Assert.That(rebased.Uri.ToString(), Is.EqualTo("https://example.com/test.html"));
}

[Test]
public void RebaseToShouldNotAlterTheUriIfItIsAbsolute()
{
var sut = new NamedUri("https://foobar.example.com/test.html", "name");
var rebased = sut.RebaseTo("https://example.com");
Assert.That(rebased.Uri.ToString(), Is.EqualTo("https://foobar.example.com/test.html"));
}

[Test]
public void RebaseToShouldReturnAUriWithTheSameName()
{
var sut = new NamedUri("test.html", "name");
var rebased = sut.RebaseTo("https://example.com");
Assert.That(rebased.Name, Is.EqualTo("name"));
}

[Test]
public void RebaseToShouldThrowIfUriIsNull()
{
var sut = new NamedUri("test.html", "name");
Assert.That(() => sut.RebaseTo(null), Throws.ArgumentNullException);
}

[Test]
public void RebaseToShouldThrowIfUriIsNullString()
{
var sut = new NamedUri("test.html", "name");
Assert.That(() => sut.RebaseTo((string?) null), Throws.ArgumentNullException);
}

[Test]
public void ImplicitCastFromUriShouldCreateANamedUri()
{
NamedUri uri = new Uri("https://example.com/foo.html");
Assert.That(uri.Uri.ToString(), Is.EqualTo("https://example.com/foo.html"));
}

[Test]
public void ImplicitCastFromStringShouldCreateANamedUri()
{
NamedUri uri = "https://example.com/foo.html";
Assert.That(uri.Uri.ToString(), Is.EqualTo("https://example.com/foo.html"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System;
using CSF.Extensions.WebDriver;
using CSF.Extensions.WebDriver.Factories;
using CSF.Screenplay.Actors;
using CSF.Screenplay.Reporting;
using CSF.Screenplay.Selenium.Actions;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using OpenQA.Selenium;

namespace CSF.Screenplay.Selenium.Tasks;

[TestFixture, Parallelizable]
public class OpenUrlRespectingBaseTests
{
[Test, AutoMoqData]
public async Task TheActionCreatedByThisTaskShouldContainTheCorrectReport(IWebDriver driver, DriverOptions options)
{
var actor = new Actor("Anthony", Guid.NewGuid());
IPerformable? performable = null;

void OnPerform(object? sender, PerformableEventArgs ev) => performable = (IPerformable)ev.Performable;

var namedUri = new NamedUri("test.html", "the test page");
var baseUri = "https://example.com";
actor.IsAbleTo(new UseABaseUri(new Uri(baseUri, UriKind.Absolute)));
actor.IsAbleTo(new BrowseTheWeb(Mock.Of<IGetsWebDriver>(x => x.GetDefaultWebDriver(It.IsAny<Action<DriverOptions>>()) == new WebDriverAndOptions(driver, options))));
var sut = new OpenUrlRespectingBase(namedUri);
var valueFormatterProvider = new ValueFormatterProvider(new ServiceCollection().AddTransient<ToStringFormatter>().BuildServiceProvider(),
new ValueFormatterRegistry { typeof(ToStringFormatter) });

var formatter = new ReportFragmentFormatter(new ReportFormatCreator(), valueFormatterProvider);

actor.EndPerformable += OnPerform;
try
{
await sut.PerformAsAsync(actor);

Assert.Multiple(() =>
{
Assert.That(performable, Is.InstanceOf<OpenUrl>(), "Performable is correct type");
Assert.That(((OpenUrl) performable!).GetReportFragment(actor, formatter).ToString(),
Is.EqualTo("Anthony opens their browser at the test page: https://example.com/test.html"),
"The report is correct");
});
}
finally
{
actor.EndPerformable -= OnPerform;
}
}

[Test, AutoMoqData]
public async Task TheActionCreatedByThisTaskShouldContainTheCorrectReportWhenTheActorDoesNotHaveABaseUrl(IWebDriver driver, DriverOptions options)
{
var actor = new Actor("Anthony", Guid.NewGuid());
IPerformable? performable = null;

void OnPerform(object? sender, PerformableEventArgs ev) => performable = (IPerformable)ev.Performable;

var namedUri = new NamedUri("test.html", "the test page");
actor.IsAbleTo(new BrowseTheWeb(Mock.Of<IGetsWebDriver>(x => x.GetDefaultWebDriver(It.IsAny<Action<DriverOptions>>()) == new WebDriverAndOptions(driver, options))));
var sut = new OpenUrlRespectingBase(namedUri);
var valueFormatterProvider = new ValueFormatterProvider(new ServiceCollection().AddTransient<ToStringFormatter>().BuildServiceProvider(),
new ValueFormatterRegistry { typeof(ToStringFormatter) });

var formatter = new ReportFragmentFormatter(new ReportFormatCreator(), valueFormatterProvider);

actor.EndPerformable += OnPerform;
try
{
await sut.PerformAsAsync(actor);

Assert.Multiple(() =>
{
Assert.That(performable, Is.InstanceOf<OpenUrl>(), "Performable is correct type");
Assert.That(((OpenUrl) performable!).GetReportFragment(actor, formatter).ToString(),
Is.EqualTo("Anthony opens their browser at the test page: test.html"),
"The report is correct");
});
}
finally
{
actor.EndPerformable -= OnPerform;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class ReportFormatCreatorTests
[TestCase("}", "}")]
[TestCase("{foo} {bar", "{0} {bar")]
[TestCase("{{{{foo}}}}", "{{{{foo}}}}")]
[TestCase("{Actor} opens their browser at {UriName}: {Uri}", "{0} opens their browser at {1}: {2}")]
public void GetReportFormatShouldReturnTheCorrectFormattedTemplate(string original, string expected)
{
var sut = new ReportFormatCreator();
Expand All @@ -37,6 +38,7 @@ public void GetReportFormatShouldReturnTheCorrectFormattedTemplate(string origin
[TestCase("}", "", "", "")]
[TestCase("{foo} {bar", "foo", "", "")]
[TestCase("{{{{foo}}}}", "", "", "")]
[TestCase("{Actor} opens their browser at {UriName}: {Uri}", "Actor", "UriName", "Uri")]
public void GetReportFormatShouldReturnTheCorrectObjectNames(string template, string name1, string name2, string name3)
{
var expected = new[] { name1, name2, name3 }.Where(x => x.Length > 0).ToList();
Expand Down
Loading