summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Core.iOS.UITests/Utilities
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Core.iOS.UITests/Utilities')
-rw-r--r--Xamarin.Forms.Core.iOS.UITests/Utilities/AppExtensions.cs116
-rw-r--r--Xamarin.Forms.Core.iOS.UITests/Utilities/Drag.cs173
-rw-r--r--Xamarin.Forms.Core.iOS.UITests/Utilities/Gestures.cs114
-rw-r--r--Xamarin.Forms.Core.iOS.UITests/Utilities/Logger.cs71
-rw-r--r--Xamarin.Forms.Core.iOS.UITests/Utilities/NumericExtensions.cs126
-rw-r--r--Xamarin.Forms.Core.iOS.UITests/Utilities/ParsingUtils.cs89
-rw-r--r--Xamarin.Forms.Core.iOS.UITests/Utilities/UITestCustomExceptions.cs101
-rw-r--r--Xamarin.Forms.Core.iOS.UITests/Utilities/ViewInspector.cs342
8 files changed, 1132 insertions, 0 deletions
diff --git a/Xamarin.Forms.Core.iOS.UITests/Utilities/AppExtensions.cs b/Xamarin.Forms.Core.iOS.UITests/Utilities/AppExtensions.cs
new file mode 100644
index 00000000..c6f2508c
--- /dev/null
+++ b/Xamarin.Forms.Core.iOS.UITests/Utilities/AppExtensions.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+using NUnit.Framework;
+
+using Xamarin.UITest;
+using Xamarin.UITest.Android;
+using Xamarin.UITest.iOS;
+using Xamarin.UITest.Queries;
+using System.Text.RegularExpressions;
+
+namespace Xamarin.Forms.Core.UITests
+{
+ internal static class AppExtensions
+ {
+ public static AppRect ScreenBounds (this IApp app)
+ {
+ return app.Query (Queries.Root ()).First().Rect;
+ }
+
+ public static void NavigateBack (this IApp app)
+ {
+ app.Tap (Queries.NavigationBarBackButton ());
+ }
+
+ public static void NavigateToGallery (this IApp app, string page)
+ {
+ var text = Regex.Match (page, "'(?<text>[^']*)'").Groups["text"].Value;
+ app.EnterText (q => q.Raw ("* marked:'SearchBar'"), text);
+ //var searchBar = app.Query (q => q.Raw ("* marked:'SearchBar'")).Single ();
+ Thread.Sleep(10000);
+
+ app.Tap (q => q.Raw ("* marked:'GoToTestButton'"));
+ app.WaitForNoElement (o => o.Raw ("* marked:'GoToTestButton'"), "Timed out", TimeSpan.FromMinutes(2));
+
+ //app.Screenshot ("Navigating to gallery ...");
+ //var galleryListViewBounds = app.Query (Queries.GalleryListView)[0].Rect;
+ //app.ScrollForElement (page, new Drag (galleryListViewBounds, Drag.Direction.BottomToTop, Drag.DragLength.Long));
+ //app.Tap (q => q.Raw (page));
+ //app.Screenshot ("At gallery!");
+ }
+
+ public static void NavigateToTestCases (this IApp app, string testCase)
+ {
+ app.Tap (q => q.Button ("Go to Test Cases"));
+ app.WaitForElement (q => q.Raw ("* marked:'TestCasesIssueList'"));
+
+ app.EnterText (q => q.Raw ("* marked:'SearchBarGo'"), testCase);
+
+ app.WaitForElement (q => q.Raw ("* marked:'SearchButton'"));
+ app.Tap (q => q.Raw ("* marked:'SearchButton'"));
+
+ //app.NavigateToTestCase(testCase);
+ }
+
+ public static void NavigateToTestCase (this IApp app, string testCase)
+ {
+ string testCasesQuery = "* marked:'" + testCase + "'";
+ var testCaseIssue = app.Query (q => q.Raw ("* marked:'TestCasesIssueList'")).FirstOrDefault ();
+ if (testCaseIssue != null) {
+ AppRect scrollRect = testCaseIssue.Rect;
+ app.ScrollForElement (testCasesQuery, new Drag (scrollRect, Drag.Direction.BottomToTop, Drag.DragLength.Long));
+ app.Tap (q => q.Raw (testCasesQuery));
+ } else {
+ Debug.WriteLine (string.Format ("Failed to find test case {0}", testCase));
+ }
+ }
+
+ public static bool RectsEqual (AppRect rectOne, AppRect rectTwo)
+ {
+ const float equalsTolerance = 0.1f;
+
+ bool areEqual =
+ (Math.Abs (rectOne.X - rectTwo.X) < equalsTolerance) &&
+ (Math.Abs (rectOne.Y - rectTwo.Y) < equalsTolerance) &&
+ (Math.Abs (rectOne.Width - rectTwo.Width) < equalsTolerance) &&
+ (Math.Abs (rectOne.Height - rectTwo.Height) < equalsTolerance) &&
+ (Math.Abs (rectOne.CenterX - rectTwo.CenterX) < equalsTolerance) &&
+ (Math.Abs (rectOne.CenterY - rectTwo.CenterY) < equalsTolerance);
+
+ return areEqual;
+ }
+
+ public static void WaitForAnimating (this IApp app, Func<AppQuery, AppQuery> query)
+ {
+ // Right now only checks if bounds are moving
+ const int pollingRate = 200;
+ const int timeout = 5000;
+ var sw = new Stopwatch ();
+
+ var previousState = app.Query (query).First ().Rect;
+
+ sw.Start ();
+ while (true) {
+
+ var newState = app.Query (query).First ().Rect;
+
+ if (RectsEqual (previousState, newState))
+ break;
+
+ previousState = newState;
+
+ if (sw.ElapsedMilliseconds >= timeout)
+ throw new Exception("Timed out");
+
+ Thread.Sleep (pollingRate);
+ }
+ sw.Stop ();
+ }
+ }
+}
diff --git a/Xamarin.Forms.Core.iOS.UITests/Utilities/Drag.cs b/Xamarin.Forms.Core.iOS.UITests/Utilities/Drag.cs
new file mode 100644
index 00000000..71b1fa03
--- /dev/null
+++ b/Xamarin.Forms.Core.iOS.UITests/Utilities/Drag.cs
@@ -0,0 +1,173 @@
+using System.Reflection.Emit;
+
+using Xamarin.UITest.Queries;
+
+namespace Xamarin.Forms.Core.UITests
+{
+ internal class Drag
+ {
+ internal enum DragLength
+ {
+ Long,
+ Medium,
+ Short
+ }
+
+ internal enum Direction
+ {
+ TopToBottom,
+ BottomToTop,
+ RightToLeft,
+ LeftToRight
+ }
+
+ AppRect dragBounds;
+ float xStart;
+ float yStart;
+ float xEnd;
+ float yEnd;
+ Direction dragDirection;
+ Direction oppositeDirection;
+ DragLength dragLength;
+
+ public Drag (AppRect dragbounds, float xStart, float yStart, float xEnd, float yEnd, Direction direction)
+ {
+ dragBounds = dragbounds;
+ this.xStart = xStart;
+ this.yStart = yStart;
+ this.xEnd = xEnd;
+ this.yEnd = yEnd;
+ dragDirection = direction;
+ oppositeDirection = GetOppositeDirection (direction);
+ }
+
+ public Drag (AppRect dragBounds, Direction direction, DragLength dragLength)
+ {
+ this.dragBounds = dragBounds;
+ dragDirection = direction;
+ this.dragLength = dragLength;
+ SetDragForBounds (dragDirection, dragLength);
+ }
+
+ void SetDragForBounds (Direction direction, DragLength dragLength)
+ {
+ // percentage of bounds to scroll centered in element
+ float scrollPercentage;
+
+ switch (dragLength) {
+ case DragLength.Long:
+ scrollPercentage = 0.8f;
+ break;
+ case DragLength.Medium:
+ scrollPercentage = 0.5f;
+ break;
+ default:
+ scrollPercentage = 0.2f;
+ break;
+ }
+
+ if (direction == Direction.LeftToRight) {
+ yStart = dragBounds.CenterY;
+ yEnd = dragBounds.CenterY;
+ float xDisplacement = (dragBounds.CenterX + (dragBounds.Width / 2)) - dragBounds.X;
+ float insetForScroll = (xDisplacement - (xDisplacement * scrollPercentage)) / 2;
+ xStart = dragBounds.X + insetForScroll;
+ xEnd = (dragBounds.CenterX + (dragBounds.Width / 2)) - insetForScroll;
+ } else if (direction == Direction.RightToLeft) {
+ yStart = dragBounds.CenterY;
+ yEnd = dragBounds.CenterY;
+ float xDisplacement = (dragBounds.CenterX + (dragBounds.Width / 2)) - dragBounds.X;
+ float insetForScroll = (xDisplacement - (xDisplacement * scrollPercentage)) / 2;
+ xStart = (dragBounds.CenterX + (dragBounds.Width / 2)) - insetForScroll;
+ xEnd = dragBounds.X + insetForScroll;
+ } else if (direction == Direction.TopToBottom) {
+ xStart = dragBounds.CenterX;
+ xEnd = dragBounds.CenterX;
+ float yDisplacement = (dragBounds.CenterY + (dragBounds.Height / 2)) - dragBounds.Y;
+ float insetForScroll = (yDisplacement - (yDisplacement * scrollPercentage)) / 2;
+ yStart = dragBounds.Y + insetForScroll;
+ yEnd = (dragBounds.CenterY + (dragBounds.Height / 2)) - insetForScroll;
+ } else if (direction == Direction.BottomToTop) {
+ xStart = dragBounds.CenterX;
+ xEnd = dragBounds.CenterX;
+ float yDisplacement = (dragBounds.CenterY + (dragBounds.Height / 2)) - dragBounds.Y;
+ float insetForScroll = (yDisplacement - (yDisplacement * scrollPercentage)) / 2;
+ yStart = (dragBounds.CenterY + (dragBounds.Height / 2)) - insetForScroll;
+ yEnd = dragBounds.Y + insetForScroll;
+
+ }
+ }
+
+ Direction GetOppositeDirection (Direction direction)
+ {
+ switch (direction) {
+ case Direction.TopToBottom:
+ return Direction.BottomToTop;
+ case Direction.BottomToTop:
+ return Direction.TopToBottom;
+ case Direction.RightToLeft:
+ return Direction.LeftToRight;
+ case Direction.LeftToRight:
+ return Direction.RightToLeft;
+ default:
+ return Direction.TopToBottom;
+ }
+ }
+
+ public AppRect DragBounds
+ {
+ get { return dragBounds; }
+ }
+
+ public float XStart
+ {
+ get { return xStart; }
+ }
+
+ public float YStart
+ {
+ get { return yStart; }
+ }
+
+ public float XEnd
+ {
+ get { return xEnd; }
+ }
+
+ public float YEnd
+ {
+ get { return yEnd; }
+ }
+
+ public Direction DragDirection
+ {
+ get { return dragDirection; }
+ set
+ {
+ if (dragDirection == value)
+ return;
+
+ dragDirection = value;
+ oppositeDirection = GetOppositeDirection (dragDirection);
+ OnDragDirectionChanged ();
+ }
+ }
+
+ void OnDragDirectionChanged ()
+ {
+ SetDragForBounds (dragDirection, dragLength);
+ }
+
+ public Direction OppositeDirection
+ {
+ get { return oppositeDirection; }
+ private set
+ {
+ if (oppositeDirection == value)
+ return;
+
+ oppositeDirection = value;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core.iOS.UITests/Utilities/Gestures.cs b/Xamarin.Forms.Core.iOS.UITests/Utilities/Gestures.cs
new file mode 100644
index 00000000..548ff06d
--- /dev/null
+++ b/Xamarin.Forms.Core.iOS.UITests/Utilities/Gestures.cs
@@ -0,0 +1,114 @@
+using System;
+using System.Dynamic;
+using System.Linq;
+using System.Security.Cryptography;
+
+using Xamarin.UITest;
+using Xamarin.UITest.Android;
+using Xamarin.UITest.iOS;
+using Xamarin.UITest.Queries;
+
+namespace Xamarin.Forms.Core.UITests
+{
+ internal static class Gestures
+ {
+ public static bool ScrollForElement (this IApp app, string query, Drag drag, int maxSteps = 25)
+ {
+ int count = 0;
+
+ int centerTolerance = 50;
+
+ Func<AppQuery, AppQuery> elementQuery = q => q.Raw (query);
+
+ // Visible elements
+ if (app.Query (elementQuery).Length > 1) {
+ throw new UITestQueryMultipleResultsException (query);
+ }
+
+ // check to see if the element is visible already
+ if (app.Query (elementQuery).Length == 1) {
+ // centering an element whos CenterX is close to the bounding rectangle's center X can sometime register the swipe as a tap
+ float elementDistanceToDragCenter = Math.Abs (app.Query (elementQuery).First ().Rect.CenterY - drag.DragBounds.CenterY);
+ if (elementDistanceToDragCenter > centerTolerance)
+ app.CenterElementInView (elementQuery, drag.DragBounds, drag.DragDirection);
+ return true;
+ }
+
+ // loop until element is seen
+ while (app.Query (elementQuery).Length == 0 && count < maxSteps) {
+ app.DragCoordinates (drag.XStart, drag.YStart, drag.XEnd, drag.YEnd);
+ count++;
+ }
+
+ if (count != maxSteps) {
+ // centering an element whos CenterX is close to the bounding rectangle's center X can sometime register the swipe as a tap
+ float elementDistanceToDragCenter = Math.Abs (app.Query (elementQuery).First ().Rect.CenterY - drag.DragBounds.CenterY);
+ if (elementDistanceToDragCenter > centerTolerance)
+ app.CenterElementInView (elementQuery, drag.DragBounds, drag.DragDirection);
+ return true;
+ }
+
+ count = 0;
+ drag.DragDirection = drag.OppositeDirection;
+
+ while (app.Query (elementQuery).Length == 0 && count < maxSteps) {
+ app.DragCoordinates (drag.XStart, drag.YStart, drag.XEnd, drag.YEnd);
+ count++;
+ }
+
+ if (count != maxSteps) {
+ app.CenterElementInView (elementQuery, drag.DragBounds, drag.DragDirection);
+ return true;
+ }
+
+ return false;
+ }
+
+ static void CenterElementInView (this IApp app, Func<AppQuery, AppQuery> element, AppRect containingView, Drag.Direction direction)
+ {
+ // TODO Implement horizontal centering
+
+ if (direction == Drag.Direction.BottomToTop || direction == Drag.Direction.TopToBottom) {
+
+ var elementBounds = app.Query (element).First ().Rect;
+
+ bool elementCenterBelowContainerCenter = elementBounds.CenterY > containingView.CenterY;
+ bool elementCenterAboveContainerCenter = elementBounds.CenterY < containingView.CenterY;
+
+ var displacementToCenter = Math.Abs (elementBounds.CenterY - containingView.CenterY) / 2;
+
+ // avoid drag as touch
+ if (displacementToCenter < 50)
+ return;
+
+ if (elementCenterBelowContainerCenter) {
+
+ var drag = new Drag (
+ containingView,
+ containingView.CenterX, containingView.CenterY + displacementToCenter,
+ containingView.CenterX, containingView.CenterY - displacementToCenter,
+ Drag.Direction.BottomToTop
+ );
+
+ app.DragCoordinates (drag.XStart, drag.YStart, drag.XEnd, drag.YEnd);
+
+ } else if (elementCenterAboveContainerCenter) {
+
+ var drag = new Drag (
+ containingView,
+ containingView.CenterX, containingView.CenterY - displacementToCenter,
+ containingView.CenterX, containingView.CenterY + displacementToCenter,
+ Drag.Direction.TopToBottom
+ );
+
+ app.DragCoordinates (drag.XStart, drag.YStart, drag.XEnd, drag.YEnd);
+ }
+ }
+ }
+
+ public static void Pan (this IApp app, Drag drag)
+ {
+ app.DragCoordinates (drag.XStart, drag.YStart, drag.XEnd, drag.YEnd);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core.iOS.UITests/Utilities/Logger.cs b/Xamarin.Forms.Core.iOS.UITests/Utilities/Logger.cs
new file mode 100644
index 00000000..ed0041ae
--- /dev/null
+++ b/Xamarin.Forms.Core.iOS.UITests/Utilities/Logger.cs
@@ -0,0 +1,71 @@
+using System;
+using System.IO;
+using System.Reflection;
+
+using Xamarin.UITest.Queries;
+
+namespace Xamarin.Forms.Core.UITests
+{
+ internal static class Logger
+ {
+ static StreamWriter queryWriter;
+
+ public static void Init ()
+ {
+ queryWriter = new StreamWriter ("../../Xamarin.Forms.Core-UITest-queries.log", false);
+ }
+
+ public static void Log (string text)
+ {
+ queryWriter.Write (text);
+ }
+
+ public static void LogLine (string text = "")
+ {
+ queryWriter.WriteLine (text);
+ }
+
+ public static void Close ()
+ {
+ queryWriter.Flush ();
+ queryWriter.Close ();
+ }
+
+ public static void LogQueryResult (AppResult[] resultsForQuery)
+ {
+ foreach (AppResult result in resultsForQuery)
+ WriteAppResult (result);
+ }
+
+ static void WriteAppResult (AppResult appResult)
+ {
+ var classText = string.Format (" {0, -10} : {1}", "Class", appResult.Class);
+ var descriptionText = string.Format (" {0, -10} : {1}", "Description", appResult.Description);
+ var enabledText = string.Format (" {0, -10} : {1}", "Enabled", appResult.Enabled);
+ var idText = string.Format (" {0, -10} : {1}", "Id", appResult.Id);
+ var labelText = string.Format (" {0, -10} : {1}", "Label", appResult.Id);
+ var textText = string.Format (" {0, -10} : {1}", "Text", appResult.Text);
+
+ var rectText = string.Format (" {0, -10}", "Rect");
+ var rectContentsText = string.Format (" [X:{0} Y:{1} W:{2} H:{3}] [CX:{4} CY:{5}]",
+ appResult.Rect.X,
+ appResult.Rect.Y,
+ appResult.Rect.Width,
+ appResult.Rect.Height,
+ appResult.Rect.CenterX,
+ appResult.Rect.CenterY
+ );
+
+ queryWriter.WriteLine (classText);
+ queryWriter.WriteLine (descriptionText);
+ queryWriter.WriteLine (enabledText);
+ queryWriter.WriteLine (idText);
+ queryWriter.WriteLine (labelText);
+ queryWriter.WriteLine (textText);
+ queryWriter.WriteLine (rectText);
+ queryWriter.WriteLine (rectContentsText);
+ queryWriter.WriteLine();
+ }
+
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core.iOS.UITests/Utilities/NumericExtensions.cs b/Xamarin.Forms.Core.iOS.UITests/Utilities/NumericExtensions.cs
new file mode 100644
index 00000000..13a4b2bf
--- /dev/null
+++ b/Xamarin.Forms.Core.iOS.UITests/Utilities/NumericExtensions.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.Remoting.Messaging;
+
+using NUnit.Framework;
+
+using Xamarin.UITest;
+using Xamarin.UITest.Queries;
+using Xamarin.UITest.Android;
+using Xamarin.UITest.iOS;
+using System.Globalization;
+
+namespace Xamarin.Forms.Core.UITests
+{
+ internal class Matrix : Object
+ {
+ public double M00, M01, M02, M03;
+ public double M10, M11, M12, M13;
+ public double M20, M21, M22, M23;
+ public double M30, M31, M32, M33;
+
+ public void Log () {
+
+ //Logger.LogLine ();
+
+ //Logger.LogLine (string.Format ("{0,-3}, {1,-3}, {2,-3}, {3,-3}", M00, M01, M02, M03));
+ //Logger.LogLine (string.Format ("{0,-3}, {1,-3}, {2,-3}, {3,-3}", M10, M11, M12, M13));
+ //Logger.LogLine (string.Format ("{0,-3}, {1,-3}, {2,-3}, {3,-3}", M20, M21, M22, M23));
+ //Logger.LogLine (string.Format ("{0,-3}, {1,-3}, {2,-3}, {3,-3}", M30, M31, M32, M33));
+
+ //Logger.LogLine ();
+ }
+
+ public override bool Equals (object obj)
+ {
+ if (obj == null)
+ return false;
+
+ var transform = obj as Matrix;
+ if ((Object)transform == null)
+ return false;
+
+ const double tolerance = 0.01;
+ bool result =
+ Math.Abs (M00 - transform.M00) < tolerance &&
+ Math.Abs (M01 - transform.M01) < tolerance &&
+ Math.Abs (M02 - transform.M02) < tolerance &&
+ Math.Abs (M03 - transform.M03) < tolerance &&
+ Math.Abs (M10 - transform.M10) < tolerance &&
+ Math.Abs (M11 - transform.M11) < tolerance &&
+ Math.Abs (M12 - transform.M12) < tolerance &&
+ Math.Abs (M13 - transform.M13) < tolerance &&
+ Math.Abs (M20 - transform.M20) < tolerance &&
+ Math.Abs (M21 - transform.M21) < tolerance &&
+ Math.Abs (M22 - transform.M22) < tolerance &&
+ Math.Abs (M23 - transform.M23) < tolerance &&
+ Math.Abs (M30 - transform.M30) < tolerance &&
+ Math.Abs (M31 - transform.M31) < tolerance &&
+ Math.Abs (M32 - transform.M32) < tolerance &&
+ Math.Abs (M33 - transform.M33) < tolerance;
+
+ return result;
+ }
+
+ public override int GetHashCode ()
+ {
+ return 0;
+ }
+ }
+
+ internal enum Axis
+ {
+ X,
+ Y,
+ Z
+ }
+
+ internal static class NumericExtensions
+ {
+ public static double ToRadians(this float val)
+ {
+ return (Math.PI / 180.0) * val;
+ }
+
+ public static Matrix CalculateRotationMatrixForDegrees (float degrees, Axis rotationAxis)
+ {
+ var angle = degrees.ToRadians ();
+
+ var transform = new Matrix ();
+ if (rotationAxis == Axis.X) {
+ transform.M00 = 1; transform.M01 = 0; transform.M02 = 0; transform.M03 = 0;
+ transform.M10 = 0; transform.M11 = (float) Math.Cos (angle); transform.M12 = (float) Math.Sin (angle); transform.M13 = 0;
+ transform.M20 = 0; transform.M21 = -(float) Math.Sin (angle); transform.M22 = (float) Math.Cos (angle); transform.M23 = 0;
+ transform.M30 = 0; transform.M31 = 0; transform.M32 = 0; transform.M33 = 1;
+ } else if (rotationAxis == Axis.Y) {
+ transform.M00 = (float) Math.Cos (angle); transform.M01 = 0; transform.M02 = -(float) Math.Sin (angle); transform.M03 = 0;
+ transform.M10 = 0; transform.M11 = 1; transform.M12 = 0; transform.M13 = 0;
+ transform.M20 = (float) Math.Sin (angle); transform.M21 = 0; transform.M22 = (float) Math.Cos (angle); transform.M23 = 0;
+ transform.M30 = 0; transform.M31 = 0; transform.M32 = 0; transform.M33 = 1;
+ } else {
+ transform.M00 = (float) Math.Cos (angle); transform.M01 = (float) Math.Sin (angle); transform.M02 = 0; transform.M03 = 0;
+ transform.M10 = -(float) Math.Sin (angle); transform.M11 = (float) Math.Cos (angle); transform.M12 = 0; transform.M13 = 0;
+ transform.M20 = 0; transform.M21 = 0; transform.M22 = 1; transform.M23 = 0;
+ transform.M30 = 0; transform.M31 = 0; transform.M32 = 0; transform.M33 = 1;
+ }
+
+ return transform;
+ }
+
+ public static Matrix BuildScaleMatrix (float scale)
+ {
+ var transform = new Matrix ();
+
+ transform.M00 = scale; transform.M01 = 0; transform.M02 = 0; transform.M03 = 0;
+ transform.M10 = 0; transform.M11 = scale; transform.M12 = 0; transform.M13 = 0;
+ transform.M20 = 0; transform.M21 = 0; transform.M22 = scale; transform.M23 = 0;
+ transform.M30 = 0; transform.M31 = 0; transform.M32 = 0; transform.M33 = 1;
+
+ return transform;
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core.iOS.UITests/Utilities/ParsingUtils.cs b/Xamarin.Forms.Core.iOS.UITests/Utilities/ParsingUtils.cs
new file mode 100644
index 00000000..6598e0f5
--- /dev/null
+++ b/Xamarin.Forms.Core.iOS.UITests/Utilities/ParsingUtils.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.Remoting.Messaging;
+
+using NUnit.Framework;
+
+using Xamarin.UITest;
+using Xamarin.UITest.Queries;
+using Xamarin.UITest.Android;
+using Xamarin.UITest.iOS;
+using System.Globalization;
+using System.Text.RegularExpressions;
+using System.IO;
+
+namespace Xamarin.Forms.Core.UITests
+{
+
+ internal static class ParsingUtils
+ {
+ public static Font ParseUIFont (string font)
+ {
+ FontAttributes fontAttrs = FontAttributes.None;
+
+ // Logger.LogLine ("TEST PARSING");
+
+ if (font.Contains ("font-weight: bold;")) {
+ // Logger.LogLine ("Found Bold");
+ fontAttrs = FontAttributes.Bold;
+ }
+
+ return new Font ().WithAttributes (fontAttrs);
+ }
+
+ public static Color ParseUIColor (string backgroundColor)
+ {
+ var delimiters = new char[] { ' ' };
+ string[] words = backgroundColor.Split (delimiters, StringSplitOptions.RemoveEmptyEntries);
+ return new Color (double.Parse (words[1]), double.Parse (words[2]), double.Parse (words[3]), double.Parse (words[4]));
+ }
+
+ public static Point ParseCGPoint (object CGPoint) {
+ var point = new Point { X = 0, Y = 0 };
+ return point;
+ }
+
+ public static Matrix ParseCATransform3D (string CATransform3D)
+ {
+ // Logger.Log (CATransform3D);
+ char[] delimiters = { '<', ' ', '>' };
+ string[] words = CATransform3D.Split (delimiters, StringSplitOptions.RemoveEmptyEntries);
+
+ List<double> numbers = new List<double> ();
+
+ // Each number is represented by 2 blocks returned by server
+ for (int i = 0; i < (words.Length - 1); i += 2) {
+ string word = words[i] + words[i + 1];
+ var number = Int64.Parse (word, NumberStyles.HexNumber);
+ byte[] bytes = BitConverter.GetBytes (number);
+ byte[] reversedBytes = bytes.Reverse ().ToArray ();
+ double value = BitConverter.ToDouble (reversedBytes, 0);
+ numbers.Add (value);
+ }
+
+ var transformationMatrix = new Matrix ();
+ transformationMatrix.M00 = numbers[0];
+ transformationMatrix.M01 = numbers[1];
+ transformationMatrix.M02 = numbers[2];
+ transformationMatrix.M03 = numbers[3];
+ transformationMatrix.M10 = numbers[4];
+ transformationMatrix.M11 = numbers[5];
+ transformationMatrix.M12 = numbers[6];
+ transformationMatrix.M13 = numbers[7];
+ transformationMatrix.M20 = numbers[8];
+ transformationMatrix.M21 = numbers[9];
+ transformationMatrix.M22 = numbers[10];
+ transformationMatrix.M23 = numbers[11];
+ transformationMatrix.M30 = numbers[12];
+ transformationMatrix.M31 = numbers[13];
+ transformationMatrix.M32 = numbers[14];
+ transformationMatrix.M33 = numbers[15];
+
+ return transformationMatrix;
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core.iOS.UITests/Utilities/UITestCustomExceptions.cs b/Xamarin.Forms.Core.iOS.UITests/Utilities/UITestCustomExceptions.cs
new file mode 100644
index 00000000..6969ac52
--- /dev/null
+++ b/Xamarin.Forms.Core.iOS.UITests/Utilities/UITestCustomExceptions.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Xamarin.UITest.Queries;
+using System.Runtime.CompilerServices;
+
+namespace Xamarin.Forms.Core.UITests
+{
+ internal class UITestQueryNoResultException : Exception
+ {
+ readonly string message;
+
+ public UITestQueryNoResultException (string query)
+ {
+ message = string.Format ("Found no elements for query with target: {0}", query);
+ }
+
+ public override string Message
+ {
+ get { return message; }
+ }
+ }
+
+ internal class UITestQuerySingleResultException : Exception
+ {
+ readonly string message;
+
+ public UITestQuerySingleResultException (string query)
+ {
+ message = string.Format ("Found single element for query with target: {0}", query);
+ }
+
+ public override string Message
+ {
+ get { return message; }
+ }
+ }
+
+ internal class UITestQueryMultipleResultsException : Exception
+ {
+ readonly string message;
+
+ public UITestQueryMultipleResultsException (string query)
+ {
+ message = string.Format ("Found muliple elements for query with target: {0}", query);
+ }
+
+ public override string Message
+ {
+ get { return message; }
+ }
+ }
+
+ internal class UITestRemoteException : Exception
+ {
+ readonly string message;
+
+ public UITestRemoteException (string message)
+ {
+ this.message = message;
+ }
+
+ public override string Message
+ {
+ get { return message; }
+ }
+ }
+
+ internal class UITestRemoteQueryException : Exception
+ {
+ readonly string message;
+
+ public UITestRemoteQueryException (string query)
+ {
+ message = string.Format ("Error for query with target: {0}", query);
+ }
+
+ public override string Message
+ {
+ get { return message; }
+ }
+ }
+
+ internal class UITestErrorException : Exception
+ {
+ readonly string message;
+
+ public UITestErrorException (string message, [CallerMemberName] string caller = null)
+ {
+ message = string.Format ("Test error: {0}, {1}", caller, message);
+ }
+
+ public override string Message
+ {
+ get { return message; }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Core.iOS.UITests/Utilities/ViewInspector.cs b/Xamarin.Forms.Core.iOS.UITests/Utilities/ViewInspector.cs
new file mode 100644
index 00000000..288ee3d6
--- /dev/null
+++ b/Xamarin.Forms.Core.iOS.UITests/Utilities/ViewInspector.cs
@@ -0,0 +1,342 @@
+using System;
+using System.Collections.Generic;
+
+using Xamarin.UITest;
+using Xamarin.UITest.Android;
+using Xamarin.UITest.iOS;
+using System.Linq;
+
+namespace Xamarin.Forms.Core.UITests
+{
+ internal static class ViewInspector
+ {
+ public static void LogPropertiesForView (this IApp app, string query, bool isOnParent = false)
+ {
+ if (app is AndroidApp) {
+ LogPropertiesForAndroidView ((AndroidApp)app, query, isOnParent);
+ } else {
+ LogPropertiesForUIView ((iOSApp)app, query, isOnParent);
+ LogPropertiesForCALayer ((iOSApp)app, query, isOnParent);
+ }
+ }
+
+ static void LogPropertiesForUIView (this iOSApp app, string query, bool isOnParent = false) {
+
+ //Logger.LogLine ("--- UIView Properties ---");
+
+ var properties = new [] {
+ // just getters with no params, bools
+ "alpha",
+ "autoresizesSubviews",
+ "autoresizingMask",
+ "backgroundColor",
+ "bounds",
+ "center",
+ "clearsContextBeforeDrawing",
+ "clipsToBounds",
+ "contentMode",
+ "contentScaleFactor",
+ "exclusiveTouch",
+ "frame",
+ "gestureRecognizers",
+ "hidden",
+ "layer",
+ "motionEffects",
+ "multipleTouchEnabled",
+ "opaque",
+ "restorationIdentifier",
+ "subviews",
+ "superview",
+ "tag",
+ "tintAdjustmentMode",
+ "tintColor",
+ "transform",
+ "userInteractionEnabled",
+ "window"
+ };
+
+ if (isOnParent)
+ query = query + " parent * index:0";
+
+ foreach (var property in properties) {
+ object prop;
+ bool found =
+ MaybeGetProperty<string> (app, query, property, out prop) ||
+ MaybeGetProperty<int> (app, query, property, out prop) ||
+ MaybeGetProperty<float> (app, query, property, out prop) ||
+ MaybeGetProperty<bool> (app, query, property, out prop);
+
+ //if (found)
+ // Logger.LogLine (string.Format ("{0,-30}: {1}", property, prop));
+ }
+
+ //Logger.LogLine();
+
+ }
+
+ static void LogPropertiesForCALayer(this iOSApp app, string query, bool isOnParent = false)
+ {
+ // Logger.LogLine ("--- UIView.Layer Properties ---");
+
+ var properties = new[] {
+ "actions",
+ "anchorPoint",
+ "anchorPointZ",
+ "backgroundColor",
+ "backgroundFilters",
+ "borderColor",
+ "borderWidth",
+ "bounds",
+ "compositingFilter",
+ "contents",
+ "contentsCenter",
+ "contentsGravity",
+ "contentsRect",
+ "contentsScale",
+ "cornerRadius",
+ "delegate",
+ "doubleSided",
+ "drawsAsynchronously",
+ "edgeAntialiasingMask",
+ "filters",
+ "frame",
+ "geometryFlipped",
+ "hidden",
+ "magnificationFilter",
+ "mask",
+ "masksToBounds",
+ "minificationFilter",
+ "minificationFilterBias",
+ "name",
+ "needsDisplayOnBoundsChange",
+ "opacity",
+ "opaque",
+ "position",
+ "rasterizationScale",
+ "shadowColor",
+ "shadowOffset",
+ "shadowOpacity",
+ "shadowPath",
+ "shadowRadius",
+ "shouldRasterize",
+ "style",
+ "sublayers",
+ "sublayerTransform",
+ "superlayer",
+ "transform",
+ "visibleRect",
+ "zPosition"
+ };
+
+ if (isOnParent)
+ query = query + " parent * index:0";
+
+ foreach (var property in properties)
+ {
+ object prop;
+ bool found =
+ MaybeGetLayerProperty<string>(app, query, property, out prop) ||
+ MaybeGetLayerProperty<int>(app, query, property, out prop) ||
+ MaybeGetLayerProperty<bool>(app, query, property, out prop);
+
+ //if (found)
+ // Logger.LogLine(string.Format("{0,-30}: {1}", property, prop));
+ }
+
+ //Logger.LogLine();
+
+ }
+
+ static void LogPropertiesForAndroidView (this AndroidApp app, string query, bool isOnParent = false)
+ {
+ // Logger.LogLine( "--- Android View Properties ---");
+
+ var properties = new [] {
+ // just getters with no params, bools
+ //"getAccessibilityLiveRegion",
+ //"getAccessibilbityNodeProvider",
+ //"getAlpha",
+ //"getAnimation",
+ //"getApplicationWindowToken",
+ //"getBackground",
+ //"getBaseline",
+ //"getBottom",
+ //"getCameraDistance",
+ //"getClipBounds",
+ //"getContentDescription",
+ //"getContext",
+ //"getDefaultSize",
+ //"getDisplay",
+ //"getDrawableState",
+ //"getDrawingCache",
+ //"getDrawingCacheBackgroundColor",
+ //"getDrawingRect",
+ //"getDrawingTime",
+ //"getFilterTouchesWhenObscurred",
+ //"getFitsSystemWindows",
+ //"getFocusables",
+ //"getHandler",
+ //"getHeight",
+ //"getHitRect",
+ //"getHorizontalFadingEdgeLength",
+ //"getId",
+ //"getImportantForAccessibility",
+ //"getKeepScreenOn",
+ //"getKeyDispatcherState",
+ //"getLabelFor",
+ //"getLayerType",
+ //"getLayoutDirection",
+ //"getLayourParams",
+ //"getLeft",
+ "getMatrix",
+ //"getMeasuredHeight",
+ //"getMeasuredHeightAndState",
+ //"getMeasuredState",
+ //"getMeasuredWidth",
+ //"getMeasuredWidthAndState",
+ //"getMinimumHeight",
+ //"getMinimumWidth",
+ //"getNextFocusDownId",
+ //"getNextFocusForwardId",
+ //"getNextFocusLeftId",
+ //"getNextFocusRightId",
+ //"getNextFocusUpId",
+ //"getOnFocusChangedListener",
+ //"getOverScrollMethod",
+ //"getOverlay",
+ //"getPaddingBottom",
+ //"getPaddingEnd",
+ //"getPaddingLeft",
+ //"getPaddingRight",
+ //"getPaddingStart",
+ //"getPaddingTop",
+ //"getParent",
+ //"getParentForAccessibility",
+ //"getPivotX",
+ //"getPivotY",
+ //"getResources",
+ //"getRight",
+ //"getRootView",
+ //"getRotation",
+ //"getRotationX",
+ //"getRotationY",
+ "getScaleX",
+ "getScaleY",
+ //"getScrollBarDefaultDelayBeforeFade",
+ //"getScrollBarFadeDuration",
+ //"getScrollBarSize",
+ //"getScrollBarStyle",
+ //"getScrollX",
+ //"getScrollY",
+ //"getSolidColor",
+ //"getSystemUiVisibility",
+ //"getTag",
+ //"getTextAlignment",
+ //"getTextDirection",
+ //"getTop",
+ //"getTouchDelegate",
+ //"getTouchables",
+ //"getTranslationX",
+ //"getTranslationY",
+ //"getVerticalFadingEdgeLength",
+ //"getVerticalScrollbarPosition",
+ //"getVerticalScrollbarWidth",
+ //"getViewTreeObserver",
+ //"getVisibility",
+ //"getWidth",
+ //"getWindowId",
+ //"getWindowSystemUiVisbility",
+ //"getWindowToken",
+ //"getWindowVisibility",
+ //"getX",
+ //"getY",
+ //"hasFocus",
+ //"hasFocusable",
+ //"hasOnClickListener",
+ //"hasOverlappingRendering",
+ //"hasTransientState",
+ //"hasWindowFocus",
+ //"isActivated",
+ //"isAttachedToWindow",
+ //"isClickable",
+ //"isDirty",
+ //"isDrawingCacheEnabled",
+ //"isDuplicateParentStateEnabled",
+ //"isEnabled",
+ //"isFocusable",
+ //"isFocusableInTouchWindow",
+ //"isFocused",
+ //"isHapticFeedbackEnabled",
+ //"isHardwareAccelerated",
+ //"isHorizontalFadingEdgeEnabled",
+ //"isHovered",
+ //"idInEditMode",
+ //"isInLayout",
+ //"isInTouchMode",
+ //"isLaidOut",
+ //"isLayoutDirectionResolved",
+ //"isLayoutRequested",
+ //"isLongClickable",
+ //"isOpaque",
+ //"isPaddingRelative",
+ //"isPressed",
+ //"isSaveEnabled",
+ //"isSaveFromParentEnabled",
+ //"isScrollContainer",
+ //"isScrollBarFadingEnabled",
+ //"isSelected",
+ //"isShown",
+ //"isSoundEffectsEnabled",
+ //"isTextAlignmentResolved",
+ //"isTextDirectionResolved",
+ //"isVerticalFadingEdgeEnabled",
+ //"isVerticalScrollBarEnabled"
+ };
+
+ if (isOnParent)
+ query = query + " parent * index:0";
+
+ foreach (var property in properties) {
+ object prop;
+ bool found =
+ MaybeGetProperty<string> (app, query, property, out prop) ||
+ //MaybeGetProperty<int> (app, query, property, out prop) ||
+ MaybeGetProperty<float> (app, query, property, out prop) ||
+ MaybeGetProperty<bool> (app, query, property, out prop);
+
+ //if (found)
+ // Logger.LogLine (string.Format ("{0,-30}: {1}", property, prop));
+ }
+
+ //Logger.LogLine();
+
+ }
+
+ static bool MaybeGetLayerProperty<T> (iOSApp app, string query, string property, out object result)
+ {
+
+ try {
+ result = app.Query (q => q.Raw (query).Invoke ("layer").Invoke (property).Value<T> ()).First ();
+ } catch {
+ result = null;
+ return false;
+ }
+
+ return true;
+ }
+
+ static bool MaybeGetProperty<T> (IApp app, string query, string property, out object result)
+ {
+
+ try {
+ result = app.Query (q => q.Raw (query).Invoke (property).Value<T> ()).First ();
+ } catch {
+ result = null;
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
+