diff --git a/.appveyor.yml b/.appveyor.yml
index fa45fc94..1c075000 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -21,14 +21,17 @@ install:
cd CSF.Screenplay.JsonToHtmlReport.Template\src
npm ci
cd ..\..
+ # This was taken from https://stackoverflow.com/questions/60304251/unable-to-open-x-display-when-trying-to-run-google-chrome-on-centos-rhel-7-5
+ # It's the minimum dependencies for running Chrome in a headless environment on Linux
+ - sh: |
+ sudo apt-get update
+ sudo apt install -y xorg xvfb gtk2-engines-pixbuf dbus-x11 xfonts-base xfonts-100dpi xfonts-75dpi xfonts-cyrillic xfonts-scalable
before_build:
- dotnet --version
- dotnet restore --verbosity m
- dotnet clean
- - cmd: >
- IF NOT DEFINED APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH (SET BranchName=%APPVEYOR_REPO_BRANCH%)
- ELSE (SET BranchName=%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%)
+ - cmd: Tools\appveyor-setup-env.bat
- cmd: >
dotnet-sonarscanner begin
/k:"csf-dev_CSF.Screenplay"
@@ -36,9 +39,14 @@ before_build:
/o:craigfowler-github
/d:sonar.host.url=https://sonarcloud.io
/d:sonar.token=%SONARCLOUD_SECRET_KEY%
- /d:sonar.branch.name=%BranchName%
+ /d:%BranchParam%=%BranchName%
+ %PRParam%
/d:sonar.javascript.lcov.reportPaths=%APPVEYOR_BUILD_FOLDER%\CSF.Screenplay.JsonToHtmlReport.Template\src\TestResults\lcov.info
/s:%APPVEYOR_BUILD_FOLDER%\.sonarqube-analysisproperties.xml
+ # Activate Xvfb and export a display so that Chrome can run in Linux
+ - sh: |
+ Xvfb -ac :99 -screen 0 1280x1024x16 &
+ export DISPLAY=:99
build_script:
- dotnet build --no-incremental
diff --git a/.gitignore b/.gitignore
index c3b716f9..a7e4772f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,5 +4,6 @@ obj/
TestResults/
Tests/**/*.feature.cs
node_modules/
+*.orig
/CSF.Screenplay.JsonToHtmlReport/template/
/CSF.Screenplay.JsonToHtmlReport.Template/src/output/
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..074847c3
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,15 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "dotnet",
+ "projectPath": "${workspaceFolder}/Tests/CSF.Screenplay.Selenium.TestWebapp/CSF.Screenplay.Selenium.TestWebapp.csproj",
+ "name": "Selenium testing web app",
+ "request": "launch",
+
+ }
+ ]
+}
\ No newline at end of file
diff --git a/CSF.Screenplay.Abstractions/Abilities/GetAssetFilePaths.cs b/CSF.Screenplay.Abstractions/Abilities/GetAssetFilePaths.cs
new file mode 100644
index 00000000..4ab345a6
--- /dev/null
+++ b/CSF.Screenplay.Abstractions/Abilities/GetAssetFilePaths.cs
@@ -0,0 +1,39 @@
+using CSF.Screenplay.Reporting;
+
+namespace CSF.Screenplay.Abilities
+{
+ ///
+ /// Screenplay ability which gets the file system path for asset files generated by actors participating in the current performance.
+ ///
+ public class GetAssetFilePaths
+ {
+ readonly IGetsAssetFilePath pathProvider;
+
+ ///
+ /// Gets the file system path for the specified asset file.
+ ///
+ ///
+ ///
+ /// The returned file system path is an absolute path to which the asset file should be written. The path is determined by the
+ /// logic of the service . This means that the final filename will not be identical to the
+ /// but will include that base name within it.
+ ///
+ ///
+ /// If this method returns then the asset file should not be written to the file system.
+ ///
+ ///
+ /// A short descriptive file name fragment for the asset file, including the file extension.
+ /// The asset file path.
+ ///
+ public string GetAssetFilePath(string baseName) => pathProvider.GetAssetFilePath(baseName);
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The path provider used to get asset file paths.
+ public GetAssetFilePaths(IGetsAssetFilePath pathProvider)
+ {
+ this.pathProvider = pathProvider ?? throw new System.ArgumentNullException(nameof(pathProvider));
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSF.Screenplay.Abstractions/Actor.performer.cs b/CSF.Screenplay.Abstractions/Actor.performer.cs
index 8b95e54a..1726e2ee 100644
--- a/CSF.Screenplay.Abstractions/Actor.performer.cs
+++ b/CSF.Screenplay.Abstractions/Actor.performer.cs
@@ -79,7 +79,8 @@ protected virtual ValueTask PerformAsync(IPerformableWithResult perform
PerformableException GetPerformableException(object performable, Exception ex)
{
- return new PerformableException($"{Name} encountered an unexpected exception whilst performing a performable of type {performable.GetType().FullName}", ex)
+
+ return new PerformableException($"{Name} encountered an unexpected exception whilst executing the performable logic of {performable.GetType().FullName}", ex)
{
Performable = performable,
};
diff --git a/CSF.Screenplay.Abstractions/ActorExtensions.abilities.cs b/CSF.Screenplay.Abstractions/ActorExtensions.abilities.cs
index 8c2ac7e3..315e6262 100644
--- a/CSF.Screenplay.Abstractions/ActorExtensions.abilities.cs
+++ b/CSF.Screenplay.Abstractions/ActorExtensions.abilities.cs
@@ -80,6 +80,42 @@ public static object GetAbility(this ICanPerform actor, Type abilityType)
?? throw new InvalidOperationException($"{((IHasName) actor).Name} must have an ability of type {abilityType.FullName}");
}
+ /// Tries to get the first ability which the actor has of the specified type
+ /// The actor from whom to get the ability
+ /// If this method returns then this exposes the strongly-typed ability; if not then this value is undefined
+ /// The type of ability desired
+ /// if the actor has an ability of the specified type; if not.
+ /// If the is
+ /// If the actor does not implement
+ public static bool TryGetAbility(this ICanPerform actor, out T ability)
+ {
+ ability = default;
+ if (!TryGetAbility(actor, typeof(T), out var untypedAbility)) return false;
+ ability = (T) untypedAbility;
+ return true;
+ }
+
+ /// Gets the first ability which the actor has of the specified type
+ /// The actor from whom to get the ability
+ /// The type of ability desired
+ /// If this method returns then this exposes the strongly-typed ability; if not then this value is undefined
+ /// if the actor has an ability of the specified type; if not.
+ /// If any parameter is
+ public static bool TryGetAbility(this ICanPerform actor, Type abilityType, out object ability)
+ {
+ if(actor is null) throw new ArgumentNullException(nameof(actor));
+ if(abilityType is null) throw new ArgumentNullException(nameof(abilityType));
+
+ if(!actor.HasAbility(abilityType))
+ {
+ ability = default;
+ return false;
+ }
+
+ ability = actor.GetAbility(abilityType);
+ return true;
+ }
+
/// Adds an ability to the specified actor
/// The actor from whom to get the ability
/// The ability to add to the actor
diff --git a/CSF.Screenplay.Abstractions/Performances/IRelaysPerformanceEvents.cs b/CSF.Screenplay.Abstractions/Performances/IRelaysPerformanceEvents.cs
index cd92421a..bcb81c66 100644
--- a/CSF.Screenplay.Abstractions/Performances/IRelaysPerformanceEvents.cs
+++ b/CSF.Screenplay.Abstractions/Performances/IRelaysPerformanceEvents.cs
@@ -103,17 +103,15 @@ public interface IRelaysPerformanceEvents
///
/// Invokes an event indicating that a has begun.
///
- /// The performance identity
- /// The performance's hierarchical name
- void InvokePerformanceBegun(Guid performanceIdentity, IList namingHierarchy);
+ /// The performance
+ void InvokePerformanceBegun(IPerformance performance);
///
/// Invokes an event indicating that a has finished.
///
- /// The performance identity
- /// The performance's hierarchical name
+ /// The performance
/// A value indicating whether or not the performance was a success
- void InvokePerformanceFinished(Guid performanceIdentity, IList namingHierarchy, bool? success);
+ void InvokePerformanceFinished(IPerformance performance, bool? success);
#endregion
diff --git a/CSF.Screenplay.Abstractions/Performances/PerformanceCompleteEventArgs.cs b/CSF.Screenplay.Abstractions/Performances/PerformanceCompleteEventArgs.cs
index e3b1d6db..40eacacd 100644
--- a/CSF.Screenplay.Abstractions/Performances/PerformanceCompleteEventArgs.cs
+++ b/CSF.Screenplay.Abstractions/Performances/PerformanceCompleteEventArgs.cs
@@ -34,13 +34,10 @@ public class PerformanceFinishedEventArgs : PerformanceEventArgs
public bool? Success { get; }
/// Initialises a new instance of
- /// The performance identity
- /// The scenario hierarchy
+ /// The performance.
/// A value indicating whether or not the scenario completed with a succeess result
/// If the scenario hierarchy is
- public PerformanceFinishedEventArgs(Guid performanceIdentity,
- IReadOnlyList namingHierarchy,
- bool? success) : base(performanceIdentity, namingHierarchy)
+ public PerformanceFinishedEventArgs(IPerformance performance, bool? success) : base(performance)
{
Success = success;
}
diff --git a/CSF.Screenplay.Abstractions/Performances/PerformanceEventArgs.cs b/CSF.Screenplay.Abstractions/Performances/PerformanceEventArgs.cs
index 6806b4a9..efe6e465 100644
--- a/CSF.Screenplay.Abstractions/Performances/PerformanceEventArgs.cs
+++ b/CSF.Screenplay.Abstractions/Performances/PerformanceEventArgs.cs
@@ -10,6 +10,11 @@ namespace CSF.Screenplay.Performances
///
public class PerformanceEventArgs : PerformanceScopeEventArgs
{
+ ///
+ /// Gets the to which this event relates.
+ ///
+ public IPerformance Performance { get; }
+
/// Gets an ordered list of identifiers which indicate the 's name within an organisational hierarchy.
///
///
@@ -18,15 +23,14 @@ public class PerformanceEventArgs : PerformanceScopeEventArgs
///
///
///
- public IReadOnlyList NamingHierarchy { get; }
+ public IReadOnlyList NamingHierarchy => Performance.NamingHierarchy;
/// Initialises a new instance of
- /// The performance identity
- /// The screenplay naming hierarchy
+ /// The performance
/// If the scenario hierarchy is
- public PerformanceEventArgs(Guid performanceIdentity, IReadOnlyList namingHierarchy) : base(performanceIdentity)
+ public PerformanceEventArgs(IPerformance performance) : base(performance.PerformanceIdentity)
{
- NamingHierarchy = namingHierarchy ?? throw new ArgumentNullException(nameof(namingHierarchy));
+ Performance = performance;
}
}
}
\ No newline at end of file
diff --git a/CSF.Screenplay.Abstractions/Reporting/IGetsAssetFilePath.cs b/CSF.Screenplay.Abstractions/Reporting/IGetsAssetFilePath.cs
new file mode 100644
index 00000000..c783354a
--- /dev/null
+++ b/CSF.Screenplay.Abstractions/Reporting/IGetsAssetFilePath.cs
@@ -0,0 +1,29 @@
+namespace CSF.Screenplay.Reporting
+{
+ ///
+ /// A service which gets a filesystem path to which Screenplay asset files should be written, if they are to be written at all.
+ ///
+ public interface IGetsAssetFilePath
+ {
+ ///
+ /// Gets the filesystem path to which an asset file should be written.
+ ///
+ ///
+ ///
+ /// If reporting is disabled, for the same reasons as would return ,
+ /// then this method will also return .
+ /// In that case, reporting is disabled and no asset files should be written to the file system.
+ ///
+ ///
+ /// If reporting is enabled, then this method should return an absolute file system path to which an asset file should be written,
+ /// where the asset has the specified 'base name'. That base name should be a short filename fragment which describes the asset.
+ /// This file name will be embellished with other information by this method, such as to ensure that the file name is unique within
+ /// the current Screenplay run.
+ ///
+ ///
+ /// A short & descriptive filename fragment, which includes the file extension but no path information
+ /// An absolute file system path at which the asset file should be saved, or a reference indicating that
+ /// the asset file should not be saved.
+ string GetAssetFilePath(string baseName);
+ }
+}
\ No newline at end of file
diff --git a/CSF.Screenplay.Abstractions/Reporting/IGetsReportPath.cs b/CSF.Screenplay.Abstractions/Reporting/IGetsReportPath.cs
new file mode 100644
index 00000000..6ee81c59
--- /dev/null
+++ b/CSF.Screenplay.Abstractions/Reporting/IGetsReportPath.cs
@@ -0,0 +1,26 @@
+namespace CSF.Screenplay.Reporting
+{
+ ///
+ /// A service which gets the path to which the Screenplay report should be written.
+ ///
+ public interface IGetsReportPath
+ {
+ ///
+ /// Gets the path to which the report should be written.
+ ///
+ ///
+ ///
+ /// If the returned path is then Screenplay's reporting functionality should be disabled and no report should be written.
+ /// Otherwise, implementations of this interface should return an absolute file system path to which the report should be written.
+ /// This path must be writable by the executing process.
+ ///
+ ///
+ /// Reporting could be disabled if either the Screenplay Options report path is or a whitespace-only string, or if the path
+ /// indicated by those options is not writable.
+ ///
+ ///
+ /// The report path.
+ string GetReportPath();
+ }
+}
+
diff --git a/CSF.Screenplay.Docs/docfx.json b/CSF.Screenplay.Docs/docfx.json
index 945948b1..a9a9dacd 100644
--- a/CSF.Screenplay.Docs/docfx.json
+++ b/CSF.Screenplay.Docs/docfx.json
@@ -9,8 +9,8 @@
"docs/**",
"**/bin/**",
"**/obj/**",
- "Tests_old/**",
- "Tests/**"
+ "Tests/**",
+ "*_old/**"
]
}
],
diff --git a/CSF.Screenplay.JsonToHtmlReport/CSF.Screenplay.JsonToHtmlReport.csproj b/CSF.Screenplay.JsonToHtmlReport/CSF.Screenplay.JsonToHtmlReport.csproj
index 198c1413..b5b533fb 100644
--- a/CSF.Screenplay.JsonToHtmlReport/CSF.Screenplay.JsonToHtmlReport.csproj
+++ b/CSF.Screenplay.JsonToHtmlReport/CSF.Screenplay.JsonToHtmlReport.csproj
@@ -7,6 +7,9 @@
NU1903,NU1902CSF.Screenplay.JsonToHtmlReport$(MSBuildProjectDirectory)\bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml
+
+ false
diff --git a/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/AutoMoqDataAttribute.cs b/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/AutoMoqDataAttribute.cs
new file mode 100644
index 00000000..63115422
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/AutoMoqDataAttribute.cs
@@ -0,0 +1,14 @@
+using System;
+using Ploeh.AutoFixture;
+using Ploeh.AutoFixture.AutoMoq;
+using Ploeh.AutoFixture.NUnit3;
+
+namespace CSF.Screenplay.Selenium.Tests
+{
+ public class AutoMoqDataAttribute : AutoDataAttribute
+ {
+ public AutoMoqDataAttribute() : base(new Fixture().Customize(new AutoMoqCustomization()))
+ {
+ }
+ }
+}
diff --git a/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/CSF.Screenplay.Selenium.BrowserFlags.Tests.csproj b/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/CSF.Screenplay.Selenium.BrowserFlags.Tests.csproj
new file mode 100644
index 00000000..f1d2c1b4
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/CSF.Screenplay.Selenium.BrowserFlags.Tests.csproj
@@ -0,0 +1,83 @@
+
+
+
+ Debug
+ AnyCPU
+ {87454F03-FBB3-4506-9055-551297445891}
+ Library
+ CSF.Screenplay.Selenium.Tests
+ CSF.Screenplay.Selenium.BrowserFlags.Tests
+ v4.5
+ 1.0.0
+
+
+ true
+ full
+ false
+ bin\Debug
+ DEBUG;
+ prompt
+ 4
+
+
+ true
+ bin\Release
+ prompt
+ 4
+
+
+
+
+ ..\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll
+
+
+ ..\packages\AutoFixture.3.50.3\lib\net40\Ploeh.AutoFixture.dll
+
+
+ ..\packages\AutoFixture.AutoMoq.3.50.3\lib\net40\Ploeh.AutoFixture.AutoMoq.dll
+
+
+ ..\packages\AutoFixture.NUnit3.3.50.3\lib\net40\Ploeh.AutoFixture.NUnit3.dll
+
+
+ ..\packages\Castle.Core.4.0.0\lib\net45\Castle.Core.dll
+
+
+ ..\packages\Moq.4.7.25\lib\net45\Moq.dll
+
+
+ ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll
+
+
+ ..\packages\Selenium.WebDriver.3.4.0\lib\net40\WebDriver.dll
+
+
+
+ ..\packages\CSF.Configuration.1.1.2\lib\net45\CSF.Configuration.dll
+
+
+ ..\packages\CSF.WebDriverExtras.1.0.3\lib\net45\CSF.WebDriverExtras.dll
+
+
+
+
+
+
+
+
+
+ FooBrowser.flags.json
+
+
+
+
+
+
+
+
+ {0665F99E-DB05-4208-BCF1-137EF914CBF5}
+ CSF.Screenplay.Selenium.BrowserFlags
+
+
+
+
\ No newline at end of file
diff --git a/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/FooBrowser.flags.json b/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/FooBrowser.flags.json
new file mode 100644
index 00000000..d6c1fd8c
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/FooBrowser.flags.json
@@ -0,0 +1,8 @@
+[
+ {
+ "browserName": "FooBrowser",
+ "flags": [
+ "SampleFlag1",
+ ],
+ },
+]
\ No newline at end of file
diff --git a/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/GetBrowserFlagsDefinitionsTests.cs b/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/GetBrowserFlagsDefinitionsTests.cs
new file mode 100644
index 00000000..cd2fb6ca
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/GetBrowserFlagsDefinitionsTests.cs
@@ -0,0 +1,89 @@
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using CSF.WebDriverExtras.Flags;
+using Moq;
+using NUnit.Framework;
+
+namespace CSF.Screenplay.Selenium.Tests
+{
+ [TestFixture,Parallelizable(ParallelScope.All)]
+ public class GetBrowserFlagsDefinitionsTests
+ {
+ [Test,AutoMoqData]
+ public void GetDefinitions_from_test_assembly_returns_one_definition(IReadsFlagsDefinitions definitionReader,
+ FlagsDefinition def)
+ {
+ // Arrange
+ var testAssembly = Assembly.GetExecutingAssembly();
+ Mock.Get(definitionReader)
+ .Setup(x => x.GetFlagsDefinitions(It.IsAny()))
+ .Returns(new [] { def });
+ var sut = new GetBrowserFlagsDefinitions(definitionReader);
+
+ // Act
+ var result = sut.GetDefinitions(testAssembly);
+
+ // Assert
+ Assert.That(result, Has.Length.EqualTo(1));
+ }
+
+ [Test,AutoMoqData]
+ public void GetDefinitions_from_test_assembly_uses_definition_reader(IReadsFlagsDefinitions definitionReader,
+ FlagsDefinition def)
+ {
+ // Arrange
+ var testAssembly = Assembly.GetExecutingAssembly();
+ Mock.Get(definitionReader)
+ .Setup(x => x.GetFlagsDefinitions(It.IsAny()))
+ .Returns(new [] { def });
+ var sut = new GetBrowserFlagsDefinitions(definitionReader);
+
+ // Act
+ var result = sut.GetDefinitions(testAssembly);
+
+ // Assert
+ Mock.Get(definitionReader)
+ .Verify(x => x.GetFlagsDefinitions(It.IsAny()), Times.Once);
+ }
+
+ [Test,Category("Integration")]
+ public void GetDefinitions_integration_test_only_finds_one_definition()
+ {
+ // Arrange
+ var testAssembly = Assembly.GetExecutingAssembly();
+ var sut = new GetBrowserFlagsDefinitions();
+
+ // Act
+ var result = sut.GetDefinitions(testAssembly);
+
+ // Assert
+ Assert.That(result, Has.Length.EqualTo(1));
+ }
+
+ [Test,Category("Integration")]
+ public void GetDefinitions_integration_test_finds_definition_for_FooBrowser()
+ {
+ // Arrange
+ var testAssembly = Assembly.GetExecutingAssembly();
+ var sut = new GetBrowserFlagsDefinitions();
+
+ // Act
+ var result = sut.GetDefinitions(testAssembly).Single();
+
+ // Assert
+ Assert.That(result.BrowserNames.First(), Is.EqualTo("FooBrowser"));
+ }
+
+ [Test,Category("Integration")]
+ public void FromDefinitionsAssembly_integration_test_returns_more_than_two_flags_definitions()
+ {
+ // Act
+ var result = GetBrowserFlagsDefinitions.FromDefinitionsAssembly();
+
+ // Assert
+ Assert.That(result, Is.Not.Null);
+ Assert.That(result, Has.Length.GreaterThan(2));
+ }
+ }
+}
diff --git a/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/InvalidFlagsDefinition.json b/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/InvalidFlagsDefinition.json
new file mode 100644
index 00000000..c4b1955f
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/InvalidFlagsDefinition.json
@@ -0,0 +1,8 @@
+[
+ {
+ "browserName": "BarBrowser",
+ "flags": [
+ "SampleFlag1",
+ ],
+ },
+]
\ No newline at end of file
diff --git a/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/app.config b/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/app.config
new file mode 100644
index 00000000..437c6629
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/app.config
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/packages.config b/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/packages.config
new file mode 100644
index 00000000..3ab15217
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags.Tests_old/packages.config
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSF.Screenplay.Selenium.BrowserFlags_old/CSF.Screenplay.Selenium.BrowserFlags.csproj b/CSF.Screenplay.Selenium.BrowserFlags_old/CSF.Screenplay.Selenium.BrowserFlags.csproj
new file mode 100644
index 00000000..4a22cc42
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags_old/CSF.Screenplay.Selenium.BrowserFlags.csproj
@@ -0,0 +1,80 @@
+
+
+
+ Debug
+ AnyCPU
+ {0665F99E-DB05-4208-BCF1-137EF914CBF5}
+ Library
+ CSF.Screenplay.Selenium
+ CSF.Screenplay.Selenium.BrowserFlags
+ v4.5
+ 1.0.0
+
+
+ true
+ full
+ false
+ bin\Debug
+ DEBUG;
+ prompt
+ 4
+ bin\Debug\CSF.Screenplay.Selenium.BrowserFlags.xml
+ false
+
+
+ true
+ bin\Release
+ prompt
+ 4
+ bin\Release\CSF.Screenplay.Selenium.BrowserFlags.xml
+ false
+
+
+
+
+ ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll
+
+
+ ..\packages\Selenium.WebDriver.3.4.0\lib\net40\WebDriver.dll
+
+
+
+ ..\packages\CSF.Configuration.1.1.2\lib\net45\CSF.Configuration.dll
+
+
+ ..\packages\CSF.WebDriverExtras.1.0.3\lib\net45\CSF.WebDriverExtras.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ AppleSafari.flags.json
+
+
+ GoogleChrome.flags.json
+
+
+ InternetExplorer.flags.json
+
+
+ MicrosoftEdge.flags.json
+
+
+ MozillaFirefox.flags.json
+
+
+
+
\ No newline at end of file
diff --git a/CSF.Screenplay.Selenium.BrowserFlags_old/CSF.Screenplay.Selenium.BrowserFlags.nuspec b/CSF.Screenplay.Selenium.BrowserFlags_old/CSF.Screenplay.Selenium.BrowserFlags.nuspec
new file mode 100644
index 00000000..7e2b9106
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags_old/CSF.Screenplay.Selenium.BrowserFlags.nuspec
@@ -0,0 +1,21 @@
+
+
+
+ CSF.Screenplay.Selenium.BrowserFlags
+ 1.0.0
+ CSF.Screenplay.Selenium.BrowserFlags
+ CSF Software Ltd
+ MIT
+ https://github.com/csf-dev/CSF.Screenplay.Selenium
+ false
+ An assembly containing 'browser flags definitions' for CSF.Screenplay.Selenium. This indicates the quirks and behaviours of various popular web browsers which are compatible with Selenium WebDriver.
+ Copyright 2018
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/AppleSafari.flags.json b/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/AppleSafari.flags.json
new file mode 100644
index 00000000..286888f1
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/AppleSafari.flags.json
@@ -0,0 +1,15 @@
+[
+ {
+ "browserName": "Safari",
+ "flags": [
+ ],
+ },
+ {
+ "browserName": "Safari",
+ "minVersion": "11",
+ "flags": [
+ "HtmlElements.Select.RequiresUpdatesViaJavaScriptWorkaround",
+ ],
+ },
+]
+
diff --git a/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/GoogleChrome.flags.json b/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/GoogleChrome.flags.json
new file mode 100644
index 00000000..b62dc273
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/GoogleChrome.flags.json
@@ -0,0 +1,8 @@
+[
+ {
+ "browserName": "Chrome",
+ "flags": [
+ "HtmlElements.InputTypeDate.RequiresEntryUsingLocaleFormat",
+ ],
+ },
+]
\ No newline at end of file
diff --git a/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/InternetExplorer.flags.json b/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/InternetExplorer.flags.json
new file mode 100644
index 00000000..f92a921c
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/InternetExplorer.flags.json
@@ -0,0 +1,7 @@
+[
+ {
+ "browserName": [ "InternetExplorer", "Internet Explorer" ],
+ "flags": [
+ ],
+ },
+]
\ No newline at end of file
diff --git a/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/MicrosoftEdge.flags.json b/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/MicrosoftEdge.flags.json
new file mode 100644
index 00000000..d9fd8749
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/MicrosoftEdge.flags.json
@@ -0,0 +1,10 @@
+[
+ {
+ "browserName": "MicrosoftEdge",
+ "flags": [
+ "Browser.CannotClearDomainCookies",
+ "HtmlElements.SelectMultiple.RequiresCtrlClickToToggleOptionSelection",
+ "HtmlElements.InputTypeDate.RequiresInputViaJavaScriptWorkaround",
+ ],
+ },
+]
\ No newline at end of file
diff --git a/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/MozillaFirefox.flags.json b/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/MozillaFirefox.flags.json
new file mode 100644
index 00000000..c8e38046
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags_old/Definitions/MozillaFirefox.flags.json
@@ -0,0 +1,8 @@
+[
+ {
+ "browserName": "Firefox",
+ "flags": [
+ "HtmlElements.InputTypeDate.RequiresInputViaJavaScriptWorkaround"
+ ],
+ },
+]
diff --git a/CSF.Screenplay.Selenium.BrowserFlags_old/Flags.Browser.cs b/CSF.Screenplay.Selenium.BrowserFlags_old/Flags.Browser.cs
new file mode 100644
index 00000000..b1cef97f
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags_old/Flags.Browser.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace CSF.Screenplay.Selenium
+{
+ public static partial class Flags
+ {
+ ///
+ /// Flags relating to the web browser itself, such as management of cookies.
+ ///
+ public static class Browser
+ {
+ ///
+ /// Indicates that the web driver is incapable of clearing the cookies for the current domain.
+ ///
+ ///
+ ///
+ /// The current domain is the domain for the page upon which the web driver is currently viewing.
+ ///
+ ///
+ public static readonly string CannotClearDomainCookies = "Browser.CannotClearDomainCookies";
+ }
+ }
+}
diff --git a/CSF.Screenplay.Selenium.BrowserFlags_old/Flags.HtmlElements.cs b/CSF.Screenplay.Selenium.BrowserFlags_old/Flags.HtmlElements.cs
new file mode 100644
index 00000000..3be266d8
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags_old/Flags.HtmlElements.cs
@@ -0,0 +1,55 @@
+using System;
+namespace CSF.Screenplay.Selenium
+{
+ public static partial class Flags
+ {
+ ///
+ /// Flags relating to specific HTML elements.
+ ///
+ public static class HtmlElements
+ {
+ ///
+ /// Flags relating to HTML <input type="date"> elements.
+ ///
+ public static class InputTypeDate
+ {
+ ///
+ /// Indicates that the web driver may be used to enter a date using a format that conforms to the web browser's
+ /// current locale setting.
+ ///
+ public static readonly string RequiresEntryUsingLocaleFormat = "HtmlElements.InputTypeDate.RequiresEntryUsingLocaleFormat";
+
+ ///
+ /// Indicates that the web driver must use a JavaScript workaround to set the date, because it is impossible to do so
+ /// by typing keys.
+ ///
+ public static readonly string RequiresInputViaJavaScriptWorkaround = "HtmlElements.InputTypeDate.RequiresInputViaJavaScriptWorkaround";
+ }
+
+ ///
+ /// Flags relating to HTML <select multiple> elements.
+ ///
+ public static class SelectMultiple
+ {
+ ///
+ /// Indicates that the web driver must send Ctrl+Click in order to toggle the selection of a single
+ /// option within the select element. Without Ctrl, the click is interpreted as "change entire selection to just
+ /// the one option clicked".
+ ///
+ public static readonly string RequiresCtrlClickToToggleOptionSelection = "HtmlElements.SelectMultiple.RequiresCtrlClickToToggleOptionSelection";
+ }
+
+ ///
+ /// Flags relating to HTML <select> elements.
+ ///
+ public static class Select
+ {
+ ///
+ /// Indicates that the browser requires a JavaScript workaround in order to change the selection state
+ /// of an HTML <select> element.
+ ///
+ public static readonly string RequiresUpdatesViaJavaScriptWorkaround = "HtmlElements.Select.RequiresUpdatesViaJavaScriptWorkaround";
+ }
+ }
+ }
+}
diff --git a/CSF.Screenplay.Selenium.BrowserFlags_old/Flags.cs b/CSF.Screenplay.Selenium.BrowserFlags_old/Flags.cs
new file mode 100644
index 00000000..2a0f2e1c
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags_old/Flags.cs
@@ -0,0 +1,10 @@
+using System;
+namespace CSF.Screenplay.Selenium
+{
+ ///
+ /// A catalogue of the the various browser flags of which this library is aware.
+ ///
+ public static partial class Flags
+ {
+ }
+}
diff --git a/CSF.Screenplay.Selenium.BrowserFlags_old/GetBrowserFlagsDefinitions.cs b/CSF.Screenplay.Selenium.BrowserFlags_old/GetBrowserFlagsDefinitions.cs
new file mode 100644
index 00000000..d0a30191
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags_old/GetBrowserFlagsDefinitions.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using CSF.WebDriverExtras.Flags;
+
+namespace CSF.Screenplay.Selenium
+{
+ ///
+ /// Helper type which gets browser flags definitions stored inside an assembly as embedded resources,
+ /// using the resource suffix .flags.json.
+ ///
+ public sealed class GetBrowserFlagsDefinitions
+ {
+ #region constants
+
+ const string FlagsResourceSuffix = ".flags.json";
+
+ #endregion
+
+ #region fields
+
+ readonly IReadsFlagsDefinitions definitionReader;
+ readonly object syncRoot;
+ IReadOnlyCollection cachedDefinitions;
+
+ #endregion
+
+ #region properties
+
+ Assembly ThisAssembly => Assembly.GetExecutingAssembly();
+
+ #endregion
+
+ #region public methods
+
+ ///
+ /// Gets the browser flags definitions contained within the CSF.Screenplay.Selenium.BrowserFlags assembly.
+ ///
+ /// The browser flags definitions.
+ public IReadOnlyCollection GetDefinitions() => GetDefinitions(ThisAssembly);
+
+ ///
+ /// Gets the browser flags definitions contained within the given assembly.
+ ///
+ /// The browser flags definitions.
+ /// An assembly to search for flags definitions.
+ public IReadOnlyCollection GetDefinitions(Assembly assembly)
+ {
+ if(assembly == null)
+ throw new ArgumentNullException(nameof(assembly));
+
+ if(assembly != ThisAssembly)
+ {
+ return GetDefinitionsFromAssembly(assembly);
+ }
+
+ lock(syncRoot)
+ {
+ if(cachedDefinitions != null)
+ return cachedDefinitions;
+
+ cachedDefinitions = GetDefinitionsFromAssembly(assembly);
+ return cachedDefinitions;
+ }
+ }
+
+ #endregion
+
+ #region methods
+
+ IReadOnlyCollection GetDefinitionsFromAssembly(Assembly assembly)
+ {
+ var resourceNames = GetDefinitionsResourceNames(assembly);
+ return resourceNames
+ .SelectMany(x => GetDefinitions(x, assembly))
+ .ToArray();
+ }
+
+ IEnumerable GetDefinitionsResourceNames(Assembly assembly)
+ => assembly
+ .GetManifestResourceNames()
+ .Where(x => x.EndsWith(FlagsResourceSuffix, StringComparison.InvariantCulture));
+
+ IReadOnlyCollection GetDefinitions(string resourceName, Assembly assembly)
+ {
+ using(var stream = assembly.GetManifestResourceStream(resourceName))
+ return definitionReader.GetFlagsDefinitions(stream);
+ }
+
+ #endregion
+
+ #region constructor
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GetBrowserFlagsDefinitions() : this(null) {}
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Definition reader.
+ public GetBrowserFlagsDefinitions(IReadsFlagsDefinitions definitionReader)
+ {
+ syncRoot = new object();
+ this.definitionReader = definitionReader ?? new DefinitionReader();
+ }
+
+ #endregion
+
+ #region static methods
+
+ ///
+ /// Helper method which gets the flags definitions from the CSF.Screenplay.Selenium.BrowserFlags assembly.
+ ///
+ /// The dlags definitions.
+ public static IReadOnlyCollection FromDefinitionsAssembly()
+ => new GetBrowserFlagsDefinitions().GetDefinitions();
+
+ #endregion
+ }
+}
diff --git a/CSF.Screenplay.Selenium.BrowserFlags_old/Properties/AssemblyInfo.cs b/CSF.Screenplay.Selenium.BrowserFlags_old/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..d14392e2
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags_old/Properties/AssemblyInfo.cs
@@ -0,0 +1,26 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle("CSF.Screenplay.Selenium.BrowserFlags")]
+[assembly: AssemblyDescription("A repository of pre-defined Browser Flags for CSF.Screenplay.Selenium")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("CSF Software Limited")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Craig Fowler")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion("1.0.0.0")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
diff --git a/CSF.Screenplay.Selenium.BrowserFlags_old/ScreenplayLocator.cs b/CSF.Screenplay.Selenium.BrowserFlags_old/ScreenplayLocator.cs
new file mode 100644
index 00000000..713eab14
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags_old/ScreenplayLocator.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Collections.Concurrent;
+using System.Reflection;
+using NUnit.Framework.Interfaces;
+
+namespace CSF.Screenplay
+{
+ ///
+ /// A small static service locator of sorts, dedicated to getting an appropriate instance of for
+ /// a specified test object.
+ ///
+ ///
+ ///
+ /// This type uses reflection to find the which decorates the assembly in which the
+ /// specified object (a test, a test method or the assembly itself) resides.
+ /// It additionally caches the results in-memory to avoid repetitive reflection, only to retrieve the same results.
+ ///
+ ///
+ public static class ScreenplayLocator
+ {
+ static readonly ConcurrentDictionary screenplayCache = new ConcurrentDictionary();
+
+ ///
+ /// Gets a instance from the specified .
+ ///
+ ///
+ ///
+ /// This method makes use of the which decorates the assembly to get a
+ /// Screenplay object instance for that assembly.
+ /// If the specified assembly is not decorated with the Screenplay assembly attribute then this method will raise
+ /// an exception.
+ ///
+ ///
+ /// The test assembly for which to get a Screenplay object.
+ /// The Screenplay object for the specified assembly.
+ /// If is .
+ /// If the is not decorated with .
+ public static Screenplay GetScreenplay(Assembly assembly)
+ {
+ if (assembly is null) throw new ArgumentNullException(nameof(assembly));
+ return screenplayCache.GetOrAdd(assembly, GetScreenplayFromAssembly);
+ }
+
+ ///
+ /// Gets a instance from the specified test method.
+ ///
+ ///
+ ///
+ /// This method makes use of the which decorates the assembly in which the
+ /// specified method was declared, to get a Screenplay object instance applicable to the test method.
+ /// If the method's assembly is not decorated with the Screenplay assembly attribute then this method will raise
+ /// an exception.
+ ///
+ ///
+ /// The test method for which to get a Screenplay object.
+ /// The Screenplay object for the specified test method.
+ /// If is .
+ /// If the 's assembly is or
+ /// is not decorated with .
+ public static Screenplay GetScreenplay(IMethodInfo method)
+ {
+ if (method is null) throw new ArgumentNullException(nameof(method));
+
+ var assembly = method.MethodInfo?.DeclaringType?.Assembly;
+ if (assembly is null)
+ throw new ArgumentException($"The test method must have an associated {nameof(Assembly)}.", nameof(method));
+ return GetScreenplay(assembly);
+ }
+
+ ///
+ /// Gets a instance from the specified test.
+ ///
+ ///
+ ///
+ /// This method makes use of the which decorates the assembly in which the
+ /// specified test's method was declared, to get a Screenplay object instance applicable to the test method.
+ /// If the test's method's assembly is not decorated with the Screenplay assembly attribute then this method will raise
+ /// an exception.
+ ///
+ ///
+ /// The test for which to get a Screenplay object.
+ /// The Screenplay object for the specified test.
+ /// If is .
+ /// If the 's method's assembly is or
+ /// is not decorated with .
+ public static Screenplay GetScreenplay(ITest test)
+ {
+ if(test is null) throw new ArgumentNullException(nameof(test));
+ return GetScreenplay(test.Method);
+ }
+
+ static Screenplay GetScreenplayFromAssembly(Assembly assembly)
+ {
+ var assemblyAttrib = assembly.GetCustomAttribute()
+ ?? throw new ArgumentException($"The assembly {assembly.FullName} must be decorated with {nameof(ScreenplayAssemblyAttribute)}.", nameof(assembly));
+ return assemblyAttrib.GetScreenplay();
+ }
+ }
+}
diff --git a/CSF.Screenplay.Selenium.BrowserFlags_old/packages.config b/CSF.Screenplay.Selenium.BrowserFlags_old/packages.config
new file mode 100644
index 00000000..51dabf90
--- /dev/null
+++ b/CSF.Screenplay.Selenium.BrowserFlags_old/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/CSF.Screenplay.Selenium.JavaScriptWorkarounds.csproj b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/CSF.Screenplay.Selenium.JavaScriptWorkarounds.csproj
new file mode 100644
index 00000000..fb372e78
--- /dev/null
+++ b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/CSF.Screenplay.Selenium.JavaScriptWorkarounds.csproj
@@ -0,0 +1,76 @@
+
+
+
+ Debug
+ AnyCPU
+ {CC20F88D-C4B9-4FDB-B5B1-7349547430FA}
+ Library
+ CSF.Screenplay.Selenium
+ CSF.Screenplay.Selenium.JavaScriptWorkarounds
+ v4.5
+ 1.0.0
+
+
+ true
+ full
+ false
+ bin\Debug
+ DEBUG;
+ prompt
+ 4
+ bin\Debug\CSF.Screenplay.Selenium.JavaScriptWorkarounds.xml
+ false
+ true
+
+
+ true
+ bin\Release
+ prompt
+ 4
+ bin\Release\CSF.Screenplay.Selenium.JavaScriptWorkarounds.xml
+ false
+
+
+
+
+ ..\packages\Selenium.WebDriver.3.4.0\lib\net40\WebDriver.dll
+
+
+
+ ..\packages\CSF.Reflection.1.0.3\lib\net45\CSF.Reflection.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/Properties/AssemblyInfo.cs b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..13b84a46
--- /dev/null
+++ b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/Properties/AssemblyInfo.cs
@@ -0,0 +1,26 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle("CSF.Screenplay.Selenium.JavaScriptWorkarounds")]
+[assembly: AssemblyDescription("Workarounds, using JavaScript, for browser-specific issues relating to Selenium actions")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("CSF Software Limited")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Craig Fowler")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion("1.0.0.0")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
diff --git a/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/ArgumentsArrayValidator.cs b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/ArgumentsArrayValidator.cs
new file mode 100644
index 00000000..06e95245
--- /dev/null
+++ b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/ArgumentsArrayValidator.cs
@@ -0,0 +1,57 @@
+//
+// ArgumentsArrayValidator.cs
+//
+// Author:
+// Craig Fowler
+//
+// Copyright (c) 2018 Craig Fowler
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using CSF.Screenplay.Selenium.StoredScripts;
+
+namespace CSF.Screenplay.Selenium.ScriptResources
+{
+ ///
+ /// A stored JavaScript which validates a collection of arguments passed as an array.
+ ///
+ public class ArgumentsArrayValidator : ScriptResource
+ {
+ const string EntryPointNameConst = "validateArgumentsArray";
+
+ ///
+ /// Gets the name of the entry point for this script (which differs from the default).
+ ///
+ /// The name of the entry point.
+ public static string EntryPointName => EntryPointNameConst;
+
+ ///
+ /// Gets the name of this script.
+ ///
+ /// The name.
+ public override string Name => "a JavaScript which validates function arguments arrays";
+
+ ///
+ /// Gets the name of the entry point to the script - this is the function exposed by
+ /// .
+ ///
+ /// The name of the entry point function.
+ public override string GetEntryPointName() => EntryPointName;
+ }
+}
diff --git a/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/ArgumentsArrayValidator.js b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/ArgumentsArrayValidator.js
new file mode 100644
index 00000000..6b653445
--- /dev/null
+++ b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/ArgumentsArrayValidator.js
@@ -0,0 +1,87 @@
+var argumentsArrayValidator = function()
+{
+ 'use strict';
+
+ function getLengthParams(min, max)
+ {
+ if(typeof min === 'number' && max === undefined)
+ {
+ return {
+ count: min,
+ validateCount: true,
+ };
+ }
+
+ var minCount, validateMinCount = false, maxCount, validateMaxCount = false;
+
+ if(typeof min === 'number')
+ {
+ minCount = min;
+ validateMinCount = true;
+ }
+
+ if(typeof max === 'number')
+ {
+ maxCount = max;
+ validateMaxCount = true;
+ }
+
+ return {
+ min: minCount,
+ validateMin: validateMinCount,
+ max: maxCount,
+ validateMax: validateMaxCount,
+ validateCount: false,
+ };
+ }
+
+ function validateType(argsArray)
+ {
+ if(!argsArray) throw new Error('Arguments must be a non-null array.');
+ if(!(argsArray instanceof Array)) throw new Error('Arguments must be an array.');
+ }
+
+ function validateLength(argsArray, lengthParams)
+ {
+ if(lengthParams.validateCount && argsArray.length !== lengthParams.count)
+ throw new Error('Arguments array must contain precisely ' + lengthParams.count + ' item(s).');
+
+ if(lengthParams.validateMin && argsArray.length < lengthParams.min)
+ throw new Error('Arguments array must contain at least ' + lengthParams.min + ' item(s).');
+
+ if(lengthParams.validateMax && argsArray.length > lengthParams.max)
+ throw new Error('Arguments array must contain no more than ' + lengthParams.max + ' item(s).');
+ }
+
+ function validate(argsArray, min, max)
+ {
+ validateType(argsArray);
+ var lengthParams = getLengthParams(min, max);
+ validateLength(argsArray, lengthParams);
+
+ return true;
+ }
+
+ function selfValidate(argsArray, min, max)
+ {
+ try
+ {
+ return validate(argsArray, min, max);
+ }
+ catch(e) { throw new Error('The call to \'validateArgumentsArray\' raised an error: ' + e.message); }
+ }
+
+ return {
+ validate: validate,
+ selfValidate: selfValidate,
+ };
+}();
+
+function validateArgumentsArray(argsArray)
+{
+ 'use strict';
+
+ validator.selfValidate(argsArray, 1, 3);
+ var arrayToValidate = argsArray[0], min = argsArray[1], max = argsArray[2];
+ return validator.validate(arrayToValidate, min, max);
+}
\ No newline at end of file
diff --git a/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/GetALocalisedDate.cs b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/GetALocalisedDate.cs
new file mode 100644
index 00000000..8cb0206f
--- /dev/null
+++ b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/GetALocalisedDate.cs
@@ -0,0 +1,64 @@
+//
+// GetALocalisedDate.cs
+//
+// Author:
+// Craig Fowler
+//
+// Copyright (c) 2018 Craig Fowler
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using CSF.Screenplay.Selenium.StoredScripts;
+
+namespace CSF.Screenplay.Selenium.ScriptResources
+{
+ ///
+ /// Script resource for getting a localised date string
+ ///
+ public class GetALocalisedDate : ScriptResource
+ {
+ readonly ScriptResource argsValidator;
+
+ ///
+ /// Gets the name of this script.
+ ///
+ /// The name.
+ public override string Name => "a JavaScript which converts a date to a locale-formatted string";
+
+ ///
+ /// Gets a collection of scripts which the current script instance depends upon.
+ ///
+ /// The dependencies.
+ protected override ScriptResource[] GetDependencies() => new [] { argsValidator };
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GetALocalisedDate() : this(null) {}
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Arguments validator.
+ public GetALocalisedDate(ScriptResource argsValidator)
+ {
+ this.argsValidator = argsValidator ?? new ArgumentsArrayValidator();
+ }
+ }
+}
diff --git a/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/GetALocalisedDate.js b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/GetALocalisedDate.js
new file mode 100644
index 00000000..9456b68c
--- /dev/null
+++ b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/GetALocalisedDate.js
@@ -0,0 +1,17 @@
+function executeScript(argsArray) {
+ 'use strict';
+
+ argumentsArrayValidator.validate(argsArray, 3);
+
+ var
+ year = argsArray[0],
+ month = argsArray[1],
+ day = argsArray[2],
+ theDate;
+
+ if(typeof year !== 'number' || typeof month !== 'number' || typeof day !== 'number')
+ throw new Error('The supplied year, month and day must all be numbers.');
+
+ theDate = new Date(year, month, day);
+ return theDate.toLocaleDateString();
+}
\ No newline at end of file
diff --git a/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/GetDocumentReadyState.cs b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/GetDocumentReadyState.cs
new file mode 100644
index 00000000..bff50835
--- /dev/null
+++ b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/GetDocumentReadyState.cs
@@ -0,0 +1,42 @@
+//
+// GetDocumentReadyState.cs
+//
+// Author:
+// Craig Fowler
+//
+// Copyright (c) 2018 Craig Fowler
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using CSF.Screenplay.Selenium.StoredScripts;
+
+namespace CSF.Screenplay.Selenium.ScriptResources
+{
+ ///
+ /// Script resource for getting the document ready state.
+ ///
+ public class GetDocumentReadyState : ScriptResource
+ {
+ ///
+ /// Gets the name of this script.
+ ///
+ /// The name.
+ public override string Name => "a JavaScript which gets the current web page's ready-state";
+ }
+}
diff --git a/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/GetDocumentReadyState.js b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/GetDocumentReadyState.js
new file mode 100644
index 00000000..b8953856
--- /dev/null
+++ b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/GetDocumentReadyState.js
@@ -0,0 +1,4 @@
+function executeScript(argsArray) {
+ 'use strict';
+ return document.readyState;
+}
\ No newline at end of file
diff --git a/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/SetAnElementAttribute.cs b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/SetAnElementAttribute.cs
new file mode 100644
index 00000000..93126cf8
--- /dev/null
+++ b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/SetAnElementAttribute.cs
@@ -0,0 +1,48 @@
+//
+// SetAnElementAttribute.cs
+//
+// Author:
+// Craig Fowler
+//
+// Copyright (c) 2018 Craig Fowler
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using CSF.Screenplay.Selenium.StoredScripts;
+
+namespace CSF.Screenplay.Selenium.ScriptResources
+{
+ ///
+ /// Script resource for setting an attribute upon an element.
+ ///
+ public class SetAnElementAttribute : ScriptResource
+ {
+ ///
+ /// Gets the name of this script.
+ ///
+ /// The name.
+ public override string Name => "a JavaScript which sets an attribute upon an element";
+
+ ///
+ /// Gets a collection of scripts which the current script instance depends upon.
+ ///
+ /// The dependencies.
+ protected override ScriptResource[] GetDependencies() => new [] { new ArgumentsArrayValidator() };
+ }
+}
diff --git a/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/SetAnElementAttribute.js b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/SetAnElementAttribute.js
new file mode 100644
index 00000000..79c5f7e9
--- /dev/null
+++ b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/SetAnElementAttribute.js
@@ -0,0 +1,24 @@
+function executeScript(argsArray)
+{
+ 'use strict';
+
+ argumentsArrayValidator.validate(argsArray, 3);
+
+ var htmlElement = argsArray[0], attributeName = argsArray[1], newValue = argsArray[2];
+
+ function validateElement(element)
+ {
+ if(element === null || element === undefined)
+ throw new Error('You must provide an HTML element object.');
+ if(!(element instanceof HTMLElement))
+ throw new Error('The element must be an HTML element.');
+ }
+
+ validateElement(htmlElement);
+ if(newValue === null)
+ htmlElement.removeAttribute(attributeName);
+ else
+ htmlElement.setAttribute(attributeName, newValue);
+
+ return true;
+}
diff --git a/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/SetAnElementValue.cs b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/SetAnElementValue.cs
new file mode 100644
index 00000000..3b9279da
--- /dev/null
+++ b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/SetAnElementValue.cs
@@ -0,0 +1,64 @@
+//
+// SetAnElementValue.cs
+//
+// Author:
+// Craig Fowler
+//
+// Copyright (c) 2018 Craig Fowler
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using CSF.Screenplay.Selenium.StoredScripts;
+
+namespace CSF.Screenplay.Selenium.ScriptResources
+{
+ ///
+ /// A stored JavaScript which sets the value of an HTML element and then triggers the 'change' event for that element.
+ ///
+ public class SetAnElementValue : ScriptResource
+ {
+ readonly ScriptResource argsValidator;
+
+ ///
+ /// Gets the name of this script.
+ ///
+ /// The name.
+ public override string Name => "a JavaScript to set the value of an HTML element";
+
+ ///
+ /// Gets a collection of scripts which the current script instance depends upon.
+ ///
+ /// The dependencies.
+ protected override ScriptResource[] GetDependencies() => new [] { argsValidator };
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SetAnElementValue() : this(null) {}
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Arguments validator.
+ public SetAnElementValue(ScriptResource argsValidator)
+ {
+ this.argsValidator = argsValidator ?? new ArgumentsArrayValidator();
+ }
+ }
+}
diff --git a/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/SetAnElementValue.js b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/SetAnElementValue.js
new file mode 100644
index 00000000..d93f5b4f
--- /dev/null
+++ b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/SetAnElementValue.js
@@ -0,0 +1,36 @@
+function executeScript(argsArray)
+{
+ 'use strict';
+
+ argumentsArrayValidator.validate(argsArray, 2);
+
+ var htmlElement = argsArray[0], newValue = argsArray[1];
+
+ function validateElement(element)
+ {
+ if(element === null || element === undefined)
+ throw new Error('You must provide an HTML element object.');
+ if(!(element instanceof HTMLElement))
+ throw new Error('The element must be an HTML element.');
+ if(element.value === undefined)
+ throw new Error('The element must have a \'value\' property.');
+ }
+
+ function setValue(element, val)
+ {
+ element.value = val;
+ }
+
+ function triggerChangeEvent(element)
+ {
+ var ev = document.createEvent('Event');
+ ev.initEvent('change', true, true);
+ element.dispatchEvent(ev);
+ }
+
+ validateElement(htmlElement);
+ setValue(htmlElement, newValue);
+ triggerChangeEvent(htmlElement);
+
+ return true;
+}
diff --git a/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/UpdateSelectElementSelection.cs b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/UpdateSelectElementSelection.cs
new file mode 100644
index 00000000..ef9a59a6
--- /dev/null
+++ b/CSF.Screenplay.Selenium.JavaScriptWorkarounds_old/ScriptResources/UpdateSelectElementSelection.cs
@@ -0,0 +1,83 @@
+//
+// UpdateSelectElementSelection.cs
+//
+// Author:
+// Craig Fowler
+//
+// Copyright (c) 2018 Craig Fowler
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using CSF.Screenplay.Selenium.StoredScripts;
+
+namespace CSF.Screenplay.Selenium.ScriptResources
+{
+ ///
+ /// A stored JavaScript which sets the selected option(s) for an HTML <select> element.
+ ///
+ public class UpdateSelectElementSelection : ScriptResource
+ {
+ ///
+ /// Gets the action name for deselecting every option.
+ ///
+ public static readonly string DeselectAllActionName = "deselectAll";
+
+ ///
+ /// Gets the action name for selecting an option by its zero-based index.
+ ///
+ public static readonly string SelectByIndexActionName = "selectByIndex";
+
+ ///
+ /// Gets the action name for selecting an option by its value.
+ ///
+ public static readonly string SelectByValueActionName = "selectByValue";
+
+ ///
+ /// Gets the action name for selecting an option by its displyed text.
+ ///
+ public static readonly string SelectByTextActionName = "selectByText";
+
+ ///
+ /// Gets the action name for deselecting an option by its zero-based index.
+ ///
+ public static readonly string DeselectByIndexActionName = "deselectByIndex";
+
+ ///
+ /// Gets the action name for deselecting an option by its value.
+ ///
+ public static readonly string DeselectByValueActionName = "deselectByValue";
+
+ ///
+ /// Gets the action name for deselecting an option by its displayed text.
+ ///
+ public static readonly string DeselectByTextActionName = "deselectByText";
+
+ ///
+ /// Gets the name of this script.
+ ///
+ /// The name.
+ public override string Name => "a JavaScript to set the select options of an HTML