Skip to content

Commit 712075e

Browse files
authored
Merge pull request #351 from csf-dev/craigfowler/issue306
WIP #306 - Add asset-helper actions
2 parents bf363ec + 9deb577 commit 712075e

19 files changed

Lines changed: 575 additions & 28 deletions

CSF.Screenplay.Abstractions/Abilities/UseAStopwatch.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace CSF.Screenplay.Abilities
99
/// <remarks>
1010
/// <para>
1111
/// Use this ability with the actions which exposed by
12-
/// <see cref="CSF.Screenplay.Performables.StopwatchBuilder"/>.
12+
/// <see cref="CSF.Screenplay.PerforamableBuilder"/>.
1313
/// This ability wraps a <see cref="System.Diagnostics.Stopwatch"/> instance, allowing the actor
1414
/// to control &amp; read it from the related actions.
1515
/// </para>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System.IO;
2+
using CSF.Screenplay.Performables;
3+
4+
namespace CSF.Screenplay
5+
{
6+
/// <summary>
7+
/// Static helper class used to create/build instances of the performables which are bundled with Screenplay.
8+
/// </summary>
9+
/// <remarks>
10+
/// <para>Tip: Consume the members of this class via:</para>
11+
/// <code>using static CSF.Screenplay.PerforamableBuilder;</code>
12+
/// <para>
13+
/// This will allow you use the method names in this class in a more human-readable fashion.
14+
/// </para>
15+
/// </remarks>
16+
public static partial class PerforamableBuilder
17+
{
18+
/// <summary>
19+
/// Gets a builder for an Action which copies a file at the specified source path into the assets for the current performance.
20+
/// </summary>
21+
/// <param name="path">The path to the source file which should be copied</param>
22+
/// <returns>A builder to specify the name of the asset.</returns>
23+
public static CopyFileAsAnAssetFilenameBuilder CopyTheFile(string path)
24+
=> new CopyFileAsAnAssetFilenameBuilder(path);
25+
26+
/// <summary>
27+
/// Gets a builder for an Action which saves a stream of data into the assets for the current performance.
28+
/// </summary>
29+
/// <param name="stream">The stream which should be saved as an asset.</param>
30+
/// <returns>A builder to specify the name of the asset.</returns>
31+
public static SaveStreamAsAnAssetFilenameBuilder SaveTheStream(Stream stream)
32+
=> new SaveStreamAsAnAssetFilenameBuilder(stream);
33+
34+
/// <summary>
35+
/// Gets a builder for an Action which saves some text into an asset file for the current performance.
36+
/// </summary>
37+
/// <param name="text">The text which should be saved as an asset.</param>
38+
/// <returns>A builder to specify the name of the asset.</returns>
39+
public static SaveTheTextAsAnAssetFilenameBuilder SaveTheText(string text)
40+
=> new SaveTheTextAsAnAssetFilenameBuilder(text);
41+
}
42+
}

CSF.Screenplay.Abstractions/Performables/StopwatchBuilder.cs renamed to CSF.Screenplay.Abstractions/PerformableBuilder.Stopwatch.cs

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,17 @@
11
using CSF.Screenplay.Abilities;
2+
using CSF.Screenplay.Performables;
23

3-
namespace CSF.Screenplay.Performables
4+
namespace CSF.Screenplay
45
{
5-
/// <summary>
6-
/// A builder for actions relating to the <see cref="UseAStopwatch"/> ability.
7-
/// </summary>
8-
/// <remarks>
9-
/// <para>
10-
/// When using this class it is recommended to include <c>using static CSF.Screenplay.Performables.StopwatchBuilder;</c>
11-
/// in the source file which uses it. This will allow you use the method names in this class in a more human-readable
12-
/// fashion.
13-
/// </para>
14-
/// <para>
15-
/// The actions and the question exposed by this builder allow an actor to accurately
16-
/// track and measure time elapsed during a Performance.
17-
/// </para>
18-
/// </remarks>
19-
public static class StopwatchBuilder
6+
public static partial class PerforamableBuilder
207
{
218
/// <summary>
229
/// Gets a performable which starts the stopwatch.
2310
/// </summary>
2411
/// <remarks>
2512
/// <para>
26-
/// Use of this performable requires the actor has the <see cref="UseAStopwatch"/> ability.
13+
/// Use of this performable requires the actor has the <see cref="UseAStopwatch"/> ability,
14+
/// for accurately measuring time, during the course of a performance.
2715
/// </para>
2816
/// </remarks>
2917
public static StartTheStopwatch StartTheStopwatch() => new StartTheStopwatch();
@@ -33,7 +21,8 @@ public static class StopwatchBuilder
3321
/// </summary>
3422
/// <remarks>
3523
/// <para>
36-
/// Use of this performable requires the actor has the <see cref="UseAStopwatch"/> ability.
24+
/// Use of this performable requires the actor has the <see cref="UseAStopwatch"/> ability,
25+
/// for accurately measuring time, during the course of a performance.
3726
/// </para>
3827
/// </remarks>
3928
public static StopTheStopwatch StopTheStopwatch() => new StopTheStopwatch();
@@ -43,7 +32,8 @@ public static class StopwatchBuilder
4332
/// </summary>
4433
/// <remarks>
4534
/// <para>
46-
/// Use of this performable requires the actor has the <see cref="UseAStopwatch"/> ability.
35+
/// Use of this performable requires the actor has the <see cref="UseAStopwatch"/> ability,
36+
/// for accurately measuring time, during the course of a performance.
4737
/// </para>
4838
/// </remarks>
4939
public static ResetTheStopwatch ResetTheStopwatch() => new ResetTheStopwatch();
@@ -53,9 +43,10 @@ public static class StopwatchBuilder
5343
/// </summary>
5444
/// <remarks>
5545
/// <para>
56-
/// Use of this performable requires the actor has the <see cref="UseAStopwatch"/> ability.
46+
/// Use of this performable requires the actor has the <see cref="UseAStopwatch"/> ability,
47+
/// for accurately measuring time, during the course of a performance.
5748
/// </para>
5849
/// </remarks>
5950
public static ReadTheStopwatch ReadTheStopwatch() => new ReadTheStopwatch();
6051
}
61-
}
52+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
using System.IO;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using CSF.Screenplay.Abilities;
6+
7+
namespace CSF.Screenplay.Performables
8+
{
9+
/// <summary>
10+
/// A performable Action which copies an existing file as an asset file.
11+
/// </summary>
12+
public class CopyFileAsAnAsset : IPerformable, ICanReport
13+
{
14+
readonly string sourceFilePath, assetName, assetSummary;
15+
16+
/// <inheritdoc/>
17+
public ReportFragment GetReportFragment(Actor actor, IFormatsReportFragment formatter)
18+
=> formatter.Format("{Actor} copies {SourcePath} as an asset file named {Name}", actor, sourceFilePath, assetName);
19+
20+
/// <inheritdoc/>
21+
public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default)
22+
{
23+
var ability = actor.GetAbility<GetAssetFilePaths>();
24+
var path = ability.GetAssetFilePath(assetName);
25+
26+
if(!File.Exists(sourceFilePath)) throw new FileNotFoundException($"The source file '{sourceFilePath}' must exist", sourceFilePath);
27+
File.Copy(sourceFilePath, path);
28+
actor.RecordAsset(this, path, assetSummary);
29+
30+
return default;
31+
}
32+
33+
/// <summary>
34+
/// Initializes a new instance of <see cref="CopyFileAsAnAsset"/>.
35+
/// </summary>
36+
/// <param name="sourceFilePath">The file system path to the file which should be copied into the assets.</param>
37+
/// <param name="assetName">The name of the asset file.</param>
38+
/// <param name="assetSummary">An optional human-readable summary of the asset</param>
39+
public CopyFileAsAnAsset(string sourceFilePath, string assetName, string assetSummary = null)
40+
{
41+
if (string.IsNullOrWhiteSpace(assetName))
42+
throw new ArgumentException($"'{nameof(assetName)}' cannot be null or whitespace.", nameof(assetName));
43+
44+
this.sourceFilePath = sourceFilePath ?? throw new ArgumentNullException(nameof(sourceFilePath));
45+
this.assetName = assetName;
46+
this.assetSummary = assetSummary;
47+
}
48+
}
49+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
namespace CSF.Screenplay.Performables
2+
{
3+
/// <summary>
4+
/// A builder for customising the <see cref="CopyFileAsAnAsset"/> action, specifying the asset filename.
5+
/// </summary>
6+
public class CopyFileAsAnAssetFilenameBuilder
7+
{
8+
readonly string sourcePath;
9+
10+
/// <summary>
11+
/// Gets a builder which may be used as a performable, or which may further customise the Action,
12+
/// having specified the filename for the asset.
13+
/// </summary>
14+
/// <param name="filename">The filename of the asset to create, including its extension, but without any path/dirctory information.</param>
15+
/// <returns>A builder.</returns>
16+
public CopyFileAsAnAssetBuilder AsAnAssetWithTheFilename(string filename)
17+
=> new CopyFileAsAnAssetBuilder(sourcePath, filename);
18+
19+
/// <summary>
20+
/// Initializes a new instance of the <see cref="CopyFileAsAnAssetFilenameBuilder"/> class.
21+
/// </summary>
22+
/// <param name="sourcePath">The path to the source file which should be copied.</param>
23+
public CopyFileAsAnAssetFilenameBuilder(string sourcePath)
24+
{
25+
this.sourcePath = sourcePath ?? throw new System.ArgumentNullException(nameof(sourcePath));
26+
}
27+
}
28+
29+
/// <summary>
30+
/// A builder for customising the <see cref="CopyFileAsAnAsset"/> action.
31+
/// </summary>
32+
public class CopyFileAsAnAssetBuilder : IGetsPerformable
33+
{
34+
readonly string sourcePath, filename;
35+
36+
/// <summary>
37+
/// Gets a performable action, specifying a short human-readable summary of the asset.
38+
/// </summary>
39+
/// <param name="summary">A brief human-readable summary of the asset, which will not be used as a filename.</param>
40+
/// <returns>A performable action</returns>
41+
public CopyFileAsAnAsset WithTheSummary(string summary)
42+
=> new CopyFileAsAnAsset(sourcePath, filename, summary);
43+
44+
IPerformable IGetsPerformable.GetPerformable()
45+
=> new CopyFileAsAnAsset(sourcePath, filename);
46+
47+
/// <summary>
48+
/// Initializes a new instance of the <see cref="CopyFileAsAnAssetBuilder"/> class.
49+
/// </summary>
50+
/// <param name="sourcePath">The path to the source file which should be copied.</param>
51+
/// <param name="filename">The filename of the asset to create, including its extension, but without any path/dirctory information.</param>
52+
public CopyFileAsAnAssetBuilder(string sourcePath, string filename)
53+
{
54+
this.sourcePath = sourcePath ?? throw new System.ArgumentNullException(nameof(sourcePath));
55+
this.filename = filename ?? throw new System.ArgumentNullException(nameof(filename));
56+
}
57+
}
58+
}

CSF.Screenplay.Abstractions/Performables/ReadTheStopwatch.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace CSF.Screenplay.Performables
1212
/// <remarks>
1313
/// <para>
1414
/// This performable requires the actor has the ability <see cref="UseAStopwatch"/>.
15-
/// Use this performable via the builder method <see cref="StopwatchBuilder.ReadTheStopwatch"/>.
15+
/// Use this performable via the builder method <see cref="PerforamableBuilder.ReadTheStopwatch"/>.
1616
/// </para>
1717
/// </remarks>
1818
public class ReadTheStopwatch : IPerformableWithResult<TimeSpan>, ICanReport

CSF.Screenplay.Abstractions/Performables/ResetTheStopwatch.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace CSF.Screenplay.Performables
1111
/// <remarks>
1212
/// <para>
1313
/// This performable requires the actor has the ability <see cref="UseAStopwatch"/>.
14-
/// Use this performable via the builder method <see cref="StopwatchBuilder.ResetTheStopwatch"/>.
14+
/// Use this performable via the builder method <see cref="PerforamableBuilder.ResetTheStopwatch"/>.
1515
/// </para>
1616
/// </remarks>
1717
public class ResetTheStopwatch : IPerformable, ICanReport
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using System.IO;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using CSF.Screenplay.Abilities;
6+
7+
namespace CSF.Screenplay.Performables
8+
{
9+
/// <summary>
10+
/// A performable Action which saves a stream to an asset file.
11+
/// </summary>
12+
public class SaveAStreamAsAnAsset : IPerformable, ICanReport
13+
{
14+
readonly Stream stream;
15+
readonly string assetName, assetSummary;
16+
17+
/// <inheritdoc/>
18+
public ReportFragment GetReportFragment(Actor actor, IFormatsReportFragment formatter)
19+
=> formatter.Format("{Actor} saves a stream as an asset file named {Name}", actor, assetName);
20+
21+
/// <inheritdoc/>
22+
public async ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default)
23+
{
24+
var ability = actor.GetAbility<GetAssetFilePaths>();
25+
var path = ability.GetAssetFilePath(assetName);
26+
27+
using (var fileStream = File.Create(path))
28+
{
29+
await stream.CopyToAsync(fileStream, 81920, cancellationToken);
30+
}
31+
actor.RecordAsset(this, path, assetSummary);
32+
}
33+
34+
/// <summary>
35+
/// Initializes a new instance of <see cref="SaveAStreamAsAnAsset"/>.
36+
/// </summary>
37+
/// <param name="stream">The stream to save.</param>
38+
/// <param name="assetName">The name of the asset file.</param>
39+
/// <param name="assetSummary">An optional human-readable summary of the asset</param>
40+
public SaveAStreamAsAnAsset(Stream stream, string assetName, string assetSummary = null)
41+
{
42+
if (string.IsNullOrWhiteSpace(assetName))
43+
throw new ArgumentException($"'{nameof(assetName)}' cannot be null or whitespace.", nameof(assetName));
44+
45+
this.stream = stream ?? throw new ArgumentNullException(nameof(stream));
46+
this.assetName = assetName;
47+
this.assetSummary = assetSummary;
48+
}
49+
}
50+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System.IO;
2+
3+
namespace CSF.Screenplay.Performables
4+
{
5+
/// <summary>
6+
/// A builder for customising the <see cref="SaveAStreamAsAnAsset"/> action, specifying the asset filename.
7+
/// </summary>
8+
public class SaveStreamAsAnAssetFilenameBuilder
9+
{
10+
readonly Stream stream;
11+
12+
/// <summary>
13+
/// Gets a builder which may be used as a performable, or which may further customise the Action,
14+
/// having specified the filename for the asset.
15+
/// </summary>
16+
/// <param name="filename">The filename of the asset to create, including its extension, but without any path/dirctory information.</param>
17+
/// <returns>A builder.</returns>
18+
public SaveStreamAsAnAssetBuilder AsAnAssetWithTheFilename(string filename)
19+
=> new SaveStreamAsAnAssetBuilder(stream, filename);
20+
21+
/// <summary>
22+
/// Initializes a new instance of the <see cref="SaveStreamAsAnAssetFilenameBuilder"/> class.
23+
/// </summary>
24+
/// <param name="stream">The stream which is to be saved.</param>
25+
public SaveStreamAsAnAssetFilenameBuilder(Stream stream)
26+
{
27+
this.stream = stream ?? throw new System.ArgumentNullException(nameof(stream));
28+
}
29+
}
30+
31+
/// <summary>
32+
/// A builder for customising the <see cref="SaveAStreamAsAnAsset"/> action.
33+
/// </summary>
34+
public class SaveStreamAsAnAssetBuilder : IGetsPerformable
35+
{
36+
readonly Stream stream;
37+
readonly string filename;
38+
39+
/// <summary>
40+
/// Gets a performable action, specifying a short human-readable summary of the asset.
41+
/// </summary>
42+
/// <param name="summary">A brief human-readable summary of the asset, which will not be used as a filename.</param>
43+
/// <returns>A performable action</returns>
44+
public SaveAStreamAsAnAsset WithTheSummary(string summary)
45+
=> new SaveAStreamAsAnAsset(stream, filename, summary);
46+
47+
IPerformable IGetsPerformable.GetPerformable()
48+
=> new SaveAStreamAsAnAsset(stream, filename);
49+
50+
/// <summary>
51+
/// Initializes a new instance of the <see cref="CopyFileAsAnAssetBuilder"/> class.
52+
/// </summary>
53+
/// <param name="stream">The stream which is to be saved.</param>
54+
/// <param name="filename">The filename of the asset to create, including its extension, but without any path/dirctory information.</param>
55+
public SaveStreamAsAnAssetBuilder(Stream stream, string filename)
56+
{
57+
this.stream = stream ?? throw new System.ArgumentNullException(nameof(stream));
58+
this.filename = filename ?? throw new System.ArgumentNullException(nameof(filename));
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)