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/router.ts b/nativescript-angular/router.ts index d5494bcfb..6bfc6b359 100644 --- a/nativescript-angular/router.ts +++ b/nativescript-angular/router.ts @@ -5,7 +5,7 @@ import { Optional, SkipSelf, } from "@angular/core"; -import { RouterModule, Routes, ExtraOptions } from "@angular/router"; +import { RouterModule, Routes, ExtraOptions, RouteReuseStrategy } from "@angular/router"; import { LocationStrategy, PlatformLocation } from "@angular/common"; import { Frame } from "tns-core-modules/ui/frame"; import { NSRouterLink } from "./router/ns-router-link"; @@ -13,6 +13,7 @@ import { NSRouterLinkActive } from "./router/ns-router-link-active"; import { PageRouterOutlet } from "./router/page-router-outlet"; import { NSLocationStrategy, LocationState } from "./router/ns-location-strategy"; import { NativescriptPlatformLocation } from "./router/ns-platform-location"; +import { NSRouteReuseStrategy } from "./router/ns-route-reuse-strategy"; import { RouterExtensions } from "./router/router-extensions"; import { NativeScriptCommonModule } from "./common"; @@ -37,6 +38,8 @@ export type LocationState = LocationState; NativescriptPlatformLocation, { provide: PlatformLocation, useClass: NativescriptPlatformLocation }, RouterExtensions, + NSRouteReuseStrategy, + { provide: RouteReuseStrategy, useExisting: NSRouteReuseStrategy } ], imports: [ RouterModule, diff --git a/nativescript-angular/router/ns-location-strategy.ts b/nativescript-angular/router/ns-location-strategy.ts index 3a0f2a0b1..c474f2a24 100644 --- a/nativescript-angular/router/ns-location-strategy.ts +++ b/nativescript-angular/router/ns-location-strategy.ts @@ -67,7 +67,7 @@ export class NSLocationStrategy extends LocationStrategy { replaceState(state: any, title: string, url: string, queryParams: string): void { if (this.states.length > 0) { - routerLog("NSLocationStrategy.replaceState changing exisitng state: " + + routerLog("NSLocationStrategy.replaceState changing existing state: " + `${state}, title: ${title}, url: ${url}, queryParams: ${queryParams}`); const topState = this.peekState(); topState.state = state; @@ -104,7 +104,7 @@ export class NSLocationStrategy extends LocationStrategy { if (state.isPageNavigation) { // This was a page navigation - so navigate through frame. routerLog("NSLocationStrategy.back() while not navigating back but top" + - " state is page - will call frame.goback()"); + " state is page - will call frame.goBack()"); this.frame.goBack(); } else { // Nested navigation - just pop the state diff --git a/nativescript-angular/router/ns-route-reuse-strategy.ts b/nativescript-angular/router/ns-route-reuse-strategy.ts new file mode 100644 index 000000000..d69b924dd --- /dev/null +++ b/nativescript-angular/router/ns-route-reuse-strategy.ts @@ -0,0 +1,133 @@ +import { Injectable } from "@angular/core"; +import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from "@angular/router"; + +import { routeReuseStrategyLog as log } from "../trace"; +import { NSLocationStrategy } from "./ns-location-strategy"; +import { pageRouterActivatedSymbol, destroyComponentRef } from "./page-router-outlet"; + +interface CacheItem { + key: string; + state: DetachedRouteHandle; +} + +/** + * Detached state cache + */ +class DetachedStateCache { + private cache = new Array(); + + public get length(): number { + return this.cache.length; + } + + public push(cacheItem: CacheItem) { + this.cache.push(cacheItem); + } + + public pop(): CacheItem { + return this.cache.pop(); + } + + public peek(): CacheItem { + return this.cache[this.cache.length - 1]; + } + + public clear() { + log(`DetachedStateCache.clear() ${this.cache.length} items will be destroyed`); + + while (this.cache.length > 0) { + const state = this.cache.pop().state; + if (!state.componentRef) { + throw new Error("No componentRed found in DetachedRouteHandle"); + } + + destroyComponentRef(state.componentRef); + } + } +} + +/** + * Does not detach any subtrees. Reuses routes as long as their route config is the same. + */ +@Injectable() +export class NSRouteReuseStrategy implements RouteReuseStrategy { + private cache: DetachedStateCache = new DetachedStateCache(); + + constructor(private location: NSLocationStrategy) { } + + shouldDetach(route: ActivatedRouteSnapshot): boolean { + const key = getSnapshotKey(route); + const isPageActivated = route[pageRouterActivatedSymbol]; + const isBack = this.location._isPageNavigatingBack(); + const shouldDetach = !isBack && isPageActivated; + + log(`shouldDetach isBack: ${isBack} key: ${key} result: ${shouldDetach}`); + + return shouldDetach; + } + + shouldAttach(route: ActivatedRouteSnapshot): boolean { + const key = getSnapshotKey(route); + const isBack = this.location._isPageNavigatingBack(); + const shouldAttach = isBack && this.cache.peek().key === key; + + log(`shouldAttach isBack: ${isBack} key: ${key} result: ${shouldAttach}`); + + return shouldAttach; + } + + + store(route: ActivatedRouteSnapshot, state: DetachedRouteHandle): void { + const key = getSnapshotKey(route); + log(`store key: ${key}, state: ${state}`); + + if (state) { + this.cache.push({ key, state }); + } else { + const topItem = this.cache.peek(); + if (topItem.key === key) { + this.cache.pop(); + } else { + throw new Error("Trying to pop from DetachedStateCache but keys don't match. " + + `expected: ${topItem.key} actual: ${key}`); + } + } + } + + retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null { + const key = getSnapshotKey(route); + const isBack = this.location._isPageNavigatingBack(); + const cachedItem = this.cache.peek(); + + let state = null; + if (isBack && cachedItem && cachedItem.key === key) { + state = cachedItem.state; + } + + log(`retrieved isBack: ${isBack} key: ${key} state: ${state}`); + + return state; + } + + shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { + const shouldReuse = future.routeConfig === curr.routeConfig; + + if (shouldReuse && curr && curr[pageRouterActivatedSymbol]) { + // When reusing route - copy the pageRouterActivated to the new snapshot + // It's needed in shouldDetach to determine if the route should be detached. + future[pageRouterActivatedSymbol] = curr[pageRouterActivatedSymbol]; + } + + log(`shouldReuseRoute result: ${shouldReuse}`); + + return shouldReuse; + } + + clearCache() { + this.cache.clear(); + } +} + +function getSnapshotKey(snapshot: ActivatedRouteSnapshot): string { + return snapshot.pathFromRoot.join("->"); +} diff --git a/nativescript-angular/router/page-router-outlet.ts b/nativescript-angular/router/page-router-outlet.ts index 9f7541ec9..03469703e 100644 --- a/nativescript-angular/router/page-router-outlet.ts +++ b/nativescript-angular/router/page-router-outlet.ts @@ -2,7 +2,7 @@ import { Attribute, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, Directive, Inject, InjectionToken, Injector, - OnDestroy, OnInit, + OnDestroy, OnInit, EventEmitter, Output, Type, ViewContainerRef, } from "@angular/core"; import { @@ -24,6 +24,7 @@ import { routerLog } from "../trace"; import { DetachedLoader } from "../common/detached-loader"; import { ViewUtil } from "../view-util"; import { NSLocationStrategy } from "./ns-location-strategy"; +import { NSRouteReuseStrategy } from "./ns-route-reuse-strategy"; export class PageRoute { activatedRoute: BehaviorSubject; @@ -33,79 +34,54 @@ export class PageRoute { } } -class ChildInjector implements Injector { - constructor( - private providers: ProviderMap, - private parent: Injector - ) {} +// Used to "mark" ActivatedRoute snapshots that are handled in PageRouterOutlet +export const pageRouterActivatedSymbol = Symbol("page-router-activated"); +export const loaderRefSymbol = Symbol("loader-ref"); - get(token: Type|InjectionToken, notFoundValue?: T): T { - return this.providers.get(token) || this.parent.get(token, notFoundValue); +export function destroyComponentRef(componentRef: ComponentRef) { + if (isPresent(componentRef)) { + const loaderRef = componentRef[loaderRefSymbol]; + if (isPresent(loaderRef)) { + loaderRef.destroy(); + } + componentRef.destroy(); } } -/** - * Reference Cache - */ -class RefCache { - private cache = new Array(); - - public get length(): number { - return this.cache.length; - } - - public push(cacheItem: CacheItem) { - this.cache.push(cacheItem); - } - - public pop(): CacheItem { - return this.cache.pop(); - } - - public peek(): CacheItem { - return this.cache[this.cache.length - 1]; - } - - public clear(): void { - while (this.length) { - RefCache.destroyItem(this.pop()); - } - } +class ChildInjector implements Injector { + constructor( + private providers: ProviderMap, + private parent: Injector + ) { } - public static destroyItem(item: CacheItem) { - if (isPresent(item.componentRef)) { - item.componentRef.destroy(); + get(token: Type | InjectionToken, notFoundValue?: T): T { + let localValue = this.providers.get(token); + if (localValue) { + return localValue; } - if (isPresent(item.loaderRef)) { - item.loaderRef.destroy(); - } + return this.parent.get(token, notFoundValue); } } -interface CacheItem { - componentRef: ComponentRef; - reusedRoute: PageRoute; - loaderRef?: ComponentRef; -} - - -type ProviderMap = Map|InjectionToken, any>; +type ProviderMap = Map | InjectionToken, any>; const log = (msg: string) => routerLog(msg); @Directive({ selector: "page-router-outlet" }) // tslint:disable-line:directive-selector export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-line:directive-class-suffix - private activated: ComponentRef|null = null; - private _activatedRoute: ActivatedRoute|null = null; - private refCache: RefCache = new RefCache(); + private activated: ComponentRef | null = null; + private _activatedRoute: ActivatedRoute | null = null; + private isInitialPage: boolean = true; private detachedLoaderFactory: ComponentFactory; - private itemsToDestroy: CacheItem[] = []; private name: string; private viewUtil: ViewUtil; + @Output("activate") activateEvents = new EventEmitter(); // tslint:disable-line:no-output-rename + @Output("deactivate") deactivateEvents = new EventEmitter(); // tslint:disable-line:no-output-rename + /** @deprecated from Angular since v4 */ get locationInjector(): Injector { return this.location.injector; } /** @deprecated from Angular since v4 */ @@ -140,7 +116,8 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l private frame: Frame, private changeDetector: ChangeDetectorRef, @Inject(DEVICE) device: Device, - @Inject(PAGE_FACTORY) private pageFactory: PageFactory + @Inject(PAGE_FACTORY) private pageFactory: PageFactory, + private routeReuseStrategy: NSRouteReuseStrategy ) { this.name = name || PRIMARY_OUTLET; @@ -148,11 +125,6 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l this.viewUtil = new ViewUtil(device); this.detachedLoaderFactory = resolver.resolveComponentFactory(DetachedLoader); - log("DetachedLoaderFactory loaded"); - } - - ngOnDestroy(): void { - this.parentContexts.onChildOutletDestroyed(this.name); } ngOnInit(): void { @@ -176,41 +148,29 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l } } - deactivate(): void { - if (this.locationStrategy._isPageNavigatingBack()) { - log("PageRouterOutlet.deactivate() while going back - should destroy"); - if (!this.isActivated) { - return; - } + ngOnDestroy(): void { + this.parentContexts.onChildOutletDestroyed(this.name); + } - const poppedItem = this.refCache.pop(); - const poppedRef = poppedItem.componentRef; + deactivate(): void { + if (!this.locationStrategy._isPageNavigatingBack()) { + throw new Error("Currently not in page back navigation" + + " - component should be detached instead of deactivated."); + } - if (this.activated !== poppedRef) { - throw new Error("Current componentRef is different for cached componentRef"); - } + log("PageRouterOutlet.deactivate() while going back - should destroy"); - RefCache.destroyItem(poppedItem); - this.activated = null; - } else { - log("PageRouterOutlet.deactivate() while going forward - do nothing"); + if (!this.isActivated) { + return; } - } - private destroyQueuedCacheItems() { - while (this.itemsToDestroy.length > 0) { - this.destroyCacheItem(this.itemsToDestroy.pop()); - } - } + const c = this.activated.instance; + destroyComponentRef(this.activated); - private destroyCacheItem(poppedItem: CacheItem) { - if (isPresent(poppedItem.componentRef)) { - poppedItem.componentRef.destroy(); - } + this.activated = null; + this._activatedRoute = null; - if (isPresent(poppedItem.loaderRef)) { - poppedItem.loaderRef.destroy(); - } + this.deactivateEvents.emit(c); } /** @@ -221,11 +181,10 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l throw new Error("Outlet is not activated"); } - this.location.detach(); - const cmp = this.activated; + const component = this.activated; this.activated = null; this._activatedRoute = null; - return cmp; + return component; } /** @@ -238,7 +197,9 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l this.activated = ref; this._activatedRoute = activatedRoute; - this.location.insert(ref.hostView); + this._activatedRoute.snapshot[pageRouterActivatedSymbol] = true; + + this.locationStrategy._finishBackPageNavigation(); } /** @@ -248,20 +209,21 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l @profile activateWith( activatedRoute: ActivatedRoute, - resolver: ComponentFactoryResolver|null - ): void { + resolver: ComponentFactoryResolver | null): void { - log("PageRouterOutlet.activateWith() - " + - "instanciating new component during commit phase of a navigation"); + if (this.locationStrategy._isPageNavigatingBack()) { + throw new Error("Currently in page back navigation - component should be reattached instead of activated."); + } + + log("PageRouterOutlet.activateWith() - instantiating new component"); this._activatedRoute = activatedRoute; + this._activatedRoute.snapshot[pageRouterActivatedSymbol] = true; + resolver = resolver || this.resolver; - if (this.locationStrategy._isPageNavigatingBack()) { - this.activateOnGoBack(activatedRoute); - } else { - this.activateOnGoForward(activatedRoute, resolver); - } + this.activateOnGoForward(activatedRoute, resolver); + this.activateEvents.emit(this.activated.instance); } private activateOnGoForward( @@ -271,6 +233,7 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l const pageRoute = new PageRoute(activatedRoute); const providers = this.initProvidersMap(activatedRoute, pageRoute); + const childInjector = new ChildInjector(providers, this.location.injector); const factory = this.getComponentFactory(activatedRoute, loadedResolver); @@ -282,12 +245,6 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l this.activated = this.location.createComponent( factory, this.location.length, childInjector, []); this.changeDetector.markForCheck(); - - this.refCache.push({ - componentRef: this.activated, - reusedRoute: pageRoute, - loaderRef: null, - }); } else { log("PageRouterOutlet.activate() forward navigation - " + "create detached loader in the loader container"); @@ -306,11 +263,7 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l this.activated = loaderRef.instance.loadWithFactory(factory); this.loadComponentInPage(page, this.activated); - this.refCache.push({ - componentRef: this.activated, - reusedRoute: pageRoute, - loaderRef, - }); + this.activated[loaderRefSymbol] = loaderRef; } } @@ -329,17 +282,6 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l return providers; } - private activateOnGoBack(activatedRoute: ActivatedRoute): void { - log("PageRouterOutlet.activate() - Back navigation, so load from cache"); - - this.locationStrategy._finishBackPageNavigation(); - - const cacheItem = this.refCache.peek(); - cacheItem.reusedRoute.activatedRoute.next(activatedRoute); - - this.activated = cacheItem.componentRef; - } - @profile private loadComponentInPage(page: Page, componentRef: ComponentRef): void { // Component loaded. Find its root native view. @@ -349,9 +291,6 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l // Add it to the new page page.content = componentView; - page.on(Page.navigatedToEvent, () => setTimeout(() => { - this.destroyQueuedCacheItems(); - })); page.on(Page.navigatedFromEvent, (global).Zone.current.wrap((args: NavigatedData) => { if (args.isBackNavigation) { this.locationStrategy._beginBackPageNavigation(); @@ -360,17 +299,23 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l })); const navOptions = this.locationStrategy._beginPageNavigation(); + + // Clear refCache if navigation with clearHistory + if (navOptions.clearHistory) { + const clearCallback = () => setTimeout(() => { + this.routeReuseStrategy.clearCache(); + page.off(Page.navigatedToEvent, clearCallback); + }); + + page.on(Page.navigatedToEvent, clearCallback); + } + this.frame.navigate({ create: () => { return page; }, clearHistory: navOptions.clearHistory, animated: navOptions.animated, transition: navOptions.transition }); - - // Clear refCache if navigation with clearHistory - if (navOptions.clearHistory) { - this.refCache.clear(); - } } // NOTE: Using private APIs - potential break point! 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: [