Debugging Mouse Click Failures (UI synchronization related)

Test method SomeProject.CodedUITest1.CodedUITestMethod1 threw exception:
Microsoft.VisualStudio.TestTools.UITest.Extension.PlaybackFailureException: Cannot perform 'Click' on the control. Additional Details:
TechnologyName: 'MSAA'
Name: 'Foo'
ControlType: 'Client'
---> System.Runtime.InteropServices.COMException: Error HRESULT E_FAIL has been returned from a call to a COM component.

What if you get Exception like this when you try to click a control. There can be many reasons for this. But when you get a PlaybackFailureException the chances of UISync failure being the cause increases.

Question:- What is UISync?

Answer:- UI synchronization is a feature that checks if the click actually went to a particular control or not. Currently it is Window Handle based. It internally checks if both Mouse Up and Mouse Down went to a particular window handle or not. If any of the down/up actions goes to some other window it nullifies it’s affect and then eventually returns failure.

Lets take one such example :-

Scenario:-  Right Click on Client which eventually brings up a context menu.

The CodedUitest fails for the above scenario with the exception mentioned in the beginning.

Steps to find out if UI Sync is the cause for the failure.

1) Enable Logging (more info..)

2) Run the test.

3) Open %temp%\UITestLogs\LastRun\*.html file

4) Go to the end of the page. There you can see the exception message in the file. Now somewhere before this exception message search logs something like

I, 1756, 13, 2009/12/04, 15:10:25.149, 12883577203380, QTAgent32.exe, Playback - < Mouse Button action was not sent to the target window. If this slows your test, call EnableUISynchronization(FALSE) >
W, 1756, 13, 2009/12/04, 15:10:25.149, 12883586437500, QTAgent32.exe, Playback - [WARNING] Internal failure: Target element doesn't match actual element. Target: Title = "", Class = "WindowsForms10.Window.8.app.0.3d893c", HWND = 10FE4. Actual: Title = "some window", Class = "WindowsForms10.Window.8.app.0.3d893c", HWND = 30EE0
I, 1756, 13, 2009/12/04, 15:10:25.149, 12883597998150, QTAgent32.exe, Playback - < Performance of "WaitForElementReadyHookBased" for "SimpleClick" was 2006 milliseconds >
W, 1756, 13, 2009/12/04, 15:10:25.149, 12883606564170, QTAgent32.exe, Playback - [WARNING] Internal warning: UISynchronization failed for SimpleClick.

As mentioned in the above logs it says that due to some reason the click on the control actually didn't go through. Window handle for both the windows are also mentioned in the logs. You can use Spy++ tool to see which window actually has these window handles. This will make things clear as to what could have happened.

In this particular case one of the window handle is invalid. This means that the handle actually belonged to the Context Menu which poped up when Playback did Mouse Down.

Bingo, that can be the reason. Try performing the Click manually to understand what could have happened during playback.

1) Do Mouse Down. It brings up a context menu (Normally Context menu comes only after Mouse Up but in this case it comes as soon as you do Mouse Down).

2) Now do Mouse Up (Keeping the mouse Unmoved). You will notice that the mouse cursor is on top of the Context Menu (which has a different window handle as compared to the Client control).

This proves that Click failed at time of Mouse Up.

Solution:- Suppose below is the code generated by the CodedUitest builder :-

Mouse .Click(customClient, MouseButtons .Right, ModifierKeys .None, new Point (1365, 396 ));

Modify the above code to something like this :-

Mouse.StartDragging(customClient, new Point(1365, 396), MouseButtons.Right, ModifierKeys.None );
Mouse.StopDragging(new Point(Cursor.Position.X, Cursor.Position.Y));

Make sure the point given to the StartDragging is same as Mouse.Click in the original code.

How does the above code works as a click?

Internally StartDragging does a MouseDown (with UI Sync on) on the control.

In StopDragging because there is no Control given it will blindly do a MouseUp without moving the Mouse position (see the arguments passed to the method).