├── .appveyor.yml ├── .gitignore ├── .sonarqube-analysisproperties.xml ├── .vscode ├── extensions.json ├── settings.json └── tasks.json ├── CSF.Screenplay.Abstractions ├── Abilities │ └── UseAStopwatch.cs ├── Actor.abilities.cs ├── Actor.cs ├── Actor.disposable.cs ├── Actor.events.cs ├── Actor.performer.cs ├── Actor.phases.cs ├── ActorExtensions.abilities.cs ├── ActorExtensions.performableBuilders.cs ├── Actors │ ├── ActorEventArgs.cs │ ├── GainAbilityEventArgs.cs │ ├── ICanPerformGiven.cs │ ├── ICanPerformThen.cs │ ├── ICanPerformWhen.cs │ ├── IHasPerformableEvents.cs │ ├── PerformableAssetEventArgs.cs │ ├── PerformableEventArgs.cs │ ├── PerformableFailureEventArgs.cs │ ├── PerformableResultEventArgs.cs │ └── PerformancePhase.cs ├── CSF.Screenplay.Abstractions.csproj ├── CastExtensions.cs ├── ICanPerform.cs ├── ICanReport.cs ├── ICast.cs ├── IFormatsReportFragment.cs ├── IHasAbilities.cs ├── IHasName.cs ├── IHasPerformanceIdentity.cs ├── IHasServiceProvider.cs ├── IHostsPerformance.cs ├── IPerformable.cs ├── IPerformableWithResult.cs ├── IPerformableWithResult.generic.cs ├── IPerformance.cs ├── IPersona.cs ├── IStage.cs ├── Performables │ ├── IGetsPerformable.cs │ ├── IGetsPerformableWithResult.cs │ ├── IGetsPerformableWithResult.generic.cs │ ├── IProvidesTimeSpan.cs │ ├── PerformableException.cs │ ├── ReadTheStopwatch.cs │ ├── ResetTheStopwatch.cs │ ├── StartTheStopwatch.cs │ ├── StopTheStopwatch.cs │ ├── StopwatchBuilder.cs │ ├── TimeSpanBuilder.cs │ └── TimeSpanBuilder.generic.cs ├── PerformanceStarter.cs ├── Performances │ ├── IBeginsAndEndsPerformance.cs │ ├── ICreatesPerformance.cs │ ├── IHasPerformanceEvents.cs │ ├── IRelaysPerformanceEvents.cs │ ├── IdentifierAndName.cs │ ├── PerformanceCompleteEventArgs.cs │ ├── PerformanceEventArgs.cs │ ├── PerformanceScopeEventArgs.cs │ └── PerformanceState.cs ├── ReportFragment.cs ├── Reporting │ ├── IFormattableValue.cs │ ├── IFormatterRegistry.cs │ ├── IGetsReportFormat.cs │ ├── IGetsValueFormatter.cs │ ├── IReporter.cs │ ├── IValueFormatter.cs │ ├── NameAndValue.cs │ ├── ReportFormat.cs │ └── ValueFormatterExtensions.cs ├── Resources │ ├── AbilityReportStrings.cs │ ├── AbilityReportStrings.restext │ ├── PerformableReportStrings.cs │ └── PerformableReportStrings.restext └── StageExtensions.cs ├── CSF.Screenplay.Docs ├── .nojekyll ├── CSF.Screenplay.Docs.proj ├── README.md ├── docfx.json ├── docs │ ├── GettingReports.md │ ├── HowScreenplayAndPerformanceRelate.md │ ├── MakeupOfAScreenplay.md │ ├── ScreenplayInTheTestingStack.md │ ├── StandaloneScreenplay.md │ ├── SuitabilityAsATestingTool.md │ ├── WritingTests.md │ ├── builderPattern │ │ ├── ConsumingBuilders.md │ │ ├── WritingBuilders.md │ │ ├── index.md │ │ └── toc.yml │ ├── dependencyInjection │ │ ├── AddingServices.md │ │ ├── DependencyInjectionScope.md │ │ ├── InjectableServices.md │ │ ├── InjectingServices.md │ │ ├── Performables.md │ │ └── index.md │ ├── extendingScreenplay │ │ ├── AbilitiesActionsAndQuestions.md │ │ ├── EventHandlers.md │ │ ├── ReportFormtters.md │ │ ├── TestIntegrations.md │ │ └── index.md │ ├── nUnitTutorial │ │ ├── BestPractices.md │ │ └── index.md │ ├── performables │ │ ├── WebApis.md │ │ ├── index.md │ │ └── toc.yml │ ├── specFlowTutorial │ │ └── index.md │ ├── toc.yml │ └── writingPerformables │ │ ├── AllowCooperativeCancellation.md │ │ ├── AvoidBranchingLogic.md │ │ ├── DoNotUseDiFrameworks.md │ │ ├── ImplementICanReport.md │ │ ├── ImplementOnePerformableInterface.md │ │ ├── ParameterizeLowLevelPerformables.md │ │ ├── PureFunctionalTasks.md │ │ ├── StatefulButImmutable.md │ │ ├── TasksDoNotUseAbilities.md │ │ ├── WriteABuilder.md │ │ └── index.md ├── glossary │ ├── Ability.md │ ├── Action.md │ ├── Asset.md │ ├── Feature.md │ ├── Integration.md │ ├── Performable.md │ ├── Persona.md │ ├── Question.md │ ├── Report.md │ ├── Scenario.md │ ├── Spotlight.md │ ├── Task.md │ ├── index.md │ └── toc.yml ├── index.md └── toc.yml ├── CSF.Screenplay.JsonToHtmlReport.Template ├── CSF.Screenplay.JsonToHtmlReport.Template.proj ├── README.md └── src │ ├── .node-version │ ├── babel.config.js │ ├── css │ ├── content.css │ ├── layers.css │ ├── layout.css │ ├── reset.css │ ├── scenarioList.css │ ├── spinner.css │ └── summaryTable.css │ ├── index.js │ ├── jest.config.js │ ├── js │ ├── ReportLoader.js │ ├── ReportLoader.spec.js │ ├── ReportWriter │ │ ├── FeatureElementCreator.js │ │ ├── ReportWriter.js │ │ ├── ReportWriter.spec.js │ │ ├── ReportableElementCreator.js │ │ ├── ScenarioElementCreator.js │ │ ├── index.js │ │ └── setContentOrRemove.js │ ├── ScenarioAggregator.js │ ├── ScenarioAggregator.spec.js │ ├── SummaryDataGenerator.js │ ├── SummaryDataGenerator.spec.js │ ├── SummaryGenerator.js │ ├── SummaryGenerator.spec.js │ ├── activatePage.js │ ├── activatePage.spec.js │ ├── getElementById.js │ ├── updateReportTime.js │ └── updateReportTime.spec.js │ ├── package-lock.json │ ├── package.json │ ├── template.html │ └── webpack.config.js ├── CSF.Screenplay.JsonToHtmlReport ├── CSF.Screenplay.JsonToHtmlReport.csproj ├── IConvertsReportJsonToHtml.cs ├── IGetsHtmlTemplate.cs ├── Program.cs ├── ReportConverter.cs ├── ReportConverterApplication.cs ├── ReportConverterOptions.cs ├── ServiceRegistrations.cs └── TemplateReader.cs ├── CSF.Screenplay.NUnit ├── CSF.Screenplay.NUnit.csproj ├── ScreenplayAssemblyAttribute.cs ├── ScreenplayAttribute.cs └── ScreenplayLocator.cs ├── CSF.Screenplay.SpecFlow ├── CSF.Screenplay.SpecFlow.csproj ├── Properties │ └── RuntimePluginInfo.cs ├── ScenarioAndFeatureContextKey.cs ├── ScreenplayBinding.cs ├── ScreenplayPlugin.cs ├── ScreenplaySteps.cs ├── ServiceCollectionAdapter.cs └── ServiceProviderAdapter.cs ├── CSF.Screenplay.WebApis ├── CSF.Screenplay.WebApis.csproj ├── CommonHttpRequestLogic.cs ├── Endpoint.cs ├── Endpoint.withResult.cs ├── EndpointBase.cs ├── HttpRequestMessageBuilder.cs ├── HttpRequestMessageBuilder.generic.cs ├── HttpResponseMessageAndResponseType.cs ├── JsonEndpoint.cs ├── JsonEndpoint.withResult.cs ├── MakeWebApiRequests.cs ├── NameValueRecordCollection.cs ├── ParameterizedEndpoint.cs ├── ParameterizedEndpoint.withResult.cs ├── Resources │ ├── PerformableReportStrings.cs │ └── PerformableReportStrings.restext ├── SendTheHttpRequest.cs ├── SendTheHttpRequestAndGetJsonResponse.cs ├── SendTheHttpRequestAndGetTheResponse.cs └── WebApiBuilder.cs ├── CSF.Screenplay.sln ├── CSF.Screenplay ├── Actors │ ├── Cast.cs │ └── Stage.cs ├── CSF.Screenplay.csproj ├── IGetsScreenplay.cs ├── Performance.cs ├── Performances │ ├── PerformanceEventBus.cs │ └── PerformanceFactory.cs ├── ReportFragmentFormatter.cs ├── ReportModel │ ├── ActorCreatedReport.cs │ ├── ActorGainedAbilityReport.cs │ ├── ActorSpotlitReport.cs │ ├── IdentifierAndNameModel.cs │ ├── PerformableAsset.cs │ ├── PerformableReport.cs │ ├── PerformanceReport.cs │ ├── ReportMetadata.cs │ ├── ReportableModelBase.cs │ ├── ScreenplayReport.cs │ └── SpotlightTurnedOffReport.cs ├── Reporting │ ├── FormattableFormatter.cs │ ├── HumanizerFormatter.cs │ ├── IDeserializesReport.cs │ ├── ITestsPathForWritePermissions.cs │ ├── JsonScreenplayReportReader.cs │ ├── JsonScreenplayReporter.cs │ ├── NameFormatter.cs │ ├── NoOpReporter.cs │ ├── PerformanceReportBuilder.cs │ ├── ReportFormatCreator.cs │ ├── ScreenplayReportBuilder.cs │ ├── ToStringFormatter.cs │ ├── ValueFormatterProvider.cs │ ├── ValueFormatterRegistry.cs │ └── WritePermissionTester.cs ├── Resources │ ├── FormatterStrings.cs │ ├── FormatterStrings.restext │ ├── ReportStrings.cs │ └── ReportStrings.restext ├── ScopeAndPerformance.cs ├── Screenplay.cs ├── ScreenplayExtensions.cs ├── ScreenplayOptions.cs └── ScreenplayServiceCollectionExtensions.cs ├── LICENSE ├── README.md ├── Tests ├── CSF.Screenplay.NUnit.Tests │ ├── CSF.Screenplay.NUnit.Tests.csproj │ ├── GlobalUsings.cs │ ├── Properties │ │ └── ScreenplayAttributes.cs │ ├── ScreenplayFactory.cs │ ├── TestWithDescription.cs │ └── TestWithoutDescription.cs ├── CSF.Screenplay.SpecFlow.Tests │ ├── AddingUp.feature │ ├── AddingUp │ │ ├── AddNumbers.cs │ │ ├── AddTheNumber.cs │ │ ├── AddThreeNumbers.cs │ │ ├── AddingUpBuilder.cs │ │ ├── GetTheNumber.cs │ │ └── SetTheNumber.cs │ ├── CSF.Screenplay.SpecFlow.Tests.csproj │ ├── GlobalUsings.cs │ ├── Mathias.cs │ ├── ServiceCollectionAdapterTests.cs │ └── StepDefinitions │ │ └── AddingUpSteps.cs └── CSF.Screenplay.Tests │ ├── ActorExtensionsTests.cs │ ├── ActorTests.cs │ ├── Actors │ ├── CastTests.cs │ ├── NamedActorAttribute.cs │ ├── NamedActorCustomization.cs │ └── StageTests.cs │ ├── AutoMoqDataAttribute.cs │ ├── AutofixtureServicesAttribute.cs │ ├── AutofixtureServicesCustomization.cs │ ├── CSF.Screenplay.Tests.csproj │ ├── DefaultScreenplayAttribute.cs │ ├── DefaultScreenplayCustomization.cs │ ├── GlobalUsings.cs │ ├── Integration │ ├── EventBusIntegrationTests.cs │ └── PerformanceIntegrationTests.cs │ ├── JsonToHtmlReport │ └── TemplateReaderTests.cs │ ├── Performables │ ├── StopwatchTests.cs │ └── TimeSpanBuilderTests.cs │ ├── PerformanceTests.cs │ ├── ReportFragmentFormatterTests.cs │ ├── Reporting │ ├── FormattableFormatterTests.cs │ ├── HumanizerFormatterTests.cs │ ├── JsonScreenplayReportReaderTests.cs │ ├── JsonScreenplayReporterTests.cs │ ├── NameFormatterTests.cs │ ├── PerformanceReportBuilderTests.cs │ ├── ReportFormatCreatorTests.cs │ ├── ToStringFormatterTests.cs │ ├── ValueFormatterProviderTests.cs │ ├── ValueFormatterRegistryTests.cs │ ├── WithMemoryStreamAttribute.cs │ ├── WithMemoryStreamCustomization.cs │ └── WritePermissionTesterTests.cs │ ├── ScreenplayExtensionsTests.cs │ ├── ScreenplayTests.cs │ ├── Stubs │ ├── SampleAction.cs │ ├── SampleGenericQuestion.cs │ ├── SampleQuestion.cs │ └── ThrowingAction.cs │ └── WebApis │ ├── HttpRequestMessageBuilderTests.cs │ ├── MakeWebApiRequestsTests.cs │ ├── NameValueRecordCollectionTests.cs │ ├── SendTheHttpRequestAndGetJsonResponseTests.cs │ ├── SendTheHttpRequestAndGetTheResponseTests.cs │ ├── SendTheHttpRequestTests.cs │ ├── SendsMockHttpRequestsAttribute.cs │ └── WebApiBuilderTests.cs ├── Tools ├── appveyor-upload-test-results.ps1 ├── appveyor_publish_docs.ps1 └── run-tests-with-coverage.ps1 └── docs ├── .nojekyll ├── api ├── CSF.Screenplay.Abilities.UseAStopwatch.html ├── CSF.Screenplay.Abilities.html ├── CSF.Screenplay.Actor.html ├── CSF.Screenplay.ActorExtensions.html ├── CSF.Screenplay.Actors.ActorEventArgs.html ├── CSF.Screenplay.Actors.Cast.html ├── CSF.Screenplay.Actors.GainAbilityEventArgs.html ├── CSF.Screenplay.Actors.ICanPerformGiven.html ├── CSF.Screenplay.Actors.ICanPerformThen.html ├── CSF.Screenplay.Actors.ICanPerformWhen.html ├── CSF.Screenplay.Actors.IHasPerformableEvents.html ├── CSF.Screenplay.Actors.PerformableAssetEventArgs.html ├── CSF.Screenplay.Actors.PerformableEventArgs.html ├── CSF.Screenplay.Actors.PerformableFailureEventArgs.html ├── CSF.Screenplay.Actors.PerformableResultEventArgs.html ├── CSF.Screenplay.Actors.PerformancePhase.html ├── CSF.Screenplay.Actors.Stage.html ├── CSF.Screenplay.Actors.html ├── CSF.Screenplay.CastExtensions.html ├── CSF.Screenplay.ICanPerform.html ├── CSF.Screenplay.ICanReport.html ├── CSF.Screenplay.ICast.html ├── CSF.Screenplay.IFormatsReportFragment.html ├── CSF.Screenplay.IGetsScreenplay.html ├── CSF.Screenplay.IHasAbilities.html ├── CSF.Screenplay.IHasName.html ├── CSF.Screenplay.IHasPerformanceIdentity.html ├── CSF.Screenplay.IHasServiceProvider.html ├── CSF.Screenplay.IHostsPerformance.html ├── CSF.Screenplay.IPerformable.html ├── CSF.Screenplay.IPerformableWithResult-1.html ├── CSF.Screenplay.IPerformableWithResult.html ├── CSF.Screenplay.IPerformance.html ├── CSF.Screenplay.IPersona.html ├── CSF.Screenplay.IStage.html ├── CSF.Screenplay.JsonToHtmlReport.IConvertsReportJsonToHtml.html ├── CSF.Screenplay.JsonToHtmlReport.IGetsHtmlTemplate.html ├── CSF.Screenplay.JsonToHtmlReport.Program.html ├── CSF.Screenplay.JsonToHtmlReport.ReportConverter.html ├── CSF.Screenplay.JsonToHtmlReport.ReportConverterApplication.html ├── CSF.Screenplay.JsonToHtmlReport.ReportConverterOptions.html ├── CSF.Screenplay.JsonToHtmlReport.ServiceRegistrations.html ├── CSF.Screenplay.JsonToHtmlReport.TemplateReader.html ├── CSF.Screenplay.JsonToHtmlReport.html ├── CSF.Screenplay.Performables.IGetsPerformable.html ├── CSF.Screenplay.Performables.IGetsPerformableWithResult-1.html ├── CSF.Screenplay.Performables.IGetsPerformableWithResult.html ├── CSF.Screenplay.Performables.IProvidesTimeSpan.html ├── CSF.Screenplay.Performables.PerformableException.html ├── CSF.Screenplay.Performables.ReadTheStopwatch.html ├── CSF.Screenplay.Performables.ResetTheStopwatch.html ├── CSF.Screenplay.Performables.StartTheStopwatch.html ├── CSF.Screenplay.Performables.StopTheStopwatch.html ├── CSF.Screenplay.Performables.StopwatchBuilder.html ├── CSF.Screenplay.Performables.TimeSpanBuilder-1.html ├── CSF.Screenplay.Performables.TimeSpanBuilder.html ├── CSF.Screenplay.Performables.html ├── CSF.Screenplay.Performance.html ├── CSF.Screenplay.PerformanceStarter.html ├── CSF.Screenplay.Performances.IBeginsAndEndsPerformance.html ├── CSF.Screenplay.Performances.ICreatesPerformance.html ├── CSF.Screenplay.Performances.IHasPerformanceEvents.html ├── CSF.Screenplay.Performances.IRelaysPerformanceEvents.html ├── CSF.Screenplay.Performances.IdentifierAndName.html ├── CSF.Screenplay.Performances.PerformanceEventArgs.html ├── CSF.Screenplay.Performances.PerformanceEventBus.html ├── CSF.Screenplay.Performances.PerformanceFactory.html ├── CSF.Screenplay.Performances.PerformanceFinishedEventArgs.html ├── CSF.Screenplay.Performances.PerformanceScopeEventArgs.html ├── CSF.Screenplay.Performances.PerformanceState.html ├── CSF.Screenplay.Performances.html ├── CSF.Screenplay.ReportFragment.html ├── CSF.Screenplay.ReportFragmentFormatter.html ├── CSF.Screenplay.ReportModel.ActorCreatedReport.html ├── CSF.Screenplay.ReportModel.ActorGainedAbilityReport.html ├── CSF.Screenplay.ReportModel.ActorSpotlitReport.html ├── CSF.Screenplay.ReportModel.IdentifierAndNameModel.html ├── CSF.Screenplay.ReportModel.PerformableAsset.html ├── CSF.Screenplay.ReportModel.PerformableReport.html ├── CSF.Screenplay.ReportModel.PerformanceReport.html ├── CSF.Screenplay.ReportModel.ReportMetadata.html ├── CSF.Screenplay.ReportModel.ReportableModelBase.html ├── CSF.Screenplay.ReportModel.ScreenplayReport.html ├── CSF.Screenplay.ReportModel.SpotlightTurnedOffReport.html ├── CSF.Screenplay.ReportModel.html ├── CSF.Screenplay.Reporting.FormattableFormatter.html ├── CSF.Screenplay.Reporting.HumanizerFormatter.html ├── CSF.Screenplay.Reporting.IDeserializesReport.html ├── CSF.Screenplay.Reporting.IFormattableValue.html ├── CSF.Screenplay.Reporting.IFormatterRegistry.html ├── CSF.Screenplay.Reporting.IGetsReportFormat.html ├── CSF.Screenplay.Reporting.IGetsValueFormatter.html ├── CSF.Screenplay.Reporting.IReporter.html ├── CSF.Screenplay.Reporting.ITestsPathForWritePermissions.html ├── CSF.Screenplay.Reporting.IValueFormatter.html ├── CSF.Screenplay.Reporting.JsonScreenplayReportReader.html ├── CSF.Screenplay.Reporting.JsonScreenplayReporter.html ├── CSF.Screenplay.Reporting.NameAndValue.html ├── CSF.Screenplay.Reporting.NameFormatter.html ├── CSF.Screenplay.Reporting.NoOpReporter.html ├── CSF.Screenplay.Reporting.PerformanceReportBuilder.html ├── CSF.Screenplay.Reporting.ReportFormat.html ├── CSF.Screenplay.Reporting.ReportFormatCreator.html ├── CSF.Screenplay.Reporting.ScreenplayReportBuilder.html ├── CSF.Screenplay.Reporting.ToStringFormatter.html ├── CSF.Screenplay.Reporting.ValueFormatterExtensions.html ├── CSF.Screenplay.Reporting.ValueFormatterProvider.html ├── CSF.Screenplay.Reporting.ValueFormatterRegistry.html ├── CSF.Screenplay.Reporting.WritePermissionTester.html ├── CSF.Screenplay.Reporting.html ├── CSF.Screenplay.ScopeAndPerformance.html ├── CSF.Screenplay.Screenplay.html ├── CSF.Screenplay.ScreenplayAssemblyAttribute.html ├── CSF.Screenplay.ScreenplayAttribute.html ├── CSF.Screenplay.ScreenplayBinding.html ├── CSF.Screenplay.ScreenplayExtensions.html ├── CSF.Screenplay.ScreenplayLocator.html ├── CSF.Screenplay.ScreenplayOptions.html ├── CSF.Screenplay.ScreenplayPlugin.html ├── CSF.Screenplay.ScreenplayServiceCollectionExtensions.html ├── CSF.Screenplay.ScreenplaySteps.html ├── CSF.Screenplay.ServiceCollectionAdapter.html ├── CSF.Screenplay.ServiceProviderAdapter.html ├── CSF.Screenplay.StageExtensions.html ├── CSF.Screenplay.WebApis.Endpoint-1.html ├── CSF.Screenplay.WebApis.Endpoint.html ├── CSF.Screenplay.WebApis.EndpointBase.html ├── CSF.Screenplay.WebApis.HttpRequestMessageBuilder-1.html ├── CSF.Screenplay.WebApis.HttpRequestMessageBuilder.html ├── CSF.Screenplay.WebApis.HttpResponseMessageAndResponseType-1.html ├── CSF.Screenplay.WebApis.JsonEndpoint-1.html ├── CSF.Screenplay.WebApis.JsonEndpoint-2.html ├── CSF.Screenplay.WebApis.MakeWebApiRequests.html ├── CSF.Screenplay.WebApis.NameValueRecordCollection-2.html ├── CSF.Screenplay.WebApis.ParameterizedEndpoint-1.html ├── CSF.Screenplay.WebApis.ParameterizedEndpoint-2.html ├── CSF.Screenplay.WebApis.SendTheHttpRequest.html ├── CSF.Screenplay.WebApis.SendTheHttpRequestAndGetJsonResponse-1.html ├── CSF.Screenplay.WebApis.SendTheHttpRequestAndGetTheResponse-1.html ├── CSF.Screenplay.WebApis.WebApiBuilder.html ├── CSF.Screenplay.WebApis.html ├── CSF.Screenplay.html ├── toc.html └── toc.json ├── docs ├── GettingReports.html ├── HowScreenplayAndPerformanceRelate.html ├── MakeupOfAScreenplay.html ├── ScreenplayInTheTestingStack.html ├── StandaloneScreenplay.html ├── SuitabilityAsATestingTool.html ├── WritingTests.html ├── builderPattern │ ├── ConsumingBuilders.html │ ├── WritingBuilders.html │ ├── index.html │ ├── toc.html │ └── toc.json ├── dependencyInjection │ ├── AddingServices.html │ ├── DependencyInjectionScope.html │ ├── InjectableServices.html │ ├── InjectingServices.html │ ├── Performables.html │ └── index.html ├── extendingScreenplay │ ├── AbilitiesActionsAndQuestions.html │ ├── EventHandlers.html │ ├── ReportFormtters.html │ ├── TestIntegrations.html │ └── index.html ├── nUnitTutorial │ ├── BestPractices.html │ └── index.html ├── performables │ ├── WebApis.html │ ├── index.html │ ├── toc.html │ └── toc.json ├── specFlowTutorial │ └── index.html ├── toc.html ├── toc.json └── writingPerformables │ ├── AllowCooperativeCancellation.html │ ├── AvoidBranchingLogic.html │ ├── DoNotUseDiFrameworks.html │ ├── ImplementICanReport.html │ ├── ImplementOnePerformableInterface.html │ ├── ParameterizeLowLevelPerformables.html │ ├── PureFunctionalTasks.html │ ├── StatefulButImmutable.html │ ├── TasksDoNotUseAbilities.html │ ├── WriteABuilder.html │ └── index.html ├── favicon.ico ├── glossary ├── Ability.html ├── Action.html ├── Asset.html ├── Feature.html ├── Integration.html ├── Performable.html ├── Persona.html ├── Question.html ├── Report.html ├── Scenario.html ├── Spotlight.html ├── Task.html ├── index.html ├── toc.html └── toc.json ├── index.html ├── index.json ├── logo.svg ├── manifest.json ├── public ├── architecture-I3QFYML2-NK53GYGD.min.js ├── architecture-I3QFYML2-NK53GYGD.min.js.map ├── architectureDiagram-AYX4OTIS-UEH2Z7UQ.min.js ├── architectureDiagram-AYX4OTIS-UEH2Z7UQ.min.js.map ├── blockDiagram-XN6IQ5JY-VK3M7PUD.min.js ├── blockDiagram-XN6IQ5JY-VK3M7PUD.min.js.map ├── bootstrap-icons-OCU552PF.woff ├── bootstrap-icons-X6UQXWUS.woff2 ├── c4Diagram-GPMAACGM-P3DMZBSW.min.js ├── c4Diagram-GPMAACGM-P3DMZBSW.min.js.map ├── chunk-24EG6CQZ.min.js ├── chunk-24EG6CQZ.min.js.map ├── chunk-2TAJJIOM.min.js ├── chunk-2TAJJIOM.min.js.map ├── chunk-2YMHYP32.min.js ├── chunk-2YMHYP32.min.js.map ├── chunk-3Z74ZUXG.min.js ├── chunk-3Z74ZUXG.min.js.map ├── chunk-45RMNOPF.min.js ├── chunk-45RMNOPF.min.js.map ├── chunk-53EALYMI.min.js ├── chunk-53EALYMI.min.js.map ├── chunk-5KKNARB2.min.js ├── chunk-5KKNARB2.min.js.map ├── chunk-7JZIB2XU.min.js ├── chunk-7JZIB2XU.min.js.map ├── chunk-B5WF4DA4.min.js ├── chunk-B5WF4DA4.min.js.map ├── chunk-CIWIECNU.min.js ├── chunk-CIWIECNU.min.js.map ├── chunk-CM5D5KZN.min.js ├── chunk-CM5D5KZN.min.js.map ├── chunk-CXRPJJJE.min.js ├── chunk-CXRPJJJE.min.js.map ├── chunk-DBPRPEV6.min.js ├── chunk-DBPRPEV6.min.js.map ├── chunk-DFMVIMQJ.min.js ├── chunk-DFMVIMQJ.min.js.map ├── chunk-DTUU2GN4.min.js ├── chunk-DTUU2GN4.min.js.map ├── chunk-DXUQFVLL.min.js ├── chunk-DXUQFVLL.min.js.map ├── chunk-HOCACIQT.min.js ├── chunk-HOCACIQT.min.js.map ├── chunk-IQQ46AC6.min.js ├── chunk-IQQ46AC6.min.js.map ├── chunk-K7PO6CNQ.min.js ├── chunk-K7PO6CNQ.min.js.map ├── chunk-KF4CENUX.min.js ├── chunk-KF4CENUX.min.js.map ├── chunk-LDPOAEOH.min.js ├── chunk-LDPOAEOH.min.js.map ├── chunk-MJXJ7UGT.min.js ├── chunk-MJXJ7UGT.min.js.map ├── chunk-NRRWWJ3P.min.js ├── chunk-NRRWWJ3P.min.js.map ├── chunk-OSRY5VT3.min.js ├── chunk-OSRY5VT3.min.js.map ├── chunk-P2C2SEUI.min.js ├── chunk-P2C2SEUI.min.js.map ├── chunk-PYBRKFQJ.min.js ├── chunk-PYBRKFQJ.min.js.map ├── chunk-R7DHUQMU.min.js ├── chunk-R7DHUQMU.min.js.map ├── chunk-TERFBH2B.min.js ├── chunk-TERFBH2B.min.js.map ├── chunk-TOQ6ACLX.min.js ├── chunk-TOQ6ACLX.min.js.map ├── chunk-U4DUTLYF.min.js ├── chunk-U4DUTLYF.min.js.map ├── chunk-UOD6J27N.min.js ├── chunk-UOD6J27N.min.js.map ├── chunk-WMZJ2DJX.min.js ├── chunk-WMZJ2DJX.min.js.map ├── chunk-XBNRW4G3.min.js ├── chunk-XBNRW4G3.min.js.map ├── chunk-ZU5APUIQ.min.js ├── chunk-ZU5APUIQ.min.js.map ├── classDiagram-FEGYTUDG-YCLVHNZU.min.js ├── classDiagram-FEGYTUDG-YCLVHNZU.min.js.map ├── classDiagram-v2-R65JCUOM-VFGJ2XXK.min.js ├── classDiagram-v2-R65JCUOM-VFGJ2XXK.min.js.map ├── dagre-SWNTG5WE-7BA3SWK2.min.js ├── dagre-SWNTG5WE-7BA3SWK2.min.js.map ├── diagram-NZMEDLQF-NYTRB6HN.min.js ├── diagram-NZMEDLQF-NYTRB6HN.min.js.map ├── docfx.min.css ├── docfx.min.css.map ├── docfx.min.js ├── docfx.min.js.map ├── erDiagram-WO52GFNT-Y4NFVIDE.min.js ├── erDiagram-WO52GFNT-Y4NFVIDE.min.js.map ├── es-4I4X6RME.min.js ├── es-4I4X6RME.min.js.map ├── flowDiagram-TSWR6T2D-H32UQL2W.min.js ├── flowDiagram-TSWR6T2D-H32UQL2W.min.js.map ├── ganttDiagram-FAOCOTIY-GFLLTKXL.min.js ├── ganttDiagram-FAOCOTIY-GFLLTKXL.min.js.map ├── gitGraph-YCYPL57B-C4NBXIBD.min.js ├── gitGraph-YCYPL57B-C4NBXIBD.min.js.map ├── gitGraphDiagram-5C7YHVU6-2F4DKEGL.min.js ├── gitGraphDiagram-5C7YHVU6-2F4DKEGL.min.js.map ├── info-46DW6VJ7-MWRO75OW.min.js ├── info-46DW6VJ7-MWRO75OW.min.js.map ├── infoDiagram-P5D6MX3V-EFUDKNDH.min.js ├── infoDiagram-P5D6MX3V-EFUDKNDH.min.js.map ├── journeyDiagram-UIGPPNLY-FB6FUBJH.min.js ├── journeyDiagram-UIGPPNLY-FB6FUBJH.min.js.map ├── kanban-definition-KMT3NSR2-K4PM5M4E.min.js ├── kanban-definition-KMT3NSR2-K4PM5M4E.min.js.map ├── katex-NVDEX37K.min.js ├── katex-NVDEX37K.min.js.map ├── lunr.ar-A6ZT2INA.min.js ├── lunr.ar-A6ZT2INA.min.js.map ├── lunr.da-WWM276CR.min.js ├── lunr.da-WWM276CR.min.js.map ├── lunr.de-XXPRKDAY.min.js ├── lunr.de-XXPRKDAY.min.js.map ├── lunr.du-NO4L2LL3.min.js ├── lunr.du-NO4L2LL3.min.js.map ├── lunr.el-5ZSSJVMA.min.js ├── lunr.el-5ZSSJVMA.min.js.map ├── lunr.es-ZH6Q76E6.min.js ├── lunr.es-ZH6Q76E6.min.js.map ├── lunr.fi-S7WJSBCP.min.js ├── lunr.fi-S7WJSBCP.min.js.map ├── lunr.fr-H2QNBELV.min.js ├── lunr.fr-H2QNBELV.min.js.map ├── lunr.he-TTLAK4MN.min.js ├── lunr.he-TTLAK4MN.min.js.map ├── lunr.hi-PWWMAGLU.min.js ├── lunr.hi-PWWMAGLU.min.js.map ├── lunr.hu-DLG2DSVM.min.js ├── lunr.hu-DLG2DSVM.min.js.map ├── lunr.hy-FFQJAR7M.min.js ├── lunr.hy-FFQJAR7M.min.js.map ├── lunr.it-VQNLJLPR.min.js ├── lunr.it-VQNLJLPR.min.js.map ├── lunr.ja-J6QHZSR2.min.js ├── lunr.ja-J6QHZSR2.min.js.map ├── lunr.jp-M45D3XJE.min.js ├── lunr.jp-M45D3XJE.min.js.map ├── lunr.kn-ASLXFRTC.min.js ├── lunr.kn-ASLXFRTC.min.js.map ├── lunr.ko-RHF2BDE4.min.js ├── lunr.ko-RHF2BDE4.min.js.map ├── lunr.nl-2BITG354.min.js ├── lunr.nl-2BITG354.min.js.map ├── lunr.no-WPLSHWFO.min.js ├── lunr.no-WPLSHWFO.min.js.map ├── lunr.pt-V2XEBELC.min.js ├── lunr.pt-V2XEBELC.min.js.map ├── lunr.ro-O76266FJ.min.js ├── lunr.ro-O76266FJ.min.js.map ├── lunr.ru-G56UDXYH.min.js ├── lunr.ru-G56UDXYH.min.js.map ├── lunr.sa-LD5PRAIS.min.js ├── lunr.sa-LD5PRAIS.min.js.map ├── lunr.sv-7VRY4UDB.min.js ├── lunr.sv-7VRY4UDB.min.js.map ├── lunr.ta-OWB7AURB.min.js ├── lunr.ta-OWB7AURB.min.js.map ├── lunr.te-JGGL3BFP.min.js ├── lunr.te-JGGL3BFP.min.js.map ├── lunr.th-O4JBL3IY.min.js ├── lunr.th-O4JBL3IY.min.js.map ├── lunr.tr-WXUV733C.min.js ├── lunr.tr-WXUV733C.min.js.map ├── lunr.vi-3U4A337N.min.js ├── lunr.vi-3U4A337N.min.js.map ├── main.css ├── main.js ├── mermaid.core-RYVCAQML.min.js ├── mermaid.core-RYVCAQML.min.js.map ├── mindmap-definition-R7LC4OIY-2USVTKSJ.min.js ├── mindmap-definition-R7LC4OIY-2USVTKSJ.min.js.map ├── packet-W2GHVCYJ-73RTG6VT.min.js ├── packet-W2GHVCYJ-73RTG6VT.min.js.map ├── pie-BEWT4RHE-6E5EOTAQ.min.js ├── pie-BEWT4RHE-6E5EOTAQ.min.js.map ├── pieDiagram-BLWKPB35-RRRLPZVM.min.js ├── pieDiagram-BLWKPB35-RRRLPZVM.min.js.map ├── quadrantDiagram-QXWEEFXS-PKSQMH6W.min.js ├── quadrantDiagram-QXWEEFXS-PKSQMH6W.min.js.map ├── requirementDiagram-XAUNFCZY-5ZWIDLT6.min.js ├── requirementDiagram-XAUNFCZY-5ZWIDLT6.min.js.map ├── sankeyDiagram-LVV36NHA-T6DVYUKQ.min.js ├── sankeyDiagram-LVV36NHA-T6DVYUKQ.min.js.map ├── search-worker.min.js ├── search-worker.min.js.map ├── sequenceDiagram-D25TJ2OB-6R64PNA2.min.js ├── sequenceDiagram-D25TJ2OB-6R64PNA2.min.js.map ├── stateDiagram-GNSP7T6Y-QRHXVCK2.min.js ├── stateDiagram-GNSP7T6Y-QRHXVCK2.min.js.map ├── stateDiagram-v2-HP6YRVRG-XMVPP43U.min.js ├── stateDiagram-v2-HP6YRVRG-XMVPP43U.min.js.map ├── tex-svg-full-SL33OL2J.min.js ├── tex-svg-full-SL33OL2J.min.js.map ├── timeline-definition-27KQCCZ3-MKUJTCR4.min.js ├── timeline-definition-27KQCCZ3-MKUJTCR4.min.js.map ├── xychartDiagram-MYLB5AYS-IFQ4BEB4.min.js └── xychartDiagram-MYLB5AYS-IFQ4BEB4.min.js.map ├── toc.html ├── toc.json └── xrefmap.yml /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | /CSF.Screenplay.Docs/api/ 4 | TestResults/ 5 | Tests/**/*.feature.cs 6 | node_modules/ 7 | /CSF.Screenplay.JsonToHtmlReport/template/ 8 | /CSF.Screenplay.JsonToHtmlReport.Template/src/output/ 9 | -------------------------------------------------------------------------------- /.sonarqube-analysisproperties.xml: -------------------------------------------------------------------------------- 1 |  2 | 5 | Tests/**/*,**/*Exception.cs,*_old/**/*,**/*.spec.js,**/*.config.js 6 | Tests/**/*,*_old/**/*,**/*.spec.js 7 | Tests\**\TestResults.xml 8 | TestResults\*.opencover.xml 9 | false 10 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "alexkrechik.cucumberautocomplete", 4 | "ms-dotnettools.csdevkit", 5 | "bierner.markdown-mermaid", 6 | "davidanson.vscode-markdownlint", 7 | "bpruitt-goddard.mermaid-markdown-syntax-highlighting" 8 | ] 9 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cucumberautocomplete.steps": [ "Tests/CSF.Screenplay.SpecFlow.Tests/StepDefinitions/**/*.cs" ], 3 | "cucumberautocomplete.strictGherkinCompletion": true, 4 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "dotnet", 6 | "task": "build", 7 | "group": { 8 | "kind": "build", 9 | "isDefault": true 10 | }, 11 | "problemMatcher": [], 12 | "label": "dotnet: build" 13 | }, 14 | { 15 | "group": { 16 | "kind": "test", 17 | "isDefault": true 18 | }, 19 | "command": "dotnet", 20 | "args": ["test"], 21 | "problemMatcher": [], 22 | "label": "dotnet: test" 23 | }, 24 | { 25 | "group": { 26 | "kind": "test" 27 | }, 28 | "type": "shell", 29 | "command": "npm", 30 | "args": ["test"], 31 | "problemMatcher": [], 32 | "label": "npm: test (Javascript)", 33 | "options": { 34 | "cwd": "${workspaceFolder}/CSF.Screenplay.JsonToHtmlReport.Template/src" 35 | } 36 | }, 37 | { 38 | "group": { 39 | "kind": "build" 40 | }, 41 | "command": "dotnet", 42 | "args": ["build", "-c", "Docs"], 43 | "problemMatcher": [], 44 | "label": "Build docs website", 45 | "options": {"cwd": "${workspaceFolder}"} 46 | }, 47 | { 48 | "group": { 49 | "kind": "build" 50 | }, 51 | "command": "docfx", 52 | "args": ["CSF.Screenplay.Docs/docfx.json", "--serve"], 53 | "problemMatcher": [], 54 | "label": "Serve docs website" 55 | }, 56 | { 57 | "group": { 58 | "kind": "build" 59 | }, 60 | "type": "shell", 61 | "command": "npm", 62 | "args": ["i"], 63 | "problemMatcher": [], 64 | "label": "npm: install (Javascript)", 65 | "options": { 66 | "cwd": "${workspaceFolder}/CSF.Screenplay.JsonToHtmlReport.Template/src" 67 | } 68 | } 69 | ] 70 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Abilities/UseAStopwatch.cs: -------------------------------------------------------------------------------- 1 | using CSF.Screenplay.Resources; 2 | 3 | namespace CSF.Screenplay.Abilities 4 | { 5 | /// 6 | /// An ability that enables an actor to make use of a to accurately 7 | /// measure the passage of time. 8 | /// 9 | /// 10 | /// 11 | /// Use this ability with the actions which exposed by 12 | /// . 13 | /// This ability wraps a instance, allowing the actor 14 | /// to control & read it from the related actions. 15 | /// 16 | /// 17 | public class UseAStopwatch : ICanReport 18 | { 19 | /// 20 | /// Gets the stopwatch granted to the actor by this ability. 21 | /// 22 | public System.Diagnostics.Stopwatch Stopwatch { get; } = new System.Diagnostics.Stopwatch(); 23 | 24 | /// 25 | public ReportFragment GetReportFragment(IHasName actor, IFormatsReportFragment formatter) 26 | => formatter.Format(AbilityReportStrings.UseAStopwatchFormat, actor); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Actor.abilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace CSF.Screenplay 6 | { 7 | public partial class Actor : IHasAbilities 8 | { 9 | readonly HashSet abilities = new HashSet(); 10 | 11 | /// Gets a collection of the actor's abilities 12 | protected virtual HashSet Abilities => abilities; 13 | 14 | IReadOnlyCollection IHasAbilities.Abilities => Abilities; 15 | 16 | /// Adds a new ability to the actor 17 | /// The ability to add 18 | /// If the ability is 19 | /// If the actor already has an ability of this type or a derived type 20 | protected virtual void IsAbleTo(object ability) 21 | { 22 | if (ability is null) throw new ArgumentNullException(nameof(ability)); 23 | AssertNotDisposed(); 24 | 25 | var abilityType = ability.GetType(); 26 | 27 | if (abilities.Any(abilityType.IsInstanceOfType)) 28 | throw new InvalidOperationException($"{name} must not already have any abilities which derive from {abilityType.FullName}."); 29 | abilities.Add(ability); 30 | 31 | InvokeGainedAbility(ability); 32 | } 33 | 34 | void IHasAbilities.IsAbleTo(object ability) => IsAbleTo(ability); 35 | } 36 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Actor.disposable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay 4 | { 5 | public partial class Actor : IDisposable 6 | { 7 | bool disposedValue; 8 | 9 | /// 10 | /// Asserts that the current instance is not disposed. 11 | /// 12 | /// If the current instance is already disposed. 13 | void AssertNotDisposed() 14 | { 15 | if(disposedValue) 16 | throw new ObjectDisposedException($"{nameof(Actor)} '{Name}'", "Operations upon a disposed actor are not permitted."); 17 | } 18 | 19 | /// 20 | /// Disposes the current instance, via the Dispose Pattern. 21 | /// 22 | /// A value indicating wherher or not disposal should occur. 23 | protected virtual void Dispose(bool disposing) 24 | { 25 | if (disposedValue) return; 26 | 27 | if (disposing) 28 | { 29 | foreach(var ability in Abilities) 30 | { 31 | if (ability is IDisposable disposable) 32 | disposable.Dispose(); 33 | } 34 | } 35 | 36 | disposedValue = true; 37 | } 38 | 39 | /// 40 | public void Dispose() 41 | { 42 | Dispose(disposing: true); 43 | GC.SuppressFinalize(this); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Actors/ActorEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CSF.Screenplay.Performances; 3 | 4 | namespace CSF.Screenplay.Actors 5 | { 6 | /// 7 | /// A model for event arguments which relate to an . 8 | /// 9 | /// 10 | public class ActorEventArgs : PerformanceScopeEventArgs 11 | { 12 | /// 13 | /// Gets the name of the actor to which these event arguments relate 14 | /// 15 | public Actor Actor { get; } 16 | 17 | /// 18 | /// Initializes a new instance of 19 | /// 20 | /// The actor 21 | public ActorEventArgs(Actor actor) : base(((IHasPerformanceIdentity) actor ?? throw new ArgumentNullException(nameof(actor))).PerformanceIdentity) 22 | { 23 | Actor = actor; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Actors/GainAbilityEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay.Actors 4 | { 5 | /// 6 | /// A model for event arguments which relate to an actor gaining a new ability. 7 | /// 8 | public class GainAbilityEventArgs : ActorEventArgs 9 | { 10 | /// 11 | /// Gets the ability which the actor has gained 12 | /// 13 | public object Ability { get; } 14 | 15 | /// 16 | /// Initializes a new instance of . 17 | /// 18 | /// The actor 19 | /// The ability 20 | public GainAbilityEventArgs(Actor actor, object ability) : base(actor) 21 | { 22 | Ability = ability ?? throw new ArgumentNullException(nameof(ability)); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Actors/IHasPerformableEvents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay.Actors 4 | { 5 | /// An actor which may emit events as they participate in a 6 | public interface IHasPerformableEvents 7 | { 8 | /// 9 | /// Occurs when the actor begins the execution of a performable object. 10 | /// 11 | event EventHandler BeginPerformable; 12 | 13 | /// 14 | /// Occurs when an actor ends the execution of a performable object. 15 | /// 16 | event EventHandler EndPerformable; 17 | 18 | /// 19 | /// Occurs when an actor receives a result from a perfperformable objectrmance. 20 | /// 21 | event EventHandler PerformableResult; 22 | 23 | /// 24 | /// Occurs when a performable object fails with an exception. 25 | /// 26 | event EventHandler PerformableFailed; 27 | 28 | /// 29 | /// Occurs when an actor gains a new ability. 30 | /// 31 | event EventHandler GainedAbility; 32 | 33 | /// 34 | /// Occurs when an actor records the presence of a new file asset. 35 | /// 36 | event EventHandler RecordsAsset; 37 | } 38 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Actors/PerformableEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay.Actors 4 | { 5 | /// 6 | /// A model for event arguments which relate to an actor's use of a performable. 7 | /// 8 | public class PerformableEventArgs : ActorEventArgs 9 | { 10 | /// 11 | /// Gets the performable item to which these event arguments relate. 12 | /// 13 | public object Performable { get; } 14 | 15 | /// 16 | /// Gets the performance phase to which these event arguments relate. 17 | /// 18 | public PerformancePhase Phase { get; } 19 | 20 | /// 21 | /// Initializes a new instance of . 22 | /// 23 | /// The actor 24 | /// The performable item 25 | /// The phase of performance 26 | public PerformableEventArgs(Actor actor, 27 | object performable, 28 | PerformancePhase phase = PerformancePhase.Unspecified) : base(actor) 29 | { 30 | Performable = performable ?? throw new ArgumentNullException(nameof(performable)); 31 | Phase = Enum.IsDefined(typeof(PerformancePhase), phase) 32 | ? phase 33 | : throw new ArgumentException($"The performance phase must be a valid member of {nameof(PerformancePhase)}", nameof(phase)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Actors/PerformableFailureEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay.Actors 4 | { 5 | /// 6 | /// A specialisation of which describe the situation where 7 | /// an exception halted the execution of the performable item. 8 | /// 9 | public class PerformableFailureEventArgs : PerformableEventArgs 10 | { 11 | /// 12 | /// Gets the exception which halted the performable item. 13 | /// 14 | public Exception Exception { get; } 15 | 16 | /// 17 | /// Initializes a new instance of . 18 | /// 19 | /// The actor 20 | /// The performable item which raised the exception 21 | /// The exception which occurred 22 | /// The phase of performance which was underway when the exception occurred 23 | public PerformableFailureEventArgs(Actor actor, 24 | object performable, 25 | Exception exception, 26 | PerformancePhase phase = PerformancePhase.Unspecified) : base(actor, performable, phase) 27 | { 28 | Exception = exception ?? throw new ArgumentNullException(nameof(exception)); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Actors/PerformableResultEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay.Actors 4 | { 5 | /// 6 | /// A specialisation of which describe a scenario in which the performable 7 | /// completed and has returned a result value. 8 | /// 9 | public class PerformableResultEventArgs : PerformableEventArgs 10 | { 11 | /// 12 | /// Gets the result value which was returned by the performable 13 | /// 14 | public object Result { get; } 15 | 16 | /// 17 | /// Initializes a new instance of . 18 | /// 19 | /// The actor 20 | /// The performable item 21 | /// The result from the performable 22 | /// The phase of performance 23 | public PerformableResultEventArgs(Actor actor, 24 | object performable, 25 | object result, 26 | PerformancePhase phase = PerformancePhase.Unspecified) : base(actor, performable, phase) 27 | { 28 | Result = result; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/CSF.Screenplay.Abstractions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;netstandard2.1;net462 5 | CSF.Screenplay 6 | $(MSBuildProjectDirectory)\bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/IHasAbilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CSF.Screenplay 5 | { 6 | /// An object which has & is able to gain abilities. 7 | /// 8 | /// 9 | /// Abilities are the mechanism by which actors: interact with the application and system. 10 | /// They are arbitrary objects which provide functionality. 11 | /// 12 | /// 13 | public interface IHasAbilities 14 | { 15 | /// Gets the collection of the actor's abilities. 16 | IReadOnlyCollection Abilities { get; } 17 | 18 | /// Adds an ability to the specified actor 19 | /// The ability to add to the actor 20 | /// If is 21 | /// If the actor already has an ability of the same type as 22 | /// , or which derives from the same type 23 | void IsAbleTo(object ability); 24 | } 25 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/IHasName.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay 2 | { 3 | /// 4 | /// A part of a Screenplay performance which has a human-readable name. 5 | /// 6 | /// 7 | /// 8 | /// Use this interface for any object within an which could benefit from having a human-readable name. 9 | /// For example, static parameter values like Web API endpoints, web page URLs or elements on a web UI. 10 | /// By referring to an object by its name, and using that name in report-generating logic, reports generated from a Screenplay can 11 | /// become much easier to read and comprehend. 12 | /// 13 | /// 14 | /// The property is used to provide a human-readable string which represents the object in the report text. 15 | /// This interface is a part of the mechanism for in Screenplay. 16 | /// 17 | /// 18 | /// 19 | /// 20 | public interface IHasName 21 | { 22 | /// 23 | /// Gets the human-readable name of the current object. 24 | /// 25 | /// 26 | /// 27 | /// is strongly discouraged here. All types which implement 28 | /// should return a non-null response from this property. 29 | /// 30 | /// 31 | string Name { get; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/IHasPerformanceIdentity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay 4 | { 5 | /// An object which provides a value which uniquely identifies the currently-executing . 6 | public interface IHasPerformanceIdentity 7 | { 8 | /// Gets the unique identifier 9 | /// 10 | /// 11 | /// This value is used to uniquely identify a performance within a . 12 | /// 13 | /// 14 | Guid PerformanceIdentity { get; } 15 | } 16 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/IHasServiceProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay 4 | { 5 | /// An object which has an associated , which resolves 6 | /// services from dependency injection. 7 | public interface IHasServiceProvider 8 | { 9 | /// Gets a service provider/resolver instance associated with this object. 10 | IServiceProvider ServiceProvider { get; } 11 | } 12 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/IHostsPerformance.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using CSF.Screenplay.Performances; 4 | 5 | namespace CSF.Screenplay 6 | { 7 | /// 8 | /// An object which encapsulates the logic of an in a standalone Screenplay. 9 | /// 10 | /// 11 | /// 12 | /// Implementors should inject any dependencies they require into their constructors. 13 | /// The method is used to execute the logic of 14 | /// an , returning its result. 15 | /// 16 | /// 17 | public interface IHostsPerformance 18 | { 19 | /// 20 | /// Executes the logic of a performance, returning the result. 21 | /// 22 | /// 23 | /// 24 | /// The result of the performance has the same semantics as . 25 | /// Implementors should use this method to execute the logic of the performance. 26 | /// 27 | /// 28 | /// A cancellation token 29 | /// A task which exposes the result of the performance. 30 | Task ExecutePerformanceAsync(CancellationToken cancellationToken); 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/IPersona.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay 4 | { 5 | /// A persona is a factory for a commonly-used actor 6 | /// 7 | /// 8 | /// In Screenplay is is recommended to use memorable actors which are widely understood and recognisable 9 | /// by the team. This is easier if the composition of an actor is the same across every in which 10 | /// they participate. 11 | /// 12 | /// 13 | /// By using a separate persona implementation for each named actor, the developer can ensure consistent creation 14 | /// for instances of those actors. 15 | /// 16 | /// 17 | public interface IPersona : IHasName 18 | { 19 | /// Gets the actor which is associated with the current persona 20 | /// 21 | /// 22 | /// Implementors should not only create and return the actor from this method, but also 23 | /// configure the actor with the standard abilities associated with this persona. 24 | /// 25 | /// 26 | /// A unique identity for the currently-executing performance 27 | Actor GetActor(Guid performanceIdentity); 28 | } 29 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Performables/IGetsPerformable.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Performables 2 | { 3 | /// 4 | /// An object which can get an instance, such as a performable builder. 5 | /// 6 | public interface IGetsPerformable 7 | { 8 | /// 9 | /// Gets the performable object from the current instance. 10 | /// 11 | /// A performable object 12 | IPerformable GetPerformable(); 13 | } 14 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Performables/IGetsPerformableWithResult.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Performables 2 | { 3 | /// 4 | /// An object which can get a non-generic instance, such as a performable builder. 5 | /// 6 | public interface IGetsPerformableWithResult 7 | { 8 | /// 9 | /// Gets the performable object from the current instance. 10 | /// 11 | /// A performable object 12 | IPerformableWithResult GetPerformable(); 13 | } 14 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Performables/IGetsPerformableWithResult.generic.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Performables 2 | { 3 | /// 4 | /// An object which can get an instance, such as a performable builder. 5 | /// 6 | public interface IGetsPerformableWithResult 7 | { 8 | /// 9 | /// Gets the performable object from the current instance. 10 | /// 11 | /// A performable object 12 | IPerformableWithResult GetPerformable(); 13 | } 14 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Performables/IProvidesTimeSpan.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay.Performables 4 | { 5 | /// 6 | /// A type which may provide a . 7 | /// 8 | /// 9 | /// 10 | /// Many performables make use of time; this interface provides a common abstraction for 11 | /// objects that provide time spans. 12 | /// 13 | /// 14 | /// 15 | /// 16 | public interface IProvidesTimeSpan 17 | { 18 | /// 19 | /// Gets the which is exposed by the current instance. 20 | /// 21 | /// The time span 22 | TimeSpan GetTimeSpan(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Performables/ReadTheStopwatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using CSF.Screenplay.Abilities; 5 | using CSF.Screenplay.Resources; 6 | 7 | namespace CSF.Screenplay.Performables 8 | { 9 | /// 10 | /// An action which reads the current value of the stopwatch. 11 | /// 12 | /// 13 | /// 14 | /// This performable requires the actor has the ability . 15 | /// Use this performable via the builder method . 16 | /// 17 | /// 18 | public class ReadTheStopwatch : IPerformableWithResult, ICanReport 19 | { 20 | /// 21 | public ReportFragment GetReportFragment(IHasName actor, IFormatsReportFragment formatter) 22 | => formatter.Format(PerformableReportStrings.ReadTheStopwatchFormat, actor); 23 | 24 | /// 25 | public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default) 26 | { 27 | var ability = actor.GetAbility(); 28 | return new ValueTask(ability.Stopwatch.Elapsed); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Performables/ResetTheStopwatch.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using CSF.Screenplay.Abilities; 4 | using CSF.Screenplay.Resources; 5 | 6 | namespace CSF.Screenplay.Performables 7 | { 8 | /// 9 | /// An action which resets the stopwatch. 10 | /// 11 | /// 12 | /// 13 | /// This performable requires the actor has the ability . 14 | /// Use this performable via the builder method . 15 | /// 16 | /// 17 | public class ResetTheStopwatch : IPerformable, ICanReport 18 | { 19 | /// 20 | public ReportFragment GetReportFragment(IHasName actor, IFormatsReportFragment formatter) 21 | => formatter.Format(PerformableReportStrings.ResetTheStopwatchFormat, actor); 22 | 23 | /// 24 | public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default) 25 | { 26 | var ability = actor.GetAbility(); 27 | ability.Stopwatch.Reset(); 28 | return default; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Performables/StartTheStopwatch.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using CSF.Screenplay.Abilities; 4 | using CSF.Screenplay.Resources; 5 | 6 | namespace CSF.Screenplay.Performables 7 | { 8 | /// 9 | /// An action which starts the stopwatch. 10 | /// 11 | /// 12 | /// 13 | /// This performable requires the actor has the ability . 14 | /// Use this performable via the builder method . 15 | /// 16 | /// 17 | public class StartTheStopwatch : IPerformable, ICanReport 18 | { 19 | /// 20 | public ReportFragment GetReportFragment(IHasName actor, IFormatsReportFragment formatter) 21 | => formatter.Format(PerformableReportStrings.StartTheStopwatchFormat, actor); 22 | 23 | /// 24 | public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default) 25 | { 26 | var ability = actor.GetAbility(); 27 | ability.Stopwatch.Start(); 28 | return default; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Performables/StopTheStopwatch.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using CSF.Screenplay.Abilities; 4 | using CSF.Screenplay.Resources; 5 | 6 | namespace CSF.Screenplay.Performables 7 | { 8 | /// 9 | /// An action which stops the stopwatch. 10 | /// 11 | /// 12 | /// 13 | /// This performable requires the actor has the ability . 14 | /// Use this performable via the builder method . 15 | /// 16 | /// 17 | public class StopTheStopwatch : IPerformable, ICanReport 18 | { 19 | /// 20 | public ReportFragment GetReportFragment(IHasName actor, IFormatsReportFragment formatter) 21 | => formatter.Format(PerformableReportStrings.StopTheStopwatchFormat, actor); 22 | 23 | /// 24 | public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default) 25 | { 26 | var ability = actor.GetAbility(); 27 | ability.Stopwatch.Stop(); 28 | return default; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Performables/TimeSpanBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Performables 2 | { 3 | /// 4 | /// Static helper class for creating instances of . 5 | /// 6 | /// 7 | /// 8 | /// See the documentation for for more information 9 | /// about how this class is to be used. 10 | /// 11 | /// 12 | public static class TimeSpanBuilder 13 | { 14 | /// 15 | /// Creates and returns a which can hold time span information 16 | /// and then continue the building process associated with the other builder. 17 | /// 18 | /// 19 | /// 20 | /// See the documentation for for more information 21 | /// about how this method is to be used. 22 | /// 23 | /// 24 | /// An instance of another performable builder 25 | /// The absolute time span value, without any units 26 | /// The type of the other performable builder 27 | public static TimeSpanBuilder Create(TOtherBuilder otherBuilder, int value) where TOtherBuilder : class 28 | => new TimeSpanBuilder(otherBuilder, value); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Performances/IBeginsAndEndsPerformance.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Performances 2 | { 3 | /// An object which controls the beginning and ending of a performance 4 | public interface IBeginsAndEndsPerformance 5 | { 6 | /// Begins the performance 7 | void BeginPerformance(); 8 | 9 | /// Finishes the performance with a value indicating whether or not it was a success 10 | /// If then the performance is to be considered a success; if 11 | /// then a failure. A value of indicates that the performance did not 12 | /// succeed but should not be considered a failure either. 13 | void FinishPerformance(bool? success); 14 | } 15 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Performances/ICreatesPerformance.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Performances 2 | { 3 | /// An object which creates instances of ; a factory service. 4 | public interface ICreatesPerformance 5 | { 6 | /// Creates a new performance instance. 7 | /// A new performance instance 8 | IPerformance CreatePerformance(); 9 | } 10 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Performances/PerformanceScopeEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay.Performances 4 | { 5 | /// 6 | /// A model for event arguments which relate to a scope of a . 7 | /// 8 | /// 9 | /// 10 | /// This event arguments class is often used as a base for models which identify a . 11 | /// 12 | /// 13 | /// 14 | public class PerformanceScopeEventArgs : EventArgs, IHasPerformanceIdentity 15 | { 16 | /// 17 | public Guid PerformanceIdentity { get; } 18 | 19 | /// 20 | /// Initializes a new instance of 21 | /// 22 | /// The performance identity 23 | public PerformanceScopeEventArgs(Guid performanceIdentity) 24 | { 25 | PerformanceIdentity = performanceIdentity; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Reporting/IFormattableValue.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Reporting 2 | { 3 | /// 4 | /// An object which has its own functionality for generating a human-readable representation of itself for a Screenplay report. 5 | /// 6 | /// 7 | /// 8 | /// Implement this interface in your own types in order to provide a custom representation of the object when it is included in 9 | /// a Screenplay report. 10 | /// The method should be used to create a human-readable string which represents the object in the report text. 11 | /// This interface is a part of the mechanism for in Screenplay. 12 | /// 13 | /// 14 | /// 15 | /// 16 | public interface IFormattableValue 17 | { 18 | /// 19 | /// Gets a human-readable formatted string which represents the current object instance, suitable to be used in a Screenplay report. 20 | /// 21 | /// A formatted string which represents the current instance. 22 | string FormatForReport(); 23 | } 24 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Reporting/IFormatterRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CSF.Screenplay.Reporting 5 | { 6 | /// 7 | /// A registry of the concrete types of which are available for use by the Screenplay reporting functionality. 8 | /// 9 | /// 10 | /// 11 | /// Formatter types stored within this registry will be selected for use in the reverse order in which they appear in the collection. 12 | /// When selecting a formatter for a particular object/value, this collection will be iterated starting at the end, moving toward the 13 | /// beginning. 14 | /// This means that (presuming new formatter types are added using ), types which are added later will 15 | /// be used with precedence over those which are added earlier. 16 | /// This is the mechanism which is used as a tie-breaker if two or more types would both return 17 | /// from . 18 | /// 19 | /// 20 | /// Developers might want to add a type to this collection, which is to have a lower precedence than another which has already 21 | /// been added, when they are both able to format the same value. 22 | /// In that scenario, use to explicitly add the formatter at a lower index (closer to the start of 23 | /// the collection). 24 | /// 25 | /// 26 | public interface IFormatterRegistry : IList {} 27 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Reporting/IGetsValueFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay.Reporting 4 | { 5 | /// 6 | /// An object which can select the most appropriate implementation of from a . 7 | /// 8 | public interface IGetsValueFormatter 9 | { 10 | /// 11 | /// Selects and returns an which is most appropriate to the specified . 12 | /// 13 | /// 14 | /// 15 | /// See the remarks for for more information about the algorithm by which an 16 | /// appropriate formatter is selected. 17 | /// 18 | /// 19 | /// It should be very rare for this method to raise an exception; as implementations of this type should come pre-loaded 20 | /// with fallback formatters which may format any value. 21 | /// Exceptions might only be expected if a developer removes these default formatters and does not replace them with suitable 22 | /// implementaton types that can cover all scenarios. 23 | /// 24 | /// 25 | /// The value to be formatted 26 | /// A value formatter 27 | /// If no appropriate formatter could be selected 28 | IValueFormatter GetValueFormatter(object value); 29 | } 30 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Reporting/NameAndValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay.Reporting 4 | { 5 | /// 6 | /// A simple model for a value that is to be included in a formatted , which has an associated name. 7 | /// 8 | public class NameAndValue 9 | { 10 | /// 11 | /// Gets the name associated with this value. 12 | /// 13 | public string Name { get; } 14 | 15 | /// 16 | /// Gets the value. 17 | /// 18 | public object Value { get; } 19 | 20 | /// 21 | /// Initializes a new instance of . 22 | /// 23 | /// The name for the value 24 | /// The value 25 | /// If is . 26 | public NameAndValue(string name, object value) 27 | { 28 | Name = name ?? throw new ArgumentNullException(nameof(name)); 29 | Value = value; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Reporting/ValueFormatterExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay.Reporting 4 | { 5 | /// 6 | /// Extension methods for . 7 | /// 8 | public static class ValueFormatterExtensions 9 | { 10 | /// 11 | /// Formats the specified value using an appropriate implementation of , retrieved from the factory. 12 | /// 13 | /// 14 | /// 15 | /// This extension method is a convenience, equivalent to using , 16 | /// followed by , both using as the parameter. 17 | /// 18 | /// 19 | /// A value formatter factory 20 | /// The value to be formatted 21 | /// The formatted value 22 | /// If is . 23 | public static string FormatValue(this IGetsValueFormatter formatterProvider, object value) 24 | { 25 | if (formatterProvider is null) 26 | throw new ArgumentNullException(nameof(formatterProvider)); 27 | return formatterProvider.GetValueFormatter(value).FormatForReport(value); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Resources/AbilityReportStrings.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | 3 | namespace CSF.Screenplay.Resources 4 | { 5 | /// Provides access to localisable string values which relate to abilities. 6 | internal static class AbilityReportStrings 7 | { 8 | static readonly ResourceManager resourceManager = new ResourceManager(typeof(AbilityReportStrings).FullName, typeof(AbilityReportStrings).Assembly); 9 | 10 | /// Gets a string which is the report format for . 11 | internal static string UseAStopwatchFormat => resourceManager.GetString("UseAStopwatchFormat"); 12 | } 13 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Resources/AbilityReportStrings.restext: -------------------------------------------------------------------------------- 1 | UseAStopwatchFormat = {Actor} is able to use a stopwatch -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Resources/PerformableReportStrings.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | 3 | namespace CSF.Screenplay.Resources 4 | { 5 | /// Provides access to localisable string values which relate to performables. 6 | internal static class PerformableReportStrings 7 | { 8 | static readonly ResourceManager resourceManager = new ResourceManager(typeof(PerformableReportStrings).FullName, typeof(PerformableReportStrings).Assembly); 9 | 10 | /// Gets a string which is the report format for . 11 | internal static string StopTheStopwatchFormat => resourceManager.GetString("StopTheStopwatchFormat"); 12 | 13 | /// Gets a string which is the report format for . 14 | internal static string StartTheStopwatchFormat => resourceManager.GetString("StartTheStopwatchFormat"); 15 | 16 | /// Gets a string which is the report format for . 17 | internal static string ResetTheStopwatchFormat => resourceManager.GetString("ResetTheStopwatchFormat"); 18 | 19 | /// Gets a string which is the report format for . 20 | internal static string ReadTheStopwatchFormat => resourceManager.GetString("ReadTheStopwatchFormat"); 21 | } 22 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Abstractions/Resources/PerformableReportStrings.restext: -------------------------------------------------------------------------------- 1 | StartTheStopwatchFormat = {Actor} starts their stopwatch 2 | StopTheStopwatchFormat = {Actor} stops their stopwatch 3 | ResetTheStopwatchFormat = {Actor} resets their stopwatch to zero 4 | ReadTheStopwatchFormat = {Actor} reads their stopwatch -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csf-dev/CSF.Screenplay/842ee7d745f2c77d80a1ba276435f7420750eb11/CSF.Screenplay.Docs/.nojekyll -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/CSF.Screenplay.Docs.proj: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(MSBuildProjectDirectory)\..\docs\ 4 | $(MSBuildProjectDirectory)\api 5 | docfx.json 6 | docfx.exe 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/README.md: -------------------------------------------------------------------------------- 1 | # Documentation source files 2 | 3 | This directory contains a [DocFx documentation project]. 4 | These are the source files which generate the documentation website in the `docs/` directory. 5 | 6 | Files in the `api/` subdirectory are auto-generated by the DocFx build process; they are not for hand-editing. 7 | API documentation is auto-generated from C# types and members and their XML documentation comments. 8 | 9 | Markdown files (except this README file) in this directory and subdirectories are converted to HTML pages by DocFx. 10 | The `CSF.Screenplay.Docs.proj` file ensures that the latest documentation is built just by executing `dotnet build`. 11 | If you do not yet have the `docfx` .NET tool installed then you will receive a build error; use the following command to install (or update) it. 12 | 13 | ```txt 14 | dotnet tool update -g docfx 15 | ``` 16 | 17 | [DocFx documentation project]: https://dotnet.github.io/docfx/index.html 18 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": [ 3 | { 4 | "src": [ 5 | { 6 | "src": "../", 7 | "files": [ "**/*.csproj" ], 8 | "exclude": [ 9 | "docs/**", 10 | "**/bin/**", 11 | "**/obj/**", 12 | "Tests_old/**", 13 | "Tests/**" 14 | ] 15 | } 16 | ], 17 | "dest": "api", 18 | "properties": { 19 | "ProduceReferenceAssembly": "true" 20 | } 21 | } 22 | ], 23 | "build": { 24 | "content": [ 25 | { 26 | "files": [ "**/*.{md,yml}" ], 27 | "exclude": [ "README.md" ] 28 | } 29 | ], 30 | "resource": [ 31 | { 32 | "files": [ 33 | "images/**", 34 | ".nojekyll" 35 | ] 36 | } 37 | ], 38 | "output": "../docs", 39 | "template": [ 40 | "default", 41 | "modern" 42 | ], 43 | "globalMetadata": { 44 | "_appName": "CSF.Screenplay", 45 | "_appTitle": "Screenplay docs", 46 | "_enableSearch": true, 47 | "pdf": false 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/HowScreenplayAndPerformanceRelate.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: HowScreenplayAndPerformanceRelateArticle 3 | --- 4 | 5 | # Top-down look at a screenplay 6 | 7 | The diagram below shows a top-down look at a [Screenplay] and how it relates to [Performances], [Actors] and [Performables]. 8 | The Screenplay _might itself be controlled by_ a [Test Integration], if Screenplay is being used for automated tests. 9 | 10 | The lifetime shown, for the [Performance] also indicates the lifetime of [the dependency injection scope]. 11 | 12 | ```mermaid 13 | sequenceDiagram 14 | accDescr { 15 | A single instance of Screenplay runs each Performance. 16 | Each Performance contains scripts for one or more Actors. 17 | Within the Performance, the Actor(s) perform one or more Performables. 18 | The lifetime of a single performance is shown, to illustrate the DI lifetime scope. 19 | } 20 | Screenplay->>Performance: Runs each 21 | actor A as Actor 22 | activate Performance 23 | Performance->>A: Contains scripts for
one or more 24 | A->>Performable: Performs one
or more 25 | Performable-->>A: Complete 26 | A-->>Performance: Complete 27 | Performance-->>Screenplay: Complete 28 | deactivate Performance 29 | ``` 30 | 31 | [Screenplay]: xref:CSF.Screenplay.Screenplay 32 | [Performances]: xref:CSF.Screenplay.IPerformance 33 | [Actors]: xref:CSF.Screenplay.Actor 34 | [Performables]: ../glossary/Performable.md 35 | [Test Integration]: ../glossary/Integration.md 36 | [Performance]: xref:CSF.Screenplay.IPerformance 37 | [the dependency injection scope]: dependencyInjection/DependencyInjectionScope.md -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/builderPattern/ConsumingBuilders.md: -------------------------------------------------------------------------------- 1 | # Consuming builders 2 | 3 | Core to a Screenplay [Performance] are [Performables]. 4 | Getting these performables is easiest when using a builder. 5 | This results in clear, human-readable code inside your performance. 6 | 7 | The entry point to most builders is a `static class` which exposes [factory methods] for the performables which it supports. 8 | These are easiest to consume with [a `using static` directive], such as: 9 | 10 | ```csharp 11 | using static MyNamespace.Builders.MyBuilderEntryPoint; 12 | ``` 13 | 14 | This means that the methods of the static entry point class may be used in the positions where a performable is required, such as the following. 15 | 16 | ```csharp 17 | using static DrinksNamespace.DrinksBuilder; 18 | 19 | await actor.PerformAsync(MakeACupOf("Coffee"), cancellationToken); 20 | ``` 21 | 22 | In the example above, the fictitious `MakeACupOf` method is a static method of the fictitious `DrinksBuilder` static entry-point class. 23 | 24 | [Performance]: xref:CSF.Screenplay.IPerformance 25 | [Performables]: ../../glossary/Performable.md 26 | [factory methods]: https://en.wikipedia.org/wiki/Factory_method_pattern 27 | [a `using static` directive]: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-directive#the-static-modifier -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/builderPattern/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: BuilderPatternMainArticle 3 | --- 4 | 5 | # The builder pattern 6 | 7 | As noted elsewhere, [Performables] are _stateful_ objects which _are not resolved from dependency injection_. 8 | The state/dependencies which should be provided to a performable object should be _parameter values_. 9 | For example, a performable which adds a product to a shopping cart might need two pieces of state: 10 | 11 | * Which product to add 12 | * The quantity 13 | 14 | Recall that any external dependencies should come from the performing actor's [Abilities]. 15 | 16 | It is possible to create instances of performable types with just the `new` keyword. 17 | The state could be provided using constructor-injected parameter values or via property setters. 18 | Whilst the techniques above will work, neither are as attractive or easy to read and comprehend as the builder pattern. 19 | 20 | [Performables]: ../../glossary/Performable.md 21 | [Abilities]: ../../glossary/Ability.md 22 | 23 | * [Writing builders for Screenplay](WritingBuilders.md) 24 | * [Consuming builders to create performables](ConsumingBuilders.md) 25 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/builderPattern/toc.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csf-dev/CSF.Screenplay/842ee7d745f2c77d80a1ba276435f7420750eb11/CSF.Screenplay.Docs/docs/builderPattern/toc.yml -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/dependencyInjection/InjectableServices.md: -------------------------------------------------------------------------------- 1 | # Injectable services 2 | 3 | TODO: Write this docco 4 | 5 | It should be a list of services which Screenplay makes available for DI, such as the cast & stage etc. 6 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/extendingScreenplay/EventHandlers.md: -------------------------------------------------------------------------------- 1 | # Writing new Event handlers 2 | 3 | A [Screenplay] is an architecture for directing a series of steps, organised into groups named [Performances]. 4 | Within that architecture is an **event** model, which allows arbitrary logic to subscribe to the progress of a Screenplay and react accordingly. 5 | This is one of the natural _extension points_ within the Screenplay library. 6 | 7 | [Screenplay]: xref:CSF.Screenplay.Screenplay 8 | [Performances]: xref:CSF.Screenplay.IPerformance 9 | 10 | ## How to subscribe to events 11 | 12 | Within the CSF.Screenplay.Abstractions library/NuGet package is the type [`IHasPerformanceEvents`]. 13 | An instance of this type is available to resolve via [dependency injection] whilst a Screenplay is in-progress. 14 | Within DI, this object is a singleton which will emit when any significant Screenplay-related event occurs. 15 | Take a look at the API of the interface and its documentation to read about the available events and their meaning. 16 | 17 | If you wish to extend Screenplay you may subscribe to these from your own logic as you please. 18 | 19 | [`IHasPerformanceEvents`]: xref:CSF.Screenplay.Performances.IHasPerformanceEvents 20 | [dependency injection]: ../dependencyInjection/index.md -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/extendingScreenplay/index.md: -------------------------------------------------------------------------------- 1 | # Extending Screenplay 2 | 3 | Screenplay has numerous extension points available for developers to add their own functionality. 4 | 5 | * [Abilities, Actions & Questions] 6 | * [Dependency services] 7 | * [Event handlers] 8 | * [Report value formatters] 9 | * [Test integrations] 10 | 11 | [Abilities, Actions & Questions]: AbilitiesActionsAndQuestions.md 12 | [Dependency services]: ../dependencyInjection/AddingServices.md 13 | [Event handlers]: EventHandlers.md 14 | [Report value formatters]: ReportFormtters.md 15 | [Test integrations]: TestIntegrations.md 16 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/nUnitTutorial/BestPractices.md: -------------------------------------------------------------------------------- 1 | # NUnit & Screenplay best practices 2 | 3 | TODO: Write this docco 4 | 5 | * Keep assertions out of performables 6 | * Use the async test syntax -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/performables/index.md: -------------------------------------------------------------------------------- 1 | # Performables 2 | 3 | Screenplay comes with a few pre-created [Abilities], [Performables] and [Builders], for common tasks. 4 | Some of these require the installation of additional **NuGet packages**. 5 | 6 | [Abilities]: ../../glossary/Ability.md 7 | [Performables]: ../../glossary/Performable.md 8 | [Builders]: ../builderPattern/index.md 9 | 10 | ## Using a Stopwatch 11 | 12 | When an actor needs to keep precise track of time, you may give them the [`UseAStopwatch`] ability. 13 | Actors with this ability may use Actions and Questions which relate to use of the stopwatch. 14 | These are all accessible from the builder class [`StopwatchBuilder`]. 15 | 16 | [`UseAStopwatch`]: xref:CSF.Screenplay.Abilities.UseAStopwatch 17 | [`StopwatchBuilder`]: xref:CSF.Screenplay.Performables.StopwatchBuilder 18 | 19 | ## Interacting with web APIs 20 | 21 | The NuGet package [CSF.Screenplay.WebApis] provides an ability, performables and supporting types to interact with web API endpoints. 22 | Further information is available on [the web API documentation page]. 23 | 24 | [CSF.Screenplay.WebApis]: https://www.nuget.org/packages/CSF.Screenplay.WebApis/ 25 | [the web API documentation page]: WebApis.md 26 | 27 | ## TimeSpan builder 28 | 29 | The [`TimeSpanBuilder`] is not a complete performable builder; it is intended to supplement other builders such as those of your own design. 30 | It handles a commonly-used aspect of building performables in a reusable manner. 31 | 32 | [`TimeSpanBuilder`]: xref:CSF.Screenplay.Performables.TimeSpanBuilder`1 -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/performables/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Introduction 2 | href: index.md 3 | - name: Stopwatch 4 | uid: CSF.Screenplay.Performables.StopwatchBuilder 5 | - name: Web APIs 6 | href: WebApis.md 7 | - name: TimeSpan builder 8 | uid: CSF.Screenplay.Performables.TimeSpanBuilder`1 -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Documentation home 2 | href: ../index.md 3 | - name: Screenplay concepts 4 | items: 5 | - name: Makeup of a Screenplay 6 | href: MakeupOfAScreenplay.md 7 | - name: Screenplay sequence diagram 8 | href: HowScreenplayAndPerformanceRelate.md 9 | - name: Dependency injection 10 | href: dependencyInjection/index.md 11 | - name: Using Screenplay 12 | items: 13 | - name: Writing performables 14 | href: writingPerformables/index.md 15 | - name: Builder pattern 16 | href: builderPattern/index.md 17 | - name: Reports 18 | href: GettingReports.md 19 | - name: As a testing tool 20 | href: SuitabilityAsATestingTool.md 21 | items: 22 | - name: Screenplay in the testing stack 23 | href: ScreenplayInTheTestingStack.md 24 | - name: NUnit tutorial 25 | href: nUnitTutorial/index.md 26 | - name: SpecFlow tutorial 27 | href: specFlowTutorial/index.md 28 | - name: Performables 29 | href: performables/toc.yml 30 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/writingPerformables/AvoidBranchingLogic.md: -------------------------------------------------------------------------------- 1 | # Avoid branching logic 2 | 3 | [Performances] are written _a lot like scripts_. 4 | Where possible, [Performables] should avoid branching or looping logic. 5 | _This is particularly true_ when Screenplay is being used [as a tool for testing]. 6 | Good test logic has [a cyclomatic complexity] of precisely one. 7 | 8 | Sometimes looping logic is unavoidable and desirable in a Screenplay, imagine a performable which has an [Actor] repeat a process `N` times. 9 | This is acceptable if used judiciously. 10 | 11 | Performables should always avoid branching logic like `if` or `switch` though, and _should never_ contain such logic when being used for tests. 12 | If more than one mode of operation is required then write more than one performable. 13 | The path through a Performance should be completely deterministic, short of an unexpected error or failure. 14 | 15 | [Performances]: xref:CSF.Screenplay.IPerformance 16 | [Performables]: ../../glossary/Performable.md 17 | [as a tool for testing]: ../SuitabilityAsATestingTool.md 18 | [a cyclomatic complexity]: https://en.wikipedia.org/wiki/Cyclomatic_complexity 19 | [Actor]: xref:CSF.Screenplay.Actor 20 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/writingPerformables/ImplementICanReport.md: -------------------------------------------------------------------------------- 1 | # Implement `ICanReport` 2 | 3 | When writing a [Performable] class, implement the [`ICanReport`] interface a well as the relevant performable interface. 4 | This enables a performable to emit a formatted, human-readable fragment of a report for the current performable. 5 | 6 | If you plan to redistribute your performables as a library then consider making your report fragments [localizable strings], so that they may be translated for other locales. 7 | 8 | [Performable]: ../../glossary/Performable.md 9 | [`ICanReport`]: xref:CSF.Screenplay.ICanReport 10 | [localizable strings]: https://learn.microsoft.com/en-us/dotnet/core/extensions/localization 11 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/writingPerformables/ImplementOnePerformableInterface.md: -------------------------------------------------------------------------------- 1 | # Implementing the performable interface 2 | 3 | A [Performable] is a class which implements one of the three performable interfaces: 4 | 5 | * [`IPerformable`] 6 | * [`IPerformableWithResult`] 7 | * [`IPerformableWithResult`] 8 | 9 | Performables must implement **_precisely one_** of these interfaces. 10 | Implementing more than one upon a single performable is not recommended or supported. 11 | Doing so is liable to cause difficulties. 12 | 13 | If you wish to share code then move the reusable logic into [a Task] and consume that from separate performable classes. 14 | 15 | [Performable]: ../../glossary/Performable.md 16 | [`IPerformable`]: xref:CSF.Screenplay.IPerformable 17 | [`IPerformableWithResult`]: xref:CSF.Screenplay.IPerformableWithResult 18 | [`IPerformableWithResult`]: xref:CSF.Screenplay.IPerformableWithResult`1 19 | [a Task]: ../../glossary/Task.md 20 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/writingPerformables/ParameterizeLowLevelPerformables.md: -------------------------------------------------------------------------------- 1 | # Parameterize low-level Performables 2 | 3 | When writing low-level [Performables], these classes should expose parameters which allow the consumer to provide as many of the variables as make sense. 4 | This _is especially important for [Actions] and/or [Questions]_ but also relevant for lower-level/reusable [Tasks]. 5 | 6 | Parameters should be accepted via the class' public constructor, the values to these parameters [should then be held `readonly`]. 7 | Accept as many parameters as are reasonable, although avoid going so far that parameterisation makes a Performable unclear as to what it does. 8 | 9 | [Performables]: ../../glossary/Performable.md 10 | [Actions]: ../../glossary/Action.md 11 | [Questions]: ../../glossary/Question.md 12 | [Tasks]: ../../glossary/Task.md 13 | [should then be held `readonly`]: StatefulButImmutable.md 14 | 15 | ## An example 16 | 17 | Imagine we are writing a Task which makes a cup of coffee, ready to serve. 18 | It would make sense to include parameters which decide the strength of the coffee, how much milk and sweetener to add and similar. 19 | 20 | It would usually be a mistake to create a Task which can make _any hot drink_, where the parameters provided decide which hot drink to make. 21 | The processes for making various hot drinks are often fundamentally different; consider the process for making coffee and then that for brewing tea. 22 | 23 | In the most extreme case, where such a Task is required, separate the logic of making of each hot drink into Tasks of their own, and consume/execute the relevant one of these from the _any hot drink_ Task. 24 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/writingPerformables/PureFunctionalTasks.md: -------------------------------------------------------------------------------- 1 | # 'Pure functional' tasks 2 | 3 | To maximise their reusability, developers are advised to write **[Tasks]** in a way which _either_: 4 | 5 | * Changes the application's state 6 | * Gets information from the application without changing its state 7 | 8 | This draws from [the lessons that writing pure functions] teaches us. 9 | If 'getting some information' has unwanted or unexpected _side-effects_ then the task becomes less reusable. 10 | If a task _must have_ side effects then consider separating the parts of it which do not require those side-effects into a lower-level task which is reusable without the side-effects. 11 | That lower-level task would then be consumed by a higher-level task which does include the side-effects. 12 | 13 | [Tasks]: ../../glossary/Task.md 14 | [the lessons that writing pure functions]: https://en.wikipedia.org/wiki/Pure_function 15 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/writingPerformables/StatefulButImmutable.md: -------------------------------------------------------------------------------- 1 | # Performables are stateful, but immutable 2 | 3 | Instances of [Performable] classes _are not intended to be reused_. 4 | A single class may have many instances created, but each instance should be used only once. 5 | This is because [the 'parameter values'] for each performable are provided into that performable object instance. 6 | 7 | Parameters should ideally be provided into the performable class' public constructor. 8 | Another viable technique could be [`init`-only property setters]. 9 | Once these values are set, they should be `readonly` so that they may not be changed. 10 | 11 | Performable classes should also avoid the use of mutable class-level data such as fields or properties. 12 | Any temporary state should be scoped only to the relevant `PerformAsAsync` method. 13 | 14 | [Performable]: ../../glossary/Performable.md 15 | [the 'parameter values']: ParameterizeLowLevelPerformables.md 16 | [`init`-only property setters]: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/init 17 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/writingPerformables/TasksDoNotUseAbilities.md: -------------------------------------------------------------------------------- 1 | # Tasks should not use abilities 2 | 3 | The logic of [Task classes] should not interact with the actor's **[Abilities]**. 4 | Logic which interacts with abilities should be limited to [Action] and/or [Question] classes. 5 | 6 | Move logic which needs to interact with Abilities into Action/Question classes and ensure that they are appropriately parameterized. 7 | Consume such actions or questions from your custom Task class. 8 | 9 | [Task classes]: ../../glossary/Task.md 10 | [Abilities]: ../../glossary/Ability.md 11 | [Action]: ../../glossary/Action.md 12 | [Question]: ../../glossary/Question.md 13 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/docs/writingPerformables/WriteABuilder.md: -------------------------------------------------------------------------------- 1 | # Write builders for your performables 2 | 3 | If you write a new performable class, it is strongly recommended to write [a static builder] for it. 4 | Benefits include: 5 | 6 | * Ensuring that the performable may only be created in a valid state 7 | * Making your performance logic more human-readable, almost like a [domain specific language] 8 | 9 | [a static builder]: ../builderPattern/index.md 10 | [domain specific language]: https://en.wikipedia.org/wiki/Domain-specific_language 11 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/glossary/Action.md: -------------------------------------------------------------------------------- 1 | # Action 2 | 3 | An Action is a kind of **[Performable]** in which an **[Actor]** does something or interacts with the application in such as way as to change its state. 4 | Specifically, an action should be the smallest, most granular change or interaction possible; something which cannot reasonably be split into constituent parts. 5 | 6 | In an application of Screenplay which controls a web browser, an action might be a single mouse click, or entering some specified text into an input field. 7 | To create higher-level interactions, use **[Tasks]** to compose actions. 8 | Out of the kinds of performable, actions are the smallest building blocks available. 9 | 10 | Actions don't have a direct representation in Screenplay code because they are really just an arbitrary category of performable. 11 | Actions _most commonly_ implement the [`IPerformable`] interface though, as they usually do not return any results. 12 | 13 | Generally, the logic of actions interacts directly with the actor's **[Abilities]** in order to provide the functionality required to perform the action. 14 | 15 | [Performable]: Performable.md 16 | [Actor]: xref:CSF.Screenplay.Actor 17 | [Tasks]: Task.md 18 | [`IPerformable`]: xref:CSF.Screenplay.IPerformable 19 | [Abilities]: Ability.md -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/glossary/Asset.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: AssetGlossaryItem 3 | --- 4 | 5 | # Asset 6 | 7 | An Asset forms part of a Screenplay **[Report]**. 8 | Assets are **files** which are generated during the course of [Performances], which are saved to the filesystem. 9 | These assets are saved alongside the report, and should be kept with it. 10 | 11 | Assets may be any type of file system file, in any format conceivable. 12 | Their usage/content is somewhat open-ended but they are best-used for files which help a reader understand the report of the [Screenplay's] performances. 13 | 14 | Assets _are not intended_ for cases when Screenplay is used to actually generate output which will be used outside of reading the report. 15 | Indeed, _asset files must not be created or saved_ if [reporting is disabled]. 16 | 17 | [Report]: Report.md 18 | [Performances]: xref:CSF.Screenplay.IPerformance 19 | [Screenplay's]: xref:CSF.Screenplay.Screenplay 20 | [reporting is disabled]: TODO 21 | 22 | ## How to create assets 23 | 24 | TODO: Write this docco 25 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/glossary/Feature.md: -------------------------------------------------------------------------------- 1 | # Feature 2 | 3 | A **Feature** is a concept which is relevant when the Screenplay library is being used to perform automated tests. 4 | 5 | Each feature is a logical group of one or more related **[Scenarios]**. 6 | In Screenplay, features exist only for organising scenarios. 7 | They have no first-class representation in the code; their only appearance is within **[Reports]**. 8 | 9 | Everything related to features is handled automatically when consuming Screenplay from an appropriate **[Integration]**. 10 | 11 | Depending upon the testing framework in use, features might alternatively be named "test fixture" or "test class". 12 | 13 | [Scenarios]: Scenario.md 14 | [Reports]: Report.md 15 | [Integration]: Integration.md 16 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/glossary/Integration.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: IntegrationGlossaryItem 3 | --- 4 | 5 | # Integration 6 | 7 | An **Integration** refers to an integration library between the Screenplay library and a framework for performing automated tests. 8 | 9 | The integration library performs the necessary scaffolding to make the Screenplay types available for dependency injection. 10 | It also deals with the association of **[Scenarios]** with **[Performances]** and the lifetime of the whole **[Screenplay]**, culminating with the production of the **[Report]**. 11 | 12 | [Scenarios]: Scenario.md 13 | [Performances]: xref:CSF.Screenplay.IPerformance 14 | [Screenplay]: xref:CSF.Screenplay.Screenplay 15 | [Report]: Report.md 16 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/glossary/Persona.md: -------------------------------------------------------------------------------- 1 | # Persona 2 | 3 | A Persona is a class which serves as [a factory] for a specific **[Actor]**. 4 | In Screenplay, it is strongly recommended to create and re-use well-known Actors across your **[Performances]**. 5 | That is, if an actor with a specified [`Name`] has a certain set of **[Abilities]** in one Performance, then ideally all actors of the same name should have that same set of abilities in other performances. 6 | 7 | This leads to the creation of well-known Actors which are well-understood by the team who are working with Screenplay. 8 | Personas help facilitate that by providing a reusable location at which to set the actor's name and to assign & configure their abilities. 9 | 10 | Personas in Screenplay are classes which implement the [`IPersona`] interface. 11 | 12 | [a factory]: https://en.wikipedia.org/wiki/Factory_method_pattern 13 | [Actor]: xref:CSF.Screenplay.Actor 14 | [Performances]: xref:CSF.Screenplay.IPerformance 15 | [`Name`]: xref:CSF.Screenplay.IHasName.Name 16 | [Abilities]: Ability.md 17 | [`IPersona`]: xref:CSF.Screenplay.IPersona -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/glossary/Question.md: -------------------------------------------------------------------------------- 1 | # Question 2 | 3 | A Question is a kind of **[Performable]** in which an **[Actor]** gets or reads some information from the application, ideally in such a way that does not change the application's state. 4 | Similar to **[Actions]**, questions should be as small in their scope as possible, to make them as reusable and composable as possible. 5 | In code, questions get a value and return it to the consuming logic, so they will always implement one of [`IPerformableWithResult`] or its non-generic counterpart [`IPerformableWithResult`]. 6 | 7 | In an application of Screenplay which controls a web browser, a questions might represent reading the text from a single HTML element, or reading the enabled/disabled state of a button. 8 | To create higher-level questions with broader scope, compose them with **[Tasks]**. 9 | 10 | Generally, the logic of questions interacts directly with the actor's **[Abilities]** in order to provide the functionality required to get the requested information. 11 | 12 | [Performable]: Performable.md 13 | [Actor]: xref:CSF.Screenplay.Actor 14 | [Actions]: Action.md 15 | [`IPerformableWithResult`]: xref:CSF.Screenplay.IPerformableWithResult 16 | [`IPerformableWithResult`]: xref:CSF.Screenplay.IPerformableWithResult`1 17 | [Tasks]: Task.md 18 | [Abilities]: Ability.md 19 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/glossary/Report.md: -------------------------------------------------------------------------------- 1 | # Report 2 | 3 | After a **[Screenplay]** has completed, the Screenplay software may produce a machine-readable report of what occurred. 4 | This report may be read, processed, stored or transformed into an alternative format as desired. 5 | 6 | The report is hierarchical; at its topmost level are **[Features]** and within are **[Scenarios]**. 7 | The scenario directly corresponds to a single **[Performance]**, and within are contained all of the **[Performables]** for that performance. 8 | 9 | Performables in a report are also included hierarchically. This means that high-level **[Tasks]** contain information about the performables from which they are composed. 10 | 11 | Reports are useful to document what has been performed in a Screenplay. 12 | They help developers diagnose and debug issues when they arise. 13 | 14 | [Screenplay]: xref:CSF.Screenplay.Screenplay 15 | [Features]: Feature.md 16 | [Scenarios]: Scenario.md 17 | [Performance]: xref:CSF.Screenplay.IPerformance 18 | [Performables]: Performable.md 19 | [Tasks]: Task.md -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/glossary/Spotlight.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: SpotlightGlossaryItem 3 | --- 4 | 5 | # Spotlight 6 | 7 | When the [Stage] is used to get an [Actor], that actor is placed _in the **Spotlight**_. 8 | That same actor remains in the spotlight until a different actor is placed there. 9 | Note, though, that a Stage and thus its corresponding Spotlight is scoped to the current [Performance]. 10 | A different performance has a completely independent Stage and Spotlight. 11 | 12 | A stage may be used at any time to get the actor who is currently in the spotlight. 13 | Thus, it is possible to infer an actor in [Performance] logic without needing to use their name. 14 | 15 | See the documentation for the [Stage] for more information. 16 | 17 | [Performance]: xref:CSF.Screenplay.IPerformance 18 | [Actor]: xref:CSF.Screenplay.Actor 19 | [Stage]: xref:CSF.Screenplay.IStage 20 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/glossary/Task.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: TaskGlossaryItem 3 | --- 4 | 5 | # Task 6 | 7 | A Task is a kind of **[Performable]** which represents a high-level interaction with the application. 8 | Where **[Actions]** or **[Questions]** are highly granular for maximum reusability, tasks may be as specific as the use-case warrants. 9 | 10 | Action & question classes are often shipped with Screenplay frameworks. 11 | Tasks are typically written by the developer who is making use of Screenplay. 12 | In practice, tasks are just compositions of actions, questions or other lower-level tasks. 13 | 14 | In a Screenplay which controls a web application, an example of a task is the completion of a registration form which involves entering data into multiple input fields. 15 | 16 | [Performable]: Performable.md 17 | [Actions]: Action.md 18 | [Questions]: Question.md 19 | 20 | ## Writing tasks 21 | 22 | Tasks may implement [any of the three performable interfaces]. 23 | Developers are encouraged to [follow these best practices] when writing Task classes. 24 | 25 | [any of the three performable interfaces]: Performable.md#the-three-performable-interfaces-and-icanreport 26 | [follow these best practices]: ../docs/writingPerformables/index.md 27 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/glossary/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Glossary 2 | href: index.md 3 | - name: Ability 4 | href: Ability.md 5 | - name: Action 6 | href: Action.md 7 | - name: Actor 8 | uid: CSF.Screenplay.Actor 9 | - name: Cast 10 | uid: CSF.Screenplay.ICast 11 | - name: Feature 12 | href: Feature.md 13 | - name: Integration 14 | href: Integration.md 15 | - name: Performable 16 | href: Performable.md 17 | - name: Performance 18 | uid: CSF.Screenplay.Performance 19 | - name: Persona 20 | href: Persona.md 21 | - name: Question 22 | href: Question.md 23 | - name: Report 24 | href: Report.md 25 | - name: Scenario 26 | href: Scenario.md 27 | - name: Screenplay 28 | uid: CSF.Screenplay.Screenplay 29 | - name: Stage 30 | uid: CSF.Screenplay.IStage 31 | - name: Task 32 | href: Task.md 33 | -------------------------------------------------------------------------------- /CSF.Screenplay.Docs/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Documentation 2 | href: index.md 3 | - name: Glossary 4 | href: glossary/ 5 | - name: API reference 6 | href: api/ 7 | - name: GitHub repo 8 | href: https://github.com/csf-dev/CSF.Screenplay -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/README.md: -------------------------------------------------------------------------------- 1 | # HTML template source 2 | 3 | This project contains a small node/Webpack project, used to build the HTML file template for **CSF.Screenplay.JsonToHtmlReport**. 4 | 5 | The HTML template is used as an embedded resource in that app/library. 6 | This project contains the _source code_ for building the HTML template; the build output of this project is sent to `CSF.Screenplay.JsonToHtmlReport/template/template.html`, where it is consumed as an embedded resource. 7 | -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/.node-version: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csf-dev/CSF.Screenplay/842ee7d745f2c77d80a1ba276435f7420750eb11/CSF.Screenplay.JsonToHtmlReport.Template/src/.node-version -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [['@babel/preset-env', {targets: {node: 'current'}}]], 3 | }; -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/css/content.css: -------------------------------------------------------------------------------- 1 | @layer content { 2 | body { 3 | font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; 4 | line-height: 1.35em; 5 | } 6 | .hidden { 7 | display: none; 8 | } 9 | h1, h2, h3, h4 { 10 | font-family: Arial, Helvetica, sans-serif; 11 | } 12 | h1 { 13 | font-size: 2.5em; 14 | line-height: 1.35em; 15 | margin: 0.2em 0 0.1em 1em; 16 | } 17 | h2 { 18 | font-size: 1.4em; 19 | line-height: 1.35em; 20 | margin: 1em 0 0; 21 | } 22 | h3 { 23 | font-size: 1.2em; 24 | line-height: 1.35em; 25 | margin: 0; 26 | font-weight: bold; 27 | } 28 | h4 { 29 | line-height: 1.35em; 30 | margin: 0.4em 0 0; 31 | font-weight: bold; 32 | } 33 | body > footer { 34 | color: #666; 35 | } 36 | body > footer a { 37 | color: #88A; 38 | } 39 | body > footer a:hover { 40 | color: #66D 41 | } 42 | } -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/css/layers.css: -------------------------------------------------------------------------------- 1 | @layer reset, layout, content, spinner; -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/css/layout.css: -------------------------------------------------------------------------------- 1 | @layer layout { 2 | html, body { 3 | height: 100%; 4 | } 5 | body { 6 | display: flex; 7 | flex-direction: column; 8 | background: #f0fcff; 9 | } 10 | body > header { 11 | flex: none; 12 | background: #FFF; 13 | border-bottom: 1px solid #e0e0e0; 14 | } 15 | body > main { 16 | flex: 1 auto; 17 | padding: 0 1em; 18 | } 19 | body > footer { 20 | flex: none; 21 | background: #dfecf1; 22 | border-top: 1px solid #DDD; 23 | padding: 0.3em 1em; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/css/reset.css: -------------------------------------------------------------------------------- 1 | @layer reset { 2 | /* http://meyerweb.com/eric/tools/css/reset/ 3 | v2.0 | 20110126 4 | License: none (public domain) 5 | */ 6 | 7 | html, body, div, span, applet, object, iframe, 8 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 9 | a, abbr, acronym, address, big, cite, code, 10 | del, dfn, em, img, ins, kbd, q, s, samp, 11 | small, strike, strong, sub, sup, tt, var, 12 | b, u, i, center, 13 | dl, dt, dd, ol, ul, li, 14 | fieldset, form, label, legend, 15 | table, caption, tbody, tfoot, thead, tr, th, td, 16 | article, aside, canvas, details, embed, 17 | figure, figcaption, footer, header, hgroup, 18 | menu, nav, output, ruby, section, summary, 19 | time, mark, audio, video { 20 | margin: 0; 21 | padding: 0; 22 | border: 0; 23 | font-size: 100%; 24 | font-family: inherit; 25 | vertical-align: baseline; 26 | } 27 | /* HTML5 display-role reset for older browsers */ 28 | article, aside, details, figcaption, figure, 29 | footer, header, hgroup, menu, nav, section { 30 | display: block; 31 | } 32 | body { 33 | line-height: 1; 34 | } 35 | ol, ul { 36 | list-style: none; 37 | } 38 | blockquote, q { 39 | quotes: none; 40 | } 41 | blockquote:before, blockquote:after, 42 | q:before, q:after { 43 | content: ''; 44 | content: none; 45 | } 46 | table { 47 | border-collapse: collapse; 48 | border-spacing: 0; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/css/spinner.css: -------------------------------------------------------------------------------- 1 | @layer spinner { 2 | #pageMask { 3 | position: fixed; 4 | top: 0; 5 | left: 0; 6 | width: 100%; 7 | height: 100%; 8 | background: #FFFFFF88; 9 | backdrop-filter: blur(3px); 10 | } 11 | #spinnerContainer { 12 | margin: 0; 13 | position: absolute; 14 | top: 50%; 15 | left: 50%; 16 | transform: translate(-50%, -50%); 17 | } 18 | #loadingSpinner { 19 | width:100px; 20 | height:100px; 21 | border-radius:50%; 22 | background:conic-gradient(#0000 10%,#766DF4); 23 | -webkit-mask:radial-gradient(farthest-side,#0000 calc(100% - 16px),#000 0); 24 | animation:s3 1s infinite linear; 25 | } 26 | @keyframes s3 {to{transform: rotate(1turn)}} 27 | } -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/css/summaryTable.css: -------------------------------------------------------------------------------- 1 | @layer content { 2 | #summaryTable { 3 | border-collapse: collapse; 4 | margin: 0.4em 0; 5 | } 6 | #summaryTable th, #summaryTable td { 7 | border: 1px solid #00000066; 8 | padding: 0.2em 0.5em; 9 | } 10 | #summaryTable th { 11 | background: #00000022; 12 | } 13 | #summaryTable thead th.type span { 14 | display: none; 15 | } 16 | #summaryTable th.type, 17 | #summaryTable td { 18 | text-align: right; 19 | } 20 | } -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/index.js: -------------------------------------------------------------------------------- 1 | import "./css/layers.css"; 2 | import "./css/reset.css"; 3 | import "./css/spinner.css" 4 | import "./css/layout.css"; 5 | import "./css/content.css"; 6 | import "./css/summaryTable.css"; 7 | import "./css/scenarioList.css"; 8 | import { getReportLoader } from "./js/ReportLoader"; 9 | import { activatePage } from "./js/activatePage"; 10 | import { updateReportTime } from "./js/updateReportTime"; 11 | import { getScenarioAggregator } from "./js/ScenarioAggregator"; 12 | import { getSummaryGenerator } from "./js/SummaryGenerator"; 13 | import { getReportWriter } from "./js/ReportWriter"; 14 | 15 | document.onreadystatechange = () => { 16 | if (document.readyState !== "complete") return; 17 | 18 | const loader = getReportLoader(); 19 | const report = loader.loadJson(); 20 | 21 | updateReportTime(report.Metadata.Timestamp); 22 | 23 | const aggregator = getScenarioAggregator(report.Performances); 24 | const scenariosByFeature = aggregator.getScenariosByFeature(); 25 | console.debug('Raw scenario data', scenariosByFeature); 26 | const summaryGenerator = getSummaryGenerator(scenariosByFeature); 27 | const summary = summaryGenerator.generateSummary(); 28 | const reportWriter = getReportWriter(); 29 | const featureReport = reportWriter.getReport(scenariosByFeature); 30 | 31 | activatePage(summary, featureReport); 32 | } 33 | 34 | -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('jest').Config} */ 2 | const config = { 3 | clearMocks: true, 4 | collectCoverage: true, 5 | coverageDirectory: "TestResults", 6 | }; 7 | 8 | module.exports = config; 9 | -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/js/ReportLoader.js: -------------------------------------------------------------------------------- 1 | import { getElementById } from './getElementById'; 2 | 3 | const reportContentId = 'reportSrc'; 4 | 5 | export class ReportLoader { 6 | scriptId; 7 | 8 | constructor(scriptId) { 9 | this.scriptId = scriptId; 10 | } 11 | 12 | loadJson() { 13 | const scriptElement = getElementById(this.scriptId); 14 | if (!scriptElement) { 15 | throw new Error(`Element with id ${this.scriptId} not found`); 16 | } 17 | 18 | try { 19 | const jsonData = JSON.parse(scriptElement.textContent); 20 | return jsonData; 21 | } catch (error) { 22 | console.error(error); 23 | throw new Error('Failed to parse JSON content'); 24 | } 25 | } 26 | } 27 | 28 | export function getReportLoader() { 29 | return new ReportLoader(reportContentId); 30 | } -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/js/ReportLoader.spec.js: -------------------------------------------------------------------------------- 1 | import { ReportLoader } from "./ReportLoader"; 2 | import { getElementById } from './getElementById'; 3 | 4 | jest.mock('./getElementById'); 5 | 6 | test('ReportLoader should load JSON content from the specified script element', () => { 7 | const scriptElement = { textContent: '{"foo": "bar"}' }; 8 | getElementById.mockReturnValue(scriptElement); 9 | 10 | const reportLoader = new ReportLoader('scriptId'); 11 | const jsonData = reportLoader.loadJson(); 12 | 13 | expect(jsonData).toEqual({foo: 'bar'}); 14 | }); 15 | 16 | test('ReportLoader should throw an error if the specified script element is not found', () => { 17 | getElementById.mockReturnValue(null); 18 | 19 | const reportLoader = new ReportLoader('scriptId'); 20 | expect(() => reportLoader.loadJson()).toThrowError('Element with id scriptId not found'); 21 | }); 22 | 23 | test('ReportLoader should throw an error if the specified script element does not contain valid JSON', () => { 24 | const scriptElement = { textContent: 'invalid json' }; 25 | getElementById.mockReturnValue(scriptElement); 26 | 27 | const reportLoader = new ReportLoader('scriptId'); 28 | expect(() => reportLoader.loadJson()).toThrowError('Failed to parse JSON content'); 29 | }); -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/js/ReportWriter/ReportWriter.js: -------------------------------------------------------------------------------- 1 | import { getFeatureElementCreator } from './FeatureElementCreator'; 2 | 3 | export class ReportWriter { 4 | constructor(featureElementCreator) { 5 | this.featureElementCreator = featureElementCreator; 6 | } 7 | 8 | getReport(scenariosByFeature) { 9 | const report = document.createDocumentFragment(); 10 | for (const feature in scenariosByFeature.features) { 11 | const featureElement = this.featureElementCreator.createFeatureElement(scenariosByFeature.features[feature]); 12 | report.appendChild(featureElement); 13 | } 14 | if (scenariosByFeature.noFeatureScenarios.scenarios.length) { 15 | const featureElement = this.featureElementCreator.createFeatureElement(scenariosByFeature.noFeatureScenarios); 16 | report.appendChild(featureElement); 17 | } 18 | return report; 19 | } 20 | 21 | } 22 | 23 | export function getReportWriter() { 24 | return new ReportWriter(getFeatureElementCreator()); 25 | } -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/js/ReportWriter/ScenarioElementCreator.js: -------------------------------------------------------------------------------- 1 | import { getReportableElementCreator } from './ReportableElementCreator'; 2 | import { setContentOrRemove } from './setContentOrRemove'; 3 | import { getElementById } from '../getElementById'; 4 | 5 | export class ScenarioElementCreator { 6 | constructor(scenarioTemplate, reportableElementCreator) { 7 | this.scenarioTemplate = scenarioTemplate; 8 | this.reportableElementCreator = reportableElementCreator; 9 | } 10 | 11 | createScenarioElement(scenario) { 12 | const scenarioElement = this.scenarioTemplate.content.cloneNode(true); 13 | 14 | scenarioElement.querySelector('.scenarioName').textContent = scenario.scenario.Name; 15 | scenarioElement.querySelector('.scenarioName').addEventListener('click', ev => ev.currentTarget.parentElement.classList.toggle('collapsed')); 16 | 17 | setContentOrRemove(scenarioElement, scenario.scenario, '.scenarioIdentifier', s => !s.WasIdentifierAutoGenerated, s => s.Identifier); 18 | 19 | const reportablesElement = scenarioElement.querySelector('.reportableList'); 20 | for (const reportable of scenario.performance.Reportables) { 21 | const reportableElement = this.reportableElementCreator.createReportableElement(reportable); 22 | reportablesElement.appendChild(reportableElement); 23 | } 24 | 25 | return scenarioElement; 26 | } 27 | } 28 | 29 | export function getScenarioElementCreator() { 30 | return new ScenarioElementCreator(getElementById('scenarioTemplate'), getReportableElementCreator()); 31 | } -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/js/ReportWriter/index.js: -------------------------------------------------------------------------------- 1 | export { getReportWriter, ReportWriter } from "./ReportWriter"; -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/js/ReportWriter/setContentOrRemove.js: -------------------------------------------------------------------------------- 1 | export function setContentOrRemove(baseElement, dataContext, elementEelector, predicate, contentSelector) { 2 | const element = baseElement.querySelector(elementEelector); 3 | if(predicate(dataContext)) element.textContent = contentSelector(dataContext); 4 | else element.remove(); 5 | } -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/js/activatePage.js: -------------------------------------------------------------------------------- 1 | import { getElementById } from "./getElementById"; 2 | 3 | const summaryElementId = 'summary', featuresElementId = 'featureList'; 4 | 5 | export const activatePage = (summaryTable, featureList) => { 6 | const summaryElement = getElementById(summaryElementId); 7 | summaryElement.appendChild(summaryTable); 8 | 9 | const featuresElement = getElementById(featuresElementId); 10 | featuresElement.appendChild(featureList); 11 | 12 | hideSpinner(); 13 | showFeatures(); 14 | }; 15 | 16 | const hideSpinner = () => getElementById("pageMask").classList.add("hidden"); 17 | const showFeatures = () => getElementById("features").classList.remove("hidden"); 18 | -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/js/activatePage.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment jsdom 3 | */ 4 | 5 | import { activatePage } from "./activatePage"; 6 | import { getElementById } from './getElementById'; 7 | 8 | jest.mock('./getElementById'); 9 | 10 | test('activatePage should append the summary table and feature list to the specified elements', () => { 11 | const summaryTable = document.createElement("table"); 12 | const featureList = document.createElement("ul"); 13 | const summaryElement = document.createElement("div"); 14 | const featuresElement = document.createElement("div"); 15 | const pageMask = document.createElement("div"); 16 | const features = document.createElement("div"); 17 | 18 | getElementById.mockImplementation((id) => { 19 | switch (id) { 20 | case "summary": 21 | return summaryElement; 22 | case "featureList": 23 | return featuresElement; 24 | case "pageMask": 25 | return pageMask; 26 | case "features": 27 | return features; 28 | } 29 | }); 30 | 31 | activatePage(summaryTable, featureList); 32 | 33 | expect(summaryElement.children).toContain(summaryTable); 34 | expect(featuresElement.children).toContain(featureList); 35 | expect(pageMask.classList).toContain("hidden"); 36 | expect(features.classList).not.toContain("hidden"); 37 | }); -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/js/getElementById.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Optimisation for `document.getElementById`. This function call may be minified and reduced to a single character. The original function cannot. 3 | * 4 | * @param {string} id 5 | * @returns HTMLElement 6 | */ 7 | export function getElementById(id) { 8 | return document.getElementById(id); 9 | } -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/js/updateReportTime.js: -------------------------------------------------------------------------------- 1 | import { getElementById } from './getElementById'; 2 | 3 | export const updateReportTime = (timestampString) => { 4 | const date = new Date(Date.parse(timestampString)); 5 | const element = getElementById("reportGeneratedOn"); 6 | element.innerText = date.toLocaleString(); 7 | } -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/js/updateReportTime.spec.js: -------------------------------------------------------------------------------- 1 | import { updateReportTime } from './updateReportTime'; 2 | import { getElementById } from './getElementById'; 3 | 4 | jest.mock('./getElementById'); 5 | 6 | test('updateReportTime should set the element content to the specified timestamp', () => { 7 | const timestampString = "2020-01-01T00:00:00Z"; 8 | const date = new Date(Date.parse(timestampString)); 9 | const element = { innerText: "" }; 10 | getElementById.mockReturnValue(element); 11 | 12 | updateReportTime(timestampString); 13 | 14 | expect(element.innerText).toBe(date.toLocaleString()); 15 | }); -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "screenplayreportviewer", 3 | "version": "1.0.0", 4 | "description": "Interactive viewer for Screenplay reports", 5 | "scripts": { 6 | "buildDev": "npx webpack --mode development", 7 | "buildProd": "npx webpack --mode production", 8 | "test": "jest --config jest.config.js --json --outputFile=TestResults/test-results.json" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/csf-dev/CSF.Screenplay.git" 13 | }, 14 | "author": "Craig Fowler", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/csf-dev/CSF.Screenplay/issues" 18 | }, 19 | "homepage": "https://csf-dev.github.io/CSF.Screenplay/", 20 | "devDependencies": { 21 | "@babel/core": "^7.26.0", 22 | "@babel/preset-env": "^7.26.0", 23 | "babel-jest": "^29.7.0", 24 | "css-loader": "^7.1.2", 25 | "css-minimizer-webpack-plugin": "^7.0.0", 26 | "html-inline-css-webpack-plugin": "^1.11.2", 27 | "html-inline-script-webpack-plugin": "^3.2.1", 28 | "html-webpack-plugin": "^5.6.0", 29 | "jest": "^29.7.0", 30 | "jest-environment-jsdom": "^29.7.0", 31 | "mini-css-extract-plugin": "^2.9.1", 32 | "webpack": "^5.95.0", 33 | "webpack-cli": "^5.1.4" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport.Template/src/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | const HtmlInlineScriptPlugin = require('html-inline-script-webpack-plugin'); 3 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 4 | const HTMLInlineCSSWebpackPlugin = require("html-inline-css-webpack-plugin").default; 5 | const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); 6 | const path = require('path'); 7 | 8 | const res = (p) => path.resolve(__dirname, p); 9 | 10 | module.exports = { 11 | entry: './index.js', 12 | mode: 'development', 13 | output: { 14 | path: res('./output'), 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.css$/, 20 | use: [ 21 | MiniCssExtractPlugin.loader, 22 | "css-loader" 23 | ] 24 | } 25 | ] 26 | }, 27 | optimization: { 28 | minimizer: [ 29 | `...`, 30 | new CssMinimizerPlugin() 31 | ] 32 | }, 33 | plugins: [ 34 | new HtmlWebpackPlugin({ 35 | template: './template.html', 36 | filename: 'template.html', 37 | }), 38 | new HtmlInlineScriptPlugin(), 39 | new MiniCssExtractPlugin({ 40 | filename: "[name].css", 41 | chunkFilename: "[id].css" 42 | }), 43 | new HTMLInlineCSSWebpackPlugin() 44 | ] 45 | }; -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport/CSF.Screenplay.JsonToHtmlReport.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1;net462;netstandard2.0;net6.0;net8.0 5 | Exe 6 | Library 7 | NU1903,NU1902 8 | CSF.Screenplay.JsonToHtmlReport 9 | $(MSBuildProjectDirectory)\bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | false 20 | 21 | 22 | 23 | 24 | 25 | template.html 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport/IConvertsReportJsonToHtml.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace CSF.Screenplay.JsonToHtmlReport 4 | { 5 | /// 6 | /// An object which can convert a JSON Screenplay report to an HTML format. 7 | /// 8 | public interface IConvertsReportJsonToHtml 9 | { 10 | /// 11 | /// Converts the JSON Screenplay report data to HTML asynchronously. 12 | /// 13 | /// The options for the report conversion. 14 | /// A task that represents the asynchronous operation. 15 | Task ConvertAsync(ReportConverterOptions options); 16 | } 17 | } -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport/IGetsHtmlTemplate.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace CSF.Screenplay.JsonToHtmlReport 4 | { 5 | /// 6 | /// Provides functionality to read the HTML template. 7 | /// 8 | public interface IGetsHtmlTemplate 9 | { 10 | /// 11 | /// Reads the HTML template as a string. 12 | /// 13 | Task ReadTemplate(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport/ReportConverterOptions.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.JsonToHtmlReport 2 | { 3 | /// 4 | /// Options for converting a JSON report to HTML. 5 | /// 6 | public class ReportConverterOptions 7 | { 8 | /// 9 | /// Gets or sets the file system path to the JSON report which is to be converted to HTML. 10 | /// 11 | public string ReportPath { get; set; } 12 | 13 | /// 14 | /// Gets or sets the file system path where the HTML report will be saved. 15 | /// 16 | public string OutputPath { get; set; } = "ScreenplayReport.html"; 17 | } 18 | } -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport/ServiceRegistrations.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace CSF.Screenplay.JsonToHtmlReport 4 | { 5 | /// 6 | /// Provides methods to register services for the JsonToHtmlReport application (or library). 7 | /// 8 | /// 9 | /// 10 | /// This type is consumed by the JSON to HTML report converter when it is built as an application, 11 | /// but it may also be used when consuming this project as a library, for integrating it into other solutions. 12 | /// 13 | /// 14 | public static class ServiceRegistrations 15 | { 16 | /// 17 | /// Registers the services required for the JsonToHtmlReport application (or library). 18 | /// 19 | /// The service collection to which the services will be added. 20 | public static void RegisterServices(IServiceCollection services) 21 | { 22 | services.AddTransient(); 23 | services.AddTransient(); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /CSF.Screenplay.JsonToHtmlReport/TemplateReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Threading.Tasks; 5 | 6 | namespace CSF.Screenplay.JsonToHtmlReport 7 | { 8 | /// 9 | /// Provides functionality to read the HTML template which is embedded as a resource into the current assembly. 10 | /// 11 | public class TemplateReader : IGetsHtmlTemplate 12 | { 13 | /// 14 | /// Reads the HTML template which is embedded as a resource into the current assembly. 15 | /// 16 | /// The HTML template as a string. 17 | public async Task ReadTemplate() 18 | { 19 | using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("template.html")) 20 | { 21 | if(stream is null) 22 | throw new InvalidOperationException("The embedded HTML template could not be found; this indicates a serious build error."); 23 | 24 | using (var reader = new StreamReader(stream)) 25 | { 26 | return await reader.ReadToEndAsync().ConfigureAwait(false); 27 | } 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /CSF.Screenplay.NUnit/CSF.Screenplay.NUnit.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;net462 5 | CSF.Screenplay 6 | $(MSBuildProjectDirectory)\bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 7 | false 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /CSF.Screenplay.SpecFlow/CSF.Screenplay.SpecFlow.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;net462 5 | CSF.Screenplay 6 | $(MSBuildProjectDirectory)\bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 7 | false 8 | false 9 | CSF.Screenplay.SpecFlowPlugin 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /CSF.Screenplay.SpecFlow/Properties/RuntimePluginInfo.cs: -------------------------------------------------------------------------------- 1 | using CSF.Screenplay; 2 | using TechTalk.SpecFlow.Plugins; 3 | 4 | [assembly: RuntimePlugin(typeof(ScreenplayPlugin))] 5 | -------------------------------------------------------------------------------- /CSF.Screenplay.SpecFlow/ServiceProviderAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BoDi; 3 | 4 | namespace CSF.Screenplay 5 | { 6 | /// 7 | /// Adapter class which allows a SpecFlow/BoDi IObjectContainer to be used as an . 8 | /// 9 | public class ServiceProviderAdapter : IServiceProvider 10 | { 11 | readonly IObjectContainer wrapped; 12 | 13 | /// 14 | public object GetService(Type serviceType) => wrapped.Resolve(serviceType); 15 | 16 | /// 17 | /// Initialises an instance of . 18 | /// 19 | /// The BoDi object container 20 | /// If is . 21 | public ServiceProviderAdapter(IObjectContainer wrapped) 22 | { 23 | this.wrapped = wrapped ?? throw new ArgumentNullException(nameof(wrapped)); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /CSF.Screenplay.WebApis/CSF.Screenplay.WebApis.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net5.0;netstandard2.0;net462 5 | CSF.Screenplay.WebApis 6 | $(MSBuildProjectDirectory)\bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /CSF.Screenplay.WebApis/HttpResponseMessageAndResponseType.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | 3 | namespace CSF.Screenplay.WebApis 4 | { 5 | /// 6 | /// Wraps an but also provides information about the expected response type 7 | /// from that message. 8 | /// 9 | /// The expected response type. 10 | #pragma warning disable S2326 11 | // TResponse is unused in impl logic; it is used elsewhere for generic type inference, 12 | // as it allows logic to indicate its intention. 13 | public class HttpResponseMessageAndResponseType 14 | #pragma warning restore S2326 15 | { 16 | /// 17 | /// Gets the HTTP response message. 18 | /// 19 | public HttpResponseMessage ResponseMessage { get; } 20 | 21 | /// 22 | /// Initializes a new instance of . 23 | /// 24 | /// The HTTP response message. 25 | /// If is . 26 | public HttpResponseMessageAndResponseType(HttpResponseMessage responseMessage) 27 | { 28 | ResponseMessage = responseMessage ?? throw new System.ArgumentNullException(nameof(responseMessage)); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /CSF.Screenplay.WebApis/Resources/PerformableReportStrings.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | 3 | namespace CSF.Screenplay.WebApis.Resources 4 | { 5 | /// Provides access to localisable string values which relate to performables. 6 | internal static class PerformableReportStrings 7 | { 8 | static readonly ResourceManager resourceManager = new ResourceManager(typeof(PerformableReportStrings).FullName, typeof(PerformableReportStrings).Assembly); 9 | 10 | /// Gets a string which is the report format for . 11 | internal static string SendTheHttpRequestFormat => resourceManager.GetString("SendTheHttpRequestFormat"); 12 | 13 | /// Gets a string which is the report format for . 14 | internal static string SendTheHttpRequestAndGetTheResponseFormat => resourceManager.GetString("SendTheHttpRequestAndGetTheResponseFormat"); 15 | 16 | /// Gets a string which is the report format for . 17 | internal static string SendTheHttpRequestAndGetJsonResponseFormat => resourceManager.GetString("SendTheHttpRequestAndGetJsonResponseFormat"); 18 | 19 | /// Gets a string which is the report format for . 20 | internal static string ReadTheStopwatchFormat => resourceManager.GetString("ReadTheStopwatchFormat"); 21 | } 22 | } -------------------------------------------------------------------------------- /CSF.Screenplay.WebApis/Resources/PerformableReportStrings.restext: -------------------------------------------------------------------------------- 1 | SendTheHttpRequestFormat = {Actor} sends an HTTP {Method} request to {Builder} 2 | SendTheHttpRequestAndGetTheResponseFormat = {Actor} sends an HTTP {Method} request to {Builder}, expecting the response to be {ResponseType} 3 | SendTheHttpRequestAndGetJsonResponseFormat = {Actor} sends a web request and deserializes the JSON response as {ResponseType} 4 | ReadTheStopwatchFormat = {Actor} reads their stopwatch -------------------------------------------------------------------------------- /CSF.Screenplay/CSF.Screenplay.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;net462 5 | CSF.Screenplay 6 | $(MSBuildProjectDirectory)\bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /CSF.Screenplay/Performances/PerformanceFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay.Performances 4 | { 5 | /// A factory service for instances of 6 | public class PerformanceFactory : ICreatesPerformance 7 | { 8 | readonly IServiceProvider services; 9 | 10 | /// 11 | public IPerformance CreatePerformance() => new Performance(services, performanceIdentity: Guid.NewGuid()); 12 | 13 | /// Initialises a new instance of 14 | /// Dependency injection services 15 | /// If is 16 | public PerformanceFactory(IServiceProvider services) 17 | { 18 | this.services = services ?? throw new ArgumentNullException(nameof(services)); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /CSF.Screenplay/ReportModel/ActorCreatedReport.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.ReportModel 2 | { 3 | /// 4 | /// An implementation of which represents the addition/creation of a new actor in a performance. 5 | /// 6 | /// 7 | /// 8 | /// This model exposes nothing more than the already provides. 9 | /// 10 | /// 11 | public class ActorCreatedReport : ReportableModelBase {} 12 | } -------------------------------------------------------------------------------- /CSF.Screenplay/ReportModel/ActorGainedAbilityReport.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.ReportModel 2 | { 3 | /// 4 | /// An implementation of which occurs when an gains a new 5 | /// . 6 | /// 7 | /// 8 | /// 9 | /// This model exposes nothing more than the already provides. 10 | /// 11 | /// 12 | public class ActorGainedAbilityReport : ReportableModelBase {} 13 | } -------------------------------------------------------------------------------- /CSF.Screenplay/ReportModel/ActorSpotlitReport.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.ReportModel 2 | { 3 | /// 4 | /// An implementation of which occurs when an is put into . 5 | /// 6 | /// 7 | /// 8 | /// This model exposes nothing more than the already provides. 9 | /// 10 | /// 11 | public class ActorSpotlitReport : ReportableModelBase {} 12 | } -------------------------------------------------------------------------------- /CSF.Screenplay/ReportModel/IdentifierAndNameModel.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.ReportModel 2 | { 3 | /// 4 | /// Model represents an within a Screenplay report. 5 | /// 6 | /// 7 | /// 8 | /// Like many models in this namespace, this type mimicks a first-class part of the Screenplay architecture. 9 | /// This model type is intended for use with the serialization process to JSON. 10 | /// Many of the properties of these types will correspond directly with the same-named properties on the original 11 | /// Screenplay architecture types. 12 | /// 13 | /// 14 | public class IdentifierAndNameModel 15 | { 16 | /// 17 | /// Corresponds to the value . 18 | /// 19 | public string Identifier { get; set; } 20 | 21 | /// 22 | /// Corresponds to the value . 23 | /// 24 | public string Name { get; set; } 25 | 26 | /// 27 | /// Corresponds to the value . 28 | /// 29 | public bool WasIdentifierAutoGenerated { get; set; } 30 | } 31 | } -------------------------------------------------------------------------------- /CSF.Screenplay/ReportModel/PerformableAsset.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.ReportModel 2 | { 3 | /// 4 | /// Model represents a single asset which was recorded from a performable. 5 | /// 6 | /// 7 | /// 8 | /// Assets are files which are saved to disk, containing arbitrary information, recorded by a performable. 9 | /// This might be a screenshot, some generated content or diagnostic information. Its real content is arbitrary and 10 | /// down to the implementation. 11 | /// An asset is described here by a file path and an optional human-readable summary. 12 | /// 13 | /// 14 | public class PerformableAsset 15 | { 16 | /// 17 | /// Gets or sets a full/absolute path to the asset file. 18 | /// 19 | public string FilePath { get; set; } 20 | 21 | /// 22 | /// Gets or sets an optional human-readable summary of what this asset represents. This should be one sentence at most, suitable 23 | /// for display in a UI tool-tip. 24 | /// 25 | public string FileSummary { get; set; } 26 | 27 | } 28 | } -------------------------------------------------------------------------------- /CSF.Screenplay/ReportModel/ReportMetadata.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSF.Screenplay.ReportModel 4 | { 5 | /// 6 | /// Model represents the metadata about a Screenplay report. 7 | /// 8 | public class ReportMetadata 9 | { 10 | const string reportFormatVersion = "2.0.0"; 11 | 12 | /// 13 | /// Gets or sets the UTC timestamp at which the report was generated. 14 | /// 15 | public DateTime Timestamp { get; set; } = DateTime.UtcNow; 16 | 17 | /// 18 | /// Gets or sets a version number for the format of report that has been produced. 19 | /// 20 | /// 21 | /// 22 | /// This version number is intended to comply with Semantic versioning: . 23 | /// It may be used to assist parsing logic determine whether or not it is reading a compatible report file. 24 | /// 25 | /// 26 | public string ReportFormatVersion { get; set; } = reportFormatVersion; 27 | } 28 | } -------------------------------------------------------------------------------- /CSF.Screenplay/ReportModel/ReportableModelBase.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace CSF.Screenplay.ReportModel 4 | { 5 | /// 6 | /// Model represents anything which may be reported-upon within an . 7 | /// 8 | /// 9 | /// 10 | /// This base model has subclasses for each of the specific types of event which may be reported-upon. 11 | /// 12 | /// 13 | [JsonPolymorphic(TypeDiscriminatorPropertyName = "Type")] 14 | [JsonDerivedType(typeof(ActorCreatedReport), nameof(ActorCreatedReport))] 15 | [JsonDerivedType(typeof(ActorGainedAbilityReport), nameof(ActorGainedAbilityReport))] 16 | [JsonDerivedType(typeof(ActorSpotlitReport), nameof(ActorSpotlitReport))] 17 | [JsonDerivedType(typeof(SpotlightTurnedOffReport), nameof(SpotlightTurnedOffReport))] 18 | [JsonDerivedType(typeof(PerformableReport), nameof(PerformableReport))] 19 | public abstract class ReportableModelBase 20 | { 21 | /// 22 | /// Gets or sets the human-readable text of the report. 23 | /// 24 | public string Report { get; set; } 25 | 26 | /// 27 | /// Gets or sets the name of the who is associated with this report. 28 | /// 29 | /// 30 | /// 31 | /// Almost all reportables involve an actor, it is rare for this value to be unset (IE: ). 32 | /// 33 | /// 34 | public string ActorName { get; set; } 35 | } 36 | } -------------------------------------------------------------------------------- /CSF.Screenplay/ReportModel/ScreenplayReport.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CSF.Screenplay.ReportModel 4 | { 5 | /// 6 | /// Represents a complete Screenplay report. 7 | /// 8 | /// 9 | /// 10 | /// This type is not used in the writing of Screenplay reports; the writes the report by streaming 11 | /// it to a file as it is generated. This type is used only for the deserialization of a report file. 12 | /// 13 | /// 14 | public class ScreenplayReport 15 | { 16 | /// 17 | /// Gets or sets the metadata for the report. 18 | /// 19 | public ReportMetadata Metadata { get; set; } = new ReportMetadata(); 20 | 21 | /// 22 | /// Gets or sets the performances which are included within the report. 23 | /// 24 | /// 25 | /// 26 | /// There is no defined order to the performances within the report. The order in which they appear in this collection will correspond to the 27 | /// order in which they were serialized to the report file. However, because performances may occur in parallel, there is no guarantee that 28 | /// the order will be stable between different reports, even if the and instances contained 29 | /// are the same, with the same logic. 30 | /// 31 | /// 32 | public ICollection Performances { get; set; } = new List(); 33 | } 34 | } -------------------------------------------------------------------------------- /CSF.Screenplay/ReportModel/SpotlightTurnedOffReport.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.ReportModel 2 | { 3 | /// 4 | /// An implementation of which occurs when is turned off. 5 | /// 6 | /// 7 | /// 8 | /// This model exposes nothing more than the already provides. 9 | /// 10 | /// 11 | public class SpotlightTurnedOffReport : ReportableModelBase {} 12 | } -------------------------------------------------------------------------------- /CSF.Screenplay/Reporting/FormattableFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Reporting 2 | { 3 | /// 4 | /// Implementation of which formats objects that implement . 5 | /// 6 | public class FormattableFormatter : IValueFormatter 7 | { 8 | /// 9 | public bool CanFormat(object value) => value is IFormattableValue; 10 | 11 | /// 12 | public string FormatForReport(object value) => ((IFormattableValue)value).FormatForReport(); 13 | } 14 | } -------------------------------------------------------------------------------- /CSF.Screenplay/Reporting/IDeserializesReport.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading.Tasks; 3 | using CSF.Screenplay.ReportModel; 4 | 5 | namespace CSF.Screenplay.Reporting 6 | { 7 | /// 8 | /// An object which deserializes a Screenplay report from a stream. 9 | /// 10 | public interface IDeserializesReport 11 | { 12 | /// 13 | /// Deserializes a Screenplay report from the provided stream asynchronously. 14 | /// 15 | /// The stream containing the serialized Screenplay report. 16 | /// A task that represents the asynchronous operation. The task result contains the deserialized Screenplay report. 17 | Task DeserializeAsync(Stream stream); 18 | } 19 | } -------------------------------------------------------------------------------- /CSF.Screenplay/Reporting/ITestsPathForWritePermissions.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Reporting 2 | { 3 | /// 4 | /// An object which may test a file system path for writability. 5 | /// 6 | public interface ITestsPathForWritePermissions 7 | { 8 | /// 9 | /// Gets a value indicating whether or not the current process should be able to write a file at the specified path. 10 | /// 11 | /// 12 | /// 13 | /// The path may be relative or absolute; if relative then it is treated as relative to the current working directory. 14 | /// 15 | /// 16 | /// An absolute or relative file path. 17 | /// if the current process is able to write to the specified path; if not. 18 | bool HasWritePermission(string path); 19 | } 20 | } -------------------------------------------------------------------------------- /CSF.Screenplay/Reporting/JsonScreenplayReportReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text.Json; 4 | using System.Threading.Tasks; 5 | using CSF.Screenplay.ReportModel; 6 | 7 | namespace CSF.Screenplay.Reporting 8 | { 9 | 10 | /// 11 | /// Implementation of that deserializes a Screenplay report from a JSON stream. 12 | /// 13 | public class JsonScreenplayReportReader : IDeserializesReport 14 | { 15 | /// 16 | public async Task DeserializeAsync(Stream stream) 17 | { 18 | if (stream == null) 19 | throw new ArgumentNullException(nameof(stream)); 20 | 21 | return await JsonSerializer.DeserializeAsync(stream); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /CSF.Screenplay/Reporting/NameFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Reporting 2 | { 3 | /// 4 | /// Implementation of which formats objects that implement . 5 | /// 6 | public class NameFormatter : IValueFormatter 7 | { 8 | /// 9 | public bool CanFormat(object value) => value is IHasName; 10 | 11 | /// 12 | public string FormatForReport(object value) => ((IHasName)value).Name; 13 | } 14 | } -------------------------------------------------------------------------------- /CSF.Screenplay/Reporting/NoOpReporter.cs: -------------------------------------------------------------------------------- 1 | using CSF.Screenplay.Performances; 2 | 3 | namespace CSF.Screenplay.Reporting 4 | { 5 | /// 6 | /// A no-op implementation of which does nothing. 7 | /// 8 | public sealed class NoOpReporter : IReporter 9 | { 10 | /// 11 | public void SubscribeTo(IHasPerformanceEvents events) {} 12 | 13 | /// 14 | public void UnsubscribeFrom(IHasPerformanceEvents events) {} 15 | 16 | /// 17 | public void Dispose() {} 18 | } 19 | } -------------------------------------------------------------------------------- /CSF.Screenplay/Reporting/ToStringFormatter.cs: -------------------------------------------------------------------------------- 1 | using CSF.Screenplay.Resources; 2 | 3 | namespace CSF.Screenplay.Reporting 4 | { 5 | /// 6 | /// Implementation of which formats any object by using its default method, 7 | /// or returns the string <null> if the value is . 8 | /// 9 | /// 10 | /// 11 | /// This formatter should be used as a default/last resort. It is very likely that this could produce results which are not particularly human readable. 12 | /// 13 | /// 14 | public class ToStringFormatter : IValueFormatter 15 | { 16 | /// 17 | public bool CanFormat(object value) => true; 18 | 19 | /// 20 | public string FormatForReport(object value) => value is null ? FormatterStrings.NullValue : value.ToString(); 21 | } 22 | } -------------------------------------------------------------------------------- /CSF.Screenplay/Resources/FormatterStrings.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | 3 | namespace CSF.Screenplay.Resources 4 | { 5 | /// Provides access to localisable string values which relate to formatting. 6 | internal static class FormatterStrings 7 | { 8 | static readonly ResourceManager resourceManager = new ResourceManager(typeof(FormatterStrings).FullName, typeof(FormatterStrings).Assembly); 9 | 10 | /// Gets a string which is used to indicate a value. 11 | internal static string NullValue => resourceManager.GetString("NullValue"); 12 | } 13 | } -------------------------------------------------------------------------------- /CSF.Screenplay/Resources/FormatterStrings.restext: -------------------------------------------------------------------------------- 1 | NullValue = -------------------------------------------------------------------------------- /CSF.Screenplay/Resources/ReportStrings.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | 3 | namespace CSF.Screenplay.Resources 4 | { 5 | /// Provides access to localisable string values which relate to formatting. 6 | internal static class ReportStrings 7 | { 8 | static readonly ResourceManager resourceManager = new ResourceManager(typeof(ReportStrings).FullName, typeof(ReportStrings).Assembly); 9 | 10 | /// Gets a format string which indicates a new actor has been created and joined the performance. 11 | internal static string ActorCreatedFormat => resourceManager.GetString("ActorCreatedFormat"); 12 | 13 | /// Gets a format string which indicates that an actor has gained an ability. 14 | internal static string ActorGainedAbilityFormat => resourceManager.GetString("ActorGainedAbilityFormat"); 15 | 16 | /// Gets a format string which indicates that an actor has been placed into the spotlight. 17 | internal static string ActorSpotlitFormat => resourceManager.GetString("ActorSpotlitFormat"); 18 | 19 | /// Gets a string which indicates that the spotlight has been turned off. 20 | internal static string SpotlightTurnedOff => resourceManager.GetString("SpotlightTurnedOff"); 21 | 22 | /// Gets a fallback report format string for an actor performing a performable which does not implement . 23 | internal static string FallbackReportFormat => resourceManager.GetString("FallbackReportFormat"); 24 | } 25 | } -------------------------------------------------------------------------------- /CSF.Screenplay/Resources/ReportStrings.restext: -------------------------------------------------------------------------------- 1 | ActorCreatedFormat = {0} joined the performance 2 | ActorGainedAbilityFormat = {0} is able to {1} 3 | ActorSpotlitFormat = {0} was put into the spotlight 4 | SpotlightTurnedOff = The spotlight was turned off 5 | FallbackReportFormat = {0} performed {1} -------------------------------------------------------------------------------- /CSF.Screenplay/ScopeAndPerformance.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace CSF.Screenplay 5 | { 6 | /// 7 | /// A model which contains both an and a dependency injection . 8 | /// 9 | public sealed class ScopeAndPerformance : IDisposable 10 | { 11 | /// 12 | /// Gets the performance. 13 | /// 14 | public IPerformance Performance { get; } 15 | 16 | /// 17 | /// Gets the DI scope. 18 | /// 19 | public IServiceScope Scope { get; } 20 | 21 | /// 22 | public void Dispose() => Scope.Dispose(); 23 | 24 | /// 25 | /// Initialises a new instance of . 26 | /// 27 | /// The performance 28 | /// The scope 29 | /// If any parameter is . 30 | public ScopeAndPerformance(IPerformance performance, IServiceScope scope) 31 | { 32 | Performance = performance ?? throw new ArgumentNullException(nameof(performance)); 33 | Scope = scope ?? throw new ArgumentNullException(nameof(scope)); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 CSF Software Limited 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSF.Screenplay 2 | 3 | Screenplay is a *software design pattern* to assist in the automation of complex processes. 4 | It is commonly recommended for use in writing tests using the [Behaviour Driven Development] (BDD) style. 5 | **CSF.Screenplay** is a library and framework for using this design pattern. 6 | 7 | [Behaviour Driven Development]: https://en.wikipedia.org/wiki/Behavior-driven_development 8 | 9 | ## More information 10 | 11 | Detailed information about Screenplay & how it is used is [available on the documentation website]. 12 | 13 | [available on the documentation website]: https://csf-dev.github.io/CSF.Screenplay/ 14 | 15 | ## Continuous integration status 16 | 17 | CI builds are configured via **AppVeyor**, with static code analysis & reporting in **SonarCloud**. 18 | 19 | [![AppVeyor status](https://ci.appveyor.com/api/projects/status/y9ejfko3kflosava?svg=true)](https://ci.appveyor.com/project/craigfowler/csf-screenplay) 20 | [![Test coverage](https://sonarcloud.io/api/project_badges/measure?project=csf-dev_CSF.Screenplay&metric=coverage)](https://sonarcloud.io/summary/new_code?id=csf-dev_CSF.Screenplay) 21 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.NUnit.Tests/CSF.Screenplay.NUnit.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | false 7 | true 8 | CSF.Screenplay 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.NUnit.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using NUnit.Framework; 2 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.NUnit.Tests/Properties/ScreenplayAttributes.cs: -------------------------------------------------------------------------------- 1 | using CSF.Screenplay; 2 | 3 | [assembly: ScreenplayAssembly(typeof(ScreenplayFactory))] 4 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.NUnit.Tests/ScreenplayFactory.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay; 2 | 3 | public class ScreenplayFactory : IGetsScreenplay 4 | { 5 | public Screenplay GetScreenplay() => Screenplay.Create(); 6 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.NUnit.Tests/TestWithoutDescription.cs: -------------------------------------------------------------------------------- 1 | using CSF.Screenplay.Performances; 2 | 3 | namespace CSF.Screenplay; 4 | 5 | [TestFixture,Parallelizable] 6 | public class TestWithoutDescription 7 | { 8 | [Test,Screenplay] 9 | public void FirstTest(IPerformance performance) 10 | { 11 | IdentifierAndName[] expectedNamingHierarchy = [ 12 | new(typeof(TestWithoutDescription).FullName), 13 | new($"{typeof(TestWithoutDescription).FullName}.{nameof(FirstTest)}"), 14 | ]; 15 | 16 | AssertThatPerformanceHasCorrectState(performance, expectedNamingHierarchy); 17 | } 18 | 19 | [Test,Screenplay] 20 | public void SecondTest(IPerformance performance) 21 | { 22 | IdentifierAndName[] expectedNamingHierarchy = [ 23 | new(typeof(TestWithoutDescription).FullName), 24 | new($"{typeof(TestWithoutDescription).FullName}.{nameof(SecondTest)}"), 25 | ]; 26 | 27 | AssertThatPerformanceHasCorrectState(performance, expectedNamingHierarchy); 28 | } 29 | 30 | static void AssertThatPerformanceHasCorrectState(IPerformance performance, IdentifierAndName[] expectedNamingHierarchy) 31 | { 32 | Assert.Multiple(() => 33 | { 34 | Assert.That(performance, Is.Not.Null, "Performance must not be null"); 35 | Assert.That(performance.NamingHierarchy, Is.EqualTo(expectedNamingHierarchy), "Performance naming hierarchy is correct"); 36 | }); 37 | } 38 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.SpecFlow.Tests/AddingUp.feature: -------------------------------------------------------------------------------- 1 | Feature: Adding up numbers 2 | 3 | As a math idiot, I want to be able to add numbers to a running total, to test the Screenplay/SpecFlow integration 4 | 5 | Scenario: Mathias should be able to add two numbers together and get the correct result 6 | Given Mathias has the number 50 7 | When he adds 70 8 | Then he should have the total 120 9 | 10 | Scenario: Mathias should be able to add three numbers together and get the correct result 11 | Given Mathias has the number 50 12 | When he adds 70 13 | And he adds 20 14 | Then he should have the total 140 15 | 16 | Scenario: Mathias should be able to add three numbers in a single step 17 | Given Mathias has the number 50 18 | When he adds 40, 30 and 20 19 | Then he should have the total 140 -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.SpecFlow.Tests/AddingUp/AddNumbers.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.AddingUp; 2 | 3 | public class AddNumbers : ICanReport 4 | { 5 | int currentNumber; 6 | 7 | public ReportFragment GetReportFragment(IHasName actor, IFormatsReportFragment formatter) 8 | => formatter.Format("{Actor} is able to add numbers to a running total", actor); 9 | 10 | public void Add(int number) => currentNumber += number; 11 | 12 | public void Set(int number) => currentNumber = number; 13 | 14 | public int Get() => currentNumber; 15 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.SpecFlow.Tests/AddingUp/AddTheNumber.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.AddingUp; 2 | 3 | public class AddTheNumber(int number) : IPerformable, ICanReport 4 | { 5 | public ReportFragment GetReportFragment(IHasName actor, IFormatsReportFragment formatter) 6 | => formatter.Format("{Actor} adds {Aumber} to the running total", actor, number); 7 | 8 | public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default) 9 | { 10 | var ability = actor.GetAbility(); 11 | ability.Add(number); 12 | return default; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.SpecFlow.Tests/AddingUp/AddThreeNumbers.cs: -------------------------------------------------------------------------------- 1 | using static CSF.Screenplay.AddingUp.AddingUpBuilder; 2 | 3 | namespace CSF.Screenplay.AddingUp 4 | { 5 | public class AddThreeNumbers(int number1, int number2, int number3) : IPerformable, ICanReport 6 | { 7 | public ReportFragment GetReportFragment(IHasName actor, IFormatsReportFragment formatter) 8 | => formatter.Format("{Actor} adds three numbers to the running total: {One}, {Two} & {Three}", actor, number1, number2, number3); 9 | 10 | public async ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default) 11 | { 12 | await actor.PerformAsync(Add(number1), cancellationToken); 13 | await actor.PerformAsync(Add(number2), cancellationToken); 14 | await actor.PerformAsync(Add(number3), cancellationToken); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.SpecFlow.Tests/AddingUp/AddingUpBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.AddingUp; 2 | 3 | public static class AddingUpBuilder 4 | { 5 | public static IPerformable Add(int number) => new AddTheNumber(number); 6 | public static IPerformable AddThreeNumbers(int number1, int number2, int number3) => new AddThreeNumbers(number1, number2, number3); 7 | public static IPerformable SetTheTotalTo(int number) => new SetTheNumber(number); 8 | public static IPerformableWithResult GetTheTotal() => new GetTheNumber(); 9 | } 10 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.SpecFlow.Tests/AddingUp/GetTheNumber.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.AddingUp; 2 | 3 | public class GetTheNumber() : IPerformableWithResult, ICanReport 4 | { 5 | public ReportFragment GetReportFragment(IHasName actor, IFormatsReportFragment formatter) 6 | => formatter.Format("{Actor} gets the running total", actor); 7 | 8 | ValueTask IPerformableWithResult.PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken) 9 | { 10 | var ability = actor.GetAbility(); 11 | return ValueTask.FromResult(ability.Get()); 12 | } 13 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.SpecFlow.Tests/AddingUp/SetTheNumber.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.AddingUp; 2 | 3 | public class SetTheNumber(int number) : IPerformable, ICanReport 4 | { 5 | public ReportFragment GetReportFragment(IHasName actor, IFormatsReportFragment formatter) 6 | => formatter.Format("{Actor} sets the running total to {Number}", actor, number); 7 | 8 | public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default) 9 | { 10 | var ability = actor.GetAbility(); 11 | ability.Set(number); 12 | return default; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.SpecFlow.Tests/CSF.Screenplay.SpecFlow.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | false 7 | true 8 | CSF.Screenplay 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.SpecFlow.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using NUnit.Framework; 2 | global using System.Threading; 3 | global using System.Threading.Tasks; 4 | global using TechTalk.SpecFlow; 5 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.SpecFlow.Tests/Mathias.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CSF.Screenplay.AddingUp; 3 | 4 | namespace CSF.Screenplay; 5 | 6 | public class Mathias : IPersona 7 | { 8 | public string Name => "Mathias"; 9 | 10 | public Actor GetActor(Guid performanceIdentity) 11 | { 12 | var mathias = new Actor(Name, performanceIdentity); 13 | mathias.IsAbleTo(); 14 | return mathias; 15 | } 16 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.SpecFlow.Tests/StepDefinitions/AddingUpSteps.cs: -------------------------------------------------------------------------------- 1 | using static CSF.Screenplay.PerformanceStarter; 2 | using static CSF.Screenplay.AddingUp.AddingUpBuilder; 3 | 4 | namespace CSF.Screenplay.StepDefinitions 5 | { 6 | [Binding] 7 | public class StepDefinitions(IStage stage) 8 | { 9 | [Given(@"Mathias has the number (\d+)")] 10 | public async Task GivenMathiashasthenumber(int number) 11 | { 12 | var mathias = stage.Spotlight(); 13 | await Given(mathias).WasAbleTo(SetTheTotalTo(number)); 14 | } 15 | 16 | [When(@"(?:he|she|they) adds? (\d+)")] 17 | public async Task Whenheadds(int number) 18 | { 19 | var actor = stage.GetSpotlitActor(); 20 | await When(actor).AttemptsTo(Add(number)); 21 | } 22 | 23 | [When(@"(?:he|she|they) adds? (\d+), (\d+) and (\d+)")] 24 | public async Task Whenheaddsthreenumbers(int number1, int number2, int number3) 25 | { 26 | var actor = stage.GetSpotlitActor(); 27 | await When(actor).AttemptsTo(AddThreeNumbers(number1, number2, number3)); 28 | } 29 | 30 | [Then(@"(?:he|she|they) should have the total (\d+)")] 31 | public async Task Thenheshouldhavethetotal(int number) 32 | { 33 | var actor = stage.GetSpotlitActor(); 34 | var total = await Then(actor).Should(GetTheTotal()); 35 | 36 | Assert.That(total, Is.EqualTo(number)); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/Actors/NamedActorAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using AutoFixture; 3 | 4 | namespace CSF.Screenplay.Actors; 5 | 6 | public class NamedActorAttribute(string name) : CustomizeAttribute 7 | { 8 | readonly string name = name ?? throw new ArgumentNullException(nameof(name)); 9 | 10 | public override ICustomization GetCustomization(ParameterInfo parameter) => new NamedActorCustomization(name); 11 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/Actors/NamedActorCustomization.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | 3 | namespace CSF.Screenplay.Actors; 4 | 5 | public class NamedActorCustomization(string name) : ICustomization 6 | { 7 | readonly string name = name ?? throw new ArgumentNullException(nameof(name)); 8 | 9 | public void Customize(IFixture fixture) 10 | { 11 | fixture.Customize(c => c.FromFactory((Guid guid) => new Actor(name, guid))); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/AutoMoqDataAttribute.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using AutoFixture.AutoMoq; 3 | 4 | namespace CSF.Screenplay; 5 | 6 | /// 7 | /// Sets up a test to use Autofixture & Moq together, via . 8 | /// 9 | public class AutoMoqDataAttribute : AutoDataAttribute 10 | { 11 | public AutoMoqDataAttribute() : base(() => new Fixture().Customize(new AutoMoqCustomization())) {} 12 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/AutofixtureServicesAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using AutoFixture; 3 | 4 | namespace CSF.Screenplay; 5 | 6 | /// 7 | /// AutoFixture customization attribute which creates an that 8 | /// gets services from AutoFixture. 9 | /// 10 | public class AutofixtureServicesAttribute : CustomizeAttribute 11 | { 12 | public override ICustomization GetCustomization(ParameterInfo parameter) 13 | => new AutofixtureServicesCustomization(); 14 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/AutofixtureServicesCustomization.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using AutoFixture.Kernel; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace CSF.Screenplay; 6 | 7 | public class AutofixtureServicesCustomization : ICustomization 8 | { 9 | public void Customize(IFixture fixture) 10 | { 11 | fixture.Customize(c => c.FromFactory(new ServiceProviderBuilder())); 12 | fixture.Customize(c => c.FromFactory((IServiceProvider s) => Mock.Of(x => x.ServiceProvider == s))); 13 | } 14 | 15 | class ServiceProviderBuilder : ISpecimenBuilder 16 | { 17 | public object Create(object request, ISpecimenContext context) 18 | { 19 | var serviceProvider = new Mock(); 20 | serviceProvider 21 | .Setup(x => x.GetService(It.IsAny())) 22 | .Returns((Type t) => context.Resolve(t)); 23 | serviceProvider.As() 24 | .Setup(x => x.GetRequiredService(It.IsAny())) 25 | .Returns((Type t) => context.Resolve(t)); 26 | return serviceProvider.Object; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/CSF.Screenplay.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | false 7 | true 8 | CSF.Screenplay 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/DefaultScreenplayAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using AutoFixture; 3 | 4 | namespace CSF.Screenplay; 5 | 6 | public class DefaultScreenplayAttribute : CustomizeAttribute 7 | { 8 | public override ICustomization GetCustomization(ParameterInfo parameter) 9 | => new DefaultScreenplayCustomization(); 10 | } 11 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/DefaultScreenplayCustomization.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | 3 | namespace CSF.Screenplay; 4 | 5 | public class DefaultScreenplayCustomization : ICustomization 6 | { 7 | public void Customize(IFixture fixture) 8 | { 9 | fixture.Customize(c => c.FromFactory(() => Screenplay.Create(options: o => o.ReportPath = null))); 10 | } 11 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using NUnit.Framework; 2 | global using AutoFixture.NUnit3; 3 | global using Moq; 4 | global using System; 5 | global using System.Threading; 6 | global using System.Threading.Tasks; -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/JsonToHtmlReport/TemplateReaderTests.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.JsonToHtmlReport 2 | { 3 | [TestFixture,Parallelizable] 4 | public class TemplateReaderTests 5 | { 6 | [Test, AutoMoqData] 7 | public async Task ReadTemplateShouldReturnAString(TemplateReader sut) 8 | { 9 | var result = await sut.ReadTemplate(); 10 | 11 | Assert.Multiple(() => 12 | { 13 | Assert.That(result, Is.Not.Null, "Not null"); 14 | Assert.That(result, Is.Not.Empty, "Not empty"); 15 | }); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/Reporting/FormattableFormatterTests.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Reporting; 2 | 3 | [TestFixture,Parallelizable] 4 | public class FormattableFormatterTests 5 | { 6 | [Test,AutoMoqData] 7 | public void CanFormatShouldReturnFalseForAnObjectWhichCannotBeFormatted(FormattableFormatter sut, object value) 8 | { 9 | Assert.That(sut.CanFormat(value), Is.False); 10 | } 11 | 12 | [Test,AutoMoqData] 13 | public void CanFormatShouldReturnTrueForAnObjectWhichCannotBeFormatted(FormattableFormatter sut, IFormattableValue value) 14 | { 15 | Assert.That(sut.CanFormat(value), Is.True); 16 | } 17 | 18 | [Test,AutoMoqData] 19 | public void FormatShouldReturnACorrectlyFormattedString(FormattableFormatter sut, IFormattableValue value, string format) 20 | { 21 | Mock.Get(value).Setup(x => x.FormatForReport()).Returns(format); 22 | Assert.That(sut.FormatForReport(value), Is.EqualTo(format)); 23 | } 24 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/Reporting/NameFormatterTests.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Reporting; 2 | 3 | [TestFixture,Parallelizable] 4 | public class NameFormatterTests 5 | { 6 | [Test,AutoMoqData] 7 | public void CanFormatShouldReturnFalseForAnObjectWhichCannotBeFormatted(NameFormatter sut, object value) 8 | { 9 | Assert.That(sut.CanFormat(value), Is.False); 10 | } 11 | 12 | [Test,AutoMoqData] 13 | public void CanFormatShouldReturnTrueForAnObjectWhichCannotBeFormatted(NameFormatter sut, IHasName value) 14 | { 15 | Assert.That(sut.CanFormat(value), Is.True); 16 | } 17 | 18 | [Test,AutoMoqData] 19 | public void FormatShouldReturnACorrectlyFormattedString(NameFormatter sut, IHasName value, string name) 20 | { 21 | Mock.Get(value).SetupGet(x => x.Name).Returns(name); 22 | Assert.That(sut.FormatForReport(value), Is.EqualTo(name)); 23 | } 24 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/Reporting/ToStringFormatterTests.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Reporting; 2 | 3 | [TestFixture,Parallelizable] 4 | public class ToStringFormatterTests 5 | { 6 | [Test,AutoMoqData] 7 | public void CanFormatShouldReturnTrueForAPlainObject(ToStringFormatter sut, object value) 8 | { 9 | Assert.That(sut.CanFormat(value), Is.True); 10 | } 11 | 12 | [Test,AutoMoqData] 13 | public void CanFormatShouldReturnTrueForNull(ToStringFormatter sut) 14 | { 15 | Assert.That(sut.CanFormat(null), Is.True); 16 | } 17 | 18 | [Test,AutoMoqData] 19 | public void FormatShouldReturnACorrectlyFormattedStringForAnObject(ToStringFormatter sut, object value) 20 | { 21 | Assert.That(sut.FormatForReport(value), Is.EqualTo(typeof(object).FullName)); 22 | } 23 | 24 | [Test,AutoMoqData] 25 | public void FormatShouldReturnACorrectlyFormattedStringForNull(ToStringFormatter sut) 26 | { 27 | Assert.That(sut.FormatForReport(null), Is.EqualTo("")); 28 | } 29 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/Reporting/WithMemoryStreamAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using AutoFixture; 3 | 4 | namespace CSF.Screenplay.Reporting; 5 | 6 | public class WithMemoryStreamAttribute : CustomizeAttribute 7 | { 8 | public override ICustomization GetCustomization(ParameterInfo parameter) => new WithMemoryStreamCustomization(); 9 | } 10 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/Reporting/WithMemoryStreamCustomization.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using AutoFixture; 3 | using AutoFixture.Kernel; 4 | 5 | namespace CSF.Screenplay.Reporting; 6 | 7 | public class WithMemoryStreamCustomization : ICustomization 8 | { 9 | public void Customize(IFixture fixture) 10 | { 11 | fixture.Customizations.Add(new TypeRelay( 12 | typeof(Stream), 13 | typeof(MemoryStream))); 14 | fixture.Customize(c => c.FromFactory(() => new MemoryStream()).OmitAutoProperties()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/Stubs/SampleAction.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Stubs; 2 | 3 | public class SampleAction : IPerformable 4 | { 5 | public string? ActorName { get; private set; } 6 | 7 | public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default) 8 | { 9 | ActorName = ((IHasName) actor).Name; 10 | return ValueTask.CompletedTask; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/Stubs/SampleGenericQuestion.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Stubs; 2 | 3 | public class SampleGenericQuestion : IPerformableWithResult 4 | { 5 | public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default) 6 | { 7 | var actorName = ((IHasName) actor).Name; 8 | return ValueTask.FromResult(actorName); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/Stubs/SampleQuestion.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Stubs; 2 | 3 | public class SampleQuestion : IPerformableWithResult 4 | { 5 | public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default) 6 | { 7 | return ValueTask.FromResult(5); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/Stubs/ThrowingAction.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.Stubs; 2 | 3 | public class ThrowingAction : IPerformable 4 | { 5 | internal const string Message = "This is a sample exception"; 6 | 7 | public ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default) 8 | { 9 | throw new InvalidOperationException(Message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/WebApis/HttpRequestMessageBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | 3 | namespace CSF.Screenplay.WebApis; 4 | 5 | [TestFixture,Parallelizable] 6 | public class HttpRequestMessageBuilderTests 7 | { 8 | [Test, AutoMoqData] 9 | public void IntendedUsageAsImmutableRecordShouldProvideExpectedResults() 10 | { 11 | // This test is more about proving the API and developer experience I want for this type 12 | // does actually work the way I'd like it to work. 13 | 14 | var baseValues = new HttpRequestMessageBuilder 15 | { 16 | RequestUri = new Uri("https://example.com"), 17 | Headers = new() 18 | { 19 | ["Cookie"] = "FooBar", 20 | ["Expires"] = "0", 21 | } 22 | }; 23 | var modified = baseValues with { Headers = baseValues.Headers.WithItem("Content-Type", "text/json"), Method = HttpMethod.Post }; 24 | 25 | Assert.That(modified, Is.EqualTo(new HttpRequestMessageBuilder 26 | { 27 | RequestUri = new Uri("https://example.com"), 28 | Headers = new() 29 | { 30 | ["Cookie"] = "FooBar", 31 | ["Expires"] = "0", 32 | ["Content-Type"] = "text/json", 33 | }, 34 | Method = HttpMethod.Post, 35 | })); 36 | } 37 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/WebApis/SendTheHttpRequestAndGetJsonResponseTests.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.WebApis; 2 | 3 | [TestFixture,Parallelizable] 4 | public class SendTheHttpRequestAndGetJsonResponseTests 5 | { 6 | [Test,AutoMoqData] 7 | public void GetReportFragmentShouldReturnTheCorrectString(IHasName actor, 8 | IFormatsReportFragment formatter, 9 | [Frozen] HttpRequestMessageBuilder builder, 10 | SendTheHttpRequestAndGetJsonResponse sut, 11 | ReportFragment report) 12 | { 13 | Mock.Get(formatter) 14 | .Setup(x => x.Format("{Actor} sends a web request and deserializes the JSON response as {ResponseType}", actor, "String")) 15 | .Returns(report); 16 | 17 | Assert.That(sut.GetReportFragment(actor, formatter), Is.SameAs(report)); 18 | } 19 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/WebApis/SendTheHttpRequestAndGetTheResponseTests.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.WebApis; 2 | 3 | [TestFixture,Parallelizable] 4 | public class SendTheHttpRequestAndGetTheResponseTests 5 | { 6 | [Test,AutoMoqData] 7 | public void GetReportFragmentShouldReturnTheCorrectString(IHasName actor, 8 | IFormatsReportFragment formatter, 9 | [Frozen] HttpRequestMessageBuilder builder, 10 | SendTheHttpRequestAndGetTheResponse sut, 11 | ReportFragment report) 12 | { 13 | Mock.Get(formatter) 14 | .Setup(x => x.Format("{Actor} sends an HTTP {Method} request to {Builder}, expecting the response to be {ResponseType}", actor, builder.Method, builder, "String")) 15 | .Returns(report); 16 | 17 | Assert.That(sut.GetReportFragment(actor, formatter), Is.SameAs(report)); 18 | } 19 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/WebApis/SendTheHttpRequestTests.cs: -------------------------------------------------------------------------------- 1 | namespace CSF.Screenplay.WebApis; 2 | 3 | [TestFixture,Parallelizable] 4 | public class SendTheHttpRequestTests 5 | { 6 | [Test,AutoMoqData] 7 | public void GetReportFragmentShouldReturnTheCorrectString(IHasName actor, 8 | IFormatsReportFragment formatter, 9 | [Frozen] HttpRequestMessageBuilder builder, 10 | SendTheHttpRequest sut, 11 | ReportFragment report) 12 | { 13 | Mock.Get(formatter) 14 | .Setup(x => x.Format("{Actor} sends an HTTP {Method} request to {Builder}", actor, builder.Method, builder)) 15 | .Returns(report); 16 | 17 | Assert.That(sut.GetReportFragment(actor, formatter), Is.SameAs(report)); 18 | } 19 | } -------------------------------------------------------------------------------- /Tests/CSF.Screenplay.Tests/WebApis/SendsMockHttpRequestsAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Http; 3 | using System.Reflection; 4 | using AutoFixture; 5 | 6 | namespace CSF.Screenplay.WebApis; 7 | 8 | public class SendsMockHttpRequestsAttribute : CustomizeAttribute 9 | { 10 | public override ICustomization GetCustomization(ParameterInfo parameter) => new SendsMockHttpRequestsCustomization(); 11 | } 12 | 13 | public class SendsMockHttpRequestsCustomization : ICustomization 14 | { 15 | public void Customize(IFixture fixture) 16 | { 17 | fixture.Customize(c => c.Do(actor => 18 | { 19 | var client = new Mock(); 20 | client 21 | .Setup(x => x.SendAsync(It.IsAny(), It.IsAny())) 22 | .Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK))); 23 | var makeWebApiRequests = new MakeWebApiRequests 24 | { 25 | DefaultClient = client.Object, 26 | }; 27 | actor.IsAbleTo(makeWebApiRequests); 28 | })); 29 | } 30 | } 31 | 32 | public record SerializableObject(string StringProperty, int NumericProperty); -------------------------------------------------------------------------------- /Tools/appveyor-upload-test-results.ps1: -------------------------------------------------------------------------------- 1 | # Adapted from https://www.appveyor.com/docs/running-tests/#uploading-xml-test-results 2 | 3 | $SolutionRoot = "$PSScriptRoot\.." 4 | $TestProjects = Get-ChildItem $SolutionRoot\Tests\ 5 | 6 | $wc = New-Object 'System.Net.WebClient' 7 | 8 | foreach($project in $TestProjects) 9 | { 10 | $testResultFile = "$SolutionRoot\Tests\$project\TestResults\TestResults.xml" 11 | Move-Item $testResultFile "$SolutionRoot\TestResults\$project.TestResults.xml" 12 | 13 | # Intentionally using the 'nunit' endpoint and not 'nunit3'. See the following ticket for more info: 14 | # https://help.appveyor.com/discussions/problems/37319-http-500-error-when-uploading-nunit3-test-results-suspected-failure-on-parameterized-test-cases 15 | $wc.UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path $SolutionRoot\TestResults\$project.TestResults.xml)) 16 | } 17 | -------------------------------------------------------------------------------- /Tools/appveyor_publish_docs.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = "Stop" 2 | 3 | $BaseDir = "docs" 4 | 5 | if($Env:APPVEYOR -eq "True" -and ($Env:APPVEYOR_PULL_REQUEST_NUMBER -or $Env:APPVEYOR_REPO_BRANCH -ne "master")) { 6 | Write-Host "Skipping publishing docs; we are not building on master" 7 | Exit 0; 8 | } 9 | 10 | Write-Host "Publishing the docs site to $BaseDir" 11 | 12 | if($Env:APPVEYOR -eq "True") { 13 | Write-Host "Setting up git to make a commit from Appveyor" 14 | git config --global user.name "Appveyor (on behalf of Craig Fowler)" 15 | git config --global user.email "craig+appveyor@csf-dev.com" 16 | git config --global credential.helper store 17 | Set-Content -Path "$HOME\.git-credentials" -Value "https://$($Env:GITHUB_SECRET_KEY):x-oauth-basic@github.com`n" -NoNewline 18 | } 19 | 20 | # The git commands below could report warnings which cause the script to 21 | # stop unless I change the error preference first 22 | $ErrorActionPreference = "silentlycontinue" 23 | 24 | git checkout -b temp/publish-docs 25 | git add --all $BaseDir/ 26 | git commit -m "Auto: Updates to docs website via CI [skip ci]" 27 | git checkout $Env:APPVEYOR_REPO_BRANCH 28 | git pull 29 | git merge temp/publish-docs --no-ff -m "Auto: Merge docs website via CI [skip ci]" 30 | git push origin $Env:APPVEYOR_REPO_BRANCH 31 | BRANCH 32 | -------------------------------------------------------------------------------- /Tools/run-tests-with-coverage.ps1: -------------------------------------------------------------------------------- 1 | $SolutionRoot = "$PSScriptRoot\.." 2 | $TestProjects = Get-ChildItem $SolutionRoot\Tests\ 3 | $Tfm = "net8.0" 4 | $Configuration = "Debug" 5 | Remove-Item $SolutionRoot\TestResults\* -ErrorAction Ignore 6 | 7 | foreach($project in $TestProjects) 8 | { 9 | coverlet ` 10 | "$SolutionRoot\Tests\$project\bin\$Configuration\$Tfm\$project.dll" ` 11 | --target "dotnet" ` 12 | --targetargs "test $SolutionRoot\Tests\$project --no-build --logger:nunit --test-adapter-path:." ` 13 | -f=opencover ` 14 | -o="$SolutionRoot\TestResults\$project.opencover.xml" 15 | } 16 | 17 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csf-dev/CSF.Screenplay/842ee7d745f2c77d80a1ba276435f7420750eb11/docs/.nojekyll -------------------------------------------------------------------------------- /docs/docs/builderPattern/toc.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 | 14 | 25 |
26 |
27 |
28 |
29 | -------------------------------------------------------------------------------- /docs/docs/builderPattern/toc.json: -------------------------------------------------------------------------------- 1 | 2 | {"items":[{"name":"Introdction","href":"index.html","topicHref":"index.html"},{"name":"Writing builders","href":"WritingBuilders.html","topicHref":"WritingBuilders.html"},{"name":"Consuming builders","href":"ConsumingBuilders.html","topicHref":"ConsumingBuilders.html"}],"pdf":false} 3 | -------------------------------------------------------------------------------- /docs/docs/performables/toc.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 | 14 | 28 |
29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /docs/docs/performables/toc.json: -------------------------------------------------------------------------------- 1 | 2 | {"order":100,"items":[{"name":"Introduction","href":"index.html","topicHref":"index.html"},{"name":"Stopwatch","href":"../../api/CSF.Screenplay.Performables.StopwatchBuilder.html","topicHref":"../../api/CSF.Screenplay.Performables.StopwatchBuilder.html","topicUid":"CSF.Screenplay.Performables.StopwatchBuilder"},{"name":"Web APIs","href":"WebApis.html","topicHref":"WebApis.html"},{"name":"TimeSpan builder","href":"../../api/CSF.Screenplay.Performables.TimeSpanBuilder-1.html","topicHref":"../../api/CSF.Screenplay.Performables.TimeSpanBuilder-1.html","topicUid":"CSF.Screenplay.Performables.TimeSpanBuilder`1"}],"pdf":false} 3 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csf-dev/CSF.Screenplay/842ee7d745f2c77d80a1ba276435f7420750eb11/docs/favicon.ico -------------------------------------------------------------------------------- /docs/glossary/toc.json: -------------------------------------------------------------------------------- 1 | 2 | {"items":[{"name":"Glossary","href":"index.html","topicHref":"index.html"},{"name":"Ability","href":"Ability.html","topicHref":"Ability.html"},{"name":"Action","href":"Action.html","topicHref":"Action.html"},{"name":"Actor","href":"../api/CSF.Screenplay.Actor.html","topicHref":"../api/CSF.Screenplay.Actor.html","topicUid":"CSF.Screenplay.Actor"},{"name":"Cast","href":"../api/CSF.Screenplay.ICast.html","topicHref":"../api/CSF.Screenplay.ICast.html","topicUid":"CSF.Screenplay.ICast"},{"name":"Feature","href":"Feature.html","topicHref":"Feature.html"},{"name":"Integration","href":"Integration.html","topicHref":"Integration.html"},{"name":"Performable","href":"Performable.html","topicHref":"Performable.html"},{"name":"Performance","href":"../api/CSF.Screenplay.Performance.html","topicHref":"../api/CSF.Screenplay.Performance.html","topicUid":"CSF.Screenplay.Performance"},{"name":"Persona","href":"Persona.html","topicHref":"Persona.html"},{"name":"Question","href":"Question.html","topicHref":"Question.html"},{"name":"Report","href":"Report.html","topicHref":"Report.html"},{"name":"Scenario","href":"Scenario.html","topicHref":"Scenario.html"},{"name":"Screenplay","href":"../api/CSF.Screenplay.Screenplay.html","topicHref":"../api/CSF.Screenplay.Screenplay.html","topicUid":"CSF.Screenplay.Screenplay"},{"name":"Stage","href":"../api/CSF.Screenplay.IStage.html","topicHref":"../api/CSF.Screenplay.IStage.html","topicUid":"CSF.Screenplay.IStage"},{"name":"Task","href":"Task.html","topicHref":"Task.html"}],"pdf":false} 3 | -------------------------------------------------------------------------------- /docs/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by Docfx 9 | 10 | 12 | 15 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /docs/public/architecture-I3QFYML2-NK53GYGD.min.js: -------------------------------------------------------------------------------- 1 | import{a as e,b as r}from"./chunk-DBPRPEV6.min.js";import"./chunk-TERFBH2B.min.js";import"./chunk-U4DUTLYF.min.js";import"./chunk-IQQ46AC6.min.js";import"./chunk-CXRPJJJE.min.js";import"./chunk-OSRY5VT3.min.js";export{e as ArchitectureModule,r as createArchitectureServices}; 2 | //# sourceMappingURL=architecture-I3QFYML2-NK53GYGD.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/architecture-I3QFYML2-NK53GYGD.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": [], 4 | "sourcesContent": [], 5 | "mappings": "", 6 | "names": [] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/bootstrap-icons-OCU552PF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csf-dev/CSF.Screenplay/842ee7d745f2c77d80a1ba276435f7420750eb11/docs/public/bootstrap-icons-OCU552PF.woff -------------------------------------------------------------------------------- /docs/public/bootstrap-icons-X6UQXWUS.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csf-dev/CSF.Screenplay/842ee7d745f2c77d80a1ba276435f7420750eb11/docs/public/bootstrap-icons-X6UQXWUS.woff2 -------------------------------------------------------------------------------- /docs/public/chunk-45RMNOPF.min.js: -------------------------------------------------------------------------------- 1 | import{a as t,b as a,c as o,d as i,e as f,f as e,g as u,h as d,n as s,o as l}from"./chunk-TERFBH2B.min.js";var m=class extends l{static{e(this,"InfoTokenBuilder")}constructor(){super(["info","showInfo"])}},v={parser:{TokenBuilder:e(()=>new m,"TokenBuilder"),ValueConverter:e(()=>new s,"ValueConverter")}};function I(c=i){let r=o(a(c),u),n=o(t({shared:r}),d,v);return r.ServiceRegistry.register(n),{shared:r,Info:n}}e(I,"createInfoServices");export{v as a,I as b}; 2 | //# sourceMappingURL=chunk-45RMNOPF.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-53EALYMI.min.js: -------------------------------------------------------------------------------- 1 | import{a as o,b as n,c as a,d as s,e as m,f as e,g as u,j as d,m as c,o as l}from"./chunk-TERFBH2B.min.js";var v=class extends l{static{e(this,"PieTokenBuilder")}constructor(){super(["pie","showData"])}},C=class extends c{static{e(this,"PieValueConverter")}runCustomConverter(t,r,i){if(t.name==="PIE_SECTION_LABEL")return r.replace(/"/g,"").trim()}},P={parser:{TokenBuilder:e(()=>new v,"TokenBuilder"),ValueConverter:e(()=>new C,"ValueConverter")}};function p(t=s){let r=a(n(t),u),i=a(o({shared:r}),d,P);return r.ServiceRegistry.register(i),{shared:r,Pie:i}}e(p,"createPieServices");export{P as a,p as b}; 2 | //# sourceMappingURL=chunk-53EALYMI.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-5KKNARB2.min.js: -------------------------------------------------------------------------------- 1 | import{a as i,b as o,c as t,d as n,e as c,f as e,g as u,l as d,n as l,o as s}from"./chunk-TERFBH2B.min.js";var p=class extends s{static{e(this,"GitGraphTokenBuilder")}constructor(){super(["gitGraph"])}},h={parser:{TokenBuilder:e(()=>new p,"TokenBuilder"),ValueConverter:e(()=>new l,"ValueConverter")}};function m(G=n){let r=t(o(G),u),a=t(i({shared:r}),d,h);return r.ServiceRegistry.register(a),{shared:r,GitGraph:a}}e(m,"createGitGraphServices");export{h as a,m as b}; 2 | //# sourceMappingURL=chunk-5KKNARB2.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-B5WF4DA4.min.js: -------------------------------------------------------------------------------- 1 | import{h as i}from"./chunk-UOD6J27N.min.js";function t(c,e){c.accDescr&&e.setAccDescription?.(c.accDescr),c.accTitle&&e.setAccTitle?.(c.accTitle),c.title&&e.setDiagramTitle?.(c.title)}i(t,"populateCommonDb");export{t as a}; 2 | //# sourceMappingURL=chunk-B5WF4DA4.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-B5WF4DA4.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["../../node_modules/mermaid/dist/chunks/mermaid.core/chunk-BAOP5US2.mjs"], 4 | "sourcesContent": ["import {\n __name\n} from \"./chunk-P27NXTFD.mjs\";\n\n// src/diagrams/common/populateCommonDb.ts\nfunction populateCommonDb(ast, db) {\n if (ast.accDescr) {\n db.setAccDescription?.(ast.accDescr);\n }\n if (ast.accTitle) {\n db.setAccTitle?.(ast.accTitle);\n }\n if (ast.title) {\n db.setDiagramTitle?.(ast.title);\n }\n}\n__name(populateCommonDb, \"populateCommonDb\");\n\nexport {\n populateCommonDb\n};\n"], 5 | "mappings": "4CAKA,SAASA,EAAiBC,EAAKC,EAAI,CAC7BD,EAAI,UACNC,EAAG,oBAAoBD,EAAI,QAAQ,EAEjCA,EAAI,UACNC,EAAG,cAAcD,EAAI,QAAQ,EAE3BA,EAAI,OACNC,EAAG,kBAAkBD,EAAI,KAAK,CAElC,CACAE,EAAOH,EAAkB,kBAAkB", 6 | "names": ["populateCommonDb", "ast", "db", "__name"] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/chunk-CIWIECNU.min.js: -------------------------------------------------------------------------------- 1 | import{h as i}from"./chunk-UOD6J27N.min.js";var o=i(({flowchart:t})=>{let r=t?.subGraphTitleMargin?.top??0,a=t?.subGraphTitleMargin?.bottom??0,e=r+a;return{subGraphTitleTopMargin:r,subGraphTitleBottomMargin:a,subGraphTitleTotalMargin:e}},"getSubGraphTitleMargins");export{o as a}; 2 | //# sourceMappingURL=chunk-CIWIECNU.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-CIWIECNU.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["../../node_modules/mermaid/dist/chunks/mermaid.core/chunk-V2CZVG6A.mjs"], 4 | "sourcesContent": ["import {\n __name\n} from \"./chunk-P27NXTFD.mjs\";\n\n// src/utils/subGraphTitleMargins.ts\nvar getSubGraphTitleMargins = /* @__PURE__ */ __name(({\n flowchart\n}) => {\n const subGraphTitleTopMargin = flowchart?.subGraphTitleMargin?.top ?? 0;\n const subGraphTitleBottomMargin = flowchart?.subGraphTitleMargin?.bottom ?? 0;\n const subGraphTitleTotalMargin = subGraphTitleTopMargin + subGraphTitleBottomMargin;\n return {\n subGraphTitleTopMargin,\n subGraphTitleBottomMargin,\n subGraphTitleTotalMargin\n };\n}, \"getSubGraphTitleMargins\");\n\nexport {\n getSubGraphTitleMargins\n};\n"], 5 | "mappings": "4CAKA,IAAIA,EAA0CC,EAAO,CAAC,CACpD,UAAAC,CACF,IAAM,CACJ,IAAMC,EAAyBD,GAAW,qBAAqB,KAAO,EAChEE,EAA4BF,GAAW,qBAAqB,QAAU,EACtEG,EAA2BF,EAAyBC,EAC1D,MAAO,CACL,uBAAAD,EACA,0BAAAC,EACA,yBAAAC,CACF,CACF,EAAG,yBAAyB", 6 | "names": ["getSubGraphTitleMargins", "__name", "flowchart", "subGraphTitleTopMargin", "subGraphTitleBottomMargin", "subGraphTitleTotalMargin"] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/chunk-DBPRPEV6.min.js: -------------------------------------------------------------------------------- 1 | import{a as i,b as u,c as a,d as n,e as m,f as r,g as o,k as s,m as l,o as d}from"./chunk-TERFBH2B.min.js";var h=class extends d{static{r(this,"ArchitectureTokenBuilder")}constructor(){super(["architecture"])}},A=class extends l{static{r(this,"ArchitectureValueConverter")}runCustomConverter(t,e,c){if(t.name==="ARCH_ICON")return e.replace(/[()]/g,"").trim();if(t.name==="ARCH_TEXT_ICON")return e.replace(/["()]/g,"");if(t.name==="ARCH_TITLE")return e.replace(/[[\]]/g,"").trim()}},C={parser:{TokenBuilder:r(()=>new h,"TokenBuilder"),ValueConverter:r(()=>new A,"ValueConverter")}};function v(t=n){let e=a(u(t),o),c=a(i({shared:e}),s,C);return e.ServiceRegistry.register(c),{shared:e,Architecture:c}}r(v,"createArchitectureServices");export{C as a,v as b}; 2 | //# sourceMappingURL=chunk-DBPRPEV6.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-DFMVIMQJ.min.js: -------------------------------------------------------------------------------- 1 | import{f as t}from"./chunk-TERFBH2B.min.js";var a={},o={info:t(async()=>{let{createInfoServices:e}=await import("./info-46DW6VJ7-MWRO75OW.min.js"),r=e().Info.parser.LangiumParser;a.info=r},"info"),packet:t(async()=>{let{createPacketServices:e}=await import("./packet-W2GHVCYJ-73RTG6VT.min.js"),r=e().Packet.parser.LangiumParser;a.packet=r},"packet"),pie:t(async()=>{let{createPieServices:e}=await import("./pie-BEWT4RHE-6E5EOTAQ.min.js"),r=e().Pie.parser.LangiumParser;a.pie=r},"pie"),architecture:t(async()=>{let{createArchitectureServices:e}=await import("./architecture-I3QFYML2-NK53GYGD.min.js"),r=e().Architecture.parser.LangiumParser;a.architecture=r},"architecture"),gitGraph:t(async()=>{let{createGitGraphServices:e}=await import("./gitGraph-YCYPL57B-C4NBXIBD.min.js"),r=e().GitGraph.parser.LangiumParser;a.gitGraph=r},"gitGraph")};async function n(e,r){let i=o[e];if(!i)throw new Error(`Unknown diagram type: ${e}`);a[e]||await i();let s=a[e].parse(r);if(s.lexerErrors.length>0||s.parserErrors.length>0)throw new p(s);return s.value}t(n,"parse");var p=class extends Error{constructor(e){let r=e.lexerErrors.map(c=>c.message).join(` 2 | `),i=e.parserErrors.map(c=>c.message).join(` 3 | `);super(`Parsing failed: ${r} ${i}`),this.result=e}static{t(this,"MermaidParseError")}};export{n as a}; 4 | //# sourceMappingURL=chunk-DFMVIMQJ.min.js.map 5 | -------------------------------------------------------------------------------- /docs/public/chunk-HOCACIQT.min.js: -------------------------------------------------------------------------------- 1 | import{O as x,h as r,ia as a,j as h}from"./chunk-UOD6J27N.min.js";var b=r((t,e)=>{let o;return e==="sandbox"&&(o=a("#i"+t)),(e==="sandbox"?a(o.nodes()[0].contentDocument.body):a("body")).select(`[id="${t}"]`)},"getDiagramElement"),B=r((t,e,o,n)=>{t.attr("class",o);let{width:i,height:s,x:m,y:d}=g(t,e);x(t,s,i,n);let c=w(m,d,i,s,e);t.attr("viewBox",c),h.debug(`viewBox configured: ${c} with padding: ${e}`)},"setupViewPortForSVG"),g=r((t,e)=>{let o=t.node()?.getBBox()||{width:0,height:0,x:0,y:0};return{width:o.width+e*2,height:o.height+e*2,x:o.x,y:o.y}},"calculateDimensionsWithPadding"),w=r((t,e,o,n,i)=>`${t-i} ${e-i} ${o} ${n}`,"createViewBox");export{b as a,B as b}; 2 | //# sourceMappingURL=chunk-HOCACIQT.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-IQQ46AC6.min.js: -------------------------------------------------------------------------------- 1 | import{B as d,D as v,G as P,a as e,c as u,f as i,g as a,i as m,t as w,v as f,y as b,z as j}from"./chunk-CXRPJJJE.min.js";var A=w(Object.keys,Object),S=A;var C=Object.prototype,V=C.hasOwnProperty;function D(r){if(!f(r))return S(r);var t=[];for(var o in Object(r))V.call(r,o)&&o!="constructor"&&t.push(o);return t}var T=D;var K=a(e,"DataView"),n=K;var N=a(e,"Promise"),s=N;var W=a(e,"Set"),c=W;var B=a(e,"WeakMap"),g=B;var O="[object Map]",z="[object Object]",M="[object Promise]",h="[object Set]",x="[object WeakMap]",k="[object DataView]",E=i(n),G=i(m),L=i(s),q=i(c),F=i(g),p=u;(n&&p(new n(new ArrayBuffer(1)))!=k||m&&p(new m)!=O||s&&p(s.resolve())!=M||c&&p(new c)!=h||g&&p(new g)!=x)&&(p=function(r){var t=u(r),o=t==z?r.constructor:void 0,y=o?i(o):"";if(y)switch(y){case E:return k;case G:return O;case L:return M;case q:return h;case F:return x}return t});var l=p;var H="[object Map]",I="[object Set]",J=Object.prototype,Q=J.hasOwnProperty;function R(r){if(r==null)return!0;if(d(r)&&(j(r)||typeof r=="string"||typeof r.splice=="function"||v(r)||P(r)||b(r)))return!r.length;var t=l(r);if(t==H||t==I)return!r.size;if(f(r))return!T(r).length;for(var o in r)if(Q.call(r,o))return!1;return!0}var kr=R;export{T as a,c as b,l as c,kr as d}; 2 | //# sourceMappingURL=chunk-IQQ46AC6.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-K7PO6CNQ.min.js: -------------------------------------------------------------------------------- 1 | import{h as t}from"./chunk-UOD6J27N.min.js";var s=class{constructor(i){this.init=i,this.records=this.init()}static{t(this,"ImperativeState")}reset(){this.records=this.init()}};export{s as a}; 2 | //# sourceMappingURL=chunk-K7PO6CNQ.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-K7PO6CNQ.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["../../node_modules/mermaid/dist/chunks/mermaid.core/chunk-RGXPSUNZ.mjs"], 4 | "sourcesContent": ["import {\n __name\n} from \"./chunk-P27NXTFD.mjs\";\n\n// src/utils/imperativeState.ts\nvar ImperativeState = class {\n /**\n * @param init - Function that creates the default state.\n */\n constructor(init) {\n this.init = init;\n this.records = this.init();\n }\n static {\n __name(this, \"ImperativeState\");\n }\n reset() {\n this.records = this.init();\n }\n};\n\nexport {\n ImperativeState\n};\n"], 5 | "mappings": "4CAKA,IAAIA,EAAkB,KAAM,CAI1B,YAAYC,EAAM,CAChB,KAAK,KAAOA,EACZ,KAAK,QAAU,KAAK,KAAK,CAC3B,CACA,MAAO,CACLC,EAAO,KAAM,iBAAiB,CAChC,CACA,OAAQ,CACN,KAAK,QAAU,KAAK,KAAK,CAC3B,CACF", 6 | "names": ["ImperativeState", "init", "__name"] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/chunk-NRRWWJ3P.min.js: -------------------------------------------------------------------------------- 1 | import{a as o,b as c,c as t,d as n,e as k,f as e,g as i,i as u,n as d,o as l}from"./chunk-TERFBH2B.min.js";var m=class extends l{static{e(this,"PacketTokenBuilder")}constructor(){super(["packet-beta"])}},v={parser:{TokenBuilder:e(()=>new m,"TokenBuilder"),ValueConverter:e(()=>new d,"ValueConverter")}};function p(s=n){let r=t(c(s),i),a=t(o({shared:r}),u,v);return r.ServiceRegistry.register(a),{shared:r,Packet:a}}e(p,"createPacketServices");export{v as a,p as b}; 2 | //# sourceMappingURL=chunk-NRRWWJ3P.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-OSRY5VT3.min.js: -------------------------------------------------------------------------------- 1 | var h=Object.create;var f=Object.defineProperty;var i=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var k=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty;var m=(b,a)=>()=>(a||b((a={exports:{}}).exports,a),a.exports),n=(b,a)=>{for(var c in a)f(b,c,{get:a[c],enumerable:!0})},e=(b,a,c,g)=>{if(a&&typeof a=="object"||typeof a=="function")for(let d of j(a))!l.call(b,d)&&d!==c&&f(b,d,{get:()=>a[d],enumerable:!(g=i(a,d))||g.enumerable});return b},o=(b,a,c)=>(e(b,a,"default"),c&&e(c,a,"default")),p=(b,a,c)=>(c=b!=null?h(k(b)):{},e(a||!b||!b.__esModule?f(c,"default",{value:b,enumerable:!0}):c,b));export{m as a,n as b,o as c,p as d}; 2 | //# sourceMappingURL=chunk-OSRY5VT3.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-OSRY5VT3.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": [], 4 | "sourcesContent": [], 5 | "mappings": "", 6 | "names": [] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/chunk-R7DHUQMU.min.js: -------------------------------------------------------------------------------- 1 | import{Z as s,h as n,ia as e}from"./chunk-UOD6J27N.min.js";var a=n(t=>{let{securityLevel:c}=s(),o=e("body");if(c==="sandbox"){let m=e(`#i${t}`).node()?.contentDocument??document;o=e(m.body)}return o.select(`#${t}`)},"selectSvgElement");export{a}; 2 | //# sourceMappingURL=chunk-R7DHUQMU.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-R7DHUQMU.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["../../node_modules/mermaid/dist/chunks/mermaid.core/chunk-HK56VNYQ.mjs"], 4 | "sourcesContent": ["import {\n __name,\n getConfig2 as getConfig\n} from \"./chunk-P27NXTFD.mjs\";\n\n// src/rendering-util/selectSvgElement.ts\nimport { select } from \"d3\";\nvar selectSvgElement = /* @__PURE__ */ __name((id) => {\n const { securityLevel } = getConfig();\n let root = select(\"body\");\n if (securityLevel === \"sandbox\") {\n const sandboxElement = select(`#i${id}`);\n const doc = sandboxElement.node()?.contentDocument ?? document;\n root = select(doc.body);\n }\n const svg = root.select(`#${id}`);\n return svg;\n}, \"selectSvgElement\");\n\nexport {\n selectSvgElement\n};\n"], 5 | "mappings": "2DAOA,IAAIA,EAAmCC,EAAQC,GAAO,CACpD,GAAM,CAAE,cAAAC,CAAc,EAAIC,EAAU,EAChCC,EAAOC,EAAO,MAAM,EACxB,GAAIH,IAAkB,UAAW,CAE/B,IAAMI,EADiBD,EAAO,KAAKJ,CAAE,EAAE,EACZ,KAAK,GAAG,iBAAmB,SACtDG,EAAOC,EAAOC,EAAI,IAAI,CACxB,CAEA,OADYF,EAAK,OAAO,IAAIH,CAAE,EAAE,CAElC,EAAG,kBAAkB", 6 | "names": ["selectSvgElement", "__name", "id", "securityLevel", "getConfig2", "root", "select_default", "doc"] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/chunk-TOQ6ACLX.min.js: -------------------------------------------------------------------------------- 1 | import{b as u,c as y,d as f,e as h}from"./chunk-7JZIB2XU.min.js";import{b as g,e as m,h as d}from"./chunk-LDPOAEOH.min.js";import{c as l}from"./chunk-KF4CENUX.min.js";import{D as n,N as s,h as o,j as a}from"./chunk-UOD6J27N.min.js";var p={common:s,getConfig:n,insertCluster:m,insertEdge:f,insertEdgeLabel:u,insertMarkers:h,insertNode:d,interpolateToCurve:l,labelHelper:g,log:a,positionEdgeLabel:y},t={},L=o(r=>{for(let e of r)t[e.name]=e},"registerLayoutLoaders"),w=o(()=>{L([{name:"dagre",loader:o(async()=>await import("./dagre-SWNTG5WE-7BA3SWK2.min.js"),"loader")}])},"registerDefaultLayoutLoaders");w();var R=o(async(r,e)=>{if(!(r.layoutAlgorithm in t))throw new Error(`Unknown layout algorithm: ${r.layoutAlgorithm}`);let i=t[r.layoutAlgorithm];return(await i.loader()).render(r,e,p,{algorithm:i.algorithm})},"render"),_=o((r="",{fallback:e="dagre"}={})=>{if(r in t)return r;if(e in t)return a.warn(`Layout algorithm ${r} is not registered. Using ${e} as fallback.`),e;throw new Error(`Both layout algorithms ${r} and ${e} are not registered.`)},"getRegisteredLayoutAlgorithm");export{L as a,R as b,_ as c}; 2 | //# sourceMappingURL=chunk-TOQ6ACLX.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-XBNRW4G3.min.js: -------------------------------------------------------------------------------- 1 | import{a as c}from"./chunk-CM5D5KZN.min.js";import{G as o,h as n}from"./chunk-UOD6J27N.min.js";import{d as x}from"./chunk-OSRY5VT3.min.js";var l=x(c(),1),d=n((a,t)=>{let r=a.append("rect");if(r.attr("x",t.x),r.attr("y",t.y),r.attr("fill",t.fill),r.attr("stroke",t.stroke),r.attr("width",t.width),r.attr("height",t.height),t.name&&r.attr("name",t.name),t.rx&&r.attr("rx",t.rx),t.ry&&r.attr("ry",t.ry),t.attrs!==void 0)for(let e in t.attrs)r.attr(e,t.attrs[e]);return t.class&&r.attr("class",t.class),r},"drawRect"),g=n((a,t)=>{let r={x:t.startx,y:t.starty,width:t.stopx-t.startx,height:t.stopy-t.starty,fill:t.fill,stroke:t.stroke,class:"rect"};d(a,r).lower()},"drawBackgroundRect"),h=n((a,t)=>{let r=t.text.replace(o," "),e=a.append("text");e.attr("x",t.x),e.attr("y",t.y),e.attr("class","legend"),e.style("text-anchor",t.anchor),t.class&&e.attr("class",t.class);let s=e.append("tspan");return s.attr("x",t.x+t.textMargin*2),s.text(r),e},"drawText"),y=n((a,t,r,e)=>{let s=a.append("image");s.attr("x",t),s.attr("y",r);let i=(0,l.sanitizeUrl)(e);s.attr("xlink:href",i)},"drawImage"),p=n((a,t,r,e)=>{let s=a.append("use");s.attr("x",t),s.attr("y",r);let i=(0,l.sanitizeUrl)(e);s.attr("xlink:href",`#${i}`)},"drawEmbeddedImage"),f=n(()=>({x:0,y:0,width:100,height:100,fill:"#EDF2AE",stroke:"#666",anchor:"start",rx:0,ry:0}),"getNoteRect"),w=n(()=>({x:0,y:0,width:100,height:100,"text-anchor":"start",style:"#666",textMargin:0,rx:0,ry:0,tspan:!0}),"getTextObj");export{d as a,g as b,h as c,y as d,p as e,f,w as g}; 2 | //# sourceMappingURL=chunk-XBNRW4G3.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-ZU5APUIQ.min.js: -------------------------------------------------------------------------------- 1 | var r="11.4.0";export{r as a}; 2 | //# sourceMappingURL=chunk-ZU5APUIQ.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/chunk-ZU5APUIQ.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["../../node_modules/mermaid/dist/chunks/mermaid.core/chunk-UI5P3W6P.mjs"], 4 | "sourcesContent": ["// package.json\nvar version = \"11.4.0\";\n\nexport {\n version\n};\n"], 5 | "mappings": "AACA,IAAIA,EAAU", 6 | "names": ["version"] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/classDiagram-FEGYTUDG-YCLVHNZU.min.js: -------------------------------------------------------------------------------- 1 | import{a as e,b as a,c as i,d as s}from"./chunk-P2C2SEUI.min.js";import"./chunk-HOCACIQT.min.js";import"./chunk-TOQ6ACLX.min.js";import"./chunk-7JZIB2XU.min.js";import"./chunk-MJXJ7UGT.min.js";import"./chunk-LDPOAEOH.min.js";import"./chunk-CIWIECNU.min.js";import"./chunk-PYBRKFQJ.min.js";import"./chunk-24EG6CQZ.min.js";import"./chunk-KF4CENUX.min.js";import"./chunk-CM5D5KZN.min.js";import{h as t}from"./chunk-UOD6J27N.min.js";import"./chunk-CXRPJJJE.min.js";import"./chunk-OSRY5VT3.min.js";var g={parser:e,db:a,renderer:s,styles:i,init:t(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute,a.clear()},"init")};export{g as diagram}; 2 | //# sourceMappingURL=classDiagram-FEGYTUDG-YCLVHNZU.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/classDiagram-FEGYTUDG-YCLVHNZU.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["../../node_modules/mermaid/dist/chunks/mermaid.core/classDiagram-FEGYTUDG.mjs"], 4 | "sourcesContent": ["import {\n classDb_default,\n classDiagram_default,\n classRenderer_v3_unified_default,\n styles_default\n} from \"./chunk-CXDZ2C6O.mjs\";\nimport \"./chunk-EICJXIV7.mjs\";\nimport \"./chunk-TM57GTNK.mjs\";\nimport \"./chunk-SGE5E2BZ.mjs\";\nimport \"./chunk-HKLBG3BQ.mjs\";\nimport \"./chunk-32DZJKXY.mjs\";\nimport \"./chunk-XHTJQHD2.mjs\";\nimport \"./chunk-V2CZVG6A.mjs\";\nimport \"./chunk-U6O4IJP6.mjs\";\nimport \"./chunk-MCANT3UC.mjs\";\nimport {\n __name\n} from \"./chunk-P27NXTFD.mjs\";\n\n// src/diagrams/class/classDiagram.ts\nvar diagram = {\n parser: classDiagram_default,\n db: classDb_default,\n renderer: classRenderer_v3_unified_default,\n styles: styles_default,\n init: /* @__PURE__ */ __name((cnf) => {\n if (!cnf.class) {\n cnf.class = {};\n }\n cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;\n classDb_default.clear();\n }, \"init\")\n};\nexport {\n diagram\n};\n"], 5 | "mappings": "6eAoBA,IAAIA,EAAU,CACZ,OAAQC,EACR,GAAIC,EACJ,SAAUC,EACV,OAAQC,EACR,KAAsBC,EAAQC,GAAQ,CAC/BA,EAAI,QACPA,EAAI,MAAQ,CAAC,GAEfA,EAAI,MAAM,oBAAsBA,EAAI,oBACpCJ,EAAgB,MAAM,CACxB,EAAG,MAAM,CACX", 6 | "names": ["diagram", "classDiagram_default", "classDb_default", "classRenderer_v3_unified_default", "styles_default", "__name", "cnf"] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/classDiagram-v2-R65JCUOM-VFGJ2XXK.min.js: -------------------------------------------------------------------------------- 1 | import{a as e,b as a,c as i,d as s}from"./chunk-P2C2SEUI.min.js";import"./chunk-HOCACIQT.min.js";import"./chunk-TOQ6ACLX.min.js";import"./chunk-7JZIB2XU.min.js";import"./chunk-MJXJ7UGT.min.js";import"./chunk-LDPOAEOH.min.js";import"./chunk-CIWIECNU.min.js";import"./chunk-PYBRKFQJ.min.js";import"./chunk-24EG6CQZ.min.js";import"./chunk-KF4CENUX.min.js";import"./chunk-CM5D5KZN.min.js";import{h as t}from"./chunk-UOD6J27N.min.js";import"./chunk-CXRPJJJE.min.js";import"./chunk-OSRY5VT3.min.js";var g={parser:e,db:a,renderer:s,styles:i,init:t(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute,a.clear()},"init")};export{g as diagram}; 2 | //# sourceMappingURL=classDiagram-v2-R65JCUOM-VFGJ2XXK.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/classDiagram-v2-R65JCUOM-VFGJ2XXK.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["../../node_modules/mermaid/dist/chunks/mermaid.core/classDiagram-v2-R65JCUOM.mjs"], 4 | "sourcesContent": ["import {\n classDb_default,\n classDiagram_default,\n classRenderer_v3_unified_default,\n styles_default\n} from \"./chunk-CXDZ2C6O.mjs\";\nimport \"./chunk-EICJXIV7.mjs\";\nimport \"./chunk-TM57GTNK.mjs\";\nimport \"./chunk-SGE5E2BZ.mjs\";\nimport \"./chunk-HKLBG3BQ.mjs\";\nimport \"./chunk-32DZJKXY.mjs\";\nimport \"./chunk-XHTJQHD2.mjs\";\nimport \"./chunk-V2CZVG6A.mjs\";\nimport \"./chunk-U6O4IJP6.mjs\";\nimport \"./chunk-MCANT3UC.mjs\";\nimport {\n __name\n} from \"./chunk-P27NXTFD.mjs\";\n\n// src/diagrams/class/classDiagram-v2.ts\nvar diagram = {\n parser: classDiagram_default,\n db: classDb_default,\n renderer: classRenderer_v3_unified_default,\n styles: styles_default,\n init: /* @__PURE__ */ __name((cnf) => {\n if (!cnf.class) {\n cnf.class = {};\n }\n cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;\n classDb_default.clear();\n }, \"init\")\n};\nexport {\n diagram\n};\n"], 5 | "mappings": "6eAoBA,IAAIA,EAAU,CACZ,OAAQC,EACR,GAAIC,EACJ,SAAUC,EACV,OAAQC,EACR,KAAsBC,EAAQC,GAAQ,CAC/BA,EAAI,QACPA,EAAI,MAAQ,CAAC,GAEfA,EAAI,MAAM,oBAAsBA,EAAI,oBACpCJ,EAAgB,MAAM,CACxB,EAAG,MAAM,CACX", 6 | "names": ["diagram", "classDiagram_default", "classDb_default", "classRenderer_v3_unified_default", "styles_default", "__name", "cnf"] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/gitGraph-YCYPL57B-C4NBXIBD.min.js: -------------------------------------------------------------------------------- 1 | import{a as r,b as e}from"./chunk-5KKNARB2.min.js";import"./chunk-TERFBH2B.min.js";import"./chunk-U4DUTLYF.min.js";import"./chunk-IQQ46AC6.min.js";import"./chunk-CXRPJJJE.min.js";import"./chunk-OSRY5VT3.min.js";export{r as GitGraphModule,e as createGitGraphServices}; 2 | //# sourceMappingURL=gitGraph-YCYPL57B-C4NBXIBD.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/gitGraph-YCYPL57B-C4NBXIBD.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": [], 4 | "sourcesContent": [], 5 | "mappings": "", 6 | "names": [] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/info-46DW6VJ7-MWRO75OW.min.js: -------------------------------------------------------------------------------- 1 | import{a as o,b as e}from"./chunk-45RMNOPF.min.js";import"./chunk-TERFBH2B.min.js";import"./chunk-U4DUTLYF.min.js";import"./chunk-IQQ46AC6.min.js";import"./chunk-CXRPJJJE.min.js";import"./chunk-OSRY5VT3.min.js";export{o as InfoModule,e as createInfoServices}; 2 | //# sourceMappingURL=info-46DW6VJ7-MWRO75OW.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/info-46DW6VJ7-MWRO75OW.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": [], 4 | "sourcesContent": [], 5 | "mappings": "", 6 | "names": [] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/infoDiagram-P5D6MX3V-EFUDKNDH.min.js: -------------------------------------------------------------------------------- 1 | import{a as s}from"./chunk-ZU5APUIQ.min.js";import{a as i}from"./chunk-R7DHUQMU.min.js";import{a as g}from"./chunk-DFMVIMQJ.min.js";import"./chunk-45RMNOPF.min.js";import"./chunk-NRRWWJ3P.min.js";import"./chunk-53EALYMI.min.js";import"./chunk-DBPRPEV6.min.js";import"./chunk-5KKNARB2.min.js";import"./chunk-TERFBH2B.min.js";import"./chunk-U4DUTLYF.min.js";import"./chunk-IQQ46AC6.min.js";import{O as n,h as r,j as a}from"./chunk-UOD6J27N.min.js";import"./chunk-CXRPJJJE.min.js";import"./chunk-OSRY5VT3.min.js";var v={parse:r(async e=>{let t=await g("info",e);a.debug(t)},"parse")},d={version:s},m=r(()=>d.version,"getVersion"),c={getVersion:m},f=r((e,t,p)=>{a.debug(`rendering info diagram 2 | `+e);let o=i(t);n(o,100,400,!0),o.append("g").append("text").attr("x",100).attr("y",40).attr("class","version").attr("font-size",32).style("text-anchor","middle").text(`v${p}`)},"draw"),l={draw:f},y={parser:v,db:c,renderer:l};export{y as diagram}; 3 | //# sourceMappingURL=infoDiagram-P5D6MX3V-EFUDKNDH.min.js.map 4 | -------------------------------------------------------------------------------- /docs/public/lunr.ja-J6QHZSR2.min.js: -------------------------------------------------------------------------------- 1 | import{a}from"./chunk-DTUU2GN4.min.js";import"./chunk-OSRY5VT3.min.js";export default a(); 2 | //# sourceMappingURL=lunr.ja-J6QHZSR2.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/lunr.ja-J6QHZSR2.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": [], 4 | "sourcesContent": [], 5 | "mappings": "", 6 | "names": [] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/lunr.jp-M45D3XJE.min.js: -------------------------------------------------------------------------------- 1 | import{a as i}from"./chunk-DTUU2GN4.min.js";import{a as r}from"./chunk-OSRY5VT3.min.js";var o=r((p,e)=>{e.exports=i()});export default o(); 2 | //# sourceMappingURL=lunr.jp-M45D3XJE.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/lunr.jp-M45D3XJE.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["../../node_modules/lunr-languages/lunr.jp.js"], 4 | "sourcesContent": ["// jp is the country code, while ja is the language code\n// a new lunr.ja.js has been created, but in order to\n// keep the backward compatibility, we'll leave the lunr.jp.js\n// here for a while, and just make it use the new lunr.ja.js\nmodule.exports = require('./lunr.ja');"], 5 | "mappings": "wFAAA,IAAAA,EAAAC,EAAA,CAAAC,EAAAC,IAAA,CAIAA,EAAO,QAAU", 6 | "names": ["require_lunr_jp", "__commonJSMin", "exports", "module"] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/lunr.vi-3U4A337N.min.js: -------------------------------------------------------------------------------- 1 | import{a as u}from"./chunk-OSRY5VT3.min.js";var o=u((t,r)=>{(function(e,i){typeof define=="function"&&define.amd?define(i):typeof t=="object"?r.exports=i():i()(e.lunr)})(t,function(){return function(e){if(typeof e>"u")throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(typeof e.stemmerSupport>"u")throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.vi=function(){this.pipeline.reset(),this.pipeline.add(e.vi.stopWordFilter,e.vi.trimmer)},e.vi.wordCharacters="[A-Za-z\u0300\u0350\u0301\u0351\u0309\u0323\u0303\u0343\xC2\xE2\xCA\xEA\xD4\xF4\u0102-\u0103\u0110-\u0111\u01A0-\u01A1\u01AF-\u01B0]",e.vi.trimmer=e.trimmerSupport.generateTrimmer(e.vi.wordCharacters),e.Pipeline.registerFunction(e.vi.trimmer,"trimmer-vi"),e.vi.stopWordFilter=e.generateStopWordFilter("l\xE0 c\xE1i nh\u01B0ng m\xE0".split(" "))}})});export default o(); 2 | /*! Bundled license information: 3 | 4 | lunr-languages/lunr.vi.js: 5 | (*! 6 | * Lunr languages, `Vietnamese` language 7 | * https://github.com/MihaiValentin/lunr-languages 8 | * 9 | * Copyright 2017, Keerati Thiwanruk 10 | * http://www.mozilla.org/MPL/ 11 | *) 12 | (*! 13 | * based on 14 | * Snowball JavaScript Library v0.3 15 | * http://code.google.com/p/urim/ 16 | * http://snowball.tartarus.org/ 17 | * 18 | * Copyright 2010, Oleg Mazko 19 | * http://www.mozilla.org/MPL/ 20 | *) 21 | */ 22 | //# sourceMappingURL=lunr.vi-3U4A337N.min.js.map 23 | -------------------------------------------------------------------------------- /docs/public/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csf-dev/CSF.Screenplay/842ee7d745f2c77d80a1ba276435f7420750eb11/docs/public/main.css -------------------------------------------------------------------------------- /docs/public/main.js: -------------------------------------------------------------------------------- 1 | export default {} 2 | -------------------------------------------------------------------------------- /docs/public/packet-W2GHVCYJ-73RTG6VT.min.js: -------------------------------------------------------------------------------- 1 | import{a as e,b as r}from"./chunk-NRRWWJ3P.min.js";import"./chunk-TERFBH2B.min.js";import"./chunk-U4DUTLYF.min.js";import"./chunk-IQQ46AC6.min.js";import"./chunk-CXRPJJJE.min.js";import"./chunk-OSRY5VT3.min.js";export{e as PacketModule,r as createPacketServices}; 2 | //# sourceMappingURL=packet-W2GHVCYJ-73RTG6VT.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/packet-W2GHVCYJ-73RTG6VT.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": [], 4 | "sourcesContent": [], 5 | "mappings": "", 6 | "names": [] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/pie-BEWT4RHE-6E5EOTAQ.min.js: -------------------------------------------------------------------------------- 1 | import{a as e,b as r}from"./chunk-53EALYMI.min.js";import"./chunk-TERFBH2B.min.js";import"./chunk-U4DUTLYF.min.js";import"./chunk-IQQ46AC6.min.js";import"./chunk-CXRPJJJE.min.js";import"./chunk-OSRY5VT3.min.js";export{e as PieModule,r as createPieServices}; 2 | //# sourceMappingURL=pie-BEWT4RHE-6E5EOTAQ.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/pie-BEWT4RHE-6E5EOTAQ.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": [], 4 | "sourcesContent": [], 5 | "mappings": "", 6 | "names": [] 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/stateDiagram-v2-HP6YRVRG-XMVPP43U.min.js: -------------------------------------------------------------------------------- 1 | import{a,b as i,c as r,d as o}from"./chunk-2TAJJIOM.min.js";import"./chunk-HOCACIQT.min.js";import"./chunk-TOQ6ACLX.min.js";import"./chunk-7JZIB2XU.min.js";import"./chunk-MJXJ7UGT.min.js";import"./chunk-LDPOAEOH.min.js";import"./chunk-CIWIECNU.min.js";import"./chunk-PYBRKFQJ.min.js";import"./chunk-24EG6CQZ.min.js";import"./chunk-KF4CENUX.min.js";import"./chunk-CM5D5KZN.min.js";import{h as e}from"./chunk-UOD6J27N.min.js";import"./chunk-CXRPJJJE.min.js";import"./chunk-OSRY5VT3.min.js";var k={parser:a,db:r,renderer:i,styles:o,init:e(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute,r.clear()},"init")};export{k as diagram}; 2 | //# sourceMappingURL=stateDiagram-v2-HP6YRVRG-XMVPP43U.min.js.map 3 | -------------------------------------------------------------------------------- /docs/public/stateDiagram-v2-HP6YRVRG-XMVPP43U.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["../../node_modules/mermaid/dist/chunks/mermaid.core/stateDiagram-v2-HP6YRVRG.mjs"], 4 | "sourcesContent": ["import {\n stateDb_default,\n stateDiagram_default,\n stateRenderer_v3_unified_default,\n styles_default\n} from \"./chunk-JZAHL7AJ.mjs\";\nimport \"./chunk-EICJXIV7.mjs\";\nimport \"./chunk-TM57GTNK.mjs\";\nimport \"./chunk-SGE5E2BZ.mjs\";\nimport \"./chunk-HKLBG3BQ.mjs\";\nimport \"./chunk-32DZJKXY.mjs\";\nimport \"./chunk-XHTJQHD2.mjs\";\nimport \"./chunk-V2CZVG6A.mjs\";\nimport \"./chunk-U6O4IJP6.mjs\";\nimport \"./chunk-MCANT3UC.mjs\";\nimport {\n __name\n} from \"./chunk-P27NXTFD.mjs\";\n\n// src/diagrams/state/stateDiagram-v2.ts\nvar diagram = {\n parser: stateDiagram_default,\n db: stateDb_default,\n renderer: stateRenderer_v3_unified_default,\n styles: styles_default,\n init: /* @__PURE__ */ __name((cnf) => {\n if (!cnf.state) {\n cnf.state = {};\n }\n cnf.state.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;\n stateDb_default.clear();\n }, \"init\")\n};\nexport {\n diagram\n};\n"], 5 | "mappings": "weAoBA,IAAIA,EAAU,CACZ,OAAQC,EACR,GAAIC,EACJ,SAAUC,EACV,OAAQC,EACR,KAAsBC,EAAQC,GAAQ,CAC/BA,EAAI,QACPA,EAAI,MAAQ,CAAC,GAEfA,EAAI,MAAM,oBAAsBA,EAAI,oBACpCJ,EAAgB,MAAM,CACxB,EAAG,MAAM,CACX", 6 | "names": ["diagram", "stateDiagram_default", "stateDb_default", "stateRenderer_v3_unified_default", "styles_default", "__name", "cnf"] 7 | } 8 | -------------------------------------------------------------------------------- /docs/toc.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 | 14 | 28 |
29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /docs/toc.json: -------------------------------------------------------------------------------- 1 | 2 | {"items":[{"name":"Documentation","href":"index.html","topicHref":"index.html"},{"name":"Glossary","href":"glossary/index.html","tocHref":"glossary/toc.html","topicHref":"glossary/index.html"},{"name":"API reference","href":"api/CSF.Screenplay.html","tocHref":"api/toc.html","topicHref":"api/CSF.Screenplay.html","topicUid":"CSF.Screenplay"},{"name":"GitHub repo","href":"https://github.com/csf-dev/CSF.Screenplay","topicHref":"https://github.com/csf-dev/CSF.Screenplay"}],"pdf":false} 3 | --------------------------------------------------------------------------------