Wednesday, May 9, 2012

Using Seleniun WebDriver with Windows and .NET

Wanting to write a C# program to do some automated functional testing in multiple browsers, I downloaded Selenium WebDriver version 2.21.0.

I also downloaded the Selenium Client Drivers for C#. At the time I first downloaded these, the version was 2.16.0.

I proceeded to create a Selenium hello-world program by following the C# example provided here. With all the comments and fluff stripped out, my program looked something like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;

namespace WebDriver1
{
    class Program
    {
        static void Main(string[] args)
        {
            IWebDriver driver = new FirefoxDriver();
            driver.Navigate().GoToUrl("
http://www.google.com/");
            IWebElement query = driver.FindElement(By.Name("q"));
            query.SendKeys("Cheese");
            query.Submit();
            WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
            wait.Until((d) => { return d.Title.ToLower().StartsWith("cheese"); });
            System.Console.WriteLine("Page title is: " + driver.Title);
            driver.Quit();
        }
    }
}

One note right off the bat: The example code at seleniumhq.com doesn't include using OpenQA.Selenium.Support.UI. This is needed, or else WebDriverWait can't be resolved.

When I tried to run this program, it threw an exception: OpenQA.Selenium.WebDriverException : Failed to start up socket within 45000. I found some stuff about this online, but none of it helped. What solved the problem was downloading the latest version, 2.21.0, of the Selenium Client Drivers.

With those two problems solved, the program compiled and ran, launching Firefox, doing a Google search for "cheese," and outputting the page title. Pretty cool!

Next, I wanted to do the same thing using IE.

The first stumbling block was when I added this line to my code: driver = new InternetExplorerDriver();

This class wasn't found. Easily remedied by adding this as well: using OpenQA.Selenium.IE;

My whole program now looked like this, executing a loop twice, once for each browser:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.IE;
using OpenQA.Selenium.Support.UI;

namespace WebDriver1
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 2; i++)
            {
                string browserName = "";
                IWebDriver driver = null;
                switch (i)
                {
                    case 0:
                        browserName = "FireFox";
                        driver = new FirefoxDriver();
                        break;
                    case 1:
                        browserName = "IE";
                        driver = new InternetExplorerDriver();
                        break;
                }
                driver.Navigate().GoToUrl("
http://www.google.com/");
                IWebElement query = driver.FindElement(By.Name("q"));
                query.SendKeys("Cheese");
                query.Submit();
                WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
                wait.Until((d) => { return d.Title.ToLower().StartsWith("cheese"); });
                System.Console.WriteLine(browserName + ": Page title is: " + driver.Title);
                driver.Quit();
            }
        }
    }
}

This worked for Firefox but threw an exception when it opened Internet Explorer.
System.InvalidOperationException: Unexpected error launching Internet Explorer. Protected Mode must be set to the same value (enabled or disabled) for all zones. (NoSuchDriver)

Surprisingly, this meant exactly what it said, and was corrected by opening IE, selecting Tools | Internet Options | Security, clicking on all four security zones (Internet, Local Intranet, Trusted Sites and Restricted), ensuring the Enable Protected Mode checkbox was checked for each zone, and restarting IE. (Unchecking the checkbox would work, too. The point is that the setting for all four zones must be the same.) A huge tip of the hat to Tom Dupont for that.

With IE's Protected Mode settings properly configured, the above program ran successfully, automating both browsers.

Friday, April 6, 2012

Database trigger can cause problems with @@IDENTITY

My .NET web application with a SQL Server back end was working fine, until...

I tested a feature that added a new record to one of the database tables, and I got an exception saying that a primary key constraint had been violated.

Using SQL Server Profiler, I was able to isolate the stored procedure call that violated the constraint.

Examining the SQL code in that sproc, I saw something similar to this:
DECLARE @newID INT
INSERT INTO Table1 (Foo, Goo) VALUES ('abc', 'def')
SET @newID = @@IDENTITY
INSERT INTO Table2 (Bar, Car) VALUES (@newID, 'ghi')

The problem was that someone had recently added a trigger on Table1. The trigger fired upon inserting a row into Table1, and executed some code that did an insert into some other table, changing the value of @@IDENTITY. The new value of @@IDENTITY happened to be one that was already in use as the primary key of a row in Table2, violating the constraint.

The fix was simple: use SCOPE_IDENTITY() in place of @@IDENTITY. Thanks to this article for that tip.

Tuesday, December 27, 2011

DOS command-line tricks

Remember DOS? It's still with us, in the form of the Windows command prompt, and it still has some useful tricks up its sleeve. I recently needed to automate the daily copying of some database backup files from one server to another. I combined several DOS, Windows and Windows task schedule techniques to make this work.

The goal: At 7:30 AM each day, copy all files named ynot_backup_*.bak from the folder G:\sqlbackup on the database server to the folder G:\ynot_backup on one of the web servers. Both servers are running Windows Server 2003.

First, I came up with an xcopy command to copy the appropriate files: xcopy /Y g:\SQLBackup\ynot_backup_*.bak \\10.2.66.30\g$\ynot_backup.A few points of interest:

  • This command uses a UNC path (a path starting with two backslashes) to access the web server across the LAN. Specifically, \\10.2.66.30\g$ refers to the default share of the G: drive on the web server, and I've configured the permissions, both in the share and in the file system -- to give write access to authenticated users. (This is safe, because the share is accessible only to authenticated users using a non-routable private IP address on a local area network.)
  • The /Y flag tells xcopy to overwrite any existing files without prompting the user for confirmation.


The above xcopy command works fine when executed in a DOS window on the database server. But to be able to schedule it, two changes are needed.

  • First, we need to give the full path to xcopy.
  • Second, we need to execute the command shell, cmd.exe, passing it the /c flag and the command to execute.
It ends up looking like this: C:\WINDOWS\system32\cmd.exe /c "xcopy /Y g:\SQLBackup\ynot_backup_*.bak \\10.2.66.30\g$\ynot_backup".

The final step is to go to Control Panel | Scheduled Tasks and create a new scheduled task to execute the above command at 7:30 AM each day. The only trick here is choosing an appropriate account under which to run the task. I chose an administrator account that has sufficient permissions and whose password doesn't change too frequently -- because each time the password changes, we must inform the scheduled task of the new password.

Now all I need to do is keep an eye on the web server and make sure the whole G: drive doesn't fill up with old database backups!

Friday, November 25, 2011

How to simulate any browser by editing the user agent string in Safari

I recently modified one of my websites to display different content depending on which device is used to access it. For conventional web browsers, a traditional homepage is displayed, while for mobile devices the user can choose between downloading a mobile app or proceeding to the website. The question was how to test this without borrowing several different types of mobile devices and installing several web browsers. Safari provides a convenient way. Here's the procedure, using Safari 5.1.1 on Windows...

If the "Develop" menu isn't already enabled, click the "Tools" icon at the top right and select "Preferences..." from the resulting menu. On the "Advanced" tab, ensure that "Show Develop menu in menu bar" is selected.

Now use the "Develop" menu -- either by selecting the icon just to the left of the tools icon, or by pressing the Alt key to display the menu bar across the top of the window. On the "Develop" menu, click "User Agent".

You'll be presented with a list of several user agent strings to choose from, simulating popular browsers. Better still, you can click "Other..." and type in any user agent string you wish.

Safari uses the specified user agent string for the current page. The setting appears to persist for any page viewed in the current tab, but doesn't apply in new tabs, new windows, or after your close the browser.

Thanks to my colleague Nidhi Bhargava for researching this tip.


Monday, November 7, 2011

Notes from a WordPress newbie

So my day job involves a lot of .NET and SQL, and I've worked with dozens of programming languages and software packages during my career, but I've never done anything with WordPress. Thanks to a volunteer project for the Youth Orchestra of Essex County, that's about to change. I've been asked to maintain some content on their website, which uses WordPress.

One problem I noticed was with the menu at the top of the site. It worked fine in Chrome, but in Internet Explorer 9, when I hovered the mouse over a menu tab, and the submenu was displayed, a small vertical gap appeared between menu and submenu. When passing the mouse over this gap to try to select a submenu item, the submenu disappeared. Very annoying!

I posted this problem to the WordPress forum, and got a response within hours. (Thanks, vtxyzzy!) The response said:
Try adding this to the end of style.css:
ul.children { top: 24px !important }


So I had to figure out how do to that, having never used WordPress before. Here's what I did...

  • Log in to the admin page for yoec.org, yoec.org/wp-admin.
  • There's a menu of links on the left. One of them is Appearance. I clicked that. and it expanded.
  • This displayed a menu of links related to Themes. One link was Editor. I clicked that.
  • This displayed a text editor pane in the middle of the screen, and a list of files on the right.
  • I scrolled through the list of files, found a category labeled Styles, and under that, a file labeled Stylesheet (style.css).
  • I clicked that filename, and the file was displayed in the text editor pane.
  • I scrolled through the (rather long) text of style.css until I reached the bottom.
  • I appended the recommended line, ul.children { top: 24px !important }.
  • I saved my changes by clicking the Update File button at the bottom of the page.
  • I viewed the website in IE9, and the problem was gone!
I will probably have more to say about WordPress, from a total noob perspective, in a future post.

Friday, November 4, 2011

TFS command-line interface - unlocking files locked by other developers

One of the developers who works for me went to work on a project in Visual Studio, which is under source control in Team Foundation Server. He discovered that some files were locked for editing by another user, so he couldn't complete his task. The other user was an intern who no longer worked for us, and couldn't be reached to correct the problem. I used TFS's command-line utility, tf.exe, to unlock the files.

First I determined which files were locked, and by whom. I did this using Visual Studio 2010 on the laptop I use for software development. (My installation of Visual Studio includes Visual Studio 2010 Team Explorer.) From the Team tab, I opened the Source Control window. Looking in the Pending Change column and the User column, I could see which files were locked for edit, and by which users.

Then I used Remote Desktop to connect to the server on which TFS is hosted.

I opened a Visual Studio command prompt. On my server, this is accomplished by selecting Start | All Programs | Microsoft Visual Studio 2010 | Visual Studio Tools | Visual Studio Command Prompt (2010).

At the command prompt, I used a command similar to the one below to unlock each file.

tf undo "$/LocalUp/LocalupMenus/CMSBackEnd/bin/CMS.BackEnd.ClassLibrary.dll" /WORKSPACE:INTERN2-PC;mbakiev /server:http://10.2.66.30:8080/tfs/defaultcollection

There are three pieces of information you need to construct such a command.

First, in red, is the name -- as TFS understands it -- of the file to unlock. You can get this by right-clicking the file in Source Control Explorer and selecting Properties. On the General tab, look for the Server Name.

Second, in blue, is the name of the workspace of the user who has locked the file. Get this by right-clicking the file and selecting the Status tab. There you can see the name of the user's workspaceFor example, one of my developers' workspaces is named INTERN2-PC;mbakiev.

Third, in green, is the name -- as TFS understands it -- of the server. To determine this, I clicked the top node of the source tree in the Team Explorer window. Note this has to be in the small Team Explorer pane, not the big Source Control Explorer pane. Then the Properties window will display various information, including the "Url". That's the value -- http://10.2.66.30:8080/tfs/defaultcollection in my case -- you need to supply as the server name.

Not simple, but it worked!

A tip of the hat to this helpful article.

Friday, October 7, 2011

Working with a dynamic RadioButtonList

ASP.NET provides a RadioButtonList control, which has the useful ability to bind text and values (perhaps, for example, retrieved from a database) to a set of radio buttons at runtime. You might find the need to examine and manipulate attributes of the radio buttons in the list, even though you don't know in advance what those radio buttons may be. Here's an example of how to do that.

In my example, there's a list of radio buttons. The list may (or may not) include a radio button labeled "Countdown". If the list includes a "Countdown" radio button, then I want to disable it. Furthermore, if "Countdown" was selected, I want to instead select the first radio button in the list. Here's the code:

foreach (ListItem listItem in rblSelectionType.Items)
{
if (listItem.Text == "Countdown")
{
if (listItem.Selected)
rblSelectionType.SelectedIndex = 0;
listItem.Enabled = false;
break;
}
}

Thanks to this tutorial for setting me on the right track.