CSS in Custom Reports

159 views
Skip to first unread message

Todd J

unread,
Mar 22, 2016, 4:57:34 PM3/22/16
to SpecRun
I have some log functions I am adding to the cshtml report and I am running into a bit of an issue. My message appears but my css is lost. I am trying to add color the errors, warns and informational log messages. Anyone have an idea?


        public static void WriteWarning(string message)
       
{
            message
= "<div class=\"infoMessage\">" + message + "</div>";
           
Console.WriteLine(message);
       
}

My report with then display the below instead of rendering the html from the .infoMessage from the css.


Stephen McCafferty

unread,
Apr 1, 2016, 7:15:27 AM4/1/16
to SpecRun
At first glance it looks like you are missing the opening angle bracket around the closing "div" tag in your output:

<div class="infoMessage">jill@no.com has been typed into the element.</div>

That said, if I take your code snippet, I get the angled bracket in my output; and the style is also applied correctly.

Stephen McCafferty

unread,
Apr 1, 2016, 7:20:06 AM4/1/16
to SpecRun
Here is what I have in my .cshtml file btw, as it's not 100% the same as your code (I was primarily interested in checking that the HTML was correctly formatted):

@functions
{
/* XXX MY CUSTOMISED FUNCTION XXX */
   
string compose_message(string message)

   
{
        message
= "<div class=\"infoMessage\">" + message + "</div>";

       
return message;
   
}
}
As you can see, I haven't written to the console, I just formatted the HTML and returned the output.

And then in my report I have:
        @compose_message("ji...@no.com has been typed into the element")

Which calls the function and outputs the HTML to the report with the style applied. I obviously also added the .infoMessage style (using copy/paste). The resulting text is green and bold in my report.



Todd J

unread,
Apr 1, 2016, 10:55:12 AM4/1/16
to SpecRun
Stephen  - Thanks for taking the time to look at this. Not sure how the closing div tag was missing but it should have been displayed. I have tried a few different ways and no luck. I changed my LogFunctions.cs from public static void to a public static string and returned the message and my report then had nothing appearing in it. i got tired of looking at the html tags in the report so I am now just returning the text but below is how it was setup.

 
        public static void WriteInfo(string message)
        {
            message = "<div class=\"infoMessage\">" + message + "</div>";
            Console.WriteLine(message);
        }

 
Report Output

xDazedx

unread,
Oct 27, 2016, 2:22:05 PM10/27/16
to SpecRun
Hate to drag up old posts but since I am looking at using custom reports again I thought I would look at this also. I wanted to show you what my report is producing on this in the html and see if you had any ideas on how to resolve? Below shows how my code is displayed and how some correct code is displayed. Notice on mine how the div class="errorMessage" is within the pre tag and also begins with a quote? The working code has the pre tag closed and then the errorMessage. I am not sure how my code is doing this.

Non-working html





Working html





Stephen McCafferty

unread,
Oct 28, 2016, 9:34:16 AM10/28/16
to SpecRun
Couple of questions:
  • Do you want everything to be rendered as <pre>? As in, are the expected and received error messages, as well as the "done" section meant to be formatted with <pre>?
  • Are you passing this information via the console, as in the first post?
  • Can you copy/paste the code from your .cshtml file for this section so I can grab it and try it out directly?
  • Can you also copy/paste the code that writes to the console (presuming that is what you are doing)?
  • Can you maybe post quick mock-up or similar that shows how you want the final report to appear (i.e. so I can see the desired effect of your formatting and what you are trying to achieve as the end result)? It doesn't need to be anything fancy, just so I get the general idea of what is formatted how.
  • What is == $0?
  • I take it the quotation mark before the first "<div" isn't actually supposed to be there?

The working HTML is essentially the same as taking the <pre> tags out altogether (as there is no text affected by the tag).


xDazedx

unread,
Oct 28, 2016, 1:01:53 PM10/28/16
to SpecRun
Stephen it is the same as it is above. I have a LogFunction.cs that looks like the below and I added the css for the other code. 

On our methods for a "type" we may add: 
LogFunctions.WriteInfo("Confirm Password " + confirmPass + " entered into website.");

On the specflow report this should just appear as a line for that step in Green as: Confirm Password MyPass123 entered into website.
This helps troubleshooting a bit more since we have the exact data that was entered.

So to answer your other questions above:
1. it is just falling into the pre tag that way. On the system generated stack trace it appears correctly so that is why I was comparing my non-working code to the working code. 
2. ==$0 I noticed as well but am not sure how that is being added

The reason I question that beginning (and ending) double quote is it appears to render as text instead of as html. Just a guess though.


LogFunctions.cs
using System;


namespace TestFramework
{
   
public static class LogFunctions
   
{
       
public static void WriteWarning(string message)
       
{
            message
= "<div class=\"warningMessage\">" + message + "</div>";
           
Console.WriteLine(message);
       
}
       
public static void WriteError(string message)
       
{
            message
= "<div class=\"errorMessage\">" + message + "</div>";

           
Console.WriteLine(message);


       
}
       
public static void WriteInfo(string message)
       
{
            message
= "<div class=\"infoMessage\">" + message + "</div>";
           
Console.WriteLine(message);
       
}
   
}
}


Added to ReportTemplate.cshtml
            .errorMessage
           
{
                width
: 600px;
                color
: Red;
                font
-weight: bold;

           
}
           
.infoMessage
           
{
                width
: 600px;
                color
: Green;
                font
-weight: bold;
           
}

           
.warnMessage
           
{
                width
: 600px;
                color
: Yellow;
                font
-weight: bold;
           
}

Stephen McCafferty

unread,
Oct 31, 2016, 8:49:45 AM10/31/16
to SpecRun

I take it you want something like this:






I used your 3 styles and basically just wrote a silly message to the console using each of the functions you had defined. So the code side of things should be really obvious and what you are doing already. My resulting HTML looks like this:
<div class="errorMessage">Error: Function not declared at customs</div>          
<br>                                        
<pre class="log">done: CalculatorSteps.GivenIHaveEnteredIntoTheCalculator(50) (0.0s)</pre>

So I have separated out the error message and the log entry, and added a line break between the two to make things more legible.

The trick is that you need to parse the information you are passing via the console from within your template. If you don't do this, SpecFlow will assume that it should regurgitate the message verbatim, and will format everything as if it were plain text, not HTML.

I added some code to the .cshtml file for the test events table, just after this line:
<!-- [@traceEvent.Type: @relatedNode.Type - @relatedNode.Title] -->

This is the code:
@{                                
   
String source = traceEvent.TechMessages.TrimEnd();  //Get the console message without HTML formatting
    String formattedText = "";                          //This will contain the error/info/warning message if present
    String message = source;                            //This is the message passed to the console
    String tag = "</div>";                              //This is the closing tag we are looking for that designates the end of the error/info/warning
    int a = source.LastIndexOf(tag);                    //Find the position of the tag in the source string (-1 means not present)
    if (a > -1)                                         //If the formatting tag is present...
    {
        formattedText = source.Substring(0, a + tag.Length );   //Strip out the substring until the end of the </div> tag
        message = source.Substring(formattedText.Length + 1, source.Length - formattedText.Length-1);  //and remove the extra data from the message
    }
}

So I now have two strings, the formattedText corresponding to your message (including HTML tags) and the default message that SpecFlow+ always outputs. We now need to include these in the report...

To do this, we need to replace the standard output in the following line...
<pre class="log">@Raw(FormatTechMessages(traceEvent.TechMessages.TrimEnd()))</pre>
... with the desired output:

@Raw(formattedText)         
<br />                                        
<pre class="log">@Raw(message)</pre>

And voila...


HTH

xDazedx

unread,
Oct 31, 2016, 9:29:13 AM10/31/16
to SpecRun
Beautiful!! This is exactly what I wanted. Thanks for the help Stephen. I owe you a beer. 

xDazedx

unread,
Nov 10, 2016, 3:51:17 PM11/10/16
to SpecRun
Stephen - Sorry to bother but I have an error I need to ask you about. I have this working on a few projects but for some reason, it is failing on one. The projects are virtually the same though. Same framework, same package versions etc. Any idea why this might be throwing the below?

System.ArgumentOutOfRangeException: startIndex cannot be larger than length of string.
Parameter name: startIndex
   at System.String.Substring(Int32 startIndex, Int32 length)
   at CompiledRazorTemplates.Dynamic.RazorEngine_79e72a39cb2c45a9a38e485ef30b13ca.Execute()
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.DynamicWrapperService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass1.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at RazorEngine.Templating.RazorEngineServiceExtensions.RunCompile(IRazorEngineService service, ITemplateKey key, Type modelType, Object model, DynamicViewBag viewBag)
   at TechTalk.SpecRun.Framework.Reporting.RazorRenderer.RenderReport(TestRunResult testRunResult, Report report)
   at TechTalk.SpecRun.Framework.Reporting.ReportingEngine.GenerateReport(TestRunResult testRunResult, String outputFileName, Report report)
   at TechTalk.SpecRun.Framework.Reporting.ExecutionReporter.ReportResult(TestRunResult result)
   at TechTalk.SpecRun.Framework.ExecutionEngine.ExecuteTestSuite(TestProfile testProfile, TestRunExecutionConfiguration executionConfiguration, IExecutionContainerBuilder containerBuilder)
   at TechTalk.SpecRun.VisualStudio.TestAdapter.SpecRunTestExecutor.<>c__DisplayClass9_0.<ExecuteTestSuite>b__1()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
   at System.String.Substring(Int32 startIndex, Int32 length)
   at CompiledRazorTemplates.Dynamic.RazorEngine_79e72a39cb2c45a9a38e485ef30b13ca.Execute()
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.DynamicWrapperService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass1.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at RazorEngine.Templating.RazorEngineServiceExtensions.RunCompile(IRazorEngineService service, ITemplateKey key, Type modelType, Object model, DynamicViewBag viewBag)
   at TechTalk.SpecRun.Framework.Reporting.RazorRenderer.RenderReport(TestRunResult testRunResult, Report report)
   at TechTalk.SpecRun.Framework.Reporting.ReportingEngine.GenerateReport(TestRunResult testRunResult, String outputFileName, Report report)
   at TechTalk.SpecRun.Framework.Reporting.ExecutionReporter.ReportResult(TestRunResult result)
   at TechTalk.SpecRun.Framework.ExecutionEngine.ExecuteTestSuite(TestProfile testProfile, TestRunExecutionConfiguration executionConfiguration, IExecutionContainerBuilder containerBuilder)
   at TechTalk.SpecRun.VisualStudio.TestAdapter.SpecRunTestExecutor.<>c__DisplayClass9_0.<ExecuteTestSuite>b__1()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
Log file: file:///D:\Proj\TestResults\Tests_Default_2016-11-10T134909.log
An exception occurred while invoking executor 'executor://specrun/executorV1.0.0.0': Object reference not set to an instance of an object.
========== Run test finished: 1 run (0:00:48.485) ==========



Stephen McCafferty

unread,
Nov 11, 2016, 7:18:42 AM11/11/16
to SpecRun
From the error message it looks like you are parsing strings and have got a string where for some reason, your initial index for the substring is higher than the actual length of the string. This might be because the string is empty, i.e. it hasn't been written to in this project, or because the calculations of the offsets are wrong.

The code I posted above only did limited sanity checking, so it won't cover all use cases. For example, it assumes that you always have more to process than just your message itself; if the entire string is just the message you are sending, it could fail on this line:

message = source.Substring(formattedText.Length + 1, source.Length - formattedText.Length-1);

i.e. if the formattedText string is set to the entire source string (i.e. "</div"> is at the end of the source string), the start index (formattedText.Length+1) will be invalid (higher than the length of the source string). I would go through the parts where you take substrings and check whether it is possible that you are making an implicit assumption that there will be more text like I did. You might want to add some debug code that outputs the length of your strings and the start index you have calculated (and comment out the lines with the substrings) to track down the issue.


Todd Johnson

unread,
Nov 11, 2016, 8:49:54 AM11/11/16
to spe...@googlegroups.com
Thanks Stephen. I will try and hack my way through it. <---is not a dev :)
Reply all
Reply to author
Forward
0 new messages