diff --git a/e2e/renderer/.gitignore b/e2e/renderer/.gitignore
new file mode 100644
index 000000000..d3df84cc8
--- /dev/null
+++ b/e2e/renderer/.gitignore
@@ -0,0 +1,8 @@
+platforms
+node_modules
+hooks
+
+app/**/*.js
+e2e/**/*.js
+test-results.xml
+
diff --git a/e2e/renderer/app/App_Resources/Android/AndroidManifest.xml b/e2e/renderer/app/App_Resources/Android/AndroidManifest.xml
new file mode 100644
index 000000000..9db832151
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/Android/AndroidManifest.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/renderer/app/App_Resources/Android/app.gradle b/e2e/renderer/app/App_Resources/Android/app.gradle
new file mode 100644
index 000000000..b9e2b2982
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/Android/app.gradle
@@ -0,0 +1,23 @@
+// Add your native dependencies here:
+
+// Uncomment to add recyclerview-v7 dependency
+//dependencies {
+// compile 'com.android.support:recyclerview-v7:+'
+//}
+
+android {
+ defaultConfig {
+ generatedDensities = []
+ applicationId = "org.nativescript.renderer"
+
+ //override supported platforms
+ // ndk {
+ // abiFilters.clear()
+ // abiFilters "armeabi-v7a"
+ // }
+
+ }
+ aaptOptions {
+ additionalParameters "--no-version-vectors"
+ }
+}
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-hdpi/background.png b/e2e/renderer/app/App_Resources/Android/drawable-hdpi/background.png
new file mode 100644
index 000000000..eb381c258
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-hdpi/background.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-hdpi/icon.png b/e2e/renderer/app/App_Resources/Android/drawable-hdpi/icon.png
new file mode 100755
index 000000000..1034356e2
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-hdpi/icon.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-hdpi/logo.png b/e2e/renderer/app/App_Resources/Android/drawable-hdpi/logo.png
new file mode 100644
index 000000000..5218f4c90
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-hdpi/logo.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-ldpi/background.png b/e2e/renderer/app/App_Resources/Android/drawable-ldpi/background.png
new file mode 100644
index 000000000..748b2adf5
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-ldpi/background.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-ldpi/icon.png b/e2e/renderer/app/App_Resources/Android/drawable-ldpi/icon.png
new file mode 100755
index 000000000..ddfc17a71
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-ldpi/icon.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-ldpi/logo.png b/e2e/renderer/app/App_Resources/Android/drawable-ldpi/logo.png
new file mode 100644
index 000000000..b9e102a76
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-ldpi/logo.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-mdpi/background.png b/e2e/renderer/app/App_Resources/Android/drawable-mdpi/background.png
new file mode 100644
index 000000000..efeaf2907
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-mdpi/background.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-mdpi/icon.png b/e2e/renderer/app/App_Resources/Android/drawable-mdpi/icon.png
new file mode 100755
index 000000000..486e41091
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-mdpi/icon.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-mdpi/logo.png b/e2e/renderer/app/App_Resources/Android/drawable-mdpi/logo.png
new file mode 100644
index 000000000..626338766
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-mdpi/logo.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-nodpi/splash_screen.xml b/e2e/renderer/app/App_Resources/Android/drawable-nodpi/splash_screen.xml
new file mode 100644
index 000000000..ada77f92c
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/Android/drawable-nodpi/splash_screen.xml
@@ -0,0 +1,8 @@
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xhdpi/background.png b/e2e/renderer/app/App_Resources/Android/drawable-xhdpi/background.png
new file mode 100644
index 000000000..612bbd072
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-xhdpi/background.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xhdpi/icon.png b/e2e/renderer/app/App_Resources/Android/drawable-xhdpi/icon.png
new file mode 100644
index 000000000..f29188209
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-xhdpi/icon.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xhdpi/logo.png b/e2e/renderer/app/App_Resources/Android/drawable-xhdpi/logo.png
new file mode 100644
index 000000000..ad8ee2f4b
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-xhdpi/logo.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/background.png b/e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/background.png
new file mode 100644
index 000000000..0fa88e235
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/background.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/icon.png b/e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/icon.png
new file mode 100644
index 000000000..4f69cb25b
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/icon.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/logo.png b/e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/logo.png
new file mode 100644
index 000000000..668327832
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/logo.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/background.png b/e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/background.png
new file mode 100644
index 000000000..c650f6438
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/background.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/icon.png b/e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/icon.png
new file mode 100644
index 000000000..50887a856
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/icon.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/logo.png b/e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/logo.png
new file mode 100644
index 000000000..fa6331c8d
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/logo.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/values-v21/colors.xml b/e2e/renderer/app/App_Resources/Android/values-v21/colors.xml
new file mode 100644
index 000000000..a64641a9d
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/Android/values-v21/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #3d5afe
+
\ No newline at end of file
diff --git a/e2e/renderer/app/App_Resources/Android/values-v21/styles.xml b/e2e/renderer/app/App_Resources/Android/values-v21/styles.xml
new file mode 100644
index 000000000..dac8727c8
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/Android/values-v21/styles.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/renderer/app/App_Resources/Android/values/colors.xml b/e2e/renderer/app/App_Resources/Android/values/colors.xml
new file mode 100644
index 000000000..74ad8829c
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/Android/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #F5F5F5
+ #757575
+ #33B5E5
+ #272734
+
\ No newline at end of file
diff --git a/e2e/renderer/app/App_Resources/Android/values/styles.xml b/e2e/renderer/app/App_Resources/Android/values/styles.xml
new file mode 100644
index 000000000..1e8c7f29b
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/Android/values/styles.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..1953734f4
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,92 @@
+{
+ "images" : [
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "icon-29.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "icon-29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "icon-29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "icon-40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "icon-40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "icon-60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "icon-60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "icon-29.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "icon-29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "icon-40.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "icon-40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "icon-76.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "icon-76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "icon-83.5@2x.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png
new file mode 100644
index 000000000..9e15af09d
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png
new file mode 100644
index 000000000..7b9e55537
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png
new file mode 100644
index 000000000..76f61ec1f
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png
new file mode 100644
index 000000000..15b06db11
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
new file mode 100644
index 000000000..585065f94
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
new file mode 100644
index 000000000..a450c421d
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
new file mode 100644
index 000000000..457b6d94c
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
new file mode 100644
index 000000000..fa5a6ac86
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png
new file mode 100644
index 000000000..94abcf70d
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
new file mode 100644
index 000000000..2e71dd3a0
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png
new file mode 100644
index 000000000..4abc9ec50
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/Contents.json b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/Contents.json
new file mode 100644
index 000000000..da4a164c9
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 000000000..4414bad08
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,158 @@
+{
+ "images" : [
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "736h",
+ "filename" : "Default-736h@3x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "portrait",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "736h",
+ "filename" : "Default-Landscape@3x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "landscape",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "667h",
+ "filename" : "Default-667h@2x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "portrait",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "retina4",
+ "filename" : "Default-568h@2x.png",
+ "minimum-system-version" : "7.0",
+ "orientation" : "portrait",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default.png",
+ "extent" : "full-screen",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default@2x.png",
+ "extent" : "full-screen",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default-568h@2x.png",
+ "extent" : "full-screen",
+ "subtype" : "retina4",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "extent" : "to-status-bar",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait.png",
+ "extent" : "full-screen",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "extent" : "to-status-bar",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape.png",
+ "extent" : "full-screen",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "extent" : "to-status-bar",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait@2x.png",
+ "extent" : "full-screen",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "extent" : "to-status-bar",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape@2x.png",
+ "extent" : "full-screen",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png
new file mode 100644
index 000000000..d7f17fcd2
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png
new file mode 100644
index 000000000..b88415405
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png
new file mode 100644
index 000000000..faab4b631
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png
new file mode 100644
index 000000000..3365ba3cd
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
new file mode 100644
index 000000000..a44945c1a
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png
new file mode 100644
index 000000000..e6dca6269
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png
new file mode 100644
index 000000000..1a5007962
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
new file mode 100644
index 000000000..73d8b920f
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
new file mode 100644
index 000000000..9f1f6ce3e
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png
new file mode 100644
index 000000000..514fc5cde
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
new file mode 100644
index 000000000..4f4e9c506
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchScreen-AspectFill.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchScreen-AspectFill@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png
new file mode 100644
index 000000000..c293f9c7a
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png
new file mode 100644
index 000000000..233693a6e
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
new file mode 100644
index 000000000..23c0ffd7a
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchScreen-Center.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchScreen-Center@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png
new file mode 100644
index 000000000..a5a775a2b
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png
new file mode 100644
index 000000000..154c19343
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/Info.plist b/e2e/renderer/app/App_Resources/iOS/Info.plist
new file mode 100644
index 000000000..ea3e3ea23
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/iOS/Info.plist
@@ -0,0 +1,47 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ ${PRODUCT_NAME}
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIRequiresFullScreen
+
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+
+
diff --git a/e2e/renderer/app/App_Resources/iOS/LaunchScreen.storyboard b/e2e/renderer/app/App_Resources/iOS/LaunchScreen.storyboard
new file mode 100644
index 000000000..2ad9471e1
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/iOS/LaunchScreen.storyboard
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/renderer/app/App_Resources/iOS/build.xcconfig b/e2e/renderer/app/App_Resources/iOS/build.xcconfig
new file mode 100644
index 000000000..4b0118490
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/iOS/build.xcconfig
@@ -0,0 +1,7 @@
+// You can add custom settings here
+// for example you can uncomment the following line to force distribution code signing
+// CODE_SIGN_IDENTITY = iPhone Distribution
+// To build for device with Xcode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html
+// DEVELOPMENT_TEAM = YOUR_TEAM_ID;
+ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
diff --git a/e2e/renderer/app/README.md b/e2e/renderer/app/README.md
new file mode 100644
index 000000000..ebe60c416
--- /dev/null
+++ b/e2e/renderer/app/README.md
@@ -0,0 +1,5 @@
+# NativeScript Tutorial Angular Template
+
+This repo serves as the starting point for NativeScript’s [Angular Getting Started Guide](https://docs.nativescript.org/angular/tutorial/ng-chapter-0).
+
+Please file any issues with this template on the [NativeScript/docs repository](https://github.com/nativescript/docs), which is where the tutorial content lives.
\ No newline at end of file
diff --git a/e2e/renderer/app/app-routing.module.ts b/e2e/renderer/app/app-routing.module.ts
new file mode 100644
index 000000000..576923862
--- /dev/null
+++ b/e2e/renderer/app/app-routing.module.ts
@@ -0,0 +1,75 @@
+import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+import { NativeScriptRouterModule } from "nativescript-angular/router";
+
+import { ListComponent } from "./list.component";
+import { NgForComponent } from "./ngfor.component";
+import { NgForOfComponent } from "./ngforof.component";
+import { NgIfNoLayoutComponent } from "./ngif-no-layout.component";
+import { NgIfInbetweenComponent } from "./ngif-inbetween.component";
+import { NgIfElseComponent } from "./ngifelse.component";
+import { NgIfThenElseComponent } from "./ngif-then-else.component";
+import { NgIfSubsequent } from "./ngif-subsequent.component";
+import { ContentViewComponent } from "./content-view.component";
+
+export const routes = [
+ {
+ path: "",
+ redirectTo: "/list",
+ pathMatch: "full"
+ },
+ {
+ path: "list",
+ component: ListComponent,
+ },
+ {
+ path: "ngfor",
+ component: NgForComponent,
+ },
+ {
+ path: "ngforof",
+ component: NgForOfComponent,
+ },
+ {
+ path: "ngif-no-layout",
+ component: NgIfNoLayoutComponent,
+ },
+ {
+ path: "ngif-inbetween",
+ component: NgIfInbetweenComponent,
+ },
+ {
+ path: "ngifelse",
+ component: NgIfElseComponent,
+ },
+ {
+ path: "ngif-then-else",
+ component: NgIfThenElseComponent,
+ },
+ {
+ path: "ngif-subsequent",
+ component: NgIfSubsequent,
+ },
+ {
+ path: "content-view",
+ component: ContentViewComponent,
+ },
+];
+
+export const navigatableComponents = [
+ ListComponent,
+ NgForComponent,
+ NgForOfComponent,
+ NgIfNoLayoutComponent,
+ NgIfInbetweenComponent,
+ NgIfElseComponent,
+ NgIfThenElseComponent,
+ NgIfSubsequent,
+ ContentViewComponent,
+];
+
+@NgModule({
+ imports: [ NativeScriptRouterModule.forRoot(routes) ],
+ exports: [ NativeScriptRouterModule ],
+})
+export class AppRoutingModule { }
+
diff --git a/e2e/renderer/app/app.component.ts b/e2e/renderer/app/app.component.ts
new file mode 100644
index 000000000..2311d63e0
--- /dev/null
+++ b/e2e/renderer/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from "@angular/core";
+
+@Component({
+ template: ``
+})
+export class AppComponent { }
+
diff --git a/e2e/renderer/app/app.css b/e2e/renderer/app/app.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/e2e/renderer/app/app.module.ts b/e2e/renderer/app/app.module.ts
new file mode 100644
index 000000000..c5bf7e4dd
--- /dev/null
+++ b/e2e/renderer/app/app.module.ts
@@ -0,0 +1,33 @@
+import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+import { NativeScriptModule } from "nativescript-angular/nativescript.module";
+
+import {
+ AppRoutingModule,
+ navigatableComponents,
+} from "./app-routing.module";
+
+import { AppComponent } from "./app.component";
+import { ItemsService } from "./items.service";
+
+import { rendererTraceCategory, viewUtilCategory } from "nativescript-angular/trace";
+import { setCategories, enable } from "trace";
+setCategories(rendererTraceCategory + "," + viewUtilCategory);
+enable();
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ ...navigatableComponents,
+ ],
+ bootstrap: [AppComponent],
+ providers: [
+ ItemsService
+ ],
+ imports: [
+ NativeScriptModule,
+ AppRoutingModule,
+ ],
+ schemas: [NO_ERRORS_SCHEMA],
+})
+export class AppModule {}
+
diff --git a/e2e/renderer/app/content-view.component.ts b/e2e/renderer/app/content-view.component.ts
new file mode 100644
index 000000000..6ec92a1ba
--- /dev/null
+++ b/e2e/renderer/app/content-view.component.ts
@@ -0,0 +1,31 @@
+import { Component } from "@angular/core";
+
+@Component({
+ selector: "my-app",
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class ContentViewComponent {
+ public show = true;
+
+ toggle() {
+ this.show = !this.show;
+ }
+}
+
diff --git a/e2e/renderer/app/items-accessor.ts b/e2e/renderer/app/items-accessor.ts
new file mode 100644
index 000000000..58774bd5a
--- /dev/null
+++ b/e2e/renderer/app/items-accessor.ts
@@ -0,0 +1,18 @@
+import { ItemsService } from "./items.service";
+
+export class ItemsAccessor {
+ public items: number[];
+
+ constructor(public itemsService: ItemsService) {
+ this.items = itemsService.getAll();
+ }
+
+ add() {
+ this.items = this.itemsService.add(this.items);
+ }
+
+ remove(item) {
+ this.items = this.itemsService.remove(this.items, item);
+ }
+
+}
diff --git a/e2e/renderer/app/items.service.ts b/e2e/renderer/app/items.service.ts
new file mode 100644
index 000000000..94a33ef3f
--- /dev/null
+++ b/e2e/renderer/app/items.service.ts
@@ -0,0 +1,34 @@
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class ItemsService {
+ private items = [ 0 ];
+
+ getAll() {
+ return [...this.items];
+ }
+
+ add(items) {
+ return [
+ ...items,
+ items.length,
+ ];
+ }
+
+ remove(items, item?: number) {
+ const index = item ? items.indexOf(item) : items.length - 1;
+
+ return this.removeAt(items, index);
+ }
+
+ private removeAt(items: any[], index: number) {
+ items = [
+ ...items.slice(0, index),
+ ...items.slice(index + 1),
+ ];
+
+ console.log(`Removed ${index}th element`);
+
+ return items;
+ }
+}
diff --git a/e2e/renderer/app/list.component.ts b/e2e/renderer/app/list.component.ts
new file mode 100644
index 000000000..ec4ca839c
--- /dev/null
+++ b/e2e/renderer/app/list.component.ts
@@ -0,0 +1,18 @@
+import { Component } from "@angular/core";
+
+@Component({
+ template: `
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class ListComponent {
+}
diff --git a/e2e/renderer/app/main.aot.ts b/e2e/renderer/app/main.aot.ts
new file mode 100644
index 000000000..98bf134fc
--- /dev/null
+++ b/e2e/renderer/app/main.aot.ts
@@ -0,0 +1,4 @@
+import { platformNativeScript } from "nativescript-angular/platform-static";
+import { AppModuleNgFactory } from "./app.module.ngfactory";
+
+platformNativeScript().bootstrapModuleFactory(AppModuleNgFactory);
diff --git a/e2e/renderer/app/main.ts b/e2e/renderer/app/main.ts
new file mode 100644
index 000000000..639bfd513
--- /dev/null
+++ b/e2e/renderer/app/main.ts
@@ -0,0 +1,4 @@
+import { platformNativeScriptDynamic } from "nativescript-angular/platform";
+import { AppModule } from "./app.module";
+
+platformNativeScriptDynamic().bootstrapModule(AppModule);
diff --git a/e2e/renderer/app/ngfor.component.ts b/e2e/renderer/app/ngfor.component.ts
new file mode 100644
index 000000000..a3a6238fc
--- /dev/null
+++ b/e2e/renderer/app/ngfor.component.ts
@@ -0,0 +1,21 @@
+import { Component } from "@angular/core";
+
+import { ItemsService } from "./items.service";
+import { ItemsAccessor } from "./items-accessor";
+
+@Component({
+ template: `
+
+
+
+
+
+
+ `
+})
+export class NgForComponent extends ItemsAccessor {
+ constructor(public itemsService: ItemsService) {
+ super(itemsService);
+ }
+}
+
diff --git a/e2e/renderer/app/ngforof.component.ts b/e2e/renderer/app/ngforof.component.ts
new file mode 100644
index 000000000..79ad8e3d9
--- /dev/null
+++ b/e2e/renderer/app/ngforof.component.ts
@@ -0,0 +1,25 @@
+import { Component } from "@angular/core";
+
+import { ItemsService } from "./items.service";
+import { ItemsAccessor } from "./items-accessor";
+
+@Component({
+ template: `
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class NgForOfComponent extends ItemsAccessor {
+ constructor(public itemsService: ItemsService) {
+ super(itemsService);
+ }
+}
+
diff --git a/e2e/renderer/app/ngif-inbetween.component.ts b/e2e/renderer/app/ngif-inbetween.component.ts
new file mode 100644
index 000000000..a8a644186
--- /dev/null
+++ b/e2e/renderer/app/ngif-inbetween.component.ts
@@ -0,0 +1,30 @@
+import { Component } from "@angular/core";
+
+@Component({
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class NgIfInbetweenComponent {
+ public show = true;
+
+ toggle() {
+ this.show = !this.show;
+ }
+}
+
diff --git a/e2e/renderer/app/ngif-no-layout.component.ts b/e2e/renderer/app/ngif-no-layout.component.ts
new file mode 100644
index 000000000..087e14b55
--- /dev/null
+++ b/e2e/renderer/app/ngif-no-layout.component.ts
@@ -0,0 +1,24 @@
+import { Component } from "@angular/core";
+
+@Component({
+ template: `
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class NgIfNoLayoutComponent {
+ public show = false;
+
+ toggle() {
+ this.show = !this.show;
+ }
+}
+
diff --git a/e2e/renderer/app/ngif-subsequent.component.ts b/e2e/renderer/app/ngif-subsequent.component.ts
new file mode 100644
index 000000000..1e7125c95
--- /dev/null
+++ b/e2e/renderer/app/ngif-subsequent.component.ts
@@ -0,0 +1,23 @@
+import { Component} from "@angular/core";
+
+@Component({
+ moduleId: module.id,
+ selector: "renderer-test",
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class NgIfSubsequent {
+ public first: boolean = false;
+ public second: boolean = false;
+}
diff --git a/e2e/renderer/app/ngif-then-else.component.ts b/e2e/renderer/app/ngif-then-else.component.ts
new file mode 100644
index 000000000..6d4113190
--- /dev/null
+++ b/e2e/renderer/app/ngif-then-else.component.ts
@@ -0,0 +1,33 @@
+import { Component } from "@angular/core";
+
+@Component({
+ selector: "ng-if-then-else",
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class NgIfThenElseComponent {
+ public show: boolean = true;
+
+ toggle() {
+ this.show = !this.show;
+ }
+}
+
+
diff --git a/e2e/renderer/app/ngifelse.component.ts b/e2e/renderer/app/ngifelse.component.ts
new file mode 100644
index 000000000..645c0a201
--- /dev/null
+++ b/e2e/renderer/app/ngifelse.component.ts
@@ -0,0 +1,26 @@
+import { Component } from "@angular/core";
+
+@Component({
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class NgIfElseComponent {
+ public show: boolean = true;
+
+ toggle() {
+ this.show = !this.show;
+ }
+}
+
diff --git a/e2e/renderer/app/package.json b/e2e/renderer/app/package.json
new file mode 100644
index 000000000..d5356c0cc
--- /dev/null
+++ b/e2e/renderer/app/package.json
@@ -0,0 +1,5 @@
+{
+ "main": "main.js",
+ "name": "nativescript-template-ng-tutorial",
+ "version": "3.1.0"
+}
\ No newline at end of file
diff --git a/e2e/renderer/e2e/config/appium.capabilities.json b/e2e/renderer/e2e/config/appium.capabilities.json
new file mode 100644
index 000000000..f3936d6ea
--- /dev/null
+++ b/e2e/renderer/e2e/config/appium.capabilities.json
@@ -0,0 +1,84 @@
+{
+ "nexus5": {
+ "browserName": "",
+ "appium-version": "1.6.5",
+ "platformName": "Android",
+ "platformVersion": "6.0",
+ "deviceName": "device",
+ "udid": "077e4a47003b7698",
+ "-lt": 60000,
+ "automationName": "Appium",
+ "appActivity": "com.tns.NativeScriptActivity",
+ "app": ""
+ },
+ "android19": {
+ "browserName": "",
+ "appium-version": "1.6.5",
+ "platformName": "Android",
+ "platformVersion": "4.4",
+ "deviceName": "Emulator-Api19-Default",
+ "avd": "Emulator-Api19-Default",
+ "-lt": 60000,
+ "automationName": "Appium",
+ "appActivity": "com.tns.NativeScriptActivity",
+ "newCommandTimeout": 720,
+ "noReset": false,
+ "fullReset": true,
+ "app": ""
+ },
+ "android21": {
+ "browserName": "",
+ "appium-version": "1.6.5",
+ "platformName": "Android",
+ "platformVersion": "5.0",
+ "deviceName": "Emulator-Api21-Default",
+ "avd": "Emulator-Api21-Default",
+ "-lt": 60000,
+ "automationName": "Appium",
+ "appActivity": "com.tns.NativeScriptActivity",
+ "newCommandTimeout": 720,
+ "noReset": false,
+ "fullReset": true,
+ "app": ""
+ },
+ "android23": {
+ "browserName": "",
+ "appium-version": "1.6.5",
+ "platformName": "Android",
+ "platformVersion": "6.0",
+ "deviceName": "Emulator-Api23-Default",
+ "avd": "Emulator-Api23-Default",
+ "-lt": 60000,
+ "automationName": "Appium",
+ "appActivity": "com.tns.NativeScriptActivity",
+ "newCommandTimeout": 720,
+ "noReset": false,
+ "fullReset": true,
+ "app": ""
+ },
+ "android24": {
+ "browserName": "",
+ "appium-version": "1.6.5",
+ "platformName": "Android",
+ "platformVersion": "7.0",
+ "deviceName": "Emulator-Api24-Default",
+ "avd": "Emulator-Api24-Default",
+ "-lt": 60000,
+ "automationName": "UIAutomator2",
+ "appActivity": "com.tns.NativeScriptActivity",
+ "newCommandTimeout": 720,
+ "noReset": false,
+ "fullReset": true,
+ "app": ""
+ },
+ "sim.iPhone7.iOS100": {
+ "browserName": "",
+ "appium-version": "1.6.5",
+ "platformName": "iOS",
+ "platformVersion": "10.0",
+ "deviceName": "iPhone 7 100",
+ "noReset": true,
+ "fullReset": false,
+ "app": ""
+ }
+}
diff --git a/e2e/renderer/e2e/config/mocha.opts b/e2e/renderer/e2e/config/mocha.opts
new file mode 100644
index 000000000..796ec4724
--- /dev/null
+++ b/e2e/renderer/e2e/config/mocha.opts
@@ -0,0 +1,4 @@
+--timeout 80000
+--recursive e2e
+--reporter mocha-multi
+--reporter-options spec=-,mocha-junit-reporter=test-results.xml
\ No newline at end of file
diff --git a/e2e/renderer/e2e/helpers/appium-elements.ts b/e2e/renderer/e2e/helpers/appium-elements.ts
new file mode 100644
index 000000000..26a020a4d
--- /dev/null
+++ b/e2e/renderer/e2e/helpers/appium-elements.ts
@@ -0,0 +1,41 @@
+import { AppiumDriver } from "nativescript-dev-appium";
+
+import { UIElement } from "nativescript-dev-appium/ui-element";
+
+export class ExtendedUIElement extends UIElement {
+ refetch(): Promise {
+ return Promise.resolve(this);
+ }
+}
+
+const refetchable = () =>
+ (target: any, propertyKey: string, descriptor: PropertyDescriptor): any => {
+ const originalMethod = descriptor.value;
+ const patchRefetch = async (args, fetchMethod) => {
+ const result = await fetchMethod() as ExtendedUIElement;
+ result.refetch = () => patchRefetch(args, fetchMethod);
+
+ return result;
+ }
+
+ descriptor.value = async function (...args: any[]): Promise {
+ const fetchMethod = () => originalMethod.apply(this, args);
+ const result = await patchRefetch(args, fetchMethod);
+
+ return result;
+ }
+
+ return descriptor;
+ };
+
+export class DriverWrapper {
+ constructor(private driver: AppiumDriver) {
+ }
+
+ @refetchable()
+ async findElementByText(...args: any[]): Promise {
+ const result = await (this.driver).findElementByText(...args);
+
+ return result;
+ }
+}
diff --git a/e2e/renderer/e2e/helpers/location.ts b/e2e/renderer/e2e/helpers/location.ts
new file mode 100644
index 000000000..189dfbbcc
--- /dev/null
+++ b/e2e/renderer/e2e/helpers/location.ts
@@ -0,0 +1,13 @@
+import { assert } from "chai";
+
+import { ExtendedUIElement } from "./appium-elements";
+
+export const isAbove = async (first: ExtendedUIElement, second: ExtendedUIElement) => {
+ first = await first.refetch();
+ second = await second.refetch();
+
+ const { y: firstY } = await first.location();
+ const { y: secondY } = await second.location();
+
+ assert.isTrue(firstY < secondY);
+}
diff --git a/e2e/renderer/e2e/ngfor.e2e-spec.ts b/e2e/renderer/e2e/ngfor.e2e-spec.ts
new file mode 100644
index 000000000..e1181aa13
--- /dev/null
+++ b/e2e/renderer/e2e/ngfor.e2e-spec.ts
@@ -0,0 +1,126 @@
+import {
+ AppiumDriver,
+ createDriver,
+ SearchOptions,
+} from "nativescript-dev-appium";
+
+import { isAbove } from "./helpers/location";
+import { DriverWrapper, ExtendedUIElement } from "./helpers/appium-elements";
+
+describe("ngFor scenario", () => {
+ let driver: AppiumDriver;
+ let driverWrapper: DriverWrapper;
+ let addButton: ExtendedUIElement;
+ let removeButton: ExtendedUIElement;
+ let elements: ExtendedUIElement[] = [];
+ let lastAddedElementId = 0;
+
+ before(async () => {
+ driver = await createDriver();
+ driverWrapper = new DriverWrapper(driver);
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Driver quits!");
+ });
+
+ it("should navigate to page", async () => {
+ const navigationButton =
+ await driverWrapper.findElementByText("NgFor", SearchOptions.exact);
+ await navigationButton.click();
+
+ const actionBar =
+ await driverWrapper.findElementByText("ngFor", SearchOptions.exact);
+ });
+
+ it("should find elements", async () => {
+ const first = await driverWrapper.findElementByText(
+ lastAddedElementId.toString(), SearchOptions.exact);
+ elements.push(first);
+
+ addButton = await driverWrapper.findElementByText("add", SearchOptions.exact);
+ removeButton = await driverWrapper.findElementByText("remove", SearchOptions.exact);
+
+ await isAbove(first, addButton);
+ });
+
+ it("should render elements in correct order", async () => {
+ await isAbove(elements[0], addButton);
+ await isAbove(addButton, removeButton);
+ });
+
+ it("should place new elements in the right places", async () => {
+ for (let i = 0; i < 5; i += 1) {
+ await addElement();
+ await checkAppendedCorrectly();
+ }
+ });
+
+ it("shouldn't reorder elements when last is removed", async () => {
+ while (elements.length) {
+ await removeElement();
+ await checkCorrectOrderAll();
+ }
+ });
+
+ it("should render new elements correctly after all old ones are removed", async () => {
+ for (let i = 0; i < 5; i += 1) {
+ await addElement();
+ await checkCorrectOrderAll();
+ }
+ });
+
+ it("shouldn't reorder elements when middle is removed", async () => {
+ const middleIndex = Math.floor(elements.length / 2);
+ await removeElement(middleIndex);
+ await checkCorrectOrderAll();
+ });
+
+ const addElement = async () => {
+ addButton = await addButton.refetch();
+ await addButton.click();
+
+ lastAddedElementId += 1;
+ const newElement = await driverWrapper.findElementByText(
+ lastAddedElementId.toString(), SearchOptions.exact);
+
+ elements.push(newElement);
+ };
+
+ const removeElement = async (index?: number) => {
+ index;
+ if (index) {
+ let element = await elements[index];
+ element = await element.refetch();
+ await element.click();
+ } else {
+ index = elements.length - 1;
+ removeButton = await removeButton.refetch();
+ await removeButton.click();
+ }
+
+ elements.splice(index, 1);
+ lastAddedElementId -= 1;
+ };
+
+ const checkAppendedCorrectly = async () => {
+ const lastAdded = await driverWrapper.findElementByText(
+ lastAddedElementId.toString(), SearchOptions.exact);
+
+ await isAbove(elements.slice(-2)[0], lastAdded);
+ await isAbove(lastAdded, addButton);
+ await isAbove(addButton, removeButton);
+ };
+
+ const checkCorrectOrderAll = async () => {
+ for (let i = 0; i < elements.length - 1; i += 1) {
+ await isAbove(elements[i], elements[i + 1]);
+ }
+
+ if (elements.length) {
+ await isAbove(elements.slice(-1)[0], addButton);
+ }
+ await isAbove(addButton, removeButton);
+ };
+});
\ No newline at end of file
diff --git a/e2e/renderer/e2e/ngforof.e2e-spec.ts b/e2e/renderer/e2e/ngforof.e2e-spec.ts
new file mode 100644
index 000000000..db5fbf2c9
--- /dev/null
+++ b/e2e/renderer/e2e/ngforof.e2e-spec.ts
@@ -0,0 +1,156 @@
+import {
+ AppiumDriver,
+ createDriver,
+ SearchOptions,
+} from "nativescript-dev-appium";
+
+import { isAbove } from "./helpers/location";
+import { DriverWrapper, ExtendedUIElement } from "./helpers/appium-elements";
+
+interface ElementTuple {
+ label: ExtendedUIElement,
+ button: ExtendedUIElement,
+}
+
+describe("ngForOf scenario", () => {
+ let driver: AppiumDriver;
+ let driverWrapper: DriverWrapper;
+ let addButton: ExtendedUIElement;
+ let removeButton: ExtendedUIElement;
+ let elements: ElementTuple[] = [];
+ let lastAddedElementId = 0;
+
+ before(async () => {
+ driver = await createDriver();
+ driverWrapper = new DriverWrapper(driver);
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Driver quits!");
+ });
+
+ it("should navigate to page", async () => {
+ const navigationButton =
+ await driverWrapper.findElementByText("NgForOf", SearchOptions.exact);
+ await navigationButton.click();
+
+ const actionBar =
+ await driverWrapper.findElementByText("ngForOf", SearchOptions.exact);
+ });
+
+ it("should find elements", async () => {
+ const firstElement = await getElement(lastAddedElementId);
+ elements.push(firstElement);
+
+ addButton = await driverWrapper.findElementByText("add", SearchOptions.exact);
+ removeButton = await driverWrapper.findElementByText("remove", SearchOptions.exact);
+
+ await elementTupleCorrectlyRendered(firstElement);
+ await isAbove(firstElement.button, addButton);
+ });
+
+ it("should render elements in correct order", async () => {
+ await elementTupleCorrectlyRendered(elements[0]);
+ await isAbove(elements[0].button, addButton);
+ await isAbove(addButton, removeButton);
+ });
+
+
+ it("should place new elements in the right places", async () => {
+ for (let i = 0; i < 2; i += 1) {
+ await addElement();
+ await checkAppendedCorrectly();
+ }
+ });
+
+ it("shouldn't reorder elements when last is removed", async () => {
+ while (elements.length) {
+ await removeElement();
+ await checkCorrectOrderAll();
+ }
+ });
+
+ it("should render new elements correctly after all old ones are removed", async () => {
+ for (let i = 0; i < 5; i += 1) {
+ await addElement();
+ await checkCorrectOrderAll();
+ }
+ });
+
+ it("shouldn't reorder elements when middle is removed", async () => {
+ const middleIndex = Math.floor(elements.length / 2);
+ await removeElement(middleIndex);
+ await checkCorrectOrderAll();
+ });
+
+
+ const addElement = async () => {
+ addButton = await addButton.refetch();
+ await addButton.click();
+
+ lastAddedElementId += 1;
+ const newElement = await getElement(lastAddedElementId);
+
+ elements.push(newElement);
+ };
+
+ const removeElement = async (index?: number) => {
+ if (index) {
+ let { button } = await elements[index];
+ button = await button.refetch();
+ await button.click();
+ } else {
+ index = elements.length - 1;
+ removeButton = await removeButton.refetch();
+ await removeButton.click();
+ }
+
+ elements.splice(index, 1);
+ lastAddedElementId -= 1;
+ };
+
+ const checkAppendedCorrectly = async () => {
+ const lastAdded = await getElement(lastAddedElementId);
+
+ await elementIsAbove(elements.slice(-2)[0], lastAdded);
+ await isAbove(lastAdded.button, addButton);
+ await isAbove(addButton, removeButton);
+ };
+
+ const checkCorrectOrderAll = async () => {
+ for (let i = 0; i < elements.length - 1; i += 1) {
+ await elementIsAbove(elements[i], elements[i + 1]);
+ }
+
+ if (elements.length) {
+ const last = elements.slice(-1)[0];
+ await elementTupleCorrectlyRendered(last);
+ await isAbove(last.button, addButton);
+ }
+
+ await isAbove(addButton, removeButton);
+ };
+
+ const elementIsAbove = async (first: ElementTuple, second: ElementTuple) => {
+ await elementTupleCorrectlyRendered(first);
+ await elementTupleCorrectlyRendered(second);
+
+ await isAbove(first.button, second.label);
+ };
+
+ const elementTupleCorrectlyRendered = async (element: ElementTuple) => {
+ await isAbove(element.label, element.button);
+ };
+
+ const getElement = async (id: number) => {
+ const label = await driverWrapper.findElementByText(
+ "label: " + id.toString(), SearchOptions.exact);
+
+ const button = await driverWrapper.findElementByText(
+ id.toString(), SearchOptions.exact);
+
+ return { label, button };
+ };
+});
+
diff --git a/e2e/renderer/e2e/ngif.e2e-spec.ts b/e2e/renderer/e2e/ngif.e2e-spec.ts
new file mode 100644
index 000000000..53201e594
--- /dev/null
+++ b/e2e/renderer/e2e/ngif.e2e-spec.ts
@@ -0,0 +1,413 @@
+import {
+ AppiumDriver,
+ createDriver,
+ SearchOptions,
+} from "nativescript-dev-appium";
+
+import { isAbove } from "./helpers/location";
+import { DriverWrapper, ExtendedUIElement } from "./helpers/appium-elements";
+
+import { assert } from "chai";
+
+describe("ngIf scenario", () => {
+ let driver: AppiumDriver;
+ let driverWrapper: DriverWrapper;
+ let toggleButton: ExtendedUIElement;
+
+ describe("without layout", async () => {
+ before(async () => {
+ driver = await createDriver();
+ driverWrapper = new DriverWrapper(driver);
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Driver quits!");
+ });
+
+ it("should navigate to page", async () => {
+ const navigationButton =
+ await driverWrapper.findElementByText("NgIf no layout", SearchOptions.exact);
+ await navigationButton.click();
+
+ const actionBar =
+ await driverWrapper.findElementByText("ngIf - no layout", SearchOptions.exact);
+ });
+
+ it("should find elements", async () => {
+ await driverWrapper.findElementByText("false", SearchOptions.exact);
+ toggleButton = await driverWrapper.findElementByText("Toggle", SearchOptions.exact);
+ });
+
+ it("show 'true' button when show is true", async () => {
+ toggleButton = await toggleButton.refetch();
+ await toggleButton.click();
+
+ await driverWrapper.findElementByText("true", SearchOptions.exact);
+ });
+ });
+
+ describe("label inbetween", async () => {
+ let firstButton: ExtendedUIElement;
+ let secondButton: ExtendedUIElement;
+ let conditionalLabel: ExtendedUIElement;
+ let toggle: ExtendedUIElement;
+
+ before(async () => {
+ driver = await createDriver();
+ driverWrapper = new DriverWrapper(driver);
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Driver quits!");
+ });
+
+ it("should navigate to page", async () => {
+ const navigationButton =
+ await driverWrapper.findElementByText("NgIf inbetween", SearchOptions.exact);
+ await navigationButton.click();
+
+ const actionBar =
+ await driverWrapper.findElementByText("ngIf - inbetween", SearchOptions.exact);
+ });
+
+ it("should find elements", async () => {
+ firstButton = await driverWrapper.findElementByText("Button 1", SearchOptions.exact);
+ secondButton = await driverWrapper.findElementByText("Button 2", SearchOptions.exact);
+ toggleButton = await driverWrapper.findElementByText("Toggle", SearchOptions.exact);
+
+ conditionalLabel = await driverWrapper.findElementByText("Label", SearchOptions.exact);
+ const labelIsDisplayed = await conditionalLabel.isDisplayed();
+ assert.isTrue(labelIsDisplayed);
+ });
+
+ it("detach label when condition is false", done => {
+ (async () => {
+ toggleButton = await toggleButton.refetch();
+ await toggleButton.click();
+
+ try {
+ await driverWrapper.findElementByText("Label", SearchOptions.exact);
+ } catch (e) {
+ done();
+ }
+ })();
+ });
+ });
+
+ describe("with else template", async () => {
+ let ifButton: ExtendedUIElement;
+ let elseButton: ExtendedUIElement;
+ let toggle: ExtendedUIElement;
+
+ before(async () => {
+ driver = await createDriver();
+ driverWrapper = new DriverWrapper(driver);
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Driver quits!");
+ });
+
+ it("should navigate to page", async () => {
+ const navigationButton =
+ await driverWrapper.findElementByText("NgIfElse", SearchOptions.exact);
+ await navigationButton.click();
+
+ const actionBar =
+ await driverWrapper.findElementByText("ngIfElse", SearchOptions.exact);
+ });
+
+ it("should find elements", async () => {
+ toggleButton = await driverWrapper.findElementByText("Toggle", SearchOptions.exact);
+ ifButton = await driverWrapper.findElementByText("If", SearchOptions.exact);
+ });
+
+ it("shouldn't render 'else' template when condition is true", done => {
+ driverWrapper.findElementByText("Else", SearchOptions.exact)
+ .then(_ => { throw new Error("Else template found!"); })
+ .catch(() => done());
+ });
+
+ it("should attach 'else' template when condition is changed to false", async () => {
+ toggleButton = await toggleButton.refetch();
+ await toggleButton.click();
+
+ elseButton = await driverWrapper.findElementByText("Else", SearchOptions.exact);
+ });
+
+ it("should detach 'if' template when condition is changed to false", done => {
+ driverWrapper.findElementByText("If", SearchOptions.exact)
+ .then(_ => { throw new Error("If template found!"); })
+ .catch(() => done());
+ });
+
+ it("should swap the content when condition is changed", done => {
+ (async () => {
+ toggleButton = await toggleButton.refetch();
+ await toggleButton.click();
+ ifButton = await ifButton.refetch();
+
+ try {
+ await driverWrapper.findElementByText("Else", SearchOptions.exact);
+ } catch (e) {
+ done();
+ }
+ })();
+ });
+ });
+
+ describe("with then-else template", async () => {
+ let thenButton: ExtendedUIElement;
+ let elseButton: ExtendedUIElement;
+ let toggle: ExtendedUIElement;
+
+ before(async () => {
+ driver = await createDriver();
+ driverWrapper = new DriverWrapper(driver);
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Driver quits!");
+ });
+
+ it("should navigate to page", async () => {
+ const navigationButton =
+ await driverWrapper.findElementByText("NgIf Then Else", SearchOptions.exact);
+ await navigationButton.click();
+
+ const actionBar =
+ await driverWrapper.findElementByText("ngIf Then Else", SearchOptions.exact);
+ });
+
+ it("should find elements", async () => {
+ toggleButton = await driverWrapper.findElementByText("Toggle", SearchOptions.exact);
+ thenButton = await driverWrapper.findElementByText("Then", SearchOptions.exact);
+ });
+
+ it("shouldn't render 'else' template when condition is true", done => {
+ driverWrapper.findElementByText("Else", SearchOptions.exact)
+ .then(_ => { throw new Error("Else template found!"); })
+ .catch(() => done());
+ });
+
+ it("should attach 'else' template when condition is changed to false", async () => {
+ toggleButton = await toggleButton.refetch();
+ await toggleButton.click();
+
+ elseButton = await driverWrapper.findElementByText("Else", SearchOptions.exact);
+ });
+
+ it("should detach 'then' template when condition is changed to false", done => {
+ driverWrapper.findElementByText("Then", SearchOptions.exact)
+ .then(_ => { throw new Error("Then template found!"); })
+ .catch(() => done());
+ });
+
+ it("should swap the content when condition is changed", done => {
+ (async () => {
+ toggleButton = await toggleButton.refetch();
+ await toggleButton.click();
+ thenButton = await thenButton.refetch();
+
+ try {
+ await driverWrapper.findElementByText("Else", SearchOptions.exact);
+ } catch (e) {
+ done();
+ }
+ })();
+ });
+ });
+
+ describe("then-else templates inside content view", async () => {
+ let thenButton: ExtendedUIElement;
+ let elseButton: ExtendedUIElement;
+ let toggle: ExtendedUIElement;
+
+ before(async () => {
+ driver = await createDriver();
+ driverWrapper = new DriverWrapper(driver);
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Driver quits!");
+ });
+
+ it("should navigate to page", async () => {
+ const navigationButton =
+ await driverWrapper.findElementByText("Content view", SearchOptions.exact);
+ await navigationButton.click();
+
+ const actionBar =
+ await driverWrapper.findElementByText("Content View", SearchOptions.exact);
+ });
+
+ it("should find elements", async () => {
+ toggleButton = await driverWrapper.findElementByText("Toggle", SearchOptions.exact);
+ thenButton = await driverWrapper.findElementByText("Then", SearchOptions.exact);
+ });
+
+ it("shouldn't render 'else' template when condition is true", done => {
+ driverWrapper.findElementByText("Else", SearchOptions.exact)
+ .then(_ => { throw new Error("Else template found!"); })
+ .catch(() => done());
+ });
+
+ it("should attach 'else' template when condition is changed to false", async () => {
+ toggleButton = await toggleButton.refetch();
+ await toggleButton.click();
+
+ elseButton = await driverWrapper.findElementByText("Else", SearchOptions.exact);
+ });
+
+ it("should detach 'then' template when condition is changed to false", done => {
+ driverWrapper.findElementByText("Then", SearchOptions.exact)
+ .then(_ => { throw new Error("Then template found!"); })
+ .catch(() => done());
+ });
+
+ it("should swap the content when condition is changed", done => {
+ (async () => {
+ toggleButton = await toggleButton.refetch();
+ await toggleButton.click();
+ thenButton = await thenButton.refetch();
+
+ try {
+ await driverWrapper.findElementByText("Else", SearchOptions.exact);
+ } catch (e) {
+ done();
+ }
+ })();
+ });
+ });
+
+ describe("subsequent ifs", async () => {
+ let firstButton: ExtendedUIElement;
+ let secondButton: ExtendedUIElement;
+ let firstLabel: ExtendedUIElement;
+ let secondLabel: ExtendedUIElement;
+
+ before(async () => {
+ driver = await createDriver();
+ driverWrapper = new DriverWrapper(driver);
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Driver quits!");
+ });
+
+ it("should navigate to page", async () => {
+ const navigationButton =
+ await driverWrapper.findElementByText("NgIf Subsequent Ifs", SearchOptions.exact);
+ await navigationButton.click();
+ });
+
+ it("should find elements", async () => {
+ firstButton = await driverWrapper.findElementByText("Toggle first", SearchOptions.exact);
+ secondButton = await driverWrapper.findElementByText("Toggle second", SearchOptions.exact);
+
+ firstLabel = await driverWrapper.findElementByText("== 1 ==", SearchOptions.exact);
+ secondLabel = await driverWrapper.findElementByText("== 2 ==", SearchOptions.exact);
+
+ assert.isDefined(firstButton);
+ assert.isDefined(secondButton);
+ assert.isDefined(firstLabel);
+ assert.isDefined(secondLabel);
+ });
+
+ it("should toggle on first view", async () => {
+ await firstButton.click();
+
+ let conditional = await driverWrapper.findElementByText("first", SearchOptions.exact);
+
+ await isAbove(firstLabel, conditional);
+ await isAbove(conditional, secondLabel);
+ });
+
+ it("should toggle off first view", done => {
+ (async () => {
+ await firstButton.click();
+
+ driverWrapper.findElementByText("first", SearchOptions.exact, 500)
+ .then(_ => { throw new Error("first label found!"); })
+ .catch(() => done());
+ })();
+ });
+
+ it("should toggle on second view", async () => {
+ await secondButton.click();
+
+ let conditional = await driverWrapper.findElementByText("second", SearchOptions.exact);
+ await isAbove(firstLabel, conditional);
+ await isAbove(conditional, secondLabel);
+ });
+
+ it("should toggle off second view", done => {
+ (async () => {
+ await secondButton.click();
+
+ driverWrapper.findElementByText("first", SearchOptions.exact, 500)
+ .then(_ => { throw new Error("first label found!"); })
+ .catch(() => done());
+ })();
+ });
+
+ it("should toggle on both views", async () => {
+ await firstButton.click();
+ await secondButton.click();
+
+ let conditional1 = await driverWrapper.findElementByText("first", SearchOptions.exact);
+ let conditional2 = await driverWrapper.findElementByText("second", SearchOptions.exact);
+ await isAbove(firstLabel, conditional1);
+ await isAbove(conditional1, conditional2);
+ await isAbove(conditional2, secondLabel);
+ });
+
+ it("should toggle off both views", done => {
+ (async () => {
+ await firstButton.click();
+ await secondButton.click();
+
+ driverWrapper.findElementByText("first", SearchOptions.exact, 500)
+ .then(_ => { throw new Error("first label found!"); })
+ .catch(() => {
+ driverWrapper.findElementByText("second", SearchOptions.exact, 500)
+ .then(_ => { throw new Error("second label found!"); })
+ .catch(() => done());
+ });
+ })();
+ });
+
+ it("should toggle on both views in reverse", async () => {
+ await secondButton.click();
+ await firstButton.click();
+
+ let conditional1 = await driverWrapper.findElementByText("first", SearchOptions.exact);
+ let conditional2 = await driverWrapper.findElementByText("second", SearchOptions.exact);
+ await isAbove(firstLabel, conditional1);
+ await isAbove(conditional1, conditional2);
+ await isAbove(conditional2, secondLabel);
+ });
+
+ it("should toggle off both views in reverse", done => {
+ (async () => {
+ await secondButton.click();
+ await firstButton.click();
+
+ driverWrapper.findElementByText("first", SearchOptions.exact, 500)
+ .then(_ => { throw new Error("first label found!"); })
+ .catch(() => {
+ driverWrapper.findElementByText("second", SearchOptions.exact, 500)
+ .then(_ => { throw new Error("second label found!"); })
+ .catch(() => done());
+ });
+ })();
+ });
+ });
+
+});
diff --git a/e2e/renderer/e2e/setup.ts b/e2e/renderer/e2e/setup.ts
new file mode 100644
index 000000000..8b26e66e9
--- /dev/null
+++ b/e2e/renderer/e2e/setup.ts
@@ -0,0 +1,9 @@
+import { startServer, stopServer } from "nativescript-dev-appium";
+
+before("start server", async () => {
+ await startServer();
+});
+
+after("stop server", async () => {
+ await stopServer();
+});
diff --git a/e2e/renderer/e2e/tsconfig.json b/e2e/renderer/e2e/tsconfig.json
new file mode 100644
index 000000000..040f56ed4
--- /dev/null
+++ b/e2e/renderer/e2e/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "extends": "../tsconfig",
+ "compilerOptions": {
+ "importHelpers": true,
+ "types": [
+ "node",
+ "mocha",
+ "chai"
+ ]
+ }
+}
diff --git a/e2e/renderer/package.json b/e2e/renderer/package.json
new file mode 100644
index 000000000..3c32dff5b
--- /dev/null
+++ b/e2e/renderer/package.json
@@ -0,0 +1,50 @@
+{
+ "description": "NativeScript Application",
+ "license": "SEE LICENSE IN ",
+ "readme": "NativeScript Application",
+ "repository": "",
+ "nativescript": {
+ "id": "org.nativescript.renderer",
+ "tns-android": {
+ "version": "next"
+ }
+ },
+ "dependencies": {
+ "@angular/animations": "~4.2.0",
+ "@angular/common": "~4.2.0",
+ "@angular/compiler": "~4.2.0",
+ "@angular/core": "~4.2.0",
+ "@angular/forms": "~4.2.0",
+ "@angular/http": "~4.2.0",
+ "@angular/platform-browser": "~4.2.0",
+ "@angular/router": "~4.2.0",
+ "nativescript-angular": "file:../../nativescript-angular",
+ "nativescript-intl": "^3.0.0",
+ "reflect-metadata": "~0.1.8",
+ "rxjs": "~5.3.0",
+ "tns-core-modules": "next",
+ "zone.js": "~0.8.2"
+ },
+ "devDependencies": {
+ "@types/chai": "^4.0.2",
+ "@types/mocha": "^2.2.41",
+ "@types/node": "^7.0.5",
+ "babel-traverse": "6.25.0",
+ "babel-types": "6.25.0",
+ "babylon": "6.17.4",
+ "chai": "~4.1.1",
+ "chai-as-promised": "~7.1.1",
+ "colors": "^1.1.2",
+ "lazy": "1.0.11",
+ "mocha": "~3.5.0",
+ "mocha-junit-reporter": "^1.13.0",
+ "mocha-multi": "^0.11.0",
+ "nativescript-dev-appium": "next",
+ "nativescript-dev-typescript": "~0.4.0",
+ "tslib": "^1.7.1",
+ "typescript": "~2.2.1"
+ },
+ "scripts": {
+ "e2e": "tsc -p e2e && mocha --opts ./e2e/config/mocha.opts"
+ }
+}
diff --git a/e2e/renderer/tsconfig.json b/e2e/renderer/tsconfig.json
new file mode 100644
index 000000000..17700d54e
--- /dev/null
+++ b/e2e/renderer/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es5",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "noEmitHelpers": true,
+ "noEmitOnError": true,
+ "lib": [
+ "es6",
+ "dom",
+ "es2015.iterable"
+ ],
+ "baseUrl": ".",
+ "paths": {
+ "*": [
+ "./node_modules/tns-core-modules/*",
+ "./node_modules/*"
+ ]
+ }
+ },
+ "exclude": [
+ "node_modules",
+ "platforms",
+ "**/*.aot.ts"
+ ]
+}
\ No newline at end of file
diff --git a/nativescript-angular/element-registry.ts b/nativescript-angular/element-registry.ts
index 9a0f044aa..8c0fad5f1 100644
--- a/nativescript-angular/element-registry.ts
+++ b/nativescript-angular/element-registry.ts
@@ -2,25 +2,30 @@ import { View } from "tns-core-modules/ui/core/view";
import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base";
export type NgView = (View & ViewExtensions);
-export type NgElement = NgView | InvisibleNode;
export interface ViewExtensions {
+ meta: ViewClassMeta;
nodeType: number;
nodeName: string;
templateParent: NgView;
+ nextSibling: NgView;
+ firstChild: NgView;
+ lastChild: NgView;
ngCssClasses: Map;
- meta: ViewClassMeta;
}
export interface ViewClass {
new (): View;
}
-export abstract class InvisibleNode extends View implements ViewExtensions {
+export abstract class InvisibleNode extends View implements NgView {
meta: { skipAddToDom: boolean };
- templateParent: NgView;
nodeType: number;
nodeName: string;
+ templateParent: NgView;
+ nextSibling: NgView;
+ firstChild: NgView;
+ lastChild: NgView;
ngCssClasses: Map;
constructor() {
@@ -42,7 +47,7 @@ export class CommentNode extends InvisibleNode {
super();
this.meta = {
- skipAddToDom: false,
+ skipAddToDom: true,
};
this.id = CommentNode.id.toString();
CommentNode.id += 1;
@@ -67,7 +72,7 @@ const getClassName = instance => instance.constructor.name;
export interface ViewClassMeta {
skipAddToDom?: boolean;
- insertChild?: (parent: NgView, child: NgView, atIndex: number) => void;
+ insertChild?: (parent: NgView, child: NgView) => void;
removeChild?: (parent: NgView, child: NgView) => void;
}
diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts
index ddf247c0b..f4edba521 100644
--- a/nativescript-angular/renderer.ts
+++ b/nativescript-angular/renderer.ts
@@ -23,6 +23,11 @@ export const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
export const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
const ATTR_SANITIZER = /-/g;
+export interface ElementReference {
+ previous: NgView;
+ next: NgView;
+}
+
@Injectable()
export class NativeScriptRendererFactory implements RendererFactory2 {
private componentRenderers = new Map();
@@ -85,15 +90,16 @@ export class NativeScriptRenderer extends Renderer2 {
}
@profile
- appendChild(parent: any, newChild: NgView): void {
+ appendChild(parent: NgView, newChild: NgView): void {
traceLog(`NativeScriptRenderer.appendChild child: ${newChild} parent: ${parent}`);
this.viewUtil.insertChild(parent, newChild);
}
@profile
- insertBefore(parent: NgView, newChild: NgView, refChildIndex: number): void {
- traceLog(`NativeScriptRenderer.insertBefore child: ${newChild} parent: ${parent}`);
- this.viewUtil.insertChild(parent, newChild, refChildIndex);
+ insertBefore(parent: NgView, newChild: NgView, { previous, next }: ElementReference): void {
+ traceLog(`NativeScriptRenderer.insertBefore child: ${newChild} ` +
+ `parent: ${parent} previous: ${previous} next: ${next}`);
+ this.viewUtil.insertChild(parent, newChild, previous, next);
}
@profile
@@ -110,14 +116,18 @@ export class NativeScriptRenderer extends Renderer2 {
@profile
parentNode(node: NgView): any {
- traceLog("NativeScriptRenderer.parentNode for node: " + node);
+ traceLog(`NativeScriptRenderer.parentNode for node: ${node}`);
return node.parent || node.templateParent;
}
@profile
- nextSibling(node: NgView): number {
- traceLog(`NativeScriptRenderer.nextSibling ${node}`);
- return this.viewUtil.nextSiblingIndex(node);
+ nextSibling(node: NgView): ElementReference {
+ traceLog(`NativeScriptRenderer.nextSibling of ${node} is ${node.nextSibling}`);
+
+ return {
+ previous: node,
+ next: node.nextSibling,
+ };
}
@profile
diff --git a/nativescript-angular/trace.ts b/nativescript-angular/trace.ts
index 800b96b22..efe8ddab6 100644
--- a/nativescript-angular/trace.ts
+++ b/nativescript-angular/trace.ts
@@ -2,6 +2,7 @@ import { write, categories, messageType } from "tns-core-modules/trace";
export const animationsTraceCategory = "ns-animations";
export const rendererTraceCategory = "ns-renderer";
+export const viewUtilCategory = "ns-view-util";
export const routerTraceCategory = "ns-router";
export const listViewTraceCategory = "ns-list-view";
@@ -17,6 +18,10 @@ export function rendererError(message: string): void {
write(message, rendererTraceCategory, messageType.error);
}
+export function viewUtilLog(msg): void {
+ write(msg, viewUtilCategory);
+}
+
export function routerLog(message: string): void {
write(message, routerTraceCategory);
}
diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts
index 99b196197..5532a39dc 100644
--- a/nativescript-angular/view-util.ts
+++ b/nativescript-angular/view-util.ts
@@ -6,7 +6,6 @@ import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base";
import {
CommentNode,
InvisibleNode,
- NgElement,
NgView,
TextNode,
ViewExtensions,
@@ -17,10 +16,10 @@ import {
} from "./element-registry";
import { platformNames, Device } from "tns-core-modules/platform";
-import { rendererLog as traceLog } from "./trace";
+import { viewUtilLog as traceLog } from "./trace";
-const XML_ATTRIBUTES = Object.freeze(["style", "rows", "columns", "fontAttributes"]);
const ELEMENT_NODE_TYPE = 1;
+const XML_ATTRIBUTES = Object.freeze(["style", "rows", "columns", "fontAttributes"]);
const whiteSpaceSplitter = /\s+/;
export type ViewExtensions = ViewExtensions;
@@ -53,54 +52,168 @@ export class ViewUtil {
this.isAndroid = device.os === platformNames.android;
}
- public insertChild(parent: any, child: NgElement, atIndex: number = -1) {
+ public insertChild(
+ parent: NgView,
+ child: NgView,
+ previous: NgView = parent.lastChild,
+ next?: NgView
+ ) {
+ if (!parent) {
+ return;
+ }
+
+ this.addToQueue(parent, child, previous, next);
+
if (child instanceof InvisibleNode) {
child.templateParent = parent;
}
- if (!parent || isDetachedElement(child)) {
- return;
+ if (!isDetachedElement(child)) {
+ this.addToVisualTree(parent, child, next);
}
+ }
+ private addToQueue(
+ parent: NgView,
+ child: NgView,
+ previous: NgView,
+ next: NgView
+ ): void {
+ if (previous) {
+ previous.nextSibling = child;
+ } else {
+ parent.firstChild = child;
+ }
+
+ if (next) {
+ child.nextSibling = next;
+ } else {
+ this.appendToQueue(parent, child);
+ }
+ }
+
+ private appendToQueue(parent: NgView, view: NgView) {
+ traceLog(`ViewUtil.appendToQueue parent: ${parent} view: ${view}`);
+ if (parent.lastChild) {
+ parent.lastChild.nextSibling = view;
+ }
+
+ parent.lastChild = view;
+ }
+
+ private addToVisualTree(parent: NgView, child: NgView, next: NgView): void {
if (parent.meta && parent.meta.insertChild) {
- parent.meta.insertChild(parent, child, atIndex);
+ parent.meta.insertChild(parent, child);
} else if (isLayout(parent)) {
- if (child.parent === parent) {
- const index = (parent).getChildIndex(child);
- if (index !== -1) {
- parent.removeChild(child);
- }
- }
- if (atIndex !== -1) {
- parent.insertChild(child, atIndex);
- } else {
- parent.addChild(child);
- }
+ this.insertToLayout(parent, child, next);
} else if (isContentView(parent)) {
parent.content = child;
- } else if (parent && parent._addChildFromBuilder) {
- parent._addChildFromBuilder(child.nodeName, child);
+ } else if (parent && (parent)._addChildFromBuilder) {
+ (parent)._addChildFromBuilder(child.nodeName, child);
+ }
+ }
+
+ private insertToLayout(
+ parent: NgLayoutBase,
+ child: NgView,
+ next: NgView
+ ): void {
+ if (child.parent === parent) {
+ this.removeLayoutChild(parent, child);
+ }
+
+ const nextVisual = this.findNextVisual(next);
+ if (nextVisual) {
+ const index = parent.getChildIndex(nextVisual);
+ parent.insertChild(child, index);
+ } else {
+ parent.addChild(child);
+ }
+ }
+
+ private findNextVisual(view: NgView) {
+ let next = view;
+ while (next && isDetachedElement(next)) {
+ next = next.nextSibling;
}
+
+ return next;
}
- public removeChild(parent: any, child: NgElement) {
- if (!parent || isDetachedElement(child)) {
+ public removeChild(parent: NgView, child: NgView) {
+ if (!parent) {
return;
}
if (parent.meta && parent.meta.removeChild) {
parent.meta.removeChild(parent, child);
} else if (isLayout(parent)) {
- parent.removeChild(child);
- } else if (isContentView(parent)) {
- if (parent.content === child) {
- parent.content = null;
- }
+ this.removeLayoutChild(parent, child);
+ } else if (isContentView(parent) && parent.content === child) {
+ parent.content = null;
+ parent.lastChild = null;
+ parent.firstChild = null;
} else if (isView(parent)) {
parent._removeView(child);
}
}
+ private removeLayoutChild(parent: NgLayoutBase, child: NgView): void {
+ const index = parent.getChildIndex(child);
+ this.removeFromQueue(parent, child, index);
+ if (index === -1) {
+ return;
+ }
+
+ parent.removeChild(child);
+ }
+
+ private removeFromQueue(parent: NgLayoutBase, child: NgView, index: number) {
+ traceLog(`ViewUtil.removeFromQueue ` +
+ `parent: ${parent} child: ${child} index: ${index}`);
+
+ if (parent.firstChild === child && parent.lastChild === child) {
+ parent.firstChild = null;
+ parent.lastChild = null;
+ return;
+ }
+
+ if (parent.firstChild === child) {
+ parent.firstChild = child.nextSibling;
+ }
+
+ const previous = this.findPreviousElement(parent, child, index);
+ if (parent.lastChild === child) {
+ parent.lastChild = previous;
+ }
+
+ if (previous) {
+ previous.nextSibling = child.nextSibling;
+ }
+ }
+
+ // NOTE: This one is O(n) - use carefully
+ private findPreviousElement(parent: NgLayoutBase, child: NgView, elementIndex: number): NgView {
+ const previousVisual = this.getPreviousVisualElement(parent, elementIndex);
+ let previous = previousVisual || parent.firstChild;
+
+ // since detached elements are not added to the visual tree,
+ // we need to find the actual previous sibling of the view,
+ // which may as well be an invisible node
+ while (previous && previous !== child && previous.nextSibling !== child) {
+ previous = previous.nextSibling;
+ }
+
+ return previous;
+ }
+
+ private getPreviousVisualElement(parent: NgLayoutBase, elementIndex: number): NgView {
+ if (elementIndex > 0) {
+ return parent.getChildAt(elementIndex - 1) as NgView;
+ }
+ }
+
+ // NOTE: This one is O(n) - use carefully
public getChildIndex(parent: any, child: NgView) {
if (isLayout(parent)) {
return parent.getChildIndex(child);
@@ -131,9 +244,7 @@ export class ViewUtil {
// we're setting the node type of the view
// to 'element' because of checks done in the
- // dom animation engine:
- // tslint:disable-next-line:max-line-length
- // https://github.com/angular/angular/blob/master/packages/animations/browser/src/render/dom_animation_engine.ts#L70-L81
+ // dom animation engine
view.nodeType = ELEMENT_NODE_TYPE;
return view;
@@ -168,34 +279,11 @@ export class ViewUtil {
}
}
- // finds the node in the parent's views and returns the next index
- // returns -1 if the node has no parent or next sibling
- public nextSiblingIndex(node: NgView): number {
- const parent = node.parent;
- if (!parent) {
- return -1;
- }
-
- let index = 0;
- let found = false;
- parent.eachChild(child => {
- if (child === node) {
- found = true;
- }
-
- index += 1;
- return !found;
- });
-
- return found ? index : -1;
- }
-
private runsIn(platform: string): boolean {
return (platform === "ios" && this.isIos) ||
(platform === "android" && this.isAndroid);
}
-
private setPropertyInternal(view: NgView, attributeName: string, value: any): void {
traceLog(`Setting attribute: ${attributeName}=${value} to ${view}`);
diff --git a/nativescript-angular/yarn.lock b/nativescript-angular/yarn.lock
deleted file mode 100644
index 2ade79c72..000000000
--- a/nativescript-angular/yarn.lock
+++ /dev/null
@@ -1,381 +0,0 @@
-# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
-# yarn lockfile v1
-
-
-"@angular/animations@~4.0.0 || ~4.1.0":
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-4.1.0.tgz#97b642aee01b5406e03ec65e499342ba91e2dd38"
-
-"@angular/common@~4.0.0 || ~4.1.0":
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/@angular/common/-/common-4.1.0.tgz#4370f569e51ddd99963b7f4aa58c1a5dcc5fea52"
-
-"@angular/compiler-cli@~4.0.0 || ~4.1.0":
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-4.1.0.tgz#727aaada8bfd94285e9818995925048f7fdf1200"
- dependencies:
- "@angular/tsc-wrapped" "4.1.0"
- minimist "^1.2.0"
- reflect-metadata "^0.1.2"
-
-"@angular/compiler@~4.0.0 || ~4.1.0":
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-4.1.0.tgz#be1ade5b6aec81f03c29d52bcb95925a28900dcb"
-
-"@angular/core@~4.0.0 || ~4.1.0":
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/@angular/core/-/core-4.1.0.tgz#72ec173316879571880c9c483ed6dfc0caab94b0"
-
-"@angular/forms@~4.0.0 || ~4.1.0":
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-4.1.0.tgz#8eae2a45c4ba064b377f9280e59c012b5dac6b80"
-
-"@angular/http@~4.0.0 || ~4.1.0":
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/@angular/http/-/http-4.1.0.tgz#7ba0c4d044dee964021b7cf19cb146a2c31577a5"
-
-"@angular/platform-browser@~4.0.0 || ~4.1.0":
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-4.1.0.tgz#b981386be1a36f2af7f0679447fd97b7267b25de"
-
-"@angular/router@~4.0.0 || ~4.1.0":
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/@angular/router/-/router-4.1.0.tgz#dd3563662f95ca3aa3dd9ff13c6ed4bea1d90b06"
-
-"@angular/tsc-wrapped@4.1.0":
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/@angular/tsc-wrapped/-/tsc-wrapped-4.1.0.tgz#07cbd61d91adde4c2daf9a41605152952b8832b3"
- dependencies:
- tsickle "^0.21.0"
-
-ansi-regex@^2.0.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
-
-ansi-styles@^2.2.1:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
-
-app-root-path@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.0.1.tgz#cd62dcf8e4fd5a417efc664d2e5b10653c651b46"
-
-babel-code-frame@^6.22.0:
- version "6.22.0"
- resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4"
- dependencies:
- chalk "^1.1.0"
- esutils "^2.0.2"
- js-tokens "^3.0.0"
-
-balanced-match@^0.4.1:
- version "0.4.2"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
-
-brace-expansion@^1.0.0:
- version "1.1.7"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59"
- dependencies:
- balanced-match "^0.4.1"
- concat-map "0.0.1"
-
-chalk@^1.1.0:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
- dependencies:
- ansi-styles "^2.2.1"
- escape-string-regexp "^1.0.2"
- has-ansi "^2.0.0"
- strip-ansi "^3.0.0"
- supports-color "^2.0.0"
-
-codelyzer@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/codelyzer/-/codelyzer-3.0.1.tgz#ba66b7b2aa564fe9f45d6004b4003ad2cf116828"
- dependencies:
- app-root-path "^2.0.1"
- css-selector-tokenizer "^0.7.0"
- cssauron "^1.4.0"
- semver-dsl "^1.0.1"
- source-map "^0.5.6"
- sprintf-js "^1.0.3"
-
-colors@^1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
-
-concat-map@0.0.1:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
-
-css-selector-tokenizer@^0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86"
- dependencies:
- cssesc "^0.1.0"
- fastparse "^1.1.1"
- regexpu-core "^1.0.0"
-
-cssauron@^1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/cssauron/-/cssauron-1.4.0.tgz#a6602dff7e04a8306dc0db9a551e92e8b5662ad8"
- dependencies:
- through X.X.X
-
-cssesc@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
-
-diff@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
-
-escape-string-regexp@^1.0.2:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
-
-esutils@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
-
-fastparse@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
-
-findup-sync@~0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.3.0.tgz#37930aa5d816b777c03445e1966cc6790a4c0b16"
- dependencies:
- glob "~5.0.0"
-
-fs.realpath@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
-
-glob@^7.1.1:
- version "7.1.1"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.0.2"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-glob@~5.0.0:
- version "5.0.15"
- resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
- dependencies:
- inflight "^1.0.4"
- inherits "2"
- minimatch "2 || 3"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-has-ansi@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
- dependencies:
- ansi-regex "^2.0.0"
-
-inflight@^1.0.4:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
- dependencies:
- once "^1.3.0"
- wrappy "1"
-
-inherits@2:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
-
-js-tokens@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
-
-jsesc@~0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
-
-"minimatch@2 || 3", minimatch@^3.0.2:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774"
- dependencies:
- brace-expansion "^1.0.0"
-
-minimist@0.0.8:
- version "0.0.8"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
-
-minimist@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
-
-minimist@~0.0.1:
- version "0.0.10"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
-
-mkdirp@^0.5.1:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
- dependencies:
- minimist "0.0.8"
-
-nativescript-intl@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/nativescript-intl/-/nativescript-intl-3.0.0.tgz#82ee9be7d377172b3c4295734723037628e186a7"
-
-once@^1.3.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
- dependencies:
- wrappy "1"
-
-optimist@~0.6.0:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
- dependencies:
- minimist "~0.0.1"
- wordwrap "~0.0.2"
-
-path-is-absolute@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
-
-path-parse@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
-
-reflect-metadata@^0.1.2, reflect-metadata@^0.1.8:
- version "0.1.10"
- resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.10.tgz#b4f83704416acad89988c9b15635d47e03b9344a"
-
-regenerate@^1.2.1:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
-
-regexpu-core@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b"
- dependencies:
- regenerate "^1.2.1"
- regjsgen "^0.2.0"
- regjsparser "^0.1.4"
-
-regjsgen@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
-
-regjsparser@^0.1.4:
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"
- dependencies:
- jsesc "~0.5.0"
-
-resolve@^1.3.2:
- version "1.3.3"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5"
- dependencies:
- path-parse "^1.0.5"
-
-rxjs@^5.0.1:
- version "5.3.0"
- resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.3.0.tgz#d88ccbdd46af290cbdb97d5d8055e52453fabe2d"
- dependencies:
- symbol-observable "^1.0.1"
-
-semver-dsl@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/semver-dsl/-/semver-dsl-1.0.1.tgz#d3678de5555e8a61f629eed025366ae5f27340a0"
- dependencies:
- semver "^5.3.0"
-
-semver@^5.3.0:
- version "5.3.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
-
-source-map-support@^0.4.2:
- version "0.4.15"
- resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
- dependencies:
- source-map "^0.5.6"
-
-source-map@^0.5.6:
- version "0.5.6"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
-
-sprintf-js@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
-
-strip-ansi@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
- dependencies:
- ansi-regex "^2.0.0"
-
-supports-color@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
-
-symbol-observable@^1.0.1:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
-
-through@X.X.X:
- version "2.3.8"
- resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
-
-tns-core-modules-widgets@internal-preview:
- version "3.1.0-2017-04-28-266"
- resolved "https://registry.yarnpkg.com/tns-core-modules-widgets/-/tns-core-modules-widgets-3.1.0-2017-04-28-266.tgz#56d82a9952604e064b76bbd232d56cad9a13337a"
-
-tns-core-modules@internal-preview:
- version "3.1.0-2017-04-26-6412"
- resolved "https://registry.yarnpkg.com/tns-core-modules/-/tns-core-modules-3.1.0-2017-04-26-6412.tgz#ae7717292d511e3e6fcac14330d0b0adc18e905d"
- dependencies:
- tns-core-modules-widgets internal-preview
-
-tsickle@^0.21.0:
- version "0.21.6"
- resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.21.6.tgz#53b01b979c5c13fdb13afb3fb958177e5991588d"
- dependencies:
- minimist "^1.2.0"
- mkdirp "^0.5.1"
- source-map "^0.5.6"
- source-map-support "^0.4.2"
-
-tslint@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.1.0.tgz#51a47baeeb58956fcd617bd2cf00e2ef0eea2ed9"
- dependencies:
- babel-code-frame "^6.22.0"
- colors "^1.1.2"
- diff "^3.2.0"
- findup-sync "~0.3.0"
- glob "^7.1.1"
- optimist "~0.6.0"
- resolve "^1.3.2"
- semver "^5.3.0"
- tsutils "^1.4.0"
-
-tsutils@^1.4.0:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-1.8.0.tgz#bf8118ed8e80cd5c9fc7d75728c7963d44ed2f52"
-
-typescript@^2.3.2:
- version "2.3.2"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.3.2.tgz#f0f045e196f69a72f06b25fd3bd39d01c3ce9984"
-
-wordwrap@~0.0.2:
- version "0.0.3"
- resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
-
-wrappy@1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
-
-zone.js@^0.8.4:
- version "0.8.9"
- resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.9.tgz#34aaa9a3ec6d0e4acebd1b761adafa590473638b"
diff --git a/ng-sample/app/examples/list/list-test-async.ts b/ng-sample/app/examples/list/list-test-async.ts
index 146ee9cba..ed635997c 100644
--- a/ng-sample/app/examples/list/list-test-async.ts
+++ b/ng-sample/app/examples/list/list-test-async.ts
@@ -15,11 +15,11 @@ import "rxjs/add/operator/combineLatest";
-
+
-
+
@@ -68,11 +68,11 @@ export class ListTestAsync {
-
+
-
+
diff --git a/ng-sample/app/examples/list/list-test.ts b/ng-sample/app/examples/list/list-test.ts
index ccec8a964..9390b6dfd 100644
--- a/ng-sample/app/examples/list/list-test.ts
+++ b/ng-sample/app/examples/list/list-test.ts
@@ -42,26 +42,26 @@ export class ItemComponent implements AfterViewChecked, DoCheck {
-
+
-
+
`
// TEMPLATE WITH COMPONENT
- //
+ //
//
- //
+ //
// IN-PLACE TEMPLATE
- //
+ //
//
//
//
//
- //
+ //
})
export class ListTest {
public myItems: Array;
diff --git a/ng-sample/app/examples/list/template-selector.ts b/ng-sample/app/examples/list/template-selector.ts
index 4557b7e9c..54c11b28e 100644
--- a/ng-sample/app/examples/list/template-selector.ts
+++ b/ng-sample/app/examples/list/template-selector.ts
@@ -43,13 +43,13 @@ export class HeaderComponent implements DoCheck {
row="1" margin="10"
[itemTemplateSelector]="templateSelector">
-
+
-
+
-
+
-
+
diff --git a/ng-sample/app/examples/router/clear-history-test.ts b/ng-sample/app/examples/router/clear-history-test.ts
index c83b40936..dba5a8e86 100644
--- a/ng-sample/app/examples/router/clear-history-test.ts
+++ b/ng-sample/app/examples/router/clear-history-test.ts
@@ -40,10 +40,10 @@ class LocationLogService {
(service.routerEvents$ | async)"
margin="10">
-
+
-
+
diff --git a/ng-sample/app/examples/tab-view/tab-view-test.html b/ng-sample/app/examples/tab-view/tab-view-test.html
index f66474024..5caed39c8 100644
--- a/ng-sample/app/examples/tab-view/tab-view-test.html
+++ b/ng-sample/app/examples/tab-view/tab-view-test.html
@@ -1,9 +1,9 @@
-
+
-
+
diff --git a/tests/app/tests/ns-location-strategy.ts b/tests/app/tests/ns-location-strategy.ts
index 3a89ff541..13fd898a9 100644
--- a/tests/app/tests/ns-location-strategy.ts
+++ b/tests/app/tests/ns-location-strategy.ts
@@ -19,7 +19,7 @@ class FakeFrame extends View implements Frame {
}
}
- navigate(entry: NavigationEntry) { }
+ navigate(entry: any) { }
constructor(private backCB?: () => void) {
super();
diff --git a/tests/app/tests/property-sets.ts b/tests/app/tests/property-sets.ts
index e62bb8c74..e3d2694d4 100644
--- a/tests/app/tests/property-sets.ts
+++ b/tests/app/tests/property-sets.ts
@@ -11,11 +11,14 @@ import {Red} from "color/known-colors";
import {device, platformNames} from "platform";
import {createDevice} from "./test-utils";
-class TestView extends View implements ViewExtensions {
- public nodeName: string = "TestView";
+class TestView extends View implements NgView {
+ public meta: ViewClassMeta = { skipAddToDom: false };
public nodeType: number = 1;
+ public nodeName: string = "TestView";
public templateParent: NgView = null;
- public meta: ViewClassMeta = { skipAddToDom: false };
+ public nextSibling: NgView;
+ public firstChild: NgView;
+ public lastChild: NgView;
public ngCssClasses: Map = new Map();
public stringValue: string = "";
diff --git a/tests/app/tests/renderer-tests.ts b/tests/app/tests/renderer-tests.ts
index 6efc463c8..3c2ccd506 100644
--- a/tests/app/tests/renderer-tests.ts
+++ b/tests/app/tests/renderer-tests.ts
@@ -291,7 +291,7 @@ describe("Renderer E2E", () => {
it("ngIf hides component when false", () => {
return testApp.loadComponent(NgIfLabel).then((componentRef) => {
const componentRoot = componentRef.instance.elementRef.nativeElement;
- assert.equal("(ProxyViewContainer (CommentNode))", dumpView(componentRoot));
+ assert.equal("(ProxyViewContainer)", dumpView(componentRoot));
});
});
@@ -302,7 +302,7 @@ describe("Renderer E2E", () => {
component.show = true;
testApp.appRef.tick();
- assert.equal("(ProxyViewContainer (CommentNode), (Label))", dumpView(componentRoot));
+ assert.equal("(ProxyViewContainer (Label))", dumpView(componentRoot));
});
});
@@ -314,12 +314,11 @@ describe("Renderer E2E", () => {
component.show = true;
testApp.appRef.tick();
assert.equal(
- "(ProxyViewContainer (StackLayout (CommentNode), (Label), (Button)))",
+ "(ProxyViewContainer (StackLayout (Label), (Button)))",
dumpView(componentRoot));
});
});
-
it("ngIf shows elements in correct order when multiple are rendered and there's *ngIf", () => {
return testApp.loadComponent(NgIfMultiple).then((componentRef) => {
const component = componentRef.instance;
@@ -333,8 +332,7 @@ describe("Renderer E2E", () => {
"(Label[text=1]), " +
"(Label[text=2]), " +
"(Label[text=3]), " +
- "(CommentNode), " + // ng-reflect comment
- "(Label[text=4]), " + // the content to be displayed and its anchor
+ "(Label[text=4]), " + // the content to be conditionally displayed
"(Label[text=5])" +
")" +
")",
@@ -348,13 +346,15 @@ describe("Renderer E2E", () => {
const componentRoot = component.elementRef.nativeElement;
testApp.appRef.tick();
+
assert.equal(
"(ProxyViewContainer " +
"(StackLayout " +
- "(CommentNode), " + // ng-reflect comment
- "(Label[text=If]), (CommentNode)))", // the content to be displayed and its anchor
+ "(Label[text=If])" +
+ ")" +
+ ")",
- dumpView(componentRoot, true));
+ dumpView(componentRoot, true));
});
});
@@ -368,10 +368,11 @@ describe("Renderer E2E", () => {
assert.equal(
"(ProxyViewContainer " +
"(StackLayout " +
- "(CommentNode), " + // ng-reflect comment
- "(Label[text=Else]), (CommentNode)))", // the content to be displayed and its anchor
+ "(Label[text=Else])" +
+ ")" +
+ ")",
- dumpView(componentRoot, true));
+ dumpView(componentRoot, true));
});
});
@@ -384,11 +385,11 @@ describe("Renderer E2E", () => {
assert.equal(
"(ProxyViewContainer " +
"(StackLayout " +
- "(CommentNode), " + // ng-reflect comment
- "(Label[text=Then]), (CommentNode), " + // the content to be displayed and its anchor
- "(CommentNode)))", // the anchor for the else template
+ "(Label[text=Then])" +
+ ")" +
+ ")",
- dumpView(componentRoot, true));
+ dumpView(componentRoot, true));
});
});
@@ -403,11 +404,11 @@ describe("Renderer E2E", () => {
assert.equal(
"(ProxyViewContainer " +
"(StackLayout " +
- "(CommentNode), " + // the content to be displayed
- "(Label[text=Else]), (CommentNode), " + // the content to be displayed
- "(CommentNode)))", // the content to be displayed
+ "(Label[text=Else])" +
+ ")" +
+ ")",
- dumpView(componentRoot, true));
+ dumpView(componentRoot, true));
});
});
@@ -415,7 +416,7 @@ describe("Renderer E2E", () => {
return testApp.loadComponent(NgForLabel).then((componentRef) => {
const componentRoot = componentRef.instance.elementRef.nativeElement;
assert.equal(
- "(ProxyViewContainer (CommentNode), (Label[text=one]), (Label[text=two]), (Label[text=three]))",
+ "(ProxyViewContainer (Label[text=one]), (Label[text=two]), (Label[text=three]))",
dumpView(componentRoot, true));
});
});
@@ -429,7 +430,7 @@ describe("Renderer E2E", () => {
testApp.appRef.tick();
assert.equal(
- "(ProxyViewContainer (CommentNode), (Label[text=one]), (Label[text=three]))",
+ "(ProxyViewContainer (Label[text=one]), (Label[text=three]))",
dumpView(componentRoot, true));
});
});
@@ -443,7 +444,7 @@ describe("Renderer E2E", () => {
testApp.appRef.tick();
assert.equal(
- "(ProxyViewContainer (CommentNode), " +
+ "(ProxyViewContainer " +
"(Label[text=one]), (Label[text=new]), (Label[text=two]), (Label[text=three]))",
dumpView(componentRoot, true));
});