diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..48ff912
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,44 @@
+# Autosave files
+*~
+
+# build
+[Oo]bj/
+[Bb]in/
+packages/
+TestResults/
+
+# globs
+Makefile.in
+*.DS_Store
+*.sln.cache
+*.suo
+*.cache
+*.pidb
+*.userprefs
+*.usertasks
+config.log
+config.make
+config.status
+aclocal.m4
+install-sh
+autom4te.cache/
+*.user
+*.tar.gz
+tarballs/
+test-results/
+Thumbs.db
+
+# Mac bundle stuff
+*.dmg
+*.app
+
+# resharper
+*_Resharper.*
+*.Resharper
+
+# dotCover
+*.dotCover
+
+Droid/Resources/Resource.designer.cs
+
+.vs
diff --git a/src/Droid/Assets/AboutAssets.txt b/src/Droid/Assets/AboutAssets.txt
new file mode 100644
index 0000000..a9b0638
--- /dev/null
+++ b/src/Droid/Assets/AboutAssets.txt
@@ -0,0 +1,19 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories) and given a Build Action of "AndroidAsset".
+
+These files will be deployed with your package and will be accessible using Android's
+AssetManager, like this:
+
+public class ReadAsset : Activity
+{
+ protected override void OnCreate (Bundle bundle)
+ {
+ base.OnCreate (bundle);
+
+ InputStream input = Assets.Open ("my_asset.txt");
+ }
+}
+
+Additionally, some Android functions will automatically load asset files:
+
+Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");
diff --git a/src/Droid/MainActivity.cs b/src/Droid/MainActivity.cs
new file mode 100644
index 0000000..d7a92cc
--- /dev/null
+++ b/src/Droid/MainActivity.cs
@@ -0,0 +1,33 @@
+using System;
+
+using Android.App;
+using Android.Content;
+using Android.Content.PM;
+using Android.Runtime;
+using Android.Views;
+using Android.Widget;
+using Android.OS;
+
+namespace UITestDemo.Droid
+{
+ [Activity(Label = "UITestDemo.Droid", Icon = "@drawable/icon", Theme = "@style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
+ public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
+ {
+ protected override void OnCreate(Bundle bundle)
+ {
+ TabLayoutResource = Resource.Layout.Tabbar;
+ ToolbarResource = Resource.Layout.Toolbar;
+
+ base.OnCreate(bundle);
+
+ global::Xamarin.Forms.Forms.Init(this, bundle);
+
+ LoadApplication(new App());
+ }
+
+ protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
+ {
+ base.OnActivityResult(requestCode, resultCode, data);
+ }
+ }
+}
diff --git a/src/Droid/Properties/AndroidManifest.xml b/src/Droid/Properties/AndroidManifest.xml
new file mode 100644
index 0000000..3131983
--- /dev/null
+++ b/src/Droid/Properties/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Droid/Properties/AssemblyInfo.cs b/src/Droid/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..5a134a2
--- /dev/null
+++ b/src/Droid/Properties/AssemblyInfo.cs
@@ -0,0 +1,27 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using Android.App;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle("UITestDemo.Droid")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("${AuthorCopyright}")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion("1.0.0")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
diff --git a/src/Droid/Resources/AboutResources.txt b/src/Droid/Resources/AboutResources.txt
new file mode 100644
index 0000000..10f52d4
--- /dev/null
+++ b/src/Droid/Resources/AboutResources.txt
@@ -0,0 +1,44 @@
+Images, layout descriptions, binary blobs and string dictionaries can be included
+in your application as resource files. Various Android APIs are designed to
+operate on the resource IDs instead of dealing with images, strings or binary blobs
+directly.
+
+For example, a sample Android app that contains a user interface layout (main.axml),
+an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
+would keep its resources in the "Resources" directory of the application:
+
+Resources/
+ drawable/
+ icon.png
+
+ layout/
+ main.axml
+
+ values/
+ strings.xml
+
+In order to get the build system to recognize Android resources, set the build action to
+"AndroidResource". The native Android APIs do not operate directly with filenames, but
+instead operate on resource IDs. When you compile an Android application that uses resources,
+the build system will package the resources for distribution and generate a class called "R"
+(this is an Android convention) that contains the tokens for each one of the resources
+included. For example, for the above Resources layout, this is what the R class would expose:
+
+public class R {
+ public class drawable {
+ public const int icon = 0x123;
+ }
+
+ public class layout {
+ public const int main = 0x456;
+ }
+
+ public class strings {
+ public const int first_string = 0xabc;
+ public const int second_string = 0xbcd;
+ }
+}
+
+You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
+to reference the layout/main.axml file, or R.strings.first_string to reference the first
+string in the dictionary file values/strings.xml.
diff --git a/src/Droid/Resources/drawable-hdpi/icon.png b/src/Droid/Resources/drawable-hdpi/icon.png
new file mode 100644
index 0000000..964f110
Binary files /dev/null and b/src/Droid/Resources/drawable-hdpi/icon.png differ
diff --git a/src/Droid/Resources/drawable-xhdpi/icon.png b/src/Droid/Resources/drawable-xhdpi/icon.png
new file mode 100644
index 0000000..3c01e60
Binary files /dev/null and b/src/Droid/Resources/drawable-xhdpi/icon.png differ
diff --git a/src/Droid/Resources/drawable-xxhdpi/icon.png b/src/Droid/Resources/drawable-xxhdpi/icon.png
new file mode 100644
index 0000000..0d8c1c5
Binary files /dev/null and b/src/Droid/Resources/drawable-xxhdpi/icon.png differ
diff --git a/src/Droid/Resources/drawable/icon.png b/src/Droid/Resources/drawable/icon.png
new file mode 100644
index 0000000..b0ba715
Binary files /dev/null and b/src/Droid/Resources/drawable/icon.png differ
diff --git a/src/Droid/Resources/drawable/xamarin_logo.png b/src/Droid/Resources/drawable/xamarin_logo.png
new file mode 100644
index 0000000..b36d00e
Binary files /dev/null and b/src/Droid/Resources/drawable/xamarin_logo.png differ
diff --git a/src/Droid/Resources/layout/Tabbar.axml b/src/Droid/Resources/layout/Tabbar.axml
new file mode 100644
index 0000000..0bc7e9d
--- /dev/null
+++ b/src/Droid/Resources/layout/Tabbar.axml
@@ -0,0 +1,2 @@
+
+
diff --git a/src/Droid/Resources/layout/Toolbar.axml b/src/Droid/Resources/layout/Toolbar.axml
new file mode 100644
index 0000000..d685cba
--- /dev/null
+++ b/src/Droid/Resources/layout/Toolbar.axml
@@ -0,0 +1,2 @@
+
+
diff --git a/src/Droid/Resources/values/styles.xml b/src/Droid/Resources/values/styles.xml
new file mode 100644
index 0000000..3dc5ef6
--- /dev/null
+++ b/src/Droid/Resources/values/styles.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/src/Droid/UITestDemo.Droid.csproj b/src/Droid/UITestDemo.Droid.csproj
new file mode 100644
index 0000000..edd5421
--- /dev/null
+++ b/src/Droid/UITestDemo.Droid.csproj
@@ -0,0 +1,257 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}
+ {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ Library
+ UITestDemo.Droid
+ UITestDemo.Droid
+ v9.0
+ True
+ Resources\Resource.designer.cs
+ Resource
+ Properties\AndroidManifest.xml
+ Resources
+ Assets
+ false
+ armeabi-v7a;armeabi;x86;arm64-v8a;x86_64
+
+
+ true
+ full
+ false
+ bin\Debug
+ DEBUG;
+ prompt
+ 4
+ None
+ true
+ x86;arm64-v8a;armeabi-v7a;x86_64
+ true
+
+
+ true
+ pdbonly
+ true
+ bin\Release
+ prompt
+ 4
+ true
+ armeabi-v7a;x86;arm64-v8a;x86_64
+
+
+
+
+
+
+
+
+ ..\packages\Xamarin.Android.Support.Annotations.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.Annotations.dll
+
+
+
+ ..\packages\Xamarin.Android.Arch.Core.Common.1.1.1.1\lib\monoandroid90\Xamarin.Android.Arch.Core.Common.dll
+
+
+ ..\packages\Xamarin.Android.Arch.Core.Runtime.1.1.1.1\lib\monoandroid90\Xamarin.Android.Arch.Core.Runtime.dll
+
+
+ ..\packages\Xamarin.Android.Arch.Lifecycle.Common.1.1.1.1\lib\monoandroid90\Xamarin.Android.Arch.Lifecycle.Common.dll
+
+
+ ..\packages\Xamarin.Android.Arch.Lifecycle.LiveData.Core.1.1.1.1\lib\monoandroid90\Xamarin.Android.Arch.Lifecycle.LiveData.Core.dll
+
+
+ ..\packages\Xamarin.Android.Arch.Lifecycle.LiveData.1.1.1.1\lib\monoandroid90\Xamarin.Android.Arch.Lifecycle.LiveData.dll
+
+
+ ..\packages\Xamarin.Android.Arch.Lifecycle.Runtime.1.1.1.1\lib\monoandroid90\Xamarin.Android.Arch.Lifecycle.Runtime.dll
+
+
+ ..\packages\Xamarin.Android.Arch.Lifecycle.ViewModel.1.1.1.1\lib\monoandroid90\Xamarin.Android.Arch.Lifecycle.ViewModel.dll
+
+
+ ..\packages\Xamarin.Android.Support.Collections.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.Collections.dll
+
+
+ ..\packages\Xamarin.Android.Support.CursorAdapter.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.CursorAdapter.dll
+
+
+ ..\packages\Xamarin.Android.Support.DocumentFile.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.DocumentFile.dll
+
+
+ ..\packages\Xamarin.Android.Support.Interpolator.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.Interpolator.dll
+
+
+ ..\packages\Xamarin.Android.Support.LocalBroadcastManager.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.LocalBroadcastManager.dll
+
+
+ ..\packages\Xamarin.Android.Support.Print.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.Print.dll
+
+
+ ..\packages\Xamarin.Android.Support.v7.CardView.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.v7.CardView.dll
+
+
+ ..\packages\Xamarin.Android.Support.VersionedParcelable.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.VersionedParcelable.dll
+
+
+ ..\packages\Xamarin.Android.Support.Compat.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.Compat.dll
+
+
+ ..\packages\Xamarin.Android.Support.AsyncLayoutInflater.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.AsyncLayoutInflater.dll
+
+
+ ..\packages\Xamarin.Android.Support.CustomView.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.CustomView.dll
+
+
+ ..\packages\Xamarin.Android.Support.CoordinaterLayout.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.CoordinaterLayout.dll
+
+
+ ..\packages\Xamarin.Android.Support.DrawerLayout.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.DrawerLayout.dll
+
+
+ ..\packages\Xamarin.Android.Support.Loader.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.Loader.dll
+
+
+ ..\packages\Xamarin.Android.Support.Core.Utils.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.Core.Utils.dll
+
+
+ ..\packages\Xamarin.Android.Support.Media.Compat.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.Media.Compat.dll
+
+
+ ..\packages\Xamarin.Android.Support.SlidingPaneLayout.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.SlidingPaneLayout.dll
+
+
+ ..\packages\Xamarin.Android.Support.SwipeRefreshLayout.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.SwipeRefreshLayout.dll
+
+
+ ..\packages\Xamarin.Android.Support.v7.Palette.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.v7.Palette.dll
+
+
+ ..\packages\Xamarin.Android.Support.Vector.Drawable.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.Vector.Drawable.dll
+
+
+ ..\packages\Xamarin.Android.Support.ViewPager.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.ViewPager.dll
+
+
+ ..\packages\Xamarin.Android.Support.Core.UI.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.Core.UI.dll
+
+
+ ..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.Animated.Vector.Drawable.dll
+
+
+ ..\packages\Xamarin.Android.Support.CustomTabs.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.CustomTabs.dll
+
+
+ ..\packages\Xamarin.Android.Support.Fragment.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.Fragment.dll
+
+
+ ..\packages\Xamarin.Android.Support.Transition.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.Transition.dll
+
+
+ ..\packages\Xamarin.Android.Support.v4.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.v4.dll
+
+
+ ..\packages\Xamarin.Android.Support.v7.AppCompat.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.v7.AppCompat.dll
+
+
+ ..\packages\Xamarin.Android.Support.v7.RecyclerView.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.v7.RecyclerView.dll
+
+
+ ..\packages\Xamarin.Android.Support.Design.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.Design.dll
+
+
+ ..\packages\Xamarin.Android.Support.v7.MediaRouter.28.0.0.1\lib\monoandroid90\Xamarin.Android.Support.v7.MediaRouter.dll
+
+
+ ..\packages\Xamarin.Forms.4.1.0.673156\lib\MonoAndroid90\FormsViewGroup.dll
+
+
+ ..\packages\Xamarin.Forms.4.1.0.673156\lib\MonoAndroid90\Xamarin.Forms.Core.dll
+
+
+ ..\packages\Xamarin.Forms.4.1.0.673156\lib\MonoAndroid90\Xamarin.Forms.Platform.Android.dll
+
+
+ ..\packages\Xamarin.Forms.4.1.0.673156\lib\MonoAndroid90\Xamarin.Forms.Platform.dll
+
+
+ ..\packages\Xamarin.Forms.4.1.0.673156\lib\MonoAndroid90\Xamarin.Forms.Xaml.dll
+
+
+ ..\packages\Newtonsoft.Json.12.0.2\lib\netstandard2.0\Newtonsoft.Json.dll
+
+
+ ..\packages\Xam.Plugin.Connectivity.3.2.0\lib\MonoAndroid10\Plugin.Connectivity.Abstractions.dll
+
+
+ ..\packages\Xam.Plugin.Connectivity.3.2.0\lib\MonoAndroid10\Plugin.Connectivity.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Droid/appcenter-post-build.sh b/src/Droid/appcenter-post-build.sh
new file mode 100644
index 0000000..0d16772
--- /dev/null
+++ b/src/Droid/appcenter-post-build.sh
@@ -0,0 +1,37 @@
+# Environment Variables created by App Center
+# $APPCENTER_SOURCE_DIRECTORY
+# $APPCENTER_OUTPUT_DIRECTORY
+# $APPCENTER_BRANCH
+
+# Custom Environment Variables
+# $API_KEY
+# $TEAM_APP
+# $DEVICE_SET
+UITEST_PATH='Xamarin.UITest/UITestDemo/UITestDemo.UITest'
+
+# DEBUGGING
+# echo "Hello World! I'm a post-build script!"
+# echo "I run at the end of your build."
+# echo "Documentation: https://docs.microsoft.com/en-us/appcenter/build/custom/scripts/#post-build"
+# echo "Referencing source directory: " $APPCENTER_SOURCE_DIRECTORY
+# echo "Contents: "
+# ls $APPCENTER_SOURCE_DIRECTORY
+# echo "& output directory: " $APPCENTER_OUTPUT_DIRECTORY
+# echo "Contents: "
+# ls $APPCENTER_OUTPUT_DIRECTORY
+
+# Build UITest project
+eval MSBuild $APPCENTER_SOURCE_DIRECTORY/$UITEST_PATH -v:q
+
+# DEBUGGING
+# echo "contents of UITest directory:"
+# ls $APPCENTER_SOURCE_DIRECTORY/$UITEST_PATH
+
+# Upload tests
+App_Center_Test_Command='appcenter test run uitest --app $TEAM_APP --devices $DEVICE_SET --app-path $APPCENTER_OUTPUT_DIRECTORY/com.companyname.UITestDemo.apk --test-series "gh-$APPCENTER_BRANCH" --locale "en_US" --build-dir $APPCENTER_SOURCE_DIRECTORY/$UITEST_PATH/bin/Debug --async --token $API_KEY --uitest-tools-dir $APPCENTER_SOURCE_DIRECTORY/Xamarin.UITest/UITestDemo/packages/Xamarin.UITest.*/tools'
+
+echo $App_Center_Test_Command
+eval $App_Center_Test_Command
+
+# End
+echo "end post-build script"
\ No newline at end of file
diff --git a/src/Droid/appcenter-post-clone.sh b/src/Droid/appcenter-post-clone.sh
new file mode 100644
index 0000000..6f97201
--- /dev/null
+++ b/src/Droid/appcenter-post-clone.sh
@@ -0,0 +1,9 @@
+# Comments
+
+echo "Hello World! I'm a post-clone script!"
+
+echo "I run immediately after your project has finished cloning."
+
+echo "Documentation: https://docs.microsoft.com/en-us/appcenter/build/custom/scripts/#post-clone"
+
+echo "end post-clone script"
\ No newline at end of file
diff --git a/src/Droid/appcenter-pre-build.sh b/src/Droid/appcenter-pre-build.sh
new file mode 100644
index 0000000..284e12d
--- /dev/null
+++ b/src/Droid/appcenter-pre-build.sh
@@ -0,0 +1,9 @@
+# Comments
+
+echo "Hello World! I'm a pre-build script!"
+
+echo "I execute after your repo is cloned and dependencies are restored; but before the build actually occurs."
+
+echo "Documentation: https://docs.microsoft.com/en-us/appcenter/build/custom/scripts/#pre-build"
+
+echo "end pre-build script"
\ No newline at end of file
diff --git a/src/Droid/packages.config b/src/Droid/packages.config
new file mode 100644
index 0000000..28021d4
--- /dev/null
+++ b/src/Droid/packages.config
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/UITestDemo.UITest/AppInitializer.cs b/src/UITestDemo.UITest/AppInitializer.cs
new file mode 100644
index 0000000..cde6917
--- /dev/null
+++ b/src/UITestDemo.UITest/AppInitializer.cs
@@ -0,0 +1,52 @@
+using System;
+using System.IO;
+using System.Linq;
+using Xamarin.UITest;
+using Xamarin.UITest.Queries;
+
+namespace UITestDemo.UITest
+{
+ public class AppInitializer
+ {
+ public static IApp StartApp(Platform platform)
+ {
+ // TODO: If the iOS or Android app being tested is included in the solution
+ // then open the Unit Tests window, right click Test Apps, select Add App Project
+ // and select the app projects that should be tested.
+ //
+ // The iOS project should have the Xamarin.TestCloud.Agent NuGet package
+ // installed. To start the Test Cloud Agent the following code should be
+ // added to the FinishedLaunching method of the AppDelegate:
+ //
+ // #if ENABLE_TEST_CLOUD
+ // Xamarin.Calabash.Start();
+ // #endif
+ if (platform == Platform.Android)
+ {
+ return ConfigureApp
+ .Android
+ // TODO: You must use .ApkFile() if:
+ // a) runnning on Windows
+ // b) or your Android project is not referenced by the UITest project.
+ //.ApkFile ("../../../precompiledApps/com.companyname.UITestDemo.apk")
+ .StartApp();
+ }
+
+ // Workaround for iOS simulator reset bug
+ Environment.SetEnvironmentVariable("UITEST_FORCE_IOS_SIM_RESTART", "1");
+
+ return ConfigureApp
+ .iOS
+ // TODO: Update this path to point to your iOS app and uncomment the
+ // code if the app is not included in the solution.
+ // The .AppBundle method is only supported for iOS simulators.
+ // .AppBundle ("../../../precompiledApps/UITestDemo.iOS.app")
+ //
+ // .InstalledApp requires you to build an IPA using the Debug
+ // configuration & a valid provisioning profile, and preinstalling
+ // it on the target device.
+ // .InstalledApp("com.companyname.UITestDemo")
+ .StartApp();
+ }
+ }
+}
diff --git a/src/UITestDemo.UITest/Tests.cs b/src/UITestDemo.UITest/Tests.cs
new file mode 100644
index 0000000..eafceea
--- /dev/null
+++ b/src/UITestDemo.UITest/Tests.cs
@@ -0,0 +1,47 @@
+using System;
+using System.IO;
+using System.Linq;
+using NUnit.Framework;
+using Xamarin.UITest;
+using Xamarin.UITest.Queries;
+
+namespace UITestDemo.UITest
+{
+ [TestFixture(Platform.Android)]
+ [TestFixture(Platform.iOS)]
+ public class Tests
+ {
+ IApp app;
+ Platform platform;
+
+ public Tests(Platform platform)
+ {
+ this.platform = platform;
+ }
+
+ [SetUp]
+ public void BeforeEachTest()
+ {
+ app = AppInitializer.StartApp(platform);
+ }
+
+ [Test]
+ public void AppLaunches()
+ {
+ app.Screenshot("First screen.");
+ }
+
+ [Test]
+ public void ClearTextDemo()
+ {
+ app.Tap(x => x.Marked("Add"));
+ app.Tap(x => x.Text("Item name"));
+
+ app.Screenshot("Before calling ClearText");
+ app.ClearText();
+ app.EnterText("The test worked!");
+ app.Screenshot("Text cleared & replaced");
+ app.Back();
+ }
+ }
+}
diff --git a/src/UITestDemo.UITest/UITestDemo.UITest.csproj b/src/UITestDemo.UITest/UITestDemo.UITest.csproj
new file mode 100644
index 0000000..229dc80
--- /dev/null
+++ b/src/UITestDemo.UITest/UITestDemo.UITest.csproj
@@ -0,0 +1,60 @@
+
+
+
+
+
+ Debug
+ AnyCPU
+ {912248EA-0FED-48E1-8994-6C3080551B2C}
+ Library
+ UITestDemo.UITest
+ UITestDemo.UITest
+ v4.6.1
+
+
+ true
+ full
+ false
+ bin\Debug
+ DEBUG;
+ prompt
+ 4
+
+
+ true
+ bin\Release
+ prompt
+ 4
+
+
+
+
+ ..\packages\Xamarin.UITest.3.0.3\lib\net45\Xamarin.UITest.dll
+
+
+ ..\packages\NUnit.3.12.0\lib\net45\nunit.framework.dll
+
+
+
+
+
+
+
+
+
+
+
+ {499F854C-5244-4E34-92C5-4072B83B84D7}
+ UITestDemo.iOS
+ False
+ False
+
+
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}
+ UITestDemo.Droid
+ False
+ False
+
+
+
+
\ No newline at end of file
diff --git a/src/UITestDemo.UITest/packages.config b/src/UITestDemo.UITest/packages.config
new file mode 100644
index 0000000..87b0ea2
--- /dev/null
+++ b/src/UITestDemo.UITest/packages.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/UITestDemo.sln b/src/UITestDemo.sln
new file mode 100644
index 0000000..0008e44
--- /dev/null
+++ b/src/UITestDemo.sln
@@ -0,0 +1,78 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27703.2026
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "UITestDemo", "UITestDemo\UITestDemo.shproj", "{2933EC8A-6E14-4F77-BEF6-51ACEAFD1040}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UITestDemo.iOS", "iOS\UITestDemo.iOS.csproj", "{499F854C-5244-4E34-92C5-4072B83B84D7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UITestDemo.Droid", "Droid\UITestDemo.Droid.csproj", "{3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UITestDemo.UITest", "UITestDemo.UITest\UITestDemo.UITest.csproj", "{912248EA-0FED-48E1-8994-6C3080551B2C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "upload scripts", "upload scripts", "{18CBDE2C-B150-4C7E-B99F-B1569ED96CB9}"
+ ProjectSection(SolutionItems) = preProject
+ android.sh = android.sh
+ ios.sh = ios.sh
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SharedMSBuildProjectFiles) = preSolution
+ UITestDemo\UITestDemo.projitems*{2933ec8a-6e14-4f77-bef6-51aceafd1040}*SharedItemsImports = 13
+ UITestDemo\UITestDemo.projitems*{3b2fe7a6-e04b-4a11-9872-9ed1ffa093a7}*SharedItemsImports = 4
+ UITestDemo\UITestDemo.projitems*{499f854c-5244-4e34-92c5-4072b83b84d7}*SharedItemsImports = 4
+ EndGlobalSection
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|iPhone = Debug|iPhone
+ Debug|iPhoneSimulator = Debug|iPhoneSimulator
+ Release|Any CPU = Release|Any CPU
+ Release|iPhone = Release|iPhone
+ Release|iPhoneSimulator = Release|iPhoneSimulator
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {499F854C-5244-4E34-92C5-4072B83B84D7}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator
+ {499F854C-5244-4E34-92C5-4072B83B84D7}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator
+ {499F854C-5244-4E34-92C5-4072B83B84D7}.Debug|iPhone.ActiveCfg = Debug|iPhone
+ {499F854C-5244-4E34-92C5-4072B83B84D7}.Debug|iPhone.Build.0 = Debug|iPhone
+ {499F854C-5244-4E34-92C5-4072B83B84D7}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
+ {499F854C-5244-4E34-92C5-4072B83B84D7}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
+ {499F854C-5244-4E34-92C5-4072B83B84D7}.Release|Any CPU.ActiveCfg = Release|iPhone
+ {499F854C-5244-4E34-92C5-4072B83B84D7}.Release|Any CPU.Build.0 = Release|iPhone
+ {499F854C-5244-4E34-92C5-4072B83B84D7}.Release|iPhone.ActiveCfg = Release|iPhone
+ {499F854C-5244-4E34-92C5-4072B83B84D7}.Release|iPhone.Build.0 = Release|iPhone
+ {499F854C-5244-4E34-92C5-4072B83B84D7}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
+ {499F854C-5244-4E34-92C5-4072B83B84D7}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}.Release|iPhone.Build.0 = Release|Any CPU
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {3B2FE7A6-E04B-4A11-9872-9ED1FFA093A7}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {912248EA-0FED-48E1-8994-6C3080551B2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {912248EA-0FED-48E1-8994-6C3080551B2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {912248EA-0FED-48E1-8994-6C3080551B2C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {912248EA-0FED-48E1-8994-6C3080551B2C}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {912248EA-0FED-48E1-8994-6C3080551B2C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {912248EA-0FED-48E1-8994-6C3080551B2C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {912248EA-0FED-48E1-8994-6C3080551B2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {912248EA-0FED-48E1-8994-6C3080551B2C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {912248EA-0FED-48E1-8994-6C3080551B2C}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {912248EA-0FED-48E1-8994-6C3080551B2C}.Release|iPhone.Build.0 = Release|Any CPU
+ {912248EA-0FED-48E1-8994-6C3080551B2C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {912248EA-0FED-48E1-8994-6C3080551B2C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {63409134-6C45-425B-B5D5-F645B9FB3D3D}
+ EndGlobalSection
+EndGlobal
diff --git a/src/UITestDemo/App.xaml b/src/UITestDemo/App.xaml
new file mode 100644
index 0000000..bb06350
--- /dev/null
+++ b/src/UITestDemo/App.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+ #2196F3
+ #1976D2
+ #96d1ff
+ #FAFAFA
+ #C0C0C0
+ #4d4d4d
+ #999999
+
+
+
+
diff --git a/src/UITestDemo/App.xaml.cs b/src/UITestDemo/App.xaml.cs
new file mode 100644
index 0000000..24713de
--- /dev/null
+++ b/src/UITestDemo/App.xaml.cs
@@ -0,0 +1,27 @@
+using System;
+
+using Xamarin.Forms;
+
+namespace UITestDemo
+{
+ public partial class App : Application
+ {
+ public static bool UseMockDataStore = true;
+ public static string BackendUrl = "https://localhost:5000";
+
+ public App()
+ {
+ InitializeComponent();
+
+ if (UseMockDataStore)
+ DependencyService.Register();
+ else
+ DependencyService.Register();
+
+ if (Device.RuntimePlatform == Device.iOS)
+ MainPage = new MainPage();
+ else
+ MainPage = new NavigationPage(new MainPage());
+ }
+ }
+}
diff --git a/src/UITestDemo/Models/Item.cs b/src/UITestDemo/Models/Item.cs
new file mode 100644
index 0000000..ede97ad
--- /dev/null
+++ b/src/UITestDemo/Models/Item.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace UITestDemo
+{
+ public class Item
+ {
+ public string Id { get; set; }
+ public string Text { get; set; }
+ public string Description { get; set; }
+ }
+}
diff --git a/src/UITestDemo/Services/CloudDataStore.cs b/src/UITestDemo/Services/CloudDataStore.cs
new file mode 100644
index 0000000..3a2c43b
--- /dev/null
+++ b/src/UITestDemo/Services/CloudDataStore.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+
+using Newtonsoft.Json;
+using Plugin.Connectivity;
+
+namespace UITestDemo
+{
+ public class CloudDataStore : IDataStore-
+ {
+ HttpClient client;
+ IEnumerable
- items;
+
+ public CloudDataStore()
+ {
+ client = new HttpClient();
+ client.BaseAddress = new Uri($"{App.BackendUrl}/");
+
+ items = new List
- ();
+ }
+
+ public async Task> GetItemsAsync(bool forceRefresh = false)
+ {
+ if (forceRefresh && CrossConnectivity.Current.IsConnected)
+ {
+ var json = await client.GetStringAsync($"api/item");
+ items = await Task.Run(() => JsonConvert.DeserializeObject>(json));
+ }
+
+ return items;
+ }
+
+ public async Task
- GetItemAsync(string id)
+ {
+ if (id != null && CrossConnectivity.Current.IsConnected)
+ {
+ var json = await client.GetStringAsync($"api/item/{id}");
+ return await Task.Run(() => JsonConvert.DeserializeObject
- (json));
+ }
+
+ return null;
+ }
+
+ public async Task AddItemAsync(Item item)
+ {
+ if (item == null || !CrossConnectivity.Current.IsConnected)
+ return false;
+
+ var serializedItem = JsonConvert.SerializeObject(item);
+
+ var response = await client.PostAsync($"api/item", new StringContent(serializedItem, Encoding.UTF8, "application/json"));
+
+ return response.IsSuccessStatusCode;
+ }
+
+ public async Task UpdateItemAsync(Item item)
+ {
+ if (item == null || item.Id == null || !CrossConnectivity.Current.IsConnected)
+ return false;
+
+ var serializedItem = JsonConvert.SerializeObject(item);
+ var buffer = Encoding.UTF8.GetBytes(serializedItem);
+ var byteContent = new ByteArrayContent(buffer);
+
+ var response = await client.PutAsync(new Uri($"api/item/{item.Id}"), byteContent);
+
+ return response.IsSuccessStatusCode;
+ }
+
+ public async Task DeleteItemAsync(string id)
+ {
+ if (string.IsNullOrEmpty(id) && !CrossConnectivity.Current.IsConnected)
+ return false;
+
+ var response = await client.DeleteAsync($"api/item/{id}");
+
+ return response.IsSuccessStatusCode;
+ }
+ }
+}
diff --git a/src/UITestDemo/Services/IDataStore.cs b/src/UITestDemo/Services/IDataStore.cs
new file mode 100644
index 0000000..33af80a
--- /dev/null
+++ b/src/UITestDemo/Services/IDataStore.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace UITestDemo
+{
+ public interface IDataStore
+ {
+ Task AddItemAsync(T item);
+ Task UpdateItemAsync(T item);
+ Task DeleteItemAsync(string id);
+ Task GetItemAsync(string id);
+ Task> GetItemsAsync(bool forceRefresh = false);
+ }
+}
diff --git a/src/UITestDemo/Services/MockDataStore.cs b/src/UITestDemo/Services/MockDataStore.cs
new file mode 100644
index 0000000..b308b47
--- /dev/null
+++ b/src/UITestDemo/Services/MockDataStore.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace UITestDemo
+{
+ public class MockDataStore : IDataStore
-
+ {
+ List
- items;
+
+ public MockDataStore()
+ {
+ items = new List
- ();
+ var mockItems = new List
-
+ {
+ new Item { Id = Guid.NewGuid().ToString(), Text = "First item", Description="This is an item description." },
+ new Item { Id = Guid.NewGuid().ToString(), Text = "Second item", Description="This is an item description." },
+ new Item { Id = Guid.NewGuid().ToString(), Text = "Third item", Description="This is an item description." },
+ new Item { Id = Guid.NewGuid().ToString(), Text = "Fourth item", Description="This is an item description." },
+ new Item { Id = Guid.NewGuid().ToString(), Text = "Fifth item", Description="This is an item description." },
+ new Item { Id = Guid.NewGuid().ToString(), Text = "Sixth item", Description="This is an item description." },
+ };
+
+ foreach (var item in mockItems)
+ {
+ items.Add(item);
+ }
+ }
+
+ public async Task AddItemAsync(Item item)
+ {
+ items.Add(item);
+
+ return await Task.FromResult(true);
+ }
+
+ public async Task UpdateItemAsync(Item item)
+ {
+ var _item = items.Where((Item arg) => arg.Id == item.Id).FirstOrDefault();
+ items.Remove(_item);
+ items.Add(item);
+
+ return await Task.FromResult(true);
+ }
+
+ public async Task DeleteItemAsync(string id)
+ {
+ var _item = items.Where((Item arg) => arg.Id == id).FirstOrDefault();
+ items.Remove(_item);
+
+ return await Task.FromResult(true);
+ }
+
+ public async Task
- GetItemAsync(string id)
+ {
+ return await Task.FromResult(items.FirstOrDefault(s => s.Id == id));
+ }
+
+ public async Task> GetItemsAsync(bool forceRefresh = false)
+ {
+ return await Task.FromResult(items);
+ }
+ }
+}
diff --git a/src/UITestDemo/UITestDemo.projitems b/src/UITestDemo/UITestDemo.projitems
new file mode 100644
index 0000000..099fdcb
--- /dev/null
+++ b/src/UITestDemo/UITestDemo.projitems
@@ -0,0 +1,44 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ {2933EC8A-6E14-4F77-BEF6-51ACEAFD1040}
+
+
+ UITestDemo
+
+
+
+
+
+
+
+
+
+
+
+
+ AboutPage.xaml
+
+
+ ItemDetailPage.xaml
+
+
+ ItemsPage.xaml
+
+
+ NewItemPage.xaml
+
+
+ App.xaml
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/UITestDemo/UITestDemo.shproj b/src/UITestDemo/UITestDemo.shproj
new file mode 100644
index 0000000..c409d9f
--- /dev/null
+++ b/src/UITestDemo/UITestDemo.shproj
@@ -0,0 +1,11 @@
+
+
+
+ {2933EC8A-6E14-4F77-BEF6-51ACEAFD1040}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/UITestDemo/ViewModels/AboutViewModel.cs b/src/UITestDemo/ViewModels/AboutViewModel.cs
new file mode 100644
index 0000000..0c3c366
--- /dev/null
+++ b/src/UITestDemo/ViewModels/AboutViewModel.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Windows.Input;
+
+using Xamarin.Forms;
+
+namespace UITestDemo
+{
+ public class AboutViewModel : BaseViewModel
+ {
+ public AboutViewModel()
+ {
+ Title = "About";
+
+ OpenWebCommand = new Command(() => Device.OpenUri(new Uri("https://xamarin.com/platform")));
+ }
+
+ public ICommand OpenWebCommand { get; }
+ }
+}
diff --git a/src/UITestDemo/ViewModels/BaseViewModel.cs b/src/UITestDemo/ViewModels/BaseViewModel.cs
new file mode 100644
index 0000000..e728ffe
--- /dev/null
+++ b/src/UITestDemo/ViewModels/BaseViewModel.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+using Xamarin.Forms;
+
+namespace UITestDemo
+{
+ public class BaseViewModel : INotifyPropertyChanged
+ {
+ public IDataStore
- DataStore => DependencyService.Get>() ?? new MockDataStore();
+
+ bool isBusy = false;
+ public bool IsBusy
+ {
+ get { return isBusy; }
+ set { SetProperty(ref isBusy, value); }
+ }
+
+ string title = string.Empty;
+ public string Title
+ {
+ get { return title; }
+ set { SetProperty(ref title, value); }
+ }
+
+ protected bool SetProperty(ref T backingStore, T value,
+ [CallerMemberName]string propertyName = "",
+ Action onChanged = null)
+ {
+ if (EqualityComparer.Default.Equals(backingStore, value))
+ return false;
+
+ backingStore = value;
+ onChanged?.Invoke();
+ OnPropertyChanged(propertyName);
+ return true;
+ }
+
+ #region INotifyPropertyChanged
+ public event PropertyChangedEventHandler PropertyChanged;
+ protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
+ {
+ var changed = PropertyChanged;
+ if (changed == null)
+ return;
+
+ changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ #endregion
+ }
+}
diff --git a/src/UITestDemo/ViewModels/ItemDetailViewModel.cs b/src/UITestDemo/ViewModels/ItemDetailViewModel.cs
new file mode 100644
index 0000000..6aeffb8
--- /dev/null
+++ b/src/UITestDemo/ViewModels/ItemDetailViewModel.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace UITestDemo
+{
+ public class ItemDetailViewModel : BaseViewModel
+ {
+ public Item Item { get; set; }
+ public ItemDetailViewModel(Item item = null)
+ {
+ Title = item?.Text;
+ Item = item;
+ }
+ }
+}
diff --git a/src/UITestDemo/ViewModels/ItemsViewModel.cs b/src/UITestDemo/ViewModels/ItemsViewModel.cs
new file mode 100644
index 0000000..252e634
--- /dev/null
+++ b/src/UITestDemo/ViewModels/ItemsViewModel.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Threading.Tasks;
+
+using Xamarin.Forms;
+
+namespace UITestDemo
+{
+ public class ItemsViewModel : BaseViewModel
+ {
+ public ObservableCollection
- Items { get; set; }
+ public Command LoadItemsCommand { get; set; }
+
+ public ItemsViewModel()
+ {
+ Title = "Browse";
+ Items = new ObservableCollection
- ();
+ LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
+
+ MessagingCenter.Subscribe(this, "AddItem", async (obj, item) =>
+ {
+ var _item = item as Item;
+ Items.Add(_item);
+ await DataStore.AddItemAsync(_item);
+ });
+ }
+
+ async Task ExecuteLoadItemsCommand()
+ {
+ if (IsBusy)
+ return;
+
+ IsBusy = true;
+
+ try
+ {
+ Items.Clear();
+ var items = await DataStore.GetItemsAsync(true);
+ foreach (var item in items)
+ {
+ Items.Add(item);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex);
+ }
+ finally
+ {
+ IsBusy = false;
+ }
+ }
+ }
+}
diff --git a/src/UITestDemo/Views/AboutPage.xaml b/src/UITestDemo/Views/AboutPage.xaml
new file mode 100644
index 0000000..2cf4a9d
--- /dev/null
+++ b/src/UITestDemo/Views/AboutPage.xaml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/UITestDemo/Views/AboutPage.xaml.cs b/src/UITestDemo/Views/AboutPage.xaml.cs
new file mode 100644
index 0000000..48cea16
--- /dev/null
+++ b/src/UITestDemo/Views/AboutPage.xaml.cs
@@ -0,0 +1,14 @@
+using System;
+
+using Xamarin.Forms;
+
+namespace UITestDemo
+{
+ public partial class AboutPage : ContentPage
+ {
+ public AboutPage()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/src/UITestDemo/Views/ItemDetailPage.xaml b/src/UITestDemo/Views/ItemDetailPage.xaml
new file mode 100644
index 0000000..23ac8ed
--- /dev/null
+++ b/src/UITestDemo/Views/ItemDetailPage.xaml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/src/UITestDemo/Views/ItemDetailPage.xaml.cs b/src/UITestDemo/Views/ItemDetailPage.xaml.cs
new file mode 100644
index 0000000..d3924a4
--- /dev/null
+++ b/src/UITestDemo/Views/ItemDetailPage.xaml.cs
@@ -0,0 +1,33 @@
+using System;
+
+using Xamarin.Forms;
+
+namespace UITestDemo
+{
+ public partial class ItemDetailPage : ContentPage
+ {
+ ItemDetailViewModel viewModel;
+
+ // Note - The Xamarin.Forms Previewer requires a default, parameterless constructor to render a page.
+ public ItemDetailPage()
+ {
+ InitializeComponent();
+
+ var item = new Item
+ {
+ Text = "Item 1",
+ Description = "This is an item description."
+ };
+
+ viewModel = new ItemDetailViewModel(item);
+ BindingContext = viewModel;
+ }
+
+ public ItemDetailPage(ItemDetailViewModel viewModel)
+ {
+ InitializeComponent();
+
+ BindingContext = this.viewModel = viewModel;
+ }
+ }
+}
diff --git a/src/UITestDemo/Views/ItemsPage.xaml b/src/UITestDemo/Views/ItemsPage.xaml
new file mode 100644
index 0000000..b39ed09
--- /dev/null
+++ b/src/UITestDemo/Views/ItemsPage.xaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/UITestDemo/Views/ItemsPage.xaml.cs b/src/UITestDemo/Views/ItemsPage.xaml.cs
new file mode 100644
index 0000000..50b3563
--- /dev/null
+++ b/src/UITestDemo/Views/ItemsPage.xaml.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+using Xamarin.Forms;
+
+namespace UITestDemo
+{
+ public partial class ItemsPage : ContentPage
+ {
+ ItemsViewModel viewModel;
+
+ public ItemsPage()
+ {
+ InitializeComponent();
+
+ BindingContext = viewModel = new ItemsViewModel();
+ }
+
+ async void OnItemSelected(object sender, SelectedItemChangedEventArgs args)
+ {
+ var item = args.SelectedItem as Item;
+ if (item == null)
+ return;
+
+ await Navigation.PushAsync(new ItemDetailPage(new ItemDetailViewModel(item)));
+
+ // Manually deselect item
+ ItemsListView.SelectedItem = null;
+ }
+
+ async void AddItem_Clicked(object sender, EventArgs e)
+ {
+ await Navigation.PushAsync(new NewItemPage());
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+
+ if (viewModel.Items.Count == 0)
+ viewModel.LoadItemsCommand.Execute(null);
+ }
+ }
+}
diff --git a/src/UITestDemo/Views/MainPage.cs b/src/UITestDemo/Views/MainPage.cs
new file mode 100644
index 0000000..1897a08
--- /dev/null
+++ b/src/UITestDemo/Views/MainPage.cs
@@ -0,0 +1,53 @@
+using System;
+
+using Xamarin.Forms;
+
+namespace UITestDemo
+{
+ public class MainPage : TabbedPage
+ {
+ public MainPage()
+ {
+ Page itemsPage, aboutPage = null;
+
+ switch (Device.RuntimePlatform)
+ {
+ case Device.iOS:
+ itemsPage = new NavigationPage(new ItemsPage())
+ {
+ Title = "Browse"
+ };
+
+ aboutPage = new NavigationPage(new AboutPage())
+ {
+ Title = "About"
+ };
+ itemsPage.Icon = "tab_feed.png";
+ aboutPage.Icon = "tab_about.png";
+ break;
+ default:
+ itemsPage = new ItemsPage()
+ {
+ Title = "Browse"
+ };
+
+ aboutPage = new AboutPage()
+ {
+ Title = "About"
+ };
+ break;
+ }
+
+ Children.Add(itemsPage);
+ Children.Add(aboutPage);
+
+ Title = Children[0].Title;
+ }
+
+ protected override void OnCurrentPageChanged()
+ {
+ base.OnCurrentPageChanged();
+ Title = CurrentPage?.Title ?? string.Empty;
+ }
+ }
+}
diff --git a/src/UITestDemo/Views/NewItemPage.xaml b/src/UITestDemo/Views/NewItemPage.xaml
new file mode 100644
index 0000000..6ace672
--- /dev/null
+++ b/src/UITestDemo/Views/NewItemPage.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/UITestDemo/Views/NewItemPage.xaml.cs b/src/UITestDemo/Views/NewItemPage.xaml.cs
new file mode 100644
index 0000000..81ca5b6
--- /dev/null
+++ b/src/UITestDemo/Views/NewItemPage.xaml.cs
@@ -0,0 +1,30 @@
+using System;
+
+using Xamarin.Forms;
+
+namespace UITestDemo
+{
+ public partial class NewItemPage : ContentPage
+ {
+ public Item Item { get; set; }
+
+ public NewItemPage()
+ {
+ InitializeComponent();
+
+ Item = new Item
+ {
+ Text = "Item name",
+ Description = "This is an item description."
+ };
+
+ BindingContext = this;
+ }
+
+ async void Save_Clicked(object sender, EventArgs e)
+ {
+ MessagingCenter.Send(this, "AddItem", Item);
+ await Navigation.PopToRootAsync();
+ }
+ }
+}
diff --git a/src/android.sh b/src/android.sh
new file mode 100644
index 0000000..b22d873
--- /dev/null
+++ b/src/android.sh
@@ -0,0 +1,18 @@
+# Setup & usage
+# Step 0 Build the UITest project & generate an APK using the "Release" configuration (or use the provided APK)
+# Step 1 Generate an AppCenter upload command and paste it to the variable
+AppCenter_Test_Command='paste upload command here'
+
+# Step 2 Provide the (absolute or relative) path to the apk
+app_path='precompiledApps/com.companyname.UITestDemo.apk'
+
+# Step 3 Provide the (absolute or relative) path to the UITest project bin folder
+build_dir='UITestDemo.UITest/bin/Debug'
+
+# Step 4 run using the command "sh android.sh"
+
+# Script injects app_path & build_dir and executes resulting command
+AppCenter_Test_Command=${AppCenter_Test_Command/'pathToFile.apk'/$app_path}
+AppCenter_Test_Command=${AppCenter_Test_Command/'pathToUITestBuildDir'/$build_dir}
+echo $AppCenter_Test_Command
+eval $AppCenter_Test_Command
diff --git a/src/iOS/AppDelegate.cs b/src/iOS/AppDelegate.cs
new file mode 100644
index 0000000..67b14a9
--- /dev/null
+++ b/src/iOS/AppDelegate.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Foundation;
+using UIKit;
+
+namespace UITestDemo.iOS
+{
+ [Register("AppDelegate")]
+ public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
+ {
+ public override bool FinishedLaunching(UIApplication app, NSDictionary options)
+ {
+ global::Xamarin.Forms.Forms.Init();
+ LoadApplication(new App());
+
+#if ENABLE_TEST_CLOUD
+ Xamarin.Calabash.Start();
+#endif
+
+
+ return base.FinishedLaunching(app, options);
+ }
+ }
+}
diff --git a/src/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/src/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..64d0e3d
--- /dev/null
+++ b/src/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,202 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "20x20",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "83.5x83.5",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "size" : "1024x1024",
+ "scale" : "1x"
+ },
+ {
+ "size" : "24x24",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "notificationCenter",
+ "subtype" : "38mm"
+ },
+ {
+ "size" : "27.5x27.5",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "notificationCenter",
+ "subtype" : "42mm"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "watch",
+ "role" : "companionSettings",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "watch",
+ "role" : "companionSettings",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "appLauncher",
+ "subtype" : "38mm"
+ },
+ {
+ "size" : "44x44",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "longLook",
+ "subtype" : "42mm"
+ },
+ {
+ "size" : "86x86",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "quickLook",
+ "subtype" : "38mm"
+ },
+ {
+ "size" : "98x98",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "quickLook",
+ "subtype" : "42mm"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "128x128",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "128x128",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "256x256",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "256x256",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/src/iOS/Assets.xcassets/Contents.json b/src/iOS/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..4caf392
--- /dev/null
+++ b/src/iOS/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/src/iOS/Entitlements.plist b/src/iOS/Entitlements.plist
new file mode 100644
index 0000000..9ae5993
--- /dev/null
+++ b/src/iOS/Entitlements.plist
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/iOS/Info.plist b/src/iOS/Info.plist
new file mode 100644
index 0000000..b1964ef
--- /dev/null
+++ b/src/iOS/Info.plist
@@ -0,0 +1,50 @@
+
+
+
+
+ CFBundleDisplayName
+ UITestDemo
+ CFBundleName
+ UITestDemo
+ CFBundleIdentifier
+ com.appcenter.UITestDemo
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1.0
+ LSRequiresIPhoneOS
+
+ MinimumOSVersion
+ 8.0
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/AppIcon.appiconset
+ UIStatusBarStyle
+ UIStatusBarStyleLightContent
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/src/iOS/LaunchScreen.storyboard b/src/iOS/LaunchScreen.storyboard
new file mode 100644
index 0000000..5d2e905
--- /dev/null
+++ b/src/iOS/LaunchScreen.storyboard
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/iOS/Main.cs b/src/iOS/Main.cs
new file mode 100644
index 0000000..b1a5919
--- /dev/null
+++ b/src/iOS/Main.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Foundation;
+using UIKit;
+
+namespace UITestDemo.iOS
+{
+ public class Application
+ {
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, "AppDelegate");
+ }
+ }
+}
diff --git a/src/iOS/Resources/tab_about.png b/src/iOS/Resources/tab_about.png
new file mode 100644
index 0000000..4884525
Binary files /dev/null and b/src/iOS/Resources/tab_about.png differ
diff --git a/src/iOS/Resources/tab_about@2x.png b/src/iOS/Resources/tab_about@2x.png
new file mode 100644
index 0000000..f9ba9f9
Binary files /dev/null and b/src/iOS/Resources/tab_about@2x.png differ
diff --git a/src/iOS/Resources/tab_about@3x.png b/src/iOS/Resources/tab_about@3x.png
new file mode 100644
index 0000000..fa470c2
Binary files /dev/null and b/src/iOS/Resources/tab_about@3x.png differ
diff --git a/src/iOS/Resources/tab_feed.png b/src/iOS/Resources/tab_feed.png
new file mode 100644
index 0000000..fdf6daf
Binary files /dev/null and b/src/iOS/Resources/tab_feed.png differ
diff --git a/src/iOS/Resources/tab_feed@2x.png b/src/iOS/Resources/tab_feed@2x.png
new file mode 100644
index 0000000..845d96c
Binary files /dev/null and b/src/iOS/Resources/tab_feed@2x.png differ
diff --git a/src/iOS/Resources/tab_feed@3x.png b/src/iOS/Resources/tab_feed@3x.png
new file mode 100644
index 0000000..812cf73
Binary files /dev/null and b/src/iOS/Resources/tab_feed@3x.png differ
diff --git a/src/iOS/Resources/xamarin_logo.png b/src/iOS/Resources/xamarin_logo.png
new file mode 100644
index 0000000..7d5007d
Binary files /dev/null and b/src/iOS/Resources/xamarin_logo.png differ
diff --git a/src/iOS/Resources/xamarin_logo@2x.png b/src/iOS/Resources/xamarin_logo@2x.png
new file mode 100644
index 0000000..a5b9fc0
Binary files /dev/null and b/src/iOS/Resources/xamarin_logo@2x.png differ
diff --git a/src/iOS/Resources/xamarin_logo@3x.png b/src/iOS/Resources/xamarin_logo@3x.png
new file mode 100644
index 0000000..b36d00e
Binary files /dev/null and b/src/iOS/Resources/xamarin_logo@3x.png differ
diff --git a/src/iOS/UITestDemo.iOS.csproj b/src/iOS/UITestDemo.iOS.csproj
new file mode 100644
index 0000000..d1479c6
--- /dev/null
+++ b/src/iOS/UITestDemo.iOS.csproj
@@ -0,0 +1,147 @@
+
+
+
+
+ Debug
+ iPhoneSimulator
+ {499F854C-5244-4E34-92C5-4072B83B84D7}
+ {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ Exe
+ UITestDemo.iOS
+ UITestDemo.iOS
+ Resources
+
+
+ true
+ full
+ false
+ bin\iPhoneSimulator\Debug
+ DEBUG;ENABLE_TEST_CLOUD;
+ prompt
+ 4
+ iPhone Developer
+ true
+ true
+ true
+ true
+ 25512
+ None
+ i386, x86_64
+ NSUrlSessionHandler
+ x86
+
+
+ pdbonly
+ true
+ bin\iPhone\Release
+ prompt
+ 4
+ iPhone Developer
+ true
+ Entitlements.plist
+ SdkOnly
+ ARM64
+ HttpClientHandler
+ x86
+
+
+ pdbonly
+ true
+ bin\iPhoneSimulator\Release
+ prompt
+ 4
+ iPhone Developer
+ true
+ None
+ x86_64
+ HttpClientHandler
+ x86
+
+
+ true
+ full
+ false
+ bin\iPhone\Debug
+ DEBUG;ENABLE_TEST_CLOUD;
+ prompt
+ 4
+ iPhone Developer
+ true
+ true
+ true
+ true
+ true
+ Entitlements.plist
+ 59905
+ SdkOnly
+ ARMv7, ARMv7s, ARM64
+ HttpClientHandler
+ x86
+
+
+
+
+
+
+
+
+ ..\packages\Newtonsoft.Json.12.0.2\lib\netstandard2.0\Newtonsoft.Json.dll
+
+
+ ..\packages\Xam.Plugin.Connectivity.3.2.0\lib\Xamarin.iOS10\Plugin.Connectivity.Abstractions.dll
+
+
+ ..\packages\Xam.Plugin.Connectivity.3.2.0\lib\Xamarin.iOS10\Plugin.Connectivity.dll
+
+
+ ..\packages\Xamarin.TestCloud.Agent.0.21.8\lib\Xamarin.iOS\Calabash.dll
+ False
+
+
+ ..\packages\Xamarin.Forms.4.1.0.673156\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll
+
+
+ ..\packages\Xamarin.Forms.4.1.0.673156\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll
+
+
+ ..\packages\Xamarin.Forms.4.1.0.673156\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll
+
+
+ ..\packages\Xamarin.Forms.4.1.0.673156\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll
+
+
+
+
+ false
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/iOS/appcenter-post-build.sh b/src/iOS/appcenter-post-build.sh
new file mode 100644
index 0000000..b08a1ed
--- /dev/null
+++ b/src/iOS/appcenter-post-build.sh
@@ -0,0 +1,37 @@
+# Environment Variables created by App Center
+# $APPCENTER_SOURCE_DIRECTORY
+# $APPCENTER_OUTPUT_DIRECTORY
+# $APPCENTER_BRANCH
+
+# Custom Environment Variables
+# $API_KEY
+# $TEAM_APP
+# $DEVICE_SET
+UITEST_PATH='Xamarin.UITest/UITestDemo/UITestDemo.UITest'
+
+# DEBUGGING
+# echo "Hello World! I'm a post-build script!"
+# echo "I run at the end of your build."
+# echo "Documentation: https://docs.microsoft.com/en-us/appcenter/build/custom/scripts/#post-build"
+# echo "Referencing source directory: " $APPCENTER_SOURCE_DIRECTORY
+# echo "Contents: "
+# ls $APPCENTER_SOURCE_DIRECTORY
+# echo "& output directory: " $APPCENTER_OUTPUT_DIRECTORY
+# echo "Contents: "
+# ls $APPCENTER_OUTPUT_DIRECTORY
+
+# Build UITest project
+eval MSBuild $APPCENTER_SOURCE_DIRECTORY/$UITEST_PATH -v:q
+
+# DEBUGGING
+# echo "contents of UITest directory:"
+# ls $APPCENTER_SOURCE_DIRECTORY/$UITEST_PATH
+
+# Upload tests
+App_Center_Test_Command='appcenter test run uitest --app $TEAM_APP --devices $DEVICE_SET --app-path $APPCENTER_OUTPUT_DIRECTORY/UITestDemo.ipa --test-series "gh-$APPCENTER_BRANCH" --locale "en_US" --build-dir $APPCENTER_SOURCE_DIRECTORY/$UITEST_PATH/bin/Debug --async --token $API_KEY --uitest-tools-dir $APPCENTER_SOURCE_DIRECTORY/Xamarin.UITest/UITestDemo/packages/Xamarin.UITest.*/tools'
+
+echo $App_Center_Test_Command
+eval $App_Center_Test_Command
+
+# End
+echo "end post-build script"
diff --git a/src/iOS/appcenter-post-clone.sh b/src/iOS/appcenter-post-clone.sh
new file mode 100644
index 0000000..6f97201
--- /dev/null
+++ b/src/iOS/appcenter-post-clone.sh
@@ -0,0 +1,9 @@
+# Comments
+
+echo "Hello World! I'm a post-clone script!"
+
+echo "I run immediately after your project has finished cloning."
+
+echo "Documentation: https://docs.microsoft.com/en-us/appcenter/build/custom/scripts/#post-clone"
+
+echo "end post-clone script"
\ No newline at end of file
diff --git a/src/iOS/appcenter-pre-build.sh b/src/iOS/appcenter-pre-build.sh
new file mode 100644
index 0000000..284e12d
--- /dev/null
+++ b/src/iOS/appcenter-pre-build.sh
@@ -0,0 +1,9 @@
+# Comments
+
+echo "Hello World! I'm a pre-build script!"
+
+echo "I execute after your repo is cloned and dependencies are restored; but before the build actually occurs."
+
+echo "Documentation: https://docs.microsoft.com/en-us/appcenter/build/custom/scripts/#pre-build"
+
+echo "end pre-build script"
\ No newline at end of file
diff --git a/src/iOS/packages.config b/src/iOS/packages.config
new file mode 100644
index 0000000..ad89df5
--- /dev/null
+++ b/src/iOS/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/ios.sh b/src/ios.sh
new file mode 100644
index 0000000..0e46a47
--- /dev/null
+++ b/src/ios.sh
@@ -0,0 +1,18 @@
+# Setup & usage
+# Step 0 Build the UITest project & generate an IPA using the "Debug" configuration (or use the provided IPA)
+# Step 1 Generate an AppCenter upload command and paste it to the variable
+AppCenter_Test_Command='paste upload command here'
+
+# Step 2 Provide the (absolute or relative) path to the IPA
+app_path='precompiledApps/UITestDemo.ipa'
+
+# Step 3 Provide the (absolute or relative) path to the UITest project bin folder
+build_dir='UITestDemo.UITest/bin/Debug'
+
+# Step 4 run using the command "sh ios.sh"
+
+# Script injects app_path & build_dir and executes resulting command
+AppCenter_Test_Command=${AppCenter_Test_Command/'pathToFile.ipa'/$app_path}
+AppCenter_Test_Command=${AppCenter_Test_Command/'pathToUITestBuildDir'/$build_dir}
+echo $AppCenter_Test_Command
+eval $AppCenter_Test_Command
\ No newline at end of file
diff --git a/src/precompiledApps/UITestDemo.iOS.app.zip b/src/precompiledApps/UITestDemo.iOS.app.zip
new file mode 100644
index 0000000..1c5bf3c
Binary files /dev/null and b/src/precompiledApps/UITestDemo.iOS.app.zip differ
diff --git a/src/precompiledApps/UITestDemo.ipa b/src/precompiledApps/UITestDemo.ipa
new file mode 100644
index 0000000..fbe2a7c
Binary files /dev/null and b/src/precompiledApps/UITestDemo.ipa differ
diff --git a/src/precompiledApps/com.companyname.UITestDemo-Signed.apk b/src/precompiledApps/com.companyname.UITestDemo-Signed.apk
new file mode 100644
index 0000000..1318387
Binary files /dev/null and b/src/precompiledApps/com.companyname.UITestDemo-Signed.apk differ
diff --git a/src/precompiledApps/com.companyname.UITestDemo.apk b/src/precompiledApps/com.companyname.UITestDemo.apk
new file mode 100644
index 0000000..bc114c9
Binary files /dev/null and b/src/precompiledApps/com.companyname.UITestDemo.apk differ
diff --git a/src/readme.md b/src/readme.md
new file mode 100644
index 0000000..4be37f5
--- /dev/null
+++ b/src/readme.md
@@ -0,0 +1,90 @@
+# Build Status Badges
+- iOS App (simulator build) (master branch) - [![Build status](https://build.appcenter.ms/v0.1/apps/0165f989-7687-45c4-8495-fd1f3e22a728/branches/master/badge)](https://appcenter.ms)
+
+# Overview (Xamarin.UITest - Cross-Platform)
+This sample is designed for use in Visual Studio & includes the sourcecode for a Xamarin.Forms app on Android & iOS platforms & a Xamarin.UITest project. It also includes a precompiled APK & IPA incase you want to try only building, running and uploading Xamarin.UITest.
+
+# Building & running locally
+You can either build the entire solution, or you can just build the Xamarin.UITest project.
+
+## Windows users
+Compared to running on Mac, Xamarin.UITests have additional requirements and limitations as follows:
+- Xamarin.UITest cannot be run locally against iOS apps in Windows, even if using a paired Mac capable of building iOS apps.
+- NUnitTestAdapter 2.1.1 is required to run tests locally in Windows. It is not required to run tests in Mac or tests submitted to Visual Studio App Center Test.
+- You must set the ANDROID_HOME enviroment variable to the main folder of your Android SDK. You can create this variable as follows:
+ 1. Go to **Control Panel > System > Advanced System Settings > Environment Variables > System Variables* > New…**
+ 2. Name the variable `ANDROID_HOME` and set it to your Android SDK path. (For example `C:\Program Files (x86)\Android\android-sdk`, your location may vary.)
+ 3. Close all instances of Visual Studio and reopen for the updated `ANDROID_HOME` to take effect.
+- You must use the `.ApkFile()` method to [point to the APK](UITestDemo.UITest/AppInitializer.cs#L30).
+
+## Running locally without building the Android/iOS apps
+You can run the UITest project locally without building the app projects by using precompiled files:
+
+1. Open "UITestDemo.UITest > AppInitializer.cs"
+2. Point to the app files:
+- To use the precompiled APK, uncomment the line for '.ApkFile'
+- To use the precompiled iOS simulator .app, unzip the app & uncomment the line for '.AppBundle'
+(You cannot use the precompiled IPA for local testing because it will not be signed for one of your devices. If you build the IPA for the project first though, you can install it on the device and point to it using '.InstalledApp'.)
+
+3. Build the Xamarin.UITest project.
+4. Running the tests differs slightly if you're using Visual Studio for Mac or Visual Studio on Windows:
+ - **VS for Mac** - Go to **View > Pads > Unit Tests > Run All**
+ - **VS for Windows** - Go to **Test > Windows > Test Explorer > Run All**. When testing on Windows, only Android is supported.
+
+
+## Running locally with building the Android/iOS apps
+1. You can build individual projects or the entire solution by right-clicking them in the solution pad and selecting "Build [Project/Solution Name]."
+
+2. Be aware of the following considerations for using Xamarin.UITest with these built projects:
+ - 'UITestDemo.Droid' must be built using a "Release" configuration. This is because by default a "Debug" build of a Xamarin.Android project includes the "Shared Mono Runtime" which is not compatible with Xamarin.UITest.
+ - 'UITestDemo.iOS' must be built using a "Debug" configuration. This is because by default iOS apps built for distrbution are rejected from the iOS app store if they contain 'Calabash' the testing framework which allows Xamarin.UITest to interact with iOS apps.
+ - Android APKs can be run on either a physical Android device or emulator interchangably. iOS must use a '.app' build to run on an iOS simulator & an '.IPA' with a valid signing identity to run on an iOS device.
+
+3. Running the tests differs slightly if you're using Visual Studio for Mac or Visual Studio on Windows:
+ - **VS for Mac** - Go to **View > Pads > Unit Tests > Run All**
+ - **VS for Windows** - Go to **Test > Windows > Test Explorer > Run All**. When testing on Windows, only Android is supported; and you must also set the [`.ApkFile()` path in the AppInitializer.cs ConfigureApp statement](/Xamarin.UITest/UITestDemo/UITestDemo.UITest/AppInitializer.cs#L31)
+
+# Uploading tests
+1. Build the Xamarin.UITest project.
+2. Generate a command line for upload: [Directions](/../../#upload-commands)
+3. Update the upload command with project-specific arguments:
+ - **OS X** paste your command as the value for 'AppCenter_Test_Command' in 'android.sh' or 'ios.sh' depending on the platform. You can run these files using 'sh android.sh' or 'sh ios.sh'.
+ - **Windows** The '.sh' files are not technically compatible with Windows, however it shows how to modify the generated command to upload this sample manually.
+
+#### See Also
+- Android upload script: [android.sh](android.sh)
+- iOS upload script: [ios.sh](ios.sh)
+
+# Building in App Center
+Documentation reference: https://docs.microsoft.com/en-us/appcenter/build/
+This blog also details most of the steps required, though a few details are out of date: https://tomsoderling.github.io/AppCenter-Automated-UI-tests-on-build/
+
+**To build apps in App Center, you must own the repository you wish to build from. For example, to use the samples in this repo, you have to fork this repository.**
+
+## Integrating Test Suite on Android
+If you have the build working on it's own in App Center Build, then there just a few more steps to enable Test support. These steps are handled by the script called [appcenter-post-build.sh](Droid/appcenter-post-build.sh) in the "Droid" project folder.
+
+1. For this example, Add the Custom Environment Varaibles to your build settings in App Center:
+ - `$API_KEY` - You can use an existing API key or generate a new one (https://intercom.help/appcenter/articles/1841885-how-to-use-app-center-s-api)
+ - `$TEAM_APP` - This is the argument given to the `--app` flag in your Test upload command.
+ - `$DEVICE_SET` - This is the argument given to the `--devices` flag in your Test upload command.
+
+2. Make sure the first time you use the script to manually select "Save & Build" in the App Center Build dialog. Otherwise the build script will be ignored.
+
+### Background Info on how the script works
+To use this script note the following:
+- Technically all of the commented sections & 'echo' statements are just there to help you understand what the script is doing; the script would work if reduced only to declaring the variables & evaluating statements.
+
+- It's important to build the Xamarin.UITest project in the command line; as that is not handled automatically as part of the Android app project build. This is handled by the `MSBuild` command in the script.
+
+- Your App Center upload command will require a few extra arguments compared to a typical upload:
+ - `--aync` - This prevents your build from waiting for the test results and timing out.
+ - `--token` - Setting an API token since the Cloud Build machine is not logged in to your App Center identity.
+ - `--uitest-tools-dir` - Explicitly pointing to the Xamarin.UITest package tools folder. (Usually in a local system this is found automatically.)
+
+- There are 3 types of environment variables used to help the script:
+ 1. Variables created by App Center, which can be accessed directly by your script. These variables behave similarly to variables set universally on a local system. (Documentation: https://docs.microsoft.com/en-us/appcenter/build/custom/scripts/#app-center-variables)
+ 2. Variables created in your Build configuration settings. These will automatically be set for you on the Build VM, and can be used to store secure data such as login information; or even data that's tedious to manage within the script itself. (Documentation: https://docs.microsoft.com/en-us/appcenter/build/custom/variables/)
+ 3. Variables defined in the script itself. These are usually optional, but can make it easier to consistently handle particular actions or point to certain paths. In the sample post-build script, these include `UITEST_PATH` & `App_Center_Test_Command`
+
+