*This is the third post in a series about web test extensibility points.  The first post was about extending web tests using custom IHttpBody classes and the second post was about a custom ValidationRule to catch redirects to error pages.*

The ExtractHiddenFields rule that is present in most web tests works by extracting every hidden field on a page into the web test context.  The naming convention used for these hidden fields is $HIDDENx.y where x is the context parameter name on ExtractHiddenFields (usually '1') and y is the hidden field name.  This convention works well for most web pages, but it can't distinguish between multiple hidden fields with the same name, like a page with multiple forms might have.  Fortunately, this scenario can be supported with the following custom extracton rule and some minor modifications to the web test.

using System;
using System.Collections;
using System.ComponentModel;
using Microsoft.VisualStudio.TestTools.WebTesting;

namespace OcracokeSamples {
    public class ExtractFormFieldWithIndex : ExtractionRule {
        private string _name;
        private int _index = 1;

        [Description("The name of the form field to extract.")]
        public string Name {
            get { return _name; }
            set { _name = value; }
        }

        [Description("The index of the form field.  For example, specifying '2' would " +
            "extract the value of the second form field with the given name.")]
        public int Index {
            get { return _index; }
            set { _index = value; }
        }

        public override string RuleName {
            get { return "Extract Form Field with Index"; }
        }

        public override string RuleDescription {
            get {
                return "Extracts a form field from a page based on the order it appears.  " +
                "Useful when a page contains multiple forms with fields of the same name.";
            }
        }

        public override void Extract(object sender, ExtractionEventArgs e) {
            //if the response is not HTML, display an error
            if (!e.Response.IsHtml) {
                e.Success = false;
                e.Message = "The response did not contain HTML.";
                return;
            }

            string formFieldValue = null;
            int currentIndex = 0;

            //examine each input tag
            foreach (HtmlTag tag in e.Response.HtmlDocument.GetFilteredHtmlTags("input")) {
                if (String.Equals(tag.GetAttributeValueAsString("name"), _name, StringComparison.OrdinalIgnoreCase)) {
                    currentIndex++;

                    if (currentIndex == _index) {
                        formFieldValue = tag.GetAttributeValueAsString("value");
                       
                        //if the form field was found, but had no value property, set the value to empty string
                        if (formFieldValue == null) {
                            formFieldValue = String.Empty;
                        }

                        break;
                    }
                }
            }

            if (formFieldValue != null) {
                e.WebTest.Context.Add(this.ContextParameterName, formFieldValue);
                e.Success = true;
                return;
            } else {
                e.Success = false;
                e.Message = String.Format("Form field named '{0}' with index '{1}' was not found.", _name, _index);
            }
        }
    }
}

If you read my earlier post about creating a custom validation rule, you'll reconize that creating a custom extraction rule is nearly identical.  You simply create a class that derives from ExtractionRule and implement your extraction logic in the Extract method.  The ExtractionEventArgs parameter to the Extract method provides access to the WebTestResponse containing all the response data received from the server.  Setting e.Success reports whether the rule passed or failed and e.Message allows you to provide an error message that will be displayed in the web test result viewer or the load test monitor.

To use a custom extraction rule in a web test, you must first let your test project know it exists.  If the rule class is included in the test project, just build the project and the rule will show up in the Add Extraction Rule dialog.  If, however, the rule is part of a separate class library project and potentially shared by multiple test projects, just build that class library project and add a reference to it in your test project(s).  Now when you right-click a request and select Add Extraction Rule, you should see your rule in the Add Extraction Rule dialog shown below.

Add Extraction Rule dialog

To use this extraction rule, you enter a form field name and specify an index to determine which occurrence of that form field you want to extract (1 for the first, 2 for the second, etc.).  Once you specify a context parameter name for the form field you're extracting and add this extraction rule to a request, you'll just need to update the parameter that corresponds to this form field.  Instead of $HIDDENx.y, bind the parameter's value to this new context parameter.

That's it.  Any questions about implementing custom extraction rules or using this sample rule?