IT:AD:Continuous Testing:HowTo:Test The Html Of A Running Website
Summary
When I first looked at IT:AD:HTML Tidy, Total Validator and IT:AD:CSE HTML Validator I had not considered a basic fact. You can't get valuable reports from inspecting files – partial *.cshtml pages are not valid html – but instead have to run tests on running websites that are assembling the webpages.
Below is a starting point for a post-deployment test.
Process
This will help:
namespace UnitTests
{
/// <summary>
/// Represents a result from the W3C Markup Validation Service.
/// </summary>
public class W3CValidityCheckResult
{
public bool IsValid { get; set; }
public int WarningsCount { get; set; }
public int ErrorsCount { get; set; }
public string Body { get; set; }
}
static class TestHelper
{
private static AutoResetEvent _w3cValidatorBlock = new AutoResetEvent(true);
private static void ResetBlocker(object state)
{
// Ensures that W3C Validator service is not called more than once a second
Thread.Sleep(1000);
_w3cValidatorBlock.Set();
}
/// <summary>
/// Determines whether the ASP.NET page returns valid HTML by checking the response against the W3C Markup Validator.
/// </summary>
///
<param name="testContext">The test context.</param>
///
<param name="aspNetServerName">Name of the ASP.NET server.</param>
///
<param name="path">The relative path of the resource to check.</param>
/// <returns>
/// An object representing indicating whether the HTML generated is valid.
/// </returns>
public static W3CValidityCheckResult ReturnsValidHtml(TestContext testContext, string aspNetServerName, string path)
{
var result = new W3CValidityCheckResult();
WebHeaderCollection w3cResponseHeaders = new WebHeaderCollection();
using (var wc = new WebClient())
{
string url = String.Format("{0}{1}",
testContext.Properties["AspNetDevelopmentServer." + aspNetServerName].ToString(),
path);
string html = GetPageHtml(wc, url);
// Send to W3C validator
string w3cUrl = "http://validator.w3.org/check";
wc.Encoding = System.Text.Encoding.UTF8;
var values = new NameValueCollection();
values.Add("fragment", html);
values.Add("prefill", "0");
values.Add("group", "0");
values.Add("doctype", "inline");
try
{
_w3cValidatorBlock.WaitOne();
byte[] w3cRawResponse = wc.UploadValues(w3cUrl, values);
result.Body = Encoding.UTF8.GetString(w3cRawResponse);
w3cResponseHeaders.Add(wc.ResponseHeaders);
}
finally
{
ThreadPool.QueueUserWorkItem(ResetBlocker); // Reset on background thread
}
}
// Extract result from response headers
int warnings = -1;
int errors = -1;
int.TryParse(w3cResponseHeaders["X-W3C-Validator-Warnings"], out warnings);
int.TryParse(w3cResponseHeaders["X-W3C-Validator-Errors"], out errors);
string status = w3cResponseHeaders["X-W3C-Validator-Status"];
result.WarningsCount = warnings;
result.ErrorsCount = errors;
result.IsValid = (!String.IsNullOrEmpty(status) && status.Equals("Valid", StringComparison.InvariantCultureIgnoreCase));
return result;
}
private static string GetPageHtml(WebClient wc, string url)
{
// Pretend to be Firefox 3 so that ASP.NET renders compliant HTML
wc.Headers["User-Agent"] = "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1 (.NET CLR 3.5.30729)";
wc.Headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
wc.Headers["Accept-Language"] = "en-au,en-us;q=0.7,en;q=0.3";
// Read page HTML
string html = "";
using (Stream responseStream = wc.OpenRead(url))
{
using (var sr = new StreamReader(responseStream))
{
html = sr.ReadToEnd();
sr.Close();
}
}
return html;
}
}
}