MenuBar.MenuItem() unreliable.

May 29, 2012 at 8:58 PM

I am automating the testing of a WPF application on .NET 4.0 (VS 2010) and have run into an issue with MenuBar.MenuItem() method. When requesting a submenu, such as in

Menu menu = menuBar.MenuItem("Help""About")


the call to MenuItem often seems unreliable and ends up returning null; not always, mind you, but often enough for it to be really annoying. 

Overall, this looks a lot like a timing issue, becasue visually it seems the Whte framework is not giving the WPF app enough time to drop down the menus, as needed. If I - instead - split the above into ints constituent parts:

Menu menu = menuBar.MenuItem("File");
menu.Click();
Thread.Sleep(1000);
Menus items = menu.ChildMenus;
Menu subMenu = items.Find("About");
subMenu.Click();
Thread.Sleep(1000);

(notice the generous sprinkling of Thread.Sleep() in there) it seems to be working fine every time.

Has anyone else run into this issue? If so, what was your approach to resolve it?

Regards,
Andrew

Jun 6, 2012 at 10:19 PM

Personally, I find the Menu support in White quite unusable. To resolve menu timing issues, I use the following extension methods (not quite following the DRY principle, but working):

 

        public static void SelectSubmenuByAutomationIds(this MenuBar menuBar, params string[] automationIds)
        {
            menuBar.Click();
            Thread.Sleep(50);
            var menu = UIItemsVerificationHelper.GetWhenReady<Menu>(
                () => menuBar.Get<Menu>(SearchCriteria.ByAutomationId(automationIds[0])),
                () => menuBar.IsMenuVisible(automationIds[0]),
                5000);
 
            Assert.IsNotNull(menu, "menu should not be null");
            menu.Click();
 
            for (int i = 1; i < automationIds.Length; i++)
            {
                Thread.Sleep(50);
                var submenu = UIItemsVerificationHelper.GetWhenReady<Menu>(
                    () => menu.SubMenu(SearchCriteria.ByAutomationId(automationIds[i])), 
                    () => menu.IsSubMenuVisible(automationIds[i]), 
                    5000);
 
                Assert.IsNotNull(submenu, "submenu should not be null");
                submenu.Click();
            }
        }
 
        public static bool IsMenuVisible(this MenuBar menuBar, string automationId)
        {
            bool result = false;
            var menu = menuBar.Get<Menu>(SearchCriteria.ByAutomationId(automationId));
            if ((null != menu) && !menu.IsOffScreen)
            {
                result = true;
            }
            return result;
        }
 
        public static bool IsSubMenuVisible(this Menu menu, string automationId)
        {
            bool result = false;
            var submenu = menu.SubMenu(SearchCriteria.ByAutomationId(automationId));
            if ((null != submenu) && !submenu.IsOffScreen)
            {
                result = true;
            }
            return result;
        }

as well as:

        public static T GetWhenReady<T>(Func<T> getSomething, Func<bool> waitForSomething, int mSecTimeOut = 10000)
        {
            int time = 0;
            int loopTime = 50;
            T result = getSomething();
            while (!waitForSomething() && (time < mSecTimeOut))
            {
                result = getSomething();
 
                if (IsNotDefaultValue<T>(result))
                {
                    Log.Trace("Result: " + result.ToString());
                }
                else
                {
                    Log.Trace("Result is still null");
                }
                Thread.Sleep(loopTime);
                time += loopTime;
            }
 
            Log.Trace("Timeout: " + mSecTimeOut + ", elapsed: " + time);
 
            if (time >= mSecTimeOut)
            {
                Log.Error("Timeout expired! Timeout set: " + mSecTimeOut + ", elapsed: " + time);
            }
            else
            {
                Log.Trace("Result: " + result.ToString());
            }
 
            return result;
        }
 
        public static bool IsDefaultValue<T>(T res)
        {
            return Object.Equals(res, null);
        }
 
        public static bool IsNotDefaultValue<T>(T res)
        {
            return !Object.Equals(res, null);
        }

Jun 8, 2012 at 9:12 PM

Thanks. Your solution is effectively like mine - insert sleep-time before clicking each menu level. Thanks for confirming I wasn't the only one with this issue.

Nov 16, 2012 at 7:17 PM
Edited Nov 17, 2012 at 8:05 AM

Here is another approach, works fine for me, but a little bit more automation code

Use extension method like below

public static void ClickMe(this UIItem menu, int sleepBeforeClick = 100, int sleepAfterClick = 0)
{
   Thread.Sleep(sleepBeforeClick);
   menu.Click();
   Thread.Sleep(sleepAfterClick);
}

And use in automation like below (Calculator)

window.Get<Menu>("View").ClickMe();
window.Get<Menu>("Standard").ClickMe();
window.Get<Menu>("View").ClickMe();
window.Get<Menu>("Scientific").ClickMe(100, 500);