diff --git a/CSF.Screenplay.Abstractions/Performables/IProvidesTimeSpan.cs b/CSF.Screenplay.Abstractions/Performables/IProvidesTimeSpan.cs
deleted file mode 100644
index 2830aad0..00000000
--- a/CSF.Screenplay.Abstractions/Performables/IProvidesTimeSpan.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-namespace CSF.Screenplay.Performables
-{
- ///
- /// A type which may provide a .
- ///
- ///
- ///
- /// Many performables make use of time; this interface provides a common abstraction for
- /// objects that provide time spans.
- ///
- ///
- ///
- ///
- public interface IProvidesTimeSpan
- {
- ///
- /// Gets the which is exposed by the current instance.
- ///
- /// The time span
- TimeSpan GetTimeSpan();
- }
-}
diff --git a/CSF.Screenplay.Abstractions/Performables/TimeSpanBuilder.cs b/CSF.Screenplay.Abstractions/Performables/TimeSpanBuilder.cs
deleted file mode 100644
index 7002687b..00000000
--- a/CSF.Screenplay.Abstractions/Performables/TimeSpanBuilder.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-namespace CSF.Screenplay.Performables
-{
- ///
- /// Static helper class for creating instances of .
- ///
- ///
- ///
- /// See the documentation for for more information
- /// about how this class is to be used.
- ///
- ///
- public static class TimeSpanBuilder
- {
- ///
- /// Creates and returns a which can hold time span information
- /// and then continue the building process associated with the other builder.
- ///
- ///
- ///
- /// See the documentation for for more information
- /// about how this method is to be used.
- ///
- ///
- /// An instance of another performable builder
- /// The absolute time span value, without any units
- /// The type of the other performable builder
- public static TimeSpanBuilder Create(TOtherBuilder otherBuilder, int value) where TOtherBuilder : class
- => new TimeSpanBuilder(otherBuilder, value);
- }
-}
diff --git a/CSF.Screenplay.Abstractions/Performables/TimeSpanBuilder.generic.cs b/CSF.Screenplay.Abstractions/Performables/TimeSpanBuilder.generic.cs
deleted file mode 100644
index 7a165610..00000000
--- a/CSF.Screenplay.Abstractions/Performables/TimeSpanBuilder.generic.cs
+++ /dev/null
@@ -1,146 +0,0 @@
-using System;
-
-namespace CSF.Screenplay.Performables
-{
- ///
- /// A supplementary builder type which enables the collection of instances.
- ///
- ///
- ///
- /// When consuming it is recommended to use
- /// to create them.
- /// A commonly-used 'parameter' which may be specified in builders is 'an amount of time', IE a .
- ///
- ///
- /// This builder is intended to supplement another builder, for the purpose of specifying an amount of time.
- /// The 'other' builder is passed as a constructor parameter to this builder, along with an absolute amount.
- /// The consumer should then execute one of the methods of this type, which selects the unit of time and thus
- /// determines the value.
- /// The method which determines the units then returns that other builder instance, allowing the building process
- /// to continue with that other builder.
- ///
- ///
- /// Whilst it is possible to create instances of this type via its public constructor, it is often easier to create
- /// instances using the static class.
- ///
- ///
- ///
- ///
- /// The example below shows how the time span builder is intended to be used. It is consumed from within another builder,
- /// which needs to include a developer-configurable time span.
- /// See for more information
- /// about the makeup of the EatLunchPerformableBuilder.
- ///
- ///
- /// public class EatLunchPerformableBuilder : IGetsPerformable
- /// {
- /// IProvidesTimeSpan? timeSpanBuilder;
- ///
- /// protected string? FoodName { get; init; }
- ///
- /// IPerformable IGetsPerformable.GetPerformable()
- /// => new EatLunch(FoodName, timeSpanBuilder?.GetTimeSpan() ?? TimeSpan.Zero);
- ///
- /// public TimeSpanBuilder<EatLunchPerformableBuilder> For(int howMany)
- /// {
- /// var builder = TimeSpanBuilder.Create(this, howMany);
- /// timeSpanBuilder = builder;
- /// return builder;
- /// }
- ///
- /// public static EatLunchPerformableBuilder Eat(string foodName) => new EatLunchPerformableBuilder() { FoodName = foodName };
- /// }
- ///
- ///
- /// The sample builder above would be used to build an instance of a (fictitious) EachLunch performable, which derives from
- /// . The fictitious performable requires two parameters; the name of the food being eaten for lunch
- /// and how long the lunch break lasts. The time span builder is used for that second parameter.
- /// A consumer which uses this builder in an , or another ,
- /// might consume it as follows.
- ///
- ///
- /// using static EatLunchPerformableBuilder;
- ///
- /// // ...
- ///
- /// actor.PerformAsync(Eat("Sandwiches").For(30).Minutes(), cancellationToken);
- ///
- ///
- /// A note for developers with access to the source code for this library.
- /// There is a small integration test which sets up and exercises the example above; it is named TimeSpanBuilderTests.
- ///
- ///
- /// The builder type for which this builder will supplement
- ///
- public class TimeSpanBuilder : IProvidesTimeSpan where TOtherBuilder : class
- {
- readonly TOtherBuilder otherBuilder;
- readonly int value;
- TimeSpan timespan;
-
- TimeSpan IProvidesTimeSpan.GetTimeSpan() => timespan;
-
- ///
- /// Configures the contained time span to be measured in milliseconds, then returns the contained builder.
- ///
- public TOtherBuilder Milliseconds()
- {
- timespan = TimeSpan.FromMilliseconds(value);
- return otherBuilder;
- }
-
- ///
- /// Configures the contained time span to be measured in seconds, then returns the contained builder.
- ///
- public TOtherBuilder Seconds()
- {
- timespan = TimeSpan.FromSeconds(value);
- return otherBuilder;
- }
-
- ///
- /// Configures the contained time span to be measured in minutes, then returns the contained builder.
- ///
- public TOtherBuilder Minutes()
- {
- timespan = TimeSpan.FromMinutes(value);
- return otherBuilder;
- }
-
- ///
- /// Configures the contained time span to be measured in hours, then returns the contained builder.
- ///
- public TOtherBuilder Hours()
- {
- timespan = TimeSpan.FromHours(value);
- return otherBuilder;
- }
-
- ///
- /// Configures the contained time span to be measured in days, then returns the contained builder.
- ///
- public TOtherBuilder Days()
- {
- timespan = TimeSpan.FromDays(value);
- return otherBuilder;
- }
-
- ///
- /// Initializes a new instance of .
- ///
- /// The absolute value of time, but without units
- /// The other builder which shall be supplemented by this
- /// If is .
- /// If is less than zero.
- public TimeSpanBuilder(TOtherBuilder otherBuilder, int value)
- {
- if (otherBuilder == null)
- throw new ArgumentNullException(nameof(otherBuilder));
- if (value < 0)
- throw new ArgumentOutOfRangeException(nameof(value), value, "Value must not be negative");
-
- this.value = value;
- this.otherBuilder = otherBuilder;
- }
- }
-}
diff --git a/CSF.Screenplay.Docs/docs/extendingScreenplay/ScreenplayExtensions.md b/CSF.Screenplay.Docs/docs/extendingScreenplay/ScreenplayExtensions.md
index 5079a1a5..47ede299 100644
--- a/CSF.Screenplay.Docs/docs/extendingScreenplay/ScreenplayExtensions.md
+++ b/CSF.Screenplay.Docs/docs/extendingScreenplay/ScreenplayExtensions.md
@@ -3,18 +3,18 @@
Screenplay ships [with a small number of Abilities and performables] but it is designed to be extended with new ones.
New [Abilities], [Actions] and [Questions] extend [Screenplay] by allowing [Actors] to interact with new APIs, services and libraries.
-Broadly-speaking to extend Screenplay in this way you must:
+Broadly-speaking, to extend Screenplay in this way you must:
* Write one or more new ability types which provide access to the API of the service or library with which you'd like to interact
* Write one or more Action and/or Question [Performables] which make use of that ability
-[with a small number of Abilities and performables]: ../performables/index.md
+[with a small number of Abilities and performables]: ../extensions/index.md#built-in-abilities-and-performables
[Screenplay]: xref:CSF.Screenplay.Screenplay
## Writing abilities
Recall that [Abilities] represent capabilities & dependencies granted to or associated with [Actors].
-It is normal for developers to want to write new Ability classes in order to provide capabilities/dependencies which are not yet catered-for.
+It is normal for developers to want to write new Ability classes in order to provide capabilities/dependencies which are not yet catered-for.
Ability classes do not _need to derive_ from any particular base type, although it is strongly recommended that they implement [`ICanReport`].
Ability classes [may constructor-inject dependencies] and should declare whatever API is appropriate.
diff --git a/CSF.Screenplay.Docs/docs/extensions/index.md b/CSF.Screenplay.Docs/docs/extensions/index.md
index 8b9f69b1..d3d3c19f 100644
--- a/CSF.Screenplay.Docs/docs/extensions/index.md
+++ b/CSF.Screenplay.Docs/docs/extensions/index.md
@@ -1,8 +1,30 @@
# Screenplay extensions
-This page lists the officially-supported **[Screenplay Extensions]**.
+Most of the useful functionality of Screenplay comes from **[Screenplay Extensions]**.
+Extensions typically provide [Abilities], [Performables] and [Builders] which offer functionality relevant to that extension's technology.
+
+[Screenplay Extensions]: ../../glossary/Extension.md
+[Abilities]: ../../glossary/Ability.md
+[Performables]: ../../glossary/Performable.md
+[Builders]: ../builderPattern/index.md
+
+## Officially-supported extensions
+
+These extensions are authored and maintained alongside the main Screenplay library.
+Developers are encouraged and welcomed to create their own extensions, too.
* **[Selenium](selenium/index.md)**: _remote control Web Browsers using Selenium Web Driver_
* **[Web APIs](webApis/index.md)**: _communicate with Web APIs with an HTTP Client_
-[Screenplay Extensions]: ../../glossary/Extension.md
+## Built-in abilities and performables
+
+Screenplay offers a very small number of abilities and performables within the base library; these do not require the installation of an extension.
+
+### Using a Stopwatch
+
+When an actor needs to keep precise track of time, you may give them the [`UseAStopwatch`] ability.
+Actors with this ability may use Actions and Questions which relate to use of the stopwatch.
+These are all accessible from the builder class [`StopwatchBuilder`].
+
+[`UseAStopwatch`]: xref:CSF.Screenplay.Abilities.UseAStopwatch
+[`StopwatchBuilder`]: xref:CSF.Screenplay.Performables.StopwatchBuilder
diff --git a/CSF.Screenplay.Docs/docs/performables/index.md b/CSF.Screenplay.Docs/docs/performables/index.md
deleted file mode 100644
index 1a8734d5..00000000
--- a/CSF.Screenplay.Docs/docs/performables/index.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# Performables
-
-Screenplay comes with a few pre-created [Abilities], [Performables] and [Builders], for common tasks.
-Some of these require the installation of additional **NuGet packages**.
-
-[Abilities]: ../../glossary/Ability.md
-[Performables]: ../../glossary/Performable.md
-[Builders]: ../builderPattern/index.md
-
-## Using a Stopwatch
-
-When an actor needs to keep precise track of time, you may give them the [`UseAStopwatch`] ability.
-Actors with this ability may use Actions and Questions which relate to use of the stopwatch.
-These are all accessible from the builder class [`StopwatchBuilder`].
-
-[`UseAStopwatch`]: xref:CSF.Screenplay.Abilities.UseAStopwatch
-[`StopwatchBuilder`]: xref:CSF.Screenplay.Performables.StopwatchBuilder
-
-## TimeSpan builder
-
-The [`TimeSpanBuilder`] is not a complete performable builder; it is intended to supplement other builders such as those of your own design.
-It handles a commonly-used aspect of building performables in a reusable manner.
-
-[`TimeSpanBuilder`]: xref:CSF.Screenplay.Performables.TimeSpanBuilder`1
\ No newline at end of file
diff --git a/CSF.Screenplay.Docs/docs/performables/toc.yml b/CSF.Screenplay.Docs/docs/performables/toc.yml
deleted file mode 100644
index b34ca748..00000000
--- a/CSF.Screenplay.Docs/docs/performables/toc.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-- name: Introduction
- href: index.md
-- name: Stopwatch
- uid: CSF.Screenplay.Performables.StopwatchBuilder
-- name: TimeSpan builder
- uid: CSF.Screenplay.Performables.TimeSpanBuilder`1
\ No newline at end of file
diff --git a/CSF.Screenplay.Docs/docs/toc.yml b/CSF.Screenplay.Docs/docs/toc.yml
index 0d090c7b..200ba62a 100644
--- a/CSF.Screenplay.Docs/docs/toc.yml
+++ b/CSF.Screenplay.Docs/docs/toc.yml
@@ -66,8 +66,6 @@
items:
- name: Assets
href: Assets.md
- - name: Performables
- href: performables/toc.yml
- name: Best practices
items:
- name: When to use for tests
diff --git a/CSF.Screenplay.Docs/glossary/Extension.md b/CSF.Screenplay.Docs/glossary/Extension.md
index dd5b892c..644c6062 100644
--- a/CSF.Screenplay.Docs/glossary/Extension.md
+++ b/CSF.Screenplay.Docs/glossary/Extension.md
@@ -4,14 +4,15 @@ The Screenplay library doesn't do much on its own.
The packages CSF.Screenplay.Abstractions and CSF.Screenplay offer very little in the way of [Abilities], [Actions] or [Questions].
Since these are the building blocks for writing [Tasks] and [Performances], developers won't get very far without installing one or more extensions.
-There are [a few extensions available] which are maintained by the authors of CSF.Screenplay.
+A few extensions are authored and maintained alongside the CSF.Screenplay library.
+These are listed on **[the extensions documentation page]**.
[Abilities]: Ability.md
[Actions]: Action.md
[Questions]: Question.md
[Tasks]: Task.md
[Performances]: xref:CSF.Screenplay.IPerformance
-[a few extensions available]: ../docs/extensions/index.md
+[the extensions documentation page]: ../docs/extensions/index.md
## Can't see what you need?
diff --git a/Tests/CSF.Screenplay.NUnit.Tests/TestWithDescription.cs b/Tests/CSF.Screenplay.NUnit.Tests/TestWithDescription.cs
index 1e31d875..1ab677e2 100644
--- a/Tests/CSF.Screenplay.NUnit.Tests/TestWithDescription.cs
+++ b/Tests/CSF.Screenplay.NUnit.Tests/TestWithDescription.cs
@@ -29,10 +29,8 @@ public void SecondTest(IPerformance performance)
static void AssertThatPerformanceHasCorrectState(IPerformance performance, IdentifierAndName[] expectedNamingHierarchy)
{
- Assert.Multiple(() =>
- {
- Assert.That(performance, Is.Not.Null, "Performance must not be null");
- Assert.That(performance.NamingHierarchy, Is.EqualTo(expectedNamingHierarchy), "Performance naming hierarchy is correct");
- });
+ using var scope = Assert.EnterMultipleScope();
+ Assert.That(performance, Is.Not.Null, "Performance must not be null");
+ Assert.That(performance.NamingHierarchy, Is.EqualTo(expectedNamingHierarchy), "Performance naming hierarchy is correct");
}
}
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.NUnit.Tests/TestWithoutDescription.cs b/Tests/CSF.Screenplay.NUnit.Tests/TestWithoutDescription.cs
index 475afb64..1c4c1b8b 100644
--- a/Tests/CSF.Screenplay.NUnit.Tests/TestWithoutDescription.cs
+++ b/Tests/CSF.Screenplay.NUnit.Tests/TestWithoutDescription.cs
@@ -29,10 +29,8 @@ public void SecondTest(IPerformance performance)
static void AssertThatPerformanceHasCorrectState(IPerformance performance, IdentifierAndName[] expectedNamingHierarchy)
{
- Assert.Multiple(() =>
- {
- Assert.That(performance, Is.Not.Null, "Performance must not be null");
- Assert.That(performance.NamingHierarchy, Is.EqualTo(expectedNamingHierarchy), "Performance naming hierarchy is correct");
- });
+ using var scope = Assert.EnterMultipleScope();
+ Assert.That(performance, Is.Not.Null, "Performance must not be null");
+ Assert.That(performance.NamingHierarchy, Is.EqualTo(expectedNamingHierarchy), "Performance naming hierarchy is correct");
}
}
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Reqnroll.Tests/ServiceCollectionAdapterTests.cs b/Tests/CSF.Screenplay.Reqnroll.Tests/ServiceCollectionAdapterTests.cs
index a28e5202..b21287b4 100644
--- a/Tests/CSF.Screenplay.Reqnroll.Tests/ServiceCollectionAdapterTests.cs
+++ b/Tests/CSF.Screenplay.Reqnroll.Tests/ServiceCollectionAdapterTests.cs
@@ -14,20 +14,18 @@ public void UnsupportedFunctionalityShouldThrowNotSupportedException()
var container = new ObjectContainer();
var sut = new ServiceCollectionAdapter(container);
- Assert.Multiple(() =>
- {
- Assert.That(() => sut[0], Throws.InstanceOf(), "Indexer get");
- Assert.That(() => sut[0] = null, Throws.InstanceOf(), "Indexer set");
- Assert.That(() => sut.Clear(), Throws.InstanceOf(), nameof(IServiceCollection.Clear));
- Assert.That(() => sut.Contains(null), Throws.InstanceOf(), nameof(IServiceCollection.Contains));
- Assert.That(() => sut.CopyTo(null, default), Throws.InstanceOf(), nameof(IServiceCollection.CopyTo));
- Assert.That(() => sut.GetEnumerator(), Throws.InstanceOf(), nameof(IServiceCollection.GetEnumerator));
- Assert.That(() => ((IEnumerable) sut).GetEnumerator(), Throws.InstanceOf(), nameof(IServiceCollection.GetEnumerator) + ": " + nameof(IEnumerable));
- Assert.That(() => sut.IndexOf(default), Throws.InstanceOf(), nameof(IServiceCollection.IndexOf));
- Assert.That(() => sut.Insert(default, null), Throws.InstanceOf(), nameof(IServiceCollection.Insert));
- Assert.That(() => sut.Remove(null), Throws.InstanceOf(), nameof(IServiceCollection.Remove));
- Assert.That(() => sut.RemoveAt(default), Throws.InstanceOf(), nameof(IServiceCollection.RemoveAt));
- });
+ using var scope = Assert.EnterMultipleScope();
+ Assert.That(() => sut[0], Throws.InstanceOf(), "Indexer get");
+ Assert.That(() => sut[0] = null, Throws.InstanceOf(), "Indexer set");
+ Assert.That(() => sut.Clear(), Throws.InstanceOf(), nameof(IServiceCollection.Clear));
+ Assert.That(() => sut.Contains(null), Throws.InstanceOf(), nameof(IServiceCollection.Contains));
+ Assert.That(() => sut.CopyTo(null, default), Throws.InstanceOf(), nameof(IServiceCollection.CopyTo));
+ Assert.That(() => sut.GetEnumerator(), Throws.InstanceOf(), nameof(IServiceCollection.GetEnumerator));
+ Assert.That(() => ((IEnumerable) sut).GetEnumerator(), Throws.InstanceOf(), nameof(IServiceCollection.GetEnumerator) + ": " + nameof(IEnumerable));
+ Assert.That(() => sut.IndexOf(default), Throws.InstanceOf(), nameof(IServiceCollection.IndexOf));
+ Assert.That(() => sut.Insert(default, null), Throws.InstanceOf(), nameof(IServiceCollection.Insert));
+ Assert.That(() => sut.Remove(null), Throws.InstanceOf(), nameof(IServiceCollection.Remove));
+ Assert.That(() => sut.RemoveAt(default), Throws.InstanceOf(), nameof(IServiceCollection.RemoveAt));
}
[Test]
diff --git a/Tests/CSF.Screenplay.Selenium.Tests/Builders/QueryPredicatePrototypeBuilderTests.cs b/Tests/CSF.Screenplay.Selenium.Tests/Builders/QueryPredicatePrototypeBuilderTests.cs
index 5ff3d6b1..23da53d7 100644
--- a/Tests/CSF.Screenplay.Selenium.Tests/Builders/QueryPredicatePrototypeBuilderTests.cs
+++ b/Tests/CSF.Screenplay.Selenium.Tests/Builders/QueryPredicatePrototypeBuilderTests.cs
@@ -24,11 +24,9 @@ public void AttributeValueShouldCreateASpecificationThatUsesGetAttribute(QueryPr
var sut = builder.AttributeValue("data-test", v => v.StartsWith("foo")).GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -45,11 +43,9 @@ public void AttributeValueWithPlainValueShouldCreateASpecificationThatUsesGetAtt
var sut = builder.AttributeValue("data-test", "foobar").GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -66,11 +62,9 @@ public void AttributeShouldCreateASpecificationThatUsesGetAttribute(QueryPredica
var sut = builder.Attribute("data-test").GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -87,11 +81,9 @@ public void ClassShouldCreateASpecificationThatUsesGetAttribute(QueryPredicatePr
var sut = builder.Class("foobar").GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -108,11 +100,9 @@ public void NotClassShouldCreateASpecificationThatUsesGetAttribute(QueryPredicat
var sut = builder.NotClass("qux").GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -129,11 +119,9 @@ public void AllClassesShouldCreateASpecificationThatUsesGetAttribute(QueryPredic
var sut = builder.AllClasses("foobar", "baz").GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -154,13 +142,11 @@ public void ClickableShouldCreateASpecificationThatTestsVisibilityAndEnabledStat
var sut = builder.Clickable().GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement1), Is.False, "Non-matching element 1 should not match");
Assert.That(sut.Matches(nonMatchingElement2), Is.False, "Non-matching element 2 should not match");
Assert.That(sut.Matches(nonMatchingElement3), Is.False, "Non-matching element 3 should not match");
- });
}
[Test, AutoMoqData]
@@ -177,11 +163,9 @@ public void CssPropertyShouldCreateASpecificationThatUsesGetCssValue(QueryPredic
var sut = builder.CssProperty("color", v => v == Colors.RED).GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -198,11 +182,9 @@ public void CssPropertyWithValueShouldCreateASpecificationThatUsesGetCssValue(Qu
var sut = builder.CssProperty("color", Colors.RED.ToRgbaString()).GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -215,11 +197,9 @@ public void LocationShouldCreateASpecificationThatUsesLocationQuery(QueryPredica
var sut = builder.Location(new Point(100, 200)).GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -232,11 +212,9 @@ public void SizeShouldCreateASpecificationThatUsesSizeQuery(QueryPredicateProtot
var sut = builder.Size(new Size(100, 200)).GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -249,11 +227,9 @@ public void TextShouldCreateASpecificationThatUsesTextQuery(QueryPredicateProtot
var sut = builder.Text("Hello World").GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -266,11 +242,9 @@ public void ValueShouldCreateASpecificationThatUsesValueQuery(QueryPredicateProt
var sut = builder.Value("Hello World").GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -283,11 +257,9 @@ public void VisibleShouldCreateASpecificationThatUsesVisibilityQuery(QueryPredic
var sut = builder.Visible().GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -312,11 +284,9 @@ public void SelectedOptionsWithTextShouldCreateASpecificationThatUsesOptionsQuer
var sut = builder.SelectedOptionsWithText("Option 1", "Option 3").GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -341,11 +311,9 @@ public void SelectedOptionsWithValueShouldCreateASpecificationThatUsesOptionsQue
var sut = builder.SelectedOptionsWithValue("1", "3").GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -370,11 +338,9 @@ public void UnselectedOptionsWithTextShouldCreateASpecificationThatUsesOptionsQu
var sut = builder.UnselectedOptionsWithText("Option 2").GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -399,11 +365,9 @@ public void UnselectedOptionsWithValueShouldCreateASpecificationThatUsesOptionsQ
var sut = builder.UnselectedOptionsWithValue("2").GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -427,11 +391,9 @@ public void OptionsWithTextShouldCreateASpecificationThatUsesOptionsQuery(QueryP
var sut = builder.OptionsWithText("Option 1", "Option 2", "Option 3").GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
[Test, AutoMoqData]
@@ -455,10 +417,8 @@ public void OptionsWithValueShouldCreateASpecificationThatUsesOptionsQuery(Query
var sut = builder.OptionsWithValue("1", "2", "3").GetElementSpecification();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sut.Matches(matchingElement), Is.True, "Matching element should match");
Assert.That(sut.Matches(nonMatchingElement), Is.False, "Non-matching element should not match");
- });
}
}
diff --git a/Tests/CSF.Screenplay.Selenium.Tests/Builders/UnnamedWaitBuilderTests.cs b/Tests/CSF.Screenplay.Selenium.Tests/Builders/UnnamedWaitBuilderTests.cs
index 2c48b1b0..af1dfde6 100644
--- a/Tests/CSF.Screenplay.Selenium.Tests/Builders/UnnamedWaitBuilderTests.cs
+++ b/Tests/CSF.Screenplay.Selenium.Tests/Builders/UnnamedWaitBuilderTests.cs
@@ -19,11 +19,10 @@ public void GetPerformableShouldGetAnObjectWithTheCorrectConfiguration()
var performable = (Wait) ((IGetsPerformable) sut).GetPerformable();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
+
Assert.That(performable, Has.PrivateFieldEqualTo("timeout", TimeSpan.FromSeconds(10)));
Assert.That(performable, Has.PrivateFieldEqualTo("pollingInterval", TimeSpan.FromMilliseconds(500)));
Assert.That(performable, Has.PrivateFieldEqualTo("ignoredExceptionTypes", new [] { typeof(InvalidOperationException), typeof(ArgumentException) }));
- });
}
}
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Selenium.Tests/ColorTests.cs b/Tests/CSF.Screenplay.Selenium.Tests/ColorTests.cs
index 8684fe32..7246fd19 100644
--- a/Tests/CSF.Screenplay.Selenium.Tests/ColorTests.cs
+++ b/Tests/CSF.Screenplay.Selenium.Tests/ColorTests.cs
@@ -28,11 +28,9 @@ public class ColorTests
public void TryParseShouldReturnTrueAndExposeACorrespondingColorForAValidString(string colorString, byte red, byte green, byte blue, double alpha)
{
var result = Color.TryParse(colorString, out var color);
- Assert.Multiple(() =>
- {
- Assert.That(result, Is.True, "Parsing success");
- Assert.That(color, Is.EqualTo(new Color(red, green, blue, alpha)), "Expected color matches");
- });
+ using var scope = Assert.EnterMultipleScope();
+ Assert.That(result, Is.True, "Parsing success");
+ Assert.That(color, Is.EqualTo(new Color(red, green, blue, alpha)), "Expected color matches");
}
[TestCase("rgb(1, 2, 3)", 1, 2, 3, 1)]
diff --git a/Tests/CSF.Screenplay.Selenium.Tests/Tasks/EnterTheDateTests.cs b/Tests/CSF.Screenplay.Selenium.Tests/Tasks/EnterTheDateTests.cs
index 1be82a05..e31c707d 100644
--- a/Tests/CSF.Screenplay.Selenium.Tests/Tasks/EnterTheDateTests.cs
+++ b/Tests/CSF.Screenplay.Selenium.Tests/Tasks/EnterTheDateTests.cs
@@ -58,10 +58,8 @@ public async Task EnteringADateInAnUnusualCultureShouldYieldIncorrectResults(ISt
await When(webster).AttemptsTo(EnterTheDate(new DateTime(2025, 11, 12)).Into(inputArea).ForTheCultureNamed("ja-JP"));
var result = await Then(webster).Should(ReadFromTheElement(displayText).TheText());
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(result, Is.Not.EqualTo(string.Empty), "The date shouldn't be empty");
Assert.That(result, Is.Not.EqualTo("2025-11-12"), "The date shouldn't be the value which was entered either, because of the culture/format difference");
- });
}
}
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Selenium.Tests/Tasks/OpenUrlRespectingBaseTests.cs b/Tests/CSF.Screenplay.Selenium.Tests/Tasks/OpenUrlRespectingBaseTests.cs
index e5d7cdb2..94a4ce84 100644
--- a/Tests/CSF.Screenplay.Selenium.Tests/Tasks/OpenUrlRespectingBaseTests.cs
+++ b/Tests/CSF.Screenplay.Selenium.Tests/Tasks/OpenUrlRespectingBaseTests.cs
@@ -36,13 +36,11 @@ public async Task TheActionCreatedByThisTaskShouldContainTheCorrectReport(IWebDr
{
await sut.PerformAsAsync(actor);
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(performable, Is.InstanceOf(), "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
{
@@ -71,13 +69,11 @@ public async Task TheActionCreatedByThisTaskShouldNotUpdateAnAlreadyAbsoluteUrl(
{
await sut.PerformAsAsync(actor);
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(performable, Is.InstanceOf(), "Performable is correct type");
Assert.That(((OpenUrl) performable!).GetReportFragment(actor, formatter).ToString(),
Is.EqualTo("Anthony opens their browser at the test page: https://contoso.com/test.html"),
"The report is correct");
- });
}
finally
{
diff --git a/Tests/CSF.Screenplay.Selenium.Tests/Tasks/TakeAndSaveAScreenshotTests.cs b/Tests/CSF.Screenplay.Selenium.Tests/Tasks/TakeAndSaveAScreenshotTests.cs
index d73ab8b6..513a52b7 100644
--- a/Tests/CSF.Screenplay.Selenium.Tests/Tasks/TakeAndSaveAScreenshotTests.cs
+++ b/Tests/CSF.Screenplay.Selenium.Tests/Tasks/TakeAndSaveAScreenshotTests.cs
@@ -28,11 +28,9 @@ public async Task TakeAndSaveAScreenshotShouldSaveAFile(IStage stage)
webster.RecordsAsset -= OnRecordsAsset;
}
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(screenshotPath, Is.Not.Null);
Assert.That(screenshotPath, Does.Exist);
- });
}
void OnRecordsAsset(object? sender, PerformableAssetEventArgs e)
diff --git a/Tests/CSF.Screenplay.SpecFlow.Tests/ServiceCollectionAdapterTests.cs b/Tests/CSF.Screenplay.SpecFlow.Tests/ServiceCollectionAdapterTests.cs
index abf78ef1..77b92a83 100644
--- a/Tests/CSF.Screenplay.SpecFlow.Tests/ServiceCollectionAdapterTests.cs
+++ b/Tests/CSF.Screenplay.SpecFlow.Tests/ServiceCollectionAdapterTests.cs
@@ -14,8 +14,7 @@ public void UnsupportedFunctionalityShouldThrowNotSupportedException()
var container = new ObjectContainer();
var sut = new ServiceCollectionAdapter(container);
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(() => sut[0], Throws.InstanceOf(), "Indexer get");
Assert.That(() => sut[0] = null, Throws.InstanceOf(), "Indexer set");
Assert.That(() => sut.Clear(), Throws.InstanceOf(), nameof(IServiceCollection.Clear));
@@ -27,7 +26,6 @@ public void UnsupportedFunctionalityShouldThrowNotSupportedException()
Assert.That(() => sut.Insert(default, null), Throws.InstanceOf(), nameof(IServiceCollection.Insert));
Assert.That(() => sut.Remove(null), Throws.InstanceOf(), nameof(IServiceCollection.Remove));
Assert.That(() => sut.RemoveAt(default), Throws.InstanceOf(), nameof(IServiceCollection.RemoveAt));
- });
}
[Test]
diff --git a/Tests/CSF.Screenplay.Tests/ActorTests.cs b/Tests/CSF.Screenplay.Tests/ActorTests.cs
index f931c26d..5f487c60 100644
--- a/Tests/CSF.Screenplay.Tests/ActorTests.cs
+++ b/Tests/CSF.Screenplay.Tests/ActorTests.cs
@@ -63,11 +63,9 @@ public void DisposeShouldDisposeDisposableAbilities(Mock abi
sut.Dispose();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
ability1.Verify(x => x.Dispose(), Times.Once, "Ability 1 has been disposed");
ability2.Verify(x => x.Dispose(), Times.Once, "Ability 2 has been disposed");
- });
}
[Test,AutoMoqData]
@@ -150,11 +148,9 @@ public async Task PerformAsyncWithoutResultShouldRaiseTwoEvents(Actor sut, IPerf
sut.BeginPerformable -= OnBeginPerformable;
sut.EndPerformable -= OnEndPerformable;
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(beginPerformable, Is.True, "BeginPerformable was triggered");
Assert.That(endPerformable, Is.True, "EndPerformable was triggered");
- });
}
[Test,AutoMoqData]
@@ -166,12 +162,10 @@ public void PerformAsyncWithoutResultShouldRaisePerformableFailedEventIfPerforma
Mock.Get(performable).Setup(x => x.PerformAsAsync(sut, It.IsAny())).Throws();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(async () => await ((ICanPerform)sut).PerformAsync(performable), Throws.InstanceOf(), "PerformAsync throws an exception");
sut.PerformableFailed -= OnPerformableFailed;
Assert.That(exceptionCaught, Is.InstanceOf(), "The correct exception was caught");
- });
}
[Test,AutoMoqData]
@@ -211,12 +205,10 @@ public async Task PerformAsyncWithNongenericResultShouldRaiseThreeEvents(Actor s
sut.PerformableResult -= OnPerformableResult;
sut.EndPerformable -= OnEndPerformable;
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(beginPerformable, Is.True, "BeginPerformable was triggered");
Assert.That(result, Is.SameAs(expectedResult), "PerformableResult was triggered");
Assert.That(endPerformable, Is.True, "EndPerformable was triggered");
- });
}
[Test,AutoMoqData]
@@ -228,12 +220,10 @@ public void PerformAsyncWithNongenericResultShouldRaisePerformableFailedEventIfP
Mock.Get(performable).Setup(x => x.PerformAsAsync(sut, It.IsAny())).Throws();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(async () => await ((ICanPerform)sut).PerformAsync(performable), Throws.InstanceOf(), "PerformAsync throws an exception");
sut.PerformableFailed -= OnPerformableFailed;
Assert.That(exceptionCaught, Is.InstanceOf(), "The correct exception was caught");
- });
}
[Test,AutoMoqData]
@@ -273,12 +263,10 @@ public async Task PerformAsyncWithGenericResultShouldRaiseThreeEvents(Actor sut,
sut.PerformableResult -= OnPerformableResult;
sut.EndPerformable -= OnEndPerformable;
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(beginPerformable, Is.True, "BeginPerformable was triggered");
Assert.That(result, Is.SameAs(expectedResult), "PerformableResult was triggered");
Assert.That(endPerformable, Is.True, "EndPerformable was triggered");
- });
}
[Test,AutoMoqData]
@@ -290,12 +278,10 @@ public void PerformAsyncWithGenericResultShouldRaisePerformableFailedEventIfPerf
Mock.Get(performable).Setup(x => x.PerformAsAsync(sut, It.IsAny())).Throws();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(async () => await ((ICanPerform)sut).PerformAsync(performable), Throws.InstanceOf(), "PerformAsync throws an exception");
sut.PerformableFailed -= OnPerformableFailed;
Assert.That(exceptionCaught, Is.InstanceOf(), "The correct exception was caught");
- });
}
[Test,AutoMoqData]
diff --git a/Tests/CSF.Screenplay.Tests/Actors/CastTests.cs b/Tests/CSF.Screenplay.Tests/Actors/CastTests.cs
index a3444412..c4ba846f 100644
--- a/Tests/CSF.Screenplay.Tests/Actors/CastTests.cs
+++ b/Tests/CSF.Screenplay.Tests/Actors/CastTests.cs
@@ -27,11 +27,9 @@ public void GetActorByNameShouldReturnTheSameActorIfUsedTwice(string name,
var actor1 = sut.GetActor(name);
var actor2 = sut.GetActor(name);
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(actor1, Is.Not.Null, "Actors aren't null");
Assert.That(actor1, Is.SameAs(actor2), "Same actor for second usage of the method");
- });
}
[Test, AutoMoqData]
diff --git a/Tests/CSF.Screenplay.Tests/Integration/EventBusIntegrationTests.cs b/Tests/CSF.Screenplay.Tests/Integration/EventBusIntegrationTests.cs
index 70b6062b..9f1fba3e 100644
--- a/Tests/CSF.Screenplay.Tests/Integration/EventBusIntegrationTests.cs
+++ b/Tests/CSF.Screenplay.Tests/Integration/EventBusIntegrationTests.cs
@@ -54,15 +54,13 @@ await sut.ExecuteAsPerformanceAsync(async (s, c) =>
eventPublisher.EndPerformable -= OnEndPerformable;
eventPublisher.PerformableResult -= OnPerformableResult;
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(performanceBegun, Is.True, $"{nameof(OnPerformanceBegun)} was triggered");
Assert.That(performanceFinished, Is.True, $"{nameof(OnPerformanceFinished)} was triggered");
Assert.That(createdActor, Has.Property(nameof(IHasName.Name)).EqualTo("Joe"), $"{nameof(OnActorCreated)} was triggered");
Assert.That(performablesBegun, Is.EqualTo(new object[] { sampleAction, sampleQuestion }), $"{nameof(OnBeginPerformable)} was triggered with the right performables");
Assert.That(performablesEnded, Is.EqualTo(new object[] { sampleAction, sampleQuestion }), $"{nameof(OnEndPerformable)} was triggered with the right performables");
Assert.That(performableResults, Is.EqualTo(new object[] { "Joe" }), $"{nameof(OnPerformableResult)} was triggered with the right performables");
- });
}
[Test,AutoMoqData]
@@ -91,11 +89,9 @@ await sut.ExecuteAsPerformanceAsync((s, c) =>
eventPublisher.ActorSpotlit -= OnActorSpotlit;
eventPublisher.SpotlightTurnedOff -= OnSpotlightTurnedOff;
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(spotlitActor, Has.Property(nameof(IHasName.Name)).EqualTo("Joe"), $"{nameof(OnActorSpotlit)} was triggered");
Assert.That(spotlightOff, Is.True, $"{nameof(OnSpotlightTurnedOff)} was triggered");
- });
}
[Test,AutoMoqData]
@@ -149,13 +145,11 @@ await sut.ExecuteAsPerformanceAsync(async (s, c) =>
eventPublisher.PerformableFailed -= OnPerformableFailed;
eventPublisher.PerformanceFinished -= OnPerformanceFinished;
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(exceptionCaught,
Is.InstanceOf().And.Property(nameof(Exception.Message)).EqualTo(ThrowingAction.Message),
$"{nameof(OnPerformableFailed)} was triggered");
Assert.That(result, Is.False, $"{nameof(OnPerformanceFinished)} was triggered");
- });
}
[Test,AutoMoqData]
@@ -230,10 +224,8 @@ await sut.ExecuteAsPerformanceAsync((s, c) =>
eventPublisher.RecordAsset -= OnRecordsAsset;
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(filePath, Is.EqualTo(expectedPath), "File path");
Assert.That(fileSummary, Is.EqualTo(expectedSummary), "File summary");
- });
}
}
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Tests/Integration/PerformanceIntegrationTests.cs b/Tests/CSF.Screenplay.Tests/Integration/PerformanceIntegrationTests.cs
index 2d134fb7..65a529f7 100644
--- a/Tests/CSF.Screenplay.Tests/Integration/PerformanceIntegrationTests.cs
+++ b/Tests/CSF.Screenplay.Tests/Integration/PerformanceIntegrationTests.cs
@@ -25,10 +25,9 @@ await screenplay.ExecuteAsPerformanceAsync(async (s, c) => {
return true;
});
- Assert.Multiple(() => {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(sampleAction.ActorName, Is.EqualTo("Joe"), "Action result");
Assert.That(question1Result, Is.EqualTo(5), "Question 1 result");
Assert.That(question2Result, Is.EqualTo("Joe"), "Question 2 result");
- });
}
}
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Tests/JsonToHtmlReport/AssetEmbedderTests.cs b/Tests/CSF.Screenplay.Tests/JsonToHtmlReport/AssetEmbedderTests.cs
index 4e9d2e74..69de2b11 100644
--- a/Tests/CSF.Screenplay.Tests/JsonToHtmlReport/AssetEmbedderTests.cs
+++ b/Tests/CSF.Screenplay.Tests/JsonToHtmlReport/AssetEmbedderTests.cs
@@ -25,15 +25,13 @@ public async Task EmbedReportAssetsAsyncShouldNotEmbedAnAssetOfAnUnsupportedType
await sut.EmbedReportAssetsAsync(report, new () { EmbeddedFileExtensions = "jpeg", EmbeddedFileSizeThresholdKb = 50 });
var performable = report.Performances.First().Reportables.OfType().First();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(performable.Assets,
Has.One.Matches(a => a is { FileName: SampleAssetsCustomization.Asset2Filename, FileData: not null }),
"JPEG asset has been embedded");
Assert.That(performable.Assets,
Has.One.Matches(a => a is { FileName: SampleAssetsCustomization.Asset1Filename, FileData: null }),
"PNG asset has not been embedded");
- });
}
[Test, AutoMoqData]
@@ -50,15 +48,13 @@ public async Task EmbedReportAssetsAsyncShouldNotEmbedAnAssetWhichIsTooLarge(Ass
await sut.EmbedReportAssetsAsync(report, new () { EmbeddedFileExtensions = "png,jpeg", EmbeddedFileSizeThresholdKb = 10 });
var performable = report.Performances.First().Reportables.OfType().First();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(performable.Assets,
Has.One.Matches(a => a is { FileName: SampleAssetsCustomization.Asset2Filename, FileData: null }),
"JPEG asset has not been embedded");
Assert.That(performable.Assets,
Has.One.Matches(a => a is { FileName: SampleAssetsCustomization.Asset1Filename, FileData: not null }),
"PNG asset has been embedded");
- });
}
[Test, AutoMoqData]
@@ -67,14 +63,12 @@ public async Task EmbedReportAssetsAsyncShouldUseCorrectBase64(AssetEmbedder sut
await sut.EmbedReportAssetsAsync(report, new () { EmbeddedFileExtensions = "png,jpeg", EmbeddedFileSizeThresholdKb = 50 });
var performable = report.Performances.First().Reportables.OfType().First();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
var asset1Data = performable.Assets.FirstOrDefault(x => x.FileName == SampleAssetsCustomization.Asset1Filename)?.FileData;
Assert.That(asset1Data, Is.EqualTo(asset1Base64), "PNG asset encoded correctly");
var asset2Data = performable.Assets.FirstOrDefault(x => x.FileName == SampleAssetsCustomization.Asset2Filename)?.FileData;
Assert.That(asset2Data, Is.EqualTo(asset2Base64), "JPEG asset encoded correctly");
- });
}
}
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Tests/JsonToHtmlReport/ReportConverterTests.cs b/Tests/CSF.Screenplay.Tests/JsonToHtmlReport/ReportConverterTests.cs
index a30ea220..721eeafd 100644
--- a/Tests/CSF.Screenplay.Tests/JsonToHtmlReport/ReportConverterTests.cs
+++ b/Tests/CSF.Screenplay.Tests/JsonToHtmlReport/ReportConverterTests.cs
@@ -34,12 +34,10 @@ public async Task ConvertAsyncShouldWriteTheReportUsingATemplate([NoAutoProperti
await sut.ConvertAsync(options);
- Assert.Multiple(async () =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(options.ReportPath, Does.Exist, "Report exists");
using var reader = new StreamReader(File.Open(options.OutputPath, FileMode.Open));
var reportContent = await reader.ReadToEndAsync();
Assert.That(reportContent, Is.EqualTo(""), "Report has correct content");
- });
}
}
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Tests/JsonToHtmlReport/TemplateReaderTests.cs b/Tests/CSF.Screenplay.Tests/JsonToHtmlReport/TemplateReaderTests.cs
index 866f217e..ceb0093e 100644
--- a/Tests/CSF.Screenplay.Tests/JsonToHtmlReport/TemplateReaderTests.cs
+++ b/Tests/CSF.Screenplay.Tests/JsonToHtmlReport/TemplateReaderTests.cs
@@ -8,11 +8,9 @@ public async Task ReadTemplateShouldReturnAString(TemplateReader sut)
{
var result = await sut.ReadTemplate();
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(result, Is.Not.Null, "Not null");
Assert.That(result, Is.Not.Empty, "Not empty");
- });
}
}
}
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Tests/Performables/TimeSpanBuilderTests.cs b/Tests/CSF.Screenplay.Tests/Performables/TimeSpanBuilderTests.cs
deleted file mode 100644
index ed5ce104..00000000
--- a/Tests/CSF.Screenplay.Tests/Performables/TimeSpanBuilderTests.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-using static CSF.Screenplay.PerformanceStarter;
-using static CSF.Screenplay.Performables.TimeSpanBuilderTests.EatLunchPerformableBuilder;
-using CSF.Screenplay.Actors;
-
-namespace CSF.Screenplay.Performables;
-
-[TestFixture,Parallelizable]
-public class TimeSpanBuilderTests
-{
- [Test,AutoMoqData]
- public async Task GetTimeSpanShouldReturnACorrectTimeSpanInTheContextOfALargerBuilderIntegrationTest(Actor actor, string foodName)
- {
- object? performable = null;
- void OnBeginPerformable(object? sender, PerformableEventArgs ev) => performable = ev.Performable;
-
- actor.BeginPerformable += OnBeginPerformable;
- await When(actor).AttemptsTo(Eat(foodName).For(10).Minutes());
- actor.BeginPerformable -= OnBeginPerformable;
-
- Assert.Multiple(() =>
- {
- Assert.That(performable, Is.Not.Null, "Performable must not be null");
- Assert.That(performable, Is.InstanceOf(), $"Performable must not be an instance of {nameof(EatLunch)}");
- Assert.That(performable, Has.Property(nameof(EatLunch.FoodName)).EqualTo(foodName), $"The performable must have the correct {nameof(EatLunch.FoodName)}");
- Assert.That(performable, Has.Property(nameof(EatLunch.HowLong)).EqualTo(TimeSpan.FromMinutes(10)), $"The performable must have the correct {nameof(EatLunch.HowLong)}");
- });
- }
-
- [Test,AutoMoqData]
- public void MillisecondsShouldCreateAnAmountInMilliseconds(object otherBuilder)
- {
- var sut = TimeSpanBuilder.Create(otherBuilder, 20);
- sut.Milliseconds();
- Assert.That(((IProvidesTimeSpan)sut).GetTimeSpan(), Is.EqualTo(TimeSpan.FromMilliseconds(20)));
- }
-
- [Test,AutoMoqData]
- public void SecondsShouldCreateAnAmountInSeconds(object otherBuilder)
- {
- var sut = TimeSpanBuilder.Create(otherBuilder, 20);
- sut.Seconds();
- Assert.That(((IProvidesTimeSpan)sut).GetTimeSpan(), Is.EqualTo(TimeSpan.FromSeconds(20)));
- }
-
- [Test,AutoMoqData]
- public void MinutesShouldCreateAnAmountInMinutes(object otherBuilder)
- {
- var sut = TimeSpanBuilder.Create(otherBuilder, 20);
- sut.Minutes();
- Assert.That(((IProvidesTimeSpan)sut).GetTimeSpan(), Is.EqualTo(TimeSpan.FromMinutes(20)));
- }
-
- [Test,AutoMoqData]
- public void HoursShouldCreateAnAmountInHours(object otherBuilder)
- {
- var sut = TimeSpanBuilder.Create(otherBuilder, 20);
- sut.Hours();
- Assert.That(((IProvidesTimeSpan)sut).GetTimeSpan(), Is.EqualTo(TimeSpan.FromHours(20)));
- }
-
- [Test,AutoMoqData]
- public void DaysShouldCreateAnAmountInDays(object otherBuilder)
- {
- var sut = TimeSpanBuilder.Create(otherBuilder, 20);
- sut.Days();
- Assert.That(((IProvidesTimeSpan)sut).GetTimeSpan(), Is.EqualTo(TimeSpan.FromDays(20)));
- }
-
- // Note that this class is identical to the example in the docco comments for TimeSpanBuilder
- public class EatLunchPerformableBuilder : IGetsPerformable
- {
- IProvidesTimeSpan? timeSpanBuilder;
-
- protected string? FoodName { get; init; }
-
- IPerformable IGetsPerformable.GetPerformable()
- => new EatLunch(FoodName, timeSpanBuilder?.GetTimeSpan() ?? TimeSpan.Zero);
-
- public TimeSpanBuilder For(int howMany)
- {
- var builder = TimeSpanBuilder.Create(this, howMany);
- timeSpanBuilder = builder;
- return builder;
- }
-
- public static EatLunchPerformableBuilder Eat(string foodName) => new EatLunchPerformableBuilder() { FoodName = foodName };
- }
-
- public class EatLunch(string? foodName, TimeSpan howLong) : IPerformable
- {
- public string? FoodName { get; } = foodName;
- public TimeSpan HowLong { get; } = howLong;
-
- // Intentional no-op, this is just a testing class.
- public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default)
- => ValueTask.CompletedTask;
- }
-}
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Tests/ReportFragmentFormatterTests.cs b/Tests/CSF.Screenplay.Tests/ReportFragmentFormatterTests.cs
index c68aebeb..ab10ac39 100644
--- a/Tests/CSF.Screenplay.Tests/ReportFragmentFormatterTests.cs
+++ b/Tests/CSF.Screenplay.Tests/ReportFragmentFormatterTests.cs
@@ -22,13 +22,11 @@ public void FormatShouldReturnCorrectReportFragment([Frozen] IGetsReportFormat f
var result = sut.Format(template, values);
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(result.OriginalTemplate, Is.EqualTo(template), "Original template is correct");
Assert.That(result.FormattedFragment, Is.EqualTo("Joe washes 5 dishes"), "Formatted fragment is correct");
Assert.That(result.PlaceholderValues, Has.Count.EqualTo(2), "Count of placeholder items");
Assert.That(result.PlaceholderValues, Has.One.Matches(x => x.Name == "Actor" && Equals(x.Value, "Joe")), "First placeholder item");
Assert.That(result.PlaceholderValues, Has.One.Matches(x => x.Name == "Count" && Equals(x.Value, 5)), "Second placeholder item");
- });
}
}
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Tests/Reporting/JsonScreenplayReporterTests.cs b/Tests/CSF.Screenplay.Tests/Reporting/JsonScreenplayReporterTests.cs
index 6dfaa2a4..416ccf6e 100644
--- a/Tests/CSF.Screenplay.Tests/Reporting/JsonScreenplayReporterTests.cs
+++ b/Tests/CSF.Screenplay.Tests/Reporting/JsonScreenplayReporterTests.cs
@@ -13,8 +13,7 @@ public void SubscribeTo_ShouldSubscribeToEvents([Frozen] Mock
- {
+ using var scope = Assert.EnterMultipleScope();
mockEvents.VerifyAdd(e => e.ScreenplayStarted += It.IsAny(), Times.Once);
mockEvents.VerifyAdd(e => e.ScreenplayEnded += It.IsAny(), Times.Once);
mockEvents.VerifyAdd(e => e.PerformanceBegun += It.IsAny>(), Times.Once);
@@ -28,7 +27,6 @@ public void SubscribeTo_ShouldSubscribeToEvents([Frozen] Mock e.GainedAbility += It.IsAny>(), Times.Once);
mockEvents.VerifyAdd(e => e.ActorSpotlit += It.IsAny>(), Times.Once);
mockEvents.VerifyAdd(e => e.SpotlightTurnedOff += It.IsAny>(), Times.Once);
- });
}
[Test, AutoMoqData]
@@ -39,8 +37,7 @@ public void UnsubscribeFrom_ShouldUnsubscribeFromEvents([Frozen] Mock
- {
+ using var scope = Assert.EnterMultipleScope();
mockEvents.VerifyRemove(e => e.ScreenplayStarted -= It.IsAny(), Times.Once);
mockEvents.VerifyRemove(e => e.ScreenplayEnded -= It.IsAny(), Times.Once);
mockEvents.VerifyRemove(e => e.PerformanceBegun -= It.IsAny>(), Times.Once);
@@ -54,7 +51,6 @@ public void UnsubscribeFrom_ShouldUnsubscribeFromEvents([Frozen] Mock e.GainedAbility -= It.IsAny>(), Times.Once);
mockEvents.VerifyRemove(e => e.ActorSpotlit -= It.IsAny>(), Times.Once);
mockEvents.VerifyRemove(e => e.SpotlightTurnedOff -= It.IsAny>(), Times.Once);
- });
}
[Test, AutoMoqData]
diff --git a/Tests/CSF.Screenplay.Tests/Reporting/PerformanceReportBuilderTests.cs b/Tests/CSF.Screenplay.Tests/Reporting/PerformanceReportBuilderTests.cs
index fba9d7d7..2a2ea5fe 100644
--- a/Tests/CSF.Screenplay.Tests/Reporting/PerformanceReportBuilderTests.cs
+++ b/Tests/CSF.Screenplay.Tests/Reporting/PerformanceReportBuilderTests.cs
@@ -123,8 +123,7 @@ public void BeginAndEndPerformableShouldBeAbleToCreateAHierarchyOfPerformableRep
sut.EndPerformable(performable1, actor);
var report = sut.GetReport(outcome);
- Assert.Multiple(() =>
- {
+ using var scope = Assert.EnterMultipleScope();
Assert.That(report.Reportables, Has.Count.EqualTo(1), "Only one reportable should be present at root level");
var taskReport = report.Reportables.OfType().Single();
@@ -132,7 +131,6 @@ public void BeginAndEndPerformableShouldBeAbleToCreateAHierarchyOfPerformableRep
Assert.That(taskReport.Reportables.OfType().Select(x => x.PerformableType),
Is.EqualTo(new[] { "CSF.Screenplay.Performables.StartTheStopwatch", "CSF.Screenplay.Performables.StopTheStopwatch" }),
"The child reports should be of the correct performable types");
- });
}
diff --git a/Tests/CSF.Screenplay.Tests/Reporting/ReportFormatCreatorTests.cs b/Tests/CSF.Screenplay.Tests/Reporting/ReportFormatCreatorTests.cs
index 24dbbd3d..a98c6095 100644
--- a/Tests/CSF.Screenplay.Tests/Reporting/ReportFormatCreatorTests.cs
+++ b/Tests/CSF.Screenplay.Tests/Reporting/ReportFormatCreatorTests.cs
@@ -21,11 +21,9 @@ public void GetReportFormatShouldReturnTheCorrectFormattedTemplate(string origin
var sut = new ReportFormatCreator();
var actual = sut.GetReportFormat(original, Array.Empty