Retrieve Browser Log using Selenium RC

Posted on Friday, August 26, 2011

I wasn't able to locate anything that natively allowed me to save the browser log being written to during the test.

I included the following javascript as a user extension, which hooks into Selenium's browser logging functionality:

You can then retrieve the log text as a string at the end of your test using GetEval

I go a step further and utilize TestContext.AddResultFile(String filename), included in Visual Studio 2010's testing suite, to neatly attach the browser log to the test result:

Labels: , ,


 

Selenium IE9 WaitForPageToLoad Fix

There appears to be an issue in Selenium that causes WaitForPageToLoad to act improperly in IE9.

To resolve the issue copy the following code into a file called "user-extensions.js":

Reference that file when you start the Selenium server using the "-userExtensions" option:

java -jar selenium-server-standalone-2.4.0.jar -userExtensions user-extensions.js

Now for the explanation...

I noticed that when I experienced the WaitForPageToLoad timeout, the browser logs showed an unusual error message coming from IEBrowserBot.windowclosed:

debug(1314123427103): _isSamePage: sameLoc: true
debug(1314123427103): _isSamePage: sameHref: true
debug(1314123427103): _isSamePage: markedLoc: true
debug(1314123427824): replaced getReadyState used
debug(1314123427824): pageUnloading = true!!!!
debug(1314123427824): getReadyState returning loading
debug(1314123427824): pollForLoad continue (selenium1314123422376): undefined
debug(1314123427824): runScheduledPollers DONE
debug(1314123427843): runScheduledPollers
debug(1314123427843): IEBrowserBot.pollForLoad: selenium1314123422376
debug(1314123427843): pollForLoad original (selenium1314123422376): http://localhost:8000/weblo/Gfe.aspx
debug(1314123427899): IEBrowserBot.windowClosed: couldn't read win.document, assume closed:  (this.pageUnloading=true)
debug(1314123427899): pollForLoad WINDOW CLOSED (selenium1314123422376)
debug(1314123427899): runScheduledPollers DONE
debug(1314123427900): runScheduledPollers
debug(1314123427900): runScheduledPollers DONE
debug(1314123427919): runScheduledPollers
debug(1314123427919): runScheduledPollers DONE

Looking at browser-bot.js, I noticed that the condition with the logged error "couldn't read win.document, assume closed" is the only one that returns true:

I changed that to return false and included the entire function as a user extension, thus overwriting the original:

Since then, I have run over a thousand tests (on IE9) and not one has faltered because of WaitForPageToLoad.

Labels: , , , , , ,


 

Obtaining IIS logs for specific sites programmatically

Posted on Saturday, April 4, 2009

The company I work for switched to a different system for web analytics which did not support drawing web log data from multiple servers therefore it became necessary to copy IIS web logs to a central location with the site name as the folder name. Since a site's log directory name is composed of "W3SVC" + site id, if we can obtain the site id we can link a specific site to its log folder. After a little research I found that this could be done using System.DirectoryServices which, among other things, gives access to IIS's metadata thus providing the link between site name and site id. The following code iterates through the sites in IIS and copies their logs to a folder named with the host header in some destination directory:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Configuration;
using System.DirectoryServices;
namespace DirectoryServicesExample
{
 class Program
 {
  static String logPath = @"WINDOWS\system32\LogFiles";
  static void Main(string[] args)
  {
   //Get the metadata for the instance of IIS running on the current computer
   DirectoryEntry iis = new DirectoryEntry("IIS://localhost/w3svc");
   
   //Iterate through the children of the IIS directory entry
   foreach(DirectoryEntry site in iis.Children)
   {
    //Check to make sure that the child is a site. IIS websites have a schema class name of IISWebServer
    if(site.SchemaClassName == "IIsWebServer")
    {
     //Retrieve the comment property of the site
     String comment = (String)site.Properties["ServerComment"].Value;
     
     //Creates the path to the current site's log file directory. site.Name is the site's id
     String sourcePath = Path.Combine(Path.Combine(@"C:\", logPath), "W3SVC" + site.Name);
     String hostHeader = null;

     /*Retrieves the site's host header/port bindings. If there is more than one binding, then
      * site.Properties["ServerBindings"].Value returns an object array of binding strings
      * (:80:host.header.com)*/
     String serverBinding = (site.Properties["ServerBindings"].Value as String);
     if(!String.IsNullOrEmpty(serverBinding))
     {
      String[] bindingParts = serverBinding.Split(':');
      if(bindingParts.Length == 3)
       hostHeader = bindingParts[2].Replace('.', '_');
     }
     if(!String.IsNullOrEmpty(hostHeader))
     {
      String destinationPath = Path.Combine(ConfigurationSettings.AppSettings["DestinationDirectoryPath"], hostHeader);
      if(!Directory.Exists(destinationPath))
       Directory.CreateDirectory(destinationPath);
      FileInfo[] logFiles = new DirectoryInfo(sourcePath).GetFiles();
      foreach(FileInfo logFile in logFiles)
       logFile.CopyTo(Path.Combine(destinationPath, logFile.Name));
     }
    }
   }
  }
 }
}


 

Silverlight: Solution to System.ExecutionEngineException

Posted on Wednesday, December 31, 2008

Today I ran into the charming System.ExecutionEngineException while trying to display a JavaScript alert using HtmlPage.Window.Alert(message). I was able to correlate the throwing of the exception to when I tried to display the alert in the handler of a KeyDown event leading me to suspect a threading issue. The solution I discovered was to asynchronously call the HtmlPage.Window.Alert method from the main thread using the Dispatcher.BeginInvoke() method. Specifically:
((Page)Application.Current.RootVisual).Dispatcher.BeginInvoke(new Action<String>(HtmlPage.Window.Alert), message);

 

Sql Server: Delete/Remove/Clean Up Backup History Script

Posted on Monday, December 29, 2008

If you have suddenly discovered that your msdb database is gigabytes large due to the storage of backup/restore history, you may be tempted to use sp_delete_backuphistory to trim down the history. Do not be tempted! sp_delete_backuphistory is poorly written and will often take days to finish if you have not or seldom maintain your backup history. After seeing logs indicating that sp_delete_backuphistory had run for 100+ hours before ultimately failing, I put together a backup history maintenance script that cleaned up the million plus rows in each of the backup history tables in just over an hour. Also, feel free to try the other alternative sp_delete_backuphistory script that I found.
declare @DaysToRetain INT
set @DaysToRetain = 7

SET NOCOUNT ON

--delete restorefile
delete restorefile from restorehistory rh
join restorefile rf on rh.restore_history_id = rh.restore_history_id
where rh.restore_date < GetDate()- @DaysToRetain

--delete restorefilegroup
delete restorefilegroup from restorehistory rh
join restorefilegroup rfg on rfg.restore_history_id = rh.restore_history_id
where rh.restore_date < GetDate()- @DaysToRetain

--delete restorehistory
delete restorehistory 
where restore_date < GetDate()- @DaysToRetain

--delete backupfile
delete backupfile from backupset bs
join backupfile bf on bf.backup_set_id = bs.backup_set_id
where bs.backup_finish_date < GetDate()- @DaysToRetain

--delete backupset
delete backupset
where backup_finish_date < GetDate()- @DaysToRetain

--delete backupmediafamily
delete backupmediafamily from backupmediaset bms 
left join backupset bs on bms.media_set_id = bs.media_set_id
join backupmediafamily bmf on bmf.media_set_id = bms.media_set_id
where bs.backup_set_id is null

--delete backupmediaset
delete backupmediaset from backupmediaset bms 
left join backupset bs on bms.media_set_id = bs.media_set_id
where bs.backup_set_id is null

SET NOCOUNT OFF

 

Multiple submit buttons on a .NET page

Posted on Wednesday, December 24, 2008

Since .NET pages can only have one managed <form> element, some problems arise when pressing enter to submit a login request on a page with a search bar and a login form. Since the ASP button controls convert to submit html inputs, the browser can not determine that because the user hit enter after typing in their password in the login form, that the submit button for the login form should be pressed instead of the search button for the search bar. One solution to this problem is to make the search bar using actual html elements instead of ASP controls and then use javascript to grab the search query and put it into a get request made by changing window.location. My solution was to whip up some javascript to determine the correct submit button to press using proximity of the text field that had focus when the enter key was pressed to the submit button using the DOM model of the page. Here is what I came up with:
<script type="javascript">
// SubmitManager
var activeObject = "";
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
function FindSubmitButton(element,down)
{
var found = false;
if(element.hasChildNodes()){
for(var i in element.childNodes){
if(element.childNodes[i].nodeName != undefined){
if(element.childNodes[i].hasChildNodes()){
found = FindSubmitButton(element.childNodes[i], true);
if(found){
break;
}
}
else{
if(element.childNodes[i].type == "submit"){
found = element.childNodes[i];
break;
}
}
}
}
}
if(!down){
if(found == false){
if(element.nodeName != "#document"){
return FindSubmitButton(element.parentNode, false);
}
else{
return null;
}
}
else{
return found;
}
}
else{
return found
}
}
function ManageKeyPress(e)
{
var code;
var allow = true;
if (!e) var e = window.event;
if (e.keyCode) code = e.keyCode;
else if (e.which) code = e.which;
if(code == 13){
allow = false;
var button = FindSubmitButton(activeObject, false);
if(button != null){
button.click();
}
}
return allow;
}

function InitializeManager(){
elements = document.getElementsByTagName("INPUT");
for(var i in elements){
if(elements[i].type == "text"){
elements[i].onfocus = function(){
activeObject = this;
}
elements[i].onkeypress = ManageKeyPress;
}
}
}
addLoadEvent(InitializeManager);
</script>
The SubmitManager can be included right into an existing page without any extra work, but it would probably be best to put the code into a .js file and then include it that way:
<script type="javascript" src="SubmitManager.js"></script>