Skip to content

Instantly share code, notes, and snippets.

@gabrieljoelc
Last active December 20, 2015 18:59
Show Gist options
  • Save gabrieljoelc/6180319 to your computer and use it in GitHub Desktop.
Save gabrieljoelc/6180319 to your computer and use it in GitHub Desktop.
Custom `Microsoft.VisualStudio.TestTools.WebTesting.ExtractionRule` that groups all of the form field `Microsoft.VisualStudio.TestTools.WebTesting.HtmlTag`s for ASP.NET MVC-style view model array form field names.
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.WebTesting;
namespace GabesWebTesting.Extraction
{
public class ExtractArrayFields : ExtractionRule
{
public const string ArrayRootElementPlaceholderName = "root";
public bool Required { get; set; }
public string FullyQualifiedNameAttributeValueUpToArrayName { get; set; }
public override void Extract(object sender, ExtractionEventArgs e)
{
if (!e.Response.IsHtml)
{
e.Success = !Required;
return;
}
var tagsForArrayIndexes = GetHtmlTagsFromArrayFields(e.Response, FullyQualifiedNameAttributeValueUpToArrayName);
if (tagsForArrayIndexes.Any())
{
e.Success = true;
e.WebTest.Context[ContextParameterName] = tagsForArrayIndexes;
}
else
{
e.Success = !Required;
e.Message = string.Format("No array tags for array {0}.", FullyQualifiedNameAttributeValueUpToArrayName);
}
}
internal static IEnumerable<IDictionary<string, HtmlTag>> GetHtmlTagsFromArrayFields(WebTestResponse response, string fullyQualifiedNameAttributeValueUpToArrayName)
{
if (!response.IsHtml)
{
return Enumerable.Empty<IDictionary<string, HtmlTag>>();
}
var startsWith = fullyQualifiedNameAttributeValueUpToArrayName + "[";
var tagsForArrayIndexes = new List<IDictionary<string, HtmlTag>>();
foreach (var tag in response.HtmlDocument.HtmlTags)
{
var name = tag.GetNameAttributeValue();
var doesStartWith = name.StartsWith(startsWith);
if (!doesStartWith)
{
continue;
}
var indexAndSuffix = name.Substring(startsWith.Length, name.Length - startsWith.Length);
if (string.IsNullOrWhiteSpace(indexAndSuffix))
{
continue;
}
var indexStr = string.Concat(indexAndSuffix.TakeWhile(c => !c.Equals(']')));
int index;
if (!int.TryParse(indexStr, out index) || index < 0)
{
continue;
}
var suffix = indexAndSuffix.Substring((indexStr + "]").Length,
indexAndSuffix.Length - (indexStr + "]").Length).Trim('.');
var arrayElemName = !string.IsNullOrWhiteSpace(suffix) ? suffix : ArrayRootElementPlaceholderName;
if (tagsForArrayIndexes.Count == 0 || index >= tagsForArrayIndexes.Count)
{
tagsForArrayIndexes.Add(new Dictionary<string, HtmlTag> { { arrayElemName, tag } });
}
else if (tagsForArrayIndexes[index].ContainsKey(arrayElemName)
|| tagsForArrayIndexes[index].ContainsKey(string.Format("{0}.{1}", tag.Name, arrayElemName))
|| tagsForArrayIndexes[index].ContainsKey(string.Format("input[type={0}].{1}", tag.GetAttributeValueAsString("type"), arrayElemName)))
{
if (tag.Name.Equals("input") && tag.Attributes.Any(a => a.Name.Equals("type")))
{
tagsForArrayIndexes[index].Add(
string.Format("input[type={0}].{1}", tag.GetAttributeValueAsString("type"), arrayElemName),
tag);
}
else
{
tagsForArrayIndexes[index].Add(
string.Format("{0}.{1}", tag.Name, arrayElemName),
tag);
}
}
else
{
tagsForArrayIndexes[index].Add(arrayElemName, tag);
}
}
return tagsForArrayIndexes;
}
}
public static class HtmlTagExtensions
{
/// <summary>
/// Gets the value for name attribute of the specified <see cref="HtmlTag"/>.
/// </summary>
/// <param name="htmlTag"></param>
/// <returns>Defaults to empty <see cref="String"/>.</returns>
public static string GetNameAttributeValue(this HtmlTag htmlTag)
{
if (htmlTag == null)
{
return string.Empty;
}
return htmlTag.GetAttributeValueAsString("name") ?? string.Empty;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment