Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document file sideloading #789

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions documentation/documentation/using/result_document.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!--Title:Building the Storyteller HTML Report-->

The resulting HTML document from running Storyteller is a self-contained report with all CSS and Javascript packaged inside the HTML file. The **HtmlDocument** itself is assembled by **HtmlDocumentBuilder** out of a collection of builder object **IDocumentPartBuilder**.

## Default Behavior: DefaultDocumentBuilder / HtmlDocumentBuilder

**DefaulDocumentBuilder** the default implementation of **HtmlDocumentBuilder** assembles the report from the following internal assembly resources:

* JS : StoryTeller.batch-bundle.js
* CSS : Storyteller.stylesheets.bootstrap.min.css (v 3.3.2)
* CSS : StoryTeller.stylesheets.storyteller.css
* CSS : StoryTeller.stylesheets.fixed-data-table.min.css

The best way to modify the generated report is to append additional builders to the **DefaultDocumentBuilder** using the **Add** method with any **IDocumentPartBuilder**.

## Document Modification: IDocumentPartBuilder

When the report is being assembled, a **HtmlDocument** and **BatchRunResponse** is passed to each **IDocumentPartBuilder** registered with the **HtmlDocumentBuilder** being executed. Because the **HtmlDocument** is available, any coded behavior could be defined inside a **IDocumentPartBuilder**, including behaviors that replace/modify previous builder results.

### The build-in builders are:

* ReportPartBuilder - (*Required*) Creates the report data and container elements in the reports.
* StoryTellerTitleBuilder - Updates the **HtmlDocument** Title property.
* StyleTagBuilder - Creates a style tag from loaded content.
* ScriptTagBuilder - Creates a script tag from loaded content or uri.
* LinkTagBuilder - Creates a link tag from a uri.
* HtmlTagBuilder - A base level class for building self-appending **IDocumentPartBuilder** that are also **HtmlTag** implementations. **StyleTagBuilder**, **ScriptTagBuilder** and **LinkTagBuilder** are examples of this.


## Content Resolution: IDocumentPartLoader

Many of the built-in **IDocumentPartBuilder** classes require some kind of content to be wrapped inside an **HtmlTag**. The content is often stored in files either on the local file system or embedded into the assembly. The **IDocumentPartLoader** exists to bridge the gap between these sources by expecting a **Read** method implementation that results in a content string.

### The build-in loaders are:

* VirtualFileLoader - loads a given string as the loaded content, replaces needing a content file to load.
* LocalFileLoader - loads a file from the file system as the loaded content.
* ResourceFileLoader - loads an embedded resource from a given assembly as the loaded content.
* StoryTellerResourceLoader - loads an embedded resource from the storyteller assembly as the loaded content.

42 changes: 42 additions & 0 deletions src/StoryTeller.Testing/Results/CompoundResourceLoaderTester.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Shouldly;
using StoryTeller.Results;
using Xunit;

namespace StoryTeller.Testing.Results
{
public class CompoundResourceLoaderTester
{
[Fact]
public void empty_builder_list_returns_an_empty_string()
{
var loader = new CompoundResourceLoader();
loader.Read().ShouldBeEmpty();
}

[Fact]
public void single_builder_returns_the_single_builder_result()
{
var loader = new CompoundResourceLoader()
.AddLoader(new VirtualFileLoader("test"));
loader.Read().ShouldBe("test");
}

[Fact]
public void multiple_builder_returns_the_builder_joined_by_default_delimiter()
{
var loader = new CompoundResourceLoader()
.AddLoader(new VirtualFileLoader("test"))
.AddLoader(new VirtualFileLoader("test"));
loader.Read().ShouldBe("test\n\ntest");
}

[Fact]
public void multiple_builder_returns_the_builder_joined_by_set_delimiter()
{
var loader = new CompoundResourceLoader("\n")
.AddLoader(new VirtualFileLoader("test"))
.AddLoader(new VirtualFileLoader("test"));
loader.Read().ShouldBe("test\ntest");
}
}
}
115 changes: 115 additions & 0 deletions src/StoryTeller.Testing/Results/DefaultHtmlDocumentBuilderTester.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System.Linq;
using Shouldly;
using StoryTeller.Engine;
using StoryTeller.Results;
using StoryTeller.Util;
using Xunit;

namespace StoryTeller.Testing.Results
{
public class DefaultHtmlDocumentBuilderTester
{

[Fact]
public void additional_builders_can_be_added()
{
var builder = new DefaultHtmlDocumentBuilder();
builder.Add(new ScriptTagBuilder(string.Empty));

builder.Get<IDocumentPartBuilder>().Count().ShouldBe(5);
}

[Fact]
public void defines_four_parts()
{
var builder = new DefaultHtmlDocumentBuilder();
builder.Get<IDocumentPartBuilder>().Count().ShouldBe(4);
}

[Fact]
public void defines_the_title()
{
var builder = new DefaultHtmlDocumentBuilder();
builder.Get<StoryTellerTitleBuilder>().ShouldNotBeEmpty();
}

[Fact]
public void defines_the_javascript()
{
var builder = new DefaultHtmlDocumentBuilder();
builder.Get<ScriptTagBuilder>().ShouldNotBeEmpty();
}


[Fact]
public void defines_the_styles()
{
var builder = new DefaultHtmlDocumentBuilder();
builder.Get<StyleTagBuilder>().ShouldNotBeEmpty();
}


[Fact]
public void defines_the_report_content()
{
var builder = new DefaultHtmlDocumentBuilder();
builder.Get<ReportPartBuilder>().ShouldNotBeEmpty();
}

[Fact]
public void building_the_document_results_in_html_document()
{
var response = new BatchRunResponse();
var builder = new DefaultHtmlDocumentBuilder();
builder.Build(response).ShouldBeOfType<HtmlDocument>();
}

[Fact]
public void result_has_set_title()
{
var response = new BatchRunResponse() { system = "system", suite = "suite"};
var builder = new DefaultHtmlDocumentBuilder();
var result = builder.Build(response);

result.Head.Children[0].TagName().ShouldBe("title");
result.Head.Children[0].Text().ShouldBe("Storyteller Batch Results for system: suite");
}

[Fact]
public void result_has_set_the_style()
{
var response = new BatchRunResponse() { system = "system", suite = "suite"};
var builder = new DefaultHtmlDocumentBuilder();
var result = builder.Build(response);

result.Head.Children[1].TagName().ShouldBe("style");
result.Head.Children[1].Text().ShouldNotBeEmpty();
}

[Fact]
public void result_has_set_the_report()
{
var response = new BatchRunResponse() { system = "system", suite = "suite"};
var builder = new DefaultHtmlDocumentBuilder();
var result = builder.Build(response);

result.Body.Children[0].TagName().ShouldBe("div");
result.Body.Children[0].Text().ShouldNotBeEmpty();

result.Body.Children[1].TagName().ShouldBe("div");
result.Body.Children[1].Text().ShouldBeEmpty();
}

[Fact]
public void result_has_set_the_script()
{
var response = new BatchRunResponse() { system = "system", suite = "suite"};
var builder = new DefaultHtmlDocumentBuilder();
var result = builder.Build(response);

result.Body.Children[2].TagName().ShouldBe("script");
result.Body.Children[2].Text().ShouldNotBeEmpty();
}

}
}
53 changes: 53 additions & 0 deletions src/StoryTeller.Testing/Results/LinkTagBuilderTester.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using Shouldly;
using StoryTeller.Engine;
using StoryTeller.Results;
using StoryTeller.Util;
using Xunit;

namespace StoryTeller.Testing.Results
{
public class LinkTagBuilderTester
{
[Fact]
public void creates_a_link_tag()
{
var builder = new LinkTagBuilder(new Uri("/test", UriKind.Relative));
builder.TagName().ShouldBe("link");
}

[Fact]
public void sets_the_tag_href_attribute_from_uri()
{
var builder = new LinkTagBuilder(new Uri("/test", UriKind.Relative));
builder.Attr("href").ShouldBe("/test");
}

[Fact]
public void set_the_link_relationship_to_stylesheet_by_default()
{
var builder = new LinkTagBuilder(new Uri("/test", UriKind.Relative));
builder.Attr("rel").ShouldBe("stylesheet");
}

[Fact]
public void set_the_link_relationship_to_passed_in_value()
{
var builder = new LinkTagBuilder(new Uri("/test", UriKind.Relative), "rel_test");
builder.Attr("rel").ShouldBe("rel_test");
}

[Fact]
public void the_style_tag_will_attach_itself_to_the_document_head()
{
var doc = new HtmlDocument();
var results = new BatchRunResponse();
var builder = new LinkTagBuilder(new Uri("/test", UriKind.Relative));

builder.Apply(doc, results);

doc.Head.Children.Count.ShouldBe(2);
doc.Head.Children[1].TagName().ShouldBe("link");
}
}
}
28 changes: 28 additions & 0 deletions src/StoryTeller.Testing/Results/LocalFileLoaderTester.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.IO;
using Shouldly;
using StoryTeller.Results;
using Xunit;

namespace StoryTeller.Testing.Results
{
public class LocalFileLoaderTester
{
[Fact]
public void non_existing_file_return_emptry_string()
{
var loader = new LocalFileLoader("none.existing.file");
loader.Read().ShouldBeNullOrEmpty();
}

[Fact]
public void existing_file_return_the_file_text()
{
var file = Path.GetTempFileName();
File.WriteAllText(file, "test");

var loader = new LocalFileLoader(new FileInfo(file));
loader.Read().ShouldBe("test");
File.Delete(file);
}
}
}
83 changes: 83 additions & 0 deletions src/StoryTeller.Testing/Results/ReportPartBuilderTester.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using Shouldly;
using StoryTeller.Engine;
using StoryTeller.Results;
using StoryTeller.Util;
using Xunit;

namespace StoryTeller.Testing.Results
{
public class ReportPartBuilderTester
{
[Fact]
public void adds_two_tags_to_the_document()
{
var doc = new HtmlDocument();
var results = new BatchRunResponse();
new ReportPartBuilder().Apply(doc, results);

doc.Body.Children.Count.ShouldBe(2);
}

[Fact]
public void adds_a_div_tag_with_id_batch_data()
{
var doc = new HtmlDocument();
var results = new BatchRunResponse();
new ReportPartBuilder().Apply(doc, results);

doc.Body.Children[0].TagName().ShouldBe("div");
doc.Body.Children[0].Id().ShouldBe("batch-data");
}

[Fact]
public void batch_data_tag_is_not_empty()
{
var doc = new HtmlDocument();
var results = new BatchRunResponse();
new ReportPartBuilder().Apply(doc, results);

doc.Body.Children[0].Text().ShouldNotBeNullOrEmpty();
}

[Fact]
public void batch_data_tag_is_hidden()
{
var doc = new HtmlDocument();
var results = new BatchRunResponse();
new ReportPartBuilder().Apply(doc, results);

doc.Body.Children[0].Style("display").ShouldBe("none");
}

[Fact]
public void adds_a_div_tag_with_id_main()
{
var doc = new HtmlDocument();
var results = new BatchRunResponse();
new ReportPartBuilder().Apply(doc, results);

doc.Body.Children[1].TagName().ShouldBe("div");
doc.Body.Children[1].Id().ShouldBe("main");
}

[Fact]
public void main_tag_is_empty()
{
var doc = new HtmlDocument();
var results = new BatchRunResponse();
new ReportPartBuilder().Apply(doc, results);

doc.Body.Children[1].Text().ShouldBeNullOrEmpty();
}

[Fact]
public void main_tag_is_not_hidden()
{
var doc = new HtmlDocument();
var results = new BatchRunResponse();
new ReportPartBuilder().Apply(doc, results);

doc.Body.Children[1].HasStyle("display").ShouldBeFalse();
}
}
}
27 changes: 27 additions & 0 deletions src/StoryTeller.Testing/Results/ResourceFileLoaderTester.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.IO;
using Shouldly;
using StoryTeller.Results;
using Xunit;

namespace StoryTeller.Testing.Results
{
public class ResourceFileLoaderTester
{
[Fact]
public void non_existing_file_return_emptry_string()
{
var loader = new StoryTellerResourceLoader("none.existing.file");
loader.Read().ShouldBeNullOrEmpty();
}

[Fact]
public void existing_file_return_the_file_text()
{
var file = Path.GetTempFileName();
File.WriteAllText(file, "test");

var loader = new StoryTellerResourceLoader("StoryTeller.batch-bundle.js");
loader.Read().ShouldNotBeNullOrEmpty();
}
}
}
Loading