Thursday, August 7, 2008

Coding like Ike and Tina...

I'm working on a WebTestRequest that changes an address and in the post back the POST data contains a bunch of information that is displayed in the HTML and I need to extract data from text boxes, check boxes and drop down lists. Extracting the textboxes and checkboxes are pretty simple, it is the drop down boxes that are a pain.

The .NET HTML parser treats the HtmlTags that reside inside the SELECT tag separately so I cannot just get the attributes for the select and then slice and dice and get the default selected value for the drop in.

I tried to be "elegant" in my solution and use the HTML Agility Pack and a XPath pulled from FireBug but alas, there was no love to be found. I'm not sure what the exact problem is other than me being a total XPath n00b (I normally use an open source Java project to extract XPaths from XML docs) but something was amiss. After wasting several hours trying to get the elegant solution I decided to be like Ike and Tina and just brute force the solution of identifying the default selected value for the drop down lists. I populate the Context of the WebTestRequest with all the values that are extracted and just reference them by element name in the address change POST ala the HIDDEN1$ that VS2008 does for hidden field extraction and it seems to work pretty good.

Here is the extraction code that I wrote to pull the fields that I needed to reference in the Context:



   1:  void ExtractCustomerDataToContext(object sender, ExtractionEventArgs e) {

   2:    Dictionary<string, string> customerData = new Dictionary<string, string>();

   3:    string lastDropDown = null;

   4:    foreach (HtmlTag tag in e.Response.HtmlDocument.HtmlTags) {

   5:      string type = tag.GetAttributeValueAsString("type");

   6:      if (type != null) {

   7:        lastDropDown = null ;

   8:        if (type == "text" || type == "checkbox") {

   9:          if (tag.GetAttributeValueAsString("checked") != null) {

  10:            if (tag.GetAttributeValueAsString("checked") == "checked") {

  11:              customerData.Add(tag.GetAttributeValueAsString("name"), "on");

  12:            } else {

  13:              customerData.Add(tag.GetAttributeValueAsString("name"), "off");

  14:            };

  15:          } else {

  16:            customerData.Add(tag.GetAttributeValueAsString("name"), tag.GetAttributeValueAsString("value"));

  17:          };

  18:        };

  19:      } else {

  20:        if (tag.GetAttributeValueAsString("class") != null) {

  21:          if (tag.GetAttributeValueAsString("class") == "dropDownListDefault") {

  22:            lastDropDown = tag.GetAttributeValueAsString("name");

  23:          };

  24:        } else {

  25:          if (tag.GetAttributeValueAsString("selected") != null) {

  26:            if (lastDropDown != null) {

  27:              customerData.Add(lastDropDown, tag.GetAttributeValueAsString("value"));

  28:            };

  29:          };

  30:        };

  31:      };

  32:    };

  33:    foreach (KeyValuePair<string, string> kvp in customerData) {

  34:      if (this.Context.ContainsKey("CUSTOMER_UPDATE_DATA." + kvp.Key)) {

  35:        this.Context.Remove("CUSTOMER_UPDATE_DATA." + kvp.Key);

  36:      };

  37:      if (kvp.Value == null) {

  38:        this.Context.Add("CUSTOMER_UPDATE_DATA." + kvp.Key, "");

  39:      } else {

  40:        this.Context.Add("CUSTOMER_UPDATE_DATA." + kvp.Key, kvp.Value);

  41:      };

  42:    };

  43:  }



So, if the previous page HTML has a text field by the name of "txtCustomerAccountNumber" I can simply add that information to the post with:



   1:  changeAddressRequestBody.FormPostParameters.Add("txtCustomerAccountNumber", this.Context["CUSTOMER_UPDATE_DATA.txtCustomerAccountNumber"].ToString());



As long as the information was displayed via HTML in the prior hit, I should be good to go.

No comments: