Skip to content

Commit 05675b6

Browse files
authored
Merge pull request #329 from csf-dev/242-adapter-type-names
Resolve #242 - more useful type names
2 parents 264538d + 478b843 commit 05675b6

13 files changed

Lines changed: 70 additions & 6 deletions
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
namespace CSF.Screenplay
2+
{
3+
/// <summary>
4+
/// An object which can provide a custom human-readable .NET type name.
5+
/// </summary>
6+
/// <remarks>
7+
/// <para>
8+
/// This is particularly important when reporting, particularly when writing the name of the performable type
9+
/// into a report. Some performables may be written using the adapter or decorator patterns, in which a general-use
10+
/// class wraps a specific class which implements a subset of a performable. Using <see cref="System.Type.GetType()"/>
11+
/// will yield the type name of the general-use 'outer' class, which is usually not very useful on its own.
12+
/// </para>
13+
/// <para>
14+
/// If general-use performables, such as adapters, implement this interface, then they can return more useful human-readable
15+
/// type names to the consuming logic, making use of their inner/wrapped implementation type.
16+
/// </para>
17+
/// </remarks>
18+
public interface IHasCustomTypeName
19+
{
20+
/// <summary>
21+
/// Gets a human-readable name of the type of the current instance.
22+
/// </summary>
23+
/// <remarks>
24+
/// <para>See the remarks on <see cref="IHasCustomTypeName"/>; this does not need to be the same as <see cref="System.Type.GetType()"/>.</para>
25+
/// </remarks>
26+
/// <returns>A human-readable name of the .NET type of the current instance, which could (for example) be
27+
/// qualified with additional context, such as a wrapped implementation.</returns>
28+
string GetHumanReadableTypeName();
29+
}
30+
}

CSF.Screenplay.Selenium/Actions/SingleElementPerformableAdapter.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace CSF.Screenplay.Selenium.Actions
1818
/// to fetch it using the WebDriver more than once. As such, instances of this adapter (like all performables) should not be re-used.
1919
/// </para>
2020
/// </remarks>
21-
public class SingleElementPerformableAdapter : IPerformable, ICanReport
21+
public class SingleElementPerformableAdapter : IPerformable, ICanReport, IHasCustomTypeName
2222
{
2323
readonly ISingleElementPerformable performable;
2424
readonly ITarget target;
@@ -38,6 +38,10 @@ public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellatio
3838
return performable.PerformAsAsync(actor, actor.GetAbility<BrowseTheWeb>().WebDriver, lazyElement, cancellationToken);
3939
}
4040

41+
/// <inheritdoc/>
42+
public string GetHumanReadableTypeName()
43+
=> $"{performable.GetType().FullName}, via {nameof(SingleElementPerformableAdapter)}";
44+
4145
/// <summary>
4246
/// Initializes a new instance of the <see cref="SingleElementPerformableAdapter"/> class with the specified performable and target.
4347
/// </summary>

CSF.Screenplay.Selenium/Questions/ElementCollectionPerformableWithResultAdapter.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace CSF.Screenplay.Selenium.Questions
1919
/// to fetch it using the WebDriver more than once. As such, instances of this adapter (like all performables) should not be re-used.
2020
/// </para>
2121
/// </remarks>
22-
public class ElementCollectionPerformableWithResultAdapter<TResult> : IPerformableWithResult<IReadOnlyList<TResult>>, ICanReport
22+
public class ElementCollectionPerformableWithResultAdapter<TResult> : IPerformableWithResult<IReadOnlyList<TResult>>, ICanReport, IHasCustomTypeName
2323
{
2424
readonly IElementCollectionPerformableWithResult<TResult> performable;
2525
readonly ITarget target;
@@ -39,6 +39,10 @@ public ValueTask<IReadOnlyList<TResult>> PerformAsAsync(ICanPerform actor, Cance
3939
return performable.PerformAsAsync(actor, actor.GetAbility<BrowseTheWeb>().WebDriver, lazyElements, cancellationToken);
4040
}
4141

42+
/// <inheritdoc/>
43+
public string GetHumanReadableTypeName()
44+
=> $"{performable.GetHumanReadableTypeName()}, via {nameof(ElementCollectionPerformableWithResultAdapter)}";
45+
4246
/// <summary>
4347
/// Initializes a new instance of the <see cref="ElementCollectionPerformableWithResultAdapter{TResult}"/> class with the specified performable and target.
4448
/// </summary>

CSF.Screenplay.Selenium/Questions/ElementCollectionQuery.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ public ReportFragment GetReportFragment(Actor actor, Lazy<SeleniumElementCollect
5757
public ValueTask<IReadOnlyList<TResult>> PerformAsAsync(ICanPerform actor, IWebDriver webDriver, Lazy<SeleniumElementCollection> elements, CancellationToken cancellationToken = default)
5858
=> new ValueTask<IReadOnlyList<TResult>>(elements.Value.Select(query.GetValue).ToList());
5959

60+
/// <inheritdoc/>
61+
public string GetHumanReadableTypeName()
62+
=> $"{query.GetType().FullName}, via {nameof(ElementCollectionQuery)}";
63+
6064
/// <summary>
6165
/// Initializes a new instance of the <see cref="ElementCollectionQuery{TResult}"/> class with the specified query.
6266
/// </summary>

CSF.Screenplay.Selenium/Questions/FindElement.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ Lazy<IHasSearchContext> GetLazyElement(ICanPerform actor)
8585
return new Lazy<IHasSearchContext>(() => searchContext);
8686
}
8787

88+
/// <inheritdoc/>
89+
public string GetHumanReadableTypeName() => GetType().FullName;
90+
8891
/// <summary>
8992
/// Initializes a new instance of the <see cref="FindElement"/> class.
9093
/// </summary>

CSF.Screenplay.Selenium/Questions/FindElements.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ Lazy<IHasSearchContext> GetLazyElement(ICanPerform actor)
8484
return new Lazy<IHasSearchContext>(() => searchContext);
8585
}
8686

87+
/// <inheritdoc/>
88+
public string GetHumanReadableTypeName() => GetType().FullName;
89+
8790
/// <summary>
8891
/// Initializes a new instance of the <see cref="FindElements"/> class.
8992
/// </summary>

CSF.Screenplay.Selenium/Questions/GetShadowRootNatively.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public class GetShadowRootNatively : ISingleElementPerformableWithResult<Element
3434
public ReportFragment GetReportFragment(Actor actor, Lazy<SeleniumElement> element, IFormatsReportFragment formatter)
3535
=> formatter.Format("{Actor} gets the Shadow Root node from {Element} using the native Selenium technique", actor, element.Value);
3636

37+
/// <inheritdoc/>
38+
public string GetHumanReadableTypeName() => GetType().FullName;
39+
3740
/// <inheritdoc/>
3841
public ValueTask<Elements.ShadowRoot> PerformAsAsync(ICanPerform actor, IWebDriver webDriver, Lazy<SeleniumElement> element, CancellationToken cancellationToken = default)
3942
{

CSF.Screenplay.Selenium/Questions/GetShadowRootWithJavaScript.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ public class GetShadowRootWithJavaScript : ISingleElementPerformableWithResult<E
3535
public ReportFragment GetReportFragment(Actor actor, Lazy<SeleniumElement> element, IFormatsReportFragment formatter)
3636
=> formatter.Format("{Actor} gets the Shadow Root node from {Element} using JavaScript", actor, element.Value);
3737

38+
/// <inheritdoc/>
39+
public string GetHumanReadableTypeName() => GetType().FullName;
40+
3841
/// <inheritdoc/>
3942
public async ValueTask<Elements.ShadowRoot> PerformAsAsync(ICanPerform actor, IWebDriver webDriver, Lazy<SeleniumElement> element, CancellationToken cancellationToken = default)
4043
{

CSF.Screenplay.Selenium/Questions/IElementCollectionPerformableWithResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ namespace CSF.Screenplay.Selenium.Questions
2121
/// <see cref="SingleElementPerformableWithResultAdapter{TResult}"/>.
2222
/// </para>
2323
/// </remarks>
24-
public interface IElementCollectionPerformableWithResult<TResult> : ICanReportForElements
24+
public interface IElementCollectionPerformableWithResult<TResult> : ICanReportForElements, IHasCustomTypeName
2525
{
2626
/// <summary>
2727
/// Counterpart to <see cref="IPerformableWithResult{TResult}.PerformAsAsync(ICanPerform, CancellationToken)"/> except that this method also offers

CSF.Screenplay.Selenium/Questions/ISingleElementPerformableWithResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace CSF.Screenplay.Selenium.Questions
2020
/// <see cref="SingleElementPerformableWithResultAdapter{TResult}"/>.
2121
/// </para>
2222
/// </remarks>
23-
public interface ISingleElementPerformableWithResult<TResult> : ICanReportForElement
23+
public interface ISingleElementPerformableWithResult<TResult> : ICanReportForElement, IHasCustomTypeName
2424
{
2525
/// <summary>
2626
/// Counterpart to <see cref="IPerformableWithResult{TResult}.PerformAsAsync(ICanPerform, CancellationToken)"/> except that this method also offers

0 commit comments

Comments
 (0)