ArtOfTest

Skip Navigation Links

Feed from our Blogs

Automating Real World Silverlight Apps.

As promised I will dedicate this post to talk more about Silverlight automation using WebAii in more depth. To help illustrate the power of WebAii, I decided to pick a real world application rather than a “Hello World”/”Calculator” type of application. This will hopefully make this post more relevant to what testers might face as more application developers adopt Silverlight.

The application I chose is a Health Care application built by Microsoft’s Health Common User Interface (CUI). The application is live and can be viewed here. The application shows off the capabilities of Silverlight and gives us a glimpse into what we might expect from the next generation of web applications built using Silverlight. 

Let’s start by dissecting some aspects of that application that a tester might be interested in testing. I will then demonstrate how some of these areas can be automated using WebAii.

As a tester I might be interested in testing the following areas of the application:

Note: I picked these areas for the purpose of this post. It does not mean only these areas need to be tested for such an application. The goal for this post is to demonstrate WebAii’s capabilities rather than actually fully testing this application. 

1. Test the Guidance UI. [Show/Hide Guidance] and validate its data:

image

2. Validate the search functionality of the application

image

3. Test the data zoom-in/zoom-out of the search results

image

UI manipulation/positioning which will include:

4. Scrolling – Manipulate ScrollViewers and querying scroll positions.

5. Drag/Drop of UI elements and re-positioning them + validation.

6. Sync tests with UI elements in motion.

image

Now that we have a better idea of what we plan to test, let’s build some automation.

 Note: If you are new to WebAii’s Silverlight automation, I encourage you to read our last blog post which introduces you to it using a simple application.

Also, some of the feature we are using are not in the current 2.0 beta. We plan to publish a Beta2 for 2.0 soon which will include everything we will show in this post.

Initialization

All our tests need to do the same initialization: Launch a browser, navigate to the page and initialize an instance of SilverlightApp object.

 1: // Launch browser
 2: Manager.LaunchNewBrowser(BrowserType.InternetExplorer);
 3:  
 4: // Navigate to our app
 5: ActiveBrowser.NavigateTo("http://www.mscui.net/PatientJourneyDemonstrator/PrimaryCareAdmin.htm");
 6:  
 7: // Get an instance of the running Silverlight Application.
 8: SilverlightApp app = ActiveBrowser.SilverlightApps()[0];

Task #1: Validate that clicking on the “Show Guide” button shows the guide and validate that it shows 15 guidance tooltips. Click the button again, and validate that it hides the guide.

 1: //
 2: // Verify Show/Hide of the Guide works
 3: //
 4:  
 5: // Click the ShowGuide button.
 6: app.FindName("guideButton").User.Click();
 7:  
 8: //
 9: // Wait for the guidance to be fully visible.
 10: //
 11: // In this application they use the Opacity of the
 12: // "GuidanceCanvas" to show/hide the guidance.
 13: Canvas guidanceCanvas = app.FindName<Canvas>("GuidanceCanvas");
 14: guidanceCanvas.Wait.For(canvas => canvas.Opacity == 1);
 15:  
 16: guidanceCanvas.Refresh();
 17:  
 18: // Make sure we got the correct # of guidance popups
 19: // The application pops 15 overlays on top
 20: IList<FrameworkElement> guidanceOverlays = guidanceCanvas.Find.AllByType("GuidanceOverlay");
 21: Assert.IsTrue(guidanceOverlays.Count == 15);
 22:  
 23: // Now hide the guidance
 24: app.FindName("guideButton").User.Click();
 25: guidanceCanvas.Wait.For(canvas => canvas.Opacity == 0);
Notes:
  1. Line #14: The Framework.Wait.For() is a great utility that allows you to wait on any property on the FrameworkElement object.
  2. Line #20: Scoped searches. Every FrameworkElement, has a Find object associated with it an is scope to only search within that element’s children in the Visual Tree. This can help you avoid any name conflicts (Which is very common in these rich applications) and makes your test automation much less prone to breaking and much easier to debug for failures.
  3. Line #21, you can perform more verification for each popup in the guidanceOverlays list. You can verify the text, size or any other information.

Task #2:  Perform a search to test the search functionality. Input text in the search box, validate the number of search results you expect. 

 1: //
 2: // Let's test the search functionality
 3: // 
 4:  
 5: app.VisualTree.Refresh();
 6:  
 7: // Find the Patient Search TextBox
 8: TextBox searchText = app.FindName<TextBox>("searchText");
 9:  
 10: // Move the mouse over the search box until we have an IBeam.
 11: searchText.User.DetectHotSpot(100, System.Windows.Forms.Cursors.IBeam);
 12:  
 13: // Make sure the navigation bar is hidden before trying to type.
 14: // Currently it hides half the text box. 
 15:  
 16: // The Grid uses a TranslateTransform to hide/shotthe top bit
 17: app.FindName<Grid>("navBarGrid").Wait.For(
 18:     r => (r.RenderTransform as TranslateTransform).Y == -50);
 19:  
 20: // Now we can start typing.
 21: searchText.User.TypeText("A", 100);
Notes:
  1. Line #11: Not how we are able to move the mouse to the location exactly just like they user. The DetectHotSpot function allows you to search a certain radius from the center point of the FrameworkElement in a spiral motion until a certain hot spot is detected. A hot spot is detected when the mouse cursor changes from its current state or when the defined state by the function is detected. In this case we want to detect the IBeam cursor for the TextBox. Other application for this could be a certain resize handle for a window or a dynamic Thumb that you want to lock on and you can’t easily find using absolute or even relative X/Y coordinates.

  2. Line #17: This is very powerful. Given that we have access to even complex properties of the FrameworkElement, we are able to perform wait on things like RenderTransform objects. In this case we are waiting for the “TranslateTransform” for this element to move –50 in Y direction. This is very important when trying to sync your tests with certain animation points of the application.

Now that we have typed “A” in the search box. Let’s validate the list of search results:

 1: ///
 2: /// Validate the search results.
 3: ///
 4:  
 5: // Get the search lists. 
 6: ItemsControl searchList = app.FindName<ItemsControl>("patientSearchList");
 7:  
 8: // Get the number of results in the search. 
 9: // 
 10: // OBSERVER: WebAii can search custom types too not available
 11: // in ArtOfTest.WebAii.Silverlight.UI namespace. All the Find.xx methods have a non-generic
 12: // overload too that takes in a control type name.
 13: //
 14: IList<FrameworkElement> foundPatients = 
 15:     searchList.Find.AllByType("patientsearchitem").Where(
 16:     fx => fx.Visibility == Visibility.Visible).ToList();
 17:  
 18: // Validate the search
 19: Assert.IsTrue(foundPatients.Count == 95);
 Notes:
  1. Line #14. The health care application we are using utilizes some custom controls not defined in our Silverlight.UI namespace. All our Find.By/Find.AllBy routines have a none generic companion method for every generic method which allows users to also perform searches against certain control types. In this case, we are searching for the “patientsearchitem” control.
  2. Line #19: Again, you can look through each item in the “foundPatients” list and validate more data about each found patient. We will actually demonstrate some of how to do that in our next part.

Task #3: With the search results open, validate that details zoom-in/zoom-out shows more/less data about each patient. 

 1: ///
 2: /// Verify the zoom in/zoom out functionality.
 3: ///
 4: FrameworkElement zoomBox = app.FindName("zoomBox");
 5:  
 6: // Inside of it, find the plus part
 7: Button plusButton = zoomBox.Find.ByName<Button>("~PlusButton");
 8:  
 9: // Let's grab one of the patients to verify the zoom functionality on.
 10: FrameworkElement patientSearchItem = foundPatients.First();
 11: FrameworkElement patientAddress = patientSearchItem.Find.ByName("Address");
 12: FrameworkElement patientContactIcons = patientSearchItem.Find.ByName("ContactDetailsIcons");
 13: FrameworkElement patientAdditionalInfo = patientSearchItem.Find.ByName("ContactDetailsIcons");
 14:  
 15:  
 16: // Level 0 - Address & ContactDetails are not visible
 17: Assert.IsTrue(patientAddress.Visibility == Visibility.Collapsed);
 18: Assert.IsTrue(patientContactIcons.Visibility == Visibility.Collapsed);
 19: Assert.IsTrue(patientAdditionalInfo.Visibility == Visibility.Collapsed);
 20:  
 21: // Zoom level #1
 22: plusButton.User.Click();
 23:  
 24: // Level 0 - Address & ContactDetails is visible. Additional info is not visible.
 25: Assert.IsTrue(patientAddress.Visibility == Visibility.Visible);
 26: Assert.IsTrue(patientContactIcons.Visibility == Visibility.Visible);
 27: Assert.IsTrue(patientAdditionalInfo.Visibility == Visibility.Collapsed);
 28:  
 29: // Zoom level #2
 30: plusButton.User.Click();
 31:  
 32: // Level 0 - Address + additional info is visible only.
 33: Assert.IsTrue(patientAddress.Visibility == Visibility.Visible);
 34: Assert.IsTrue(patientContactIcons.Visibility == Visibility.Collapsed);
 35: Assert.IsTrue(patientAdditionalInfo.Visibility == Visibility.Visible);
Notes: 

In this scenario here we opted to simply use FrameworkElement and not any strongly typed object since most of our validations are against the Visibility property which is on the base FrameworkElement.

Task #4. Scroll a certain container and validate the scroll position.

 1: //
 2: // Scroll the search results
 3: //
 4:  
 5: // Find the scroll viewer
 6: ScrollViewer searchScroll = app.FindName("patientSearchScroller").Find.ByType<ScrollViewer>();
 7:  
 8: AutomationMethod scrollVert = new AutomationMethod("ScrollToVerticalOffset", null);
 9: searchScroll.Invoke(scrollVert, 2000);
 10:  
 11: // Assert scrolling position.
 12: Assert.IsTrue(searchScroll.VerticalOffset == 2000);
 13:  
 14: //
 15: // Close search results
 16: //
 17: app.FindName("clearSearch").User.Click();
Notes:

This scenario is more to demonstrate how you can manipulate scrolling/scrollviewers with WebAii.

Line #8: Note how we are able to invoke any method + params on any object. If you have custom control, you can easily use this methodology to invoke any method on them you might have defined.

Task #5: Perform UI element drag/drop and validate the correct positioning after the moves.

 1: //
 2: // Re-arrange admins UI
 3: //
 4: Grid adminsPanels = app.FindName<Grid>("adminPanels");
 5:  
 6: IList<FrameworkElement> admins = adminsPanels.Find.AllByName("~adminDockPanel");
 7:  
 8: FrameworkElement admin1 = admins.Where(adm => adm.Name.Equals("adminDockPanel1")).First();
 9: FrameworkElement admin2 = admins.Where(adm => adm.Name.Equals("adminDockPanel2")).First();
 10: FrameworkElement admin3 = admins.Where(adm => adm.Name.Equals("adminDockPanel3")).First();
 11:  
 12: // Get locations before the move
 13: System.Drawing.Rectangle admin1Loc = admin1.GetScreenRectangle();
 14: System.Drawing.Rectangle admin2Loc = admin2.GetScreenRectangle();
 15: System.Drawing.Rectangle admin3Loc = admin3.GetScreenRectangle();
 16:  
 17: // Now perform the drag/drop
 18: admin1.Find.ByType("Thumb").User.DragTo(admin2.Find.ByType("Thumb"));
 19: admin2.Find.ByType("Thumb").User.DragTo(admin3.Find.ByType("Thumb"));
 20:  
 21: // Validate the new locations.
 22: adminsPanels.Refresh();
 23: adminsPanels.Find.ByName(admin1.Name).GetScreenRectangle().Equals(admin1Loc);
Notes:

Line #18: We basically find the “Thumb” object inside the admin1 UI and move it to the center point in the admin2 UI. the User.DragTo() method by default will calculate the center point of the FrameworkElement it is on and perform a drag and drop to the center point of the FrameworkElement passed in. DragTo also supports other overloads that can be used to do offset dropping.

Task #6: Perform a UI action that triggers some animation and wait until the animation is complete

We kind of touched on this in Task #2 where we used the Framework.Wait.For() to wait on a certain property of the element including its RenderTransform but I wanted to also demonstrate another feature that customers can use to detect when animation is complete.

 1: //
 2: // Expand Admin1 information
 3: //
 4:  
 5: admin1.Find.ByName("maximiseButton").User.Click();
 6: admin1.Wait.ForNoMotion(100); // Wait until the animation is complete.
Notes:

Line #6 : Some customers might find this useful. The Wait.ForNoMotion() allows you to wait until a certain framework element is fixed. Meaning its location within the application is no longer changing. You can customize the period to use to deem a certain element if done moving. This function is very handy when you are trying to sync your test with a certain animation if you don’t want to mess with RenderTransforms.

That’s about it. I do hope this example was useful in helping you perform some of the automation tasks that you might be faced with in Silverlight.

If there are specific topics you would like to see covered in future blog posts, feel free to email us at contact _at_ artoftest.com or write us a tweet at http://twitter.com/artoftest.

Here is a recorded demo of this test in action. We will post the code of this test as part of the release of WebAii Beta2.

Enjoy

ArtOfTest, Inc.

Facebook DZone It! Digg It! StumbleUpon Technorati Del.icio.us NewsVine Reddit Blinklist Furl it!

 Copyright 2009 © ArtOfTest, Inc. All rights reserved  |   Privacy Policy  |  Terms of Use 

Have a question for ArtOfTest?
First Name
Last Name
Company
Email
Question:

701 Brazos Street
Suite 320
Austin, TX 78701
Tel: (512) 535-2428
Fax: (512) 722-7748
contact@artoftest.com