diff --git a/.gitignore b/.gitignore
index 9995f912d..7b21aa525 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@ tags
.DS_Store
npm-debug.log
nativescript-angular*.tgz
+package-lock.json
tests/app/**/*.js
tests/test-output.txt
diff --git a/e2e/router/.gitignore b/e2e/router/.gitignore
new file mode 100644
index 000000000..d3df84cc8
--- /dev/null
+++ b/e2e/router/.gitignore
@@ -0,0 +1,8 @@
+platforms
+node_modules
+hooks
+
+app/**/*.js
+e2e/**/*.js
+test-results.xml
+
diff --git a/e2e/router/app/App_Resources/Android/AndroidManifest.xml b/e2e/router/app/App_Resources/Android/AndroidManifest.xml
new file mode 100644
index 000000000..9db832151
--- /dev/null
+++ b/e2e/router/app/App_Resources/Android/AndroidManifest.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/router/app/App_Resources/Android/app.gradle b/e2e/router/app/App_Resources/Android/app.gradle
new file mode 100644
index 000000000..8df47fb37
--- /dev/null
+++ b/e2e/router/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.router"
+
+ //override supported platforms
+ // ndk {
+ // abiFilters.clear()
+ // abiFilters "armeabi-v7a"
+ // }
+
+ }
+ aaptOptions {
+ additionalParameters "--no-version-vectors"
+ }
+}
diff --git a/e2e/router/app/App_Resources/Android/drawable-nodpi/background.png b/e2e/router/app/App_Resources/Android/drawable-nodpi/background.png
new file mode 100644
index 000000000..748b2adf5
Binary files /dev/null and b/e2e/router/app/App_Resources/Android/drawable-nodpi/background.png differ
diff --git a/e2e/router/app/App_Resources/Android/drawable-nodpi/icon.png b/e2e/router/app/App_Resources/Android/drawable-nodpi/icon.png
new file mode 100755
index 000000000..ddfc17a71
Binary files /dev/null and b/e2e/router/app/App_Resources/Android/drawable-nodpi/icon.png differ
diff --git a/e2e/router/app/App_Resources/Android/drawable-nodpi/logo.png b/e2e/router/app/App_Resources/Android/drawable-nodpi/logo.png
new file mode 100644
index 000000000..b9e102a76
Binary files /dev/null and b/e2e/router/app/App_Resources/Android/drawable-nodpi/logo.png differ
diff --git a/e2e/router/app/App_Resources/Android/drawable-nodpi/splash_screen.xml b/e2e/router/app/App_Resources/Android/drawable-nodpi/splash_screen.xml
new file mode 100644
index 000000000..ada77f92c
--- /dev/null
+++ b/e2e/router/app/App_Resources/Android/drawable-nodpi/splash_screen.xml
@@ -0,0 +1,8 @@
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/e2e/router/app/App_Resources/Android/values-v21/colors.xml b/e2e/router/app/App_Resources/Android/values-v21/colors.xml
new file mode 100644
index 000000000..a64641a9d
--- /dev/null
+++ b/e2e/router/app/App_Resources/Android/values-v21/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #3d5afe
+
\ No newline at end of file
diff --git a/e2e/router/app/App_Resources/Android/values-v21/styles.xml b/e2e/router/app/App_Resources/Android/values-v21/styles.xml
new file mode 100644
index 000000000..dac8727c8
--- /dev/null
+++ b/e2e/router/app/App_Resources/Android/values-v21/styles.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/router/app/App_Resources/Android/values/colors.xml b/e2e/router/app/App_Resources/Android/values/colors.xml
new file mode 100644
index 000000000..74ad8829c
--- /dev/null
+++ b/e2e/router/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/router/app/App_Resources/Android/values/styles.xml b/e2e/router/app/App_Resources/Android/values/styles.xml
new file mode 100644
index 000000000..1e8c7f29b
--- /dev/null
+++ b/e2e/router/app/App_Resources/Android/values/styles.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/e2e/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..1953734f4
--- /dev/null
+++ b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/Contents.json b/e2e/router/app/App_Resources/iOS/Assets.xcassets/Contents.json
new file mode 100644
index 000000000..da4a164c9
--- /dev/null
+++ b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json b/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 000000000..4414bad08
--- /dev/null
+++ b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png b/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
new file mode 100644
index 000000000..9f1f6ce3e
Binary files /dev/null and b/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json b/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
new file mode 100644
index 000000000..4f4e9c506
--- /dev/null
+++ b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json b/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
new file mode 100644
index 000000000..23c0ffd7a
--- /dev/null
+++ b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png b/e2e/router/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/router/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png differ
diff --git a/e2e/router/app/App_Resources/iOS/Info.plist b/e2e/router/app/App_Resources/iOS/Info.plist
new file mode 100644
index 000000000..ea3e3ea23
--- /dev/null
+++ b/e2e/router/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/router/app/App_Resources/iOS/LaunchScreen.storyboard b/e2e/router/app/App_Resources/iOS/LaunchScreen.storyboard
new file mode 100644
index 000000000..2ad9471e1
--- /dev/null
+++ b/e2e/router/app/App_Resources/iOS/LaunchScreen.storyboard
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/router/app/App_Resources/iOS/build.xcconfig b/e2e/router/app/App_Resources/iOS/build.xcconfig
new file mode 100644
index 000000000..4b0118490
--- /dev/null
+++ b/e2e/router/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/router/app/README.md b/e2e/router/app/README.md
new file mode 100644
index 000000000..ebe60c416
--- /dev/null
+++ b/e2e/router/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/router/app/app-routing.module.ts b/e2e/router/app/app-routing.module.ts
new file mode 100644
index 000000000..dc4cf285c
--- /dev/null
+++ b/e2e/router/app/app-routing.module.ts
@@ -0,0 +1,40 @@
+import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+import { NativeScriptRouterModule } from "nativescript-angular/router";
+
+import { FirstComponent } from "./first/first.component"
+import { SecondComponent } from "./second/second.component"
+import { MasterComponent } from "./second/master.component"
+import { DetailComponent } from "./second/detail.component"
+
+export const routes = [
+ {
+ path: "",
+ redirectTo: "/first",
+ pathMatch: "full"
+ },
+ {
+ path: "first",
+ component: FirstComponent,
+ },
+ {
+ path: "second/:depth", component: SecondComponent,
+ children: [
+ { path: "", component: MasterComponent },
+ { path: "detail/:id", component: DetailComponent }
+ ]
+ },
+];
+
+export const navigatableComponents = [
+ FirstComponent,
+ SecondComponent,
+ MasterComponent,
+ DetailComponent
+];
+
+@NgModule({
+ imports: [NativeScriptRouterModule.forRoot(routes)],
+ exports: [NativeScriptRouterModule],
+})
+export class AppRoutingModule { }
+
diff --git a/e2e/router/app/app.component.ts b/e2e/router/app/app.component.ts
new file mode 100644
index 000000000..2311d63e0
--- /dev/null
+++ b/e2e/router/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from "@angular/core";
+
+@Component({
+ template: ``
+})
+export class AppComponent { }
+
diff --git a/e2e/router/app/app.css b/e2e/router/app/app.css
new file mode 100644
index 000000000..54e8ca56c
--- /dev/null
+++ b/e2e/router/app/app.css
@@ -0,0 +1,16 @@
+.header {
+ font-size: 32;
+}
+
+.nested-header {
+ font-size: 26;
+}
+
+.nested-outlet {
+ margin: 20;
+ background-color: lightgreen;
+}
+
+Label {
+ text-align: center;
+}
\ No newline at end of file
diff --git a/e2e/router/app/app.module.ts b/e2e/router/app/app.module.ts
new file mode 100644
index 000000000..e6b2aeb6e
--- /dev/null
+++ b/e2e/router/app/app.module.ts
@@ -0,0 +1,32 @@
+import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+import { NativeScriptModule } from "nativescript-angular/nativescript.module";
+
+import "./rxjs-operators";
+
+import {
+ AppRoutingModule,
+ navigatableComponents,
+} from "./app-routing.module";
+
+import { AppComponent } from "./app.component";
+
+import { rendererTraceCategory, viewUtilCategory } from "nativescript-angular/trace";
+import { setCategories, enable } from "trace";
+setCategories(rendererTraceCategory + "," + viewUtilCategory);
+enable();
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ ...navigatableComponents,
+ ],
+ bootstrap: [AppComponent],
+ providers: [],
+ imports: [
+ NativeScriptModule,
+ AppRoutingModule,
+ ],
+ schemas: [NO_ERRORS_SCHEMA],
+})
+export class AppModule { }
+
diff --git a/e2e/router/app/first/first.component.ts b/e2e/router/app/first/first.component.ts
new file mode 100644
index 000000000..e1509ae76
--- /dev/null
+++ b/e2e/router/app/first/first.component.ts
@@ -0,0 +1,42 @@
+import { Component, OnInit, OnDestroy, OnChanges } from "@angular/core";
+import { ActivatedRoute, Router, Route } from "@angular/router";
+import { Location } from "@angular/common";
+import { RouterExtensions } from "nativescript-angular/router";
+
+import { Page } from "ui/page";
+import { Observable } from "rxjs/Observable";
+
+@Component({
+ selector: "first",
+ template: `
+
+
+
+
+
+
+ `
+})
+export class FirstComponent implements OnInit, OnDestroy {
+ public message: string = "";
+ constructor(private routerExt: RouterExtensions, page: Page) {
+ console.log("FirstComponent - constructor() page: " + page);
+ }
+
+ ngOnInit() {
+ console.log("FirstComponent - ngOnInit()");
+ }
+
+ ngOnDestroy() {
+ console.log("FirstComponent - ngOnDestroy()");
+ }
+
+ goBack() {
+ this.message = "";
+ if (this.routerExt.canGoBack()) {
+ this.routerExt.back();
+ } else {
+ this.message = "canGoBack() - false"
+ }
+ }
+}
diff --git a/e2e/router/app/main.aot.ts b/e2e/router/app/main.aot.ts
new file mode 100644
index 000000000..98bf134fc
--- /dev/null
+++ b/e2e/router/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/router/app/main.ts b/e2e/router/app/main.ts
new file mode 100644
index 000000000..639bfd513
--- /dev/null
+++ b/e2e/router/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/router/app/package.json b/e2e/router/app/package.json
new file mode 100644
index 000000000..d5356c0cc
--- /dev/null
+++ b/e2e/router/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/router/app/rxjs-operators.ts b/e2e/router/app/rxjs-operators.ts
new file mode 100644
index 000000000..480bbe40f
--- /dev/null
+++ b/e2e/router/app/rxjs-operators.ts
@@ -0,0 +1 @@
+import "rxjs/add/operator/map";
\ No newline at end of file
diff --git a/e2e/router/app/second/detail.component.ts b/e2e/router/app/second/detail.component.ts
new file mode 100644
index 000000000..e005f6dee
--- /dev/null
+++ b/e2e/router/app/second/detail.component.ts
@@ -0,0 +1,32 @@
+import { Component, OnInit, OnDestroy } from "@angular/core";
+import { ActivatedRoute, Router, Route } from "@angular/router";
+import { Location } from "@angular/common";
+import { Page } from "ui/page";
+import { Observable } from "rxjs/Observable";
+
+@Component({
+ selector: "detail",
+ template: `
+
+
+
+
+
+ `
+})
+export class DetailComponent {
+ public id$: Observable;
+
+ constructor(private router: Router, private route: ActivatedRoute) {
+ console.log("DetailComponent - constructor()");
+ this.id$ = route.params.map(r => r["id"]);
+ }
+
+ ngOnInit() {
+ console.log("DetailComponent - ngOnInit()");
+ }
+
+ ngOnDestroy() {
+ console.log("DetailComponent - ngOnDestroy()");
+ }
+}
\ No newline at end of file
diff --git a/e2e/router/app/second/master.component.ts b/e2e/router/app/second/master.component.ts
new file mode 100644
index 000000000..08605d5e5
--- /dev/null
+++ b/e2e/router/app/second/master.component.ts
@@ -0,0 +1,27 @@
+import { Component, OnInit, OnDestroy } from "@angular/core";
+
+@Component({
+ selector: "master",
+ template: `
+
+
+
+
+
+ `
+})
+export class MasterComponent implements OnInit, OnDestroy {
+ public details: Array = [1, 2, 3];
+
+ constructor() {
+ console.log("MasterComponent - constructor()");
+ }
+
+ ngOnInit() {
+ console.log("MasterComponent - ngOnInit()");
+ }
+
+ ngOnDestroy() {
+ console.log("MasterComponent - ngOnDestroy()");
+ }
+}
\ No newline at end of file
diff --git a/e2e/router/app/second/second.component.ts b/e2e/router/app/second/second.component.ts
new file mode 100644
index 000000000..75acb847b
--- /dev/null
+++ b/e2e/router/app/second/second.component.ts
@@ -0,0 +1,46 @@
+import { Component, OnInit, OnDestroy } from "@angular/core";
+import { ActivatedRoute, Router, Route } from "@angular/router";
+
+import { RouterExtensions } from "nativescript-angular/router";
+import { Page } from "ui/page";
+import { Observable } from "rxjs/Observable";
+
+@Component({
+ selector: "second",
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class SecondComponent implements OnInit, OnDestroy {
+ public depth$: Observable;
+ public nextDepth$: Observable;
+
+ constructor(private routerExt: RouterExtensions, route: ActivatedRoute, page: Page) {
+ console.log("SecondComponent - constructor() page: " + page);
+ this.depth$ = route.params.map(r => r["depth"]);
+ this.nextDepth$ = route.params.map(r => +r["depth"] + 1);
+ }
+
+ ngOnInit() {
+ console.log("SecondComponent - ngOnInit()");
+ }
+
+ ngOnDestroy() {
+ console.log("SecondComponent - ngOnDestroy()");
+ }
+
+ goBack() {
+ this.routerExt.back();
+ }
+}
\ No newline at end of file
diff --git a/e2e/router/e2e/config/appium.capabilities.json b/e2e/router/e2e/config/appium.capabilities.json
new file mode 100644
index 000000000..676f5e070
--- /dev/null
+++ b/e2e/router/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": true,
+ "fullReset": false,
+ "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": true,
+ "fullReset": false,
+ "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": true,
+ "fullReset": false,
+ "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": true,
+ "fullReset": false,
+ "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/router/e2e/config/mocha.opts b/e2e/router/e2e/config/mocha.opts
new file mode 100644
index 000000000..796ec4724
--- /dev/null
+++ b/e2e/router/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/router/e2e/helpers/appium-elements.ts b/e2e/router/e2e/helpers/appium-elements.ts
new file mode 100644
index 000000000..26a020a4d
--- /dev/null
+++ b/e2e/router/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/router/e2e/router.e2e-spec.ts b/e2e/router/e2e/router.e2e-spec.ts
new file mode 100644
index 000000000..f5b983a8d
--- /dev/null
+++ b/e2e/router/e2e/router.e2e-spec.ts
@@ -0,0 +1,309 @@
+import {
+ AppiumDriver,
+ createDriver,
+ SearchOptions,
+} from "nativescript-dev-appium";
+
+import { DriverWrapper, ExtendedUIElement } from "./helpers/appium-elements";
+
+describe("Simple navigate and back", () => {
+ let driver: AppiumDriver;
+ let driverWrapper: DriverWrapper;
+
+ before(async () => {
+ driver = await createDriver();
+ driverWrapper = new DriverWrapper(driver);
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Driver quits!");
+ });
+
+ it("should find First", async () => {
+ await assureFirstComponent(driverWrapper);
+ });
+
+ it("should navigate to Second(1)/master", async () => {
+ await goFromFirstToSecond(driverWrapper);
+
+ await assureSecondComponent(driverWrapper, 1);
+ await assureNestedMasterComponent(driverWrapper);
+ });
+
+ it("should navigate back to First", async () => {
+ await goBack(driverWrapper);
+ await assureFirstComponent(driverWrapper);
+ });
+});
+
+describe("Navigate inside nested outlet", () => {
+ let driver: AppiumDriver;
+ let driverWrapper: DriverWrapper;
+
+ before(async () => {
+ driver = await createDriver();
+ driverWrapper = new DriverWrapper(driver);
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Driver quits!");
+ });
+
+ it("should find First", async () => {
+ await assureFirstComponent(driverWrapper);
+ });
+
+ it("should navigate to Second(1)/master", async () => {
+ await goFromFirstToSecond(driverWrapper);
+
+ await assureSecondComponent(driverWrapper, 1)
+ await assureNestedMasterComponent(driverWrapper);
+ });
+
+ it("should navigate to Second(1)/detail(1) and back", async () => {
+ const detailBtn = await driverWrapper.findElementByText("DETAIL 1", SearchOptions.exact);
+ detailBtn.click();
+ await assureSecondComponent(driverWrapper, 1)
+ await assureNestedDetailComponent(driverWrapper, 1);
+
+ await goBack(driverWrapper);
+ await assureSecondComponent(driverWrapper, 1)
+ await assureNestedMasterComponent(driverWrapper);
+ });
+
+ it("should navigate to Second(1)/detail(2) and back", async () => {
+ const detailBtn = await driverWrapper.findElementByText("DETAIL 2", SearchOptions.exact);
+ detailBtn.click();
+ await assureSecondComponent(driverWrapper, 1)
+ await assureNestedDetailComponent(driverWrapper, 2);
+
+ await goBack(driverWrapper);
+ await assureSecondComponent(driverWrapper, 1)
+ await assureNestedMasterComponent(driverWrapper);
+ });
+
+ it("should navigate back to First", async () => {
+ await goBack(driverWrapper);
+ await assureFirstComponent(driverWrapper);
+ });
+});
+
+describe("Navigate to same component with different param", () => {
+ let driver: AppiumDriver;
+ let driverWrapper: DriverWrapper;
+
+ before(async () => {
+ driver = await createDriver();
+ driverWrapper = new DriverWrapper(driver);
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Driver quits!");
+ });
+
+ it("should find First", async () => {
+ await assureFirstComponent(driverWrapper);
+ });
+
+ it("should navigate to Second(1)/master", async () => {
+ await goFromFirstToSecond(driverWrapper);
+
+ await assureSecondComponent(driverWrapper, 1)
+ await assureNestedMasterComponent(driverWrapper);
+ });
+
+ it("should navigate to Second(2)/master", async () => {
+ const navigationButton =
+ await driverWrapper.findElementByText("GO TO NEXT SECOND", SearchOptions.exact);
+ navigationButton.click();
+
+ await assureSecondComponent(driverWrapper, 2)
+ await assureNestedMasterComponent(driverWrapper);
+ });
+
+ it("should navigate back to Second(1)/master", async () => {
+ await goBack(driverWrapper);
+
+ await assureSecondComponent(driverWrapper, 1)
+ await assureNestedMasterComponent(driverWrapper);
+ });
+
+ it("should navigate back to First", async () => {
+ await goBack(driverWrapper);
+ await assureFirstComponent(driverWrapper);
+ });
+});
+
+describe("Nested navigation + page navigation", () => {
+ let driver: AppiumDriver;
+ let driverWrapper: DriverWrapper;
+
+ before(async () => {
+ driver = await createDriver();
+ driverWrapper = new DriverWrapper(driver);
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Driver quits!");
+ });
+
+ it("should find First", async () => {
+ await assureFirstComponent(driverWrapper);
+ });
+
+ it("should navigate to Second(1)/master", async () => {
+ await goFromFirstToSecond(driverWrapper);
+
+ await assureSecondComponent(driverWrapper, 1)
+ await assureNestedMasterComponent(driverWrapper);
+ });
+
+ it("should navigate to Second(1)/detail(1)", async () => {
+ const detailBtn = await driverWrapper.findElementByText("DETAIL 1", SearchOptions.exact);
+ detailBtn.click();
+
+ await assureSecondComponent(driverWrapper, 1)
+ await assureNestedDetailComponent(driverWrapper, 1);
+ });
+
+ it("should navigate to Second(2)/master", async () => {
+ const navigationButton =
+ await driverWrapper.findElementByText("GO TO NEXT SECOND", SearchOptions.exact);
+ navigationButton.click();
+
+ await assureSecondComponent(driverWrapper, 2)
+ await assureNestedMasterComponent(driverWrapper);
+ });
+
+ it("should navigate to Second(2)/detail(2)", async () => {
+ const detailBtn = await driverWrapper.findElementByText("DETAIL 2", SearchOptions.exact);
+ detailBtn.click();
+
+ await assureSecondComponent(driverWrapper, 2)
+ await assureNestedDetailComponent(driverWrapper, 2);
+ });
+
+ it("should navigate to First", async () => {
+ const navigationButton =
+ await driverWrapper.findElementByText("GO TO FIRST", SearchOptions.exact);
+ navigationButton.click();
+
+ await assureFirstComponent(driverWrapper);
+ });
+
+ it("should navigate the whole stack", async () => {
+ await goBack(driverWrapper);
+ await assureSecondComponent(driverWrapper, 2)
+ await assureNestedDetailComponent(driverWrapper, 2);
+
+ await goBack(driverWrapper);
+ await assureSecondComponent(driverWrapper, 2)
+ await assureNestedMasterComponent(driverWrapper);
+
+ await goBack(driverWrapper);
+ await assureSecondComponent(driverWrapper, 1)
+ await assureNestedDetailComponent(driverWrapper, 1);
+
+ await goBack(driverWrapper);
+ await assureSecondComponent(driverWrapper, 1)
+ await assureNestedMasterComponent(driverWrapper);
+
+ await goBack(driverWrapper);
+ await assureFirstComponent(driverWrapper);
+ });
+});
+
+describe("Shouldn't be able to navigate back on startup", () => {
+ let driver: AppiumDriver;
+ let driverWrapper: DriverWrapper;
+
+ before(async () => {
+ driver = await createDriver();
+ driverWrapper = new DriverWrapper(driver);
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Driver quits!");
+ });
+
+ it("should find First", async () => {
+ await assureFirstComponent(driverWrapper);
+ });
+
+ it("shouldn't be able to go back", async () => {
+ await goBack(driverWrapper);
+ await driverWrapper.findElementByText("canGoBack() - false", SearchOptions.exact);
+ });
+});
+
+describe("Shouldn't be able to navigate back after cleared history", () => {
+ let driver: AppiumDriver;
+ let driverWrapper: DriverWrapper;
+
+ before(async () => {
+ driver = await createDriver();
+ driverWrapper = new DriverWrapper(driver);
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Driver quits!");
+ });
+
+ it("should find First", async () => {
+ await assureFirstComponent(driverWrapper);
+ });
+
+ it("should navigate to Second(1)/master", async () => {
+ await goFromFirstToSecond(driverWrapper);
+
+ await assureSecondComponent(driverWrapper, 1)
+ await assureNestedMasterComponent(driverWrapper);
+ });
+
+ it("should navigate to Second(1)/master", async () => {
+ const navigationButton =
+ await driverWrapper.findElementByText("GO TO FIRST(CLEAR)", SearchOptions.exact);
+ navigationButton.click();
+ await assureFirstComponent(driverWrapper);
+ });
+
+ it("shouldn't be able to go back", async () => {
+ await goBack(driverWrapper);
+ await driverWrapper.findElementByText("canGoBack() - false", SearchOptions.exact);
+ });
+});
+
+async function assureFirstComponent(driverWrapper: DriverWrapper) {
+ await driverWrapper.findElementByText("FirstComponent", SearchOptions.exact);
+}
+
+async function assureSecondComponent(driverWrapper: DriverWrapper, param: number) {
+ await driverWrapper.findElementByText("SecondComponent", SearchOptions.exact);
+ await driverWrapper.findElementByText(`param: ${param}`, SearchOptions.exact);
+}
+
+async function assureNestedMasterComponent(driverWrapper: DriverWrapper) {
+ await driverWrapper.findElementByText("NestedMaster", SearchOptions.exact);
+}
+
+async function assureNestedDetailComponent(driverWrapper: DriverWrapper, param: number) {
+ await driverWrapper.findElementByText("NestedDetail", SearchOptions.exact);
+ await driverWrapper.findElementByText(`nested-param: ${param}`, SearchOptions.exact);
+}
+
+async function goBack(driverWrapper: DriverWrapper) {
+ const backButton = await driverWrapper.findElementByText("BACK", SearchOptions.exact);
+ await backButton.click();
+}
+
+async function goFromFirstToSecond(driverWrapper: DriverWrapper) {
+ const navigationButton =
+ await driverWrapper.findElementByText("GO TO SECOND", SearchOptions.exact);
+ navigationButton.click();
+}
\ No newline at end of file
diff --git a/e2e/router/e2e/setup.ts b/e2e/router/e2e/setup.ts
new file mode 100644
index 000000000..8b26e66e9
--- /dev/null
+++ b/e2e/router/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/router/e2e/tsconfig.json b/e2e/router/e2e/tsconfig.json
new file mode 100644
index 000000000..040f56ed4
--- /dev/null
+++ b/e2e/router/e2e/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "extends": "../tsconfig",
+ "compilerOptions": {
+ "importHelpers": true,
+ "types": [
+ "node",
+ "mocha",
+ "chai"
+ ]
+ }
+}
diff --git a/e2e/router/package.json b/e2e/router/package.json
new file mode 100644
index 000000000..52b596b89
--- /dev/null
+++ b/e2e/router/package.json
@@ -0,0 +1,50 @@
+{
+ "description": "NativeScript Application",
+ "license": "SEE LICENSE IN ",
+ "readme": "NativeScript Application",
+ "repository": "",
+ "nativescript": {
+ "id": "org.nativescript.router",
+ "tns-android": {
+ "version": "3.2.0-2017-9-4-1"
+ }
+ },
+ "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/router/tsconfig.json b/e2e/router/tsconfig.json
new file mode 100644
index 000000000..17700d54e
--- /dev/null
+++ b/e2e/router/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/trace.ts b/nativescript-angular/trace.ts
index efe8ddab6..8f81c0005 100644
--- a/nativescript-angular/trace.ts
+++ b/nativescript-angular/trace.ts
@@ -4,6 +4,7 @@ export const animationsTraceCategory = "ns-animations";
export const rendererTraceCategory = "ns-renderer";
export const viewUtilCategory = "ns-view-util";
export const routerTraceCategory = "ns-router";
+export const routeReuseStrategyTraceCategory = "ns-route-reuse-strategy";
export const listViewTraceCategory = "ns-list-view";
export function animationsLog(message: string): void {
@@ -26,6 +27,10 @@ export function routerLog(message: string): void {
write(message, routerTraceCategory);
}
+export function routeReuseStrategyLog(message: string): void {
+ write(message, routeReuseStrategyTraceCategory);
+}
+
export function styleError(message: string): void {
write(message, categories.Style, messageType.error);
}
diff --git a/ng-sample/app/app.ts b/ng-sample/app/app.ts
index 9934ffea8..581f039fe 100644
--- a/ng-sample/app/app.ts
+++ b/ng-sample/app/app.ts
@@ -12,16 +12,18 @@ import {
routerTraceCategory,
listViewTraceCategory,
animationsTraceCategory,
+ routeReuseStrategyTraceCategory,
} from "nativescript-angular/trace";
import { PAGE_FACTORY, PageFactory, PageFactoryOptions } from "nativescript-angular/platform-providers";
import { Page } from "ui/page";
import { Color } from "color";
import { setCategories, enable } from "trace";
-setCategories(
- `${animationsTraceCategory},${rendererTraceCategory}`
-);
+// setCategories(
+// `${animationsTraceCategory},${rendererTraceCategory}`
+// );
// setCategories(routerTraceCategory);
// setCategories(listViewTraceCategory);
+setCategories(`${routeReuseStrategyTraceCategory}`);
enable();
import { RendererTest } from "./examples/renderer-test";
@@ -132,10 +134,10 @@ const customPageFactoryProvider = {
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ActionBarTest));
// router
-platformNativeScriptDynamic().bootstrapModule(makeExampleModule(RouterOutletAppComponent));
+// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(RouterOutletAppComponent));
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(PageRouterOutletAppComponent));
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(PageRouterOutletNestedAppComponent));
-// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ClearHistoryAppComponent));
+platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ClearHistoryAppComponent));
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(LoginAppComponent));
// animations
diff --git a/ng-sample/app/examples/router/page-router-outlet-nested-test.ts b/ng-sample/app/examples/router/page-router-outlet-nested-test.ts
index 104278c94..5f5e07da5 100644
--- a/ng-sample/app/examples/router/page-router-outlet-nested-test.ts
+++ b/ng-sample/app/examples/router/page-router-outlet-nested-test.ts
@@ -1,5 +1,5 @@
import { Component, OnInit, OnDestroy } from "@angular/core";
-import { ActivatedRoute, Router } from "@angular/router";
+import { ActivatedRoute, Router, Route } from "@angular/router";
import { Location } from "@angular/common";
import { Page } from "ui/page";
import { Observable } from "rxjs/Observable";
@@ -12,8 +12,8 @@ import "rxjs/add/operator/map";
-
-
+
+
`
})
@@ -70,6 +70,14 @@ class DetailComponent {
console.log("DetailComponent.constructor()");
this.id$ = route.params.map(r => r["id"]);
}
+
+ ngOnInit() {
+ console.log("DetailComponent - ngOnInit()");
+ }
+
+ ngOnDestroy() {
+ console.log("DetailComponent - ngOnDestroy()");
+ }
}
@Component({
@@ -79,7 +87,7 @@ class DetailComponent {
-
+
@@ -121,8 +129,11 @@ class SecondComponent implements OnInit, OnDestroy {
template: ``
})
export class PageRouterOutletNestedAppComponent {
- static routes = [
- { path: "", component: FirstComponent },
+ static routes: Route[] = [
+ { path: "", redirectTo: "/second/1/detail/3", pathMatch: "full" },
+ {
+ path: "first", component: FirstComponent,
+ },
{
path: "second/:depth", component: SecondComponent,
children: [