Application Center Usage - Script Re-use, ASP.Net testing, and postback

This is Interesting: Free IT Magazines  
Home > Archive > Application Center Usage > February 2005 > Script Re-use, ASP.Net testing, and postback





You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

Author Script Re-use, ASP.Net testing, and postback
Michael McHenry

2005-01-27, 5:49 pm

I think ACT is an awful product because it almost does something very
usefull but ultimately fails.

It's bundled with .Net as an ASP.Net testing tool and it has no support
for postbacks. So I'm writing my own script library to do this.

This tool has been in use for years and either: 1) no one else willing
to share their story has done this. or 2) There is a better tool that
is easily accessible.

This is what my testing code looks like:
function MainTest() {
var serverName = 'localhost';
var useDelays = false;
var session = new SessionRequest(serverName, useDelays);

TestLogin(session);
}

function TestLogin(session) {
var response = session.GetPage('/FeiNet/profile/Login.aspx', 13110);
var postBack = new PostBack(session, response);
postBack.Field('txtUserName') = 'testuser';
postBack.Field('txtPassword') = 'testpass';
postBack.Field('btnLogon') = 'Logon';
postBack.Post('/FeiNet/profile/Login.aspx', 18719);
}
The whole rest of the test script is completely reusable stuff that I'd
rather have in some sort of include. (that's the SessionRequest and
PostBack bits)

I want to get a page, and automatically have all the form fields
prepared for the postback. I might change a few of the fields.

This is pretty easy to do in NUnitAsp, but it doesn't collect the
performance data I need.

Anyway, all I have to say is, WTF, MicroSoft?
1) Why not have some method of script includes?
2) Why not have the ACT object model include some way of generating a
response body from the last page's form fields?
3) I've chosen to use JScript instead of VBS. Thanks for the choice,
but why not use JScript.Net? I hate weakly typed languages.
Especially when I have to write my own library to parse html.
4) Why not give me access to the testing framework from an external
application (like C#), so I could use real compiled code instead of a
script to specify a test.

Gripe.

The code doesn't quite work yet - I'm working out the bugs with finding
all of the form fields. I'll probably have it done in an hour or two
and then it will become easy to write tests.

But I will always be mad at the ACT community and MS for not thinking
of this first. And I think we all know that scripting in anger is the
path to the Dark Side. Perhaps I'll be happier once it works.

Michael McHenry

2005-01-30, 8:48 pm

// OK, so here's my testing function, once I got it working
// The specific test is just the first two functions.
// Everything else that follows are just the library functions
// that wish that they could be in a seperate file or class or
// namespace, but their stuck in a crummy script language.
// There are all kinds of reasons this code may not work:
// - I haven't implemented special handling html comments yet
// - I haven't implemented textareas yet.
//
// So the code isn't commented well yet. I'm waiting to see if there
// is any interest in it. It could also be modified to allow ACT
// to crawl a website.
// To contact me: mike.mchenry at clevermethod dot com

MainTest();

function MainTest() {
var serverName = 'localhost';
var useDelays = false;
var session = new SessionRequest(serverName, useDelays);

TestLogin(session);
}

function TestLogin(session) {
var response = session.GetPage('/FeiNet/profile/Login.aspx', 13110);
var postBack = new PostBack(session, response);
postBack.GetField('txtUserName').Value = 'testuser';
postBack.GetField('txtPassword').Value = 'testpassword';
postBack.GetField('btnLogon').Value = 'Logon';
response = postBack.Post('/FeiNet/profile/Login.aspx', 18719);
Test.Trace(response.Body);
}

// ****************************************
****************************
// Everything past this point is library code
// ****************************************
****************************

// Clases and functions needed to simulate postbacks
function PostBack(session, previousResponse) {
this.InitFields = PostBack_InitFields;

this.Session = session;
this.Response = previousResponse;
this.Body = null;
this.Fields = new Array();

this.InitFields();

function PostBack_InitFields() {
Test.Trace('Creating postback request for response to' +
previousResponse.Path);

var parser = new PageParser(previousResponse.Body);
var fieldTags = parser.TagSearch( new Array('input', 'textarea') );

for ( var tagIndex=0; tagIndex<fieldTags.length; tagIndex++) {
var tag = fieldTags[tagIndex];
var field = new Object();

var attributes = tag.Attributes;
for (var key in attributes) {
var att = attributes[key];
//Test.Trace('Attribute key:' + key + ' name:' + att.Name + '
value:' + att.Value);
}

var nameAtt = tag.Attributes['name'];
var valueAtt = tag.Attributes['value'];

field.Name = nameAtt.Value;
if ( valueAtt===undefined ) {
field.Value = '';
} else {
field.Value = valueAtt.Value;
}

//Test.Trace(previousResponse.Body.substr(tag.Offset, tag.Length));
//Test.Trace(tag.TagType + ' Postback field. Name: ' + field.Name +
' Value: ' + field.Value);

this.Fields[field.Name.toLowerCase()] = field;
}
};

this.Post = function(pageName, delay) {
return this.Session.PostPage(pageName, delay, this.GetBody());
};

this.GetBody = function() {
var body = '';
for (var key in this.Fields) {
var field = this.Fields[key];

//Test.Trace('Field key:' + key);
Test.Trace('Field key:' + key + ' name:' + field.Name + ' value:'
+ field.Value);

if ( body.length > 0 ) { body+= '&'; }
body+= field.Name + '=' + field.Value;
}
Test.Trace('Created Body: \n' + body);
return body;
};

this.GetField = function(fieldName) {
var lFieldName = fieldName.toLowerCase();
var field = this.Fields[lFieldName];
if (field===undefined) {
for (var fieldKey in this.Fields) {
var partialFieldKey = fieldKey.substr( fieldKey.length -
lFieldName.length );
//Test.Trace('Partial field Key: ' + partialFieldKey )
if ( partialFieldKey==lFieldName ) {
field = this.Fields[fieldKey];
break;
}
}
if (field===undefined) {
Test.Trace('Postback field not found, creating new: ' +
lFieldName);
field = new Object();
field.Name = lFieldName;
field.Value = '';
this.Fields[field.Name] = field;
}
}
return field;
};
}

function SessionRequest(serverName, useDelays) {
this.ServerName = serverName;
this.UseDelays = useDelays;
this.AcceptTypes = 'image/gif, image/x-xbitmap, image/jpeg,
image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel,
application/vnd.ms-powerpoint, application/msword, */*';
this.UseSsl = false;
this.ServerPort = 80;

this.StatusCode = null;

this.GetPage = function(pageName, delay) {
var response;

if (this.UseDelays) { Test.Sleep(delay); }
var connection = Test.CreateConnection(this.ServerName,
this.ServerPort, this.UseSsl);
if ( connection===undefined ) { // or is it undefined I should be
looking for?
Test.Trace('Error: Unable to connect to to host ' + this.ServerName
+ ':' + this.ServerPort);
} else {
var request = this.CreateRequest(pageName, 'GET');

Test.Trace('Getting page: ' + pageName);
var statusCode;
response = connection.Send(request);

Test.Trace('response returned: ' + pageName + ' FB: ' +
response.TTFB + ' LB: ' + response.TTLB);
if (response===undefined) {
Test.Trace('Error: Failed to receive response for URL to ' +
pageName);
} else {
this.StatusCode = response.ResultCode;
}

connection.Close();
}
return response;
};

this.PostPage = function(pageName, delay, body) {
var response;

if (this.UseDelays) { Test.Sleep(delay); }
var connection = Test.CreateConnection(this.ServerName,
this.ServerPort, this.UseSsl);
if ( connection===undefined ) { // or is it undefined I should be
looking for?
Test.Trace('Error: Unable to connect to to host ' + this.ServerName
+ ':' + this.ServerPort);
} else {
var request = this.CreateRequest(pageName, 'POST');
request.EncodeBody = false;
var headers = request.Headers;
headers.Add('Referer',
'http://localhost/FeiNet/profile/Login.aspx');
headers.Add('Content-Type', 'application/x-www-form-urlencoded');
headers.Add('Pragma', 'no-cache');
headers.Add('Content-Length', '(automatic)');

request.Body = body;

var statusCode;
response = connection.Send(request);
if (response===undefined) {
Test.Trace('Error: Failed to receive response for URL to ' +
pageName);
} else {
this.StatusCode = response.ResultCode;
}

connection.Close();
}
return response;
};

this.CreateRequest = function(pageName, verb) {
var request = Test.CreateRequest();
request.Path = pageName;
request.Verb = verb;
request.HTTPVersion = 'HTTP/1.0';
request.ResponseBufferSize = 500000;

var headers = request.Headers;
headers.RemoveAll();
headers.Add('Accept', this.AcceptTypes);
headers.Add('Accept-Language', 'en-us');
headers.Add('Cookie', '(automatic)');
headers.Add('User-Agent', 'Mozilla/4.0 (compatible; MSIE 6.0;
Windows NT 5.1; FunWebProducts-MyWay; .NET CLR 1.1.4322)');
headers.Add('Host', '(automatic)');
headers.Add('Cookie', '(automatic)');

return request;
};
}

// Classes and functions needed to parse values out of the html page.
function PageParser(pageBody) {
this.pageBody = pageBody;

this.TagSearch = function(tagArray) {
var pageBody = this.pageBody;
var parsePos = 0;
var doneParsing = false;

var foundTags = new Array();


for (var lowerInd=0; lowerInd<tagArray.length; lowerInd++) {
tagArray[lowerInd] = tagArray[lowerInd].toLowerCase();
}

while (!doneParsing) {
var tagPos = pageBody.indexOf("<", parsePos);
if (tagPos<0) {
doneParsing = true;
} else {
var tag = new HtmlTag(pageBody, tagPos);
/*Test.Trace( 'Tag found at ' + tagPos + '. Length: ' + tag.Length
+ '\n('
+ pageBody.substr(tagPos, tag.Length) + ')' );*/
parsePos= tagPos + tag.Length;

for (var searchIndex=0; searchIndex<tagArray.length; searchIndex++)
{
if ( tagArray[searchIndex]==tag.TagType.toLowerCase() ) {
foundTags.push(tag);
}
}
}
}
return foundTags;
};
}

function HtmlTag(pageBody, tagOffset) {
this.ParseTag = HtmlTag_ParseTag;

this.Attributes = new Array();
this.TagType = '';
this.Offset = tagOffset;
this.Length = 0;
this.ParseTag(pageBody, tagOffset);

function HtmlTag_ParseTag(pageBody, tagOffset) {
var tagPos = 1;
var tagEnded = false;

while (!tagEnded) {
var att = new Attribute(pageBody, tagOffset + tagPos);
if (tagPos==1) {
this.TagType = att.Name;
//Test.Trace('Tag Name: ' + att.Name);
} else {
//this.Attributes.push(att);
this.Attributes[att.Name.toLowerCase()] = att;
//Test.Trace(' Attribute:' + att.Name + ' value="' + att.Value +
'"');
}
tagPos+= att.Length;
tagEnded = att.TagEnded;
}

this.Length = tagPos;
};

function Attribute(pageBody, attOffset) {
this.Name = '';
this.Value = null;
this.Offset = attOffset;
this.TagEnded = false;
this.Length = 0;

var attDone = false;
var attPos = 0;
var isCollectingName = true;
var isExpectingValue = false;
var isCollectingValue = false;
var isValueCollected = false;
var valueDelimiter = '';
var WhiteSpace = ' \t\n';

var isParsingQuote = false;
while (!attDone) {
var attChar = pageBody.substr(attOffset + attPos, 1);
attPos++;
var isWhiteSpace = false;
if ( WhiteSpace.indexOf(attChar) > -1 ) { isWhiteSpace = true; }

/*Test.Trace('Attribute character: ' + attChar
+ ' CN-' + isCollectingName
+ ' CV-' + isCollectingValue
+ ' EV-' + isExpectingValue
+ ' VC-' + isValueCollected
+ ' PQ-' + isParsingQuote
+ ' WS-' + isWhiteSpace);
*/

if ( (!isParsingQuote && attChar==">") || attOffset+attPos >
pageBody.length ) {
attDone = true;
this.TagEnded = true;
} else if ( isCollectingName ) {
if ( isWhiteSpace ) {
isCollectingName = false;
} else if ( attChar=='=' ) {
isCollectingName = false;
isExpectingValue = true;
} else {
this.Name+= attChar;
}
} else if ( isCollectingValue ) {
if ( !isParsingQuote ) {
// Check for end of non-delimited value
if ( isWhiteSpace ) { isCollectingValue = false; }
} else {
if (attChar==valueDelimiter) { // Possible end of delimited value
- check for doubled delimiter
attChar = pageBody.substr(attOffset + attPos, 1);
if ( attChar==valueDelimiter ) { // Double escaped
attPos++;
} else { // Single delimiter - end of value
isCollectingValue = false;
isParsingQuote = false;
}
}
}
if ( isCollectingValue ) { this.Value+= attChar; }
else { isValueCollected=true; }
} else if ( isExpectingValue ) {
if ( !isWhiteSpace ) { // must be start of value or quote
if ( attChar=='"' || attChar=="'" ) {
isParsingQuote = true;
valueDelimiter = attChar;
isCollectingValue = true;
isExpectingValue = false;
this.Value = '';
} else {
isParsingQuote = false;
isCollectingValue = true;
isExpectingValue = false;
this.Value = '';
}
}
} else {
// Not collecting name or value, not expecting value
if ( !isValueCollected && attChar=="=") {
isExpectingValue = true;
} else if ( !isWhiteSpace ) {
// this is the start of the next attribute
// so end this attribute
attDone = true;
attPos--;
}
}
}

//Test.Trace('TagEnded = ' + this.TagEnded);
this.Length = attPos;

}
}

Michael McHenry

2005-01-30, 8:48 pm

Oh, my. All of the tabs were stripped out.

Well, I'll have to find a better place to put the script once it's seen
some more improvement.

Also, the script is JScript, not vbs - save it with a .js extension and
it will work fine.

Michael McHenry

2005-01-31, 8:49 pm

I've fixed most of the issues I listed above. The script now is over
500 lines long, but adding a page test only takes a few lines.

I'll probably post this at CodeProject after a few more days of testing.

FriedTurkey

2005-02-22, 2:58 am

quote:

This is pretty easy to do in NUnitAsp, but it doesn't collect the
performance data I need.



Try Browser.ElapsedServerTime.
Sponsored Links






Free braindumps | Software forum | Database administration forum

Copyright 2003 - 2008 webservertalk.com