diff --git a/CHANGELOG.md b/CHANGELOG.md
index a94fa9b9..35c79d9b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
+### Added
+- [.Net] Added possibility to customize resources for MessagesToHtmlWriter ([#400](https://github.com/cucumber/html-formatter/pull/400))
+
### Changed
- Upgrade `react-components` to [23.2.0](https://github.com/cucumber/react-components/releases/tag/v23.2.0)
diff --git a/dotnet/Cucumber.HtmlFormatter/DefaultResourceProvider.cs b/dotnet/Cucumber.HtmlFormatter/DefaultResourceProvider.cs
new file mode 100644
index 00000000..4fe90e87
--- /dev/null
+++ b/dotnet/Cucumber.HtmlFormatter/DefaultResourceProvider.cs
@@ -0,0 +1,65 @@
+using System.Reflection;
+
+namespace Cucumber.HtmlFormatter;
+
+///
+/// Default implementation of IResourceProvider
+///
+public class DefaultResourceProvider : IResourceProvider
+{
+ private const string TEMPLATE_RESOURCE_NAME = "index.mustache.html";
+ private const string CSS_RESOURCE_NAME = "main.css";
+ private const string JAVASCRIPT_RESOURCE_NAME = "main.js";
+ private readonly Assembly _assembly;
+ private readonly string _resourceNamespace;
+
+ public DefaultResourceProvider()
+ {
+ _assembly = typeof(MessagesToHtmlWriter).Assembly;
+ _resourceNamespace = "Cucumber.HtmlFormatter.Resources.";
+ }
+
+ ///
+ /// Constructor with custom assembly and namespace
+ ///
+ /// The assembly to load resources from
+ /// The namespace prefix for resources
+ public DefaultResourceProvider(Assembly assembly, string resourceNamespace)
+ {
+ _assembly = assembly;
+ _resourceNamespace = resourceNamespace;
+ }
+
+ ///
+ public string GetTemplateResource()
+ {
+ return GetResource(TEMPLATE_RESOURCE_NAME);
+ }
+
+ ///
+ public string GetCssResource()
+ {
+ return GetResource(CSS_RESOURCE_NAME);
+ }
+
+ ///
+ public string GetJavaScriptResource()
+ {
+ return GetResource(JAVASCRIPT_RESOURCE_NAME);
+ }
+
+ ///
+ /// Gets a resource from the assembly
+ ///
+ /// The resource name
+ /// The resource content
+ private string GetResource(string name)
+ {
+ var resourceStream = _assembly.GetManifestResourceStream(_resourceNamespace + name);
+ if (resourceStream == null)
+ throw new InvalidOperationException($"Resource '{name}' not found in assembly '{_assembly.FullName}'");
+
+ using var reader = new StreamReader(resourceStream);
+ return reader.ReadToEnd();
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Cucumber.HtmlFormatter/IResourceProvider.cs b/dotnet/Cucumber.HtmlFormatter/IResourceProvider.cs
new file mode 100644
index 00000000..ce9f24ad
--- /dev/null
+++ b/dotnet/Cucumber.HtmlFormatter/IResourceProvider.cs
@@ -0,0 +1,26 @@
+namespace Cucumber.HtmlFormatter;
+
+///
+/// Interface for providing resources to the HTML formatter.
+/// This is an experimental API for allowing customizations, will be replaced by a more flexible solution in an upcoming release.
+///
+public interface IResourceProvider
+{
+ ///
+ /// Gets the HTML template
+ ///
+ /// The HTML template
+ string GetTemplateResource();
+
+ ///
+ /// Gets the CSS resource
+ ///
+ /// The CSS resource
+ string GetCssResource();
+
+ ///
+ /// Gets the JavaScript resource
+ ///
+ /// The JavaScript resource
+ string GetJavaScriptResource();
+}
\ No newline at end of file
diff --git a/dotnet/Cucumber.HtmlFormatter/MessagesToHtmlWriter.cs b/dotnet/Cucumber.HtmlFormatter/MessagesToHtmlWriter.cs
index 7c460b22..d2629d2d 100644
--- a/dotnet/Cucumber.HtmlFormatter/MessagesToHtmlWriter.cs
+++ b/dotnet/Cucumber.HtmlFormatter/MessagesToHtmlWriter.cs
@@ -9,6 +9,7 @@ public class MessagesToHtmlWriter : IDisposable
private readonly Action _streamSerializer;
private readonly string _template;
private readonly JsonInHtmlWriter _jsonInHtmlWriter;
+ private readonly IResourceProvider _resourceProvider;
private bool _streamClosed = false;
private bool _preMessageWritten = false;
private bool _firstMessageWritten = false;
@@ -19,30 +20,36 @@ public class MessagesToHtmlWriter : IDisposable
public MessagesToHtmlWriter(Stream stream, Action streamSerializer) : this(new StreamWriter(stream), streamSerializer)
{
}
- public MessagesToHtmlWriter(Stream stream, Func asyncStreamSerializer) : this(new StreamWriter(stream), asyncStreamSerializer) { }
+
+ public MessagesToHtmlWriter(Stream stream, Func asyncStreamSerializer, IResourceProvider? resourceProvider = null)
+ : this(new StreamWriter(stream), asyncStreamSerializer, resourceProvider)
+ { }
[Obsolete("Cucumber.HtmlFormatter moving to async only operations. Please use the MessagesToHtmlWriter(StreamWriter, Func) constructor", false)]
public MessagesToHtmlWriter(StreamWriter writer, Action streamSerializer)
{
- this._writer = writer;
- this._streamSerializer = streamSerializer;
+ _writer = writer;
+ _streamSerializer = streamSerializer;
// Create async wrapper for sync serializer
- this._asyncStreamSerializer = (w, e) =>
+ _asyncStreamSerializer = (w, e) =>
{
streamSerializer(w, e);
return Task.CompletedTask;
};
- _template = GetResource("index.mustache.html");
+ _resourceProvider = new DefaultResourceProvider();
+ _template = _resourceProvider.GetTemplateResource();
_jsonInHtmlWriter = new JsonInHtmlWriter(writer);
_isAsyncInitialized = false;
}
- public MessagesToHtmlWriter(StreamWriter writer, Func asyncStreamSerializer)
+
+ public MessagesToHtmlWriter(StreamWriter writer, Func asyncStreamSerializer, IResourceProvider? resourceProvider = null)
{
- this._writer = writer;
- this._asyncStreamSerializer = asyncStreamSerializer;
+ _writer = writer;
+ _asyncStreamSerializer = asyncStreamSerializer;
// Create sync wrapper for async serializer (will block)
- this._streamSerializer = (w, e) => asyncStreamSerializer(w, e).GetAwaiter().GetResult();
- _template = GetResource("index.mustache.html");
+ _streamSerializer = (w, e) => asyncStreamSerializer(w, e).GetAwaiter().GetResult();
+ _resourceProvider = resourceProvider ?? new DefaultResourceProvider();
+ _template = _resourceProvider.GetTemplateResource();
_jsonInHtmlWriter = new JsonInHtmlWriter(writer);
_isAsyncInitialized = true;
}
@@ -50,28 +57,28 @@ public MessagesToHtmlWriter(StreamWriter writer, Func