diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index 6e29631db..000000000
--- a/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,7 +0,0 @@
-PSST! Want to get faster responses to bug reports and important issues? Help us keep the issue tracker organized by:
-
-- Reporting bugs. Please include all details about the problem and provide a sample app (consider using the [playground](https://play.nativescript.org) for this.
-- Proposing features and enhancements.
-- Discussing implementation details.
-
-Please move all other content to the [forums](https://discourse.nativescript.org/).
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000..70c9beb9d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,31 @@
+---
+name: Bug report
+about: 'We really appreciate your effort to provide feedback. Before opening a new
+ issue, please make sure that this case is not already reported in GitHub as an
+ issue or in StackOverflow as a question.'
+
+---
+
+**Environment**
+Provide version numbers for the following components (information can be retrieved by running `tns info` in your project folder or by inspecting the `package.json` of the project):
+ - CLI:
+ - Cross-platform modules:
+ - Android Runtime:
+ - iOS Runtime:
+ - Plugin(s):
+ - NativeScript-Angular:
+ - Angular:
+
+**Describe the bug**
+
+
+**To Reproduce**
+
+
+**Expected behavior**
+
+**Sample project**
+
+
+**Additional context**
+
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 000000000..397090987
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,17 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+
+
+**Describe the solution you'd like**
+
+
+**Describe alternatives you've considered**
+
+
+**Additional context**
+
diff --git a/.gitignore b/.gitignore
index 7b21aa525..ca4ea8626 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,11 @@
-bin/dist
node_modules
+platforms
+hooks
tags
+dist
**/*.js.map
**/*.metadata.json
-/hooks
-
/nativescript-angular/**/*.d.ts
/nativescript-angular/**/*.js
@@ -13,7 +13,16 @@ tags
!/nativescript-angular/postinstall.js
!/nativescript-angular/hooks/**/*.js
!/nativescript-angular/gulpfile.js
-!/nativescript-angular/zone-js/**/*.js
+!/nativescript-angular/zone-js/dist/*.js
+
+/nativescript-angular-package/**/*.d.ts
+/nativescript-angular-package/**/*.js
+
+!/nativescript-angular-package/global.d.ts
+!/nativescript-angular-package/postinstall.js
+!/nativescript-angular-package/hooks/**/*.js
+!/nativescript-angular-package/gulpfile.js
+!/nativescript-angular-package/zone-js/dist/*.js
.tscache
.nvm
@@ -23,24 +32,15 @@ npm-debug.log
nativescript-angular*.tgz
package-lock.json
+# Apps' JS files
tests/app/**/*.js
tests/test-output.txt
-tests/platforms
-tests/lib
-tests/node_modules
-tests/hooks
-
ng-sample/app/**/*.js
-ng-sample/app/global.d.ts
-ng-sample/platforms
-ng-sample/lib
-ng-sample/node_modules
-ng-sample/hooks
-ng-sample/app/nativescript-angular
-startup-test/platforms
-startup-test/lib
-startup-test/node_modules
+# Webpack configuration files
+webpack.config.js
+tsconfig.esm.json
+tsconfig.tns.json
# IDEs and editors
/.idea
@@ -56,3 +56,4 @@ startup-test/node_modules
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
+
diff --git a/.travis.yml b/.travis.yml
index 6cfafd7c2..7f437f40b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,143 +1,8 @@
-env:
- global:
- - NODE_VERSION=6
- - NODE_VERSION=8
- - EMULATOR_API_LEVEL=22
- - ANDROID_VERSION=25
- - ANDROID_BUILD_TOOLS_VERSION=26.0.2
- - ANDROID_ABI=armeabi-v7a
- - APPIUM_VERSION=1.7.0
- - EMULATOR_NAME=test
- - ANDROID_TAG=google_apis
-
-matrix:
- include:
- - stage: "Lint"
- language: node_js
- os: linux
- before_install:
- - nvm install $NODE_VERSION
- script:
- - cd nativescript-angular
- - npm install
- - npm run tslint
- - stage: "Build"
- os: osx
- env:
- - Build="iOS"
- osx_image: xcode8.3
- language: node_js
- node_js: "6"
- jdk: oraclejdk8
- before_install:
- - cd nativescript-angular
- - npm install
- - cd ../tests
- - npm install
- - cd ..
- script:
- - cd nativescript-angular
- - npm run ngc
- - cd ../tests
- - tns build ios
- - os: osx
- env:
- - Build="iOS"
- osx_image: xcode8.3
- language: node_js
- node_js: "8"
- jdk: oraclejdk8
- before_install:
- - cd nativescript-angular
- - npm install
- - cd ../tests
- - npm install
- - cd ..
- script:
- - cd nativescript-angular
- - npm run ngc
- - cd ../tests
- - tns build ios
- - language: android
- node_js: "6"
- os: linux
- env:
- - Build="Android"
- jdk: oraclejdk8
- before_install:
- - echo no | android create avd --force -n $EMULATOR_NAME -t "android-"$EMULATOR_API_LEVEL --abi $ANDROID_ABI --tag $ANDROID_TAG -c 12M
- - QEMU_AUDIO_DRV=none emulator -avd $EMULATOR_NAME -no-window &
- - nvm install $NODE_VERSION
- - cd nativescript-angular
- - npm install
- - cd ../tests
- - npm install
- - cd ..
- - android-wait-for-emulator
- - adb shell input keyevent 82 &
- script:
- - cd nativescript-angular
- - npm run ngc
- - cd ../tests
- - tns build android
- - language: android
- node_js: "8"
- os: linux
- env:
- - Build="Android"
- jdk: oraclejdk8
- before_install:
- - echo no | android create avd --force -n $EMULATOR_NAME -t "android-"$EMULATOR_API_LEVEL --abi $ANDROID_ABI --tag $ANDROID_TAG -c 12M
- - QEMU_AUDIO_DRV=none emulator -avd $EMULATOR_NAME -no-window &
- - nvm install $NODE_VERSION
- - cd nativescript-angular
- - npm install
- - cd ../tests
- - npm install
- - cd ..
- - android-wait-for-emulator
- - adb shell input keyevent 82 &
- script:
- - cd nativescript-angular
- - npm run ngc
- - cd ../tests
- - tns build android
-
-android:
- components:
- - tools
- - platform-tools
- - build-tools-25.0.2
- - build-tools-$ANDROID_BUILD_TOOLS_VERSION
- - android-25
- - android-$EMULATOR_API_LEVEL
- - extra-google-google_play_services
- - extra-google-m2repository
- - extra-android-m2repository
- - addon-google_apis-google-25
- # Specify at least one system image,
- # if you need to run emulator(s) during your tests
- - sys-img-armeabi-v7a-android-26
- - sys-img-armeabi-v7a-android-25
- - sys-img-armeabi-v7a-android-17
- - sys-img-armeabi-v7a-android-19
- - sys-img-armeabi-v7a-google_apis-$EMULATOR_API_LEVEL
- licenses:
- - ".+"
-
-before_cache:
- - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
-
-cache:
- directories:
- - .nvm
- - $HOME/.gradle/caches/
- - $HOME/.gradle/wrapper/
-
-install:
- - nvm install $NODE_VERSION
- - npm install -g typings
- - wget -O ./nativescript.tgz "https://s3.amazonaws.com/nativescript-ci/build_result/nativescript.tgz"
- - npm install -g nativescript.tgz --ignore-scripts
- - tns usage-reporting disable
- - tns error-reporting disable
+language: node_js
+node_js:
+ - "node"
+script:
+ - cd nativescript-angular
+ - npm install
+ - npm run format-check
+ - npm pack
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eaa1cdc72..e7e0c6b06 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,672 @@
+# [11.2.0](https://github.com/NativeScript/nativescript-angular/compare/11.0.1...11.2.0) (2021-03-24)
+
+
+### Bug Fixes
+
+* **ivy:** nsRouterLinkActive works on nsRouterLink ([#2322](https://github.com/NativeScript/nativescript-angular/issues/2322)) ([13a3562](https://github.com/NativeScript/nativescript-angular/commit/13a3562146ef8516fea8bb3d988f8e0ddc9617a7))
+
+
+
+## [11.0.1](https://github.com/NativeScript/nativescript-angular/compare/11.0.0...11.0.1) (2021-01-22)
+
+
+### Bug Fixes
+
+* blank screen on hmr ([#2317](https://github.com/NativeScript/nativescript-angular/issues/2317)) ([197d802](https://github.com/NativeScript/nativescript-angular/commit/197d802976ddf4ae3bba45f2d000a687049c8027))
+
+
+
+# [11.0.0](https://github.com/NativeScript/nativescript-angular/compare/10.1.7...11.0.0) (2020-11-17)
+
+
+### Features
+
+* **angular:** v11 ([#2297](https://github.com/NativeScript/nativescript-angular/issues/2297)) ([e3657c4](https://github.com/NativeScript/nativescript-angular/commit/e3657c446db7af23d50f8514d001e5709c2b7788))
+
+
+
+## [10.1.7](https://github.com/NativeScript/nativescript-angular/compare/10.1.5...10.1.7) (2020-10-12)
+
+
+### Bug Fixes
+
+* Export injection token used by plugins ([#2268](https://github.com/NativeScript/nativescript-angular/issues/2268)) ([75865f2](https://github.com/NativeScript/nativescript-angular/commit/75865f214d9e53eb36990c779808d50fb697f1ce))
+* expose NSLocationStrategy and NSRouteReuseStrategy ([9b8435e](https://github.com/NativeScript/nativescript-angular/commit/9b8435e13b7f6f42098e79895af5ab3156d1aac3))
+
+
+### Features
+
+* export injectiontoken for template items ([52a56d7](https://github.com/NativeScript/nativescript-angular/commit/52a56d7201b70b67edb2ca77e9042737f216a07a))
+
+
+
+## [10.1.5](https://github.com/NativeScript/nativescript-angular/compare/10.1.4...10.1.5) (2020-09-21)
+
+
+### Bug Fixes
+
+* **renderer:** dynamic views can't be prepended ([#2262](https://github.com/NativeScript/nativescript-angular/issues/2262)) ([5735a01](https://github.com/NativeScript/nativescript-angular/commit/5735a01695d7967b5a63222dcec760ddac0533e3))
+
+
+
+## [10.1.4](https://github.com/NativeScript/nativescript-angular/compare/10.1.3...10.1.4) (2020-09-21)
+
+
+### Bug Fixes
+
+* **detached-loader:** detach loadWithFactory ([#2260](https://github.com/NativeScript/nativescript-angular/issues/2260)) ([1dca81b](https://github.com/NativeScript/nativescript-angular/commit/1dca81bbc4a1a05a9eefe13988848124885f3178))
+* **renderer:** order not preserved ([#2261](https://github.com/NativeScript/nativescript-angular/issues/2261)) ([07abb9e](https://github.com/NativeScript/nativescript-angular/commit/07abb9e62c2408be83e8a694384cd529ba5d3309))
+
+
+
+## [10.1.3](https://github.com/NativeScript/nativescript-angular/compare/10.1.0...10.1.3) (2020-09-21)
+
+
+### Bug Fixes
+
+* **detached-loader:** completely deatch components ([#2257](https://github.com/NativeScript/nativescript-angular/issues/2257)) ([623d2f7](https://github.com/NativeScript/nativescript-angular/commit/623d2f79e014017fd993685051d125b3c5c72ada))
+* **ivy:** support view references in insertBefore ([#2258](https://github.com/NativeScript/nativescript-angular/issues/2258)) ([c2eaef5](https://github.com/NativeScript/nativescript-angular/commit/c2eaef52a66dbbee982f73bb968a706cee38f633))
+
+
+
+# [10.1.0](https://github.com/NativeScript/nativescript-angular/compare/10.0.3...10.1.0) (2020-09-04)
+
+
+### Bug Fixes
+
+* **list-view:** fix crash when used with ngFor ([#2121](https://github.com/NativeScript/nativescript-angular/issues/2121)) ([302afb3](https://github.com/NativeScript/nativescript-angular/commit/302afb350dea56ddbf1a0d772f6d9405413890a5))
+
+
+### Features
+
+* **angular:** ng 10.1 and ns 7 ([#2237](https://github.com/NativeScript/nativescript-angular/issues/2237)) ([548e074](https://github.com/NativeScript/nativescript-angular/commit/548e0743eb092ba3fa371ea4d26879ee5bf03983))
+
+
+
+## [10.0.3](https://github.com/NativeScript/nativescript-angular/compare/10.0.2...10.0.3) (2020-08-27)
+
+
+### Bug Fixes
+
+* **router:** page navigation bug if there's not outlet for frame after clearing history ([#2233](https://github.com/NativeScript/nativescript-angular/issues/2233)) ([8bedc2d](https://github.com/NativeScript/nativescript-angular/commit/8bedc2d14fccc8d6c1ea18151bda7ce7c87fddae))
+
+
+
+## [10.0.2](https://github.com/NativeScript/nativescript-angular/compare/10.0.1...10.0.2) (2020-08-19)
+
+
+### Bug Fixes
+
+* **router:** link active directive ([#2227](https://github.com/NativeScript/nativescript-angular/issues/2227)) ([5e165b5](https://github.com/NativeScript/nativescript-angular/commit/5e165b56c7a61d28fdb279ed8da07288e6dbb7af))
+
+
+
+# [10.0.0](https://github.com/NativeScript/nativescript-angular/compare/8.20.4...10.0.0) (2020-07-30)
+
+### Features
+
+* **angular:** support for v10 ([#2189](https://github.com/NativeScript/nativescript-angular/issues/2189)) ([fd71458](https://github.com/NativeScript/nativescript-angular/commit/fd714588dc93762c2d5a129d3a08c92317df68e4))
+* ivy support ([#2169](https://github.com/NativeScript/nativescript-angular/issues/2169)) ([f10a8fb](https://github.com/NativeScript/nativescript-angular/commit/f10a8fb9f7c8bbe35be10c736210a451f8c52670)), closes [#2152](https://github.com/NativeScript/nativescript-angular/issues/2152) [#2060](https://github.com/NativeScript/nativescript-angular/issues/2060)
+* support for async APP_INITIALIZER and animated launch screens ([#2170](https://github.com/NativeScript/nativescript-angular/issues/2170)) ([b6ac290](https://github.com/NativeScript/nativescript-angular/commit/b6ac29038e27c310803ce956f7d93aca4d8ecd2a))
+
+### Bug Fixes
+
+* **router:** query params are now preserved when navigating back ([#2062](https://github.com/NativeScript/nativescript-angular/issues/2062)) ([221e404](https://github.com/NativeScript/nativescript-angular/commit/221e404da244b64004cfb3cd3e947858133af347))
+* **segmented-bar:** listview crash when scrolling ([#2128](https://github.com/NativeScript/nativescript-angular/issues/2128)) ([1b191b0](https://github.com/NativeScript/nativescript-angular/commit/1b191b06ccd0df2871ef741ab97f14c04911864e))
+
+
+
+
+# [9.0.0](https://github.com/NativeScript/nativescript-angular/compare/8.20.4...9.0.0) (2020-06-03)
+
+
+
+### Bug Fixes
+
+* **bindable:** parent referenced expression-values now load properly using an update call ([#8670](https://github.com/NativeScript/NativeScript/issues/8670)) ([6b0028a](https://github.com/NativeScript/NativeScript/commit/6b0028afd7b554914b039cdf371e8e30f6e02dac)), closes [#8666](https://github.com/NativeScript/NativeScript/issues/8666) [#6981](https://github.com/NativeScript/NativeScript/issues/6981) [#5054](https://github.com/NativeScript/NativeScript/issues/5054)
+* **scroll-view:** android 'isScrollEnabled' will apply if changed while gesture is underway ([#8695](https://github.com/NativeScript/NativeScript/issues/8695)) ([02ec7f1](https://github.com/NativeScript/NativeScript/commit/02ec7f104d327df53df687ddd1b8ac5b1cdc04ba))
+* **snapshots:** android is not defined ([#8691](https://github.com/NativeScript/NativeScript/issues/8691)) ([a8bbd7c](https://github.com/NativeScript/NativeScript/commit/a8bbd7c1e580e77e7ad5ddc7be6845e3d8fb02de))
+* **text-view:** only reload text if hint is showing on ios ([#8662](https://github.com/NativeScript/NativeScript/issues/8662)) ([ec17727](https://github.com/NativeScript/NativeScript/commit/ec17727e91f7a3209ada2c7de0bcf59c98c4e62a))
+
+
+### Features
+
+* **connectivity:** getActiveNetworkInfo and NetworkInfo modern compliance [#8580](https://github.com/NativeScript/NativeScript/issues/8580) ([#8652](https://github.com/NativeScript/NativeScript/issues/8652)) ([635f31f](https://github.com/NativeScript/NativeScript/commit/635f31f81f7826112142c707aff2a66c2b480b0e))
+* **dialog:** ios destructive style from options ([#8676](https://github.com/NativeScript/NativeScript/issues/8676)) ([bb531ce](https://github.com/NativeScript/NativeScript/commit/bb531ce71028f9c4fd4d753df16c82104f158e35))
+* **ImageSource:** resize method ([#8678](https://github.com/NativeScript/NativeScript/issues/8678)) ([bd12baf](https://github.com/NativeScript/NativeScript/commit/bd12bafb4aae8f1c523be4c7e04fa73722092304))
+* **text-view:** allow easy subclassing on ios ([#8663](https://github.com/NativeScript/NativeScript/issues/8663)) ([7d36447](https://github.com/NativeScript/NativeScript/commit/7d364474c23e17acf7696f159d3945d8a73d63e6))
+
+
+### Features
+
+* angular 9 ivy ([fbe2450](https://github.com/NativeScript/nativescript-angular/commit/fbe2450))
+
+
+
+## [8.20.4](https://github.com/NativeScript/nativescript-angular/compare/8.20.3...8.20.4) (2020-01-07)
+
+
+### Bug Fixes
+
+* add context to frame navigation ([#2100](https://github.com/NativeScript/nativescript-angular/pull/2100))
+* wrong import path in compat package ([#2097](https://github.com/NativeScript/nativescript-angular/pull/2097))
+
+
+
+
+## [8.20.3](https://github.com/NativeScript/nativescript-angular/compare/8.20.2...8.20.3) (2019-11-13)
+
+
+### Bug Fixes
+
+* add exports for backwards compatibility ([fbd46c1](https://github.com/NativeScript/nativescript-angular/commit/fbd46c1))
+
+
+## [8.20.2](https://github.com/NativeScript/nativescript-angular/compare/8.20.1...8.20.2) (2019-11-12)
+
+
+### Bug Fixes
+
+* add exports in `nativescript-angular` package for backwards compatibility ([4a5d022](https://github.com/NativeScript/nativescript-angular/commit/4a5d022))
+
+
+
+# [8.20.1](https://github.com/NativeScript/nativescript-angular/compare/8.20.0...8.20.1) (2019-11-07)
+
+### Bug Fixes
+
+* add exports in `nativescript-angular` package for backwards compatibility
+
+# [8.20.0](https://github.com/NativeScript/nativescript-angular/compare/8.2.2...8.20.0) (2019-10-23)
+
+### Features
+* add scoped package @nativescript/angular ([#2014](https://github.com/NativeScript/nativescript-angular/pull/2014))
+
+### Bug Fixes
+
+* **animations:** resolve issue with "query animations" on iOS 13 ([#2022](https://github.com/NativeScript/nativescript-angular/issues/2022)) ([c382682](https://github.com/NativeScript/nativescript-angular/commit/c382682))
+
+
+## [8.2.2](https://github.com/NativeScript/nativescript-angular/compare/8.2.1...8.2.2) (2019-10-16)
+
+### Features
+
+* Support for adding scoped CSS without triggering global refresh ([#1999](https://github.com/NativeScript/nativescript-angular/pull/1999)) ([662c122](https://github.com/NativeScript/nativescript-angular/commit/662c122))
+
+
+
+
+## [8.2.1](https://github.com/NativeScript/nativescript-angular/compare/8.2.0...8.2.1) (2019-08-28)
+
+
+### Bug Fixes
+
+* **hmr:** close modal views during livesync [#7669](https://github.com/NativeScript/nativescript-angular/issues/7669) ([#1944](https://github.com/NativeScript/nativescript-angular/issues/1944)) ([73d83ed](https://github.com/NativeScript/nativescript-angular/commit/73d83ed))
+
+
+
+
+# [8.2.0](https://github.com/NativeScript/nativescript-angular/compare/8.1.0...8.2.0) (2019-08-13)
+
+
+### Features
+
+* Update to Angular 8.2.x ([dbe983b](https://github.com/NativeScript/nativescript-angular/commit/dbe983b))
+* Remove `@angular/http` from the dependecies ([#1842](https://github.com/NativeScript/nativescript-angular/issues/1842)) ([c6af21d](https://github.com/NativeScript/nativescript-angular/commit/c6af21d))
+
+### BREAKING CHANGES:
+
+* The `NativeScriptHttpModule` is removed
+* `@angular/http` is removed from the dependencies
+
+Migration steps:
+* If using `NativeScriptHttpModule`, replace with `NativeScriptHttpClientModule` as done [here](https://github.com/NativeScript/nativescript-sdk-examples-ng/commit/16d3caee2b0ee2d88d328b75bde49eea6c96920a)
+* as `@angular/http` is no longer a peerDependency of `nativescript-angular`, you can remove if from your dependencies
+
+
+
+# [8.1.0](https://github.com/NativeScript/nativescript-angular/compare/8.0.3...8.1.0) (2019-07-31)
+
+
+### Features
+
+* update to Angular 8.1.x ([#1907](https://github.com/NativeScript/nativescript-angular/issues/1907)) ([e1db5f8](https://github.com/NativeScript/nativescript-angular/commit/e1db5f8))
+
+
+
+## [8.0.3](https://github.com/NativeScript/nativescript-angular/compare/8.0.2...8.0.3) (2019-07-26)
+
+
+### Bug Fixes
+
+* add backwards compatibility for 'tns-core-modules' version 5.4.x ([635bb36](https://github.com/NativeScript/nativescript-angular/commit/635bb36))
+
+
+### Features
+
+* **tabs:** register tabs elements ([#1883](https://github.com/NativeScript/nativescript-angular/issues/1883)) ([092a833](https://github.com/NativeScript/nativescript-angular/commit/092a833))
+
+
+
+
+## [8.0.2](https://github.com/NativeScript/nativescript-angular/compare/8.0.1...8.0.2) (2019-06-28)
+
+### Bug Fixes
+
+ * remove deprecated API usage ([#1874](https://github.com/NativeScript/nativescript-angular/pull/1874)) ([ab740cd](https://github.com/NativeScript/nativescript-angular/commit/ab740cd))
+ * migrate from ReflectiveInjector (deprecated) to StaticInjector ([#1868](https://github.com/NativeScript/nativescript-angular/pull/1868)) ([e432841](https://github.com/NativeScript/nativescript-angular/pull/1868/commits/e432841e9b474ad188f87044b74666b322d68b5d))
+
+ ### Features
+
+* Update peer dependencies to Angular 8.x.x ([#1857](https://github.com/NativeScript/nativescript-angular/pull/1857)) ([060aabf](https://github.com/NativeScript/nativescript-angular/pull/1857/commits/060aabf146aa0f132f3617dcfaeea0efa7baf228))
+* **tabs:** register tabs elements ([#1883](https://github.com/NativeScript/nativescript-angular/issues/1883)) ([092a833](https://github.com/NativeScript/nativescript-angular/commit/092a833))
+
+
+
+## [8.0.1](https://github.com/NativeScript/nativescript-angular/compare/8.0.0...8.0.1) (2019-06-12)
+
+
+### Bug Fixes
+
+* do not throw if element already registered ([#1838](https://github.com/NativeScript/nativescript-angular/issues/1838)) ([e90f8b5](https://github.com/NativeScript/nativescript-angular/commit/e90f8b5))
+* **1845:** CSS special selector ":host" only work first time ([#1852](https://github.com/NativeScript/nativescript-angular/issues/1852)) ([59a5bd8](https://github.com/NativeScript/nativescript-angular/commit/59a5bd8))
+
+
+
+
+# [8.0.0](https://github.com/NativeScript/nativescript-angular/compare/7.2.4...8.0.0) (2019-05-29)
+
+
+### Bug Fixes
+
+* **router:** fix return value for getTransition of NSRouterLink ([d20b645](https://github.com/NativeScript/nativescript-angular/commit/d20b645)), closes [#1784](https://github.com/NativeScript/nativescript-angular/issues/1784)
+* **tsconfig:** exclude aot files from compilation ([a4eb409](https://github.com/NativeScript/nativescript-angular/commit/a4eb409))
+
+
+### Features
+
+* upgrade to Angular 8 ([b16046d](https://github.com/NativeScript/nativescript-angular/commit/b16046d))
+
+
+### BREAKING CHANGES
+
+* In Angular version 8, it's required that all `@ViewChild` and `@ContentChild` queries have a `static` flag specifying whether the query is `static` or `dynamic`. More details about this change can be found [here](https://angular.io/guide/static-query-migration).
+
+Migration steps:
+
+Anywhere you previously had `@ViewChild` with a single param you now have to provide a second param with a `static` property set to either `true` or `false`
+
+**Previous code:**
+```
+import { ElementRef } from "@angular/core";
+
+@ViewChild("myElement") myElement: ElementRef;
+```
+
+**Migrated code:**
+```
+import { ElementRef } from "@angular/core";
+
+@ViewChild("myElement", { static: false }) myElement: ElementRef;
+```
+
+
+
+## [7.2.4](https://github.com/NativeScript/nativescript-angular/compare/7.2.3...7.2.4) (2019-05-14)
+
+
+### Bug Fixes
+
+* **router:** routing services should be provided in forRoot only ([#1729](https://github.com/NativeScript/nativescript-angular/issues/1729)) ([0f6a975](https://github.com/NativeScript/nativescript-angular/commit/0f6a975))
+* mark reattached view for CD ([#1803](https://github.com/NativeScript/nativescript-angular/issues/1803)) ([b6dbe57](https://github.com/NativeScript/nativescript-angular/commit/b6dbe57))
+* Potentiel leak: NativeScript views not cleaned up on removal ([#1738](https://github.com/NativeScript/nativescript-angular/issues/1738)) ([59a1cde](https://github.com/NativeScript/nativescript-angular/commit/59a1cde))
+
+### Features
+
+* **modal:** add ‘ios presentationStyle’ option to ModalDialogParams ([9cfa127](https://github.com/NativeScript/nativescript-angular/commit/9cfa127))
+
+
+
+
+## [7.2.3](https://github.com/NativeScript/nativescript-angular/compare/7.2.2...7.2.3) (2019-03-14)
+
+
+### Bug Fixes
+
+* **location-strategy:** crash on going back with router-outlet after closing modal ([#1748](https://github.com/NativeScript/nativescript-angular/issues/1748)) ([0ed7de6](https://github.com/NativeScript/nativescript-angular/commit/0ed7de6))
+
+
+
+
+## [7.2.2](https://github.com/NativeScript/nativescript-angular/compare/7.2.1...7.2.2) (2019-02-19)
+
+
+### Bug Fixes
+
+* **list-view:** add support for default item template ([4061cc7](https://github.com/NativeScript/nativescript-angular/commit/4061cc7))
+
+
+
+
+## [7.2.1](https://github.com/NativeScript/nativescript-angular/compare/7.2.0...7.2.1) (2019-02-10)
+
+
+### Bug Fixes
+
+* **location-strategy:** extend support for nested primary outlets ([566896d](https://github.com/NativeScript/nativescript-angular/commit/566896d))
+* Router tracing does not work with webpack ([e87ef68](https://github.com/NativeScript/nativescript-angular/commit/e87ef68))
+
+
+
+
+# [7.2.0](https://github.com/NativeScript/nativescript-angular/compare/7.1.2...7.2.0) (2019-01-31)
+
+
+### Bug Fixes
+
+* **p-r-o:** needless forward navigation after back inside nested named outlet ([130e392](https://github.com/NativeScript/nativescript-angular/commit/130e392))
+
+
+### Features
+
+* **router:** detach change detection on navigation ([#1507](https://github.com/NativeScript/nativescript-angular/issues/1507)) ([86cd290](https://github.com/NativeScript/nativescript-angular/commit/86cd290))
+
+
+
+
+## [7.1.2](https://github.com/NativeScript/nativescript-angular/compare/7.1.1...7.1.2) (2019-01-21)
+
+
+### Bug Fixes
+
+ * **page-router-outlet:** fix(empty-outlet): remove unnecessary moduleId ([#1686](https://github.com/NativeScript/nativescript-angular/issues/1686)) ([1222e57](https://github.com/NativeScript/nativescript-angular/commit/1222e57))
+
+
+
+## [7.1.1](https://github.com/NativeScript/nativescript-angular/compare/7.1.0...7.1.1) (2018-12-20)
+
+
+### Bug Fixes
+
+ * **page-router-outlet:** prevent needless forward navigation after back inside nested named outlet ([d8a0653](https://github.com/NativeScript/nativescript-angular/commit/d8a0653))
+
+
+
+
+# [7.1.0](https://github.com/NativeScript/nativescript-angular/compare/7.0.3...7.1.0) (2018-12-07)
+
+
+### Features
+
+* Angular 7.1 support
+
+
+## [7.0.3](https://github.com/NativeScript/nativescript-angular/compare/7.0.2...7.0.3) (2018-12-05)
+
+
+### Bug Fixes
+
+* **router:** handle nested primary p-r-o ([#1645](https://github.com/NativeScript/nativescript-angular/issues/1645)) ([e632fc5](https://github.com/NativeScript/nativescript-angular/commit/e632fc5))
+
+
+
+
+## [7.0.2](https://github.com/NativeScript/nativescript-angular/compare/7.0.1...7.0.2) (2018-11-26)
+
+
+### Bug Fixes
+
+* **page-router-outlet:** actionBarVisibility not applied ([#1621](https://github.com/NativeScript/nativescript-angular/issues/1621)) ([a6ff509](https://github.com/NativeScript/nativescript-angular/commit/a6ff509))
+* **router-extensions:** unable to go back with relativeTo param ([#1632](https://github.com/NativeScript/nativescript-angular/issues/1632)) ([63900dc](https://github.com/NativeScript/nativescript-angular/commit/63900dc))
+
+
+
+
+## [7.0.1](https://github.com/NativeScript/nativescript-angular/compare/7.0.0...7.0.1) (2018-11-20)
+
+
+### Bug Fixes
+
+* crash in deactivate page router outlet ([#1590](https://github.com/NativeScript/nativescript-angular/issues/1590)) ([f8c7468](https://github.com/NativeScript/nativescript-angular/commit/f8c7468))
+* **dialogs:** unable to reopen shared modal view when tab as root ([199c245](https://github.com/NativeScript/nativescript-angular/commit/199c245))
+* **location-strategy:** crash when going back on nested named lazy loaded module ([#1618](https://github.com/NativeScript/nativescript-angular/issues/1618)) ([d9ffb83](https://github.com/NativeScript/nativescript-angular/commit/d9ffb83))
+
+
+
+
+# [7.0.0](https://github.com/NativeScript/nativescript-angular/compare/6.2.0...7.0.0) (2018-11-12)
+
+
+### Bug Fixes
+
+* crash in deactivate page router outlet ([#1590](https://github.com/NativeScript/nativescript-angular/issues/1590)) ([f8c7468](https://github.com/NativeScript/nativescript-angular/commit/f8c7468))
+
+* **location-strategy:** crash on going back to TabView with nested outlets ([#1582](https://github.com/NativeScript/nativescript-angular/issues/1582)) ([f755991](https://github.com/NativeScript/nativescript-angular/commit/f755991))
+
+
+### Features
+
+* Angular 7 support
+
+
+
+
+# [6.2.0](https://github.com/NativeScript/nativescript-angular/compare/6.1.0...6.2.0) (2018-10-30)
+
+
+### Bug Fixes
+
+* **frame-service:** move FrameService provider to NativeScriptModule ([#1489](https://github.com/NativeScript/nativescript-angular/issues/1489)) ([3b434c9](https://github.com/NativeScript/nativescript-angular/commit/3b434c9))
+* Import reflect-metadata(needed in JIT mode) before [@angular](https://github.com/angular) ([#1530](https://github.com/NativeScript/nativescript-angular/issues/1530)) ([6e45af0](https://github.com/NativeScript/nativescript-angular/commit/6e45af0))
+* Persist the original "parentNode" when "retrieving" the root View created by createEmbeddedView ([#1542](https://github.com/NativeScript/nativescript-angular/issues/1542)) ([0b8d2c5](https://github.com/NativeScript/nativescript-angular/commit/0b8d2c5))
+
+
+### Features
+
+* add actionBarVisibility property to page-router-outlet ([#1573](https://github.com/NativeScript/nativescript-angular/issues/1573)) ([c645ca8](https://github.com/NativeScript/nativescript-angular/commit/c645ca8))
+* enable nesting named page router outlets ([#1556](https://github.com/NativeScript/nativescript-angular/issues/1556)) ([46a0dc0](https://github.com/NativeScript/nativescript-angular/commit/46a0dc0))
+* HMR bootstrap and livesync options ([#1531](https://github.com/NativeScript/nativescript-angular/issues/1531)) ([1e92c7b](https://github.com/NativeScript/nativescript-angular/commit/1e92c7b))
+
+
+
+
+## [6.1.0](https://github.com/NativeScript/nativescript-angular/compare/6.0.6...6.1.0) (2018-08-06)
+
+
+### Bug Fixes
+
+* mark NativeScriptModule as root injector ([#1418](https://github.com/NativeScript/nativescript-angular/issues/1418)) ([ce70add](https://github.com/NativeScript/nativescript-angular/commit/ce70add))
+* provide NullViewportScroller in NativeScriptModule ([dd412bf](https://github.com/NativeScript/nativescript-angular/commit/dd412bf))
+* **animations:** inject document object in the animation engine ([#1395](https://github.com/NativeScript/nativescript-angular/issues/1395)) ([379e958](https://github.com/NativeScript/nativescript-angular/commit/379e958)), closes [angular/angular#23300](https://github.com/angular/angular/issues/23300) [#1393](https://github.com/NativeScript/nativescript-angular/issues/1393)
+* **forms:** TextValueAccessor raises onTouched on blur ([#1230](https://github.com/NativeScript/nativescript-angular/issues/1230)) ([06ca3a0](https://github.com/NativeScript/nativescript-angular/commit/06ca3a0))
+* remove global document object ([2b201be](https://github.com/NativeScript/nativescript-angular/commit/2b201be))
+* **location-strategy:** find the correct outlet when navigating back and forward ([#1404](https://github.com/NativeScript/nativescript-angular/issues/1404)) ([f0119a0](https://github.com/NativeScript/nativescript-angular/commit/f0119a0))
+* **modal:** lower isModalNavigation flag when closing modal ([#1378](https://github.com/NativeScript/nativescript-angular/issues/1378)) ([6ab1cac](https://github.com/NativeScript/nativescript-angular/commit/6ab1cac))
+* **modal:** throw from tns-core-modules is now properly caught and rejected ([70730d9](https://github.com/NativeScript/nativescript-angular/commit/70730d9))
+* **router:** avoiding throw for app stability improvements ([#1344](https://github.com/NativeScript/nativescript-angular/issues/1344)) ([82747df](https://github.com/NativeScript/nativescript-angular/commit/82747df))
+* **tabview:** implement setter for TabViewItem Directive's configuration ([#845](https://github.com/NativeScript/nativescript-angular/issues/845)) ([#1370](https://github.com/NativeScript/nativescript-angular/issues/1370)) ([1d44679](https://github.com/NativeScript/nativescript-angular/commit/1d44679))
+
+
+### Features
+
+* **test-bed:** Run render fixtures in a full-page container ([#1416](https://github.com/NativeScript/nativescript-angular/issues/1416)) ([e551df2](https://github.com/NativeScript/nativescript-angular/commit/e551df2))
+
+
+### BREAKING CHANGES
+
+* The `document` object is no longer property of the `global` object. This
+may cause behavioral changes in some plugin that use the `document`
+object to determine if they're running in browser context.
+
+Fixes https://github.com/NativeScript/nativescript-angular/issues/1144.
+
+
+
+## [6.0.6](https://github.com/NativeScript/nativescript-angular/compare/6.0.0...6.0.6) (2018-06-22)
+
+> IMPORTANT! You should use this version with @angular/* v6.0.6 and up.
+
+### Bug Fixes
+
+* clean up properly shared modal page router outlets ([#1360](https://github.com/NativeScript/nativescript-angular/issues/1360)) ([3332ca2](https://github.com/NativeScript/nativescript-angular/commit/3332ca2))
+* **animations:** inject document object in the animation engine ([#1395](https://github.com/NativeScript/nativescript-angular/issues/1395)) ([379e958](https://github.com/NativeScript/nativescript-angular/commit/379e958)), closes [#1393](https://github.com/NativeScript/nativescript-angular/issues/1393)
+* **modal:** lower isModalNavigation flag when closing modal ([#1378](https://github.com/NativeScript/nativescript-angular/issues/1378)) ([6ab1cac](https://github.com/NativeScript/nativescript-angular/commit/6ab1cac))
+
+
+
+# [6.0.0](https://github.com/NativeScript/nativescript-angular/compare/5.3.0...6.0.0) (2018-05-30)
+
+
+### Bug Fixes
+
+* Use cssType (uglify safe) instead typeName of for view metadata. ([d85910c](https://github.com/NativeScript/nativescript-angular/commit/d85910c))
+* **modal:** closeCallback(...) should not have side effects when called multiple times ([#1349](https://github.com/NativeScript/nativescript-angular/issues/1349)) ([bffbbc2](https://github.com/NativeScript/nativescript-angular/commit/bffbbc2))
+* **modal:** missing animated & stretched params ([#1293](https://github.com/NativeScript/nativescript-angular/issues/1293)) ([a9a901b](https://github.com/NativeScript/nativescript-angular/commit/a9a901b))
+* **router:** state is not guarded before use ([#1331](https://github.com/NativeScript/nativescript-angular/issues/1331)) ([d27a893](https://github.com/NativeScript/nativescript-angular/commit/d27a893)), closes [/github.com/NativeScript/nativescript-angular/commit/b98da83adb3f5c51ee448fa38a51b7c65274c82e#diff-a7820fa2a2eb0ce14f3f0b8bfc666dd5R49](https://github.com//github.com/NativeScript/nativescript-angular/commit/b98da83adb3f5c51ee448fa38a51b7c65274c82e/issues/diff-a7820fa2a2eb0ce14f3f0b8bfc666dd5R49)
+* **TabViewItemDirective :** textTransform property added ([#1315](https://github.com/NativeScript/nativescript-angular/issues/1315)) ([11d01f9](https://github.com/NativeScript/nativescript-angular/commit/11d01f9))
+
+
+### Features
+
+* Angular 6 support
+* **router:** enable flexible page router outlets ([#1298](https://github.com/NativeScript/nativescript-angular/issues/1298)) ([b98da83](https://github.com/NativeScript/nativescript-angular/commit/b98da83))
+* **testing:** Testing Components with TestBed ([#1175](https://github.com/NativeScript/nativescript-angular/issues/1175)) ([52f3ec6](https://github.com/NativeScript/nativescript-angular/commit/52f3ec6))
+
+
+
+# [5.3.0](https://github.com/NativeScript/nativescript-angular/compare/5.2.0...v5.3.0) (2018-04-10)
+
+> This version requires NativeScript 4.0.
+
+### Bug Fixes
+
+* **animations:** provide fake document object in both AoT and JiT mode ([#1164](https://github.com/NativeScript/nativescript-angular/issues/1164)) ([040e0e3](https://github.com/NativeScript/nativescript-angular/commit/040e0e3)), closes [#1163](https://github.com/NativeScript/nativescript-angular/issues/1163)
+* App crashes on restart in android ([#1261](https://github.com/NativeScript/nativescript-angular/issues/1261)) ([331b878](https://github.com/NativeScript/nativescript-angular/commit/331b878))
+
+
+### Features
+
+* NS 4.0 Integration ([#1250](https://github.com/NativeScript/nativescript-angular/issues/1250)) ([f84fbdc](https://github.com/NativeScript/nativescript-angular/commit/f84fbdc))
+* prevent core modules from getting loaded multiple times ([#1196](https://github.com/NativeScript/nativescript-angular/issues/1196)) ([010fed7](https://github.com/NativeScript/nativescript-angular/commit/010fed7))
+
+
+### BREAKING CHANGES
+
+#### Importing `NativeScriptModule` and `NativeScriptAnimationsModule` in multiple ngModules is no longer allowed.
+
+To migrate:
+ * in `AppModule`:
+ * import `NativeScriptModule`
+ * import`NativeScriptAnimationsModule` - only if you are planning to use Angular Animations
+ * in the remaining modules:
+ * remove `NativeScriptModule` imports and replace with `NativeScriptCommonModule` import
+ * remove `NativeScriptAnimationsModule` imports
+
+BEFORE:
+
+app.module.ts:
+```
+import { NativeScriptModule } from 'nativescript-angular/nativescript.module';
+import { NativeScriptAnimationsModule } from 'nativescript-angular/animations';
+...
+@NgModule({
+ imports: [
+ NativeScriptModule,
+ NativeScriptAnimationsModule
+ ],
+...
+})
+```
+
+another.module.ts:
+```
+import { NativeScriptModule } from 'nativescript-angular/nativescript.module';
+import { NativeScriptAnimationsModule } from 'nativescript-angular/animations';
+...
+@NgModule({
+ imports: [
+ NativeScriptModule,
+ NativeScriptAnimationsModule
+ ],
+...
+})
+```
+
+AFTER:
+
+app.module.ts:
+```
+import { NativeScriptModule } from 'nativescript-angular/nativescript.module';
+import { NativeScriptAnimationsModule } from 'nativescript-angular/animations';
+...
+@NgModule({
+ imports: [
+ NativeScriptModule,
+ NativeScriptAnimationsModule
+ ],
+...
+})
+```
+
+another.module.ts:
+```
+import { NativeScriptCommonModule } from 'nativescript-angular/common';
+...
+@NgModule({
+ imports: [
+ NativeScriptCommonModule
+ ],
+...
+})
+```
+
+#### NativeScript 4.0 Compatible Bootstrap and Navigation
+NativeScript 4.0 allows you to put any view as the root (not only Frame) of the application. To support in angular projects we had to introduce some changes in how A{N}gular apps are bootstrapped.
+
+PREVIOUS BEHAVIOR
+
+Bootstrap creates a root `Frame` and initial `Page`. Then it bootstraps the angular application inside this page. Navigation with `` will always navigate in the `Frame` created by the bootstrap.
+
+Limitations:
+- You cannot change the root view of the app (to `RadSideDrawer` for example). It is always the `Frame` created by the bootstrap.
+- You can have only one `` as there is only one `Frame`.
+- You always have a `Page` view wrapping your components. Because the `ActionBar` is part of that `Page` you can always change it with the `` component.
+
+NEW BEHAVIOR
+
+Bootstrap will **not** create root view by default. It will use the root view of your main application component as the root view of the application. The `` component will create its own `Frame` and will use it for navigation. It will also wrap the components you navigate to in a `Page` and will navigate to it as it did before.
+
+Which means:
+
+- You can use any view for application root. Finally, you can have application-wide `RadSideDrawer`.
+
+- You have more flexibility over where to place the ``, you can even have more than one for more advanced scenarios.
+
+- If you **don't use ``** in your app you will not get the default `Page` and `Frame`, which means you will not be able to inject them in you components or show the `ActionBar`. There is special `createFrameOnBootstrap` option you can pass on bootstrap to make things as _before_:
+```
+platformNativeScript({ createFrameOnBootstrap: true })
+ .bootstrapModuleFactory(AppModuleNgFactory);
+```
+
+- If you **are using ``** you probably don't have to do any changes. Bootstrap will not create `Frame` and `Page`, but the outlet will do that. It will also take care of providing `Page` and so the `ActionBar` should work as _before_.
+
+
+WORKING WITH FRAMES
+
+There might be multiple frames (if you have multiple ``'s). Angular DI works with singletons, so it will always return one instance of `Frame`. We have introduced `FrameService` (still experimental) which has a `getFrame()` method. It will return the current frame (the one you have navigated last).
+
+#### Signature of `onAfterLivesync` changed
+
+The signature `onAfterLivesync` observable changed from:
+```
+export const onAfterLivesync = new EventEmitter>();
+```
+to:
+```
+export const onAfterLivesync = new EventEmitter<{ moduleRef?: NgModuleRef; error?: Error }>();
+```
+
+
# [5.2.0](https://github.com/NativeScript/nativescript-angular/compare/5.0.0...5.2.0) (2018-01-17)
@@ -124,7 +793,7 @@ Angular apps described in [this](https://angular.io/guide/ngmodule-faq#q-browser
Migration steps:
In all NgModules, except the root one (`AppModule`), replace:
```
-import { NativeScriptModule } from "nativescript-angular/nativescript.module";
+import { NativeScriptModule } from "@nativescript/angular";
…
@NgModule({
imports: [
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9e66edd30..80bff2ab2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -11,6 +11,7 @@ Here are some guides on how to do that:
- [Requesting Features](#requesting-features)
- [Submitting a PR](#submitting-a-pr)
- [Where to Start](#where-to-start)
+- [Publishing new versions](#publishing-new-versions)
@@ -76,3 +77,110 @@ It's our turn from there on! We will review the PR and discuss changes you might
## Where to Start
If you want to contribute, but you are not sure where to start - look for [issues labeled `help wanted`](https://github.com/NativeScript/nativescript-angular/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22).
+
+
+## Publishing new versions
+
+
+## Releasing new versions
+Instructions how to release a new version for **NativeScript Core Team Members**.
+
+
+
+1. Checkout release branch
+```
+cd nativescript-angular/nativescript-angular && git checkout release && git pull
+```
+#### If we prepare major or minor release, merge master in release branch else **skip this step**.
+```
+git merge --ff-only origin/master
+```
+*** Note: If there are commits in release branch which are not merged in master branch '-ff-merge' command will fail.
+In this case the commits should be merge firstly from release in master branch as explained in section 'Merge changes from release into master' and then repeat step 1.
+
+2. Execute `npm i` to install dependencies:
+```
+cd nativescript-angular && npm i
+cd nativescript-angular-package && npm i
+```
+3. Execute [`npm version`](https://docs.npmjs.com/cli/version) to bump the version in both `nativescript-angular` and `nativescript-angular-package` folders:
+```
+npm --no-git-tag-version version [patch|minor|major] -m "release: cut the %s release"
+```
+or
+```
+npm --no-git-tag-version version [version] --allow-same-version -m "release: cut the %s release"
+```
+NOTE: Check the changelog!!!
+
+4. Create release-branch with change log
+```
+git checkout -b release-[version]
+```
+5. Add changes
+```
+git add changed-files
+git commit -m "release: cut the %s release"
+git push
+```
+NOTE: Make sure the PR is based on release branch
+
+6. Merge PR into release branch.
+
+7. The merge will produce package with rc tag in npm. If all checks have passed, publish official package. Usually the night builds will be triggered and the package will be ready to be released on the next day.
+
+8. Don't forget to tag the release branch
+```
+git tag [version]
+git push --tags
+```
+Only if needed to Tips to remove tags:
+```
+git push --delete origin [version]
+git tag -d [version]
+```
+
+## Checkout master branch and bump version usually should be minor or major.
+
+## Merge changes from release into master
+
+## NOTE: Don't use git IDE/WEB
+
+
+
+### Here are steps described in the diagram above.
+
+1. Make sure you are in release branch:
+```
+git checkout release && git pull
+```
+2. Create PR to merge changes back in master and preserve history:
+```
+git checkout -b merge-release-in-master-[branch]/[sha]
+git push --set-upstream origin merge-release-in-master-branch-[branch]/[sha]
+git merge origin/master
+```
+3. Resolve conflicts. Choose to keep the version of master branch. If it is needed to revert versions of modules, see at the bottom.
+
+4. Add conflicts:
+```
+git add resolved files
+```
+5. Commit changes with default merge message:
+```
+git commit
+git push
+```
+
+6. Create pull request which should be based on master. Replace replace env merge-release-in-master-branch with its value
+```
+curl -d '{"title": "chore: merge release in master","body": "chore: merge release in master","head": "merge-release-in-master","base": "master"}' -X POST https://api.github.com/repos/NativeScript/NativeScript/pulls -H "Authorization: token ${GIT_TOKEN}"
+```
+
+**If needed, to revert file and take it from master:**
+```
+git checkout origin/master nativescript-angular/[some-file]
+git commit --amend
+git push --force-with-lease
+```
+This could require to repeat steps from 1 to 4, since we need to keep the branches with the same history
diff --git a/DevelopmentWorkflow.md b/DevelopmentWorkflow.md
index d0e11860b..5df7f425b 100644
--- a/DevelopmentWorkflow.md
+++ b/DevelopmentWorkflow.md
@@ -2,13 +2,15 @@
-- [Running locally](#running-locally)
+- [Development Workflow](#development-workflow)
+ - [Running locally](#running-locally)
- [Prerequisites](#prerequisites)
- - [Install dependencies](#install-dependencies)
- - [Run the sample application (ng-sample)](#run-the-sample-application-ng-sample)
-- [Running the tests](#running-the-tests)
-- [Testing locally by running e2e tests](#testing-locally-by-running-e2e-tests)
-- [Developer workflow](#developer-workflow)
+ - [Clone repository](#clone-repository)
+ - [Install dependencies of the compatibility package (nativescript-angular)](#install-dependencies-of-the-compatibility-package-nativescript-angular)
+ - [Install dependencies of the scoped package (@nativescript/angular)](#install-dependencies-of-the-scoped-package-nativescriptangular)
+ - [Run some of the e2e applications e.g. router-tab-view](#run-some-of-the-e2e-applications-eg-router-tab-view)
+ - [Running the tests](#running-the-tests)
+ - [Testing locally by running e2e tests](#testing-locally-by-running-e2e-tests)
@@ -18,21 +20,34 @@
Install your native toolchain and NativeScript as described in the docs:
-https://docs.nativescript.org/setup/quick-setup
+https://docs.nativescript.org/angular/start/quick-setup
+### Clone repository
-### Install dependencies
+```
+$ git clone git@github.com:NativeScript/nativescript-angular.git
+$ cd nativescript-angular
+```
+
+### Install dependencies of the compatibility package (nativescript-angular)
```
$ cd nativescript-angular
$ npm install
```
-### Run the sample application (ng-sample)
+### Install dependencies of the scoped package (@nativescript/angular)
+
+```
+$ cd nativescript-angular-package
+$ npm install
+```
+
+### Run some of the e2e applications e.g. router-tab-view
Install NPM packages (use the local copy of `nativescript-angular`):
```
-$ cd ng-sample
+$ cd e2e/router-tab-view
$ npm install
```
@@ -43,6 +58,8 @@ $ tns run android
$ tns run ios
```
+Make changes to `nativescript-angular` (in `./nativescript-angular-package` folder) or `@nativescript/angular` (in `./nativescript-angular` folder) and see them applied in the running app.
+
## Running the tests
Install the NPM dependencies (use the local copy of `nativescript-angular`):
@@ -89,8 +106,20 @@ NOTE: The steps below describe how to run `renderer` tests, but the same approac
``` bash
npm run e2e -- --runType capabilityName
```
+
+## Building Packages
-## Developer workflow
+1. Build `@nativescript/angular` (a.k.a. "scoped package"):
+ ```
+ cd nativescript-angular
+ npm install
+ npm run pack
+ ```
-1. Make changes to the `test`, `ng-sample` projects or in `nativescript-angular` folder.
-2. Run the `tests` or `ng-sample` as shown above.
+2. Build `nativescript-angular` (a.k.a. "compat-package"):
+ ```
+ cd nativescript-angular-package
+ npm install
+ npm run pack-with-scoped-version -- ../dist/nativescript-angular-scoped.tgz
+ ```
+Packages are available in the `dist` folder.
diff --git a/LICENSE b/LICENSE
index ced13b45c..e519ff171 100755
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Apache License
+ Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@@ -186,7 +186,7 @@ Apache License
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright (c) 2015-2018 Telerik AD
+ Copyright (c) 2020 nStudio, LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -198,4 +198,4 @@ Apache License
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
- limitations under the License.
\ No newline at end of file
+ limitations under the License.
diff --git a/README.md b/README.md
index 69ccc7a75..8819a1595 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,7 @@
+## Note on repo
+
+This repo is for Angular <= 11. If looking for 12+ (including 18+, etc) it's maintained here [NativeScript/angular](https://github.com/NativeScript/angular).
+
# NativeScript Angular
[](https://travis-ci.org/NativeScript/nativescript-angular)
@@ -8,11 +12,12 @@ This repository contains the code for integration of NativeScript with Angular.
-- [Watch the video explaining Angular and NativeScript](#watch-the-video-explaining-angular-and-nativescript)
-- [Explore the examples](#explore-the-examples)
-- [Contribute](#contribute)
-- [Known issues](#known-issues)
-- [Get Help](#get-help)
+- [NativeScript Angular](#nativescript-angular)
+ - [Watch the video explaining Angular and NativeScript](#watch-the-video-explaining-angular-and-nativescript)
+ - [Explore the examples](#explore-the-examples)
+ - [Contribute](#contribute)
+ - [Known issues](#known-issues)
+ - [Get Help](#get-help)
@@ -22,10 +27,13 @@ This repository contains the code for integration of NativeScript with Angular.
## Explore the examples
-The `ng-sample` app is meant for testing stuff while developing the renderer code, and isn't the best example out there. You can take a look at these sample apps that use the published builds from npm:
+The `e2e` apps are meant for testing stuff. You can take a look at these additional sample apps that use the published builds from npm:
-* [Hello world starter](https://github.com/NativeScript/template-hello-world-ng)
-* [TodoMVC sample implementation](https://github.com/NativeScript/sample-ng-todomvc)
+* [Hello world starter](https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-hello-world-ng)
+* [Master-detail template](https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-master-detail-ng)
+* [Drawer navigation template](https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-drawer-navigation-ng)
+* [TabView navigation template](https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-tab-navigation-ng)
+* [NativeScript Angular SDK examples](https://github.com/NativeScript/nativescript-sdk-examples-ng)
## Contribute
We love PRs! Check out the [contributing guidelines](CONTRIBUTING.md) and [development workflow for local setup](DevelopmentWorkflow.md). If you want to contribute, but you are not sure where to start - look for [issues labeled `help wanted`](https://github.com/NativeScript/nativescript-angular/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22).
@@ -36,6 +44,6 @@ We love PRs! Check out the [contributing guidelines](CONTRIBUTING.md) and [devel
* Self-closing elements (``) get parsed wrong (in this case Button gets parsed as a Label child.
## Get Help
-Please, use [github issues](https://github.com/NativeScript/nativescript-angular/issues) strictly for [reporting bugs](CONTRIBUTING.md#reporting-bugs) or [requesting features](CONTRIBUTING.md#requesting-new-features). For general questions and support, check out the [NativeScript community forum](https://discourse.nativescript.org/) or ask our experts in [NativeScript community Slack channel](http://developer.telerik.com/wp-login.php?action=slack-invitation).
+Please, use [github issues](https://github.com/NativeScript/nativescript-angular/issues) strictly for [reporting bugs](CONTRIBUTING.md#reporting-bugs) or [requesting features](CONTRIBUTING.md#requesting-new-features). For general questions and support, check out [Stack Overflow](https://stackoverflow.com/questions/tagged/nativescript) or ask our experts in [NativeScript community Slack channel](http://developer.telerik.com/wp-login.php?action=slack-invitation).

diff --git a/build-docs.sh b/build-docs.sh
new file mode 100755
index 000000000..a0f39ec85
--- /dev/null
+++ b/build-docs.sh
@@ -0,0 +1,9 @@
+set -e
+
+ENV="${ENV:-dev}"
+DIST_DIR="nativescript-angular/bin/dist"
+APIREF_DIR="$DIST_DIR/ng-api-reference"
+rm -rf "$APIREF_DIR"
+cd "nativescript-angular"
+npm install
+npm run typedoc
diff --git a/build/pack-scripts/pack-compat.ts b/build/pack-scripts/pack-compat.ts
new file mode 100644
index 000000000..1df263165
--- /dev/null
+++ b/build/pack-scripts/pack-compat.ts
@@ -0,0 +1,73 @@
+import * as path from "path";
+import * as fs from "fs-extra";
+import { execSync } from "child_process";
+
+/**
+ * Use this script to pack .tgz for nativescript-angular package. The first passed param can be:
+ * 1. Path to .tgz file - in this case the script replaces the scoped dependency (@nativescript/angular) with it in the package.json file. Then packs.
+ * 2. Tag or exact version - in this case the script does `npm install --save-exact` to save the exact version (in case if tag). Then packs.
+ * 3. `auto-version` - this is interpreted by getting version from the scoped package.json file (nativescript-angular folder).
+ */
+
+var scopedVersion = process.argv[2];
+
+console.log(`Packing nativescript-angular package with @nativescript/angular: ${scopedVersion}`);
+
+const distFolderPath = path.resolve("../../dist");
+const tempFolderPath = path.resolve("./temp-compat");
+const outFileName = "nativescript-angular-compat.tgz";
+
+const nsAngularScopedPackageJSONPath = path.resolve("../../nativescript-angular/package.json");
+const nsAngularPackagePath = path.resolve("../../nativescript-angular-package");
+const packageJsonPath = path.resolve(`${nsAngularPackagePath}/package.json`);
+console.log("Getting package.json from", packageJsonPath);
+
+
+function prepareCompatPackageJSON(scopedVersion: string) {
+ const packageJsonObject = JSON.parse(fs.readFileSync(packageJsonPath, { encoding: "utf8" }));
+ packageJsonObject.dependencies["@nativescript/angular"] = scopedVersion;
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJsonObject, null, 4));
+}
+
+if (scopedVersion === "auto-version") {
+ // We use this when build for release. In this case we need to get version from scoped package (nativescript-angular)
+ // and use it in the compat package.
+
+ scopedVersion = JSON.parse(fs.readFileSync(nsAngularScopedPackageJSONPath, { encoding: "utf8" })).version;
+ prepareCompatPackageJSON(scopedVersion)
+} else {
+ let npmInstallParams = "";
+
+ if (scopedVersion.indexOf(".tgz") > 0) {
+ // If building with .tgz, we need to update the package.json of the compat package
+ prepareCompatPackageJSON(scopedVersion)
+ } else {
+ // If building with tag or exact version, just install it with --save-exact
+ npmInstallParams = `@nativescript/angular@${scopedVersion}`;
+ }
+
+ execSync(`npm install --save-exact ${npmInstallParams}`, {
+ cwd: nsAngularPackagePath
+ });
+}
+
+// ensure empty temp and existing dist folders
+fs.emptyDirSync(tempFolderPath);
+fs.ensureDirSync(distFolderPath);
+
+// Install, run tsc and run ngc
+execSync(`npm i && npm run tsc && npm run ngc`, {
+ cwd: nsAngularPackagePath
+});
+
+// create .tgz in temp folder
+execSync(`npm pack ${nsAngularPackagePath}`, {
+ cwd: tempFolderPath
+});
+
+// assume we have a single file built in temp folder, take its name
+const currentFileName = fs.readdirSync(tempFolderPath)[0];
+
+// move built file and remove temp folder
+fs.moveSync(`${tempFolderPath}/${currentFileName}`, `${distFolderPath}/${outFileName}`, { overwrite: true });
+fs.removeSync(`${tempFolderPath}`);
diff --git a/build/pack-scripts/pack-scoped.ts b/build/pack-scripts/pack-scoped.ts
new file mode 100644
index 000000000..8d5605b84
--- /dev/null
+++ b/build/pack-scripts/pack-scoped.ts
@@ -0,0 +1,33 @@
+import * as path from "path";
+import * as fs from "fs-extra";
+import { execSync } from "child_process";
+
+console.log(`Packing @nativescript/angular package`);
+
+const distFolderPath = path.resolve("../../dist");
+const outFileName = "nativescript-angular-scoped.tgz";
+
+const nsAngularPackagePath = path.resolve("../../nativescript-angular");
+const nsAngularPackageDistPath = path.resolve(nsAngularPackagePath + "/dist");
+
+function getFilesFromPath(path, extension) {
+ let files = fs.readdirSync( path );
+ return files.filter(file => file.match(new RegExp(`.*\.(${extension})`, 'ig')));
+}
+
+// execSync(`npm install --save-exact`, {
+// cwd: nsAngularPackagePath
+// });
+
+// ensure empty temp and dist folders
+fs.ensureDirSync(distFolderPath);
+
+// create .tgz in temp folder
+execSync(`cd ${nsAngularPackagePath} && npm run build.pack`);
+
+// assume we have a single file built in temp folder, take its name
+const currentFileName = getFilesFromPath(nsAngularPackageDistPath, ".tgz")[0];
+console.log('currentFileName:', currentFileName);
+
+// move built file and remove temp folder
+fs.moveSync(`${nsAngularPackageDistPath}/${currentFileName}`, `${distFolderPath}/${outFileName}`, { overwrite: true });
diff --git a/build/pack-scripts/package.json b/build/pack-scripts/package.json
new file mode 100644
index 000000000..3cdfbcd0e
--- /dev/null
+++ b/build/pack-scripts/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "build",
+ "version": "1.0.0",
+ "description": "",
+ "main": "prepublish-next.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "@types/node": "^12.7.12",
+ "fs-extra": "^9.0.0",
+ "rimraf": "^3.0.0",
+ "ts-node": "^8.10.2",
+ "typescript": "^3.8.3"
+ }
+}
diff --git a/build/pack-scripts/tsconfig.json b/build/pack-scripts/tsconfig.json
new file mode 100644
index 000000000..0bda36752
--- /dev/null
+++ b/build/pack-scripts/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es2015",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "noEmitHelpers": true,
+ "noEmitOnError": true,
+ "skipLibCheck": true,
+ "lib": [
+ "es2017",
+ "dom",
+ "es6"
+ ],
+ "types": [
+ "node"
+ ],
+ "typeRoots": [ "./node_modules/@types" ]
+ },
+ "include": [
+ "./**/*.ts"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}
\ No newline at end of file
diff --git a/doc/upgrading-zonejs.md b/doc/upgrading-zonejs.md
index d99dc2083..6ea5d173f 100644
--- a/doc/upgrading-zonejs.md
+++ b/doc/upgrading-zonejs.md
@@ -2,7 +2,7 @@
`nativescript-angular` uses a fork of the `zone.js` package in order to work around incompatibilities between node, browser, and mobile implementations.
-The fork resides at https://github.com/NativeScript/zone.js in the `zone-nativescript` branch. It adds a separate `lib/nativescript/nativescript.ts` entry point that is used to generate a new bundle: `dist/zone-nativescript.js`.
+The fork resides at https://github.com/NativeScript/zone.js in the `zone-nativescript` branch. It adds a separate `lib/nativescript/nativescript.ts` entry point that is used to generate a new bundle: `zone-nativescript.js`.
To upgrade to a newer release of `zone.js`:
@@ -11,5 +11,5 @@ To upgrade to a newer release of `zone.js`:
3. Rebuild: `gulp build`
4. Run the node-based smoke tests: `gulp test/nativescript`
5. Run the browser tests: `node_modules/.bin/karma start karma.conf.js --single-run` (You need to run node `test/ws-server.js` in a separate console first)
-6. Commit `dist/zone-nativescript.js`, drop the previous `dist/zone-nativescript.js` commit from the branch. Force push the new `zone-nativescript` branch to GitHub.
+6. Commit `zone-nativescript.js`, drop the previous `zone-nativescript.js` commit from the branch. Force push the new `zone-nativescript` branch to GitHub.
7. Update your copy of `nativescript-angular/zone.js/dist/zone-nativescript.js` with the bundle you just built.
diff --git a/e2e/README.md b/e2e/README.md
new file mode 100644
index 000000000..32bd92ad0
--- /dev/null
+++ b/e2e/README.md
@@ -0,0 +1,50 @@
+# NativeScript Angular E2E Tests
+
+This folder contains a number of projects containing e2e tests for NativeScript Angular integration.
+
+## Projects Description
+
+ - [`renderer`](renderer) - Tests for the nativescript-angular renderer features. Creating/adding/removing elements for the nativescript visual tree.
+
+ - [`router`](router) - Tests for angular router and navigation. This app contains a mixed scenario with `` and ``.
+
+ - [`single-page`](single-page) - Tests for angular router and navigation. This is a simple app with only one level navigation using ``.
+
+ - [`modal-navigation-ng`](modal-navigation-ng) - Tests for different scenarios of showing and navigation in modal dialogs.
+
+ - [`router-tab-view`](router-tab-view) - Tests for navigation in a TabComponent containing named(aux) `` instances.
+
+## Global Appium Setup
+
+Install external dependencies for nativescript-dev-appium described [here](https://github.com/NativeScript/nativescript-dev-appium#setup).
+
+
+All projects use the same [appium capabilities files](config/appium.capabilities.json).
+
+
+## Running Tests
+
+Make sure you build or run the project with the NativeScript CLI first:
+```
+tns build android
+tns run ios
+```
+
+Run appium tests with the `e2e` npm task. Check ns-dev-appium options [here](https://github.com/NativeScript/nativescript-dev-appium#options)
+
+Examples:
+
+```
+npm run e2e -- --runType android23
+
+npm run e2e -- --runType sim.iPhone8.iOS112
+```
+
+For **development** you can use `--devMode` flag to reuse the current emulator and the currently installed application:
+
+Examples:
+```
+npm run e2e -- --runType android23 --devMode
+
+npm run e2e -- --runType sim.iPhone8.iOS112 --devMode
+```
diff --git a/e2e/animation-examples/.gitignore b/e2e/animation-examples/.gitignore
new file mode 100644
index 000000000..2e6e00a8f
--- /dev/null
+++ b/e2e/animation-examples/.gitignore
@@ -0,0 +1,21 @@
+node_modules
+platforms
+hooks
+.vscode
+
+app/**/*.js
+app/**/*.map
+
+e2e/**/*.js
+e2e/**/*.map
+
+# Webpack files
+tsconfig.esm.json
+webpack.config.js
+
+#tests
+instruments*
+e2e/reports
+test-results.xml
+.DS_Store
+mochawesome-report
\ No newline at end of file
diff --git a/e2e/animation-examples/CODE_OF_CONDUCT.md b/e2e/animation-examples/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..1c845d0b1
--- /dev/null
+++ b/e2e/animation-examples/CODE_OF_CONDUCT.md
@@ -0,0 +1,83 @@
+# NativeScript Community Code of Conduct
+
+Our community members come from all walks of life and are all at different stages of their personal and professional journeys. To support everyone, we've prepared a short code of conduct. Our mission is best served in an environment that is friendly, safe, and accepting; free from intimidation or harassment.
+
+Towards this end, certain behaviors and practices will not be tolerated.
+
+## tl;dr
+
+- Be respectful.
+- We're here to help.
+- Abusive behavior is never tolerated.
+- Violations of this code may result in swift and permanent expulsion from the NativeScript community channels.
+
+## Administrators
+
+- Dan Wilson (@DanWilson on Slack)
+- Jen Looper (@jen.looper on Slack)
+- TJ VanToll (@tjvantoll on Slack)
+
+## Scope
+
+We expect all members of the NativeScript community, including administrators, users, facilitators, and vendors to abide by this Code of Conduct at all times in our community venues, online and in person, and in one-on-one communications pertaining to NativeScript affairs.
+
+This policy covers the usage of the NativeScript Slack community, as well as the NativeScript support forums, NativeScript GitHub repositories, the NativeScript website, and any NativeScript-related events. This Code of Conduct is in addition to, and does not in any way nullify or invalidate, any other terms or conditions related to use of NativeScript.
+
+The definitions of various subjective terms such as "discriminatory", "hateful", or "confusing" will be decided at the sole discretion of the NativeScript administrators.
+
+## Friendly, Harassment-Free Space
+
+We are committed to providing a friendly, safe, and welcoming environment for all, regardless of gender identity, sexual orientation, disability, ethnicity, religion, age, physical appearance, body size, race, or similar personal characteristics.
+
+We ask that you please respect that people have differences of opinion regarding technical choices, and acknowledge that every design or implementation choice carries a trade-off and numerous costs. There is seldom a single right answer. A difference of technology preferences is never a license to be rude.
+
+Any spamming, trolling, flaming, baiting, or other attention-stealing behaviour is not welcome, and will not be tolerated.
+
+Harassing other users of NativeScript is never tolerated, whether via public or private media.
+
+Avoid using offensive or harassing package names, nicknames, or other identifiers that might detract from a friendly, safe, and welcoming environment for all.
+
+Harassment includes, but is not limited to: harmful or prejudicial verbal or written comments related to gender identity, sexual orientation, disability, ethnicity, religion, age, physical appearance, body size, race, or similar personal characteristics; inappropriate use of nudity, sexual images, and/or sexually explicit language in public spaces; threats of physical or non-physical harm; deliberate intimidation, stalking or following; harassing photography or recording; sustained disruption of talks or other events; inappropriate physical contact; and unwelcome sexual attention.
+
+## Acceptable Content
+
+The NativeScript administrators reserve the right to make judgement calls about what is and isn't appropriate in published content. These are guidelines to help you be successful in our community.
+
+Content must contain something applicable to the previously stated goals of the NativeScript community. "Spamming", that is, publishing any form of content that is not applicable, is not allowed.
+
+Content must not contain illegal or infringing content. You should only publish content to NativeScript properties if you have the right to do so. This includes complying with all software license agreements or other intellectual property restrictions. For example, redistributing an MIT-licensed module with the copyright notice removed, would not be allowed. You will be responsible for any violation of laws or others’ intellectual property rights.
+
+Content must not be malware. For example, content (code, video, pictures, words, etc.) which is designed to maliciously exploit or damage computer systems, is not allowed.
+
+Content name, description, and other visible metadata must not include abusive, inappropriate, or harassing content.
+
+## Reporting Violations of this Code of Conduct
+
+If you believe someone is harassing you or has otherwise violated this Code of Conduct, please contact the administrators and send us an abuse report. If this is the initial report of a problem, please include as much detail as possible. It is easiest for us to address issues when we have more context.
+
+## Consequences
+
+All content published to the NativeScript community channels is hosted at the sole discretion of the NativeScript administrators.
+
+Unacceptable behavior from any community member, including sponsors, employees, customers, or others with decision-making authority, will not be tolerated.
+
+Anyone asked to stop unacceptable behavior is expected to comply immediately.
+
+If a community member engages in unacceptable behavior, the NativeScript administrators may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event or service).
+
+## Addressing Grievances
+
+If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify the administrators. We will do our best to ensure that your grievance is handled appropriately.
+
+In general, we will choose the course of action that we judge as being most in the interest of fostering a safe and friendly community.
+
+## Contact Info
+Please contact Dan Wilson @DanWilson if you need to report a problem or address a grievance related to an abuse report.
+
+You are also encouraged to contact us if you are curious about something that might be "on the line" between appropriate and inappropriate content. We are happy to provide guidance to help you be a successful part of our community.
+
+## Credit and License
+
+This Code of Conduct borrows heavily from the WADE Code of Conduct, which is derived from the NodeBots Code of Conduct, which in turn borrows from the npm Code of Conduct, which was derived from the Stumptown Syndicate Citizen's Code of Conduct, and the Rust Project Code of Conduct.
+
+This document may be reused under a Creative Commons Attribution-ShareAlike License.
\ No newline at end of file
diff --git a/e2e/animation-examples/LICENSE b/e2e/animation-examples/LICENSE
new file mode 100755
index 000000000..061c44028
--- /dev/null
+++ b/e2e/animation-examples/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright (c) 2015-2019 Progress Software Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/e2e/animation-examples/README.md b/e2e/animation-examples/README.md
new file mode 100644
index 000000000..f1e68bd59
--- /dev/null
+++ b/e2e/animation-examples/README.md
@@ -0,0 +1,3 @@
+# NativeScript Angular Animations Examples
+
+A NativeScript Angular applications showcasing the [New Wave of Animation Features in Angular](https://www.yearofmoo.com/2017/06/new-wave-of-animation-features.html).
\ No newline at end of file
diff --git a/e2e/animation-examples/app/App_Resources/Android/app.gradle b/e2e/animation-examples/app/App_Resources/Android/app.gradle
new file mode 100644
index 000000000..0ad328b9a
--- /dev/null
+++ b/e2e/animation-examples/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.ng4animations"
+
+ //override supported platforms
+ // ndk {
+ // abiFilters.clear()
+ // abiFilters "armeabi-v7a"
+ // }
+
+ }
+ aaptOptions {
+ additionalParameters "--no-version-vectors"
+ }
+}
diff --git a/e2e/animation-examples/app/App_Resources/Android/src/main/AndroidManifest.xml b/e2e/animation-examples/app/App_Resources/Android/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1673c6800
--- /dev/null
+++ b/e2e/animation-examples/app/App_Resources/Android/src/main/AndroidManifest.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-hdpi/background.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-hdpi/background.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-hdpi/icon.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-hdpi/icon.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-hdpi/logo.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-hdpi/logo.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-ldpi/background.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-ldpi/background.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-ldpi/icon.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-ldpi/icon.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-ldpi/logo.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-ldpi/logo.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-mdpi/background.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-mdpi/background.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-mdpi/icon.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-mdpi/icon.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-mdpi/logo.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-mdpi/logo.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-nodpi/splash_screen.xml b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-nodpi/splash_screen.xml
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xhdpi/background.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-xhdpi/background.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xhdpi/icon.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-xhdpi/icon.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xhdpi/logo.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-xhdpi/logo.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/background.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/background.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/icon.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/icon.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/logo.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-xxhdpi/logo.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/background.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/background.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/icon.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/icon.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png
diff --git a/e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/logo.png b/e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/drawable-xxxhdpi/logo.png
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png
diff --git a/e2e/renderer/app/App_Resources/Android/values-v21/colors.xml b/e2e/animation-examples/app/App_Resources/Android/src/main/res/values-v21/colors.xml
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/values-v21/colors.xml
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/values-v21/colors.xml
diff --git a/e2e/renderer/app/App_Resources/Android/values-v21/styles.xml b/e2e/animation-examples/app/App_Resources/Android/src/main/res/values-v21/styles.xml
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/values-v21/styles.xml
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/values-v21/styles.xml
diff --git a/e2e/renderer/app/App_Resources/Android/values/colors.xml b/e2e/animation-examples/app/App_Resources/Android/src/main/res/values/colors.xml
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/values/colors.xml
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/values/colors.xml
diff --git a/e2e/renderer/app/App_Resources/Android/values/styles.xml b/e2e/animation-examples/app/App_Resources/Android/src/main/res/values/styles.xml
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/values/styles.xml
rename to e2e/animation-examples/app/App_Resources/Android/src/main/res/values/styles.xml
diff --git a/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..1953734f4
--- /dev/null
+++ b/e2e/animation-examples/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/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/Contents.json b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/Contents.json
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/Contents.json
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/Contents.json
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png b/e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png
rename to e2e/animation-examples/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png
diff --git a/ng-sample/app/App_Resources/iOS/Info.plist b/e2e/animation-examples/app/App_Resources/iOS/Info.plist
similarity index 90%
rename from ng-sample/app/App_Resources/iOS/Info.plist
rename to e2e/animation-examples/app/App_Resources/iOS/Info.plist
index fe97598cf..ea3e3ea23 100644
--- a/ng-sample/app/App_Resources/iOS/Info.plist
+++ b/e2e/animation-examples/app/App_Resources/iOS/Info.plist
@@ -43,11 +43,5 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- NSAppTransportSecurity
-
-
- NSAllowsArbitraryLoads
-
-
diff --git a/ng-sample/app/App_Resources/iOS/LaunchScreen.storyboard b/e2e/animation-examples/app/App_Resources/iOS/LaunchScreen.storyboard
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/LaunchScreen.storyboard
rename to e2e/animation-examples/app/App_Resources/iOS/LaunchScreen.storyboard
diff --git a/e2e/animation-examples/app/App_Resources/iOS/build.xcconfig b/e2e/animation-examples/app/App_Resources/iOS/build.xcconfig
new file mode 100644
index 000000000..e77e78db9
--- /dev/null
+++ b/e2e/animation-examples/app/App_Resources/iOS/build.xcconfig
@@ -0,0 +1,6 @@
+// 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;
diff --git a/e2e/animation-examples/app/animate-child.component.ts b/e2e/animation-examples/app/animate-child.component.ts
new file mode 100644
index 000000000..2af68882d
--- /dev/null
+++ b/e2e/animation-examples/app/animate-child.component.ts
@@ -0,0 +1,46 @@
+import {
+ animate,
+ animateChild,
+ query,
+ style,
+ transition,
+ trigger,
+} from "@angular/animations";
+import { Component } from "@angular/core";
+
+@Component({
+ template: `
+
+ >
+
+ `,
+ animations: [
+ trigger("parent", [
+ transition("* => *", [
+ style({ transform: "translate(200px)" }),
+ animate(1000, style({ transform: "translate(0px)" })),
+ query("@child", animateChild())
+ ])
+ ]),
+ trigger("child", [
+ transition("* => *", [
+ style({ opacity: 0 }),
+ animate(400, style({ opacity: 1 }))
+ ])
+ ])
+ ],
+ styles: [
+ `.parent {
+ background-color: red;
+ }`,
+ `.child {
+ height: 50%;
+ width: 50%;
+ background-color: green;
+ }`,
+ ],
+})
+export class AnimateChildComponent {
+ public parentVal;
+ public childVal;
+}
diff --git a/e2e/animation-examples/app/animation-builder.component.scss b/e2e/animation-examples/app/animation-builder.component.scss
new file mode 100644
index 000000000..ca3407610
--- /dev/null
+++ b/e2e/animation-examples/app/animation-builder.component.scss
@@ -0,0 +1,3 @@
+.btn-primary {
+ background-color: pink;
+}
\ No newline at end of file
diff --git a/e2e/animation-examples/app/animation-builder.component.ts b/e2e/animation-examples/app/animation-builder.component.ts
new file mode 100644
index 000000000..3713242c8
--- /dev/null
+++ b/e2e/animation-examples/app/animation-builder.component.ts
@@ -0,0 +1,25 @@
+import { AnimationBuilder, style, animate } from '@angular/animations';
+import { Component, ViewChild } from '@angular/core';
+
+@Component({
+ template: `
+
+ `,
+ styleUrls: ['./animation-builder.component.scss']
+})
+export class AnimationBuilderComponent {
+ @ViewChild('button', { static: false }) button;
+
+ constructor(private _builder: AnimationBuilder) {}
+
+ makeAnimation() {
+ const myAnimation = this._builder.build([
+ style({ "opacity": 1 }),
+ animate(1000, style({ "opacity": 0 }))
+ ]);
+
+ const player = myAnimation.create(this.button.nativeElement);
+
+ player.play();
+ }
+}
diff --git a/e2e/animation-examples/app/animations-list.component.ts b/e2e/animation-examples/app/animations-list.component.ts
new file mode 100644
index 000000000..52da0deea
--- /dev/null
+++ b/e2e/animation-examples/app/animations-list.component.ts
@@ -0,0 +1,33 @@
+import { Component } from '@angular/core';
+
+class Link {
+ constructor(public title: string, public link: string, public id?: string) {
+ this.id = this.id || this.link.replace("/", "");
+ }
+}
+
+@Component({
+ template: `
+
+
+
+ `
+})
+export class AnimationsListComponent {
+ public links: Link[] = [
+ new Link("Animation builder", "/builder"),
+ new Link("External animation", "/external"),
+ new Link("Selector", "/selector"),
+ new Link("Query with stagger", "/query-stagger"),
+ new Link("Fade in/out animation", "/fade-in-out"),
+ new Link("Animation with options", "/options"),
+ new Link("Animation with default options", "/options-default"),
+ new Link("Animate child", "/animate-child"),
+ new Link("Angular docs animations", "/hero"),
+ ]
+}
diff --git a/e2e/animation-examples/app/animations.ts b/e2e/animation-examples/app/animations.ts
new file mode 100644
index 000000000..15531b7aa
--- /dev/null
+++ b/e2e/animation-examples/app/animations.ts
@@ -0,0 +1,9 @@
+import { animation, style, animate } from "@angular/animations";
+
+export const fadeAnimation = animation([
+ style({ opacity: "{{ from }}" }),
+ animate("{{ time }}", style({ opacity: "{{ to }}" }))
+], {
+ params: { time: "1s" }
+});
+
diff --git a/e2e/animation-examples/app/app.android.css b/e2e/animation-examples/app/app.android.css
new file mode 100644
index 000000000..3886f4ffe
--- /dev/null
+++ b/e2e/animation-examples/app/app.android.css
@@ -0,0 +1,10 @@
+Button {
+ font-size: 8px;
+ padding: 0px;
+ margin: 0px;
+}
+
+Label {
+ font-size: 8px;
+}
+
diff --git a/e2e/animation-examples/app/app.component.ts b/e2e/animation-examples/app/app.component.ts
new file mode 100644
index 000000000..74eb76f2f
--- /dev/null
+++ b/e2e/animation-examples/app/app.component.ts
@@ -0,0 +1,8 @@
+import { Component } from '@angular/core';
+
+@Component({
+ moduleId: module.id,
+ template: ``
+})
+export class AppComponent {
+}
\ No newline at end of file
diff --git a/e2e/animation-examples/app/app.ios.css b/e2e/animation-examples/app/app.ios.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/e2e/animation-examples/app/app.module.ngfactory.d.ts b/e2e/animation-examples/app/app.module.ngfactory.d.ts
new file mode 100644
index 000000000..9d15b9419
--- /dev/null
+++ b/e2e/animation-examples/app/app.module.ngfactory.d.ts
@@ -0,0 +1 @@
+export const AppModuleNgFactory: any;
\ No newline at end of file
diff --git a/e2e/animation-examples/app/app.module.ts b/e2e/animation-examples/app/app.module.ts
new file mode 100644
index 000000000..4a729f4b2
--- /dev/null
+++ b/e2e/animation-examples/app/app.module.ts
@@ -0,0 +1,64 @@
+import {
+ NgModule,
+ NO_ERRORS_SCHEMA,
+ NgModuleFactoryLoader,
+ APP_INITIALIZER
+} from "@angular/core";
+
+import { NativeScriptModule, NativeScriptAnimationsModule } from "@nativescript/angular";
+
+import { AppRoutingModule } from "./app.routing";
+import { AnimationsListComponent } from "./animations-list.component";
+import { AnimationBuilderComponent } from "./animation-builder.component";
+import { ExternalAnimationComponent } from "./external-animation.component";
+import { FadeInOutComponent } from "./fade-in-out.component";
+import { OptionsComponent } from "./options.component";
+import { OptionsDefaultComponent } from "./options-default.component";
+import { AnimateChildComponent } from "./animate-child.component";
+import { SelectorAllComponent } from "./selector-all.component";
+import { QueryStaggerComponent } from "./query-stagger.component";
+
+import { AppComponent } from "./app.component";
+
+export function asyncBoot(): Function {
+ return (): Promise => new Promise(resolve => {
+ setTimeout(() => {
+ resolve();
+ }, 5000);
+ })
+}
+
+@NgModule({
+ bootstrap: [
+ AppComponent,
+ ],
+ declarations: [
+ AppComponent,
+ AnimationsListComponent,
+ AnimationBuilderComponent,
+ ExternalAnimationComponent,
+ FadeInOutComponent,
+ OptionsComponent,
+ OptionsDefaultComponent,
+ AnimateChildComponent,
+ SelectorAllComponent,
+ QueryStaggerComponent,
+ ],
+ imports: [
+ NativeScriptModule,
+ NativeScriptAnimationsModule,
+ AppRoutingModule,
+ ],
+ /**
+ * Uncomment to test APP_INITIALIZER
+ */
+ // providers: [
+ // {
+ // provide: APP_INITIALIZER,
+ // useFactory: asyncBoot,
+ // multi: true
+ // },
+ // ],
+ schemas: [NO_ERRORS_SCHEMA],
+})
+export class AppModule {}
diff --git a/e2e/animation-examples/app/app.routing.ts b/e2e/animation-examples/app/app.routing.ts
new file mode 100644
index 000000000..305e244e9
--- /dev/null
+++ b/e2e/animation-examples/app/app.routing.ts
@@ -0,0 +1,33 @@
+import { NgModule } from "@angular/core";
+import { Routes } from "@angular/router";
+import { NativeScriptRouterModule } from "@nativescript/angular";
+
+import { AnimationsListComponent } from "./animations-list.component";
+import { AnimationBuilderComponent } from "./animation-builder.component";
+import { ExternalAnimationComponent } from "./external-animation.component";
+import { FadeInOutComponent } from "./fade-in-out.component";
+import { OptionsComponent } from "./options.component";
+import { OptionsDefaultComponent } from "./options-default.component";
+import { AnimateChildComponent } from "./animate-child.component";
+import { SelectorAllComponent } from "./selector-all.component";
+import { QueryStaggerComponent } from "./query-stagger.component";
+
+const routes: Routes = [
+ { path: "", pathMatch: "full", redirectTo: "list" },
+ { path: "list", component: AnimationsListComponent },
+ { path: "builder", component: AnimationBuilderComponent },
+ { path: "external", component: ExternalAnimationComponent },
+ { path: "fade-in-out", component: FadeInOutComponent },
+ { path: "options", component: OptionsComponent },
+ { path: "options-default", component: OptionsDefaultComponent },
+ { path: "animate-child", component: AnimateChildComponent },
+ { path: "selector", component: SelectorAllComponent },
+ { path: "query-stagger", component: QueryStaggerComponent },
+ { path: "hero", loadChildren: () => import("./hero/hero.module").then(m => m.HeroModule) },
+];
+
+@NgModule({
+ imports: [NativeScriptRouterModule.forRoot(routes)],
+ exports: [NativeScriptRouterModule],
+})
+export class AppRoutingModule { }
diff --git a/e2e/animation-examples/app/external-animation.component.ts b/e2e/animation-examples/app/external-animation.component.ts
new file mode 100644
index 000000000..80eb38fbb
--- /dev/null
+++ b/e2e/animation-examples/app/external-animation.component.ts
@@ -0,0 +1,58 @@
+import { useAnimation, trigger, transition, style, animate } from "@angular/animations";
+import { Component } from "@angular/core";
+
+import { fadeAnimation } from "./animations";
+
+@Component({
+ template: `
+
+
+
+
+
+ `,
+ animations: [
+ trigger("coolAnimation", [
+ transition("invisible => visible", [
+ useAnimation(fadeAnimation, {
+ params: {
+ from: 0,
+ to: 1,
+ time: "1s",
+ },
+ })
+ ]),
+
+ transition("visible => invisible", [
+ useAnimation(fadeAnimation, {
+ params: {
+ from: 1,
+ to: 0,
+ time: "1.0s",
+ },
+ })
+ ]),
+
+ ])
+ ],
+})
+export class ExternalAnimationComponent {
+ public isVisible = "visible";
+
+ toggle() {
+ this.isVisible = this.isVisible === "visible" ?
+ "invisible" :
+ "visible";
+ }
+}
+
diff --git a/e2e/animation-examples/app/fade-in-out.component.ts b/e2e/animation-examples/app/fade-in-out.component.ts
new file mode 100644
index 000000000..2d3ebe149
--- /dev/null
+++ b/e2e/animation-examples/app/fade-in-out.component.ts
@@ -0,0 +1,54 @@
+import { Component } from "@angular/core";
+import { trigger, transition, style, animate } from "@angular/animations";
+
+@Component({
+ template: `
+
+
+
+
+
+ `,
+ animations: [
+ trigger("someCoolAnimation", [
+ transition("* => fadeIn", [
+ style({ opacity: 0 }),
+ animate(600, style({ opacity: 1 }))
+ ]),
+ transition("* => fadeOut", [
+ animate(600, style({ opacity: 0 }))
+ ])
+ ])
+ ]
+})
+export class FadeInOutComponent {
+ bindingVar = "";
+
+ fadeIn() {
+ this.bindingVar = "fadeIn";
+ }
+
+ fadeOut() {
+ this.bindingVar = "fadeOut";
+ }
+
+ toggle() {
+ this.bindingVar == "fadeOut" ? this.fadeIn() : this.fadeOut();
+ }
+
+ hide() {
+ this.fadeOut();
+ }
+}
diff --git a/e2e/animation-examples/app/hero/hero-list-basic.component.ts b/e2e/animation-examples/app/hero/hero-list-basic.component.ts
new file mode 100644
index 000000000..260fa4008
--- /dev/null
+++ b/e2e/animation-examples/app/hero/hero-list-basic.component.ts
@@ -0,0 +1,56 @@
+import {
+ Component,
+ Input
+} from "@angular/core";
+import {
+ trigger,
+ state,
+ style,
+ animate,
+ transition
+} from "@angular/animations";
+
+import { Heroes } from "./hero.service";
+
+@Component({
+ moduleId: module.id,
+ selector: "hero-list-basic",
+ /* The click event calls hero.toggleState(), which
+ * causes the state of that hero to switch from
+ * active to inactive or vice versa.
+ */
+ template: `
+
+
+
+ `,
+ styleUrls: ["./hero-list.component.css"],
+ /**
+ * Define two states, "inactive" and "active", and the end
+ * styles that apply whenever the element is in those states.
+ * Then define animations for transitioning between the states,
+ * one in each direction
+ */
+ animations: [
+ trigger("heroState", [
+ state("inactive", style({
+ backgroundColor: "#eee",
+ transform: "scale(1)"
+ })),
+ state("active", style({
+ backgroundColor: "#cfd8dc",
+ transform: "scale(1.1)"
+ })),
+
+ transition("inactive => active", animate("100ms ease-in")),
+ transition("active => inactive", animate("100ms ease-out")),
+ ])
+ ]
+})
+export class HeroListBasicComponent {
+ @Input() heroes: Heroes;
+}
diff --git a/e2e/animation-examples/app/hero/hero-list-combined-transitions.component.ts b/e2e/animation-examples/app/hero/hero-list-combined-transitions.component.ts
new file mode 100644
index 000000000..bf588976a
--- /dev/null
+++ b/e2e/animation-examples/app/hero/hero-list-combined-transitions.component.ts
@@ -0,0 +1,51 @@
+import {
+ Component,
+ Input
+} from "@angular/core";
+import {
+ trigger,
+ state,
+ style,
+ animate,
+ transition
+} from "@angular/animations";
+
+import { Heroes } from "./hero.service";
+
+@Component({
+ moduleId: module.id,
+ selector: "hero-list-combined-transitions",
+ template: `
+
+
+
+ `,
+ styleUrls: ["./hero-list.component.css"],
+ /*
+ * Define two states, "inactive" and "active", and the end
+ * styles that apply whenever the element is in those states.
+ * Then define an animated transition between these two
+ * states, in *both* directions.
+ */
+ animations: [
+ trigger("heroState", [
+ state("inactive", style({
+ backgroundColor: "#eee",
+ transform: "scale(1)"
+ })),
+ state("active", style({
+ backgroundColor: "#cfd8dc",
+ transform: "scale(1.1)"
+ })),
+ transition("inactive => active, active => inactive",
+ animate("100ms ease-out"))
+ ])
+ ]
+})
+export class HeroListCombinedTransitionsComponent {
+ @Input() heroes: Heroes;
+}
diff --git a/e2e/animation-examples/app/hero/hero-list-enter-leave-states.component.ts b/e2e/animation-examples/app/hero/hero-list-enter-leave-states.component.ts
new file mode 100644
index 000000000..f8c63da1b
--- /dev/null
+++ b/e2e/animation-examples/app/hero/hero-list-enter-leave-states.component.ts
@@ -0,0 +1,60 @@
+import {
+ Component,
+ Input
+} from "@angular/core";
+import {
+ trigger,
+ state,
+ style,
+ animate,
+ transition
+} from "@angular/animations";
+
+import { Heroes } from "./hero.service";
+
+@Component({
+ moduleId: module.id,
+ selector: "hero-list-enter-leave-states",
+ template: `
+
+
+
+ `,
+ styleUrls: ["./hero-list.component.css"],
+ /* The elements here have two possible states based
+ * on the hero state, "active", or "inactive". We animate
+ * six transitions: Between the two states in both directions,
+ * and between each state and void. With this we can animate
+ * the enter and leave of elements differently based on which
+ * state they are in when they are added and removed.
+ */
+ animations: [
+ trigger("heroState", [
+ state("inactive", style({ transform: "translateX(0) scale(1)" })),
+ state("active", style({ transform: "translateX(0) scale(1.1)" })),
+ transition("inactive => active", animate("100ms ease-in")),
+ transition("active => inactive", animate("100ms ease-out")),
+ transition("void => inactive", [
+ style({ transform: "translateX(-100%) scale(1)" }),
+ animate(100)
+ ]),
+ transition("inactive => void", [
+ animate(100, style({ transform: "translateX(100%) scale(1)" }))
+ ]),
+ transition("void => active", [
+ style({ transform: "translateX(0) scale(0)" }),
+ animate(200)
+ ]),
+ transition("active => void", [
+ animate(200, style({ transform: "translateX(0) scale(0)" }))
+ ])
+ ])
+ ]
+})
+export class HeroListEnterLeaveStatesComponent {
+ @Input() heroes: Heroes;
+}
diff --git a/e2e/animation-examples/app/hero/hero-list-enter-leave.component.ts b/e2e/animation-examples/app/hero/hero-list-enter-leave.component.ts
new file mode 100644
index 000000000..87468a59e
--- /dev/null
+++ b/e2e/animation-examples/app/hero/hero-list-enter-leave.component.ts
@@ -0,0 +1,48 @@
+import {
+ Component,
+ Input
+} from "@angular/core";
+import {
+ trigger,
+ state,
+ style,
+ animate,
+ transition
+} from "@angular/animations";
+
+import { Heroes } from "./hero.service";
+
+@Component({
+ moduleId: module.id,
+ selector: "hero-list-enter-leave",
+ template: `
+
+
+
+ `,
+ styleUrls: ["./hero-list.component.css"],
+ /* The element here always has the state "in" when it
+ * is present. We animate two transitions: From void
+ * to in and from in to void, to achieve an animated
+ * enter and leave transition. The element enters from
+ * the left and leaves to the right using translateX.
+ */
+ animations: [
+ trigger("flyInOut", [
+ state("in", style({ transform: "translateX(0)" })),
+ transition("void => *", [
+ style({ transform: "translateX(-100%)" }),
+ animate(100)
+ ]),
+ transition("* => void", [
+ animate(100, style({ transform: "translateX(100%)" }))
+ ])
+ ])
+ ]
+})
+export class HeroListEnterLeaveComponent {
+ @Input() heroes: Heroes;
+}
diff --git a/e2e/animation-examples/app/hero/hero-list-inline-styles.component.ts b/e2e/animation-examples/app/hero/hero-list-inline-styles.component.ts
new file mode 100644
index 000000000..6c378942a
--- /dev/null
+++ b/e2e/animation-examples/app/hero/hero-list-inline-styles.component.ts
@@ -0,0 +1,52 @@
+import {
+ Component,
+ Input,
+} from "@angular/core";
+import {
+ trigger,
+ style,
+ animate,
+ transition
+} from "@angular/animations";
+
+import { Heroes } from "./hero.service";
+
+@Component({
+ moduleId: module.id,
+ selector: "hero-list-inline-styles",
+ template: `
+
+
+
+ `,
+ styleUrls: ["./hero-list.component.css"],
+ /**
+ * Define two states, "inactive" and "active", and the end
+ * styles that apply whenever the element is in those states.
+ * Then define an animation for the inactive => active transition.
+ * This animation has no end styles, but only styles that are
+ * defined inline inside the transition and thus are only kept
+ * as long as the animation is running.
+ */
+ animations: [
+ trigger("heroState", [
+ transition("inactive => active", [
+ style({
+ backgroundColor: "#cfd8dc",
+ transform: "scale(1.3)"
+ }),
+ animate("80ms ease-in", style({
+ backgroundColor: "#eee",
+ transform: "scale(1)"
+ }))
+ ]),
+ ])
+ ]
+})
+export class HeroListInlineStylesComponent {
+ @Input() heroes: Heroes;
+}
diff --git a/e2e/animation-examples/app/hero/hero-list-timings.component.ts b/e2e/animation-examples/app/hero/hero-list-timings.component.ts
new file mode 100644
index 000000000..46deaacf4
--- /dev/null
+++ b/e2e/animation-examples/app/hero/hero-list-timings.component.ts
@@ -0,0 +1,57 @@
+import {
+ Component,
+ Input
+} from "@angular/core";
+import {
+ trigger,
+ state,
+ style,
+ animate,
+ transition
+} from "@angular/animations";
+
+import { Heroes } from "./hero.service";
+
+@Component({
+ moduleId: module.id,
+ selector: "hero-list-timings",
+ template: `
+
+
+
+ `,
+ styleUrls: ["./hero-list.component.css"],
+ /* The element here always has the state "in" when it
+ * is present. We animate two transitions: From void
+ * to in and from in to void, to achieve an animated
+ * enter and leave transition. The element enters from
+ * the left and leaves to the right using translateX,
+ * and fades in/out using opacity. We use different easings
+ * for enter and leave.
+ */
+ animations: [
+ trigger("flyInOut", [
+ state("in", style({ opacity: 1, transform: "translateX(0)" })),
+ transition("void => *", [
+ style({
+ opacity: 0,
+ transform: "translateX(-100%)"
+ }),
+ animate("0.2s ease-in")
+ ]),
+ transition("* => void", [
+ animate("0.2s 0.1s ease-out", style({
+ opacity: 0,
+ transform: "translateX(100%)"
+ }))
+ ])
+ ])
+ ]
+})
+export class HeroListTimingsComponent {
+ @Input() heroes: Heroes;
+}
diff --git a/e2e/animation-examples/app/hero/hero-list-twoway.component.ts b/e2e/animation-examples/app/hero/hero-list-twoway.component.ts
new file mode 100644
index 000000000..4367814b4
--- /dev/null
+++ b/e2e/animation-examples/app/hero/hero-list-twoway.component.ts
@@ -0,0 +1,50 @@
+import {
+ Component,
+ Input
+} from "@angular/core";
+import {
+ trigger,
+ state,
+ style,
+ animate,
+ transition
+} from "@angular/animations";
+
+import { Heroes } from "./hero.service";
+
+@Component({
+ moduleId: module.id,
+ selector: "hero-list-twoway",
+ template: `
+
+
+
+ `,
+ styleUrls: ["./hero-list.component.css"],
+ /*
+ * Define two states, "inactive" and "active", and the end
+ * styles that apply whenever the element is in those states.
+ * Then define an animated transition between these two
+ * states, in *both* directions.
+ */
+ animations: [
+ trigger("heroState", [
+ state("inactive", style({
+ backgroundColor: "#eee",
+ transform: "scale(1)"
+ })),
+ state("active", style({
+ backgroundColor: "#cfd8dc",
+ transform: "scale(1.1)"
+ })),
+ transition("inactive <=> active", animate("100ms ease-out"))
+ ])
+ ]
+})
+export class HeroListTwowayComponent {
+ @Input() heroes: Heroes;
+}
diff --git a/e2e/animation-examples/app/hero/hero-list.component.css b/e2e/animation-examples/app/hero/hero-list.component.css
new file mode 100644
index 000000000..d432e70ae
--- /dev/null
+++ b/e2e/animation-examples/app/hero/hero-list.component.css
@@ -0,0 +1,8 @@
+.active {
+ background-color: #cfd8dc;
+ transform: scale(1.1);
+}
+.inactive {
+ background-color: #eee;
+ transform: scale(1);
+}
diff --git a/e2e/animation-examples/app/hero/hero-routing.module.ts b/e2e/animation-examples/app/hero/hero-routing.module.ts
new file mode 100644
index 000000000..6f65be89b
--- /dev/null
+++ b/e2e/animation-examples/app/hero/hero-routing.module.ts
@@ -0,0 +1,19 @@
+import { NgModule } from "@angular/core";
+import { Routes } from "@angular/router";
+import { NativeScriptRouterModule } from "@nativescript/angular";
+
+import { HeroTeamBuilderComponent } from './hero-team-builder.component';
+
+export const routes: Routes = [
+ { path: "", pathMatch: "full", component: HeroTeamBuilderComponent },
+];
+
+@NgModule({
+ imports: [NativeScriptRouterModule.forChild(routes)],
+ exports: [NativeScriptRouterModule],
+})
+export class HeroRoutingModule { }
+
+export const routedComponents = [
+ HeroTeamBuilderComponent,
+];
diff --git a/e2e/animation-examples/app/hero/hero-team-builder.component.html b/e2e/animation-examples/app/hero/hero-team-builder.component.html
new file mode 100644
index 000000000..843813e0f
--- /dev/null
+++ b/e2e/animation-examples/app/hero/hero-team-builder.component.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/animation-examples/app/hero/hero-team-builder.component.ts b/e2e/animation-examples/app/hero/hero-team-builder.component.ts
new file mode 100644
index 000000000..cb029a15b
--- /dev/null
+++ b/e2e/animation-examples/app/hero/hero-team-builder.component.ts
@@ -0,0 +1,13 @@
+import { Component } from "@angular/core";
+
+import { Heroes } from "./hero.service";
+
+@Component({
+ moduleId: module.id,
+ selector: "hero-team-builder",
+ templateUrl: "./hero-team-builder.component.html",
+ providers: [Heroes]
+})
+export class HeroTeamBuilderComponent {
+ constructor(public heroes: Heroes) { }
+}
diff --git a/e2e/animation-examples/app/hero/hero.module.ts b/e2e/animation-examples/app/hero/hero.module.ts
new file mode 100644
index 000000000..938ec409c
--- /dev/null
+++ b/e2e/animation-examples/app/hero/hero.module.ts
@@ -0,0 +1,31 @@
+import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+import { NativeScriptCommonModule } from "@nativescript/angular";
+
+import { HeroRoutingModule, routedComponents } from "./hero-routing.module";
+
+import { HeroListBasicComponent } from './hero-list-basic.component';
+import { HeroListInlineStylesComponent } from './hero-list-inline-styles.component';
+import { HeroListEnterLeaveComponent } from './hero-list-enter-leave.component';
+import { HeroListEnterLeaveStatesComponent } from './hero-list-enter-leave-states.component';
+import { HeroListCombinedTransitionsComponent } from './hero-list-combined-transitions.component';
+import { HeroListTwowayComponent } from './hero-list-twoway.component';
+import { HeroListTimingsComponent } from './hero-list-timings.component';
+
+@NgModule({
+ declarations: [
+ ...routedComponents,
+ HeroListBasicComponent,
+ HeroListInlineStylesComponent,
+ HeroListEnterLeaveComponent,
+ HeroListEnterLeaveStatesComponent,
+ HeroListCombinedTransitionsComponent,
+ HeroListTwowayComponent,
+ HeroListTimingsComponent,
+ ],
+ imports: [
+ NativeScriptCommonModule,
+ HeroRoutingModule,
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+})
+export class HeroModule { }
diff --git a/e2e/animation-examples/app/hero/hero.service.ts b/e2e/animation-examples/app/hero/hero.service.ts
new file mode 100644
index 000000000..d61874cd6
--- /dev/null
+++ b/e2e/animation-examples/app/hero/hero.service.ts
@@ -0,0 +1,70 @@
+import { Injectable } from "@angular/core";
+
+class Hero {
+ constructor(
+ public name: string,
+ public state = "inactive"
+ ) { }
+
+ toggleState() {
+ this.state = (this.state === "active" ? "inactive" : "active");
+ }
+}
+
+let ALL_HEROES = [
+ "Windstorm",
+ "RubberMan",
+ "Bombasto",
+ "Magneta",
+ "Dynama",
+ "Narco",
+ "Celeritas",
+ "Dr IQ",
+ "Magma",
+ "Tornado",
+ "Mr. Nice"
+].map(name => new Hero(name));
+
+@Injectable()
+export class Heroes implements Iterable {
+
+ currentHeroes: Hero[] = [new Hero("Narco")];
+
+ [Symbol.iterator]() {
+ return makeIterator(this.currentHeroes);
+ }
+
+ addActive() {
+ let hero = ALL_HEROES[this.currentHeroes.length];
+ hero.state = "active";
+ this.currentHeroes.push(hero);
+ }
+
+ addInactive() {
+ let hero = ALL_HEROES[this.currentHeroes.length];
+ hero.state = "inactive";
+ this.currentHeroes.push(hero);
+ }
+
+ remove() {
+ if (this.currentHeroes.length) {
+ this.currentHeroes.splice(this.currentHeroes.length - 1, 1);
+ }
+ }
+
+ reset() {
+ this.currentHeroes = [];
+ }
+}
+
+function makeIterator(array) {
+ let nextIndex = 0;
+
+ return {
+ next: function() {
+ return nextIndex < array.length ?
+ { value: array[nextIndex++], done: false } :
+ { value: "empty", done: true };
+ }
+ };
+}
diff --git a/e2e/animation-examples/app/main.ts b/e2e/animation-examples/app/main.ts
new file mode 100644
index 000000000..92fc7dbca
--- /dev/null
+++ b/e2e/animation-examples/app/main.ts
@@ -0,0 +1,74 @@
+import { platformNativeScriptDynamic, NativeScriptDebug, AppLaunchView } from "@nativescript/angular";
+import { Trace, GridLayout, GridUnitType, ItemSpec, Application } from "@nativescript/core";
+
+import { AppModule } from "./app.module";
+
+Trace.setCategories(NativeScriptDebug.animationsTraceCategory);
+Trace.enable();
+
+class LaunchAnimation extends GridLayout implements AppLaunchView {
+ circle: GridLayout;
+ finished = false;
+ complete: () => void;
+
+ constructor() {
+ super();
+ this.backgroundColor = "#4caef7";
+ this.className = "w-full h-full";
+
+ // construct any creative animation
+ this.circle = new GridLayout();
+ this.circle.width = 30;
+ this.circle.height = 30;
+ this.circle.borderRadius = 15;
+ this.circle.horizontalAlignment = "center";
+ this.circle.verticalAlignment = "middle";
+ this.circle.backgroundColor = "#fff";
+
+ this.addChild(this.circle);
+ }
+
+ async startAnimation() {
+ await this.circle.animate({
+ scale: { x: 2, y: 2 },
+ duration: 800,
+ });
+
+ await this.circle.animate({
+ scale: { x: 1, y: 1 },
+ duration: 800,
+ });
+
+ if (this.finished) {
+ await this.circle.animate({
+ scale: { x: 30, y: 30 },
+ duration: 400,
+ });
+ this.fadeOut();
+ } else {
+ // keep looping
+ this.startAnimation();
+ }
+ }
+
+ cleanup() {
+ return new Promise((resolve) => {
+ this.complete = resolve;
+ this.finished = true;
+ });
+ }
+
+ async fadeOut() {
+ await this.animate({
+ opacity: 0,
+ duration: 400,
+ });
+ this.complete();
+ }
+}
+
+platformNativeScriptDynamic({
+ launchView: new LaunchAnimation(),
+ // backgroundColor: 'purple',
+ // async: true
+}).bootstrapModule(AppModule);
diff --git a/e2e/animation-examples/app/options-default.component.ts b/e2e/animation-examples/app/options-default.component.ts
new file mode 100644
index 000000000..f7246ab3d
--- /dev/null
+++ b/e2e/animation-examples/app/options-default.component.ts
@@ -0,0 +1,77 @@
+import { Component } from '@angular/core';
+import {
+ trigger,
+ state,
+ style,
+ animate,
+ transition
+} from '@angular/animations';
+
+@Component({
+ template: `
+
+
+
+
+
+
+
+ `,
+ styles: [
+ '.enabled: { background-color: green; color: white }',
+ '.disabled: { background-color: grey; color: #777 }'
+ ],
+ animations: [
+ trigger('flyInOut', [
+ state('in', style({transform: 'translate(0)'})),
+
+ transition('void => *', [
+ style({transform: 'translate(100%)'}),
+ animate('{{ appearTime }}')
+ ], {
+ params: {
+ appearTime: '1s'
+ }
+ }),
+
+ transition('* => void', [
+ animate("{{ disappearTime }}",
+ style({transform: 'translateX(100%)'}))
+ ], {
+ params: {
+ disappearTime: '0.4s'
+ }
+ })
+ ])
+ ]
+})
+export class OptionsDefaultComponent {
+ public heroes = ["Harley Quinn", "Wonder Woman", "Joker", "Aquaman"];
+ public extraHeroes = ["Batman", "Superman", "Killer Frost", "The Flash"];
+
+ flyOut(hero) {
+ const heroIndex = this.heroes.indexOf(hero);
+
+ this.extraHeroes.push(hero);
+ this.heroes.splice(heroIndex, 1);
+ }
+
+ addNew() {
+ const newHero = this.extraHeroes.pop();
+ this.heroes.push(newHero);
+ }
+}
diff --git a/e2e/animation-examples/app/options.component.ts b/e2e/animation-examples/app/options.component.ts
new file mode 100644
index 000000000..8f86d8613
--- /dev/null
+++ b/e2e/animation-examples/app/options.component.ts
@@ -0,0 +1,44 @@
+import { trigger, transition, style, animate } from "@angular/animations";
+import { Component } from "@angular/core";
+
+@Component({
+ template: `
+
+
+
+
+
+ `,
+ animations: [
+ trigger("someCoolAnimation", [
+ transition(":leave", [
+ animate("{{ time }}", style({ opacity: "{{ minOpacity }}" }))
+ ]),
+ ]),
+ ],
+})
+export class OptionsComponent {
+ public animationData = {
+ value: "active",
+ params: {
+ time: "0.5s",
+ minOpacity: 0,
+ }
+ };
+
+ fadeOut() {
+ this.animationData.value = "inactive";
+ }
+}
+
diff --git a/e2e/animation-examples/app/query-stagger.component.ts b/e2e/animation-examples/app/query-stagger.component.ts
new file mode 100644
index 000000000..6047d1ecf
--- /dev/null
+++ b/e2e/animation-examples/app/query-stagger.component.ts
@@ -0,0 +1,50 @@
+import {
+ animate,
+ query,
+ stagger,
+ style,
+ transition,
+ trigger,
+} from "@angular/animations";
+import { Component } from "@angular/core";
+
+@Component({
+ moduleId: module.id,
+ animations: [
+ trigger("listAnimation", [
+ transition("* => *", [
+ // this hides everything right away
+ query(":enter", style({ opacity: 0 })),
+
+ // starts to animate things with a stagger in between
+ query(":enter", stagger(200, [
+ animate(1000, style({ opacity: 1 }))
+ ]), { delay: 200 })
+ ])
+ ])
+ ],
+ template: `
+
+
+
+
+
+
+
+ `
+})
+export class QueryStaggerComponent {
+ public items = [
+ "Dramatic",
+ "Entrance",
+ "With",
+ "Really",
+ "Cool",
+ "Stagger",
+ ];
+
+ add() {
+ const newItem = `Item ${this.items.length}`;
+ this.items.push(newItem);
+ }
+}
diff --git a/e2e/animation-examples/app/selector-all.component.ts b/e2e/animation-examples/app/selector-all.component.ts
new file mode 100644
index 000000000..e2ab9768f
--- /dev/null
+++ b/e2e/animation-examples/app/selector-all.component.ts
@@ -0,0 +1,74 @@
+import {
+ animate,
+ query,
+ style,
+ transition,
+ trigger,
+} from "@angular/animations";
+import { Component, NgZone } from "@angular/core";
+
+@Component({
+ moduleId: module.id,
+ animations: [
+ trigger("listAnimation", [
+
+ transition(":enter", [
+ query("*", [
+ style({ opacity: 0 }),
+ animate(1000, style({ opacity: 1 }))
+ ])
+ ]),
+
+ transition(":leave", [
+ query("*", [
+ style({ opacity: 1 }),
+ animate(1000, style({ opacity: 0 }))
+ ])
+
+ ])
+ ]),
+ ],
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class SelectorAllComponent {
+ public items = [
+ "first",
+ "second",
+ ];
+
+ constructor(private zone: NgZone) {}
+
+ add() {
+ const newItem = `Item ${this.items.length}`;
+ this.items.push(newItem);
+ }
+
+ addRandom() {
+ // this.zone.run(() => {
+ this.items.push('random');
+ // });
+ }
+
+ remove(item) {
+ const index = this.items.indexOf(item);
+ this.items.splice(index, 1)
+ }
+}
diff --git a/e2e/animation-examples/e2e/helper/utils.ts b/e2e/animation-examples/e2e/helper/utils.ts
new file mode 100644
index 000000000..76b2fe75e
--- /dev/null
+++ b/e2e/animation-examples/e2e/helper/utils.ts
@@ -0,0 +1,18 @@
+import { UIElement } from "nativescript-dev-appium";
+
+export const sort = async (array: Array) => {
+ var done = false;
+ while (!done) {
+ done = true;
+ for (var i = 1; i < array.length; i++) {
+ if ((await (await array[i - 1]).location()).y > (await (await array[i]).location()).y) {
+ done = false;
+ var tmp = array[i - 1];
+ array[i - 1] = array[i];
+ array[i] = tmp;
+ }
+ }
+ }
+
+ return array;
+}
\ No newline at end of file
diff --git a/e2e/animation-examples/e2e/pages/animate-child-page.ts b/e2e/animation-examples/e2e/pages/animate-child-page.ts
new file mode 100644
index 000000000..05e8810d3
--- /dev/null
+++ b/e2e/animation-examples/e2e/pages/animate-child-page.ts
@@ -0,0 +1,40 @@
+import { AppiumDriver, UIElement } from "nativescript-dev-appium";
+import { assert } from "chai";
+import { BasePage } from "./base-page";
+
+export class AnimateChildPage extends BasePage {
+ private _parent: UIElement;
+ private _child: UIElement;
+
+ constructor(_driver: AppiumDriver) {
+ super(_driver)
+ }
+
+ async enterExample() {
+ const exampleBtn = await this._driver.findElementByAccessibilityId("animate-child");
+ await exampleBtn.click();
+ }
+
+ async waitParentToAppear() {
+ this._parent = (await this.awaitItemToAppear("parent")).element;
+ const startTime = Date.now();
+ while ((await this._parent.location()).x !== 0 && Date.now() - startTime < 3000) { }
+ }
+
+ async waitChildToAppear() {
+ this._child = (await this.awaitItemToAppear("child")).element;
+ const startTime = Date.now();
+ while ((await this._child.location()).y !== (await this._parent.location()).y && Date.now() - startTime < 3000) { }
+ }
+
+ async assertContainersPosition() {
+ assert.isTrue((await this._parent.location()).x === 0);
+ assert.isTrue((await this._parent.location()).y === (await this._child.location()).y);
+ }
+
+ private async awaitItemToAppear(item: string, wait: number = 3000): Promise<{ isVisible: boolean, element: UIElement }> {
+ const findBtn = async () => { return await this._driver.findElementByXPathIfExists(this._elementHelper.findByTextLocator("*", item, true)); };
+
+ return await this.waitElementTo(findBtn, true, wait);;
+ }
+}
\ No newline at end of file
diff --git a/e2e/animation-examples/e2e/pages/animation-builder-page.ts b/e2e/animation-examples/e2e/pages/animation-builder-page.ts
new file mode 100644
index 000000000..5c6349a7d
--- /dev/null
+++ b/e2e/animation-examples/e2e/pages/animation-builder-page.ts
@@ -0,0 +1,25 @@
+import { AppiumDriver, UIElement } from "nativescript-dev-appium";
+import { BasePage } from "./base-page";
+
+export class AnimationBuilderPage extends BasePage {
+ static tapToDisappear: string = "tapToDisappear";
+
+ constructor(driver: AppiumDriver) {
+ super(driver);
+ }
+
+ async enterExample() {
+ const exampleBtn = await this._driver.findElementByAutomationText("builder");
+ await exampleBtn.click();
+ }
+
+ async executeAnimation() {
+ const btnTapToDisappear = await this._driver.waitForElement(AnimationBuilderPage.tapToDisappear);
+ console.log("Btn tap to disappear should disappear");
+ await btnTapToDisappear.click();
+ }
+
+ async waitElementToHide(wait: number) {
+ return this.waitElementTo(() => this._driver.waitForElement(AnimationBuilderPage.tapToDisappear), false, wait);
+ }
+}
\ No newline at end of file
diff --git a/e2e/animation-examples/e2e/pages/animation-with-options-page.ts b/e2e/animation-examples/e2e/pages/animation-with-options-page.ts
new file mode 100644
index 000000000..131ac8599
--- /dev/null
+++ b/e2e/animation-examples/e2e/pages/animation-with-options-page.ts
@@ -0,0 +1,44 @@
+import { AppiumDriver, Point, UIElement } from "nativescript-dev-appium";
+import { assert } from "chai";
+import { BasePage } from "./base-page";
+
+export class AnimationWithOptionsPage extends BasePage {
+ private initialPositionOfAnimatedBtn: Point
+
+ constructor(driver: AppiumDriver) {
+ super(driver);
+ }
+
+ async enterExample() {
+ const exampleBtn = await this._driver.findElementByAccessibilityId("options");
+ await exampleBtn.click();
+ const animatedBtn = await this.animatedBtn();
+ this.initialPositionOfAnimatedBtn = await animatedBtn.location();
+ }
+
+ async btnToggleAnimation() {
+ return await this._driver.findElementByAccessibilityId("toggleAnimation");
+ }
+
+ async animatedBtn() {
+ await this._driver.wait(2000);
+ const btn = await this._driver.findElementByAccessibilityIdIfExists("animatedBtn");
+ return btn;
+ }
+
+ async toggleAnimation() {
+ const btnTapToDisappear = await this.btnToggleAnimation();
+ await btnTapToDisappear.click();
+ }
+
+ async waitElementToHide() {
+ await this._driver.wait(2000);
+ return await this._driver.findElementByAccessibilityIdIfExists("animatedBtn");
+ }
+
+ async assertPositionOfToggleAnimationBtn() {
+ await this.waitElementTo(() => this.btnToggleAnimation(), true, 5000);
+ const point: Point = await (await this.btnToggleAnimation()).location();
+ assert.isTrue(point.y === this.initialPositionOfAnimatedBtn.y);
+ }
+}
\ No newline at end of file
diff --git a/e2e/animation-examples/e2e/pages/animations-with-default-options-page.ts b/e2e/animation-examples/e2e/pages/animations-with-default-options-page.ts
new file mode 100644
index 000000000..76e34ae04
--- /dev/null
+++ b/e2e/animation-examples/e2e/pages/animations-with-default-options-page.ts
@@ -0,0 +1,71 @@
+import { AppiumDriver, UIElement } from "nativescript-dev-appium";
+import { assert } from "chai";
+import { sort } from "../helper/utils";
+import { BasePage } from "./base-page";
+
+export class AnimationsWithDefaultOptionsPage extends BasePage {
+ private _btnAddItem: UIElement;
+ private _children: Array;
+ constructor(driver: AppiumDriver) {
+ super(driver);
+ }
+
+ async enterExample() {
+ const exampleBtn = await this._driver.findElementByAccessibilityId("options-default");
+ await exampleBtn.click();
+ this._children = await this.getChildren();
+ }
+
+ async addItem() {
+ this._btnAddItem = await this._driver.findElementByAccessibilityId("add");
+
+ await this._btnAddItem.click();
+ }
+
+ async clickOnItem(item: string) {
+ const btn = await this.getItem(item);
+
+ await btn.click();
+ }
+
+ private getItem(item) {
+ return this._driver.findElementByXPathIfExists(`${this._elementHelper.getXPathByTextAttributes("//*", "itemsContainer", true)}${this._elementHelper.getXPathByTextAttributes("//*", item, false)}`);
+ }
+
+ async awaitItemToDisappear(item: string, wait: number = 3000) {
+ const startTime = Date.now();
+ let btn = await this.getItem(item);
+ while (btn && await btn.isDisplayed() && Date.now() - startTime <= wait) {
+ btn = await this.getItem(item);
+ }
+
+ this._children = await this.getChildren();
+ }
+
+ async awaitItemToAppear(item: string, wait: number = 3000) {
+ const startTime = Date.now();
+ let btn = await this.getItem(item);
+ while (!btn && !(btn && !(await btn.isDisplayed())) && Date.now() - startTime <= wait) {
+ btn = await this.getItem(item);
+ }
+
+ this._children = await this.getChildren();
+ }
+
+ async getChildren() {
+ const children: Array = await this._driver.findElementsByXPath(`${this._elementHelper.getXPathByTextAttributes("//*", "itemsContainer", true)}/*`);
+ const orderedList: Array = await sort(children);
+
+ return orderedList;
+ }
+
+ async assertItemPosition(text: string, itemIndex: number, expectedElementsCount: number) {
+ const children = this._children;
+
+ assert.isTrue(children.length === expectedElementsCount, `Expected items count: ${expectedElementsCount} is not as actual: ${children.length}`);
+ const element = children[itemIndex];
+ console.log("Element text: ", await element.text());
+ const currentElementText = await element.text();
+ assert.isTrue(currentElementText.toLowerCase() === text.toLowerCase(), `Expected element text: ${text} at position: ${itemIndex} is not as actual: ${currentElementText}!`);
+ }
+}
\ No newline at end of file
diff --git a/e2e/animation-examples/e2e/pages/base-page.ts b/e2e/animation-examples/e2e/pages/base-page.ts
new file mode 100644
index 000000000..5fe48dba3
--- /dev/null
+++ b/e2e/animation-examples/e2e/pages/base-page.ts
@@ -0,0 +1,29 @@
+import { AppiumDriver, ElementHelper, UIElement } from "nativescript-dev-appium";
+
+export class BasePage {
+ protected _elementHelper: ElementHelper;
+
+ constructor(protected _driver: AppiumDriver) {
+ this._elementHelper = new ElementHelper(this._driver.nsCapabilities);
+ }
+
+ async waitElementTo(element: () => Promise, shouldBeVisible: boolean, wait: number) {
+ const start = Date.now();
+ let btn = await element();
+ while ((await this.isBtnDisplayed(btn)) !== shouldBeVisible && Date.now() - start <= wait) {
+ btn = await element();
+ }
+
+ return { isVisible: await this.isBtnDisplayed(btn), element: btn };
+ }
+
+ async isBtnDisplayed(element: UIElement) {
+ let btn: UIElement = await element;
+ let isBtnDisplayed = false
+ try {
+ isBtnDisplayed = btn ? await btn.isDisplayed() : false;
+ } catch (error) { }
+
+ return isBtnDisplayed;
+ }
+}
\ No newline at end of file
diff --git a/e2e/animation-examples/e2e/pages/extarnal-animation-page.ts b/e2e/animation-examples/e2e/pages/extarnal-animation-page.ts
new file mode 100644
index 000000000..96eeda94b
--- /dev/null
+++ b/e2e/animation-examples/e2e/pages/extarnal-animation-page.ts
@@ -0,0 +1,26 @@
+import { AppiumDriver, UIElement } from "nativescript-dev-appium";
+import { BasePage } from "./base-page";
+
+export class ExternalAnimationPage extends BasePage {
+ constructor(driver: AppiumDriver) {
+ super(driver);
+ }
+
+ async enterExample() {
+ const exampleBtn = await this._driver.waitForElement("external");
+ await exampleBtn.click();
+ }
+
+ async toggleAnimation() {
+ const btnTapToDisappear = await this._driver.waitForElement("toggleAnimation");
+ await btnTapToDisappear.click();
+ }
+
+ animatedBtn() {
+ return this._driver.waitForElement("animatedBtn");
+ }
+
+ async waitElementToToggleVisibilityTo(shouldBeVisible: boolean) {
+ return this.waitElementTo(() => this.animatedBtn(), shouldBeVisible, 10000);
+ }
+}
\ No newline at end of file
diff --git a/e2e/animation-examples/e2e/pages/fade-in-out-page.ts b/e2e/animation-examples/e2e/pages/fade-in-out-page.ts
new file mode 100644
index 000000000..d44c8e460
--- /dev/null
+++ b/e2e/animation-examples/e2e/pages/fade-in-out-page.ts
@@ -0,0 +1,26 @@
+import { AppiumDriver } from "nativescript-dev-appium";
+import { BasePage } from "./base-page";
+
+export class FadeInOutPage extends BasePage {
+ constructor(driver: AppiumDriver) {
+ super(driver);
+ }
+
+ async enterExample() {
+ const exampleBtn = await this._driver.findElementByAccessibilityId("fade-in-out");
+ await exampleBtn.click();
+ }
+
+ async toggleAnimation() {
+ const btnTapToDisappear = await this._driver.findElementByAccessibilityId("toggleAnimation");
+ await btnTapToDisappear.click();
+ }
+
+ animatedBtn() {
+ return this._driver.findElementByAccessibilityIdIfExists("animatedBtn");
+ }
+
+ async waitElementToToggleVisibility(shouldBeVisible: boolean) {
+ return this.waitElementTo(() => this.animatedBtn(), shouldBeVisible, 10000);
+ }
+}
\ No newline at end of file
diff --git a/e2e/animation-examples/e2e/pages/hero-page.ts b/e2e/animation-examples/e2e/pages/hero-page.ts
new file mode 100644
index 000000000..503716a3c
--- /dev/null
+++ b/e2e/animation-examples/e2e/pages/hero-page.ts
@@ -0,0 +1,34 @@
+import { AppiumDriver } from "nativescript-dev-appium";
+import { BasePage } from "./base-page";
+
+export class HeroPage extends BasePage{
+ constructor(driver: AppiumDriver) {
+ super(driver);
+ }
+
+ async enterExample() {
+ const exampleBtn = await this._driver.findElementByAccessibilityId("hero");
+ await exampleBtn.click();
+ }
+
+ public async addActive() {
+ await this.executeAction("addActive");
+ }
+
+ public async addInactive() {
+ await this.executeAction("addInactive");
+ }
+
+ public async remove() {
+ await this.executeAction("remove");
+ }
+
+ public async reset() {
+ await this.executeAction("reset");
+ }
+
+ public async executeAction(name: string) {
+ const btn = await this._driver.findElementByAccessibilityId(name);
+ await btn.click();
+ }
+}
\ No newline at end of file
diff --git a/e2e/animation-examples/e2e/pages/query-stagger-page.ts b/e2e/animation-examples/e2e/pages/query-stagger-page.ts
new file mode 100644
index 000000000..354428624
--- /dev/null
+++ b/e2e/animation-examples/e2e/pages/query-stagger-page.ts
@@ -0,0 +1,42 @@
+import { AppiumDriver, UIElement, SearchOptions } from "nativescript-dev-appium";
+import { assert } from "chai";
+import { sort } from "../helper/utils";
+import { BasePage } from "./base-page";
+
+export class QueryWithStaggerPage extends BasePage {
+ private _btnAddItem: UIElement;
+ constructor(driver: AppiumDriver) {
+ super(driver);
+ }
+
+ async enterExample() {
+ const exampleBtn = await this._driver.findElementByAccessibilityId("query-stagger");
+ await exampleBtn.click();
+ }
+
+ async addItem() {
+ this._btnAddItem = await this._driver.findElementByText("ADD", SearchOptions.contains);
+
+ await this._btnAddItem.click();
+ }
+
+ async getChildren() {
+ const children: Array = await this._driver.findElementsByXPath(`${this._elementHelper.getXPathByTextAttributes("//*", "container", true)}/*`);
+ const orderedList: Array = await sort(children);
+
+ return orderedList;
+ }
+
+ async assertItemPosition(text: string, itemIndex: number, expectedElementsCount: number) {
+ const startTime = Date.now();
+ let item = await this._driver.findElementByTextIfExists(text)
+ while ((!item || !(await item.isDisplayed())) && Date.now() - startTime <= 3000) { }
+ const children = await this.getChildren();
+ assert.isTrue(children.length === expectedElementsCount);
+ const element = children[itemIndex];
+ const elementText = await element.text();
+ console.log("Element text: ", elementText);
+
+ assert.isTrue(elementText.toLowerCase() === text.toLowerCase());
+ }
+}
\ No newline at end of file
diff --git a/e2e/animation-examples/e2e/pages/selector-page.ts b/e2e/animation-examples/e2e/pages/selector-page.ts
new file mode 100644
index 000000000..612bb18db
--- /dev/null
+++ b/e2e/animation-examples/e2e/pages/selector-page.ts
@@ -0,0 +1,58 @@
+import { AppiumDriver, UIElement } from "nativescript-dev-appium";
+import { assert } from "chai";
+import { sort } from "../helper/utils";
+import { BasePage } from "./base-page";
+
+export class SelectorPage extends BasePage {
+ private _btnAddItem: UIElement;
+ private _itemsContainerXpath;
+ private _itemsContainerChildrenXpath;
+
+ constructor(_driver: AppiumDriver) {
+ super(_driver);
+ this._itemsContainerXpath = this._elementHelper.getXPathByTextAttributes("//*", "itemsContainer", true);
+ this._itemsContainerChildrenXpath = `${this._itemsContainerXpath}/*`
+ }
+
+ async enterExample() {
+ const exampleBtn = await this._driver.findElementByAccessibilityId("selector");
+ await exampleBtn.click();
+ }
+
+ async addItem() {
+ this._btnAddItem = await this._driver.findElementByText("add");
+ await this._btnAddItem.click();
+ }
+
+ async clickOnItem(item: string) {
+ const btn = await this._driver.findElementByXPath(this.itemXpath(item));
+
+ await btn.click();
+ }
+
+ async waitItemToToggleVisibility(item: string, visibility: boolean) {
+ return this.waitElementTo(() => this._driver.findElementByXPathIfExists(this.itemXpath(item)), visibility, 5000);
+ }
+
+ async getChildren() {
+ const children: Array = await this._driver.findElementsByXPath(this._itemsContainerChildrenXpath);
+ const orderedList: Array = await sort(children);
+
+ return orderedList;
+ }
+
+ async assertElementPosition(expectedElementsCount: number) {
+ const children = await this.getChildren();
+ assert.isTrue(children.length === expectedElementsCount)
+ for (let index = 0; index < children.length - 1; index++) {
+ const element = children[index];
+ const el = await (element.driver()).elementByXPathIfExists(this._elementHelper.getXPathByTextAttributes("//*", `Item No.${index}`, true));
+ console.log(await el.text());
+ assert.isTrue(el && el !== null);
+ }
+ }
+
+ private itemXpath(item) {
+ return `${this._itemsContainerXpath}${this._elementHelper.getXPathByTextAttributes("//*", item, false)}`;
+ }
+}
\ No newline at end of file
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api19-Default/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api19-Default/add_active_items.png
new file mode 100644
index 000000000..270d1b007
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api19-Default/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api19-Default/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api19-Default/add_inactive_items.png
new file mode 100644
index 000000000..7201c5550
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api19-Default/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api19-Default/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api19-Default/add_remove_items.png
new file mode 100644
index 000000000..270d1b007
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api19-Default/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api19-Default/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api19-Default/add_reset_items.png
new file mode 100644
index 000000000..47d1bbb7e
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api19-Default/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Default/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Default/add_active_items.png
new file mode 100644
index 000000000..7ec26ac9a
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Default/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Default/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Default/add_inactive_items.png
new file mode 100644
index 000000000..48e4b3fed
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Default/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Default/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Default/add_remove_items.png
new file mode 100644
index 000000000..f5afdfa80
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Default/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Default/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Default/add_reset_items.png
new file mode 100644
index 000000000..9fc42c191
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Default/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Google/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Google/add_active_items.png
new file mode 100644
index 000000000..16a366e55
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Google/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Google/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Google/add_inactive_items.png
new file mode 100644
index 000000000..c2b6ae485
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Google/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Google/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Google/add_remove_items.png
new file mode 100644
index 000000000..8f43603c6
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Google/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Google/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Google/add_reset_items.png
new file mode 100644
index 000000000..0efdf2fdf
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api22-Google/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api23-Default/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api23-Default/add_active_items.png
new file mode 100644
index 000000000..b00ca72c1
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api23-Default/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api23-Default/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api23-Default/add_inactive_items.png
new file mode 100644
index 000000000..b5af980ac
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api23-Default/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api23-Default/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api23-Default/add_remove_items.png
new file mode 100644
index 000000000..89a245a89
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api23-Default/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api23-Default/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api23-Default/add_reset_items.png
new file mode 100644
index 000000000..8c1a81384
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api23-Default/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api24-Default/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api24-Default/add_active_items.png
new file mode 100644
index 000000000..ba1bc9e28
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api24-Default/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api24-Default/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api24-Default/add_inactive_items.png
new file mode 100644
index 000000000..48c57ada3
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api24-Default/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api24-Default/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api24-Default/add_remove_items.png
new file mode 100644
index 000000000..ba1bc9e28
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api24-Default/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api24-Default/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api24-Default/add_reset_items.png
new file mode 100644
index 000000000..d18eb2357
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api24-Default/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api25-Google/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api25-Google/add_active_items.png
new file mode 100644
index 000000000..24517e39a
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api25-Google/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api25-Google/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api25-Google/add_inactive_items.png
new file mode 100644
index 000000000..295cf5363
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api25-Google/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api25-Google/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api25-Google/add_remove_items.png
new file mode 100644
index 000000000..952bf2257
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api25-Google/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api25-Google/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api25-Google/add_reset_items.png
new file mode 100644
index 000000000..f225ab1f9
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api25-Google/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api26-Google/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api26-Google/add_active_items.png
new file mode 100644
index 000000000..24517e39a
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api26-Google/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api26-Google/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api26-Google/add_inactive_items.png
new file mode 100644
index 000000000..295cf5363
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api26-Google/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api26-Google/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api26-Google/add_remove_items.png
new file mode 100644
index 000000000..952bf2257
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api26-Google/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api26-Google/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api26-Google/add_reset_items.png
new file mode 100644
index 000000000..f225ab1f9
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api26-Google/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api27-Google/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api27-Google/add_active_items.png
new file mode 100644
index 000000000..24517e39a
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api27-Google/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api27-Google/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api27-Google/add_inactive_items.png
new file mode 100644
index 000000000..295cf5363
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api27-Google/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api27-Google/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api27-Google/add_remove_items.png
new file mode 100644
index 000000000..952bf2257
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api27-Google/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api27-Google/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api27-Google/add_reset_items.png
new file mode 100644
index 000000000..f225ab1f9
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api27-Google/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api28-Google/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api28-Google/add_active_items.png
new file mode 100644
index 000000000..a8720d5b2
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api28-Google/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api28-Google/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api28-Google/add_inactive_items.png
new file mode 100644
index 000000000..e681c7b4d
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api28-Google/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api28-Google/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api28-Google/add_remove_items.png
new file mode 100644
index 000000000..a8720d5b2
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api28-Google/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api28-Google/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api28-Google/add_reset_items.png
new file mode 100644
index 000000000..c02a2a924
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api28-Google/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api29-Google/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api29-Google/add_active_items.png
new file mode 100644
index 000000000..b39b8fa55
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api29-Google/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api29-Google/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api29-Google/add_inactive_items.png
new file mode 100644
index 000000000..69326e20d
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api29-Google/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api29-Google/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api29-Google/add_remove_items.png
new file mode 100644
index 000000000..b39b8fa55
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api29-Google/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api29-Google/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api29-Google/add_reset_items.png
new file mode 100644
index 000000000..255c1c881
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/Emulator-Api29-Google/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 110/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 110/add_active_items.png
new file mode 100644
index 000000000..ecdc5a3ce
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 110/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 110/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 110/add_inactive_items.png
new file mode 100644
index 000000000..3797ea51d
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 110/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 110/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 110/add_remove_items.png
new file mode 100644
index 000000000..ecdc5a3ce
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 110/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 110/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 110/add_reset_items.png
new file mode 100644
index 000000000..650669e53
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 110/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 12/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 12/add_active_items.png
new file mode 100644
index 000000000..121886eda
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 12/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 12/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 12/add_inactive_items.png
new file mode 100644
index 000000000..c8d8ffe29
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 12/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 12/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 12/add_remove_items.png
new file mode 100644
index 000000000..121886eda
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 12/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 12/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 12/add_reset_items.png
new file mode 100644
index 000000000..996b6c4e5
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 7 12/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 8/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 8/add_active_items.png
new file mode 100644
index 000000000..421bfc51a
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 8/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 8/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 8/add_inactive_items.png
new file mode 100644
index 000000000..b83c77259
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 8/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 8/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 8/add_remove_items.png
new file mode 100644
index 000000000..33116d0f1
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 8/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 8/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 8/add_reset_items.png
new file mode 100644
index 000000000..0a788d779
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone 8/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 110/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 110/add_active_items.png
new file mode 100644
index 000000000..fae72a389
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 110/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 110/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 110/add_inactive_items.png
new file mode 100644
index 000000000..39fbf2243
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 110/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 110/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 110/add_remove_items.png
new file mode 100644
index 000000000..fae72a389
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 110/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 110/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 110/add_reset_items.png
new file mode 100644
index 000000000..6f35565ae
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 110/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 12/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 12/add_active_items.png
new file mode 100644
index 000000000..2abe37a94
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 12/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 12/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 12/add_inactive_items.png
new file mode 100644
index 000000000..69c569cd4
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 12/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 12/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 12/add_remove_items.png
new file mode 100644
index 000000000..cabf56466
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 12/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 12/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 12/add_reset_items.png
new file mode 100644
index 000000000..034b4696d
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X 12/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 12/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 12/add_active_items.png
new file mode 100644
index 000000000..f431a03c8
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 12/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 12/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 12/add_inactive_items.png
new file mode 100644
index 000000000..1f39e498e
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 12/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 12/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 12/add_remove_items.png
new file mode 100644
index 000000000..f431a03c8
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 12/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 12/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 12/add_reset_items.png
new file mode 100644
index 000000000..b74aa4345
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 12/add_reset_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 13/add_active_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 13/add_active_items.png
new file mode 100644
index 000000000..cf0b582db
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 13/add_active_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 13/add_inactive_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 13/add_inactive_items.png
new file mode 100644
index 000000000..dd0a15a1a
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 13/add_inactive_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 13/add_remove_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 13/add_remove_items.png
new file mode 100644
index 000000000..cf0b582db
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 13/add_remove_items.png differ
diff --git a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 13/add_reset_items.png b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 13/add_reset_items.png
new file mode 100644
index 000000000..df8a19656
Binary files /dev/null and b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone XR 13/add_reset_items.png differ
diff --git "a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X\312\200/add_active_items.png" "b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X\312\200/add_active_items.png"
new file mode 100644
index 000000000..b2576fe65
Binary files /dev/null and "b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X\312\200/add_active_items.png" differ
diff --git "a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X\312\200/add_inactive_items.png" "b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X\312\200/add_inactive_items.png"
new file mode 100644
index 000000000..7d5881652
Binary files /dev/null and "b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X\312\200/add_inactive_items.png" differ
diff --git "a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X\312\200/add_remove_items.png" "b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X\312\200/add_remove_items.png"
new file mode 100644
index 000000000..0a9a802d3
Binary files /dev/null and "b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X\312\200/add_remove_items.png" differ
diff --git "a/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X\312\200/add_reset_items.png" "b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X\312\200/add_reset_items.png"
new file mode 100644
index 000000000..ae2bc14bb
Binary files /dev/null and "b/e2e/animation-examples/e2e/resources/images/ng4animations/iPhone X\312\200/add_reset_items.png" differ
diff --git a/e2e/animation-examples/e2e/setup.ts b/e2e/animation-examples/e2e/setup.ts
new file mode 100644
index 000000000..e7143ae78
--- /dev/null
+++ b/e2e/animation-examples/e2e/setup.ts
@@ -0,0 +1,18 @@
+import { startServer, stopServer, ITestReporter, nsCapabilities, LogImageType } from "nativescript-dev-appium";
+const addContext = require('mochawesome/addContext');
+
+const testReporterContext = {};
+testReporterContext.name = "mochawesome";
+testReporterContext.reportDir = "mochawesome-report";
+testReporterContext.log = addContext;
+testReporterContext.logImageTypes = [LogImageType.screenshots, LogImageType.everyImage];
+nsCapabilities.testReporter = testReporterContext;
+
+before("start server", async function () {
+ nsCapabilities.testReporter.context = this;
+ await startServer();
+});
+
+after("stop server", async function () {
+ await stopServer();
+});
diff --git a/e2e/animation-examples/e2e/smoke.e2e-spec.ts b/e2e/animation-examples/e2e/smoke.e2e-spec.ts
new file mode 100644
index 000000000..9d929b361
--- /dev/null
+++ b/e2e/animation-examples/e2e/smoke.e2e-spec.ts
@@ -0,0 +1,165 @@
+import { AppiumDriver, createDriver, nsCapabilities } from "nativescript-dev-appium";
+import { assert } from "chai";
+import { AnimationBuilderPage } from "./pages/animation-builder-page";
+import { ExternalAnimationPage } from "./pages/extarnal-animation-page";
+import { SelectorPage } from "./pages/selector-page";
+import { QueryWithStaggerPage } from "./pages/query-stagger-page";
+import { FadeInOutPage } from "./pages/fade-in-out-page";
+import { AnimationWithOptionsPage } from "./pages/animation-with-options-page";
+import { AnimationsWithDefaultOptionsPage } from "./pages/animations-with-default-options-page";
+import { AnimateChildPage } from "./pages/animate-child-page";
+import { HeroPage } from "./pages/hero-page";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+const isSauceRun = isSauceLab;
+
+describe("smoke-tests", async function () {
+ let driver: AppiumDriver;
+
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ });
+
+ after(async function () {
+ if (isSauceRun) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ try {
+ await driver.navBack();
+ } catch (error) {
+ await driver.logTestArtifacts(`${this.currentTest.title}_navBack_fail`);
+ }
+ });
+
+ it("animation builder - btn should disappear", async function () {
+ const animationBuilder = new AnimationBuilderPage(driver);
+ await animationBuilder.enterExample();
+ await animationBuilder.executeAnimation();
+ const result = await animationBuilder.waitElementToHide(driver.defaultWaitTime);
+ assert.isFalse(!result || result.isVisible, "The btn should disappear");
+ });
+
+ it("external animation - visibility", async function () {
+ this.retries(1);
+ const externalAnimationPage = new ExternalAnimationPage(driver);
+ await externalAnimationPage.enterExample();
+ await externalAnimationPage.toggleAnimation();
+ let result = await externalAnimationPage.waitElementToToggleVisibilityTo(false);
+ assert.isFalse(result.isVisible, "The button should disappear!");
+
+ await externalAnimationPage.toggleAnimation();
+ result = await externalAnimationPage.waitElementToToggleVisibilityTo(true);
+ assert.isTrue(result.isVisible, "The button should appear!");
+ });
+
+ it("selector", async function () {
+ const selectorPage = new SelectorPage(driver);
+ await selectorPage.enterExample();
+ await selectorPage.addItem();
+ await selectorPage.waitItemToToggleVisibility("Item No.2", true);
+ await selectorPage.assertElementPosition(4);
+
+ await selectorPage.clickOnItem("second");
+ await selectorPage.waitItemToToggleVisibility("second", false);
+ await selectorPage.assertElementPosition(3);
+ });
+
+ it("query with stagger", async function () {
+ const queryWithStaggerPage = new QueryWithStaggerPage(driver);
+ await queryWithStaggerPage.enterExample();
+ await queryWithStaggerPage.addItem();
+ await queryWithStaggerPage.assertItemPosition("Item 6", 6, 7);
+ });
+
+ it("fade in - out", async function () {
+ this.retries(1);
+ const fadeInOutPage = new FadeInOutPage(driver);
+ await fadeInOutPage.enterExample();
+ await fadeInOutPage.toggleAnimation();
+ let result = await fadeInOutPage.waitElementToToggleVisibility(false);
+ assert.isFalse(result.isVisible, "The button should disappear!");
+
+ await fadeInOutPage.toggleAnimation();
+ result = await fadeInOutPage.waitElementToToggleVisibility(true);
+ assert.isTrue(result.isVisible, "The button should appear!");
+ });
+
+ it("animation with options", async function () {
+ const animationWithOptionsPage = new AnimationWithOptionsPage(driver);
+ await animationWithOptionsPage.enterExample();
+ await animationWithOptionsPage.toggleAnimation();
+ const result = await animationWithOptionsPage.waitElementToHide();
+ assert.isUndefined(result, "The button should disappear!");
+
+ await animationWithOptionsPage.assertPositionOfToggleAnimationBtn();
+ });
+
+ it("animation with default options", async function () {
+ const animationWithOptionsPage = new AnimationsWithDefaultOptionsPage(driver);
+ await animationWithOptionsPage.enterExample();
+ let examplesCount = 5;
+ await animationWithOptionsPage.assertItemPosition("Harley Quinn", 1, examplesCount);
+ await animationWithOptionsPage.assertItemPosition("Wonder Woman", 2, examplesCount);
+ await animationWithOptionsPage.assertItemPosition("Joker", 3, examplesCount);
+ await animationWithOptionsPage.assertItemPosition("Aquaman", 4, examplesCount);
+
+ await animationWithOptionsPage.clickOnItem("Harley Quinn");
+ examplesCount--;
+ await animationWithOptionsPage.awaitItemToDisappear("Harley Quinn");
+ await animationWithOptionsPage.assertItemPosition("Wonder Woman", 1, examplesCount);
+ await animationWithOptionsPage.assertItemPosition("Joker", 2, examplesCount);
+ await animationWithOptionsPage.assertItemPosition("Aquaman", 3, examplesCount);
+
+ await animationWithOptionsPage.addItem();
+ examplesCount++;
+ await animationWithOptionsPage.awaitItemToAppear("Harley Quinn");
+ await animationWithOptionsPage.assertItemPosition("Wonder Woman", 1, examplesCount);
+ await animationWithOptionsPage.assertItemPosition("Joker", 2, examplesCount);
+ await animationWithOptionsPage.assertItemPosition("Aquaman", 3, examplesCount);
+ await animationWithOptionsPage.assertItemPosition("Harley Quinn", 4, examplesCount);
+ });
+
+ it("animate child", async function () {
+ this.retries(1);
+ const animateChildPage = new AnimateChildPage(driver);
+ await animateChildPage.enterExample();
+ await animateChildPage.waitParentToAppear();
+ await animateChildPage.waitChildToAppear();
+ await animateChildPage.assertContainersPosition();
+ });
+
+ it("angular docs", async function () {
+ const heroPage = new HeroPage(driver);
+ await heroPage.enterExample();
+ await heroPage.addActive();
+ let result = await driver.compareScreen("add_active_items", 5);
+
+ await heroPage.addInactive();
+ result = await driver.compareScreen("add_inactive_items", 5) && result;
+
+ await heroPage.remove();
+ result = await driver.compareScreen("add_remove_items", 5) && result;
+
+ await heroPage.reset();
+ result = await driver.compareScreen("add_reset_items", 5) && result;
+
+ assert.isTrue(result, "Image verification failed!");
+
+ });
+});
\ No newline at end of file
diff --git a/tests/e2e/tsconfig.json b/e2e/animation-examples/e2e/tsconfig.json
similarity index 79%
rename from tests/e2e/tsconfig.json
rename to e2e/animation-examples/e2e/tsconfig.json
index 99f58b8e6..6517ca58d 100644
--- a/tests/e2e/tsconfig.json
+++ b/e2e/animation-examples/e2e/tsconfig.json
@@ -1,10 +1,10 @@
{
"compilerOptions": {
"module": "commonjs",
- "target": "es5",
+ "target": "es6",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
- "importHelpers": true,
+ "importHelpers": false,
"sourceMap": true,
"types": [
"node",
@@ -12,7 +12,7 @@
"chai"
],
"lib": [
- "es6",
+ "es2015",
"dom"
]
}
diff --git a/e2e/animation-examples/nativescript.config.ts b/e2e/animation-examples/nativescript.config.ts
new file mode 100644
index 000000000..d117ed3e9
--- /dev/null
+++ b/e2e/animation-examples/nativescript.config.ts
@@ -0,0 +1,11 @@
+import { NativeScriptConfig } from '@nativescript/core'
+
+export default {
+ id: 'org.nativescript.ng4animations',
+ appResourcesPath: 'app/App_Resources',
+ android: {
+ v8Flags: '--expose_gc',
+ markingMode: 'none',
+ },
+ appPath: 'app',
+} as NativeScriptConfig
diff --git a/e2e/animation-examples/package.json b/e2e/animation-examples/package.json
new file mode 100644
index 000000000..a56c99369
--- /dev/null
+++ b/e2e/animation-examples/package.json
@@ -0,0 +1,53 @@
+{
+ "description": "NativeScript Application",
+ "main": "main.js",
+ "license": "SEE LICENSE IN ",
+ "readme": "NativeScript Application",
+ "repository": "",
+ "dependencies": {
+ "@angular/animations": "~11.0.0",
+ "@angular/common": "~11.0.0",
+ "@angular/compiler": "~11.0.0",
+ "@angular/core": "~11.0.0",
+ "@angular/forms": "~11.0.0",
+ "@angular/platform-browser": "~11.0.0",
+ "@angular/platform-browser-dynamic": "~11.0.0",
+ "@angular/router": "~11.0.0",
+ "@nativescript/angular": "file:../../dist/nativescript-angular-scoped.tgz",
+ "nativescript-theme-core": "~1.0.2",
+ "reflect-metadata": "~0.1.13",
+ "rxjs": "~6.6.0",
+ "@nativescript/core": "~7.0.0",
+ "zone.js": "^0.11.1"
+ },
+ "devDependencies": {
+ "@angular/compiler-cli": "~11.0.0",
+ "@nativescript/ios": "7.0.5",
+ "@nativescript/webpack": "~3.0.0",
+ "@ngtools/webpack": "~11.0.0",
+ "@types/chai": "~4.2.0",
+ "@types/mocha": "~7.0.0",
+ "@types/node": "~14.0.0",
+ "babel-traverse": "~6.26.0",
+ "babel-types": "~6.26.0",
+ "babylon": "~6.18.0",
+ "chai": "^4.2.0",
+ "lazy": "~1.0.11",
+ "mocha": "~8.0.1",
+ "mochawesome": "~6.1.1",
+ "nativescript-css-loader": "~0.26.0",
+ "node-sass": "~4.14.1",
+ "typescript": "~4.0.0"
+ },
+ "scripts": {
+ "clean": "ns clean",
+ "setup": "cd ../../nativescript-angular && npm run prep.apps && cd ../e2e/animation-examples && npm run clean",
+ "u": "update-ns-webpack",
+ "e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
+ "e2e-watch": "tsc -p e2e --watch",
+ "ns-verify-bundle": "ns-verify-bundle",
+ "update-ns-webpack": "update-ns-webpack",
+ "ios": "ns debug ios --emulator --no-hmr",
+ "android": "ns debug android --emulator --no-hmr"
+ }
+}
diff --git a/ng-sample/tsconfig.json b/e2e/animation-examples/tsconfig.json
similarity index 52%
rename from ng-sample/tsconfig.json
rename to e2e/animation-examples/tsconfig.json
index 7926095e1..ff6adb22e 100644
--- a/ng-sample/tsconfig.json
+++ b/e2e/animation-examples/tsconfig.json
@@ -1,30 +1,32 @@
{
"compilerOptions": {
- "module": "commonjs",
+ "module": "ESNext",
+ "target": "es2015",
"moduleResolution": "node",
- "target": "es5",
- "inlineSourceMap": true,
"experimentalDecorators": true,
- "removeComments": false,
"emitDecoratorMetadata": true,
- "noImplicitUseStrict": true,
"noEmitHelpers": true,
"noEmitOnError": true,
+ "skipLibCheck": true,
"lib": [
- "es6",
+ "es2017",
"dom",
- "es2015.iterable"
+ "es6"
],
"baseUrl": ".",
"paths": {
- "*": [
- "./node_modules/tns-core-modules/*",
- "./node_modules/*"
+ "~/*": [
+ "app/*"
]
}
},
+ "files": [
+ "./app/main.ts"
+ ],
"exclude": [
"node_modules",
- "platforms"
+ "platforms",
+ "**/*.aot",
+ "e2e"
]
}
\ No newline at end of file
diff --git a/e2e/config/appium.capabilities.json b/e2e/config/appium.capabilities.json
index 822dd9d0b..695030f6a 100644
--- a/e2e/config/appium.capabilities.json
+++ b/e2e/config/appium.capabilities.json
@@ -4,20 +4,16 @@
"platformVersion": "4.4",
"deviceName": "test",
"lt": 60000,
- "appActivity": "com.tns.NativeScriptActivity",
"newCommandTimeout": 720,
"noReset": true,
- "fullReset": false,
- "app": ""
+ "fullReset": false
},
"nexus5": {
"browserName": "",
"platformName": "Android",
"platformVersion": "6.0",
"deviceName": "device",
- "udid": "077e4a47003b7698",
- "appActivity": "com.tns.NativeScriptActivity",
- "app": ""
+ "udid": "077e4a47003b7698"
},
"android19": {
"platformName": "Android",
@@ -25,11 +21,9 @@
"deviceName": "Emulator-Api19-Default",
"avd": "Emulator-Api19-Default",
"lt": 60000,
- "appActivity": "com.tns.NativeScriptActivity",
"newCommandTimeout": 720,
"noReset": false,
- "fullReset": false,
- "app": ""
+ "fullReset": false
},
"android21": {
"platformName": "Android",
@@ -37,11 +31,34 @@
"deviceName": "Emulator-Api21-Default",
"avd": "Emulator-Api21-Default",
"lt": 60000,
- "appActivity": "com.tns.NativeScriptActivity",
"newCommandTimeout": 720,
"noReset": false,
+ "fullReset": false
+ },
+ "android22": {
+ "platformName": "Android",
+ "platformVersion": "5.1",
+ "deviceName": "Emulator-Api22-Default",
+ "avd": "Emulator-Api22-Default",
+ "lt": 60000,
+ "newCommandTimeout": 720,
+ "noReset": false,
+ "fullReset": false
+ },
+ "android22.sauce": {
+ "platformName": "Android",
+ "platformVersion": "5.1",
+ "deviceName": "Android GoogleAPI Emulator",
+ "lt": 60000,
+ "newCommandTimeout": 720,
+ "appiumVersion": "1.13.0",
+ "noReset": true,
"fullReset": false,
- "app": ""
+ "app": "",
+ "idleTimeout": 120,
+ "automationName": "Appium",
+ "density":3.2,
+ "offsetPixels":51
},
"android23": {
"platformName": "Android",
@@ -49,11 +66,9 @@
"deviceName": "Emulator-Api23-Default",
"avd": "Emulator-Api23-Default",
"lt": 60000,
- "appActivity": "com.tns.NativeScriptActivity",
"newCommandTimeout": 720,
"noReset": false,
- "fullReset": false,
- "app": ""
+ "fullReset": false
},
"android24": {
"platformName": "Android",
@@ -61,11 +76,9 @@
"deviceName": "Emulator-Api24-Default",
"avd": "Emulator-Api24-Default",
"lt": 60000,
- "appActivity": "com.tns.NativeScriptActivity",
"newCommandTimeout": 720,
"noReset": false,
- "fullReset": false,
- "app": ""
+ "fullReset": false
},
"android25": {
"platformName": "Android",
@@ -73,11 +86,9 @@
"deviceName": "Emulator-Api25-Google",
"avd": "Emulator-Api25-Google",
"lt": 60000,
- "appActivity": "com.tns.NativeScriptActivity",
"newCommandTimeout": 720,
"noReset": false,
- "fullReset": false,
- "app": ""
+ "fullReset": false
},
"android26": {
"platformName": "Android",
@@ -86,11 +97,9 @@
"avd": "Emulator-Api26-Google",
"lt": 60000,
"automationName": "UIAutomator2",
- "appActivity": "com.tns.NativeScriptActivity",
"newCommandTimeout": 720,
"noReset": false,
- "fullReset": false,
- "app": ""
+ "fullReset": false
},
"android27": {
"platformName": "Android",
@@ -98,42 +107,118 @@
"deviceName": "Emulator-Api27-Google",
"avd": "Emulator-Api27-Google",
"lt": 60000,
- "appActivity": "com.tns.NativeScriptActivity",
"newCommandTimeout": 720,
"noReset": true,
- "fullReset": false,
- "app": ""
+ "fullReset": false
+ },
+ "android28": {
+ "platformName": "Android",
+ "platformVersion": "28",
+ "deviceName": "Emulator-Api28-Google",
+ "avd": "Emulator-Api28-Google",
+ "lt": 60000,
+ "newCommandTimeout": 720,
+ "noReset": true,
+ "fullReset": false
+ },
+ "android29": {
+ "platformName": "Android",
+ "platformVersion": "29",
+ "deviceName": "Emulator-Api29-Google",
+ "avd": "Emulator-Api29-Google",
+ "lt": 60000,
+ "newCommandTimeout": 720,
+ "noReset": true,
+ "fullReset": false
},
"sim.iPhone7.iOS100": {
"platformName": "iOS",
"platformVersion": "10.0",
"deviceName": "iPhone 7 100",
"noReset": true,
- "fullReset": false,
- "app": ""
+ "fullReset": false
},
"sim.iPhone7.iOS110": {
"platformName": "iOS",
- "platformVersion": "11.2",
+ "platformVersion": "11.4",
"deviceName": "iPhone 7 110",
"noReset": true,
- "fullReset": false,
- "app": ""
+ "fullReset": false
+ },
+ "sim.iPhone7.ios12": {
+ "platformName": "iOS",
+ "platformVersion": "12.0",
+ "deviceName": "iPhone 7",
+ "noReset": true,
+ "fullReset": false
},
"sim.iPhone8.iOS112": {
"platformName": "iOS",
"platformVersion": "11.2",
"deviceName": "iPhone 8 112",
"noReset": true,
- "fullReset": false,
- "app": ""
+ "fullReset": false
+ },
+ "sim.iPhone8": {
+ "platformName": "iOS",
+ "platformVersion": "12",
+ "deviceName": "iPhone 8",
+ "noReset": true,
+ "fullReset": false
},
"sim.iPhoneX.iOS110": {
"platformName": "iOS",
"platformVersion": "11.2",
"deviceName": "iPhone X 110",
"noReset": true,
+ "fullReset": false
+ },
+ "sim.iPhoneX": {
+ "platformName": "iOS",
+ "platformVersion": "12.0",
+ "deviceName": "iPhone X",
+ "noReset": true,
+ "fullReset": false
+ },
+ "sim.iPhoneXS": {
+ "platformName": "iOS",
+ "platformVersion": "12.0",
+ "deviceName": "iPhone XS",
+ "noReset": true,
+ "fullReset": false
+ },
+ "sim.iPhoneXS.latest": {
+ "platformName": "iOS",
+ "platformVersion": "12.1",
+ "deviceName": "iPhone XS",
+ "noReset": true,
+ "fullReset": false
+ },
+ "sim.iPhoneXR": {
+ "platformName": "iOS",
+ "platformVersion": "12.0",
+ "deviceName": "iPhone XR",
+ "noReset": true,
+ "fullReset": false
+ },
+ "sim.iPhoneXR.ios12": {
+ "platformName": "iOS",
+ "platformVersion": "12.0",
+ "deviceName": "iPhone XR 12",
+ "noReset": true,
+ "fullReset": false
+ },
+ "sim.iPhoneXR.ios13": {
+ "platformName": "iOS",
+ "platformVersion": "13.0",
+ "deviceName": "iPhone XR 13",
+ "appiumVersion": "1.15.0",
+ "noReset": true,
"fullReset": false,
- "app": ""
+ "density": 3,
+ "offsetPixels": 87,
+ "app": "",
+ "idleTimeout": 120,
+ "automationName": "Appium"
}
-}
+}
\ No newline at end of file
diff --git a/e2e/config/mocha.opts b/e2e/config/mocha.opts
index ea635b0dc..92a64e8fe 100644
--- a/e2e/config/mocha.opts
+++ b/e2e/config/mocha.opts
@@ -1,4 +1,5 @@
---timeout 140000
+--timeout 120000
--recursive e2e
---reporter mocha-multi
---reporter-options spec=-,mocha-junit-reporter=test-results.xml
\ No newline at end of file
+--reporter mochawesome
+--reporter-options quiet=true,html=true,inline=true
+--exit
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/.gitignore b/e2e/modal-navigation-ng/.gitignore
new file mode 100644
index 000000000..23d2d7b1d
--- /dev/null
+++ b/e2e/modal-navigation-ng/.gitignore
@@ -0,0 +1,9 @@
+platforms
+node_modules
+hooks
+
+app/**/*.js
+e2e/**/*.js
+e2e/reports
+test-results.xml
+mochawesome-report
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/app.gradle b/e2e/modal-navigation-ng/app/App_Resources/Android/app.gradle
new file mode 100644
index 000000000..924f7c70f
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/App_Resources/Android/app.gradle
@@ -0,0 +1,16 @@
+// Add your native dependencies here:
+
+// Uncomment to add recyclerview-v7 dependency
+//dependencies {
+// compile 'com.android.support:recyclerview-v7:+'
+//}
+
+android {
+ defaultConfig {
+ generatedDensities = []
+ applicationId = "org.nativescript.modalnavigationng"
+ }
+ aaptOptions {
+ additionalParameters "--no-version-vectors"
+ }
+}
diff --git a/e2e/renderer/app/App_Resources/Android/AndroidManifest.xml b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/AndroidManifest.xml
similarity index 100%
rename from e2e/renderer/app/App_Resources/Android/AndroidManifest.xml
rename to e2e/modal-navigation-ng/app/App_Resources/Android/src/main/AndroidManifest.xml
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png
new file mode 100644
index 000000000..eb381c258
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png
new file mode 100755
index 000000000..9cde84cd5
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png
new file mode 100644
index 000000000..5218f4c90
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png differ
diff --git a/e2e/router/app/App_Resources/Android/drawable-nodpi/background.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png
similarity index 100%
rename from e2e/router/app/App_Resources/Android/drawable-nodpi/background.png
rename to e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png
new file mode 100755
index 000000000..4d6a674b3
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png differ
diff --git a/e2e/router/app/App_Resources/Android/drawable-nodpi/logo.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png
similarity index 100%
rename from e2e/router/app/App_Resources/Android/drawable-nodpi/logo.png
rename to e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png
new file mode 100644
index 000000000..efeaf2907
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png
new file mode 100755
index 000000000..92ccc85a6
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png
new file mode 100644
index 000000000..626338766
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png differ
diff --git a/e2e/router/app/App_Resources/Android/drawable-nodpi/splash_screen.xml b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
similarity index 100%
rename from e2e/router/app/App_Resources/Android/drawable-nodpi/splash_screen.xml
rename to e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png
new file mode 100644
index 000000000..612bbd072
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png
new file mode 100644
index 000000000..8bcde6277
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png
new file mode 100644
index 000000000..ad8ee2f4b
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png
new file mode 100644
index 000000000..0fa88e235
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png
new file mode 100644
index 000000000..9d81c85dc
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png
new file mode 100644
index 000000000..668327832
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png
new file mode 100644
index 000000000..c650f6438
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png
new file mode 100644
index 000000000..9a34d0d43
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png
new file mode 100644
index 000000000..fa6331c8d
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png differ
diff --git a/e2e/router/app/App_Resources/Android/values-v21/colors.xml b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/values-v21/colors.xml
similarity index 100%
rename from e2e/router/app/App_Resources/Android/values-v21/colors.xml
rename to e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/values-v21/colors.xml
diff --git a/e2e/router/app/App_Resources/Android/values-v21/styles.xml b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/values-v21/styles.xml
similarity index 100%
rename from e2e/router/app/App_Resources/Android/values-v21/styles.xml
rename to e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/values-v21/styles.xml
diff --git a/e2e/router/app/App_Resources/Android/values/colors.xml b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/values/colors.xml
similarity index 100%
rename from e2e/router/app/App_Resources/Android/values/colors.xml
rename to e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/values/colors.xml
diff --git a/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/values/styles.xml b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/values/styles.xml
new file mode 100644
index 000000000..c793e6d4c
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/App_Resources/Android/src/main/res/values/styles.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..4034b76e6
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "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"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "icon-1024.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png
new file mode 100644
index 000000000..a1d7eb479
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png
new file mode 100644
index 000000000..bb9b9e86d
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png
new file mode 100644
index 000000000..ec609dcf3
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png
new file mode 100644
index 000000000..a97180800
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png
new file mode 100644
index 000000000..214800ee6
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
new file mode 100644
index 000000000..8554b88a8
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
new file mode 100644
index 000000000..a22626bae
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
new file mode 100644
index 000000000..a22626bae
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
new file mode 100644
index 000000000..492c9c8e8
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png
new file mode 100644
index 000000000..9208113cf
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
new file mode 100644
index 000000000..24415e5a3
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png
new file mode 100644
index 000000000..b3ef1bf0c
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/Contents.json b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/Contents.json
new file mode 100644
index 000000000..da4a164c9
--- /dev/null
+++ b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 000000000..11bfcf55c
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,176 @@
+{
+ "images" : [
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "2436h",
+ "filename" : "Default-1125h.png",
+ "minimum-system-version" : "11.0",
+ "orientation" : "portrait",
+ "scale" : "3x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "iphone",
+ "extent" : "full-screen",
+ "filename" : "Default-Landscape-X.png",
+ "minimum-system-version" : "11.0",
+ "subtype" : "2436h",
+ "scale" : "3x"
+ },
+ {
+ "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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png
new file mode 100644
index 000000000..2913f85d9
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
new file mode 100644
index 000000000..cd94a3ac2
Binary files /dev/null and b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png differ
diff --git a/ng-sample/app/examples/image/img/Default.png b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
old mode 100755
new mode 100644
similarity index 100%
rename from ng-sample/app/examples/image/img/Default.png
rename to e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
new file mode 100644
index 000000000..4f4e9c506
--- /dev/null
+++ b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json b/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
new file mode 100644
index 000000000..23c0ffd7a
--- /dev/null
+++ b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png differ
diff --git a/e2e/modal-navigation-ng/app/App_Resources/iOS/Info.plist b/e2e/modal-navigation-ng/app/App_Resources/iOS/Info.plist
new file mode 100644
index 000000000..ea3e3ea23
--- /dev/null
+++ b/e2e/modal-navigation-ng/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/modal-navigation-ng/app/App_Resources/iOS/LaunchScreen.storyboard b/e2e/modal-navigation-ng/app/App_Resources/iOS/LaunchScreen.storyboard
new file mode 100644
index 000000000..c4e5a3f39
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/App_Resources/iOS/LaunchScreen.storyboard
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ng-sample/app/App_Resources/iOS/build.xcconfig b/e2e/modal-navigation-ng/app/App_Resources/iOS/build.xcconfig
similarity index 53%
rename from ng-sample/app/App_Resources/iOS/build.xcconfig
rename to e2e/modal-navigation-ng/app/App_Resources/iOS/build.xcconfig
index 056205556..4b0118490 100644
--- a/ng-sample/app/App_Resources/iOS/build.xcconfig
+++ b/e2e/modal-navigation-ng/app/App_Resources/iOS/build.xcconfig
@@ -1,5 +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/modal-navigation-ng/app/CODE_OF_CONDUCT.md b/e2e/modal-navigation-ng/app/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..1c845d0b1
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/CODE_OF_CONDUCT.md
@@ -0,0 +1,83 @@
+# NativeScript Community Code of Conduct
+
+Our community members come from all walks of life and are all at different stages of their personal and professional journeys. To support everyone, we've prepared a short code of conduct. Our mission is best served in an environment that is friendly, safe, and accepting; free from intimidation or harassment.
+
+Towards this end, certain behaviors and practices will not be tolerated.
+
+## tl;dr
+
+- Be respectful.
+- We're here to help.
+- Abusive behavior is never tolerated.
+- Violations of this code may result in swift and permanent expulsion from the NativeScript community channels.
+
+## Administrators
+
+- Dan Wilson (@DanWilson on Slack)
+- Jen Looper (@jen.looper on Slack)
+- TJ VanToll (@tjvantoll on Slack)
+
+## Scope
+
+We expect all members of the NativeScript community, including administrators, users, facilitators, and vendors to abide by this Code of Conduct at all times in our community venues, online and in person, and in one-on-one communications pertaining to NativeScript affairs.
+
+This policy covers the usage of the NativeScript Slack community, as well as the NativeScript support forums, NativeScript GitHub repositories, the NativeScript website, and any NativeScript-related events. This Code of Conduct is in addition to, and does not in any way nullify or invalidate, any other terms or conditions related to use of NativeScript.
+
+The definitions of various subjective terms such as "discriminatory", "hateful", or "confusing" will be decided at the sole discretion of the NativeScript administrators.
+
+## Friendly, Harassment-Free Space
+
+We are committed to providing a friendly, safe, and welcoming environment for all, regardless of gender identity, sexual orientation, disability, ethnicity, religion, age, physical appearance, body size, race, or similar personal characteristics.
+
+We ask that you please respect that people have differences of opinion regarding technical choices, and acknowledge that every design or implementation choice carries a trade-off and numerous costs. There is seldom a single right answer. A difference of technology preferences is never a license to be rude.
+
+Any spamming, trolling, flaming, baiting, or other attention-stealing behaviour is not welcome, and will not be tolerated.
+
+Harassing other users of NativeScript is never tolerated, whether via public or private media.
+
+Avoid using offensive or harassing package names, nicknames, or other identifiers that might detract from a friendly, safe, and welcoming environment for all.
+
+Harassment includes, but is not limited to: harmful or prejudicial verbal or written comments related to gender identity, sexual orientation, disability, ethnicity, religion, age, physical appearance, body size, race, or similar personal characteristics; inappropriate use of nudity, sexual images, and/or sexually explicit language in public spaces; threats of physical or non-physical harm; deliberate intimidation, stalking or following; harassing photography or recording; sustained disruption of talks or other events; inappropriate physical contact; and unwelcome sexual attention.
+
+## Acceptable Content
+
+The NativeScript administrators reserve the right to make judgement calls about what is and isn't appropriate in published content. These are guidelines to help you be successful in our community.
+
+Content must contain something applicable to the previously stated goals of the NativeScript community. "Spamming", that is, publishing any form of content that is not applicable, is not allowed.
+
+Content must not contain illegal or infringing content. You should only publish content to NativeScript properties if you have the right to do so. This includes complying with all software license agreements or other intellectual property restrictions. For example, redistributing an MIT-licensed module with the copyright notice removed, would not be allowed. You will be responsible for any violation of laws or others’ intellectual property rights.
+
+Content must not be malware. For example, content (code, video, pictures, words, etc.) which is designed to maliciously exploit or damage computer systems, is not allowed.
+
+Content name, description, and other visible metadata must not include abusive, inappropriate, or harassing content.
+
+## Reporting Violations of this Code of Conduct
+
+If you believe someone is harassing you or has otherwise violated this Code of Conduct, please contact the administrators and send us an abuse report. If this is the initial report of a problem, please include as much detail as possible. It is easiest for us to address issues when we have more context.
+
+## Consequences
+
+All content published to the NativeScript community channels is hosted at the sole discretion of the NativeScript administrators.
+
+Unacceptable behavior from any community member, including sponsors, employees, customers, or others with decision-making authority, will not be tolerated.
+
+Anyone asked to stop unacceptable behavior is expected to comply immediately.
+
+If a community member engages in unacceptable behavior, the NativeScript administrators may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event or service).
+
+## Addressing Grievances
+
+If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify the administrators. We will do our best to ensure that your grievance is handled appropriately.
+
+In general, we will choose the course of action that we judge as being most in the interest of fostering a safe and friendly community.
+
+## Contact Info
+Please contact Dan Wilson @DanWilson if you need to report a problem or address a grievance related to an abuse report.
+
+You are also encouraged to contact us if you are curious about something that might be "on the line" between appropriate and inappropriate content. We are happy to provide guidance to help you be a successful part of our community.
+
+## Credit and License
+
+This Code of Conduct borrows heavily from the WADE Code of Conduct, which is derived from the NodeBots Code of Conduct, which in turn borrows from the npm Code of Conduct, which was derived from the Stumptown Syndicate Citizen's Code of Conduct, and the Rust Project Code of Conduct.
+
+This document may be reused under a Creative Commons Attribution-ShareAlike License.
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/LICENSE b/e2e/modal-navigation-ng/app/LICENSE
new file mode 100755
index 000000000..ced13b45c
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/LICENSE
@@ -0,0 +1,201 @@
+Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright (c) 2015-2018 Telerik AD
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/README.md b/e2e/modal-navigation-ng/app/README.md
new file mode 100644
index 000000000..132ede3b0
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/README.md
@@ -0,0 +1,25 @@
+# NativeScript Angular Template
+
+This template creates a "Hello, world" NativeScript app using TypeScript and Angular.
+
+You can create a new app that uses this template with either the `--template` option.
+
+```
+tns create my-app-name --template tns-template-hello-world-ng
+```
+
+Or the `--ng` shorthand.
+
+```
+tns create my-app-name --ng
+```
+
+> Note: Both commands will create a new NativeScript app that uses the latest version of this template published to [npm] (https://www.npmjs.com/package/tns-template-hello-world-ng).
+
+If you want to create a new app that uses the source of the template from the `master` branch, you can execute the following:
+
+```
+tns create my-app-name --template https://github.com/NativeScript/template-hello-world-ng.git#master
+```
+
+**NB:** Please, have in mind that the master branch may refer to dependencies that are not on NPM yet!
diff --git a/e2e/modal-navigation-ng/app/app.android.css b/e2e/modal-navigation-ng/app/app.android.css
new file mode 100644
index 000000000..529b4cbd8
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/app.android.css
@@ -0,0 +1,12 @@
+Button{
+ font-size: 8;
+ margin: 0px;
+ padding: 0px;
+ height: 40;
+}
+
+TextView {
+ font-size: 10;
+ margin: 0px;
+ padding: 0px;
+}
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/app.component.html b/e2e/modal-navigation-ng/app/app.component.html
new file mode 100644
index 000000000..8a2c1a75e
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/app.component.html
@@ -0,0 +1,2 @@
+
+
diff --git a/e2e/modal-navigation-ng/app/app.component.ts b/e2e/modal-navigation-ng/app/app.component.ts
new file mode 100644
index 000000000..c5617460f
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/app.component.ts
@@ -0,0 +1,50 @@
+import { Component, ViewContainerRef } from "@angular/core";
+import { Router, NavigationEnd } from "@angular/router";
+import { NSLocationStrategy } from "@nativescript/angular";
+
+import { ViewContainerRefService } from "./shared/ViewContainerRefService";
+
+import { AppModule } from "./app.module";
+import { ModalDialogOptions, ModalDialogService } from "@nativescript/angular";
+import { ModalViewComponent } from "./modal-shared/modal-view.component";
+
+@Component({
+ selector: "ns-app",
+ templateUrl: "app.component.html"
+})
+export class AppComponent {
+ constructor(
+ router: Router,
+ location: NSLocationStrategy,
+ private modal: ModalDialogService,
+ private _vcRef: ViewContainerRef,
+ private _viewContainerRefService: ViewContainerRefService) {
+ router.events.subscribe(e => {
+ if (e instanceof NavigationEnd) {
+ console.log("[ROUTER]: " + e.toString());
+ console.log(location.toString());
+ }
+ });
+
+ this._viewContainerRefService.root = this._vcRef;
+ }
+
+ ngOnInit() {
+ if (AppModule.root === "page-router-modal") {
+ this.onRootModalTap();
+ console.log("Page modal page from frame root");
+ }
+ }
+
+ onRootModalTap(): void {
+ const options: ModalDialogOptions = {
+ viewContainerRef: this._viewContainerRefService.root,
+ context: {},
+ fullscreen: true
+ };
+
+ this.modal.showModal(ModalViewComponent, options).then((result: string) => {
+ console.log(result);
+ });
+ }
+}
diff --git a/e2e/modal-navigation-ng/app/app.css b/e2e/modal-navigation-ng/app/app.css
new file mode 100644
index 000000000..d132e79b9
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/app.css
@@ -0,0 +1,14 @@
+/*
+In NativeScript, the app.css file is where you place CSS rules that
+you would like to apply to your entire application. Check out
+http://docs.nativescript.org/ui/styling for a full list of the CSS
+selectors and properties you can use to style UI components.
+
+/*
+In many cases you may want to use the NativeScript core theme instead
+of writing your own CSS rules. For a full list of class names in the theme
+refer to http://docs.nativescript.org/ui/theme.
+*/
+Button {
+ font-size: 10px;
+}
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/app.ios.css b/e2e/modal-navigation-ng/app/app.ios.css
new file mode 100644
index 000000000..16f77c1ba
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/app.ios.css
@@ -0,0 +1,12 @@
+#home-page {
+ font-size: 10;
+ margin: 0px;
+ padding: 0px;
+}
+#home-page Button {
+ margin-bottom: 20px;
+ padding: 20px;
+ border-color: gray;
+ border-width: 2px;
+ border-radius: 8px;
+}
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/app.module.ngfactory.d.ts b/e2e/modal-navigation-ng/app/app.module.ngfactory.d.ts
new file mode 100644
index 000000000..793157de3
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/app.module.ngfactory.d.ts
@@ -0,0 +1,4 @@
+/**
+ * A dynamically generated module when compiled with AoT.
+ */
+export const AppModuleNgFactory: any;
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/app.module.ts b/e2e/modal-navigation-ng/app/app.module.ts
new file mode 100644
index 000000000..78e2e08ea
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/app.module.ts
@@ -0,0 +1,97 @@
+import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+import { NativeScriptModule } from "@nativescript/angular";
+import { AppRoutingModule } from "./app.routing";
+import { AppComponent } from "./app.component";
+import { NamedRouterComponent } from "./named-router.component";
+import { TabComponent } from "./tab.component";
+import { LayoutComponent } from "./layout.component";
+
+import { HomeComponent } from "./home/home.component";
+import { RootSectionComponent } from "./navigation/root.section.component";
+import { BasicsNavigationComponent } from "./navigation/basic.navigation.component";
+import { SecondComponent } from "./second/second.component";
+import { ModalSecondComponent } from "./modal-second/modal-second.component";
+import { ModalComponent } from "./modal/modal.component";
+import { NestedModalComponent } from "./modal-nested/modal-nested.component";
+import { ModalRouterComponent } from "./modal/modal-router/modal-router.component";
+import { ModalViewComponent } from "./modal-shared/modal-view.component";
+import { ModalViewContentComponent } from "./modal-shared/modal-view-content.component";
+import { ModalSharedSecondComponent } from "./modal-shared/modal-shared-second.component";
+import { ViewContainerRefService } from "./shared/ViewContainerRefService";
+
+import { enable as traceEnable, addCategories } from "@nativescript/core/trace";
+import { routerTraceCategory } from "@nativescript/angular/trace";
+import { NativeScriptPlatformRef } from "@nativescript/angular";
+
+addCategories(routerTraceCategory);
+traceEnable();
+
+@NgModule({
+ imports: [
+ NativeScriptModule,
+ AppRoutingModule
+ ],
+ entryComponents: [
+ AppComponent,
+ NamedRouterComponent,
+ TabComponent,
+ LayoutComponent,
+ ModalRouterComponent,
+ NestedModalComponent,
+ ModalComponent,
+ ModalViewComponent
+ ],
+ declarations: [
+ AppComponent,
+ NamedRouterComponent,
+ TabComponent,
+ LayoutComponent,
+ RootSectionComponent,
+ BasicsNavigationComponent,
+ HomeComponent,
+ SecondComponent,
+ ModalComponent,
+ NestedModalComponent,
+ ModalRouterComponent,
+ ModalSecondComponent,
+ ModalViewComponent,
+ ModalViewContentComponent,
+ ModalSharedSecondComponent
+ ],
+ providers: [
+ ViewContainerRefService
+ ],
+ schemas: [
+ NO_ERRORS_SCHEMA
+ ]
+})
+/*
+Pass your application module to the bootstrapModule function located in main.ts to start your app
+*/
+
+export class AppModule {
+ private static appRef: any;
+ public static platformRef: NativeScriptPlatformRef;
+ public static root: string = "page-router";
+
+ ngDoBootstrap(app) {
+ AppModule.appRef = app;
+ AppModule.bootstrapRootComponent();
+ }
+
+ static bootstrapRootComponent() {
+ const options = {
+ 'page-router': AppComponent,
+ 'page-router-modal': AppComponent,
+ 'named-page-router': NamedRouterComponent,
+ 'named-page-router-modal': NamedRouterComponent,
+ 'tab': TabComponent,
+ 'tab-modal': TabComponent,
+ 'layout': LayoutComponent,
+ 'layout-modal': LayoutComponent,
+ };
+
+ const component = options[AppModule.root];
+ AppModule.appRef.bootstrap(component);
+ }
+}
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/app.routing.ts b/e2e/modal-navigation-ng/app/app.routing.ts
new file mode 100644
index 000000000..f03ef2a1a
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/app.routing.ts
@@ -0,0 +1,144 @@
+import { NgModule } from "@angular/core";
+import { NativeScriptRouterModule } from "@nativescript/angular";
+import { Routes, Router } from "@angular/router";
+
+import { HomeComponent } from "./home/home.component";
+import { SecondComponent } from "./second/second.component";
+import { ModalSecondComponent } from "./modal-second/modal-second.component";
+import { ModalComponent } from "./modal/modal.component";
+import { NestedModalComponent } from "./modal-nested/modal-nested.component";
+import { ModalViewContentComponent } from "./modal-shared/modal-view-content.component";
+import { ModalSharedSecondComponent } from "./modal-shared/modal-shared-second.component";
+
+import { AppModule } from "./app.module";
+
+const routes: Routes = [
+ { path: "", redirectTo: "/home", pathMatch: "full" },
+ {
+ path: "home", component: HomeComponent, children: [
+ {
+ path: "modal", component: ModalComponent, children: [
+ { path: "nested-frame-modal", component: NestedModalComponent }]
+ },
+ { path: "modal-second", component: ModalSecondComponent }
+ ]
+ },
+ {
+ path: "second", component: SecondComponent, children: [
+ {
+ path: "modal", component: ModalComponent, children: [
+ { path: "nested-frame-modal", component: NestedModalComponent }]
+ },
+ { path: "modal-second", component: ModalSecondComponent }
+ ]
+ },
+ {
+ path: "modal-shared", component: ModalViewContentComponent, outlet: "modalOutlet"
+ },
+ {
+ path: "modal-shared-second-host", component: ModalSharedSecondComponent
+ }
+];
+
+const namedOutletRoutes: Routes = [
+ { path: "", redirectTo: "/(namedRouter:home)", pathMatch: "full" },
+ {
+ path: "home", component: HomeComponent, outlet: "namedRouter", children: [
+ {
+ path: "modal", component: ModalComponent, children: [
+ { path: "nested-frame-modal", component: NestedModalComponent }]
+ },
+ { path: "modal-second", component: ModalSecondComponent }
+ ]
+ },
+ {
+ path: "second", outlet: "namedRouter", component: SecondComponent, children: [
+ {
+ path: "modal", component: ModalComponent, children: [
+ { path: "nested-frame-modal", component: NestedModalComponent }]
+ },
+ { path: "modal-second", component: ModalSecondComponent }
+ ]
+ },
+ {
+ path: "modal-shared", component: ModalViewContentComponent, outlet: "modalOutlet"
+ },
+ {
+ path: "modal-shared-second-host", outlet: "namedRouter", component: ModalSharedSecondComponent
+ }
+];
+
+const routesTab: Routes = [
+ { path: "", redirectTo: "/home(secondOutlet:second)", pathMatch: "full" },
+ {
+ path: "home", component: HomeComponent, children: [
+ {
+ path: "modal", component: ModalComponent, children: [
+ { path: "nested-frame-modal", component: NestedModalComponent }]
+ },
+ {
+ path: "modal-second", component: ModalSecondComponent
+ }
+ ]
+ },
+ {
+ path: "second", component: SecondComponent, children: [
+ {
+ path: "modal", component: ModalComponent, children: [
+ { path: "nested-frame-modal", component: NestedModalComponent }]
+ },
+ {
+ path: "modal-second", component: ModalSecondComponent
+ }
+ ]
+ },
+ {
+ path: "second", outlet: "secondOutlet", component: SecondComponent, children: [
+ {
+ path: "modal", component: ModalComponent, children: [
+ { path: "nested-frame-modal", component: NestedModalComponent }]
+ },
+ {
+ path: "modal-second", component: ModalSecondComponent
+ }
+ ]
+ },
+ {
+ path: "modal-shared", component: ModalViewContentComponent, outlet: "modalOutlet"
+ },
+ {
+ path: "modal-shared-second-host", component: ModalSharedSecondComponent
+ }
+];
+
+const routesLayout: Routes = [
+ {
+ path: "modal", component: ModalComponent, children: [
+ { path: "nested-frame-modal", component: NestedModalComponent }]
+ },
+ { path: "modal-second", component: ModalSecondComponent },
+ {
+ path: "modal-shared", component: ModalViewContentComponent, outlet: "modalOutlet"
+ },
+ {
+ path: "modal-shared-second-host", component: ModalSharedSecondComponent
+ }
+]
+
+@NgModule({
+ imports: [NativeScriptRouterModule.forRoot(routes)],
+ exports: [NativeScriptRouterModule]
+})
+export class AppRoutingModule {
+ constructor(private router: Router) {
+ if (AppModule.root === "page-router" || AppModule.root === "page-router-modal") {
+ this.router.resetConfig(routes);
+ } else if (AppModule.root === "layout" || AppModule.root === "layout-modal") {
+ this.router.resetConfig(routesLayout);
+ } else if (AppModule.root === "named-page-router" || AppModule.root === "named-page-router-modal") {
+ this.router.resetConfig(namedOutletRoutes);
+ } else if(AppModule.root === "tab" || AppModule.root === "tab-modal"){
+ this.router.resetConfig(routesTab);
+ }
+ }
+}
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/home/home.component.html b/e2e/modal-navigation-ng/app/home/home.component.html
new file mode 100644
index 000000000..89de6c594
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/home/home.component.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/home/home.component.ts b/e2e/modal-navigation-ng/app/home/home.component.ts
new file mode 100644
index 000000000..49c26d63d
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/home/home.component.ts
@@ -0,0 +1,34 @@
+import { Component, ViewContainerRef } from "@angular/core";
+import { ModalDialogService, ModalDialogOptions, RouterExtensions } from "@nativescript/angular";
+import { EventData } from "@nativescript/core";
+
+import { ViewContainerRefService } from "../shared/ViewContainerRefService";
+import { ModalRouterComponent } from "../modal/modal-router/modal-router.component";
+import { ModalComponent } from "../modal/modal.component";
+import { ModalViewComponent } from "../modal-shared/modal-view.component";
+import { confirm } from "@nativescript/core/ui/dialogs";
+
+import { AppModule } from "../app.module";
+
+@Component({
+ moduleId: module.id,
+ selector: "home-page",
+ templateUrl: "./home.component.html"
+})
+export class HomeComponent {
+ constructor(
+ private modal: ModalDialogService,
+ private vcRef: ViewContainerRef,
+ private viewContainerRefService: ViewContainerRefService,
+ private routerExtension: RouterExtensions) {
+
+ }
+
+ onNavigateSecond() {
+ this.routerExtension.navigate(["second"]);
+ }
+
+ onNavigateSecondWithOutlet() {
+ this.routerExtension.navigate([ { outlets: { namedRouter:["second"] } }]);
+ }
+}
diff --git a/e2e/modal-navigation-ng/app/layout.component.html b/e2e/modal-navigation-ng/app/layout.component.html
new file mode 100644
index 000000000..c512f39e2
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/layout.component.html
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/layout.component.ts b/e2e/modal-navigation-ng/app/layout.component.ts
new file mode 100644
index 000000000..e31c18856
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/layout.component.ts
@@ -0,0 +1,49 @@
+import { Component, ViewContainerRef } from "@angular/core";
+import { Router, NavigationEnd } from "@angular/router";
+import { NSLocationStrategy } from "@nativescript/angular";
+import { ModalDialogService, ModalDialogOptions } from "@nativescript/angular";
+import { ModalViewComponent } from "./modal-shared/modal-view.component";
+import { ViewContainerRefService } from "./shared/ViewContainerRefService";
+import { AppModule } from "./app.module";
+
+@Component({
+ selector: "ns-layout",
+ templateUrl: "layout.component.html",
+})
+
+export class LayoutComponent {
+ constructor(
+ private modal: ModalDialogService,
+ private router: Router,
+ private location: NSLocationStrategy,
+ private vcRef: ViewContainerRef,
+ private viewContainerRefService: ViewContainerRefService) {
+ this.router.events.subscribe(e => {
+ if (e instanceof NavigationEnd) {
+ console.log("[ROUTER]: " + e.toString());
+ console.log(this.location.toString());
+ }
+ });
+
+ this.viewContainerRefService.root = this.vcRef;
+ }
+
+ ngOnInit() {
+ if (AppModule.root === "layout-modal") {
+ console.log("Show modal page from tab root view!");
+ this.onRootModalTap();
+ }
+ }
+
+ onRootModalTap(): void {
+ const options: ModalDialogOptions = {
+ viewContainerRef: this.viewContainerRefService.root,
+ context: {},
+ fullscreen: true
+ };
+
+ this.modal.showModal(ModalViewComponent, options).then((result: string) => {
+ console.log(result);
+ });
+ }
+}
diff --git a/e2e/modal-navigation-ng/app/main.ts b/e2e/modal-navigation-ng/app/main.ts
new file mode 100644
index 000000000..36e8adda7
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/main.ts
@@ -0,0 +1,8 @@
+// this import should be first in order to load some required settings (like globals and reflect-metadata)
+import { platformNativeScriptDynamic } from "@nativescript/angular/platform";
+
+import { AppModule } from "./app.module";
+import { NativeScriptPlatformRef } from "@nativescript/angular";
+
+AppModule.platformRef = platformNativeScriptDynamic();
+AppModule.platformRef.bootstrapModule(AppModule);
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/modal-nested/modal-nested.component.html b/e2e/modal-navigation-ng/app/modal-nested/modal-nested.component.html
new file mode 100644
index 000000000..3ffe16a18
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/modal-nested/modal-nested.component.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/modal-nested/modal-nested.component.ts b/e2e/modal-navigation-ng/app/modal-nested/modal-nested.component.ts
new file mode 100644
index 000000000..6fb247ad6
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/modal-nested/modal-nested.component.ts
@@ -0,0 +1,26 @@
+import { Component } from "@angular/core";
+import { View, ShownModallyData } from "@nativescript/core/ui/core/view"
+import { ModalDialogParams } from "@nativescript/angular";
+
+@Component({
+ moduleId: module.id,
+ selector: "modal-nested-page",
+ templateUrl: "./modal-nested.component.html"
+})
+export class NestedModalComponent {
+ public navigationVisibility: string = "collapse";
+
+ constructor(private params: ModalDialogParams) {
+
+ console.log("ModalNestedContent.constructor: " + JSON.stringify(params));
+ this.navigationVisibility = params.context.navigationVisibility ? "visible" : "collapse";
+ }
+
+ close(layoutRoot: View) {
+ layoutRoot.closeModal();
+ }
+
+ onShowingModally(args: ShownModallyData) {
+ console.log("modal-page showingModally");
+ }
+}
diff --git a/e2e/modal-navigation-ng/app/modal-second/modal-second.component.html b/e2e/modal-navigation-ng/app/modal-second/modal-second.component.html
new file mode 100644
index 000000000..f1086298f
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/modal-second/modal-second.component.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/modal-second/modal-second.component.ts b/e2e/modal-navigation-ng/app/modal-second/modal-second.component.ts
new file mode 100644
index 000000000..0a6d41587
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/modal-second/modal-second.component.ts
@@ -0,0 +1,25 @@
+import { Component } from "@angular/core";
+import { View } from "@nativescript/core/ui/core/view"
+import { ActivatedRoute } from "@angular/router";
+import { RouterExtensions } from "@nativescript/angular";
+
+@Component({
+ moduleId: module.id,
+ selector: "modal-second-page",
+ templateUrl: "./modal-second.component.html"
+})
+export class ModalSecondComponent {
+ constructor(private routerExtension: RouterExtensions, private activeRoute: ActivatedRoute) { }
+
+ onLoaded(args) {
+ console.log("modal-second loaded");
+ }
+
+ goBack() {
+ this.routerExtension.back({ relativeTo: this.activeRoute });
+ }
+
+ close(layoutRoot: View) {
+ layoutRoot.closeModal();
+ }
+}
diff --git a/e2e/modal-navigation-ng/app/modal-shared/modal-shared-second.component.ts b/e2e/modal-navigation-ng/app/modal-shared/modal-shared-second.component.ts
new file mode 100644
index 000000000..21aebefe3
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/modal-shared/modal-shared-second.component.ts
@@ -0,0 +1,44 @@
+import { Component } from "@angular/core";
+import { ModalDialogOptions, ModalDialogService } from "@nativescript/angular";
+
+import { ViewContainerRefService } from "../shared/ViewContainerRefService";
+import { ModalViewComponent } from "../modal-shared/modal-view.component";
+import { RouterExtensions } from "@nativescript/angular";
+
+@Component({
+ selector: "ns-second",
+ moduleId: module.id,
+ template: `
+
+
+
+
+
+ `
+})
+export class ModalSharedSecondComponent {
+ constructor(
+ private _modalService: ModalDialogService,
+ private _viewContainerRefService: ViewContainerRefService,
+ private _routerExtensions: RouterExtensions
+ ) { }
+
+ onRootModalTap(): void {
+ const options: ModalDialogOptions = {
+ context: {},
+ fullscreen: true,
+ viewContainerRef: this._viewContainerRefService.root,
+ };
+
+ this._modalService.showModal(ModalViewComponent, options)
+ .then((result: string) => {
+ console.log(result);
+ });
+ }
+
+ onBackTap() {
+ if (this._routerExtensions.canGoBack()) {
+ this._routerExtensions.back();
+ }
+ }
+}
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/modal-shared/modal-view-content.component.ts b/e2e/modal-navigation-ng/app/modal-shared/modal-view-content.component.ts
new file mode 100644
index 000000000..a3f2a3c8e
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/modal-shared/modal-view-content.component.ts
@@ -0,0 +1,27 @@
+import { Component } from "@angular/core";
+import { ModalDialogParams } from "@nativescript/angular";
+
+@Component({
+ selector: "ModalViewContent",
+ moduleId: module.id,
+ template: `
+
+
+
+
+
+
+ `,
+ styles: [`
+ .action-bar, .page {
+ background-color: chocolate;
+ }
+ `]
+})
+export class ModalViewContentComponent {
+ constructor(private _params: ModalDialogParams) { }
+
+ onTap(): void {
+ this._params.closeCallback("return value");
+ }
+}
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/modal-shared/modal-view.component.ts b/e2e/modal-navigation-ng/app/modal-shared/modal-view.component.ts
new file mode 100644
index 000000000..2e5e2a9fc
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/modal-shared/modal-view.component.ts
@@ -0,0 +1,17 @@
+import { Component, OnInit } from "@angular/core";
+import { RouterExtensions } from "@nativescript/angular";
+
+@Component({
+ selector: "ModalView",
+ moduleId: module.id,
+ template:`
+
+ `
+})
+export class ModalViewComponent implements OnInit {
+ constructor(private _routerExtensions: RouterExtensions) {}
+
+ ngOnInit(): void {
+ this._routerExtensions.navigate([{ outlets: { modalOutlet: ["modal-shared"]}}]);
+ }
+}
diff --git a/e2e/modal-navigation-ng/app/modal/modal-router/modal-router.component.html b/e2e/modal-navigation-ng/app/modal/modal-router/modal-router.component.html
new file mode 100644
index 000000000..1265aa9c8
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/modal/modal-router/modal-router.component.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/modal/modal-router/modal-router.component.ts b/e2e/modal-navigation-ng/app/modal/modal-router/modal-router.component.ts
new file mode 100644
index 000000000..87eea3b27
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/modal/modal-router/modal-router.component.ts
@@ -0,0 +1,22 @@
+import { Component, OnInit } from "@angular/core";
+import { ActivatedRoute } from "@angular/router";
+import { RouterExtensions } from "@nativescript/angular";
+import { ModalDialogParams } from "@nativescript/angular";
+
+@Component({
+ moduleId: module.id,
+ selector: "ns-modal-router",
+ templateUrl: "./modal-router.component.html",
+})
+
+export class ModalRouterComponent implements OnInit {
+ private modalRoute: string;
+
+ constructor(private params: ModalDialogParams, private routerExtension: RouterExtensions, private activeRoute: ActivatedRoute) {
+ this.modalRoute = params.context.modalRoute;
+ }
+
+ ngOnInit() {
+ this.routerExtension.navigate([this.modalRoute], { relativeTo: this.activeRoute });
+ }
+}
diff --git a/e2e/modal-navigation-ng/app/modal/modal.component.html b/e2e/modal-navigation-ng/app/modal/modal.component.html
new file mode 100644
index 000000000..d49c5720f
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/modal/modal.component.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/modal/modal.component.ts b/e2e/modal-navigation-ng/app/modal/modal.component.ts
new file mode 100644
index 000000000..6c4ee4649
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/modal/modal.component.ts
@@ -0,0 +1,86 @@
+import { Component, ViewContainerRef } from "@angular/core";
+import { ModalDialogParams, ModalDialogOptions, ModalDialogService } from "@nativescript/angular";
+import { RouterExtensions, PageRoute } from "@nativescript/angular";
+import { ActivatedRoute } from "@angular/router";
+import { View, ShownModallyData, EventData } from "@nativescript/core/ui/core/view"
+import { confirm } from "@nativescript/core/ui/dialogs";
+import { ModalRouterComponent } from "../modal/modal-router/modal-router.component";
+import { NestedModalComponent } from "../modal-nested/modal-nested.component";
+
+@Component({
+ moduleId: module.id,
+ selector: "modal-page",
+ templateUrl: "./modal.component.html"
+})
+export class ModalComponent {
+ public navigationVisibility: string = "collapse";
+
+ constructor(private params: ModalDialogParams,
+ private vcRef: ViewContainerRef,
+ private routerExtension: RouterExtensions,
+ private activeRoute: ActivatedRoute,
+ private modal: ModalDialogService) {
+
+ console.log("\nModalContent.constructor: " + JSON.stringify(params));
+ this.navigationVisibility = params.context.navigationVisibility ? "visible" : "collapse";
+ }
+
+ close(layoutRoot: View) {
+ layoutRoot.closeModal();
+ }
+
+ ngOnInit() {
+ }
+
+ ngOnDestroy() {
+ console.log("ModalContent.ngOnDestroy");
+ }
+
+ onShowingModally(args: ShownModallyData) {
+ console.log("modal-page showingModally");
+ }
+
+ showDialogConfirm() {
+ let options = {
+ title: "Dialog",
+ message: "Message",
+ okButtonText: "Yes",
+ cancelButtonText: "No"
+ }
+
+ confirm(options).then((result: boolean) => {
+ console.log(result);
+ })
+ }
+
+ showNestedModalFrame() {
+ const options: ModalDialogOptions = {
+ context: {
+ navigationVisibility: true,
+ modalRoute: "nested-frame-modal"
+ },
+ fullscreen: true,
+ viewContainerRef: this.vcRef
+ };
+
+ this.modal.showModal(ModalRouterComponent, options).then((res: string) => {
+ console.log("nested-modal-frame closed");
+ });
+ }
+
+ showNestedModal() {
+ const options: ModalDialogOptions = {
+ context: { navigationVisibility: false },
+ fullscreen: true,
+ viewContainerRef: this.vcRef
+ };
+
+ this.modal.showModal(NestedModalComponent, options).then((res: string) => {
+ console.log("nested-modal closed");
+ });
+ }
+
+ onNavigateSecondPage() {
+ this.routerExtension.navigate(["../modal-second"], { relativeTo: this.activeRoute });
+ }
+}
diff --git a/e2e/modal-navigation-ng/app/named-router.component.html b/e2e/modal-navigation-ng/app/named-router.component.html
new file mode 100644
index 000000000..83ba115c4
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/named-router.component.html
@@ -0,0 +1 @@
+
diff --git a/e2e/modal-navigation-ng/app/named-router.component.ts b/e2e/modal-navigation-ng/app/named-router.component.ts
new file mode 100644
index 000000000..69fbfc851
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/named-router.component.ts
@@ -0,0 +1,50 @@
+import { Component, ViewContainerRef } from "@angular/core";
+import { Router, NavigationEnd } from "@angular/router";
+import { NSLocationStrategy } from "@nativescript/angular";
+
+import { ViewContainerRefService } from "./shared/ViewContainerRefService";
+import { AppModule } from "./app.module";
+import { ModalDialogOptions, ModalDialogService } from "@nativescript/angular";
+import { ModalViewComponent } from "./modal-shared/modal-view.component";
+
+@Component({
+ selector: "named-router",
+ templateUrl: "named-router.component.html"
+})
+export class NamedRouterComponent {
+ constructor(
+ router: Router,
+ location: NSLocationStrategy,
+ private modal: ModalDialogService,
+ private _vcRef: ViewContainerRef,
+ private _viewContainerRefService: ViewContainerRefService
+ ) {
+ router.events.subscribe(e => {
+ if (e instanceof NavigationEnd) {
+ console.log("[ROUTER]: " + e.toString());
+ console.log(location.toString());
+ }
+ });
+
+ this._viewContainerRefService.root = this._vcRef;
+ }
+
+ ngOnInit() {
+ if (AppModule.root === "named-page-router-modal") {
+ console.log("Show modal page from tab root view!");
+ this.onRootModalTap();
+ }
+ }
+
+ onRootModalTap(): void {
+ const options: ModalDialogOptions = {
+ viewContainerRef: this._viewContainerRefService.root,
+ context: {},
+ fullscreen: true
+ };
+
+ this.modal.showModal(ModalViewComponent, options).then((result: string) => {
+ console.log(result);
+ });
+ }
+}
diff --git a/e2e/modal-navigation-ng/app/navigation/basic.navigation.component.ts b/e2e/modal-navigation-ng/app/navigation/basic.navigation.component.ts
new file mode 100644
index 000000000..c203da370
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/navigation/basic.navigation.component.ts
@@ -0,0 +1,105 @@
+import { Component, ViewContainerRef, Input, ViewChild, ElementRef } from "@angular/core";
+import { Router, NavigationEnd } from "@angular/router";
+import { ModalDialogService, ModalDialogOptions } from "@nativescript/angular";
+import { ModalComponent } from "../modal/modal.component";
+import { ModalRouterComponent } from "../modal/modal-router/modal-router.component";
+import { confirm } from "@nativescript/core/ui/dialogs";
+
+import { ViewContainerRefService } from "../shared/ViewContainerRefService";
+import { ModalViewComponent } from "~/modal-shared/modal-view.component";
+
+declare var UIModalPresentationStyle;
+
+@Component({
+ selector: "basic-nav",
+ template: `
+
+
+
+
+
+
+
+`
+})
+
+export class BasicsNavigationComponent {
+
+ @ViewChild("popoverButtonComp", { static: false }) popoverButtonComp: ElementRef;
+ @Input() col: number;
+ constructor(
+ private modal: ModalDialogService,
+ private router: Router,
+ private vcf: ViewContainerRef,
+ private viewContainerRefService: ViewContainerRefService) {
+ }
+
+ onModalNoFrame() {
+ const options: ModalDialogOptions = {
+ context: {
+ navigationVisibility: false
+ },
+ fullscreen: true,
+ viewContainerRef: this.vcf
+ };
+
+ this.modal.showModal(ModalComponent, options).then((res: string) => {
+ console.log("modal-no-frame closed");
+ });
+ }
+
+ onModalFrame() {
+ const options: ModalDialogOptions = {
+ context: {
+ navigationVisibility: true,
+ modalRoute: "modal"
+ },
+ fullscreen: true,
+ viewContainerRef: this.vcf
+ };
+
+ this.modal.showModal(ModalRouterComponent, options).then((res: string) => {
+ console.log("modal-frame closed");
+ });
+ }
+
+ onShowDialog() {
+ let options = {
+ title: "Dialog",
+ message: "Message",
+ okButtonText: "Yes",
+ cancelButtonText: "No"
+ }
+
+ confirm(options).then((result: boolean) => {
+ console.log(result);
+ })
+ }
+
+ onRootModalTap(): void {
+ const options: ModalDialogOptions = {
+ viewContainerRef: this.viewContainerRefService.root,
+ context: {},
+ fullscreen: true
+ };
+
+ this.modal.showModal(ModalViewComponent, options)
+ .then((result: string) => {
+ console.log(result);
+ });
+ }
+
+ onPopoverModal() {
+ const options: ModalDialogOptions = {
+ viewContainerRef: this.viewContainerRefService.root,
+ context: {},
+ ios: {
+ presentationStyle: UIModalPresentationStyle.Popover
+ },
+ target: this.popoverButtonComp.nativeElement
+ };
+
+ this.modal.showModal(ModalViewComponent, options)
+ .then((result: string) => { console.log(result);});
+ }
+}
diff --git a/e2e/modal-navigation-ng/app/navigation/root.section.component.html b/e2e/modal-navigation-ng/app/navigation/root.section.component.html
new file mode 100644
index 000000000..2a5bf9b84
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/navigation/root.section.component.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/navigation/root.section.component.ts b/e2e/modal-navigation-ng/app/navigation/root.section.component.ts
new file mode 100644
index 000000000..cc8dab627
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/navigation/root.section.component.ts
@@ -0,0 +1,31 @@
+import { Component } from "@angular/core";
+import { AppModule } from "../app.module";
+
+@Component({
+ moduleId: module.id,
+ selector: "root-section",
+ templateUrl: "./root.section.component.html"
+})
+export class RootSectionComponent {
+ constructor() { }
+
+ onFrameRootViewReset(showModal?: boolean) {
+ AppModule.root = showModal ? "page-router-modal" : "page-router";
+ AppModule.platformRef._livesync();
+ }
+
+ onNamedFrameRootViewReset(showModal?: boolean) {
+ AppModule.root = showModal ? "named-page-router-modal" : "named-page-router";
+ AppModule.platformRef._livesync();
+ }
+
+ onTabRootViewReset(showModal?: boolean) {
+ AppModule.root = showModal ? "tab-modal" : "tab";
+ AppModule.platformRef._livesync();
+ }
+
+ onLayoutRootViewReset(showModal?: boolean) {
+ AppModule.root = showModal ? "layout-modal" : "layout";
+ AppModule.platformRef._livesync();
+ }
+}
diff --git a/e2e/modal-navigation-ng/app/package.json b/e2e/modal-navigation-ng/app/package.json
new file mode 100644
index 000000000..e6521c70c
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/package.json
@@ -0,0 +1,9 @@
+{
+ "android": {
+ "v8Flags": "--expose_gc",
+ "markingMode": "none"
+ },
+ "main": "main.js",
+ "name": "tns-template-hello-world-ng",
+ "version": "3.4.3"
+}
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/second/second.component.html b/e2e/modal-navigation-ng/app/second/second.component.html
new file mode 100644
index 000000000..069453908
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/second/second.component.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/second/second.component.ts b/e2e/modal-navigation-ng/app/second/second.component.ts
new file mode 100644
index 000000000..290441c61
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/second/second.component.ts
@@ -0,0 +1,50 @@
+import { Component, ViewContainerRef } from "@angular/core";
+import { ModalDialogService, ModalDialogOptions } from "@nativescript/angular";
+import { EventData } from "@nativescript/core";
+import { Frame } from "@nativescript/core";
+import { View } from "@nativescript/core";
+import { ModalRouterComponent } from "../modal/modal-router/modal-router.component";
+import { RouterExtensions } from "@nativescript/angular";
+import { ModalComponent } from "../modal/modal.component";
+import { AppModule } from "../app.module";
+@Component({
+ moduleId: module.id,
+ selector: "second-page",
+ templateUrl: "./second.component.html"
+})
+export class SecondComponent {
+ constructor(private modal: ModalDialogService, private vcRef: ViewContainerRef, private routerExtension: RouterExtensions) { }
+
+ onModalNoFrame() {
+ const options: ModalDialogOptions = {
+ context: {
+ navigationVisibility: false
+ },
+ fullscreen: true,
+ viewContainerRef: this.vcRef
+ };
+
+ this.modal.showModal(ModalComponent, options).then((res: string) => {
+ console.log("moda-no-frame closed");
+ });
+ }
+
+ onModalFrame() {
+ const options: ModalDialogOptions = {
+ context: {
+ navigationVisibility: true,
+ modalRoute: "modal"
+ },
+ fullscreen: true,
+ viewContainerRef: this.vcRef
+ };
+
+ this.modal.showModal(ModalRouterComponent, options).then((res: string) => {
+ console.log("moda-frame closed");
+ });
+ }
+
+ goBack() {
+ this.routerExtension.back();
+ }
+}
diff --git a/e2e/modal-navigation-ng/app/shared/ViewContainerRefService.ts b/e2e/modal-navigation-ng/app/shared/ViewContainerRefService.ts
new file mode 100644
index 000000000..e74a57a15
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/shared/ViewContainerRefService.ts
@@ -0,0 +1,14 @@
+import { Injectable, ViewContainerRef } from "@angular/core";
+
+@Injectable()
+export class ViewContainerRefService {
+ private _rootViewContainerRef: ViewContainerRef;
+
+ get root():ViewContainerRef {
+ return this._rootViewContainerRef;
+ }
+
+ set root(viewContainerRef: ViewContainerRef) {
+ this._rootViewContainerRef = viewContainerRef;
+ }
+}
diff --git a/e2e/modal-navigation-ng/app/tab.component.html b/e2e/modal-navigation-ng/app/tab.component.html
new file mode 100644
index 000000000..d84edd2c6
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/tab.component.html
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/e2e/modal-navigation-ng/app/tab.component.ts b/e2e/modal-navigation-ng/app/tab.component.ts
new file mode 100644
index 000000000..b0afb94e3
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/tab.component.ts
@@ -0,0 +1,49 @@
+import { Component, ViewContainerRef } from "@angular/core";
+import { Router, NavigationEnd } from "@angular/router";
+import { NSLocationStrategy } from "@nativescript/angular";
+
+import { ViewContainerRefService } from "./shared/ViewContainerRefService";
+import { AppModule } from "./app.module";
+import { ModalDialogOptions, ModalDialogService } from "@nativescript/angular";
+import { ModalViewComponent } from "./modal-shared/modal-view.component";
+
+@Component({
+ selector: "ns-tab",
+ templateUrl: "tab.component.html",
+})
+export class TabComponent {
+ constructor(
+ router: Router,
+ location: NSLocationStrategy,
+ private _vcRef: ViewContainerRef,
+ private _viewContainerRefService: ViewContainerRefService,
+ private modal: ModalDialogService) {
+ router.events.subscribe(e => {
+ if (e instanceof NavigationEnd) {
+ console.log("[ROUTER]: " + e.toString());
+ console.log(location.toString());
+ }
+ });
+
+ this._viewContainerRefService.root = this._vcRef;
+ }
+
+ ngOnInit() {
+ if (AppModule.root === "tab-modal") {
+ console.log("Show modal page from tab root view!");
+ this.onRootModalTap();
+ }
+ }
+
+ onRootModalTap(): void {
+ const options: ModalDialogOptions = {
+ viewContainerRef: this._viewContainerRefService.root,
+ context: {},
+ fullscreen: true
+ };
+
+ this.modal.showModal(ModalViewComponent, options).then((result: string) => {
+ console.log(result);
+ });
+ }
+}
diff --git a/e2e/modal-navigation-ng/e2e/modal-frame.e2e-spec.ts b/e2e/modal-navigation-ng/e2e/modal-frame.e2e-spec.ts
new file mode 100644
index 000000000..4a1591617
--- /dev/null
+++ b/e2e/modal-navigation-ng/e2e/modal-frame.e2e-spec.ts
@@ -0,0 +1,100 @@
+import { AppiumDriver, createDriver, nsCapabilities } from "nativescript-dev-appium";
+import { Screen } from "./screens/screen";
+import {
+ roots,
+ modalFrameBackground,
+ testSecondPageBackground,
+ testSecondPageClose,
+ testNestedModalFrameBackground,
+ testNestedModalPageBackground,
+ testDialogBackground
+} from "./screens/shared-screen";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+const isSauceRun = isSauceLab;
+
+describe("modal-frame:", async function () {
+
+ let driver: AppiumDriver;
+ let screen: Screen;
+
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ screen = new Screen(driver);
+ });
+
+ after(async function () {
+ if (isSauceRun) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ for (let index = 0; index < roots.length; index++) {
+ const root = roots[index];
+ describe(`${root} modal frame background scenarios:`, async function () {
+
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await screen[root]();
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ await driver.resetApp();
+ await screen[root]();
+ }
+ });
+
+ after(async function () {
+ await screen.closeModal();
+ await screen.loadedHome();
+ });
+
+ it("should show dialog confirm, run in background", async function () {
+ await screen.loadModalFrame(true);
+ await testDialogBackground(driver, screen);
+ });
+
+ it("should run modal page with frame in background", async function () {
+ await screen.loadModalFrame(false);
+ await modalFrameBackground(driver, screen);
+ });
+
+ it("should navigate to second page, run in background, go back", async function () {
+ await screen.loadModalFrame(false);
+ await testSecondPageBackground(driver, screen);
+ });
+
+ it("should show nested modal page with frame, run in background, close", async function () {
+ await screen.loadModalFrame(false);
+ await testNestedModalFrameBackground(driver, screen);
+ });
+
+ it("should show nested modal page, run in background, close", async function () {
+ await screen.loadModalFrame(false);
+ await testNestedModalPageBackground(driver, screen);
+ });
+
+ it("should navigate to second page, close", async function () {
+ await screen.loadModalFrame(false);
+ await testSecondPageClose(driver, screen);
+ });
+
+ it("should navigate to second page, run in background, go back", async function () {
+ await screen.loadModalFrame(true);
+ await testSecondPageBackground(driver, screen);
+ });
+ });
+ };
+});
diff --git a/e2e/modal-navigation-ng/e2e/modal-layout.e2e-spec.ts b/e2e/modal-navigation-ng/e2e/modal-layout.e2e-spec.ts
new file mode 100644
index 000000000..40ccb5538
--- /dev/null
+++ b/e2e/modal-navigation-ng/e2e/modal-layout.e2e-spec.ts
@@ -0,0 +1,70 @@
+import { AppiumDriver, createDriver, nsCapabilities } from "nativescript-dev-appium";
+import { Screen } from "./screens/screen"
+import {
+ roots,
+ testNestedModalPageBackground,
+ testDialogBackground,
+} from "./screens/shared-screen";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+const isSauceRun = isSauceLab;
+
+describe("modal-layout:", async function () {
+ let driver: AppiumDriver;
+ let screen: Screen;
+
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ screen = new Screen(driver);
+ });
+
+ after(async function () {
+ if (isSauceRun) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ for (let index = 0; index < roots.length; index++) {
+ const root = roots[index];
+ describe(`${root} modal no frame background scenarios:`, async function () {
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await screen[root]();
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ await driver.resetApp();
+ await screen[root]();
+ }
+ });
+
+ after(async function () {
+ await screen.closeModal();
+ await screen.loadedHome();
+ });
+
+ it("should show nested modal page, run in background, close", async function () {
+ await screen.loadModalNoFrame(true);
+ await testNestedModalPageBackground(driver, screen, false);
+
+ });
+
+ it("should show dialog confirm inside modal view with no frame, run in background", async function () {
+ await screen.loadModalNoFrame(false);
+ await testDialogBackground(driver, screen, false);
+ });
+ });
+ };
+});
diff --git a/e2e/modal-navigation-ng/e2e/modal-on-init.e2e-spec.ts b/e2e/modal-navigation-ng/e2e/modal-on-init.e2e-spec.ts
new file mode 100644
index 000000000..2f0af1251
--- /dev/null
+++ b/e2e/modal-navigation-ng/e2e/modal-on-init.e2e-spec.ts
@@ -0,0 +1,172 @@
+import { AppiumDriver, createDriver, nsCapabilities } from "nativescript-dev-appium";
+import { Screen, sharedModalView, homeComponent } from "./screens/screen";
+import {
+ assertComponent,
+ goBack,
+ navigateToSecondComponent
+} from "./screens/shared-screen";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const roots = [
+ "setTabRootViewModal",
+ "setFrameRootViewModal",
+ "setNamedFrameRootViewModal",
+];
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+const isSauceRun = isSauceLab;
+
+describe("modal-on-init:", async function () {
+ let driver: AppiumDriver;
+ let screen: Screen;
+
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ screen = new Screen(driver);
+ });
+
+ after(async function () {
+ if (isSauceRun) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ for (let index = 0; index < roots.length; index++) {
+ const root = roots[index];
+ describe("Shared Modal on Init", async function () {
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await screen[root]();
+ console.log(`Root: ${root}`);
+ });
+
+ beforeEach(async function () { });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ await driver.resetApp();
+ await screen[root]();
+ }
+ });
+
+ after("root after all hook", async function () {
+ await driver.logTestArtifacts(`${root}_root_after_all_hook`);
+ });
+
+ it("should shared modal view", async function () {
+ await assertComponent(driver, sharedModalView);
+ });
+
+ it("run in background", async function () {
+ await driver.backgroundApp(1);
+ await assertComponent(driver, sharedModalView);
+ });
+
+ it("should close shared modal ", async function () {
+ await screen.closeModal();
+ await screen.loadedHome();
+ });
+
+ it("should open\\close shared modal", async function () {
+ await screen.loadSharedModal(true);
+ await screen.closeModal();
+ await screen.loadedHome();
+ });
+
+ it("should open\\close shared modal again", async function () {
+ await screen.loadSharedModal(true);
+ if (driver.isAndroid) {
+ await driver.navBack();
+ } else {
+ await screen.closeModal();
+ }
+ await screen.loadedHome();
+ });
+
+ it("should open\\close modal with frame", async function () {
+ await screen.loadModalFrame(true);
+ await screen.closeModal();
+ });
+
+ it("should open\\close shared modal again", async function () {
+ await screen.loadSharedModal(true);
+ await screen.closeModal();
+ });
+
+ it("run in background again", async function () {
+ await driver.backgroundApp(1);
+ await screen.loadedHome();
+ });
+
+ it("should open\\close shared modal second", async function () {
+ await screen.loadModalFrame(true);
+ await screen.closeModal();
+ });
+ });
+ };
+
+ describe("Shared Modal on Init", async function () {
+ const root = "setLayoutRootViewModal";
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await screen[root]();
+ console.log(`Root: ${root}`);
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ await driver.resetApp();
+ await screen[root]();
+ }
+ });
+
+ after("root after all hook", async function () {
+ await driver.logTestArtifacts(`${root}_root_after_all_hook`);
+ });
+
+ it("should shared modal view", async function () {
+ await assertComponent(driver, sharedModalView);
+ });
+
+ it("run in background", async function () {
+ await driver.backgroundApp(1);
+ await assertComponent(driver, sharedModalView);
+ });
+
+ it("should close shared modal ", async function () {
+ await screen.closeModal();
+ await screen.loadedHome();
+ });
+
+ it("should open\\close shared modal", async function () {
+ await screen.loadModalFrame(true);
+ await screen.closeModal();
+ });
+
+ it("run in background again", async function () {
+ await driver.backgroundApp(1);
+ await screen.loadedHome();
+ });
+
+ it("should open\\close shared modal second", async function () {
+ await screen.loadModalFrame(true);
+ await screen.closeModal();
+ });
+
+ it("should open\\close shared modal", async function () {
+ await screen.loadSharedModal(true);
+ await screen.closeModal();
+ });
+ });
+});
diff --git a/e2e/modal-navigation-ng/e2e/modal.shared.e2e-spec.ts b/e2e/modal-navigation-ng/e2e/modal.shared.e2e-spec.ts
new file mode 100644
index 000000000..f6613b088
--- /dev/null
+++ b/e2e/modal-navigation-ng/e2e/modal.shared.e2e-spec.ts
@@ -0,0 +1,165 @@
+import { AppiumDriver, createDriver, nsCapabilities } from "nativescript-dev-appium";
+import { Screen } from "./screens/screen";
+import { assertComponent, goBack, navigateToSecondComponent } from "./screens/shared-screen";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const homeComponent = "Home Component";
+const roots = ["setFrameRootView", "setTabRootView"];
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+const isSauceRun = isSauceLab;
+
+describe("modal-shared:", async function () {
+ let driver: AppiumDriver;
+ let screen: Screen;
+
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ screen = new Screen(driver);
+ });
+
+ after(async function () {
+ if (isSauceRun) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ for (let index = 0; index < roots.length; index++) {
+ const root = roots[index];
+ describe("Shared modal from home component and back", async function () {
+
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await screen[root]();
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ await driver.resetApp();
+ await screen[root]();
+ }
+ });
+
+ it("should find home component", async function () {
+ await assertComponent(driver, homeComponent);
+ });
+
+ it("should open\\close shared modal from home component", async function () {
+ await screen.loadSharedModal(true);
+ await screen.closeModal();
+ });
+
+ it("should open\\close shared modal from home component again", async function () {
+ await screen.loadSharedModal(true);
+ await screen.closeModal();
+ });
+
+ it("should open\\close shared modal with presentation style from home component", async function () {
+ await screen.loadSharedModalWithPresentationStyle(true);
+ await screen.closeModal();
+ });
+
+ it("should find home component again", async function () {
+ await screen.loadedHome();
+ });
+
+ it("should navigate to second component", async function () {
+ await navigateToSecondComponent(driver);
+ });
+
+ it("should find second component", async function () {
+ await assertComponent(driver, "second component");
+ });
+
+ it("should open\\close shared modal from second component", async function () {
+ await screen.loadSharedModal(true);
+ await screen.closeModal();
+ });
+
+ it("should find second component again", async function () {
+ await assertComponent(driver, "second component");
+ });
+
+ it("should navigate back to home component", async function () {
+ await goBack(driver);
+ await assertComponent(driver, homeComponent);
+ });
+ });
+ };
+
+ describe("modal-shared-different-component:", async function () {
+
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state && this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ for (let index = 0; index < roots.length; index++) {
+ describe("Shared modal from different components", async function () {
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.resetApp();
+ });
+
+ it("should find home component", async function () {
+ await screen.loadedHome();
+ });
+
+ it("should open\\close shared modal from home component", async function () {
+ await screen.loadSharedModal(true);
+ await screen.closeModal();
+ });
+
+ it("should find home component again", async function () {
+ await screen.loadedHome();
+ });
+
+ it("should navigate to second component", async function () {
+ await navigateToSecondComponent(driver);
+ });
+
+ it("should find second component", async function () {
+ await assertComponent(driver, "second component");
+ });
+
+ it("should open\\close shared modal from second component", async function () {
+ await screen.loadSharedModal(true);
+ await screen.closeModal();
+ });
+
+ it("should find second component again", async function () {
+ await assertComponent(driver, "second component");
+ });
+
+ it("should navigate back to home component", async function () {
+ await goBack(driver);
+ await screen.loadedHome();
+ });
+
+ it("should open\\close shared modal from home component after manipulations with second", async function () {
+ await screen.loadSharedModal(true);
+ await screen.closeModal();
+ });
+
+ it("should find home component again", async function () {
+ await screen.loadedHome();
+ });
+ });
+ };
+ });
+});
+
diff --git a/e2e/modal-navigation-ng/e2e/screens/screen.ts b/e2e/modal-navigation-ng/e2e/screens/screen.ts
new file mode 100644
index 000000000..690edd0ab
--- /dev/null
+++ b/e2e/modal-navigation-ng/e2e/screens/screen.ts
@@ -0,0 +1,327 @@
+import { AppiumDriver, SearchOptions } from "nativescript-dev-appium";
+import { assert } from "chai";
+
+const home = "Home Component"
+const first = "First"
+const modal = "Modal";
+const modalFirst = "Modal First";
+const dialogConfirm = "Dialog";
+const modalSecond = "Modal Second";
+const modalNested = "Modal Nested";
+
+const modalFrame = "Show Modal Page With Frame";
+const modalNoFrame = "Show Modal Without Frame";
+const modalLayout = "Show Modal Layout";
+const modalTabView = "Show Modal TabView";
+const navToSecondPage = "Navigate To Second Page";
+const showDialog = "Show Dialog";
+const resetFrameRootView = "Reset Frame Root View";
+const resetFrameRootViewModal = "Reset Frame Root View Modal";
+const resetNamedFrameRootView = "Reset Named Frame Root View";
+const resetNamedFrameRootViewModal = "Reset Named Frame Root View Modal";
+const resetTabRootView = "Reset Tab Root View";
+const resetTabRootViewModal = "Reset Tab Root View Modal";
+const resetLayoutRootView = "Reset Layout Root View";
+const resetLayoutRootViewModal = "Reset Layout Root View Modal";
+
+const showNestedModalFrame = "Show Nested Modal Page With Frame";
+const showNestedModalPage = "Show Nested Modal Page";
+
+const confirmDialog = "Yes";
+const confirmDialogMessage = "Message";
+const closeModalNested = "Close Modal Nested";
+const closeModal = "Close Modal";
+const goBack = "Go Back(activatedRoute)";
+export const sharedModalView = "SHARED MODAL VIEW";
+export const homeComponent = "Home Component";
+
+export class Screen {
+
+ private _driver: AppiumDriver
+
+ constructor(driver: AppiumDriver) {
+ this._driver = driver;
+ }
+
+ loadedHome = async () => {
+ const lblHome = await this._driver.waitForElement(home);
+ assert.isTrue(await lblHome.isDisplayed());
+ console.log(home + " loaded!");
+ }
+
+ resetFrameRootView = async () => {
+ console.log("Setting frame root ...");
+ const btnResetFrameRootView = await this._driver.findElementByAutomationText(resetFrameRootView);
+ await btnResetFrameRootView.tap();
+ }
+
+ resetNamedFrameRootView = async () => {
+ console.log("Setting named frame root ...");
+ const btnResetFrameRootView = await this._driver.findElementByAutomationText(resetNamedFrameRootView);
+ await btnResetFrameRootView.tap();
+ }
+
+ resetLayoutRootView = async () => {
+ console.log("Setting layout root ...");
+ const btnResetLayoutRootView = await this._driver.waitForElement(resetLayoutRootView);
+ await btnResetLayoutRootView.click();
+ }
+
+ resetTabRootView = async () => {
+ const btnResetTabRootView = await this._driver.findElementByAutomationText(resetTabRootView);
+ await btnResetTabRootView.tap();
+ }
+
+ resetTabRootViewModal = async () => {
+ const btnResetTabRootViewModal = await this._driver.findElementByAutomationText(resetTabRootViewModal);
+ await btnResetTabRootViewModal.click();
+ }
+
+ resetFrameRootViewModal = async () => {
+ const btnResetTabRootViewModal = await this._driver.findElementByAutomationText(resetFrameRootViewModal);
+ await btnResetTabRootViewModal.click();
+ }
+
+ resetNamedFrameRootViewModal = async () => {
+ const btnResetTabRootViewModal = await this._driver.findElementByAutomationText(resetNamedFrameRootViewModal);
+ await btnResetTabRootViewModal.click();
+ }
+
+ resetLayoutRootViewModal = async () => {
+ const btnResetTabRootViewModal = await this._driver.findElementByAutomationText(resetLayoutRootViewModal);
+ await btnResetTabRootViewModal.click();
+ }
+
+ loadedTabRootView = async () => {
+ const tabFirst = await this._driver.findElementByAutomationText(first);
+ assert.isTrue(await tabFirst.isDisplayed());
+ console.log("Tab root view loaded!");
+ }
+
+ setFrameRootView = async () => {
+ // should load frame root, no need to verify it is loaded
+ await this.loadedHome();
+ await this.resetFrameRootView();
+ }
+
+ setNamedFrameRootView = async () => {
+ // should load named frame root, no need to verify it is loaded
+ await this.loadedHome();
+ await this.resetNamedFrameRootView();
+ }
+
+ setTabRootView = async () => {
+ // should load tab root
+ await this.loadedHome();
+ await this.resetTabRootView();
+ await this.loadedTabRootView();
+ }
+
+ setLayoutRootView = async () => {
+ // should load layout root, no need to verify it is loaded
+ await this.loadedHome();
+ await this.resetLayoutRootView();
+ }
+
+ setTabRootViewModal = async () => {
+ await this.loadedHome();
+ await this.resetTabRootViewModal();
+ }
+
+ setFrameRootViewModal = async () => {
+ await this.loadedHome();
+ await this.resetFrameRootViewModal();
+ }
+
+ setNamedFrameRootViewModal = async () => {
+ await this.loadedHome();
+ await this.resetNamedFrameRootViewModal();
+ }
+
+ setLayoutRootViewModal = async () => {
+ await this.loadedHome();
+ await this.resetLayoutRootViewModal();
+ }
+
+ showModalFrame = async () => {
+ const btnModalFrame = await this._driver.findElementByAutomationText(modalFrame);
+ await btnModalFrame.tap();
+ }
+
+ loadedModalFrame = async () => {
+ const lblModal = await this._driver.waitForElement(modal, 5000);
+ assert.isTrue(await lblModal.isDisplayed(), `${modal} is not displayed!`);
+ console.log(modal + " loaded!");
+ }
+
+ showModalNoFrame = async () => {
+ const btnModalPage = await this._driver.findElementByAutomationText(modalNoFrame);
+ await btnModalPage.tap();
+ }
+
+
+ private showSharedModal = async () => {
+ const btnTap = await this._driver.waitForElement("Show Shared Modal");
+ await btnTap.click();
+ }
+
+ private showSharedModalPresentationStyle = async () => {
+ const btnTap = await this._driver.findElementByText("popover", SearchOptions.contains);
+ await btnTap.click();
+ }
+
+ loadedModalPage = async () => {
+ const btnShowNestedModalPage = await this._driver.findElementByAutomationText(showNestedModalPage);
+ assert.isTrue(await btnShowNestedModalPage.isDisplayed(), `${showNestedModalPage} is not displayed`);
+ console.log("Modal Page loaded!");
+ }
+
+ showModalLayout = async () => {
+ const btnModalLayout = await this._driver.findElementByAutomationText(modalLayout);
+ await btnModalLayout.tap();
+ }
+
+ loadedModalLayout = async () => {
+ await this.loadedModalFrame();
+ }
+
+ showModalTabView = async () => {
+ const btnModalTabView = await this._driver.findElementByAutomationText(modalTabView);
+ await btnModalTabView.tap();
+ }
+
+ loadedModalTabView = async () => {
+ const itemModalFirst = await this._driver.findElementByAutomationText(modalFirst);
+ assert.isTrue(await itemModalFirst.isDisplayed());
+ console.log("Modal TabView loaded!");
+ }
+
+ navigateToSecondPage = async () => {
+ const btnNavToSecondPage = await this._driver.findElementByAutomationText(navToSecondPage);
+ await btnNavToSecondPage.tap();
+ }
+
+ showDialogConfirm = async () => {
+ const btnShowDialogConfirm = await this._driver.findElementByAutomationText(showDialog);
+ await btnShowDialogConfirm.tap();
+ }
+
+ navigateToFirstItem = async () => {
+ const itemModalFirst = await this._driver.findElementByAutomationText(modalFirst);
+ await itemModalFirst.tap();
+ }
+
+ navigateToSecondItem = async () => {
+ const itemModalSecond = await this._driver.findElementByAutomationText(modalSecond);
+ await itemModalSecond.tap();
+ }
+
+ loadedModalNoFrame = async () => {
+ await this._driver.wait(2000);
+ const btnShowDialogConfirm = await this._driver.waitForElement(showDialog);
+ const btnCloseModal = await this._driver.waitForElement(closeModal);
+ assert.isTrue(await btnShowDialogConfirm.isDisplayed());
+ assert.isTrue(await btnCloseModal.isDisplayed());
+ console.log("Modal Without Frame shown!");
+ }
+
+ loadedConfirmDialog = async () => {
+ const lblDialogMessage = await this._driver.findElementByAutomationText(confirmDialogMessage);
+ assert.isTrue(await lblDialogMessage.isDisplayed());
+ console.log(dialogConfirm + " shown!");
+ }
+
+ loadedSecondPage = async () => {
+ const lblModalSecond = await this._driver.waitForElement(modalSecond, 5000);
+ assert.isTrue(await lblModalSecond.isDisplayed());
+ console.log(modalSecond + " loaded!");
+ }
+
+ loadedFirstItem = async () => {
+ const lblModal = await this._driver.findElementByAutomationText(modal);
+ assert.isTrue(await lblModal.isDisplayed());
+ console.log("First Item loaded!");
+ }
+
+ loadedSecondItem = async () => {
+ const btnGoBack = await this._driver.findElementByAutomationText(goBack);
+ assert.isTrue(await btnGoBack.isDisplayed());
+ console.log("Second Item loaded!");
+ }
+
+ closeDialog = async () => {
+ const btnYesDialog = await this._driver.findElementByAutomationText(confirmDialog);
+ await btnYesDialog.tap();
+ }
+
+ goBackFromSecondPage = async () => {
+ const btnGoBackFromSecondPage = await this._driver.findElementByAutomationText(goBack);
+ await btnGoBackFromSecondPage.tap();
+ }
+
+ showNestedModalFrame = async () => {
+ const btnShowNestedModalFrame = await this._driver.findElementByAutomationText(showNestedModalFrame);
+ await btnShowNestedModalFrame.tap();
+ }
+
+ loadedNestedModalFrame = async () => {
+ const lblModalNested = await this._driver.waitForElement(modalNested, 5000);
+ assert.isTrue(await lblModalNested.isDisplayed());
+ console.log(modalNested + " loaded!");
+ }
+
+ closeModalNested = async () => {
+ const btnCloseNestedModal = await this._driver.findElementByAutomationText(closeModalNested);
+ await btnCloseNestedModal.tap();
+ }
+
+ showNestedModalPage = async () => {
+ const btnShowNestedModalPage = await this._driver.findElementByAutomationText(showNestedModalPage);
+ await btnShowNestedModalPage.tap();
+ }
+
+ loadedNestedModalPage = async () => {
+ const btnCloseModalNested = await this._driver.findElementByAutomationText(closeModalNested);
+ assert.isTrue(await btnCloseModalNested.isDisplayed(), `${closeModalNested} is not shown`);
+ console.log(closeModalNested + " loaded!");
+ }
+
+ closeModal = async () => {
+ const btnCloseModal = await this._driver.waitForElement(closeModal, 10000);
+ await btnCloseModal.click();
+ }
+
+ loadModalNoFrame = async (loadShowModalPageWithFrame: boolean) => {
+ if (loadShowModalPageWithFrame) {
+ await this.showModalNoFrame();
+ }
+
+ await this.loadedModalNoFrame();
+ }
+
+ loadModalFrame = async (loadShowModalPageWithFrame: boolean) => {
+ if (loadShowModalPageWithFrame) {
+ await this.showModalFrame();
+ }
+
+ await this.loadedModalFrame();
+ }
+
+ loadSharedModal = async (loadShowModalPageWithFrame: boolean) => {
+ if (loadShowModalPageWithFrame) {
+ await this.showSharedModal();
+ }
+
+ const lbl = await this._driver.waitForElement(sharedModalView, 5000);
+ assert.isTrue(await lbl.isDisplayed());
+ }
+
+ loadSharedModalWithPresentationStyle = async (loadShowModalPageWithFrame: boolean) => {
+ if (loadShowModalPageWithFrame) {
+ await this.showSharedModalPresentationStyle();
+ }
+
+ const lbl = await this._driver.waitForElement(sharedModalView, 5000);
+ assert.isTrue(await lbl.isDisplayed());
+ }
+}
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/e2e/screens/shared-screen.ts b/e2e/modal-navigation-ng/e2e/screens/shared-screen.ts
new file mode 100644
index 000000000..8b742de23
--- /dev/null
+++ b/e2e/modal-navigation-ng/e2e/screens/shared-screen.ts
@@ -0,0 +1,101 @@
+import { AppiumDriver } from "nativescript-dev-appium";
+import { Screen } from "./screen"
+import { assert } from "chai";
+
+const time = 1;
+export const roots = ["setFrameRootView", "setLayoutRootView", "setTabRootView", "setNamedFrameRootView"];
+
+export async function modalFrameBackground(driver: AppiumDriver, screen: Screen) {
+ await driver.backgroundApp(time);
+ await screen.loadedModalFrame();
+}
+
+export async function testDialogBackground(driver: AppiumDriver, screen: Screen, isInFrame: boolean = true) {
+ await screen.showDialogConfirm();
+ await screen.loadedConfirmDialog();
+
+ await driver.backgroundApp(time);
+ await screen.loadedConfirmDialog();
+
+ await screen.closeDialog();
+ if (isInFrame) {
+ await screen.loadedModalFrame();
+ }
+}
+
+export async function testSecondPageBackground(driver: AppiumDriver, screen: Screen) {
+ await screen.navigateToSecondPage();
+ await screen.loadedSecondPage();
+
+ await driver.backgroundApp(time);
+ await screen.loadedSecondPage();
+
+ await screen.goBackFromSecondPage();
+ await screen.loadedModalFrame();
+}
+
+export async function testSecondPageClose(driver: AppiumDriver, screen: Screen) {
+ await screen.navigateToSecondPage();
+ await screen.loadedSecondPage();
+
+ await screen.closeModal();
+ await screen.loadedHome();
+}
+
+export async function testNestedModalFrameBackground(driver: AppiumDriver, screen: Screen, isInFrame: boolean = true) {
+ await screen.showNestedModalFrame();
+ await screen.loadedNestedModalFrame();
+
+ await driver.backgroundApp(time);
+ await screen.loadedNestedModalFrame();
+
+ await screen.closeModalNested();
+ isInFrame ? await screen.loadedModalFrame() : await screen.loadedModalPage();
+}
+
+export async function testNestedModalPageBackground(driver: AppiumDriver, screen: Screen, isInFrame: boolean = true) {
+ await screen.showNestedModalPage();
+ await screen.loadedNestedModalPage();
+
+ await driver.backgroundApp(time);
+ await screen.loadedNestedModalPage();
+
+ await screen.closeModalNested();
+ isInFrame ? await screen.loadedModalFrame() : await screen.loadedModalPage();
+}
+
+export async function modalPageBackground(driver: AppiumDriver, screen: Screen, isInFrame: boolean = true) {
+ await driver.backgroundApp(time);
+ isInFrame ? await screen.loadedModalFrame() : await screen.loadedModalPage();
+}
+
+export async function modalTabViewBackground(driver: AppiumDriver, screen: Screen) {
+ await driver.backgroundApp(time);
+ await screen.loadedModalTabView();
+}
+
+export async function testSecondItemBackground(driver: AppiumDriver, screen: Screen) {
+ await screen.navigateToSecondItem();
+ await screen.loadedSecondItem();
+
+ await driver.backgroundApp(time);
+ await screen.loadedSecondItem();
+
+ await screen.navigateToFirstItem();
+ await screen.loadedFirstItem();
+}
+
+export async function assertComponent(driver: AppiumDriver, message: string) {
+ const lbl = await driver.waitForElement(message, 5000);
+ assert.isTrue(await lbl.isDisplayed());
+}
+
+export async function navigateToSecondComponent(driver: AppiumDriver) {
+ const navigateBtnTap = await driver.findElementByAutomationText("Go To Second (to open shared modal)");
+ await navigateBtnTap.click();
+}
+
+export async function goBack(driver: AppiumDriver) {
+ const backBtnTap = await driver.findElementByAutomationText("go back");
+ await backBtnTap.click();
+}
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/e2e/setup.ts b/e2e/modal-navigation-ng/e2e/setup.ts
new file mode 100644
index 000000000..acfc6f70a
--- /dev/null
+++ b/e2e/modal-navigation-ng/e2e/setup.ts
@@ -0,0 +1,18 @@
+import { startServer, stopServer, ITestReporter, nsCapabilities, LogImageType } from "nativescript-dev-appium";
+const addContext = require('mochawesome/addContext');
+
+const testReporterContext = {};
+testReporterContext.name = "mochawesome";
+testReporterContext.reportDir = "mochawesome-report";
+testReporterContext.log = addContext;
+testReporterContext.logImageTypes = [LogImageType.screenshots];
+nsCapabilities.testReporter = testReporterContext;
+
+before("start server", async function () {
+ nsCapabilities.testReporter.context = this;
+ await startServer();
+});
+
+after("stop server", async function () {
+ await stopServer();
+});
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/e2e/tsconfig.json b/e2e/modal-navigation-ng/e2e/tsconfig.json
new file mode 100644
index 000000000..c297b2347
--- /dev/null
+++ b/e2e/modal-navigation-ng/e2e/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es6",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "importHelpers": false,
+ "types": [
+ "node",
+ "mocha",
+ "chai"
+ ],
+ "lib": [
+ "es2015",
+ "dom"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/ngcc.config.js b/e2e/modal-navigation-ng/ngcc.config.js
new file mode 100644
index 000000000..2d2413164
--- /dev/null
+++ b/e2e/modal-navigation-ng/ngcc.config.js
@@ -0,0 +1,19 @@
+module.exports = {
+ packages: {
+ "@nativescript/angular": {
+ entryPoints: {
+ ".": {
+ override: {
+ main: "./index.js",
+ typings: "./index.d.ts",
+ },
+ ignoreMissingDependencies: true,
+ }
+ },
+ ignorableDeepImportMatchers: [
+ /tns-core-modules\//,
+ /@nativescript\/core\//,
+ ]
+ }
+ }
+};
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/package.json b/e2e/modal-navigation-ng/package.json
new file mode 100644
index 000000000..0ad335400
--- /dev/null
+++ b/e2e/modal-navigation-ng/package.json
@@ -0,0 +1,59 @@
+{
+ "description": "NativeScript Application",
+ "license": "SEE LICENSE IN ",
+ "readme": "NativeScript Application",
+ "repository": "",
+ "nativescript": {
+ "id": "org.nativescript.modalnavigationng",
+ "tns-ios": {
+ "version": "6.0.2"
+ },
+ "tns-android": {
+ "version": "6.0.1"
+ }
+ },
+ "dependencies": {
+ "@angular/animations": "~9.1.0",
+ "@angular/common": "~9.1.0",
+ "@angular/compiler": "~9.1.0",
+ "@angular/core": "~9.1.0",
+ "@angular/forms": "~9.1.0",
+ "@angular/platform-browser": "~9.1.0",
+ "@angular/platform-browser-dynamic": "~9.1.0",
+ "@angular/router": "~9.1.0",
+ "@nativescript/angular": "file:../../dist/nativescript-angular-scoped.tgz",
+ "nativescript-theme-core": "~1.0.4",
+ "reflect-metadata": "~0.1.8",
+ "rxjs": "~6.5.5",
+ "@nativescript/core": "next",
+ "zone.js": "^0.10.3"
+ },
+ "devDependencies": {
+ "@angular/compiler-cli": "~9.1.0",
+ "@ngtools/webpack": "~9.1.0",
+ "@types/chai": "~4.1.7",
+ "@types/mocha": "~5.2.5",
+ "@types/node": "~10.12.18",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0",
+ "babylon": "6.18.0",
+ "chai": "^4.2.0",
+ "lazy": "1.0.11",
+ "mocha": "~5.2.0",
+ "mochawesome": "~3.1.2",
+ "nativescript-dev-appium": "^6.0.0",
+ "nativescript-dev-webpack": "next",
+ "tns-platform-declarations": "next",
+ "typescript": "~3.8.3"
+ },
+ "scripts": {
+ "e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
+ "e2e-watch": "tsc -p e2e --watch",
+ "clean": "npx rimraf hooks node_modules platforms package-lock.json",
+ "setup": "cd ../../nativescript-angular && npm run prep.apps && cd ../e2e/modal-navigation-ng && npm run clean",
+ "ngcc": "ngcc --properties es2015 module main --first-only",
+ "postinstall": "npm run ngcc",
+ "ios": "tns debug ios --env.aot --emulator --no-hmr",
+ "android": "tns debug android --env.aot --emulator --no-hmr"
+ }
+}
diff --git a/e2e/modal-navigation-ng/references.d.ts b/e2e/modal-navigation-ng/references.d.ts
new file mode 100644
index 000000000..992a504a0
--- /dev/null
+++ b/e2e/modal-navigation-ng/references.d.ts
@@ -0,0 +1,2 @@
+///
+///
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/tsconfig.esm.json b/e2e/modal-navigation-ng/tsconfig.esm.json
new file mode 100644
index 000000000..a96f6bbc6
--- /dev/null
+++ b/e2e/modal-navigation-ng/tsconfig.esm.json
@@ -0,0 +1,7 @@
+{
+ "extends": "./tsconfig",
+ "compilerOptions": {
+ "module": "ESNext",
+ "moduleResolution": "node"
+ }
+}
diff --git a/e2e/modal-navigation-ng/tsconfig.json b/e2e/modal-navigation-ng/tsconfig.json
new file mode 100644
index 000000000..1d8622eed
--- /dev/null
+++ b/e2e/modal-navigation-ng/tsconfig.json
@@ -0,0 +1,34 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es5",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "noEmitHelpers": true,
+ "noEmitOnError": true,
+ "skipLibCheck": true,
+ "lib": [
+ "es2017",
+ "dom",
+ "es6"
+ ],
+ "baseUrl": ".",
+ "paths": {
+ "~/*": [
+ "app/*"
+ ]
+ }
+ },
+ "includes": [
+ "./references.d.ts"
+ ],
+ "files": [
+ "./app/main.ts"
+ ],
+ "exclude": [
+ "node_modules",
+ "platforms",
+ "**/*.aot",
+ "e2e"
+ ]
+}
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/.gitignore b/e2e/nested-router-tab-view/.gitignore
new file mode 100644
index 000000000..e2c82407c
--- /dev/null
+++ b/e2e/nested-router-tab-view/.gitignore
@@ -0,0 +1,13 @@
+.vscode
+
+platforms
+node_modules
+hooks
+
+/**/*.js
+/**/*.map
+e2e/reports
+test-results.xml
+
+instrumentscli*.trace
+mochawesome-report
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/app.gradle b/e2e/nested-router-tab-view/app/App_Resources/Android/app.gradle
new file mode 100644
index 000000000..680e43542
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/App_Resources/Android/app.gradle
@@ -0,0 +1,16 @@
+// Add your native dependencies here:
+
+// Uncomment to add recyclerview-v7 dependency
+//dependencies {
+// compile 'com.android.support:recyclerview-v7:+'
+//}
+
+android {
+ defaultConfig {
+ generatedDensities = []
+ applicationId = "org.nativescript.nestedroutertabview"
+ }
+ aaptOptions {
+ additionalParameters "--no-version-vectors"
+ }
+}
diff --git a/e2e/router/app/App_Resources/Android/AndroidManifest.xml b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/AndroidManifest.xml
similarity index 100%
rename from e2e/router/app/App_Resources/Android/AndroidManifest.xml
rename to e2e/nested-router-tab-view/app/App_Resources/Android/src/main/AndroidManifest.xml
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png
new file mode 100644
index 000000000..eb381c258
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png
new file mode 100755
index 000000000..9cde84cd5
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png
new file mode 100644
index 000000000..5218f4c90
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png
new file mode 100644
index 000000000..748b2adf5
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png
new file mode 100755
index 000000000..4d6a674b3
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png
new file mode 100644
index 000000000..b9e102a76
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png
new file mode 100644
index 000000000..efeaf2907
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png
new file mode 100755
index 000000000..92ccc85a6
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png
new file mode 100644
index 000000000..626338766
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
new file mode 100644
index 000000000..ada77f92c
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
@@ -0,0 +1,8 @@
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png
new file mode 100644
index 000000000..612bbd072
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png
new file mode 100644
index 000000000..8bcde6277
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png
new file mode 100644
index 000000000..ad8ee2f4b
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png
new file mode 100644
index 000000000..0fa88e235
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png
new file mode 100644
index 000000000..9d81c85dc
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png
new file mode 100644
index 000000000..668327832
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png
new file mode 100644
index 000000000..c650f6438
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png
new file mode 100644
index 000000000..9a34d0d43
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png
new file mode 100644
index 000000000..fa6331c8d
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/values-v21/colors.xml b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/values-v21/colors.xml
new file mode 100644
index 000000000..a64641a9d
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/values-v21/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #3d5afe
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/values-v21/styles.xml b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/values-v21/styles.xml
new file mode 100644
index 000000000..dac8727c8
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/values-v21/styles.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/values/colors.xml b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/values/colors.xml
new file mode 100644
index 000000000..74ad8829c
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #F5F5F5
+ #757575
+ #33B5E5
+ #272734
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/values/styles.xml b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/values/styles.xml
new file mode 100644
index 000000000..c793e6d4c
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/App_Resources/Android/src/main/res/values/styles.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..4034b76e6
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "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"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "icon-1024.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png
new file mode 100644
index 000000000..a1d7eb479
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png
new file mode 100644
index 000000000..bb9b9e86d
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png
new file mode 100644
index 000000000..ec609dcf3
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png
new file mode 100644
index 000000000..a97180800
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png
new file mode 100644
index 000000000..214800ee6
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
new file mode 100644
index 000000000..8554b88a8
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
new file mode 100644
index 000000000..a22626bae
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
new file mode 100644
index 000000000..a22626bae
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
new file mode 100644
index 000000000..492c9c8e8
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png
new file mode 100644
index 000000000..9208113cf
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
new file mode 100644
index 000000000..24415e5a3
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png
new file mode 100644
index 000000000..b3ef1bf0c
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/Contents.json b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/Contents.json
new file mode 100644
index 000000000..da4a164c9
--- /dev/null
+++ b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 000000000..11bfcf55c
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,176 @@
+{
+ "images" : [
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "2436h",
+ "filename" : "Default-1125h.png",
+ "minimum-system-version" : "11.0",
+ "orientation" : "portrait",
+ "scale" : "3x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "iphone",
+ "extent" : "full-screen",
+ "filename" : "Default-Landscape-X.png",
+ "minimum-system-version" : "11.0",
+ "subtype" : "2436h",
+ "scale" : "3x"
+ },
+ {
+ "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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png
new file mode 100644
index 000000000..2913f85d9
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
new file mode 100644
index 000000000..cd94a3ac2
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
new file mode 100644
index 000000000..9f1f6ce3e
Binary files /dev/null and b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
new file mode 100644
index 000000000..4f4e9c506
--- /dev/null
+++ b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json b/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
new file mode 100644
index 000000000..23c0ffd7a
--- /dev/null
+++ b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png differ
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/Info.plist b/e2e/nested-router-tab-view/app/App_Resources/iOS/Info.plist
new file mode 100644
index 000000000..ea3e3ea23
--- /dev/null
+++ b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/App_Resources/iOS/LaunchScreen.storyboard b/e2e/nested-router-tab-view/app/App_Resources/iOS/LaunchScreen.storyboard
new file mode 100644
index 000000000..2ad9471e1
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/App_Resources/iOS/LaunchScreen.storyboard
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/nested-router-tab-view/app/App_Resources/iOS/build.xcconfig b/e2e/nested-router-tab-view/app/App_Resources/iOS/build.xcconfig
new file mode 100644
index 000000000..4b0118490
--- /dev/null
+++ b/e2e/nested-router-tab-view/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/nested-router-tab-view/app/about/about-nested.component.html b/e2e/nested-router-tab-view/app/about/about-nested.component.html
new file mode 100644
index 000000000..0e39308c8
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/about/about-nested.component.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/about/about-nested.component.ts b/e2e/nested-router-tab-view/app/about/about-nested.component.ts
new file mode 100644
index 000000000..ff1197b7e
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/about/about-nested.component.ts
@@ -0,0 +1,9 @@
+import { Component } from "@angular/core";
+
+@Component({
+ moduleId: module.id,
+ selector: "about-nested-page",
+ templateUrl: "./about-nested.component.html"
+})
+export class AboutNestedComponent {
+}
diff --git a/e2e/nested-router-tab-view/app/about/about.component.html b/e2e/nested-router-tab-view/app/about/about.component.html
new file mode 100644
index 000000000..9f9ae3731
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/about/about.component.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/about/about.component.ts b/e2e/nested-router-tab-view/app/about/about.component.ts
new file mode 100644
index 000000000..f1132181c
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/about/about.component.ts
@@ -0,0 +1,21 @@
+import { Component } from "@angular/core";
+import { RouterExtensions } from "@nativescript/angular/router";
+import { ActivatedRoute } from "@angular/router";
+
+@Component({
+ moduleId: module.id,
+ selector: "about-page",
+ templateUrl: "./about.component.html"
+})
+export class AboutComponent {
+ constructor(
+ private activeRoute: ActivatedRoute,
+ private routerExtension: RouterExtensions) { }
+
+ ngOnInit() {
+ }
+
+ backActivatedRoute() {
+ this.routerExtension.back({ relativeTo: this.activeRoute });
+ }
+}
diff --git a/e2e/nested-router-tab-view/app/app.component.html b/e2e/nested-router-tab-view/app/app.component.html
new file mode 100644
index 000000000..74b776fc0
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/app.component.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/app.component.ts b/e2e/nested-router-tab-view/app/app.component.ts
new file mode 100644
index 000000000..8a132b0a1
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/app.component.ts
@@ -0,0 +1,26 @@
+import { Component, OnInit, AfterViewInit, AfterContentInit, ViewChild } from "@angular/core";
+import { TabViewDirective } from "@nativescript/angular/directives";
+import { Router, NavigationEnd } from "@angular/router";
+import { NSLocationStrategy } from "@nativescript/angular";
+
+
+@Component({
+ selector: "ns-app",
+ templateUrl: "app.component.html",
+})
+
+export class AppComponent {
+ private isInitialNavigation = true;
+
+ @ViewChild(TabViewDirective, { static: false }) tabView: TabViewDirective;
+
+ constructor(router: Router, location: NSLocationStrategy) {
+ router.events.subscribe(e => {
+ if (e instanceof NavigationEnd) {
+ this.isInitialNavigation = false;
+ console.log("[ROUTER]: " + e.toString());
+ console.log(location.toString());
+ }
+ })
+ }
+}
diff --git a/e2e/nested-router-tab-view/app/app.css b/e2e/nested-router-tab-view/app/app.css
new file mode 100644
index 000000000..57ce8e6d5
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/app.css
@@ -0,0 +1,43 @@
+Button{
+ color: blue;
+}
+
+TextView{
+ color: green;
+}
+
+Button{
+ font-size: 8;
+ padding-left: 5;
+ padding-right: 5;
+ padding-top: 0;
+ padding-bottom: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+ margin-right: 0;
+ height: 50px;
+ color: blue;
+}
+
+TextView{
+ font-size: 10;
+ margin: 0;
+ padding: 0;
+ color: green;
+}
+
+Label{
+ font-size: 10;
+ margin: 0;
+ padding: 0;
+}
+
+GridLayout{
+ margin: 0;
+ padding: 0;
+}
+
+ActionBar{
+ height: 30;
+ margin: 0;
+}
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/app.module.ngfactory.d.ts b/e2e/nested-router-tab-view/app/app.module.ngfactory.d.ts
new file mode 100644
index 000000000..793157de3
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/app.module.ngfactory.d.ts
@@ -0,0 +1,4 @@
+/**
+ * A dynamically generated module when compiled with AoT.
+ */
+export const AppModuleNgFactory: any;
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/app.module.ts b/e2e/nested-router-tab-view/app/app.module.ts
new file mode 100644
index 000000000..acb5e56fb
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/app.module.ts
@@ -0,0 +1,50 @@
+import { NgModule, NO_ERRORS_SCHEMA, ErrorHandler, NgModuleFactoryLoader } from "@angular/core";
+import { NativeScriptModule } from "@nativescript/angular";
+import { AppRoutingModule, COMPONENTS, MODALCOMPONENTS } from "./app.routing";
+import { AppComponent } from "./app.component";
+
+import { DataService } from "./data.service";
+import { NSModuleFactoryLoader } from "@nativescript/angular/router";
+
+import { SharedModule } from "./shared.module";
+import { enable as traceEnable, addCategories } from "@nativescript/core/trace";
+import { routerTraceCategory } from "@nativescript/angular/trace";
+
+// addCategories(routerTraceCategory);
+traceEnable();
+
+export class MyErrorHandler implements ErrorHandler {
+ handleError(error) {
+ console.log("### ErrorHandler Error: " + error.toString());
+ console.log("### ErrorHandler Stack: " + error.stack);
+ }
+}
+
+@NgModule({
+ bootstrap: [
+ AppComponent
+ ],
+ entryComponents: [MODALCOMPONENTS],
+ imports: [
+ SharedModule,
+ NativeScriptModule,
+ AppRoutingModule,
+ ],
+ declarations: [
+ AppComponent,
+ ...COMPONENTS,
+ MODALCOMPONENTS
+ ],
+ providers: [
+ DataService,
+ { provide: ErrorHandler, useClass: MyErrorHandler },
+ { provide: NgModuleFactoryLoader, useClass: NSModuleFactoryLoader }
+ ],
+ schemas: [
+ NO_ERRORS_SCHEMA
+ ]
+})
+/*
+Pass your application module to the bootstrapModule function located in main.ts to start your app
+*/
+export class AppModule { }
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/app.routing.ts b/e2e/nested-router-tab-view/app/app.routing.ts
new file mode 100644
index 000000000..745991f51
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/app.routing.ts
@@ -0,0 +1,77 @@
+import { NgModule } from "@angular/core";
+import { NativeScriptRouterModule } from "@nativescript/angular/router";
+import { Routes } from "@angular/router";
+
+import { PlayerComponent } from "./player/players.component";
+import { PlayerDetailComponent } from "./player/player-detail.component";
+import { TeamsComponent } from "./team/teams.component";
+import { TeamDetailComponent } from "./team/team-detail.component";
+import { LoginComponent } from "./login/login.component";
+import { TabsComponent } from "./tabs/tabs.component";
+import { HomeComponent } from "./home/home.component";
+import { HomeLazyModule } from "./home-lazy/home-lazy.module";
+import { CustomTabsModule } from "./custom-tabs/custom-tabs.module"
+import { AboutComponent } from "./about/about.component";
+import { AboutNestedComponent } from "./about/about-nested.component";
+
+import { ModalComponent } from "./modal/modal.component";
+import { NestedModalComponent } from "./modal-nested/modal-nested.component";
+import { ModalSecondComponent } from "./modal-second/modal-second.component";
+import { ModalRouterComponent } from "./modal/modal-router/modal-router.component";
+
+export const COMPONENTS = [LoginComponent, TabsComponent];
+export const MODALCOMPONENTS = [ModalComponent, NestedModalComponent, ModalSecondComponent, ModalRouterComponent];
+
+const routes: Routes = [
+ { path: "", redirectTo: "/login", pathMatch: "full" },
+ //{ path: "", component: LoginComponent },
+ {
+ path: "login", component: LoginComponent
+ },
+ {
+ path: "home", component: HomeComponent, children: [
+ {
+ path: "players", component: PlayerComponent, outlet: "playerTab", children: [
+ {
+ path: "modal", outlet: "modalOutlet", component: ModalComponent, children: [
+ { path: "nested-frame-modal", component: NestedModalComponent }]
+ },
+ { path: "modal-second", outlet: "modalOutlet", component: ModalSecondComponent }
+ ]
+ },
+ { path: "player/:id", component: PlayerDetailComponent, outlet: "playerTab" },
+
+ { path: "teams", component: TeamsComponent, outlet: "teamTab" },
+ { path: "team/:id", component: TeamDetailComponent, outlet: "teamTab" },
+
+ ]
+ },
+ {
+ path: "home-lazy",
+ loadChildren: () => HomeLazyModule,
+ },
+ {
+ path: "custom-tabs",
+ loadChildren: () => CustomTabsModule,
+ },
+ {
+ path: "tabs", component: TabsComponent, children: [
+ { path: "players", component: PlayerComponent, outlet: "playerTab" },
+ { path: "player/:id", component: PlayerDetailComponent, outlet: "playerTab" },
+
+ { path: "teams", component: TeamsComponent, outlet: "teamTab" },
+ { path: "team/:id", component: TeamDetailComponent, outlet: "teamTab" },
+ ]
+ },
+ {
+ path: "about", component: AboutComponent, children: [
+ { path: "about-nested", component: AboutNestedComponent },
+ ]
+ }
+];
+
+@NgModule({
+ imports: [NativeScriptRouterModule.forRoot(routes)],
+ exports: [NativeScriptRouterModule],
+})
+export class AppRoutingModule { }
diff --git a/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.component.html b/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.component.html
new file mode 100644
index 000000000..851c8765a
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.component.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.component.ts b/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.component.ts
new file mode 100644
index 000000000..d46cb3d2b
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.component.ts
@@ -0,0 +1,44 @@
+import { Component, OnInit } from '@angular/core';
+import { ModalDialogService, ModalDialogOptions } from "@nativescript/angular/directives/dialogs";
+import { RouterExtensions } from "@nativescript/angular/router";
+import { ActivatedRoute } from "@angular/router";
+import { confirm } from "@nativescript/core/ui/dialogs";
+import { Page } from 'tns-core-modules/ui/page/page';
+
+@Component({
+ moduleId: module.id,
+ selector: 'custom-tabs',
+ templateUrl: './custom-tabs.component.html'
+})
+export class CustomTabsComponent implements OnInit {
+
+ constructor(
+ private activeRoute: ActivatedRoute,
+ private routerExtension: RouterExtensions,
+ private page: Page) { }
+
+ ngOnInit() {
+ }
+
+ canGoBackParentRoute() {
+ const canGoBackParentRoute = this.routerExtension.canGoBack({ relativeTo: this.activeRoute });
+ const title = "CanGoBack(ParentRoute)";
+ this.onShowDialog(title, title + ` ${canGoBackParentRoute}`);
+ }
+
+ onRootBack() {
+ this.page.frame.goBack();
+ }
+
+ onShowDialog(title: string, result: string) {
+ let options: any = {
+ title: title,
+ message: result,
+ okButtonText: "Ok"
+ }
+
+ confirm(options).then((result: boolean) => {
+ console.log(result);
+ })
+ }
+}
diff --git a/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.module.ts b/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.module.ts
new file mode 100644
index 000000000..cd29535d2
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.module.ts
@@ -0,0 +1,38 @@
+import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
+import { NativeScriptCommonModule } from 'nativescript-angular/common';
+import { NativeScriptRouterModule } from 'nativescript-angular/router';
+
+import { CustomTabsComponent } from './custom-tabs.component';
+import { PlayerComponent } from "../player/players.component";
+import { PlayerDetailComponent } from "../player/player-detail.component";
+import { TeamsComponent } from "../team/teams.component";
+import { TeamDetailComponent } from "../team/team-detail.component";
+import { Route } from "@angular/router";
+import { SharedModule } from "../shared.module";
+
+const routes: Route[] = [
+ {
+ path: 'tabs',
+ component: CustomTabsComponent,
+ children: [
+ { path: "players", component: PlayerComponent },
+ { path: "player/:id", component: PlayerDetailComponent },
+
+ { path: "teams", component: TeamsComponent },
+ { path: "team/:id", component: TeamDetailComponent },
+ ]
+ },
+];
+
+@NgModule({
+ declarations: [CustomTabsComponent
+ ],
+ imports: [
+ NativeScriptCommonModule,
+ NativeScriptRouterModule,
+ NativeScriptRouterModule.forChild(routes),
+ SharedModule
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+})
+export class CustomTabsModule { }
diff --git a/e2e/nested-router-tab-view/app/data.service.ts b/e2e/nested-router-tab-view/app/data.service.ts
new file mode 100644
index 000000000..8242ebd77
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/data.service.ts
@@ -0,0 +1,35 @@
+import { Injectable } from "@angular/core";
+
+export interface DataItem {
+ id: number;
+ name: string;
+}
+
+@Injectable()
+export class DataService {
+ private players = new Array(
+ { id: 1, name: "Player One" },
+ { id: 2, name: "Player Two" },
+ );
+
+ private teams = new Array(
+ { id: 1, name: "Team One" },
+ { id: 2, name: "Team Two" },
+ );
+
+ getPlayers(): DataItem[] {
+ return this.players;
+ }
+
+ getPlayer(id: number): DataItem {
+ return this.players.filter(item => item.id === id)[0];
+ }
+
+ getTeams(): DataItem[] {
+ return this.teams;
+ }
+
+ getTeam(id: number): DataItem {
+ return this.teams.filter(item => item.id === id)[0];
+ }
+}
diff --git a/e2e/nested-router-tab-view/app/home-lazy/home-lazy.module.ts b/e2e/nested-router-tab-view/app/home-lazy/home-lazy.module.ts
new file mode 100644
index 000000000..a331ba054
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/home-lazy/home-lazy.module.ts
@@ -0,0 +1,40 @@
+import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+import { Route } from "@angular/router";
+
+import { NativeScriptCommonModule } from "@nativescript/angular/common";
+import { NativeScriptRouterModule } from "@nativescript/angular/router";
+import { PlayerComponent } from "../player/players.component";
+import { PlayerDetailComponent } from "../player/player-detail.component";
+import { TeamsComponent } from "../team/teams.component";
+import { TeamDetailComponent } from "../team/team-detail.component";
+import { HomeComponent } from "../home/home.component";
+import { DataService } from "../data.service";
+import { SharedModule } from "../shared.module";
+
+const routes: Route[] = [
+ {
+ path: "home",
+ component: HomeComponent,
+ children: [
+ { path: "players", component: PlayerComponent, outlet: "playerTab" },
+ { path: "player/:id", component: PlayerDetailComponent, outlet: "playerTab" },
+
+ { path: "teams", component: TeamsComponent, outlet: "teamTab" },
+ { path: "team/:id", component: TeamDetailComponent, outlet: "teamTab" },
+ ]
+ }
+];
+
+@NgModule({
+ schemas: [NO_ERRORS_SCHEMA],
+ imports: [
+ NativeScriptCommonModule,
+ NativeScriptRouterModule,
+ NativeScriptRouterModule.forChild(routes),
+ SharedModule
+ ],
+ providers: [
+ DataService
+ ]
+})
+export class HomeLazyModule { }
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/home/home.component.html b/e2e/nested-router-tab-view/app/home/home.component.html
new file mode 100644
index 000000000..bfd588cb0
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/home/home.component.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/home/home.component.ts b/e2e/nested-router-tab-view/app/home/home.component.ts
new file mode 100644
index 000000000..d0e7cdcb3
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/home/home.component.ts
@@ -0,0 +1,84 @@
+import { Component, ViewContainerRef } from "@angular/core";
+import { ModalDialogService, ModalDialogOptions } from "@nativescript/angular/directives/dialogs";
+import { RouterExtensions } from "@nativescript/angular/router";
+import { ActivatedRoute } from "@angular/router";
+import { confirm } from "@nativescript/core/ui/dialogs";
+
+@Component({
+ moduleId: module.id,
+ selector: "home-page",
+ templateUrl: "./home.component.html"
+})
+export class HomeComponent {
+ constructor(
+ private modal: ModalDialogService,
+ private vcRef: ViewContainerRef,
+ private activeRoute: ActivatedRoute,
+ private routerExtension: RouterExtensions) { }
+
+ ngOnInit() {
+ //this.routerExtension.navigate(["first"], { relativeTo: this.activeRoute });
+ //this.routerExtension.navigate([{ outlets: { playerTab: ["players"], teamTab: ["teams"] } }], { relativeTo: this.activeRoute });
+ //this.routerExtension.navigate(['players'], { relativeTo: this.activeRoute });
+ }
+
+ canGoBack() {
+ const canGoBack = this.routerExtension.canGoBack({ relativeTo: this.activeRoute });
+ const title = "CanGoBack(ActivatedRoute)";
+ this.onShowDialog(title, title + ` ${canGoBack}`);
+ }
+
+ canGoBackPlayers() {
+ const canGoBack = this.routerExtension.canGoBack({ outlets: ["playerTab"], relativeTo: this.activeRoute });
+ const title = "CanGoBack(Players)";
+ this.onShowDialog(title, title + ` ${canGoBack}`);
+ }
+
+ canGoBackTeams() {
+ const canGoBack = this.routerExtension.canGoBack({ outlets: ["teamTab"], relativeTo: this.activeRoute });
+ const title = "CanGoBack(Teams)";
+ this.onShowDialog(title, title + ` ${canGoBack}`);
+ }
+
+ canGoBackBoth() {
+ const canGoBack = this.routerExtension.canGoBack({ outlets: ["teamTab", "playerTab"], relativeTo: this.activeRoute });
+ const title = "CanGoBack(Both)";
+ this.onShowDialog(title, title + ` ${canGoBack}`);
+ }
+
+ backPlayers() {
+ this.routerExtension.back({ outlets: ["playerTab"], relativeTo: this.activeRoute });
+ }
+
+ backTeams() {
+ this.routerExtension.back({ outlets: ["teamTab"], relativeTo: this.activeRoute });
+ }
+
+ backBoth() {
+ this.routerExtension.back({ outlets: ["teamTab", "playerTab"], relativeTo: this.activeRoute });
+ }
+
+ backActivatedRoute() {
+ this.routerExtension.back({ relativeTo: this.activeRoute });
+ }
+
+ back() {
+ this.routerExtension.back();
+ }
+
+ navigatePlayers() {
+ this.routerExtension.navigate([{ outlets: { playerTab: ['player', '1'] } }], { relativeTo: this.activeRoute, animated: true, transition: { name: "flip", duration: 2000, curve: "linear" } });
+ }
+
+ onShowDialog(title: string, result: string) {
+ let options: any = {
+ title: title,
+ message: result,
+ okButtonText: "Ok"
+ }
+
+ confirm(options).then((result: boolean) => {
+ console.log(result);
+ })
+ }
+}
diff --git a/e2e/nested-router-tab-view/app/login/login.component.html b/e2e/nested-router-tab-view/app/login/login.component.html
new file mode 100644
index 000000000..26e1c80ac
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/login/login.component.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/login/login.component.ts b/e2e/nested-router-tab-view/app/login/login.component.ts
new file mode 100644
index 000000000..6eee0a982
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/login/login.component.ts
@@ -0,0 +1,19 @@
+import { Component, ViewContainerRef } from "@angular/core";
+import { ModalDialogService, ModalDialogOptions } from "@nativescript/angular/directives/dialogs";
+import { RouterExtensions } from "@nativescript/angular/router";
+import { EventData } from "@nativescript/core/data/observable";
+import { confirm } from "@nativescript/core/ui/dialogs";
+
+import { AppModule } from "../app.module";
+
+@Component({
+ moduleId: module.id,
+ selector: "login-page",
+ templateUrl: "./login.component.html"
+})
+export class LoginComponent {
+ constructor(
+ private modal: ModalDialogService,
+ private vcRef: ViewContainerRef,
+ private routerExtension: RouterExtensions) { }
+}
diff --git a/e2e/nested-router-tab-view/app/main.ts b/e2e/nested-router-tab-view/app/main.ts
new file mode 100644
index 000000000..f61a19110
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/main.ts
@@ -0,0 +1,10 @@
+// this import should be first in order to load some required settings (like globals and reflect-metadata)
+import { platformNativeScriptDynamic } from "@nativescript/angular/platform";
+
+import { AppModule } from "./app.module";
+
+// A traditional NativeScript application starts by initializing global objects, setting up global CSS rules, creating, and navigating to the main page.
+// Angular applications need to take care of their own initialization: modules, components, directives, routes, DI providers.
+// A NativeScript Angular app needs to make both paradigms work together, so we provide a wrapper platform object, platformNativeScriptDynamic,
+// that sets up a NativeScript application and can bootstrap the Angular framework.
+platformNativeScriptDynamic().bootstrapModule(AppModule);
diff --git a/e2e/nested-router-tab-view/app/modal-nested/modal-nested.component.html b/e2e/nested-router-tab-view/app/modal-nested/modal-nested.component.html
new file mode 100644
index 000000000..13c391cb7
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/modal-nested/modal-nested.component.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/modal-nested/modal-nested.component.ts b/e2e/nested-router-tab-view/app/modal-nested/modal-nested.component.ts
new file mode 100644
index 000000000..4dd3c7ee6
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/modal-nested/modal-nested.component.ts
@@ -0,0 +1,26 @@
+import { Component } from "@angular/core";
+import { View, ShownModallyData } from "@nativescript/core/ui/core/view"
+import { ModalDialogParams } from "@nativescript/angular/directives/dialogs";
+
+@Component({
+ moduleId: module.id,
+ selector: "modal-nested-page",
+ templateUrl: "./modal-nested.component.html"
+})
+export class NestedModalComponent {
+ public navigationVisibility: string = "collapse";
+
+ constructor(private params: ModalDialogParams) {
+
+ console.log("ModalNestedContent.constructor: " + JSON.stringify(params));
+ this.navigationVisibility = params.context.navigationVisibility ? "visible" : "collapse";
+ }
+
+ close(layoutRoot: View) {
+ layoutRoot.closeModal();
+ }
+
+ onShowingModally(args: ShownModallyData) {
+ console.log("modal-page showingModally");
+ }
+}
diff --git a/e2e/nested-router-tab-view/app/modal-second/modal-second.component.html b/e2e/nested-router-tab-view/app/modal-second/modal-second.component.html
new file mode 100644
index 000000000..27a997c2f
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/modal-second/modal-second.component.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/modal-second/modal-second.component.ts b/e2e/nested-router-tab-view/app/modal-second/modal-second.component.ts
new file mode 100644
index 000000000..3eb901a0d
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/modal-second/modal-second.component.ts
@@ -0,0 +1,25 @@
+import { Component } from "@angular/core";
+import { ActivatedRoute } from "@angular/router";
+import { View, EventData } from "@nativescript/core/ui/core/view"
+import { RouterExtensions } from "@nativescript/angular/router";
+
+@Component({
+ moduleId: module.id,
+ selector: "modal-second-page",
+ templateUrl: "./modal-second.component.html"
+})
+export class ModalSecondComponent {
+ constructor(private routerExtension: RouterExtensions, private activeRoute: ActivatedRoute) { }
+
+ onLoaded() {
+ console.log("modal-second loaded");
+ }
+
+ goBack() {
+ this.routerExtension.back({ relativeTo: this.activeRoute });
+ }
+
+ close(layoutRoot: View) {
+ layoutRoot.closeModal();
+ }
+}
diff --git a/e2e/nested-router-tab-view/app/modal/modal-router/modal-router.component.html b/e2e/nested-router-tab-view/app/modal/modal-router/modal-router.component.html
new file mode 100644
index 000000000..f4a99c901
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/modal/modal-router/modal-router.component.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/modal/modal-router/modal-router.component.ts b/e2e/nested-router-tab-view/app/modal/modal-router/modal-router.component.ts
new file mode 100644
index 000000000..4c7105224
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/modal/modal-router/modal-router.component.ts
@@ -0,0 +1,23 @@
+import { Component, OnInit } from "@angular/core";
+import { ActivatedRoute } from "@angular/router";
+import { RouterExtensions } from "@nativescript/angular/router";
+import { ModalDialogParams } from "@nativescript/angular/directives/dialogs";
+
+@Component({
+ moduleId: module.id,
+ selector: "ns-modal-router",
+ templateUrl: "./modal-router.component.html",
+})
+
+export class ModalRouterComponent implements OnInit {
+ private modalRoute: string;
+
+ constructor(private params: ModalDialogParams, private routerExtension: RouterExtensions, private activeRoute: ActivatedRoute) {
+ this.modalRoute = params.context.modalRoute;
+ }
+
+ ngOnInit() {
+ //this.routerExtension.navigate([this.modalRoute], { relativeTo: this.activeRoute });
+ this.routerExtension.navigate([{ outlets: { modalOutlet: [ this.modalRoute] } }], { relativeTo: this.activeRoute });
+ }
+}
diff --git a/e2e/nested-router-tab-view/app/modal/modal.component.html b/e2e/nested-router-tab-view/app/modal/modal.component.html
new file mode 100644
index 000000000..2077becc8
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/modal/modal.component.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/modal/modal.component.ts b/e2e/nested-router-tab-view/app/modal/modal.component.ts
new file mode 100644
index 000000000..8ba0c2ae8
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/modal/modal.component.ts
@@ -0,0 +1,87 @@
+import { Component, ViewContainerRef } from "@angular/core";
+import { ModalDialogParams, ModalDialogOptions, ModalDialogService } from "@nativescript/angular/directives/dialogs";
+import { RouterExtensions, PageRoute } from "@nativescript/angular/router";
+import { ActivatedRoute } from "@angular/router";
+import { View, ShownModallyData } from "@nativescript/core/ui/core/view"
+import { confirm } from "@nativescript/core/ui/dialogs";
+import { ModalRouterComponent } from "../modal/modal-router/modal-router.component";
+import { NestedModalComponent } from "../modal-nested/modal-nested.component";
+
+@Component({
+ moduleId: module.id,
+ selector: "modal-page",
+ templateUrl: "./modal.component.html"
+})
+export class ModalComponent {
+ public navigationVisibility: string = "collapse";
+
+ constructor(private params: ModalDialogParams,
+ private vcRef: ViewContainerRef,
+ private routerExtension: RouterExtensions,
+ private activeRoute: ActivatedRoute,
+ private modal: ModalDialogService) {
+
+ console.log("\nModalContent.constructor: " + JSON.stringify(params));
+ this.navigationVisibility = params.context.navigationVisibility ? "visible" : "collapse";
+ }
+
+ close(layoutRoot: View) {
+ this.modal.closeModal();
+ // layoutRoot.closeModal();
+ }
+
+ ngOnInit() {
+ }
+
+ ngOnDestroy() {
+ console.log("ModalContent.ngOnDestroy");
+ }
+
+ onShowingModally(args: ShownModallyData) {
+ console.log("modal-page showingModally");
+ }
+
+ showDialogConfirm() {
+ let options = {
+ title: "Dialog",
+ message: "Message",
+ okButtonText: "Yes",
+ cancelButtonText: "No"
+ }
+
+ confirm(options).then((result: boolean) => {
+ console.log(result);
+ })
+ }
+
+ showNestedModalFrame() {
+ const options: ModalDialogOptions = {
+ context: {
+ navigationVisibility: true,
+ modalRoute: "nested-frame-modal"
+ },
+ fullscreen: true,
+ viewContainerRef: this.vcRef
+ };
+
+ this.modal.showModal(ModalRouterComponent, options).then((res: string) => {
+ console.log("nested-modal-frame closed");
+ });
+ }
+
+ showNestedModal() {
+ const options: ModalDialogOptions = {
+ context: { navigationVisibility: false },
+ fullscreen: true,
+ viewContainerRef: this.vcRef
+ };
+
+ this.modal.showModal(NestedModalComponent, options).then((res: string) => {
+ console.log("nested-modal closed");
+ });
+ }
+
+ onNavigateSecondPage() {
+ this.routerExtension.navigate(["../modal-second"], { relativeTo: this.activeRoute });
+ }
+}
diff --git a/e2e/nested-router-tab-view/app/package.json b/e2e/nested-router-tab-view/app/package.json
new file mode 100644
index 000000000..e6521c70c
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/package.json
@@ -0,0 +1,9 @@
+{
+ "android": {
+ "v8Flags": "--expose_gc",
+ "markingMode": "none"
+ },
+ "main": "main.js",
+ "name": "tns-template-hello-world-ng",
+ "version": "3.4.3"
+}
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/player/player-detail.component.html b/e2e/nested-router-tab-view/app/player/player-detail.component.html
new file mode 100644
index 000000000..ea76ab3cd
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/player/player-detail.component.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/nested-router-tab-view/app/player/player-detail.component.ts b/e2e/nested-router-tab-view/app/player/player-detail.component.ts
new file mode 100644
index 000000000..c9b431d12
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/player/player-detail.component.ts
@@ -0,0 +1,37 @@
+import { Component, OnInit } from "@angular/core";
+import { ActivatedRoute } from "@angular/router";
+import { RouterExtensions } from "@nativescript/angular/router";
+import { DataService, DataItem } from "../data.service";
+import { Subscription } from "rxjs";
+
+@Component({
+ selector: "ns-player-details",
+ moduleId: module.id,
+ templateUrl: "./player-detail.component.html",
+})
+export class PlayerDetailComponent implements OnInit {
+ item: DataItem;
+ subscription: Subscription;
+
+ constructor(
+ private data: DataService,
+ private route: ActivatedRoute,
+ private routerExtension: RouterExtensions,
+ private activeRoute: ActivatedRoute,
+ ) { }
+
+ ngOnInit(): void {
+ this.subscription = this.route.params.subscribe(params => {
+ const id = +params["id"];
+ this.item = this.data.getPlayer(id);
+ })
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+
+ backPlayers() {
+ this.routerExtension.back({ relativeTo: this.activeRoute });
+ }
+}
diff --git a/e2e/nested-router-tab-view/app/player/players.component.html b/e2e/nested-router-tab-view/app/player/players.component.html
new file mode 100644
index 000000000..7dc729f6d
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/player/players.component.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/player/players.component.ts b/e2e/nested-router-tab-view/app/player/players.component.ts
new file mode 100644
index 000000000..65ca694b7
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/player/players.component.ts
@@ -0,0 +1,37 @@
+import { Component, OnInit, ViewContainerRef } from "@angular/core";
+import { DataService, DataItem } from "../data.service";
+
+import { ModalDialogService, ModalDialogOptions } from "@nativescript/angular/directives/dialogs";
+import { ModalRouterComponent } from "../modal/modal-router/modal-router.component";
+@Component({
+ selector: "ns-players",
+ moduleId: module.id,
+ templateUrl: "./players.component.html",
+})
+export class PlayerComponent implements OnInit {
+ items: DataItem[];
+
+ constructor(
+ private modal: ModalDialogService,
+ private itemService: DataService,
+ private vcRef: ViewContainerRef, ) { }
+
+ ngOnInit(): void {
+ this.items = this.itemService.getPlayers();
+ }
+
+ onModalFrame() {
+ const options: ModalDialogOptions = {
+ context: {
+ navigationVisibility: true,
+ modalRoute: "modal"
+ },
+ fullscreen: true,
+ viewContainerRef: this.vcRef
+ };
+
+ this.modal.showModal(ModalRouterComponent, options).then((res: string) => {
+ console.log("moda-frame closed");
+ });
+ }
+}
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/shared.module.ts b/e2e/nested-router-tab-view/app/shared.module.ts
new file mode 100644
index 000000000..a26b47d0d
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/shared.module.ts
@@ -0,0 +1,47 @@
+import { NgModule, NO_ERRORS_SCHEMA, ErrorHandler, NgModuleFactoryLoader } from "@angular/core";
+// import { NativeScriptModule } from "@nativescript/angular";
+// import { AppRoutingModule, COMPONENTS } from "./app.routing";
+// import { AppComponent } from "./app.component";
+import { NativeScriptCommonModule } from "@nativescript/angular/common";
+import { NativeScriptRouterModule } from "@nativescript/angular/router";
+import { DataService } from "./data.service";
+
+import { HomeComponent } from "./home/home.component";
+import { AboutComponent } from "./about/about.component";
+import { AboutNestedComponent } from "./about/about-nested.component";
+import { PlayerComponent } from "./player/players.component";
+import { PlayerDetailComponent } from "./player/player-detail.component";
+import { TeamsComponent } from "./team/teams.component";
+import { TeamDetailComponent } from "./team/team-detail.component";
+
+@NgModule({
+ imports: [
+ NativeScriptCommonModule,
+ NativeScriptRouterModule,
+ ],
+ declarations: [
+ HomeComponent,
+ AboutComponent,
+ AboutNestedComponent,
+ PlayerComponent,
+ PlayerDetailComponent,
+ TeamsComponent,
+ TeamDetailComponent
+ ],
+ exports: [
+ HomeComponent,
+ AboutComponent,
+ AboutNestedComponent,
+ PlayerComponent,
+ PlayerDetailComponent,
+ TeamsComponent,
+ TeamDetailComponent
+ ],
+ schemas: [
+ NO_ERRORS_SCHEMA
+ ]
+})
+/*
+Pass your application module to the bootstrapModule function located in main.ts to start your app
+*/
+export class SharedModule { }
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/tabs/tabs.component.html b/e2e/nested-router-tab-view/app/tabs/tabs.component.html
new file mode 100644
index 000000000..4e1ddec84
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/tabs/tabs.component.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/tabs/tabs.component.ts b/e2e/nested-router-tab-view/app/tabs/tabs.component.ts
new file mode 100644
index 000000000..35dd5fc13
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/tabs/tabs.component.ts
@@ -0,0 +1,55 @@
+import { Component, ViewContainerRef } from "@angular/core";
+import { ModalDialogService, ModalDialogOptions } from "@nativescript/angular/directives/dialogs";
+import { RouterExtensions } from "@nativescript/angular/router";
+import { EventData } from "@nativescript/core/data/observable";
+import { ActivatedRoute } from "@angular/router";
+import { confirm } from "@nativescript/core/ui/dialogs";
+import { Page } from "@nativescript/core/ui/page";
+import { AppModule } from "../app.module";
+
+@Component({
+ moduleId: module.id,
+ selector: "tabs-page",
+ templateUrl: "./tabs.component.html"
+})
+export class TabsComponent {
+ constructor(
+ private modal: ModalDialogService,
+ private vcRef: ViewContainerRef,
+ private routerExtension: RouterExtensions,
+ private activeRoute: ActivatedRoute,
+ private page: Page) {
+ // this.page.actionBarHidden = true;
+ }
+
+ ngOnInit() {
+ //this.routerExtension.navigate(["players"], { relativeTo: this.activeRoute });
+ this.routerExtension.navigate([{ outlets: { playerTab: ["players"], teamTab: ["teams"] } }], { relativeTo: this.activeRoute });
+ }
+
+ canGoBack() {
+ const canGoBack = this.routerExtension.canGoBack({ relativeTo: this.activeRoute });
+ const title = "CanGoBack(ActivatedRoute)";
+ this.onShowDialog(title, title + ` ${canGoBack}`);
+ }
+
+ backActivatedRoute() {
+ this.routerExtension.back({ relativeTo: this.activeRoute });
+ }
+
+ back() {
+ this.routerExtension.back();
+ }
+
+ onShowDialog(title: string, result: string) {
+ let options: any = {
+ title: title,
+ message: result,
+ okButtonText: "Ok"
+ }
+
+ confirm(options).then((result: boolean) => {
+ console.log(result);
+ })
+ }
+}
diff --git a/e2e/nested-router-tab-view/app/team/team-detail.component.html b/e2e/nested-router-tab-view/app/team/team-detail.component.html
new file mode 100644
index 000000000..18086c638
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/team/team-detail.component.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/nested-router-tab-view/app/team/team-detail.component.ts b/e2e/nested-router-tab-view/app/team/team-detail.component.ts
new file mode 100644
index 000000000..1bb7c3f85
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/team/team-detail.component.ts
@@ -0,0 +1,37 @@
+import { Component, OnInit } from "@angular/core";
+import { ActivatedRoute } from "@angular/router";
+import { RouterExtensions } from "@nativescript/angular/router";
+import { DataService, DataItem } from "../data.service";
+import { Subscription } from "rxjs";
+
+@Component({
+ selector: "ns-team-details",
+ moduleId: module.id,
+ templateUrl: "./team-detail.component.html",
+})
+export class TeamDetailComponent implements OnInit {
+ item: DataItem;
+ subscription: Subscription;
+
+ constructor(
+ private data: DataService,
+ private route: ActivatedRoute,
+ private routerExtension: RouterExtensions,
+ private activeRoute: ActivatedRoute,
+ ) { }
+
+ ngOnInit(): void {
+ this.subscription = this.route.params.subscribe(params => {
+ const id = +params["id"];
+ this.item = this.data.getTeam(id);
+ })
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+
+ backTeams() {
+ this.routerExtension.back({ relativeTo: this.activeRoute });
+ }
+}
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/app/team/teams.component.html b/e2e/nested-router-tab-view/app/team/teams.component.html
new file mode 100644
index 000000000..4af94ca66
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/team/teams.component.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/nested-router-tab-view/app/team/teams.component.ts b/e2e/nested-router-tab-view/app/team/teams.component.ts
new file mode 100644
index 000000000..119d1d2fe
--- /dev/null
+++ b/e2e/nested-router-tab-view/app/team/teams.component.ts
@@ -0,0 +1,17 @@
+import { Component, OnInit } from "@angular/core";
+import { DataService, DataItem } from "../data.service";
+
+@Component({
+ selector: "ns-teams",
+ moduleId: module.id,
+ templateUrl: "./teams.component.html",
+})
+export class TeamsComponent implements OnInit {
+ items: DataItem[];
+
+ constructor(private itemService: DataService) { }
+
+ ngOnInit(): void {
+ this.items = this.itemService.getTeams();
+ }
+}
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/e2e/custom-tabs.e2e-spec.ts b/e2e/nested-router-tab-view/e2e/custom-tabs.e2e-spec.ts
new file mode 100644
index 000000000..43b13ebbf
--- /dev/null
+++ b/e2e/nested-router-tab-view/e2e/custom-tabs.e2e-spec.ts
@@ -0,0 +1,84 @@
+import { AppiumDriver, createDriver, nsCapabilities } from "nativescript-dev-appium";
+import { Screen } from "./screen"
+import {
+ testPlayerNavigated,
+ testTeamNavigated
+} from "./shared.e2e-spec";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+const isSauceRun = isSauceLab;
+
+describe("custom-tabs:", async function () {
+ let driver: AppiumDriver;
+ let screen: Screen;
+
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ screen = new Screen(driver);
+ });
+
+ after(async function () {
+ if (isSauceRun) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ it("loaded custom tab component and tabs", async function () {
+ await screen.navigateCustomTabsPage();
+ await screen.loadedCustomTabsPage();
+ await screen.loadedPlayersList();
+ await gotoTeamsTab(driver);
+ await screen.loadedTeamList();
+ });
+
+ it("navigate back to login and again to custom tabs", async function () {
+ await backRoot(driver);
+ await screen.loadedLogin();
+ await screen.navigateCustomTabsPage();
+ await screen.loadedCustomTabsPage();
+ await screen.loadedPlayersList();
+ await gotoTeamsTab(driver);
+ await screen.loadedTeamList();
+ });
+
+ it("navigate to custom tabs player and team details", async function () {
+ await gotoPlayersTab(driver);
+ await testPlayerNavigated(screen, screen.playerOne);
+ await gotoTeamsTab(driver);
+ await screen.loadedTeamList();
+ await testTeamNavigated(screen, screen.teamOne);
+ await backRoot(driver);
+ await screen.loadedLogin();
+ });
+});
+
+async function backRoot(driver: AppiumDriver) {
+ const btnBackRoot = await driver.findElementByAutomationText("Root Back");
+ await btnBackRoot.tap();
+}
+
+async function gotoPlayersTab(driver: AppiumDriver) {
+ const btnTabTeams = await driver.findElementByAutomationText("Players Tab");
+ await btnTabTeams.tap();
+}
+
+async function gotoTeamsTab(driver: AppiumDriver) {
+ const btnTabTeams = await driver.findElementByAutomationText("Teams Tab");
+ await btnTabTeams.tap();
+}
diff --git a/e2e/nested-router-tab-view/e2e/home-tabs.e2e-spec.ts b/e2e/nested-router-tab-view/e2e/home-tabs.e2e-spec.ts
new file mode 100644
index 000000000..a6319c29c
--- /dev/null
+++ b/e2e/nested-router-tab-view/e2e/home-tabs.e2e-spec.ts
@@ -0,0 +1,161 @@
+import { AppiumDriver, createDriver, SearchOptions, nsCapabilities } from "nativescript-dev-appium";
+import { Screen } from "./screen"
+import {
+ testPlayerNavigated,
+ testTeamNavigated,
+ testPlayerNextNavigated
+} from "./shared.e2e-spec";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+const isSauceRun = isSauceLab;
+
+const pages = ["Go To Home Page", "Go To Lazy Home Page"];
+
+describe("home-tabs:", async function () {
+ let driver: AppiumDriver;
+ let screen: Screen;
+
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ screen = new Screen(driver);
+ });
+
+ after(async function () {
+ if (isSauceRun) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ for (let index = 0; index < pages.length; index++) {
+ const page = pages[index];
+ describe(`${page} home-tab:`, async function () {
+
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ it("loaded home component and lists", async function () {
+ await screen.navigateToHomePage(page);
+ await screen.loadedHome();
+ await screen.loadedPlayersList();
+ await screen.loadedTeamList();
+ });
+
+ it("should navigate to Tabs then to About forward", async function () {
+ await screen.navigateToTabsPage();
+ await screen.loadedTabs();
+ await screen.loadedPlayersList();
+
+ // TO DO: This is related to the tns-core-modules animations overhaul intiative (removing Animators in favor of androidx Transitions)
+ // Angular test case: navigating from Page with 3 nested frames and back crashes with "IllegalArgumentException: parameter must be a descendant of this view" only from these commented appium tests (does not reproduce with manual testing). This is fixed in androidx.fragment:fragment:1.2.0 and tests must be uncommented when migrating to it.
+ // await screen.navigateToAboutPage();
+ // await screen.loadedAbout();
+ // await screen.loadedNestedAbout();
+ });
+
+ it("should go back to Tabs and then back to Home", async function () {
+ // TO DO: This is related to the tns-core-modules animations overhaul intiative (removing Animators in favor of androidx Transitions)
+ // Angular test case: navigating from Page with 3 nested frames and back crashes with "IllegalArgumentException: parameter must be a descendant of this view" only from these commented appium tests (does not reproduce with manual testing). This is fixed in androidx.fragment:fragment:1.2.0 and tests must be uncommented when migrating to it.
+ // await backActivatedRoute(driver);
+ // await screen.loadedTabs();
+ // await screen.loadedPlayersList();
+ await backActivatedRoute(driver);
+ await screen.loadedHome();
+ await screen.loadedPlayersList();
+ await screen.loadedTeamList();
+ });
+
+ it("should navigate to Tabs without Players\\Teams navigation", async function () {
+ await screen.navigateToTabsPage();
+ await screen.loadedTabs();
+ await screen.loadedPlayersList();
+ await backActivatedRoute(driver);
+ await screen.loadedHome();
+ await screen.loadedPlayersList();
+ await screen.loadedTeamList();
+ });
+
+ it("should navigate Player One\\Team One then go to Tabs and back", async function () {
+ await testPlayerNavigated(screen, screen.playerOne);
+ await testTeamNavigated(screen, screen.teamOne);
+ await screen.navigateToTabsPage();
+ await screen.loadedTabs();
+ await screen.loadedPlayersList();
+ await gotoTeamsTab(driver);
+ await screen.loadedTeamList();
+ await backActivatedRoute(driver);
+ await screen.loadedHome();
+ await screen.loadedPlayerDetails(screen.playerOne);
+ await screen.loadedTeamDetails(screen.teamOne);
+ await backBoth(driver);
+ await screen.loadedPlayersList();
+ await screen.loadedTeamList();
+ });
+
+ it("should navigate 2 times in Players go to Tabs and back", async function () {
+ await testPlayerNavigated(screen, screen.playerOne);
+ await testPlayerNextNavigated(screen, screen.playerTwo);
+ await screen.navigateToTabsPage();
+ await screen.loadedTabs();
+ await screen.loadedPlayersList();
+ await gotoTeamsTab(driver);
+ await screen.loadedTeamList();
+ await backActivatedRoute(driver);
+ await screen.loadedHome();
+ await screen.loadedPlayerDetails(screen.playerTwo);
+ await screen.loadedTeamList();
+ await backPlayers(driver);
+ await screen.loadedPlayerDetails(screen.playerOne);
+ await backPlayers(driver);
+ await screen.loadedPlayersList();
+ await screen.loadedTeamList();
+ });
+
+ it("should navigate back to Login page with back(activatedRoute)", async function () {
+ await backActivatedRoute(driver);
+ await screen.loadedLogin;
+ });
+ });
+ };
+});
+
+async function backActivatedRoute(driver: AppiumDriver) {
+ const btnBack = await driver.findElementByAutomationText("Back(ActivatedRoute)");
+ await btnBack.tap();
+}
+
+async function backPlayers(driver: AppiumDriver) {
+ const btnBackPlayers = await driver.findElementByAutomationText("Back(Players)");
+ await btnBackPlayers.tap();
+}
+
+async function backBoth(driver: AppiumDriver) {
+ const btnBackBoth = await driver.findElementByAutomationText("Back(Both)");
+ await btnBackBoth.tap();
+}
+
+async function gotoPlayersTab(driver: AppiumDriver) {
+ const btnTabPlayers = await driver.findElementByAutomationText("Players Tab");
+ await btnTabPlayers.tap();
+}
+
+async function gotoTeamsTab(driver: AppiumDriver) {
+ const btnTabTeams = await driver.findElementByAutomationText("Teams Tab");
+ await btnTabTeams.tap();
+}
diff --git a/e2e/nested-router-tab-view/e2e/screen.ts b/e2e/nested-router-tab-view/e2e/screen.ts
new file mode 100644
index 000000000..f880d33c8
--- /dev/null
+++ b/e2e/nested-router-tab-view/e2e/screen.ts
@@ -0,0 +1,178 @@
+import { AppiumDriver, SearchOptions } from "nativescript-dev-appium";
+import { assert } from "chai";
+
+const customTabs = "Custom Tabs Component";
+const home = "Home Component";
+const about = "About Component";
+const aboutNested = "Nested About Component";
+const login = "Login Component";
+const tabs = "Tabs Component";
+
+const playerList = "Player List";
+const playerDetails = "Player Details";
+const gotoNextPlayer = "next player";
+const gotoPlayers = "players";
+
+const teamDetails = "Team Details";
+const teamList = "Team List";
+const gotoNextTeam = "next team";
+const gotoTeams = "teams";
+
+const gotoHomePage = "Go To Home Page";
+const gotoCustomTabPage = "Go To Lazy Custom Tabs";
+const gotoAboutPage = "Go To About Page";
+const gotoTabsPage = "Go To Tabs Page";
+const confirmDialog = "Ok";
+
+export class Screen {
+
+ private _driver: AppiumDriver;
+
+ playerOne = "Player One";
+ playerTwo = "Player Two";
+ teamOne = "Team One";
+ teamTwo = "Team Two";
+
+ canGoBackActivatedRoute = "CanGoBack(ActivatedRoute)";
+ canGoBackPlayers = "CanGoBack(Players)";
+ canGoBackTeams = "CanGoBack(Teams)";
+ canGoBackBoth = "CanGoBack(Both)";
+
+ constructor(driver: AppiumDriver) {
+ this._driver = driver;
+ }
+
+ showDialogConfirm = async (title) => {
+ const btnShowDialogConfirm = await this._driver.findElementByAutomationText(title);
+ await btnShowDialogConfirm.tap();
+ }
+
+ loadedConfirmDialog = async (dialogMessage) => {
+ const lblDialogMessage = await this._driver.findElementByAutomationText(dialogMessage);
+ assert.isTrue(await lblDialogMessage.isDisplayed());
+ console.log(dialogMessage + " shown!");
+ }
+
+ closeDialog = async () => {
+ const btnYesDialog = await this._driver.findElementByAutomationText(confirmDialog);
+ await btnYesDialog.click();
+ }
+
+ loadedLogin = async () => {
+ const lblLogin = await this._driver.findElementByAutomationText(login);
+ assert.isTrue(await lblLogin.isDisplayed());
+ console.log(login + " loaded!");
+ }
+
+ loadedHome = async () => {
+ const lblHome = await this._driver.findElementByAutomationText(home);
+ assert.isTrue(await lblHome.isDisplayed());
+ console.log(home + " loaded!");
+ }
+
+ loadedCustomTabsPage= async () => {
+ const lblCustomTabs = await this._driver.findElementByAutomationText(customTabs);
+ assert.isTrue(await lblCustomTabs.isDisplayed());
+ console.log(home + " loaded!");
+ }
+
+ loadedAbout= async () => {
+ const lblAbout = await this._driver.findElementByAutomationText(about);
+ assert.isTrue(await lblAbout.isDisplayed());
+ console.log(about + " loaded!");
+ }
+
+ loadedNestedAbout= async () => {
+ const lblAboutNested = await this._driver.findElementByAutomationText(aboutNested);
+ assert.isTrue(await lblAboutNested.isDisplayed());
+ console.log(aboutNested + " loaded!");
+ }
+
+ loadedTabs = async () => {
+ const lblTabs = await this._driver.findElementByAutomationText(tabs);
+ assert.isTrue(await lblTabs.isDisplayed());
+ console.log(tabs + " loaded!");
+ }
+
+ navigateToTabsPage = async () => {
+ const btnNavToTabsPage = await this._driver.findElementByAutomationText(gotoTabsPage);
+ await btnNavToTabsPage.tap();
+ }
+
+ loadedPlayersList = async () => {
+ const lblPlayerList = await this._driver.findElementByAutomationText(playerList);
+ assert.isTrue(await lblPlayerList.isDisplayed());
+ console.log(playerList + " loaded!");
+ }
+
+ loadedPlayerDetails = async (player) => {
+ const lblPlayerDetail = await this._driver.findElementByAutomationText(playerDetails);
+ assert.isTrue(await lblPlayerDetail.isDisplayed());
+
+ const lblPlayer = await this._driver.findElementByAutomationText(player + " Details");
+ assert.isTrue(await lblPlayer.isDisplayed());
+
+ console.log(player + " Details" + " loaded!");
+ }
+
+ loadedTeamList = async () => {
+ const lblTeamList = await this._driver.findElementByAutomationText(teamList, 10);
+ assert.isTrue(await lblTeamList.isDisplayed());
+ console.log(teamList + " loaded!");
+ }
+
+ loadedTeamDetails = async (team) => {
+ const lblTeamDetail = await this._driver.findElementByAutomationText(teamDetails);
+ assert.isTrue(await lblTeamDetail.isDisplayed());
+
+ const lblTeam = await this._driver.findElementByAutomationText(team + " Details");
+ assert.isTrue(await lblTeam.isDisplayed());
+
+ console.log(team + " Details" + " loaded!");
+ }
+
+ navigateToHomePage = async (homePageButton?) => {
+ const btnNavToHomePage = await this._driver.findElementByAutomationText(homePageButton || gotoHomePage);
+ await btnNavToHomePage.tap();
+ }
+
+ navigateCustomTabsPage = async () => {
+ const btnNavToHomePage = await this._driver.findElementByAutomationText(gotoCustomTabPage);
+ await btnNavToHomePage.tap();
+ }
+
+ navigateToAboutPage = async () => {
+ const btnNavToAboutPage = await this._driver.findElementByAutomationText(gotoAboutPage);
+ await btnNavToAboutPage.tap();
+ }
+
+ navigateToPlayer = async (player: string) => {
+ const btnNavPlayerPage = await this._driver.findElementByAutomationText(player);
+ await btnNavPlayerPage.tap();
+ }
+
+ navigateToNextPlayer = async () => {
+ const btnNavPlayerNextPage = await this._driver.findElementByAutomationText(gotoNextPlayer);
+ await btnNavPlayerNextPage.tap();
+ }
+
+ navigateToPlayers = async () => {
+ const btnNavPlayersPage = await this._driver.findElementByAutomationText(gotoPlayers);
+ await btnNavPlayersPage.tap();
+ }
+
+ navigateToTeam = async (team: string) => {
+ const btnNavTeamPage = await this._driver.findElementByAutomationText(team);
+ await btnNavTeamPage.tap();
+ }
+
+ navigateToNextTeam = async () => {
+ const btnNavTeamNextPage = await this._driver.findElementByAutomationText(gotoNextTeam);
+ await btnNavTeamNextPage.tap();
+ }
+
+ navigateToTeams = async () => {
+ const btnNavTeamsPage = await this._driver.findElementByAutomationText(gotoTeams);
+ await btnNavTeamsPage.tap();
+ }
+}
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/e2e/setup.ts b/e2e/nested-router-tab-view/e2e/setup.ts
new file mode 100644
index 000000000..cd73140bb
--- /dev/null
+++ b/e2e/nested-router-tab-view/e2e/setup.ts
@@ -0,0 +1,18 @@
+import { startServer, stopServer, ITestReporter, nsCapabilities, LogImageType } from "nativescript-dev-appium";
+const addContext = require('mochawesome/addContext');
+
+const testReporterContext = {};
+testReporterContext.name = "mochawesome";
+testReporterContext.reportDir = "mochawesome-report";
+testReporterContext.log = addContext;
+testReporterContext.logImageTypes = [LogImageType.screenshots];
+nsCapabilities.testReporter = testReporterContext;
+
+before("start server", async function () {
+ nsCapabilities.testReporter.context = this;
+ await startServer();
+});
+
+after("stop server", async function () {
+ await stopServer();
+});
diff --git a/e2e/nested-router-tab-view/e2e/shared.e2e-spec.ts b/e2e/nested-router-tab-view/e2e/shared.e2e-spec.ts
new file mode 100644
index 000000000..1a23b1d35
--- /dev/null
+++ b/e2e/nested-router-tab-view/e2e/shared.e2e-spec.ts
@@ -0,0 +1,37 @@
+import { Screen } from "./screen"
+
+export async function canGoBack(screen: Screen, title: string, result: boolean) {
+ await screen.showDialogConfirm(title);
+ await screen.loadedConfirmDialog(title + ` ${result}`);
+ await screen.closeDialog();
+}
+
+export async function testPlayerNavigated(screen: Screen, player: string) {
+ await screen.navigateToPlayer(player);
+ await screen.loadedPlayerDetails(player);
+}
+
+export async function testPlayerNextNavigated(screen: Screen, nextPlayer: string) {
+ await screen.navigateToNextPlayer();
+ await screen.loadedPlayerDetails(nextPlayer);
+}
+
+export async function testPlayersNavigated(screen: Screen) {
+ await screen.navigateToPlayers();
+ await screen.loadedPlayersList();
+}
+
+export async function testTeamNavigated(screen: Screen, team: string) {
+ await screen.navigateToTeam(team);
+ await screen.loadedTeamDetails(team);
+}
+
+export async function testTeamNextNavigated(screen: Screen, nextTeam: string) {
+ await screen.navigateToNextTeam();
+ await screen.loadedTeamDetails(nextTeam);
+}
+
+export async function testTeamsNavigated(screen: Screen) {
+ await screen.navigateToTeams();
+ await screen.loadedTeamList();
+}
diff --git a/e2e/nested-router-tab-view/e2e/split-view.e2e-spec.ts b/e2e/nested-router-tab-view/e2e/split-view.e2e-spec.ts
new file mode 100644
index 000000000..f3a0490fe
--- /dev/null
+++ b/e2e/nested-router-tab-view/e2e/split-view.e2e-spec.ts
@@ -0,0 +1,196 @@
+import { AppiumDriver, createDriver, SearchOptions, nsCapabilities } from "nativescript-dev-appium";
+import { Screen } from "./screen"
+import {
+ testPlayerNavigated,
+ testTeamNavigated,
+ testPlayerNextNavigated,
+ testTeamNextNavigated,
+ testPlayersNavigated,
+ canGoBack
+} from "./shared.e2e-spec";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+const isSauceRun = isSauceLab;
+
+const pages = ["Go To Home Page", "Go To Lazy Home Page"];
+
+describe("split-view:", async function () {
+ let driver: AppiumDriver;
+ let screen: Screen;
+
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ screen = new Screen(driver);
+ });
+
+ after(async function () {
+ if (isSauceRun) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ for (let index = 0; index < pages.length; index++) {
+ const page = pages[index];
+ describe(`${page} split-view:`, async function () {
+
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ it("loaded home component and lists", async function () {
+ await screen.navigateToHomePage(page);
+ await screen.loadedHome();
+ await screen.loadedPlayersList();
+ await screen.loadedTeamList();
+ await canGoBack(screen, screen.canGoBackActivatedRoute, true);
+ });
+
+ it("should navigate Player One\\Team One then back separately", async function () {
+ await testPlayerNavigated(screen, screen.playerOne);
+ await testTeamNavigated(screen, screen.teamOne);
+ await canGoBack(screen, screen.canGoBackPlayers, true);
+ await canGoBack(screen, screen.canGoBackTeams, true);
+ await backPlayers(driver);
+ await screen.loadedPlayersList();
+ await backTeams(driver);
+ await canGoBack(screen, screen.canGoBackTeams, false);
+ await screen.loadedTeamList();
+ });
+
+ it("should navigate Player One\\Team One then back separately (keep order)", async function () {
+ await testPlayerNavigated(screen, screen.playerOne);
+ await testTeamNavigated(screen, screen.teamOne);
+ await backTeams(driver);
+ await screen.loadedTeamList();
+ await backPlayers(driver);
+ await screen.loadedPlayersList();
+ });
+
+ it("should navigate Player One\\Team One then back simultaneously", async function () {
+ await testPlayerNavigated(screen, screen.playerOne);
+ await testTeamNavigated(screen, screen.teamOne);
+ await canGoBack(screen, screen.canGoBackBoth, true);
+ await backBoth(driver);
+ await canGoBack(screen, screen.canGoBackBoth, false);
+ await screen.loadedTeamList();
+ await screen.loadedPlayersList();
+ });
+
+ it("should navigate Player One\\Team One then next Player/Team then back separately", async function () {
+ await testPlayerNavigated(screen, screen.playerOne);
+ await testTeamNavigated(screen, screen.teamOne);
+ await testPlayerNextNavigated(screen, screen.playerTwo);
+ await testTeamNextNavigated(screen, screen.teamTwo);
+
+ await backPlayers(driver);
+ await screen.loadedPlayerDetails(screen.playerOne);
+ await backTeams(driver);
+ await screen.loadedTeamDetails(screen.teamOne);
+ await backBoth(driver);
+ await screen.loadedTeamList();
+ await screen.loadedPlayersList();
+ });
+
+ it("should navigate Player One\\Team One then back", async function () {
+ await testPlayerNavigated(screen, screen.playerOne);
+ await testTeamNavigated(screen, screen.teamOne);
+ await back(driver);
+ await screen.loadedPlayerDetails(screen.playerOne);
+ await screen.loadedTeamList();
+
+ await backPlayers(driver);
+ await screen.loadedPlayersList();
+ });
+
+ it("should navigate Player One then navigate Players list then back", async function () {
+ await testPlayerNavigated(screen, screen.playerOne);
+ await testPlayerNextNavigated(screen, screen.playerTwo);
+ await testPlayersNavigated(screen);
+ await back(driver);
+ await screen.loadedPlayerDetails(screen.playerTwo);
+ await back(driver);
+ await screen.loadedPlayerDetails(screen.playerOne);
+ await back(driver);
+ await screen.loadedPlayersList();
+ });
+
+ it("should navigate Player One\\Team One then Android back button", async function () {
+ if (driver.isAndroid) {
+ await testPlayerNavigated(screen, screen.playerOne);
+ await testTeamNavigated(screen, screen.teamOne);
+ await driver.navBack();
+ await screen.loadedPlayerDetails(screen.playerOne);
+ await screen.loadedTeamList();
+
+ await backPlayers(driver);
+ await screen.loadedPlayersList();
+ }
+ });
+
+ it("should navigate Player One then navigate Players list then Android back button", async function () {
+ if (driver.isAndroid) {
+ await testPlayerNavigated(screen, screen.playerOne);
+ await testPlayerNextNavigated(screen, screen.playerTwo);
+ await testPlayersNavigated(screen);
+ await driver.navBack();
+ await screen.loadedPlayerDetails(screen.playerTwo);
+ await driver.navBack();
+ await screen.loadedPlayersList();
+ }
+ });
+
+ it("should not navigate back when no back stack available", async function () {
+ await backTeams(driver);
+ await screen.loadedTeamList();
+ await backPlayers(driver);
+ await screen.loadedPlayersList();
+ });
+
+ it("should navigate back to Login page with back(activatedRoute)", async function () {
+ await backActivatedRoute(driver);
+ await screen.loadedLogin;
+ });
+ });
+ };
+});
+
+async function backActivatedRoute(driver: AppiumDriver) {
+ const btnBack = await driver.findElementByAutomationText("Back(ActivatedRoute)");
+ await btnBack.tap();
+}
+
+async function back(driver: AppiumDriver) {
+ const btnBack = await driver.findElementByAutomationText("Back()");
+ await btnBack.tap();
+}
+
+async function backPlayers(driver: AppiumDriver) {
+ const btnBackPlayers = await driver.findElementByAutomationText("Back(Players)");
+ await btnBackPlayers.tap();
+}
+
+async function backTeams(driver: AppiumDriver) {
+ const btnBackTeams = await driver.findElementByAutomationText("Back(Teams)");
+ await btnBackTeams.tap();
+}
+
+async function backBoth(driver: AppiumDriver) {
+ const btnBackBoth = await driver.findElementByAutomationText("Back(Both)");
+ await btnBackBoth.tap();
+}
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/e2e/tab-view.e2e-spec.ts b/e2e/nested-router-tab-view/e2e/tab-view.e2e-spec.ts
new file mode 100644
index 000000000..a1955d5d4
--- /dev/null
+++ b/e2e/nested-router-tab-view/e2e/tab-view.e2e-spec.ts
@@ -0,0 +1,111 @@
+import { AppiumDriver, createDriver, nsCapabilities } from "nativescript-dev-appium";
+import { Screen } from "./screen"
+import {
+ testPlayerNavigated,
+ testTeamNavigated,
+ testPlayerNextNavigated,
+ testTeamNextNavigated,
+} from "./shared.e2e-spec";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+const isSauceRun = isSauceLab;
+
+describe("tab-view:", async function () {
+ let driver: AppiumDriver;
+ let screen: Screen;
+
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ screen = new Screen(driver);
+ });
+
+ after(async function () {
+ if (isSauceRun) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ it("loaded home component and lists", async function () {
+ await screen.navigateToHomePage();
+ await screen.loadedHome();
+ await screen.loadedPlayersList();
+ await screen.loadedTeamList();
+ });
+
+ it("loaded tabs component, Players List and Teams List pages", async function () {
+ await screen.navigateToTabsPage();
+ await screen.loadedTabs();
+ await screen.loadedPlayersList();
+ await gotoTeamsTab(driver);
+ await screen.loadedTeamList();
+ await gotoPlayersTab(driver);
+ await screen.loadedPlayersList();
+ });
+
+ it("should navigate Player One\\Team One then back separately", async function () {
+ this.retries(2);
+ await testPlayerNavigated(screen, screen.playerOne);
+ await gotoTeamsTab(driver);
+ await testTeamNavigated(screen, screen.teamOne);
+ await backTeams(driver);
+ await screen.loadedTeamList();
+ await gotoPlayersTab(driver);
+ await backPlayers(driver);
+ await screen.loadedPlayersList();
+ });
+
+ it("should navigate Player One\\Team One then next Player\\Team then back", async function () {
+ await testPlayerNavigated(screen, screen.playerOne);
+ await testPlayerNextNavigated(screen, screen.playerTwo);
+ await gotoTeamsTab(driver);
+ await testTeamNavigated(screen, screen.teamOne);
+ await testTeamNextNavigated(screen, screen.teamTwo);
+ await gotoPlayersTab(driver);
+ await backPlayers(driver);
+ await screen.loadedPlayerDetails(screen.playerOne);
+ await gotoTeamsTab(driver);
+ await backTeams(driver);
+ await screen.loadedTeamDetails(screen.teamOne);
+ await backTeams(driver);
+ await screen.loadedTeamList();
+ await gotoPlayersTab(driver);
+ await backPlayers(driver);
+ await screen.loadedPlayersList();
+ });
+});
+
+async function gotoPlayersTab(driver: AppiumDriver) {
+ const btnTabPlayers = await driver.findElementByAutomationText("Players Tab");
+ await btnTabPlayers.tap();
+}
+
+async function gotoTeamsTab(driver: AppiumDriver) {
+ const btnTabTeams = await driver.findElementByAutomationText("Teams Tab");
+ await btnTabTeams.tap();
+}
+
+async function backTeams(driver: AppiumDriver) {
+ const btnBackTeams = await driver.findElementByAutomationText("Back-Teams");
+ await btnBackTeams.tap();
+}
+
+async function backPlayers(driver: AppiumDriver) {
+ const btnBackPlayers = await driver.findElementByAutomationText("Back-Players");
+ await btnBackPlayers.tap();
+}
diff --git a/e2e/nested-router-tab-view/e2e/tsconfig.json b/e2e/nested-router-tab-view/e2e/tsconfig.json
new file mode 100644
index 000000000..c297b2347
--- /dev/null
+++ b/e2e/nested-router-tab-view/e2e/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es6",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "importHelpers": false,
+ "types": [
+ "node",
+ "mocha",
+ "chai"
+ ],
+ "lib": [
+ "es2015",
+ "dom"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/package.json b/e2e/nested-router-tab-view/package.json
new file mode 100644
index 000000000..791cbc65f
--- /dev/null
+++ b/e2e/nested-router-tab-view/package.json
@@ -0,0 +1,54 @@
+{
+ "description": "NativeScript Application",
+ "license": "SEE LICENSE IN ",
+ "readme": "NativeScript Application",
+ "repository": "",
+ "nativescript": {
+ "id": "org.nativescript.nestedroutertabview",
+ "tns-ios": {
+ "version": "6.5.1"
+ }
+ },
+ "dependencies": {
+ "@angular/animations": "~9.1.0",
+ "@angular/common": "~9.1.0",
+ "@angular/compiler": "~9.1.0",
+ "@angular/core": "~9.1.0",
+ "@angular/forms": "~9.1.0",
+ "@angular/platform-browser": "~9.1.0",
+ "@angular/platform-browser-dynamic": "~9.1.0",
+ "@angular/router": "~9.1.0",
+ "@nativescript/angular": "file:../../dist/nativescript-angular-scoped.tgz",
+ "nativescript-theme-core": "~1.0.4",
+ "reflect-metadata": "~0.1.8",
+ "rxjs": "~6.5.5",
+ "@nativescript/core": "next",
+ "zone.js": "^0.10.3"
+ },
+ "devDependencies": {
+ "@angular/compiler-cli": "~9.1.0",
+ "@ngtools/webpack": "~9.1.0",
+ "@types/chai": "~4.1.7",
+ "@types/mocha": "~5.2.5",
+ "@types/node": "~10.12.18",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0",
+ "babylon": "6.18.0",
+ "chai": "^4.2.0",
+ "lazy": "1.0.11",
+ "mocha": "~5.2.0",
+ "mochawesome": "~3.1.2",
+ "nativescript-dev-appium": "^6.0.0",
+ "nativescript-dev-webpack": "next",
+ "typescript": "~3.8.3"
+ },
+ "scripts": {
+ "compile-tests": "tsc -p e2e --watch",
+ "e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
+ "e2e-watch": "tsc -p e2e --watch",
+ "clean": "npx rimraf hooks node_modules platforms package-lock.json",
+ "setup": "cd ../../nativescript-angular && npm run prep.apps && cd ../e2e/nested-router-tab-view && npm run clean",
+ "ngcc": "ngcc --properties es2015 module main --first-only",
+ "postinstall": "npm run ngcc"
+ }
+}
diff --git a/ng-sample/references.d.ts b/e2e/nested-router-tab-view/references.d.ts
similarity index 100%
rename from ng-sample/references.d.ts
rename to e2e/nested-router-tab-view/references.d.ts
diff --git a/e2e/nested-router-tab-view/tsconfig.json b/e2e/nested-router-tab-view/tsconfig.json
new file mode 100644
index 000000000..1d8622eed
--- /dev/null
+++ b/e2e/nested-router-tab-view/tsconfig.json
@@ -0,0 +1,34 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es5",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "noEmitHelpers": true,
+ "noEmitOnError": true,
+ "skipLibCheck": true,
+ "lib": [
+ "es2017",
+ "dom",
+ "es6"
+ ],
+ "baseUrl": ".",
+ "paths": {
+ "~/*": [
+ "app/*"
+ ]
+ }
+ },
+ "includes": [
+ "./references.d.ts"
+ ],
+ "files": [
+ "./app/main.ts"
+ ],
+ "exclude": [
+ "node_modules",
+ "platforms",
+ "**/*.aot",
+ "e2e"
+ ]
+}
\ No newline at end of file
diff --git a/e2e/nested-router-tab-view/tsconfig.tns.json b/e2e/nested-router-tab-view/tsconfig.tns.json
new file mode 100644
index 000000000..a96f6bbc6
--- /dev/null
+++ b/e2e/nested-router-tab-view/tsconfig.tns.json
@@ -0,0 +1,7 @@
+{
+ "extends": "./tsconfig",
+ "compilerOptions": {
+ "module": "ESNext",
+ "moduleResolution": "node"
+ }
+}
diff --git a/e2e/renderer/.gitignore b/e2e/renderer/.gitignore
index 5a67ca289..f2486e508 100644
--- a/e2e/renderer/.gitignore
+++ b/e2e/renderer/.gitignore
@@ -6,4 +6,5 @@ app/**/*.js
e2e/**/*.js
test-results.xml
e2e/reports
+mochawesome-report
diff --git a/e2e/renderer/.vscode/launch.json b/e2e/renderer/.vscode/launch.json
index cd10b18ce..57e3caf4c 100644
--- a/e2e/renderer/.vscode/launch.json
+++ b/e2e/renderer/.vscode/launch.json
@@ -12,10 +12,11 @@
"--timeout",
"999999",
"--colors",
- "--reuseDevice",
- "--runType",
- "sim.iPhone7.iOS110",
- "${workspaceRoot}/e2e"
+ "${workspaceRoot}/e2e",
+ "-a",
+ "--grep",
+ "actionBarVisibility 'auto' shows action bars based on page"
+
],
"internalConsoleOptions": "openOnSessionStart"
}
diff --git a/e2e/renderer/.vscode/settings.json b/e2e/renderer/.vscode/settings.json
index 699fb4408..5d4d7a687 100644
--- a/e2e/renderer/.vscode/settings.json
+++ b/e2e/renderer/.vscode/settings.json
@@ -1,18 +1,13 @@
{
- "typescript.check.tscVersion": false,
- "window.zoomLevel": 0,
- "workbench.iconTheme": "vscode-icons",
- "vsicons.dontShowNewVersionMessage": true,
- "workbench.colorTheme": "Default Light+",
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
- "**/*.js": true,
+ // "**/*.js": true,
"**/*.map": true,
- "node_modules": true,
+ // "node_modules": true,
"hooks": true,
"platforms": true
}
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/AndroidManifest.xml b/e2e/renderer/app/App_Resources/Android/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..9db832151
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/Android/src/main/AndroidManifest.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png
new file mode 100644
index 000000000..eb381c258
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png differ
diff --git a/ng-sample/app/App_Resources/Android/drawable-hdpi/icon.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png
similarity index 100%
rename from ng-sample/app/App_Resources/Android/drawable-hdpi/icon.png
rename to e2e/renderer/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png
new file mode 100644
index 000000000..5218f4c90
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png
new file mode 100644
index 000000000..748b2adf5
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png differ
diff --git a/e2e/router/app/App_Resources/Android/drawable-nodpi/icon.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png
similarity index 100%
rename from e2e/router/app/App_Resources/Android/drawable-nodpi/icon.png
rename to e2e/renderer/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png
new file mode 100644
index 000000000..b9e102a76
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png
new file mode 100644
index 000000000..efeaf2907
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png differ
diff --git a/ng-sample/app/App_Resources/Android/drawable-mdpi/icon.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png
similarity index 100%
rename from ng-sample/app/App_Resources/Android/drawable-mdpi/icon.png
rename to e2e/renderer/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png
new file mode 100644
index 000000000..626338766
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
new file mode 100644
index 000000000..ada77f92c
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
@@ -0,0 +1,8 @@
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png
new file mode 100644
index 000000000..612bbd072
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png
new file mode 100644
index 000000000..f29188209
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png
new file mode 100644
index 000000000..ad8ee2f4b
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png
new file mode 100644
index 000000000..0fa88e235
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png differ
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png
old mode 100755
new mode 100644
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png
rename to e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png
new file mode 100644
index 000000000..668327832
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png
new file mode 100644
index 000000000..c650f6438
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/browse.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/browse.png
new file mode 100644
index 000000000..9a4d6ce88
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/browse.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/home.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/home.png
new file mode 100644
index 000000000..372293f0f
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/home.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png
new file mode 100644
index 000000000..50887a856
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png
new file mode 100644
index 000000000..fa6331c8d
Binary files /dev/null and b/e2e/renderer/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png differ
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/values-v21/colors.xml b/e2e/renderer/app/App_Resources/Android/src/main/res/values-v21/colors.xml
new file mode 100644
index 000000000..a64641a9d
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/Android/src/main/res/values-v21/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #3d5afe
+
\ No newline at end of file
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/values-v21/styles.xml b/e2e/renderer/app/App_Resources/Android/src/main/res/values-v21/styles.xml
new file mode 100644
index 000000000..dac8727c8
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/Android/src/main/res/values-v21/styles.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/renderer/app/App_Resources/Android/src/main/res/values/colors.xml b/e2e/renderer/app/App_Resources/Android/src/main/res/values/colors.xml
new file mode 100644
index 000000000..74ad8829c
--- /dev/null
+++ b/e2e/renderer/app/App_Resources/Android/src/main/res/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/renderer/app/App_Resources/Android/src/main/res/values/styles.xml
similarity index 100%
rename from e2e/router/app/App_Resources/Android/values/styles.xml
rename to e2e/renderer/app/App_Resources/Android/src/main/res/values/styles.xml
diff --git a/e2e/renderer/app/App_Resources/iOS/tabIcons/browse.png b/e2e/renderer/app/App_Resources/iOS/tabIcons/browse.png
new file mode 100644
index 000000000..cde8ecf30
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/tabIcons/browse.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/tabIcons/browse@2x.png b/e2e/renderer/app/App_Resources/iOS/tabIcons/browse@2x.png
new file mode 100644
index 000000000..37e94bd5c
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/tabIcons/browse@2x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/tabIcons/browse@3x.png b/e2e/renderer/app/App_Resources/iOS/tabIcons/browse@3x.png
new file mode 100644
index 000000000..5adce0609
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/tabIcons/browse@3x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/tabIcons/home.png b/e2e/renderer/app/App_Resources/iOS/tabIcons/home.png
new file mode 100644
index 000000000..ca969cb8c
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/tabIcons/home.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/tabIcons/home@2x.png b/e2e/renderer/app/App_Resources/iOS/tabIcons/home@2x.png
new file mode 100644
index 000000000..ace1ca2aa
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/tabIcons/home@2x.png differ
diff --git a/e2e/renderer/app/App_Resources/iOS/tabIcons/home@3x.png b/e2e/renderer/app/App_Resources/iOS/tabIcons/home@3x.png
new file mode 100644
index 000000000..ca2bef9e8
Binary files /dev/null and b/e2e/renderer/app/App_Resources/iOS/tabIcons/home@3x.png differ
diff --git a/e2e/renderer/app/app-routing.module.ts b/e2e/renderer/app/app-routing.module.ts
index a975d5be8..04ab25978 100644
--- a/e2e/renderer/app/app-routing.module.ts
+++ b/e2e/renderer/app/app-routing.module.ts
@@ -1,9 +1,16 @@
import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
-import { NativeScriptRouterModule } from "nativescript-angular/router";
+import { NativeScriptRouterModule, NSEmptyOutletComponent } from "@nativescript/angular/router";
import { ActionBarDynamicItemsComponent } from "./action-bar/action-bar-dynamic-items.component";
import { ActionBarExtensionComponent } from "./action-bar/action-bar-extension.component";
+import { ActionBarVisibilityAlwaysComponent } from "./page-router-outlet/action-bar-visibility-always.component";
+import { ActionBarVisibilityAutoComponent } from "./page-router-outlet/action-bar-visibility-auto.component"
+import { ActionBarVisibilityNeverComponent } from "./page-router-outlet/action-bar-visibility-never.component"
+import { NestedPageComponent } from "./page-router-outlet/nested-page.component"
+
+import { TabItemBindingComponent } from "./tab-view/tab-item-binding.component";
+
import { ListComponent } from "./list.component";
import { NgForComponent } from "./ngfor.component";
import { NgForOfComponent } from "./ngforof.component";
@@ -20,6 +27,43 @@ export const routes = [
redirectTo: "/list",
pathMatch: "full"
},
+ {
+ path: "action-bar-visibility-always",
+ component: ActionBarVisibilityAlwaysComponent,
+ children: [{
+ path: "nested",
+ outlet: "nested",
+ component: NestedPageComponent
+ }]
+ },
+ {
+ path: "action-bar-visibility-never",
+ component: ActionBarVisibilityNeverComponent,
+ children: [{
+ path: "nested",
+ outlet: "nested",
+ component: NestedPageComponent
+ }]
+ },
+ {
+ path: "action-bar-visibility-auto",
+ component: ActionBarVisibilityAutoComponent,
+ children: [{
+ path: "nested",
+ outlet: "nested",
+ component: NestedPageComponent
+ }]
+ },
+ {
+ path: "action-bar-visibility-never-lazy",
+ component: ActionBarVisibilityNeverComponent,
+ children: [{
+ path: "nested",
+ outlet: "nested",
+ component: NSEmptyOutletComponent,
+ loadChildren:"~/page-router-outlet/nested-lazy-page.module#NestedLazyPageModule"
+ }]
+ },
{
path: "action-bar-dynamic",
component: ActionBarDynamicItemsComponent,
@@ -28,6 +72,10 @@ export const routes = [
path: "action-bar-extension",
component: ActionBarExtensionComponent,
},
+ {
+ path: "tab-item-binding",
+ component: TabItemBindingComponent,
+ },
{
path: "list",
component: ListComponent,
@@ -70,6 +118,12 @@ export const navigatableComponents = [
ActionBarDynamicItemsComponent,
ActionBarExtensionComponent,
+ ActionBarVisibilityAlwaysComponent,
+ ActionBarVisibilityNeverComponent,
+ ActionBarVisibilityAutoComponent,
+
+ TabItemBindingComponent,
+
ListComponent,
NgForComponent,
NgForOfComponent,
diff --git a/e2e/renderer/app/app.module.ngfactory.d.ts b/e2e/renderer/app/app.module.ngfactory.d.ts
new file mode 100644
index 000000000..793157de3
--- /dev/null
+++ b/e2e/renderer/app/app.module.ngfactory.d.ts
@@ -0,0 +1,4 @@
+/**
+ * A dynamically generated module when compiled with AoT.
+ */
+export const AppModuleNgFactory: any;
\ No newline at end of file
diff --git a/e2e/renderer/app/app.module.ts b/e2e/renderer/app/app.module.ts
index c5bf7e4dd..3d8f3e5c1 100644
--- a/e2e/renderer/app/app.module.ts
+++ b/e2e/renderer/app/app.module.ts
@@ -1,5 +1,5 @@
-import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
-import { NativeScriptModule } from "nativescript-angular/nativescript.module";
+import { NgModule, NO_ERRORS_SCHEMA, ErrorHandler } from "@angular/core";
+import { NativeScriptModule } from "@nativescript/angular";
import {
AppRoutingModule,
@@ -9,11 +9,24 @@ import {
import { AppComponent } from "./app.component";
import { ItemsService } from "./items.service";
-import { rendererTraceCategory, viewUtilCategory } from "nativescript-angular/trace";
-import { setCategories, enable } from "trace";
-setCategories(rendererTraceCategory + "," + viewUtilCategory);
+import { rendererTraceCategory, viewUtilCategory, bootstrapCategory } from "@nativescript/angular/trace";
+import { addCategories, enable, categories } from "@nativescript/core/trace";
+import { SharedModule } from "./shared.module";
+
+addCategories(bootstrapCategory);
+addCategories(rendererTraceCategory);
+addCategories(viewUtilCategory);
+addCategories(categories.ViewHierarchy);
enable();
+export class MyErrorHandler implements ErrorHandler {
+ handleError(error) {
+ console.log("### ErrorHandler Error: " + error.toString());
+ console.log("### ErrorHandler Stack: " + error.stack);
+ }
+}
+
+
@NgModule({
declarations: [
AppComponent,
@@ -21,11 +34,13 @@ enable();
],
bootstrap: [AppComponent],
providers: [
- ItemsService
+ ItemsService,
+ { provide: ErrorHandler, useClass: MyErrorHandler }
],
imports: [
NativeScriptModule,
AppRoutingModule,
+ SharedModule
],
schemas: [NO_ERRORS_SCHEMA],
})
diff --git a/e2e/renderer/app/list.component.ts b/e2e/renderer/app/list.component.ts
index ddbaa8b95..9996d5a79 100644
--- a/e2e/renderer/app/list.component.ts
+++ b/e2e/renderer/app/list.component.ts
@@ -1,10 +1,16 @@
import { Component } from "@angular/core";
@Component({
+ styles: ["Button { font-size: 10; margin: 0; padding: 0 }"],
template: `
-
+
+
+
+
+
+
diff --git a/e2e/renderer/app/main.aot.ts b/e2e/renderer/app/main.aot.ts
deleted file mode 100644
index 98bf134fc..000000000
--- a/e2e/renderer/app/main.aot.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { platformNativeScript } from "nativescript-angular/platform-static";
-import { AppModuleNgFactory } from "./app.module.ngfactory";
-
-platformNativeScript().bootstrapModuleFactory(AppModuleNgFactory);
diff --git a/e2e/renderer/app/main.ts b/e2e/renderer/app/main.ts
index 639bfd513..1b1b3c521 100644
--- a/e2e/renderer/app/main.ts
+++ b/e2e/renderer/app/main.ts
@@ -1,4 +1,4 @@
-import { platformNativeScriptDynamic } from "nativescript-angular/platform";
+import { platformNativeScriptDynamic } from "@nativescript/angular/platform";
import { AppModule } from "./app.module";
platformNativeScriptDynamic().bootstrapModule(AppModule);
diff --git a/e2e/renderer/app/package.json b/e2e/renderer/app/package.json
index d5356c0cc..706d1a31b 100644
--- a/e2e/renderer/app/package.json
+++ b/e2e/renderer/app/package.json
@@ -1,5 +1,9 @@
{
"main": "main.js",
"name": "nativescript-template-ng-tutorial",
- "version": "3.1.0"
+ "version": "3.1.0",
+ "android": {
+ "v8Flags": "--expose_gc",
+ "markingMode": "none"
+ }
}
\ No newline at end of file
diff --git a/e2e/renderer/app/page-router-outlet/action-bar-visibility-always.component.ts b/e2e/renderer/app/page-router-outlet/action-bar-visibility-always.component.ts
new file mode 100644
index 000000000..2887fac3d
--- /dev/null
+++ b/e2e/renderer/app/page-router-outlet/action-bar-visibility-always.component.ts
@@ -0,0 +1,26 @@
+import { Component, OnInit } from "@angular/core";
+import { RouterExtensions } from "@nativescript/angular/router";
+import { ActivatedRoute } from "@angular/router";
+
+@Component({
+ template: `
+
+
+
+
+
+
+
+
+ `
+})
+export class ActionBarVisibilityAlwaysComponent implements OnInit {
+ constructor(
+ private routerExtension: RouterExtensions,
+ private activeRoute: ActivatedRoute) {
+ }
+
+ ngOnInit(): void {
+ this.routerExtension.navigate([{outlets: { nested: ["nested"]}}], { relativeTo: this.activeRoute });
+ }
+}
diff --git a/e2e/renderer/app/page-router-outlet/action-bar-visibility-auto.component.ts b/e2e/renderer/app/page-router-outlet/action-bar-visibility-auto.component.ts
new file mode 100644
index 000000000..b06225f2e
--- /dev/null
+++ b/e2e/renderer/app/page-router-outlet/action-bar-visibility-auto.component.ts
@@ -0,0 +1,26 @@
+import { Component, OnInit } from "@angular/core";
+import { RouterExtensions } from "@nativescript/angular/router";
+import { ActivatedRoute } from "@angular/router";
+
+@Component({
+ template: `
+
+
+
+
+
+
+
+
+ `
+})
+export class ActionBarVisibilityAutoComponent implements OnInit {
+ constructor(
+ private routerExtension: RouterExtensions,
+ private activeRoute: ActivatedRoute) {
+ }
+
+ ngOnInit(): void {
+ this.routerExtension.navigate([{ outlets: { nested: ["nested"] } }], { relativeTo: this.activeRoute });
+ }
+}
diff --git a/e2e/renderer/app/page-router-outlet/action-bar-visibility-never.component.ts b/e2e/renderer/app/page-router-outlet/action-bar-visibility-never.component.ts
new file mode 100644
index 000000000..c0f7200ac
--- /dev/null
+++ b/e2e/renderer/app/page-router-outlet/action-bar-visibility-never.component.ts
@@ -0,0 +1,26 @@
+import { Component, OnInit } from "@angular/core";
+import { RouterExtensions } from "@nativescript/angular/router";
+import { ActivatedRoute } from "@angular/router";
+
+@Component({
+ template: `
+
+
+
+
+
+
+
+
+ `
+})
+export class ActionBarVisibilityNeverComponent implements OnInit {
+ constructor(
+ private routerExtension: RouterExtensions,
+ private activeRoute: ActivatedRoute) {
+ }
+
+ ngOnInit(): void {
+ this.routerExtension.navigate([{ outlets: { nested: ["nested"] } }], { relativeTo: this.activeRoute });
+ }
+}
diff --git a/e2e/renderer/app/page-router-outlet/nested-lazy-page.module.ts b/e2e/renderer/app/page-router-outlet/nested-lazy-page.module.ts
new file mode 100644
index 000000000..26d3ce76f
--- /dev/null
+++ b/e2e/renderer/app/page-router-outlet/nested-lazy-page.module.ts
@@ -0,0 +1,20 @@
+import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+import { NativeScriptRouterModule } from "@nativescript/angular/router";
+import { NativeScriptCommonModule } from "@nativescript/angular/common";
+import { SharedModule } from "~/shared.module";
+import { NestedPageComponent } from "./nested-page.component";
+
+@NgModule({
+ imports: [
+ SharedModule,
+ NativeScriptCommonModule,
+ NativeScriptRouterModule,
+ NativeScriptRouterModule.forChild([
+ { path: "", component: NestedPageComponent }
+ ])
+ ],
+ providers: [
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+})
+export class NestedLazyPageModule { }
\ No newline at end of file
diff --git a/e2e/renderer/app/page-router-outlet/nested-page.component.ts b/e2e/renderer/app/page-router-outlet/nested-page.component.ts
new file mode 100644
index 000000000..fa7f81761
--- /dev/null
+++ b/e2e/renderer/app/page-router-outlet/nested-page.component.ts
@@ -0,0 +1,24 @@
+import { Component } from "@angular/core";
+
+@Component({
+ template: `
+
+
+
+
+
+
+
+
+
+ `
+})
+export class NestedPageComponent {
+ showActionBar(args): void {
+ args.object.page.actionBarHidden = false;
+ }
+
+ hideActionBar(args): void {
+ args.object.page.actionBarHidden = true;
+ }
+}
diff --git a/e2e/renderer/app/shared.module.ts b/e2e/renderer/app/shared.module.ts
new file mode 100644
index 000000000..d8e40bd32
--- /dev/null
+++ b/e2e/renderer/app/shared.module.ts
@@ -0,0 +1,14 @@
+import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+import { NativeScriptRouterModule } from "@nativescript/angular/router";
+import { NativeScriptCommonModule } from "@nativescript/angular/common";
+import { NestedPageComponent } from "./page-router-outlet/nested-page.component";
+
+@NgModule({
+ imports: [
+ NativeScriptCommonModule,
+ NativeScriptRouterModule
+ ],
+ declarations:[NestedPageComponent],
+ schemas: [NO_ERRORS_SCHEMA]
+})
+export class SharedModule { }
\ No newline at end of file
diff --git a/e2e/renderer/app/tab-view/tab-item-binding.component.ts b/e2e/renderer/app/tab-view/tab-item-binding.component.ts
new file mode 100644
index 000000000..f99f45b7e
--- /dev/null
+++ b/e2e/renderer/app/tab-view/tab-item-binding.component.ts
@@ -0,0 +1,56 @@
+import { Component } from "@angular/core";
+import { isAndroid } from "@nativescript/core/platform";
+
+function getIconSource(icon: string): string {
+ const iconPrefix = isAndroid ? "res://" : "res://tabIcons/";
+
+ return iconPrefix + icon;
+}
+
+const notSelected = {
+ title: "Not Selected",
+ textTransform: "lowercase",
+ iconSource: getIconSource("home")
+};
+
+const selected = {
+ title: "Selected",
+ textTransform: "uppercase",
+ iconSource: getIconSource("browse")
+};
+
+@Component({
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class TabItemBindingComponent {
+ public items = [
+ notSelected,
+ notSelected,
+ notSelected
+ ];
+
+ onIndexChange(args): void {
+ const selectedIndex = args.object.selectedIndex;
+
+ for (let i = 0; i < this.items.length; i++) {
+ this.items[i] = notSelected;
+ }
+
+ this.items[selectedIndex] = selected;
+ }
+}
+
diff --git a/e2e/renderer/app/vendor-platform.android.ts b/e2e/renderer/app/vendor-platform.android.ts
deleted file mode 100644
index 719f26498..000000000
--- a/e2e/renderer/app/vendor-platform.android.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-require("application");
-if (!global["__snapshot"]) {
- // In case snapshot generation is enabled these modules will get into the bundle
- // but will not be required/evaluated.
- // The snapshot webpack plugin will add them to the tns-java-classes.js bundle file.
- // This way, they will be evaluated on app start as early as possible.
- require("ui/frame");
- require("ui/frame/activity");
-}
diff --git a/e2e/renderer/app/vendor-platform.ios.ts b/e2e/renderer/app/vendor-platform.ios.ts
deleted file mode 100644
index 7f3e7f039..000000000
--- a/e2e/renderer/app/vendor-platform.ios.ts
+++ /dev/null
@@ -1 +0,0 @@
-void 0;
diff --git a/e2e/renderer/app/vendor.ts b/e2e/renderer/app/vendor.ts
deleted file mode 100644
index 4526eca9b..000000000
--- a/e2e/renderer/app/vendor.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-// Snapshot the ~/app.css and the theme
-const application = require("application");
-require("ui/styling/style-scope");
-const appCssContext = require.context("~/", false, /^\.\/app\.(css|scss|less|sass)$/);
-global.registerWebpackModules(appCssContext);
-application.loadAppCss();
-
-require("./vendor-platform");
-
-require("reflect-metadata");
-require("@angular/platform-browser");
-require("@angular/core");
-require("@angular/common");
-require("@angular/forms");
-require("@angular/http");
-require("@angular/router");
-
-require("nativescript-angular/platform-static");
-require("nativescript-angular/forms");
-require("nativescript-angular/router");
diff --git a/e2e/renderer/e2e/action-bar.e2e-spec.ts b/e2e/renderer/e2e/action-bar.e2e-spec.ts
index 312df8a19..e4d34f011 100644
--- a/e2e/renderer/e2e/action-bar.e2e-spec.ts
+++ b/e2e/renderer/e2e/action-bar.e2e-spec.ts
@@ -2,81 +2,113 @@ import {
AppiumDriver,
createDriver,
SearchOptions,
- UIElement
+ UIElement,
+ nsCapabilities
} from "nativescript-dev-appium";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+const isSauceRun = isSauceLab;
import { isOnTheLeft } from "./helpers/location";
+import { assert } from "chai";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
-describe("Action Bar scenario", () => {
+describe("Action Bar scenario", async function () {
let driver: AppiumDriver;
- describe("dynamically add/remove ActionItems", async () => {
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ await driver.driver.resetApp();
+ });
+
+ after(async function () {
+ if (isSauceRun) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+
+ describe("dynamically add\\remove ActionItems", async function () {
let firstActionItem: UIElement;
let secondActionItem: UIElement;
let toggleFirstButton: UIElement;
let toggleSecondButton: UIElement;
- before(async () => {
- driver = await createDriver();
- await driver.driver.resetApp();
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
});
- it("should navigate to page", async () => {
+ it("should navigate to page", async function () {
const navigationButton =
- await driver.findElementByText("ActionBar dynamic", SearchOptions.exact);
+ await driver.findElementByAutomationText("ActionBar dynamic");
await navigationButton.click();
const actionBar =
- await driver.findElementByText("Action Bar Dynamic Items", SearchOptions.exact);
+ await driver.findElementByAutomationText("Action Bar Dynamic Items");
});
- it("should find elements", async () => {
- firstActionItem = await driver.findElementByText("one");
- secondActionItem = await driver.findElementByText("two");
+ it("should find elements", async function () {
+ firstActionItem = await driver.findElementByAutomationText("one");
+ secondActionItem = await driver.findElementByAutomationText("two");
- toggleFirstButton = await driver.findElementByText("toggle 1");
- toggleSecondButton = await driver.findElementByText("toggle 2");
+ toggleFirstButton = await driver.findElementByAutomationText("toggle 1");
+ toggleSecondButton = await driver.findElementByAutomationText("toggle 2");
});
- it("should initially render the action items in the correct order", async () => {
+ it("should initially render the action items in the correct order", async function () {
await checkOrderIsCorrect();
});
it("should detach first element when its condition is false", done => {
- (async () => {
+ (async function () {
await toggleFirst();
try {
- await driver.findElementByText("one", SearchOptions.exact);
+ await driver.findElementByAutomationText("one");
} catch (e) {
done();
}
})();
});
- it("should attach first element in the correct position", async () => {
+ it("should attach first element in the correct position", async function () {
await toggleFirst();
await checkOrderIsCorrect();
});
it("should detach second element when its condition is false", done => {
- (async () => {
+ (async function () {
await toggleSecond();
try {
- await driver.findElementByText("two", SearchOptions.exact);
+ await driver.findElementByAutomationText("two");
} catch (e) {
done();
}
})();
});
- it("should attach second element in the correct position", async () => {
+ it("should attach second element in the correct position", async function () {
await toggleSecond();
await checkOrderIsCorrect();
});
- it("should detach and then reattach both at correct places", async () => {
+ it("should detach and then reattach both at correct places", async function () {
await toggleFirst();
await toggleSecond();
@@ -86,62 +118,56 @@ describe("Action Bar scenario", () => {
await checkOrderIsCorrect();
});
- const checkOrderIsCorrect = async () => {
+ const checkOrderIsCorrect = async function () {
await isOnTheLeft(firstActionItem, secondActionItem);
};
- const toggleFirst = async () => {
+ const toggleFirst = async function () {
await toggleFirstButton.click();
};
- const toggleSecond = async () => {
+ const toggleSecond = async function () {
await toggleSecondButton.click();
};
});
- describe("Action Bar extension with dynamic ActionItem", async () => {
+ describe("Action Bar extension with dynamic ActionItem", async function () {
let toggleButton: UIElement;
let conditional: UIElement;
- before(async () => {
- driver = await createDriver();
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
await driver.driver.resetApp();
});
- it("should navigate to page", async () => {
+ it("should navigate to page", async function () {
const navigationButton =
- await driver.findElementByText("ActionBarExtension", SearchOptions.exact);
+ await driver.findElementByAutomationText("ActionBarExtension");
await navigationButton.click();
});
- it("should find elements", async () => {
- toggleButton = await driver.findElementByText("toggle");
- conditional = await driver.findElementByText("conditional");
+ it("should find elements", async function () {
+ toggleButton = await driver.findElementByAutomationText("toggle");
+ conditional = await driver.findElementByAutomationText("conditional");
});
- it("should detach conditional action item when its condition is false", done => {
- (async () => {
- await toggle();
-
- try {
- await driver.findElementByText("conditional", SearchOptions.exact);
- } catch (e) {
- done();
- }
- })();
+ it("should detach conditional action item when its condition is false", async function () {
+ await toggle();
+ const conditionalBtn = await driver.waitForElement("conditional", 1000);
+ assert.isUndefined(conditionalBtn, "Conditional button should not be visible!");
});
- it("should reattach conditional action item at correct place", async () => {
+ it("should reattach conditional action item at correct place", async function () {
await toggle();
await checkOrderIsCorrect();
});
-
- const checkOrderIsCorrect = async () => {
+
+ const checkOrderIsCorrect = async function () {
await isOnTheLeft(toggleButton, conditional);
};
- const toggle = async () => {
+ const toggle = async function () {
await toggleButton.click();
};
});
diff --git a/e2e/renderer/e2e/ngfor.e2e-spec.ts b/e2e/renderer/e2e/ngfor.e2e-spec.ts
index ff0f0a162..a003f2c69 100644
--- a/e2e/renderer/e2e/ngfor.e2e-spec.ts
+++ b/e2e/renderer/e2e/ngfor.e2e-spec.ts
@@ -2,81 +2,107 @@ import {
AppiumDriver,
createDriver,
SearchOptions,
- UIElement
+ UIElement,
+ nsCapabilities
} from "nativescript-dev-appium";
import { isAbove } from "./helpers/location";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
-describe("ngFor scenario", () => {
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+const isSauceRun = isSauceLab;
+
+describe("ngFor scenario", async function () {
let driver: AppiumDriver;
let addButton: UIElement;
let removeButton: UIElement;
let elements: UIElement[] = [];
let lastAddedElementId = 0;
- before(async () => {
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
await driver.driver.resetApp();
});
- it("should navigate to page", async () => {
+ after(async function () {
+ if (isSauceRun) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ it("should navigate to page", async function () {
const navigationButton =
- await driver.findElementByText("NgFor", SearchOptions.exact);
+ await driver.findElementByAutomationText("NgFor");
await navigationButton.click();
const actionBar =
- await driver.findElementByText("ngFor", SearchOptions.exact);
+ await driver.findElementByAutomationText("ngFor");
});
- it("should find elements", async () => {
- const first = await driver.findElementByText(
- lastAddedElementId.toString(), SearchOptions.exact);
+ it("should find elements", async function () {
+ const first = await driver.findElementByAutomationText(
+ lastAddedElementId.toString());
elements.push(first);
- addButton = await driver.findElementByText("add", SearchOptions.exact);
- removeButton = await driver.findElementByText("remove", SearchOptions.exact);
+ addButton = await driver.findElementByAutomationText("add");
+ removeButton = await driver.findElementByAutomationText("remove");
await isAbove(first, addButton);
});
- it("should render elements in correct order", async () => {
+ it("should render elements in correct order", async function () {
await isAbove(elements[0], addButton);
await isAbove(addButton, removeButton);
});
- it("should place new elements in the right places", async () => {
- for (let i = 0; i < 5; i += 1) {
+ it("should place new elements in the right places", async function () {
+ for (let i = 0; i < 3; i += 1) {
await addElement();
await checkAppendedCorrectly();
}
});
- it("shouldn't reorder elements when last is removed", async () => {
+ it("shouldn't reorder elements when last is removed", async function () {
while (elements.length) {
await removeElement();
await checkCorrectOrderAll();
}
});
- it("should render new elements correctly after all old ones are removed", async () => {
- for (let i = 0; i < 5; i += 1) {
+ it("should render new elements correctly after all old ones are removed", async function () {
+ for (let i = 0; i < 3; i += 1) {
await addElement();
await checkCorrectOrderAll();
}
});
- it("shouldn't reorder elements when middle is removed", async () => {
+ it("shouldn't reorder elements when middle is removed", async function () {
const middleIndex = Math.floor(elements.length / 2);
await removeElement(middleIndex);
await checkCorrectOrderAll();
});
- const addElement = async () => {
+ const addElement = async function () {
await addButton.click();
lastAddedElementId += 1;
- const newElement = await driver.findElementByText(
- lastAddedElementId.toString(), SearchOptions.exact);
+ const newElement = await driver.findElementByAutomationText(
+ lastAddedElementId.toString());
elements.push(newElement);
};
@@ -95,16 +121,16 @@ describe("ngFor scenario", () => {
lastAddedElementId -= 1;
};
- const checkAppendedCorrectly = async () => {
- const lastAdded = await driver.findElementByText(
- lastAddedElementId.toString(), SearchOptions.exact);
+ const checkAppendedCorrectly = async function () {
+ const lastAdded = await driver.findElementByAutomationText(
+ lastAddedElementId.toString());
await isAbove(elements.slice(-2)[0], lastAdded);
await isAbove(lastAdded, addButton);
await isAbove(addButton, removeButton);
};
- const checkCorrectOrderAll = async () => {
+ const checkCorrectOrderAll = async function () {
for (let i = 0; i < elements.length - 1; i += 1) {
await isAbove(elements[i], elements[i + 1]);
}
diff --git a/e2e/renderer/e2e/ngforof.e2e-spec.ts b/e2e/renderer/e2e/ngforof.e2e-spec.ts
index e82406777..2a8cc9eb6 100644
--- a/e2e/renderer/e2e/ngforof.e2e-spec.ts
+++ b/e2e/renderer/e2e/ngforof.e2e-spec.ts
@@ -2,10 +2,15 @@ import {
AppiumDriver,
createDriver,
SearchOptions,
- UIElement
+ UIElement,
+ nsCapabilities
} from "nativescript-dev-appium";
import { isAbove } from "./helpers/location";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
interface ElementTuple {
label: UIElement,
@@ -20,67 +25,87 @@ describe("ngForOf scenario", function () {
let elements: ElementTuple[] = [];
let lastAddedElementId = 0;
- before(async () => {
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
await driver.driver.resetApp();
});
- it("should navigate to page", async () => {
+ after(async function () {
+ if (isSauceLab) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ it("should navigate to page", async function () {
const navigationButton =
- await driver.findElementByText("NgForOf", SearchOptions.exact);
+ await driver.findElementByAutomationText("NgForOf");
await navigationButton.click();
const actionBar =
- await driver.findElementByText("ngForOf", SearchOptions.exact);
+ await driver.findElementByAutomationText("ngForOf");
});
- it("should find elements", async () => {
+ it("should find elements", async function () {
const firstElement = await getElement(lastAddedElementId);
elements.push(firstElement);
- addButton = await driver.findElementByText("add", SearchOptions.exact);
- removeButton = await driver.findElementByText("remove", SearchOptions.exact);
+ addButton = await driver.findElementByAutomationText("add");
+ removeButton = await driver.findElementByAutomationText("remove");
await elementTupleCorrectlyRendered(firstElement);
await isAbove(firstElement.button, addButton);
});
- it("should render elements in correct order", async () => {
+ it("should render elements in correct order", async function () {
await elementTupleCorrectlyRendered(elements[0]);
await isAbove(elements[0].button, addButton);
await isAbove(addButton, removeButton);
});
- it("should place new elements in the right places", async () => {
+ it("should place new elements in the right places", async function () {
for (let i = 0; i < 2; i += 1) {
await addElement();
await checkAppendedCorrectly();
}
});
- it("shouldn't reorder elements when last is removed", async () => {
+ it("shouldn't reorder elements when last is removed", async function () {
while (elements.length) {
await removeElement();
await checkCorrectOrderAll();
}
});
- it("should render new elements correctly after all old ones are removed", async () => {
- for (let i = 0; i < 5; i += 1) {
+ it("should render new elements correctly after all old ones are removed", async function () {
+ for (let i = 0; i < 3; i += 1) {
await addElement();
await checkCorrectOrderAll();
}
});
- it("shouldn't reorder elements when middle is removed", async () => {
+ it("shouldn't reorder elements when middle is removed", async function () {
const middleIndex = Math.floor(elements.length / 2);
await removeElement(middleIndex);
await checkCorrectOrderAll();
});
- const addElement = async () => {
+ const addElement = async function () {
await addButton.click();
lastAddedElementId += 1;
@@ -106,7 +131,7 @@ describe("ngForOf scenario", function () {
lastAddedElementId -= 1;
};
- const checkAppendedCorrectly = async () => {
+ const checkAppendedCorrectly = async function () {
const lastAdded = await getElement(lastAddedElementId);
await elementIsAbove(elements.slice(-2)[0], lastAdded);
@@ -114,7 +139,7 @@ describe("ngForOf scenario", function () {
await isAbove(addButton, removeButton);
};
- const checkCorrectOrderAll = async () => {
+ const checkCorrectOrderAll = async function () {
for (let i = 0; i < elements.length - 1; i += 1) {
await elementIsAbove(elements[i], elements[i + 1]);
}
@@ -142,7 +167,7 @@ describe("ngForOf scenario", function () {
const getElement = async (id: number) => {
let label = null;
let button = null;
-
+
if (driver.platformName.toLowerCase().includes("ios")) {
label = await driver.findElementByAccessibilityId(
"label: " + id.toString());
@@ -150,11 +175,11 @@ describe("ngForOf scenario", function () {
button = await driver.findElementByAccessibilityId(
id.toString());
} else {
- label = await driver.findElementByText(
- "label: " + id.toString(), SearchOptions.exact);
+ label = await driver.findElementByAutomationText(
+ "label: " + id.toString());
- button = await driver.findElementByText(
- id.toString(), SearchOptions.exact);
+ button = await driver.findElementByAutomationText(
+ id.toString());
}
return { label, button };
diff --git a/e2e/renderer/e2e/ngif.e2e-spec.ts b/e2e/renderer/e2e/ngif.e2e-spec.ts
index 4684fe4df..48bdce7ed 100644
--- a/e2e/renderer/e2e/ngif.e2e-spec.ts
+++ b/e2e/renderer/e2e/ngif.e2e-spec.ts
@@ -2,80 +2,106 @@ import {
AppiumDriver,
createDriver,
SearchOptions,
- UIElement
+ UIElement,
+ nsCapabilities
} from "nativescript-dev-appium";
import { isAbove } from "./helpers/location";
import { assert } from "chai";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
-describe("ngIf scenario", () => {
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+
+
+describe("ngIf scenario", async function () {
let driver: AppiumDriver;
let toggleButton: UIElement;
- describe("without layout", async () => {
- before(async () => {
- driver = await createDriver();
- await driver.driver.resetApp();
- });
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ await driver.driver.resetApp();
+ });
+
+ after(async function () {
+ if (isSauceLab) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
- it("should navigate to page", async () => {
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ describe("without layout", async function () {
+ it("should navigate to page", async function () {
const navigationButton =
- await driver.findElementByText("NgIf no layout", SearchOptions.exact);
+ await driver.findElementByAutomationText("NgIf no layout");
await navigationButton.click();
const actionBar =
- await driver.findElementByText("ngIf - no layout", SearchOptions.exact);
+ await driver.findElementByAutomationText("ngIf - no layout");
});
- it("should find elements", async () => {
- await driver.findElementByText("false", SearchOptions.exact);
- toggleButton = await driver.findElementByText("Toggle", SearchOptions.exact);
+ it("should find elements", async function () {
+ await driver.findElementByAutomationText("false");
+ toggleButton = await driver.findElementByAutomationText("Toggle");
});
- it("show 'true' button when show is true", async () => {
+ it("show 'true' button when show is true", async function () {
await toggleButton.click();
- await driver.findElementByText("true", SearchOptions.exact);
+ await driver.findElementByAutomationText("true");
});
});
- describe("label inbetween", async () => {
+ describe("label inbetween", async function () {
let firstButton: UIElement;
let secondButton: UIElement;
let conditionalLabel: UIElement;
let toggle: UIElement;
- before(async () => {
- driver = await createDriver();
- await driver.driver.resetApp();
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.driver.resetApp();
});
- it("should navigate to page", async () => {
+ it("should navigate to page", async function () {
const navigationButton =
- await driver.findElementByText("NgIf inbetween", SearchOptions.exact);
+ await driver.findElementByAutomationText("NgIf inbetween");
await navigationButton.click();
const actionBar =
- await driver.findElementByText("ngIf - inbetween", SearchOptions.exact);
+ await driver.findElementByAutomationText("ngIf - inbetween");
});
- it("should find elements", async () => {
- firstButton = await driver.findElementByText("Button 1", SearchOptions.exact);
- secondButton = await driver.findElementByText("Button 2", SearchOptions.exact);
- toggleButton = await driver.findElementByText("Toggle", SearchOptions.exact);
+ it("should find elements", async function () {
+ firstButton = await driver.findElementByAutomationText("Button 1");
+ secondButton = await driver.findElementByAutomationText("Button 2");
+ toggleButton = await driver.findElementByAutomationText("Toggle");
- conditionalLabel = await driver.findElementByText("Label", SearchOptions.exact);
+ conditionalLabel = await driver.findElementByAutomationText("Label");
const labelIsDisplayed = await conditionalLabel.isDisplayed();
assert.isTrue(labelIsDisplayed);
});
it("detach label when condition is false", done => {
- (async () => {
+ (async function () {
await toggleButton.click();
try {
- await driver.findElementByText("Label", SearchOptions.exact);
+ await driver.findElementByAutomationText("Label");
} catch (e) {
done();
}
@@ -83,54 +109,54 @@ describe("ngIf scenario", () => {
});
});
- describe("with else template", async () => {
+ describe("with else template", async function () {
let ifButton: UIElement;
let elseButton: UIElement;
let toggle: UIElement;
- before(async () => {
- driver = await createDriver();
- await driver.driver.resetApp();
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.driver.resetApp();
});
- it("should navigate to page", async () => {
+ it("should navigate to page", async function () {
const navigationButton =
- await driver.findElementByText("NgIfElse", SearchOptions.exact);
+ await driver.findElementByAutomationText("NgIfElse");
await navigationButton.click();
const actionBar =
- await driver.findElementByText("ngIfElse", SearchOptions.exact);
+ await driver.findElementByAutomationText("ngIfElse");
});
- it("should find elements", async () => {
- toggleButton = await driver.findElementByText("Toggle", SearchOptions.exact);
- ifButton = await driver.findElementByText("If", SearchOptions.exact);
+ it("should find elements", async function () {
+ toggleButton = await driver.findElementByAutomationText("Toggle");
+ ifButton = await driver.findElementByAutomationText("If");
});
it("shouldn't render 'else' template when condition is true", done => {
- driver.findElementByText("Else", SearchOptions.exact)
+ driver.findElementByAutomationText("Else", SearchOptions.exact)
.then(_ => { throw new Error("Else template found!"); })
.catch(() => done());
});
- it("should attach 'else' template when condition is changed to false", async () => {
+ it("should attach 'else' template when condition is changed to false", async function () {
await toggleButton.click();
- elseButton = await driver.findElementByText("Else", SearchOptions.exact);
+ elseButton = await driver.findElementByAutomationText("Else");
});
it("should detach 'if' template when condition is changed to false", done => {
- driver.findElementByText("If", SearchOptions.exact)
+ driver.findElementByAutomationText("If", SearchOptions.exact)
.then(_ => { throw new Error("If template found!"); })
.catch(() => done());
});
it("should swap the content when condition is changed", done => {
- (async () => {
+ (async function () {
await toggleButton.click();
try {
- await driver.findElementByText("Else", SearchOptions.exact);
+ await driver.findElementByAutomationText("Else");
} catch (e) {
done();
}
@@ -138,54 +164,54 @@ describe("ngIf scenario", () => {
});
});
- describe("with then-else template", async () => {
+ describe("with then-else template", async function () {
let thenButton: UIElement;
let elseButton: UIElement;
let toggle: UIElement;
- before(async () => {
- driver = await createDriver();
- await driver.driver.resetApp();
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.driver.resetApp();
});
- it("should navigate to page", async () => {
+ it("should navigate to page", async function () {
const navigationButton =
- await driver.findElementByText("NgIf Then Else", SearchOptions.exact);
+ await driver.findElementByAutomationText("NgIf Then Else");
await navigationButton.click();
const actionBar =
- await driver.findElementByText("ngIf Then Else", SearchOptions.exact);
+ await driver.findElementByAutomationText("ngIf Then Else");
});
- it("should find elements", async () => {
- toggleButton = await driver.findElementByText("Toggle", SearchOptions.exact);
- thenButton = await driver.findElementByText("Then", SearchOptions.exact);
+ it("should find elements", async function () {
+ toggleButton = await driver.findElementByAutomationText("Toggle");
+ thenButton = await driver.findElementByAutomationText("Then");
});
it("shouldn't render 'else' template when condition is true", done => {
- driver.findElementByText("Else", SearchOptions.exact)
+ driver.findElementByAutomationText("Else", SearchOptions.exact)
.then(_ => { throw new Error("Else template found!"); })
.catch(() => done());
});
- it("should attach 'else' template when condition is changed to false", async () => {
+ it("should attach 'else' template when condition is changed to false", async function () {
await toggleButton.click();
- elseButton = await driver.findElementByText("Else", SearchOptions.exact);
+ elseButton = await driver.findElementByAutomationText("Else");
});
it("should detach 'then' template when condition is changed to false", done => {
- driver.findElementByText("Then", SearchOptions.exact)
+ driver.findElementByAutomationText("Then", SearchOptions.exact)
.then(_ => { throw new Error("Then template found!"); })
.catch(() => done());
});
it("should swap the content when condition is changed", done => {
- (async () => {
+ (async function () {
await toggleButton.click();
try {
- await driver.findElementByText("Else", SearchOptions.exact);
+ await driver.findElementByAutomationText("Else");
} catch (e) {
done();
}
@@ -193,54 +219,54 @@ describe("ngIf scenario", () => {
});
});
- describe("then-else templates inside content view", async () => {
+ describe("then-else templates inside content view", async function () {
let thenButton: UIElement;
let elseButton: UIElement;
let toggle: UIElement;
- before(async () => {
- driver = await createDriver();
- await driver.driver.resetApp();
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.driver.resetApp();
});
- it("should navigate to page", async () => {
+ it("should navigate to page", async function () {
const navigationButton =
- await driver.findElementByText("Content view", SearchOptions.exact);
+ await driver.findElementByAutomationText("Content view");
await navigationButton.click();
const actionBar =
- await driver.findElementByText("Content View", SearchOptions.exact);
+ await driver.findElementByAutomationText("Content View");
});
- it("should find elements", async () => {
- toggleButton = await driver.findElementByText("Toggle", SearchOptions.exact);
- thenButton = await driver.findElementByText("Then", SearchOptions.exact);
+ it("should find elements", async function () {
+ toggleButton = await driver.findElementByAutomationText("Toggle");
+ thenButton = await driver.findElementByAutomationText("Then");
});
it("shouldn't render 'else' template when condition is true", done => {
- driver.findElementByText("Else", SearchOptions.exact)
+ driver.findElementByAutomationText("Else", SearchOptions.exact)
.then(_ => { throw new Error("Else template found!"); })
.catch(() => done());
});
- it("should attach 'else' template when condition is changed to false", async () => {
+ it("should attach 'else' template when condition is changed to false", async function () {
await toggleButton.click();
- elseButton = await driver.findElementByText("Else", SearchOptions.exact);
+ elseButton = await driver.findElementByAutomationText("Else");
});
it("should detach 'then' template when condition is changed to false", done => {
- driver.findElementByText("Then", SearchOptions.exact)
+ driver.findElementByAutomationText("Then", SearchOptions.exact)
.then(_ => { throw new Error("Then template found!"); })
.catch(() => done());
});
it("should swap the content when condition is changed", done => {
- (async () => {
+ (async function () {
await toggleButton.click();
try {
- await driver.findElementByText("Else", SearchOptions.exact);
+ await driver.findElementByAutomationText("Else");
} catch (e) {
done();
}
@@ -248,29 +274,29 @@ describe("ngIf scenario", () => {
});
});
- describe("subsequent ifs", async () => {
+ describe("subsequent ifs", async function () {
let firstButton: UIElement;
let secondButton: UIElement;
let firstLabel: UIElement;
let secondLabel: UIElement;
- before(async () => {
- driver = await createDriver();
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
await driver.driver.resetApp();
});
- it("should navigate to page", async () => {
+ it("should navigate to page", async function () {
const navigationButton =
- await driver.findElementByText("NgIf Subsequent Ifs", SearchOptions.exact);
+ await driver.findElementByAutomationText("NgIf Subsequent Ifs");
await navigationButton.click();
});
- it("should find elements", async () => {
- firstButton = await driver.findElementByText("Toggle first", SearchOptions.exact);
- secondButton = await driver.findElementByText("Toggle second", SearchOptions.exact);
+ it("should find elements", async function () {
+ firstButton = await driver.findElementByAutomationText("Toggle first");
+ secondButton = await driver.findElementByAutomationText("Toggle second");
- firstLabel = await driver.findElementByText("== 1 ==", SearchOptions.exact);
- secondLabel = await driver.findElementByText("== 2 ==", SearchOptions.exact);
+ firstLabel = await driver.findElementByAutomationText("== 1 ==");
+ secondLabel = await driver.findElementByAutomationText("== 2 ==");
assert.isDefined(firstButton);
assert.isDefined(secondButton);
@@ -278,89 +304,89 @@ describe("ngIf scenario", () => {
assert.isDefined(secondLabel);
});
- it("should toggle on first view", async () => {
+ it("should toggle on first view", async function () {
await firstButton.click();
- let conditional = await driver.findElementByText("first", SearchOptions.exact);
+ let conditional = await driver.findElementByAutomationText("first");
await isAbove(firstLabel, conditional);
await isAbove(conditional, secondLabel);
});
it("should toggle off first view", done => {
- (async () => {
+ (async function () {
await firstButton.click();
- driver.findElementByText("first", SearchOptions.exact, 500)
+ driver.findElementsByAutomationText("first", 500)
.then(_ => { throw new Error("first label found!"); })
.catch(() => done());
})();
});
- it("should toggle on second view", async () => {
+ it("should toggle on second view", async function () {
await secondButton.click();
- let conditional = await driver.findElementByText("second", SearchOptions.exact);
+ let conditional = await driver.findElementByAutomationText("second");
await isAbove(firstLabel, conditional);
await isAbove(conditional, secondLabel);
});
it("should toggle off second view", done => {
- (async () => {
+ (async function () {
await secondButton.click();
- driver.findElementByText("first", SearchOptions.exact, 500)
+ driver.findElementByAutomationText("first", 500)
.then(_ => { throw new Error("first label found!"); })
.catch(() => done());
})();
});
- it("should toggle on both views", async () => {
+ it("should toggle on both views", async function () {
await firstButton.click();
await secondButton.click();
- let conditional1 = await driver.findElementByText("first", SearchOptions.exact);
- let conditional2 = await driver.findElementByText("second", SearchOptions.exact);
+ let conditional1 = await driver.findElementByAutomationText("first");
+ let conditional2 = await driver.findElementByAutomationText("second");
await isAbove(firstLabel, conditional1);
await isAbove(conditional1, conditional2);
await isAbove(conditional2, secondLabel);
});
it("should toggle off both views", done => {
- (async () => {
+ (async function () {
await firstButton.click();
await secondButton.click();
- driver.findElementByText("first", SearchOptions.exact, 500)
+ driver.findElementByAutomationText("first", 500)
.then(_ => { throw new Error("first label found!"); })
- .catch(() => {
- driver.findElementByText("second", SearchOptions.exact, 500)
+ .catch(async function () {
+ driver.findElementByAutomationText("second", 500)
.then(_ => { throw new Error("second label found!"); })
.catch(() => done());
});
})();
});
- it("should toggle on both views in reverse", async () => {
+ it("should toggle on both views in reverse", async function () {
await secondButton.click();
await firstButton.click();
- let conditional1 = await driver.findElementByText("first", SearchOptions.exact);
- let conditional2 = await driver.findElementByText("second", SearchOptions.exact);
+ let conditional1 = await driver.findElementByAutomationText("first");
+ let conditional2 = await driver.findElementByAutomationText("second");
await isAbove(firstLabel, conditional1);
await isAbove(conditional1, conditional2);
await isAbove(conditional2, secondLabel);
});
it("should toggle off both views in reverse", done => {
- (async () => {
+ (async function () {
await secondButton.click();
await firstButton.click();
- driver.findElementByText("first", SearchOptions.exact, 500)
+ driver.findElementByAutomationText("first", 500)
.then(_ => { throw new Error("first label found!"); })
- .catch(() => {
- driver.findElementByText("second", SearchOptions.exact, 500)
+ .catch(async function () {
+ driver.findElementByAutomationText("second", 500)
.then(_ => { throw new Error("second label found!"); })
.catch(() => done());
});
diff --git a/e2e/renderer/e2e/page-router-outlet.e2e-spec.ts b/e2e/renderer/e2e/page-router-outlet.e2e-spec.ts
new file mode 100644
index 000000000..8adc9e9cf
--- /dev/null
+++ b/e2e/renderer/e2e/page-router-outlet.e2e-spec.ts
@@ -0,0 +1,191 @@
+import {
+ AppiumDriver,
+ createDriver,
+ SearchOptions,
+ UIElement,
+ nsCapabilities
+} from "nativescript-dev-appium";
+import { assert } from "chai";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+
+describe("page-router-outlet-scenario", async function () {
+ let driver: AppiumDriver;
+
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ await driver.driver.resetApp();
+ });
+
+ after(async function () {
+ if (isSauceLab) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ describe("actionBarVisibility 'always' shows action bars", async function () {
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ });
+ it("should navigate to page", async function () {
+ const navigationButton =
+ await driver.findElementByAutomationText("ActionBarVisibility Always");
+ await navigationButton.click();
+
+ await driver.findElementByAutomationText("ShowActionBar");
+ });
+
+ it("should not hide action bar by default", async function () {
+ const screenMatches = await driver.compareScreen("actionBarVisibility-always-default", 5, 50, ImageOptions.pixel);
+ assert(screenMatches);
+ });
+
+ it("should not hide action bar when hidden by page", async function () {
+ const hideActionBarButton = await driver.findElementByAutomationText("HideActionBar");
+ await hideActionBarButton.click();
+
+ const screenMatches = await driver.compareScreen("actionBarVisibility-always-hidden", 5, 50, ImageOptions.pixel);
+ assert(screenMatches);
+ });
+
+ it("should not do anything when shown action bar by page", async function () {
+ const showActionBarButton = await driver.findElementByAutomationText("ShowActionBar");
+ await showActionBarButton.click();
+
+ const screenMatches = await driver.compareScreen("actionBarVisibility-always-shown", 5, 50, ImageOptions.pixel);
+ assert(screenMatches);
+ });
+ });
+
+ describe("actionBarVisibility 'never' doesn't show action bars", async function () {
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.driver.resetApp();
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ it("should navigate to page", async function () {
+ const navigationButton =
+ await driver.findElementByAutomationText("ActionBarVisibility Never");
+ await navigationButton.click();
+
+ await driver.findElementByAutomationText("ShowActionBar");
+ });
+
+ it("should hide action bar by default", async function () {
+ const screenMatches = await driver.compareScreen("actionBarVisibility-never-default", 5);
+ assert(screenMatches);
+ });
+
+ it("should not show action bar when shown by page", async function () {
+ const showActionBarButton = await driver.findElementByAutomationText("ShowActionBar");
+ await showActionBarButton.click();
+
+ const screenMatches = await driver.compareScreen("actionBarVisibility-never-shown", 5);
+ assert(screenMatches);
+ });
+
+ it("should not do anything when hidden action bar by page", async function () {
+ const hideActionBarButton = await driver.findElementByAutomationText("HideActionBar");
+ await hideActionBarButton.click();
+
+ const screenMatches = await driver.compareScreen("actionBarVisibility-never-hidden", 5);
+ assert(screenMatches);
+ });
+ });
+
+ describe("actionBarVisibility 'never' doesn't show action bars in lazy module page", async function () {
+ let imagePostFix = "";
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.driver.resetApp();
+ if (driver.isIOS && driver.nsCapabilities.device.name.toLowerCase().includes("x")) {
+ imagePostFix = "-lazy";
+ }
+ });
+
+ it("should navigate to page", async function () {
+ const navigationButton =
+ await driver.findElementByAutomationText("ActionBarVisibility Never Lazy");
+ await navigationButton.click();
+
+ await driver.findElementByAutomationText("ShowActionBar");
+ });
+
+ it("should hide action bar by default", async function () {
+ const screenMatches = await driver.compareScreen(`actionBarVisibility-never-default${imagePostFix}`, 5);
+ assert(screenMatches);
+ });
+
+ it("should not show action bar when shown by page", async function () {
+ const showActionBarButton = await driver.findElementByAutomationText("ShowActionBar");
+ await showActionBarButton.click();
+
+ const screenMatches = await driver.compareScreen(`actionBarVisibility-never-shown${imagePostFix}`, 5);
+ assert(screenMatches);
+ });
+
+ it("should not do anything when hidden action bar by page", async function () {
+ const hideActionBarButton = await driver.findElementByAutomationText("HideActionBar");
+ await hideActionBarButton.click();
+
+ const screenMatches = await driver.compareScreen(`actionBarVisibility-never-hidden${imagePostFix}`, 5);
+ assert(screenMatches);
+ });
+ });
+
+ describe("actionBarVisibility 'auto' shows action bars based on page", async function () {
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.driver.resetApp();
+ });
+
+ it("should navigate to page", async function () {
+ const navigationButton =
+ await driver.findElementByAutomationText("ActionBarVisibility Auto");
+ await navigationButton.click();
+
+ await driver.findElementByAutomationText("ShowActionBar");
+ });
+
+ it("should show action bar by default", async function () {
+ const screenMatches = await driver.compareScreen("actionBarVisibility-auto-default", 5, 50, ImageOptions.pixel);
+ assert(screenMatches);
+ });
+
+ it("should hide action bar when hidden by page", async function () {
+ const hideActionBarButton = await driver.findElementByAutomationText("HideActionBar");
+ await hideActionBarButton.click();
+ const screenMatches = await driver.compareScreen("actionBarVisibility-auto-hidden", 5, 50, ImageOptions.pixel);
+ assert(screenMatches);
+ });
+
+ it("should show action bar when shown by page", async function () {
+ const showActionBarButton = await driver.findElementByAutomationText("ShowActionBar");
+ await showActionBarButton.click();
+ const screenMatches = await driver.compareScreen("actionBarVisibility-auto-shown", 5, 50, ImageOptions.pixel);
+ assert(screenMatches);
+ });
+ });
+});
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..af6e05907
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..af6e05907
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..af6e05907
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..22ffb753d
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..1820d9968
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..22ffb753d
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..5e169fead
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..77157b59b
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..77157b59b
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..dbde0ae87
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..acf56dcb9
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..bea843a8b
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api19-Default/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..96b65ee91
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..96b65ee91
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..69387cb82
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..49b4436e5
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..6a3cd82d5
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..85fca7cb7
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..a2518a90d
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..3e918bcfe
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..a2518a90d
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..87ffb5fcc
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..46636cc03
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..37cdd55e7
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Default/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..d992e38a5
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..b6ca95617
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..b6ca95617
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..f63b5fe8d
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..3b6875b90
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..413ddf8e5
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..0f2fca64a
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..35816ac63
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..d5393b063
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..54f6fd597
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..8f3410ad4
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..da64f2b00
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api22-Google/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..c25401fb3
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..c25401fb3
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..c25401fb3
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..3bd8a4d64
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..b3a3ad9fd
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..3bd8a4d64
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..6bb8876e8
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..6bb8876e8
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..6bb8876e8
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..c4f79265e
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..c95e16540
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..d1ebe07b0
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api23-Default/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..782f0f32c
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..782f0f32c
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..1608b2c8b
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..e7a1e22ab
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..e7a1e22ab
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..75b52701a
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..8fd59b248
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..b8581a8bf
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..8fd59b248
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..bfa18e232
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..90083f434
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..ff122eae4
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api24-Default/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..6a817111d
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..6a817111d
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..cbbde7786
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..313a64685
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..71f266277
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..313a64685
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..def293798
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..79ad4bb71
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..def293798
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..4904d61c6
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..b4515ca00
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..bfa8544c5
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api25-Google/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..f056bfa0d
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..f056bfa0d
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..015a4398b
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..938b019c9
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..b47cb9e45
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..938b019c9
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..69c7c667c
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..3f2ee27fd
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..69c7c667c
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..71686fb63
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..af47cc7ed
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..b8501b98e
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api26-Google/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..1ddb743a1
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..1ddb743a1
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..b52ebf70a
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..ebea2dea8
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..836da97a3
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..40cd6413a
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..203e68d79
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..738e9dccc
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..cd58d6bbe
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..cfe2421db
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..a88ef58f3
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..d97035d23
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api27-Google/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..b8deed4a0
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..b8deed4a0
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..8cf735126
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..6d616d8fb
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..410dcb8c3
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..6c4ad9dea
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..1bbd6b2e1
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..cadcf67b6
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..cadcf67b6
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..2a6479be2
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..d5a33e4d4
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..4432ba921
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api28-Google/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..6fcbd1b78
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..6fcbd1b78
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..6fcbd1b78
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..9ac141c2f
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..bee8777f6
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..9ac141c2f
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..1f80e5739
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..1f80e5739
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..1f80e5739
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..f59d65640
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..a60bc2cf2
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..559e2567c
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/Emulator-Api29-Google/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 100/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 100/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..86e41b580
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 100/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 100/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 100/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..888039781
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 100/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 100/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 100/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..266dbc621
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 100/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..f7cad6d50
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..f7cad6d50
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..f7cad6d50
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..c8509ceab
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..ee4064e34
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..33f839482
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-default-lazy_actual.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-default-lazy_actual.png
new file mode 100644
index 000000000..ab50e264d
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-default-lazy_actual.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..ea4509395
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-hidden-lazy_actual.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-hidden-lazy_actual.png
new file mode 100644
index 000000000..afe670a4f
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-hidden-lazy_actual.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..ea4509395
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-shown-lazy_actual.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-shown-lazy_actual.png
new file mode 100644
index 000000000..afe670a4f
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-shown-lazy_actual.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..ea4509395
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..fb8a587b7
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..b00d32eda
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..bb19588cc
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 110/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..81bf64f76
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..f0212faf3
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..f0212faf3
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..283f26ca8
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..f20e481c0
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..81b463120
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..0b5d71b42
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..12af4ab42
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..12af4ab42
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..a781c90f1
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..2a0c98ebf
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..ecd473fae
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone 7 12/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..8eb8f1870
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..8eb8f1870
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..8eb8f1870
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..44c48ceb9
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..5e06beb38
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..44c48ceb9
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-default-lazy.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-default-lazy.png
new file mode 100644
index 000000000..9ebe2b597
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-default-lazy.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..99ebc13a7
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-hidden-lazy.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-hidden-lazy.png
new file mode 100644
index 000000000..6ed7234b1
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-hidden-lazy.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..a9efd56f9
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-shown-lazy.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-shown-lazy.png
new file mode 100644
index 000000000..6ed7234b1
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-shown-lazy.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..e434e2d18
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..cf2353d74
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..c4a909467
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..469c82750
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone X 110/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..6724bef38
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..61c63323c
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..61c63323c
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..4482f1334
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..306e7275e
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..1fab9e6f3
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-default-lazy.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-default-lazy.png
new file mode 100644
index 000000000..e6afdad26
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-default-lazy.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..666767c49
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-hidden-lazy.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-hidden-lazy.png
new file mode 100644
index 000000000..ac403f9f9
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-hidden-lazy.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..2c44f8ccd
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-shown-lazy.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-shown-lazy.png
new file mode 100644
index 000000000..ac403f9f9
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-shown-lazy.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..2c44f8ccd
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..e13752607
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..1998b81c6
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..2297f4b97
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 12/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..0d641d88b
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..0d641d88b
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..0d641d88b
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..ed051c10f
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..65f428e84
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..ed051c10f
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-default-lazy.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-default-lazy.png
new file mode 100644
index 000000000..6a61c9b25
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-default-lazy.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..74560afcb
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-hidden-lazy.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-hidden-lazy.png
new file mode 100644
index 000000000..6a61c9b25
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-hidden-lazy.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..74560afcb
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-shown-lazy.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-shown-lazy.png
new file mode 100644
index 000000000..6a61c9b25
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-shown-lazy.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..74560afcb
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/tab-view-binding-first-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/tab-view-binding-first-tab.png
new file mode 100644
index 000000000..714d335fa
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/tab-view-binding-first-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/tab-view-binding-second-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/tab-view-binding-second-tab.png
new file mode 100644
index 000000000..70b0ab1fb
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/tab-view-binding-second-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/tab-view-binding-third-tab.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/tab-view-binding-third-tab.png
new file mode 100644
index 000000000..4ea08ec99
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XR 13/tab-view-binding-third-tab.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-always-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-always-default.png
new file mode 100644
index 000000000..bf236a332
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-always-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-always-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-always-hidden.png
new file mode 100644
index 000000000..36532a957
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-always-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-always-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-always-shown.png
new file mode 100644
index 000000000..67a2d2515
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-always-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-auto-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-auto-default.png
new file mode 100644
index 000000000..2db5cadce
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-auto-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-auto-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-auto-hidden.png
new file mode 100644
index 000000000..c31877f38
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-auto-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-auto-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-auto-shown.png
new file mode 100644
index 000000000..e547d9e3a
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-auto-shown.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-default-lazy.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-default-lazy.png
new file mode 100644
index 000000000..54f0ffdd4
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-default-lazy.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-default.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-default.png
new file mode 100644
index 000000000..f22f58278
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-default.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-hidden-lazy.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-hidden-lazy.png
new file mode 100644
index 000000000..d3b34b6ab
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-hidden-lazy.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-hidden.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-hidden.png
new file mode 100644
index 000000000..e3059e1ed
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-hidden.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-shown-lazy.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-shown-lazy.png
new file mode 100644
index 000000000..d3b34b6ab
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-shown-lazy.png differ
diff --git a/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-shown.png b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-shown.png
new file mode 100644
index 000000000..11225f72b
Binary files /dev/null and b/e2e/renderer/e2e/resources/images/renderer/iPhone XS 12/actionBarVisibility-never-shown.png differ
diff --git a/e2e/renderer/e2e/setup.ts b/e2e/renderer/e2e/setup.ts
index 549020395..11b854fca 100644
--- a/e2e/renderer/e2e/setup.ts
+++ b/e2e/renderer/e2e/setup.ts
@@ -1,19 +1,19 @@
-import { startServer, stopServer, createDriver, AppiumDriver } from "nativescript-dev-appium";
+import { startServer, stopServer, ITestReporter, nsCapabilities, LogImageType } from "nativescript-dev-appium";
+const addContext = require('mochawesome/addContext');
-let driver: AppiumDriver;
+const testReporterContext = {};
+testReporterContext.name = "mochawesome";
+testReporterContext.reportDir = "mochawesome-report";
+testReporterContext.log = addContext;
+testReporterContext.logImageTypes = [LogImageType.screenshots];
+nsCapabilities.testReporter = testReporterContext;
-before("start server", async () => {
+before("start server", async function () {
+ nsCapabilities.testReporter.context = this;
await startServer();
- driver = await createDriver();
});
-after("stop server", async () => {
- await driver.quit();
+after("stop server", async function () {
+ nsCapabilities.testReporter.context = this;
await stopServer();
});
-
-afterEach(async function () {
- if (this.currentTest.state === "failed") {
- await driver.logScreenshot(this.currentTest.title);
- }
-});
\ No newline at end of file
diff --git a/e2e/renderer/e2e/tab-view.e2e-spec.ts b/e2e/renderer/e2e/tab-view.e2e-spec.ts
new file mode 100644
index 000000000..93950c6a6
--- /dev/null
+++ b/e2e/renderer/e2e/tab-view.e2e-spec.ts
@@ -0,0 +1,98 @@
+import {
+ AppiumDriver,
+ createDriver,
+ SearchOptions,
+ UIElement,
+ nsCapabilities
+} from "nativescript-dev-appium";
+import { assert } from "chai";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+
+describe("TabView-scenario", async function(){
+ let driver: AppiumDriver;
+
+ before(async function(){
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ await driver.driver.resetApp();
+ });
+
+ after(async function () {
+ if (isSauceLab) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ describe("dynamically change TabView item title, icon and textTransform", async function(){
+ let firstTabItem: UIElement;
+ let secondTabItem: UIElement;
+ let thirdTabItem: UIElement;
+
+ it("should navigate to page", async function(){
+ const navigationButton =
+ await driver.findElementByAutomationText("TabItem Binding");
+ await navigationButton.click();
+
+ await driver.findElementByAutomationText("Tab Item Binding");
+ });
+
+ it("should find elements", async function(){
+ await driver.findElementByAutomationText("First Tab");
+
+ const notSelectedTabItems = await driver.findElementsByText("not selected");
+
+ firstTabItem = await driver.findElementByAutomationText("SELECTED");
+ secondTabItem = notSelectedTabItems[0];
+ thirdTabItem = notSelectedTabItems[1];
+
+ const screenMatches = await driver.compareScreen("tab-view-binding-first-tab", 5, 50, ImageOptions.pixel);
+ assert(screenMatches);
+ });
+
+ it("should navigate to second tab item", async function(){
+ await secondTabItem.click();
+
+ await driver.findElementByAutomationText("Second Tab");
+
+ const notSelectedTabItems = await driver.findElementsByText("not selected");
+
+ firstTabItem = notSelectedTabItems[0];
+ secondTabItem = await driver.findElementByAutomationText("SELECTED");
+ thirdTabItem = notSelectedTabItems[1];
+
+ const screenMatches = await driver.compareScreen("tab-view-binding-second-tab", 5, 50, ImageOptions.pixel);
+ assert(screenMatches);
+ });
+
+ it("should navigate to third tab item", async function(){
+ await thirdTabItem.click();
+
+ await driver.findElementByAutomationText("Third Tab");
+
+ const notSelectedTabItems = await driver.findElementsByText("not selected");
+
+ firstTabItem = notSelectedTabItems[0];
+ secondTabItem = notSelectedTabItems[1];
+ thirdTabItem = await driver.findElementByAutomationText("SELECTED");
+
+ const screenMatches = await driver.compareScreen("tab-view-binding-third-tab", 5, 50, ImageOptions.pixel);
+ assert(screenMatches);
+ });
+ });
+});
diff --git a/e2e/renderer/e2e/tsconfig.json b/e2e/renderer/e2e/tsconfig.json
index 18b6c4302..6517ca58d 100644
--- a/e2e/renderer/e2e/tsconfig.json
+++ b/e2e/renderer/e2e/tsconfig.json
@@ -5,13 +5,14 @@
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"importHelpers": false,
+ "sourceMap": true,
"types": [
"node",
"mocha",
"chai"
],
"lib": [
- "es6",
+ "es2015",
"dom"
]
}
diff --git a/e2e/renderer/package.json b/e2e/renderer/package.json
index d74772799..3126f8f2e 100644
--- a/e2e/renderer/package.json
+++ b/e2e/renderer/package.json
@@ -4,62 +4,43 @@
"readme": "NativeScript Application",
"repository": "",
"nativescript": {
- "id": "org.nativescript.renderer",
- "tns-android": {
- "version": "3.4.1-2018-02-06-02"
- }
+ "id": "org.nativescript.renderer"
},
"dependencies": {
- "@angular/common": "~5.2.0",
- "@angular/compiler": "~5.2.0",
- "@angular/core": "~5.2.0",
- "@angular/forms": "~5.2.0",
- "@angular/http": "~5.2.0",
- "@angular/platform-browser": "~5.2.0",
- "@angular/platform-browser-dynamic": "~5.2.0",
- "@angular/router": "~5.2.0",
- "nativescript-angular": "file:../../nativescript-angular",
- "nativescript-intl": "^3.0.0",
+ "@angular/animations": "~9.1.0",
+ "@angular/common": "~9.1.0",
+ "@angular/compiler": "~9.1.0",
+ "@angular/core": "~9.1.0",
+ "@angular/forms": "~9.1.0",
+ "@angular/platform-browser": "~9.1.0",
+ "@angular/platform-browser-dynamic": "~9.1.0",
+ "@angular/router": "~9.1.0",
+ "@nativescript/angular": "file:../../dist/nativescript-angular-scoped.tgz",
+ "nativescript-theme-core": "~1.0.4",
"reflect-metadata": "~0.1.8",
- "rxjs": "^5.5.0",
- "tns-core-modules": "next",
- "zone.js": "^0.8.4"
+ "rxjs": "~6.5.5",
+ "@nativescript/core": "next",
+ "zone.js": "^0.10.3"
},
"devDependencies": {
- "@angular/compiler-cli": "~5.2.0",
- "@ngtools/webpack": "~1.9.4",
- "@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",
- "copy-webpack-plugin": "~4.3.0",
- "css-loader": "~0.28.7",
- "extract-text-webpack-plugin": "~3.0.2",
+ "@angular/compiler-cli": "~9.1.0",
+ "@ngtools/webpack": "~9.1.0",
+ "@types/chai": "~4.1.7",
+ "@types/mocha": "~5.2.5",
+ "@types/node": "~10.12.18",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0",
+ "babylon": "6.18.0",
+ "chai": "^4.2.0",
"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",
- "nativescript-dev-webpack": "^0.9.2",
- "nativescript-worker-loader": "~0.8.1",
- "raw-loader": "~0.5.1",
- "resolve-url-loader": "~2.2.1",
- "tslib": "^1.7.1",
- "typescript": "~2.6.2",
- "uglifyjs-webpack-plugin": "~1.1.6",
- "webpack": "~3.10.0",
- "webpack-bundle-analyzer": "^2.9.1",
- "webpack-sources": "~1.1.0"
+ "mocha": "~5.2.0",
+ "mochawesome": "~3.1.2",
+ "nativescript-dev-appium": "^6.0.0",
+ "nativescript-dev-webpack": "next",
+ "typescript": "~3.8.3"
},
"scripts": {
"e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
- "compile-tests-w": "tsc -p e2e --watch",
- "update-app-ng-deps": "update-app-ng-deps"
+ "compile-tests-w": "tsc -p e2e --watch"
}
}
diff --git a/e2e/renderer/tsconfig.json b/e2e/renderer/tsconfig.json
index f89ae0f58..a3ba8b51a 100644
--- a/e2e/renderer/tsconfig.json
+++ b/e2e/renderer/tsconfig.json
@@ -6,22 +6,35 @@
"emitDecoratorMetadata": true,
"noEmitHelpers": true,
"noEmitOnError": true,
+ "skipLibCheck": true,
"lib": [
- "es6",
+ "es2017",
"dom",
- "es2015.iterable"
+ "es6"
],
"baseUrl": ".",
"paths": {
+ "~/*": [
+ "app/*"
+ ],
"*": [
- "./node_modules/tns-core-modules/*",
"./node_modules/*"
]
}
},
+ "include": [
+ "../../nativescript-angular-package",
+ "../../nativescript-angular",
+ "**/*"
+ ],
"exclude": [
+ "../../nativescript-angular-package/node_modules",
+ "../../nativescript-angular-package/**/*.d.ts",
+ "../../nativescript-angular/node_modules",
+ "../../nativescript-angular/**/*.d.ts",
"node_modules",
"platforms",
+ "**/*.aot",
"e2e"
]
-}
+}
\ No newline at end of file
diff --git a/e2e/renderer/webpack.config.js b/e2e/renderer/webpack.config.js
deleted file mode 100644
index 56434bcbe..000000000
--- a/e2e/renderer/webpack.config.js
+++ /dev/null
@@ -1,163 +0,0 @@
-const { resolve, join } = require("path");
-
-const webpack = require("webpack");
-const nsWebpack = require("nativescript-dev-webpack");
-const nativescriptTarget = require("nativescript-dev-webpack/nativescript-target");
-const CopyWebpackPlugin = require("copy-webpack-plugin");
-const ExtractTextPlugin = require("extract-text-webpack-plugin");
-const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
-const { NativeScriptWorkerPlugin } = require("nativescript-worker-loader/NativeScriptWorkerPlugin");
-const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
-
-module.exports = env => {
- const platform = env && (env.android && "android" || env.ios && "ios");
- if (!platform) {
- throw new Error("You need to provide a target platform!");
- }
- const platforms = ["ios", "android"];
- const { snapshot, uglify, report, aot } = env;
- const ngToolsWebpackOptions = { tsConfigPath: "tsconfig.json" };
-
- const config = {
- context: resolve("./app"),
- target: nativescriptTarget,
- entry: {
- bundle: aot ? "./main.aot.ts" : "./main.ts",
- vendor: "./vendor",
- },
- output: {
- pathinfo: true,
- // Default destination inside platforms//...
- path: resolve(nsWebpack.getAppPath(platform)),
- libraryTarget: "commonjs2",
- filename: "[name].js",
- },
- resolve: {
- extensions: [".ts", ".js", ".scss", ".css"],
- // Resolve {N} system modules from tns-core-modules
- modules: [
- "node_modules/tns-core-modules",
- "node_modules",
- ],
- alias: {
- '~': resolve("./app")
- },
- // don't resolve symlinks to symlinked modules
- symlinks: false
- },
- resolveLoader: {
- // don't resolve symlinks to symlinked loaders
- symlinks: false
- },
- node: {
- // Disable node shims that conflict with NativeScript
- "http": false,
- "timers": false,
- "setImmediate": false,
- "fs": "empty",
- },
- module: {
- rules: [
- { test: /\.html$|\.xml$/, use: "raw-loader" },
-
- // tns-core-modules reads the app.css and its imports using css-loader
- {
- test: /[\/|\\]app\.css$/,
- use: {
- loader: "css-loader",
- options: { minimize: false, url: false },
- }
- },
- {
- test: /[\/|\\]app\.scss$/,
- use: [
- { loader: "css-loader", options: { minimize: false, url: false } },
- "sass-loader"
- ]
- },
-
- // Angular components reference css files and their imports using raw-loader
- { test: /\.css$/, exclude: /[\/|\\]app\.css$/, use: "raw-loader" },
- { test: /\.scss$/, exclude: /[\/|\\]app\.scss$/, use: ["raw-loader", "resolve-url-loader", "sass-loader"] },
-
- // Compile TypeScript files with ahead-of-time compiler.
- { test: /.ts$/, use: [
- "nativescript-dev-webpack/moduleid-compat-loader",
- { loader: "@ngtools/webpack", options: ngToolsWebpackOptions },
- ]},
- ],
- },
- plugins: [
- // Vendor libs go to the vendor.js chunk
- new webpack.optimize.CommonsChunkPlugin({
- name: ["vendor"],
- }),
- // Define useful constants like TNS_WEBPACK
- new webpack.DefinePlugin({
- "global.TNS_WEBPACK": "true",
- }),
- // Copy assets to out dir. Add your own globs as needed.
- new CopyWebpackPlugin([
- { from: "App_Resources/**" },
- { from: "fonts/**" },
- { from: "**/*.jpg" },
- { from: "**/*.png" },
- { from: "**/*.xml" },
- ]),
- // Generate a bundle starter script and activate it in package.json
- new nsWebpack.GenerateBundleStarterPlugin([
- "./vendor",
- "./bundle",
- ]),
- // Support for web workers since v3.2
- new NativeScriptWorkerPlugin(),
- // AngularCompilerPlugin with augmented NativeScript filesystem to handle platform specific resource resolution.
- new nsWebpack.NativeScriptAngularCompilerPlugin(
- Object.assign({
- entryModule: resolve(__dirname, "app/app.module#AppModule"),
- skipCodeGeneration: !aot,
- platformOptions: {
- platform,
- platforms,
- // ignore: ["App_Resources"]
- },
- }, ngToolsWebpackOptions)
- ),
- // Does IPC communication with the {N} CLI to notify events when running in watch mode.
- new nsWebpack.WatchStateLoggerPlugin(),
- ],
- };
- if (report) {
- // Generate report files for bundles content
- config.plugins.push(new BundleAnalyzerPlugin({
- analyzerMode: "static",
- openAnalyzer: false,
- generateStatsFile: true,
- reportFilename: join(__dirname, "report", `report.html`),
- statsFilename: join(__dirname, "report", `stats.json`),
- }));
- }
- if (snapshot) {
- config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({
- chunk: "vendor",
- projectRoot: __dirname,
- webpackConfig: config,
- targetArchs: ["arm", "arm64", "ia32"],
- tnsJavaClassesOptions: { packages: ["tns-core-modules" ] },
- useLibs: false
- }));
- }
- if (uglify) {
- config.plugins.push(new webpack.LoaderOptionsPlugin({ minimize: true }));
-
- // Work around an Android issue by setting compress = false
- const compress = platform !== "android";
- config.plugins.push(new UglifyJsPlugin({
- uglifyOptions: {
- mangle: { reserved: nsWebpack.uglifyMangleExcludes },
- compress,
- }
- }));
- }
- return config;
-};
diff --git a/e2e/routable-animations/.gitignore b/e2e/routable-animations/.gitignore
new file mode 100644
index 000000000..4b67c8c63
--- /dev/null
+++ b/e2e/routable-animations/.gitignore
@@ -0,0 +1,19 @@
+**/.DS_Store
+
+hooks
+node_modules
+platforms
+
+app/**/*.js
+e2e/**/*.js
+e2e/**/*.map
+e2e/reports/
+test-results.xml
+
+# Webpack files
+tsconfig.esm.json
+webpack.config.js
+
+# Tests
+/**/mochawesome-report
+
diff --git a/e2e/routable-animations/.vscode/launch.json b/e2e/routable-animations/.vscode/launch.json
new file mode 100644
index 000000000..0a26473cc
--- /dev/null
+++ b/e2e/routable-animations/.vscode/launch.json
@@ -0,0 +1,60 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "node",
+ "request": "launch",
+ "name": "Mocha Tests",
+ "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
+ "args": [
+ "-u",
+ "tdd",
+ "--timeout",
+ "999999",
+ "--colors",
+ "--opts", "./e2e/config/mocha.opts",
+ "--runType", "sim.iPhone8",
+ "--attachToDebug",
+ "--port",
+ "8300"
+ ],
+ "internalConsoleOptions": "openOnSessionStart"
+ },
+ {
+ "name": "Launch on iOS",
+ "type": "nativescript",
+ "request": "launch",
+ "platform": "ios",
+ "appRoot": "${workspaceRoot}",
+ "sourceMaps": true,
+ "watch": true
+ },
+ {
+ "name": "Attach on iOS",
+ "type": "nativescript",
+ "request": "attach",
+ "platform": "ios",
+ "appRoot": "${workspaceRoot}",
+ "sourceMaps": true,
+ "watch": false
+ },
+ {
+ "name": "Launch on Android",
+ "type": "nativescript",
+ "request": "launch",
+ "platform": "android",
+ "appRoot": "${workspaceRoot}",
+ "sourceMaps": true,
+ "watch": true
+ },
+ {
+ "name": "Attach on Android",
+ "type": "nativescript",
+ "request": "attach",
+ "platform": "android",
+ "appRoot": "${workspaceRoot}",
+ "sourceMaps": true,
+ "watch": false
+ }
+ ]
+}
\ No newline at end of file
diff --git a/e2e/routable-animations/README.md b/e2e/routable-animations/README.md
new file mode 100644
index 000000000..a52a3d621
--- /dev/null
+++ b/e2e/routable-animations/README.md
@@ -0,0 +1,3 @@
+# NativeScript Routable Animations
+
+A NativeScript Angular applications showcasing [angular routable animations](https://www.yearofmoo.com/2017/06/new-wave-of-animation-features.html#routable-animations).
\ No newline at end of file
diff --git a/e2e/routable-animations/app/App_Resources/Android/app.gradle b/e2e/routable-animations/app/App_Resources/Android/app.gradle
new file mode 100644
index 000000000..c714809c9
--- /dev/null
+++ b/e2e/routable-animations/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.nsroanimations"
+
+ //override supported platforms
+ // ndk {
+ // abiFilters.clear()
+ // abiFilters "armeabi-v7a"
+ // }
+
+ }
+ aaptOptions {
+ additionalParameters "--no-version-vectors"
+ }
+}
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/AndroidManifest.xml b/e2e/routable-animations/app/App_Resources/Android/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..9db832151
--- /dev/null
+++ b/e2e/routable-animations/app/App_Resources/Android/src/main/AndroidManifest.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png
new file mode 100644
index 000000000..eb381c258
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png differ
diff --git a/ng-sample/app/icon.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png
old mode 100644
new mode 100755
similarity index 100%
rename from ng-sample/app/icon.png
rename to e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png
new file mode 100644
index 000000000..5218f4c90
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png differ
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png
new file mode 100644
index 000000000..748b2adf5
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png differ
diff --git a/ng-sample/app/App_Resources/Android/drawable-ldpi/icon.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png
similarity index 100%
rename from ng-sample/app/App_Resources/Android/drawable-ldpi/icon.png
rename to e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png
new file mode 100644
index 000000000..b9e102a76
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png differ
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png
new file mode 100644
index 000000000..efeaf2907
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png differ
diff --git a/tests/app/App_Resources/Android/drawable-mdpi/icon.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png
similarity index 100%
rename from tests/app/App_Resources/Android/drawable-mdpi/icon.png
rename to e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png
new file mode 100644
index 000000000..626338766
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png differ
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
new file mode 100644
index 000000000..ada77f92c
--- /dev/null
+++ b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
@@ -0,0 +1,8 @@
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png
new file mode 100644
index 000000000..612bbd072
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png differ
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png
new file mode 100644
index 000000000..f29188209
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png differ
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png
new file mode 100644
index 000000000..ad8ee2f4b
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png differ
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png
new file mode 100644
index 000000000..0fa88e235
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png differ
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png
new file mode 100644
index 000000000..4f69cb25b
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png differ
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png
new file mode 100644
index 000000000..668327832
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png differ
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png
new file mode 100644
index 000000000..c650f6438
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png differ
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png
new file mode 100644
index 000000000..50887a856
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png differ
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png
new file mode 100644
index 000000000..fa6331c8d
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png differ
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/values-v21/colors.xml b/e2e/routable-animations/app/App_Resources/Android/src/main/res/values-v21/colors.xml
new file mode 100644
index 000000000..a64641a9d
--- /dev/null
+++ b/e2e/routable-animations/app/App_Resources/Android/src/main/res/values-v21/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #3d5afe
+
\ No newline at end of file
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/values-v21/styles.xml b/e2e/routable-animations/app/App_Resources/Android/src/main/res/values-v21/styles.xml
new file mode 100644
index 000000000..dac8727c8
--- /dev/null
+++ b/e2e/routable-animations/app/App_Resources/Android/src/main/res/values-v21/styles.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/values/colors.xml b/e2e/routable-animations/app/App_Resources/Android/src/main/res/values/colors.xml
new file mode 100644
index 000000000..74ad8829c
--- /dev/null
+++ b/e2e/routable-animations/app/App_Resources/Android/src/main/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #F5F5F5
+ #757575
+ #33B5E5
+ #272734
+
\ No newline at end of file
diff --git a/e2e/routable-animations/app/App_Resources/Android/src/main/res/values/styles.xml b/e2e/routable-animations/app/App_Resources/Android/src/main/res/values/styles.xml
new file mode 100644
index 000000000..1e8c7f29b
--- /dev/null
+++ b/e2e/routable-animations/app/App_Resources/Android/src/main/res/values/styles.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..1953734f4
--- /dev/null
+++ b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/Contents.json b/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/Contents.json
new file mode 100644
index 000000000..da4a164c9
--- /dev/null
+++ b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json b/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 000000000..4414bad08
--- /dev/null
+++ b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png b/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
new file mode 100644
index 000000000..9f1f6ce3e
Binary files /dev/null and b/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json b/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
new file mode 100644
index 000000000..4f4e9c506
--- /dev/null
+++ b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json b/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
new file mode 100644
index 000000000..23c0ffd7a
--- /dev/null
+++ b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png differ
diff --git a/e2e/routable-animations/app/App_Resources/iOS/Info.plist b/e2e/routable-animations/app/App_Resources/iOS/Info.plist
new file mode 100644
index 000000000..ea3e3ea23
--- /dev/null
+++ b/e2e/routable-animations/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/routable-animations/app/App_Resources/iOS/LaunchScreen.storyboard b/e2e/routable-animations/app/App_Resources/iOS/LaunchScreen.storyboard
new file mode 100644
index 000000000..2ad9471e1
--- /dev/null
+++ b/e2e/routable-animations/app/App_Resources/iOS/LaunchScreen.storyboard
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/routable-animations/app/App_Resources/iOS/build.xcconfig b/e2e/routable-animations/app/App_Resources/iOS/build.xcconfig
new file mode 100644
index 000000000..4b0118490
--- /dev/null
+++ b/e2e/routable-animations/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/routable-animations/app/README.md b/e2e/routable-animations/app/README.md
new file mode 100644
index 000000000..ebe60c416
--- /dev/null
+++ b/e2e/routable-animations/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/routable-animations/app/app-routing.module.ts b/e2e/routable-animations/app/app-routing.module.ts
new file mode 100644
index 000000000..c1f60b84c
--- /dev/null
+++ b/e2e/routable-animations/app/app-routing.module.ts
@@ -0,0 +1,29 @@
+import { NgModule } from '@angular/core';
+import { Routes } from '@angular/router';
+import { NativeScriptRouterModule } from "@nativescript/angular/router";
+
+import { HomeComponent } from './home/home.component';
+import { SupportComponent } from './support/support.component';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: HomeComponent,
+ data: {
+ animation: 'homePage'
+ },
+ },
+ {
+ path: 'support',
+ component: SupportComponent,
+ data: {
+ animation: 'supportPage'
+ }
+ },
+];
+
+@NgModule({
+ imports: [NativeScriptRouterModule.forRoot(routes)],
+ exports: [NativeScriptRouterModule]
+})
+export class AppRoutingModule { }
diff --git a/e2e/routable-animations/app/app.component.ts b/e2e/routable-animations/app/app.component.ts
new file mode 100644
index 000000000..f3883127e
--- /dev/null
+++ b/e2e/routable-animations/app/app.component.ts
@@ -0,0 +1,51 @@
+import { Component } from "@angular/core";
+import {
+ animate,
+ animateChild,
+ group,
+ query,
+ style,
+ transition,
+ trigger,
+} from "@angular/animations";
+
+const slideLeft = [
+ query(':leave', style({ transform: 'translateX(0)' })),
+ query(':enter', style({ transform: 'translateX(-400)' })),
+
+ group([
+ query(':leave', animate(500, style({ transform: 'translateX(400)' }))),
+ query(':enter', animate(500, style({ transform: 'translateX(0)' }))),
+ ], { delay: 10 }) // Needed because a wierd animation scheduling bug in IOS
+]
+
+const slideRight = [
+ query(':leave', style({ transform: 'translateX(0)'})),
+ query(':enter', style({ transform: 'translateX(400)'})),
+
+ group([
+ query(':leave', animate(500, style({ transform: 'translateX(-400)' })), { delay: 100 }),
+ query(':enter', animate(500, style({ transform: 'translateX(0)' })), { delay: 100 }),
+ ], { delay: 10 }) // Needed because a wierd animation scheduling bug in IOS
+]
+
+@Component({
+ template: `
+
+
+
+ `,
+ animations: [
+ trigger('routeAnimation', [
+ transition('homePage => supportPage', slideRight),
+ transition('supportPage => homePage', slideLeft),
+ ])
+ ],
+})
+export class AppComponent {
+ prepRouteState(outlet: any) {
+ return outlet.activatedRouteData['animation'] || 'firstPage';
+ }
+}
+
+
diff --git a/e2e/routable-animations/app/app.css b/e2e/routable-animations/app/app.css
new file mode 100644
index 000000000..8aca5e29a
--- /dev/null
+++ b/e2e/routable-animations/app/app.css
@@ -0,0 +1 @@
+@import "~nativescript-theme-core/css/core.light.css";
diff --git a/e2e/routable-animations/app/app.module.ngfactory.d.ts b/e2e/routable-animations/app/app.module.ngfactory.d.ts
new file mode 100644
index 000000000..9d15b9419
--- /dev/null
+++ b/e2e/routable-animations/app/app.module.ngfactory.d.ts
@@ -0,0 +1 @@
+export const AppModuleNgFactory: any;
\ No newline at end of file
diff --git a/e2e/routable-animations/app/app.module.ts b/e2e/routable-animations/app/app.module.ts
new file mode 100644
index 000000000..a20ebebb1
--- /dev/null
+++ b/e2e/routable-animations/app/app.module.ts
@@ -0,0 +1,29 @@
+import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+import { NativeScriptModule } from "@nativescript/angular";
+import { NativeScriptAnimationsModule } from "@nativescript/angular/animations";
+
+import { AppComponent } from "./app.component";
+import { AppRoutingModule } from './app-routing.module';
+import { HomeComponent } from './home/home.component';
+import { SupportComponent } from './support/support.component';
+
+import { NativeScriptDebug } from "@nativescript/angular/trace";
+import { setCategories, enable } from "@nativescript/core/trace";
+setCategories(NativeScriptDebug.animationsTraceCategory);
+enable();
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ HomeComponent,
+ SupportComponent
+ ],
+ bootstrap: [AppComponent],
+ imports: [
+ NativeScriptModule,
+ NativeScriptAnimationsModule,
+ AppRoutingModule,
+ ],
+ schemas: [NO_ERRORS_SCHEMA],
+})
+export class AppModule { }
diff --git a/e2e/routable-animations/app/home/home.component.css b/e2e/routable-animations/app/home/home.component.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/e2e/routable-animations/app/home/home.component.html b/e2e/routable-animations/app/home/home.component.html
new file mode 100644
index 000000000..4adabcc24
--- /dev/null
+++ b/e2e/routable-animations/app/home/home.component.html
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/e2e/routable-animations/app/home/home.component.ts b/e2e/routable-animations/app/home/home.component.ts
new file mode 100644
index 000000000..f577257f0
--- /dev/null
+++ b/e2e/routable-animations/app/home/home.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ moduleId: module.id,
+ templateUrl: './home.component.html',
+ styleUrls: ['./home.component.css']
+})
+export class HomeComponent implements OnInit {
+
+ constructor() { }
+
+ ngOnInit() {
+ }
+
+}
diff --git a/e2e/routable-animations/app/main.ts b/e2e/routable-animations/app/main.ts
new file mode 100644
index 000000000..1b1b3c521
--- /dev/null
+++ b/e2e/routable-animations/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/routable-animations/app/package.json b/e2e/routable-animations/app/package.json
new file mode 100644
index 000000000..f156bfc8f
--- /dev/null
+++ b/e2e/routable-animations/app/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "nativescript-template-ng-tutorial",
+ "version": "3.1.0",
+ "main": "main.js",
+ "android": {
+ "v8Flags": "--expose_gc",
+ "markingMode": "none"
+ }
+}
\ No newline at end of file
diff --git a/e2e/routable-animations/app/support/support.component.css b/e2e/routable-animations/app/support/support.component.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/e2e/routable-animations/app/support/support.component.html b/e2e/routable-animations/app/support/support.component.html
new file mode 100644
index 000000000..e743bbd92
--- /dev/null
+++ b/e2e/routable-animations/app/support/support.component.html
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/e2e/routable-animations/app/support/support.component.ts b/e2e/routable-animations/app/support/support.component.ts
new file mode 100644
index 000000000..c31fbc51a
--- /dev/null
+++ b/e2e/routable-animations/app/support/support.component.ts
@@ -0,0 +1,17 @@
+import { Component, OnInit } from '@angular/core';
+import { RouterExtensions } from 'nativescript-angular/router';
+
+@Component({
+ moduleId: module.id,
+ templateUrl: './support.component.html',
+ styleUrls: ['./support.component.css']
+})
+export class SupportComponent implements OnInit {
+
+ constructor(public router: RouterExtensions) {
+ }
+
+ ngOnInit() {
+ }
+
+}
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api19-Default/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api19-Default/home.png
new file mode 100644
index 000000000..e52f73d80
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api19-Default/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api19-Default/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api19-Default/support.png
new file mode 100644
index 000000000..3046d7116
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api19-Default/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api22-Default/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api22-Default/home.png
new file mode 100644
index 000000000..6ff400d3d
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api22-Default/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api22-Default/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api22-Default/support.png
new file mode 100644
index 000000000..a55be3589
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api22-Default/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api22-Google/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api22-Google/home.png
new file mode 100644
index 000000000..8c8e081af
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api22-Google/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api22-Google/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api22-Google/support.png
new file mode 100644
index 000000000..ee833c142
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api22-Google/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api23-Default/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api23-Default/home.png
new file mode 100644
index 000000000..4f4da09ef
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api23-Default/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api23-Default/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api23-Default/support.png
new file mode 100644
index 000000000..e042e48b1
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api23-Default/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api24-Default/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api24-Default/home.png
new file mode 100644
index 000000000..7e55d9dde
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api24-Default/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api24-Default/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api24-Default/support.png
new file mode 100644
index 000000000..6272bebf8
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api24-Default/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api25-Google/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api25-Google/home.png
new file mode 100644
index 000000000..d54e5fc0e
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api25-Google/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api25-Google/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api25-Google/support.png
new file mode 100644
index 000000000..d583b6dcf
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api25-Google/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api26-Google/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api26-Google/home.png
new file mode 100644
index 000000000..40de7e9c1
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api26-Google/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api26-Google/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api26-Google/support.png
new file mode 100644
index 000000000..bad8bb6e4
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api26-Google/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api27-Google/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api27-Google/home.png
new file mode 100644
index 000000000..c52b30285
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api27-Google/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api27-Google/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api27-Google/support.png
new file mode 100644
index 000000000..e83f97176
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api27-Google/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api28-Google/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api28-Google/home.png
new file mode 100644
index 000000000..985b2bacd
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api28-Google/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api28-Google/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api28-Google/support.png
new file mode 100644
index 000000000..e83f97176
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api28-Google/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api29-Google/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api29-Google/home.png
new file mode 100644
index 000000000..198993618
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api29-Google/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api29-Google/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api29-Google/support.png
new file mode 100644
index 000000000..26238af12
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/Emulator-Api29-Google/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 100/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 100/home.png
new file mode 100644
index 000000000..e45e0e7e6
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 100/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 100/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 100/support.png
new file mode 100644
index 000000000..37ed5d73d
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 100/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 110/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 110/home.png
new file mode 100644
index 000000000..e45e0e7e6
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 110/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 110/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 110/support.png
new file mode 100644
index 000000000..1a8839513
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 110/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 12/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 12/home.png
new file mode 100644
index 000000000..81be31153
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 12/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 12/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 12/support.png
new file mode 100644
index 000000000..aaf591a50
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 7 12/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 8/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 8/home.png
new file mode 100644
index 000000000..e45e0e7e6
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 8/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 8/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 8/support.png
new file mode 100644
index 000000000..37ed5d73d
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone 8/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone X 110/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone X 110/home.png
new file mode 100644
index 000000000..49139d5a6
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone X 110/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone X 110/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone X 110/support.png
new file mode 100644
index 000000000..64dd2209f
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone X 110/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone XR 12/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone XR 12/home.png
new file mode 100644
index 000000000..f32265ad8
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone XR 12/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone XR 12/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone XR 12/support.png
new file mode 100644
index 000000000..43a8a2257
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone XR 12/support.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone XR 13/home.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone XR 13/home.png
new file mode 100644
index 000000000..d039c537e
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone XR 13/home.png differ
diff --git a/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone XR 13/support.png b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone XR 13/support.png
new file mode 100644
index 000000000..f21417d83
Binary files /dev/null and b/e2e/routable-animations/e2e/resources/images/nsroanimations/iPhone XR 13/support.png differ
diff --git a/e2e/routable-animations/e2e/setup.ts b/e2e/routable-animations/e2e/setup.ts
new file mode 100644
index 000000000..11b854fca
--- /dev/null
+++ b/e2e/routable-animations/e2e/setup.ts
@@ -0,0 +1,19 @@
+import { startServer, stopServer, ITestReporter, nsCapabilities, LogImageType } from "nativescript-dev-appium";
+const addContext = require('mochawesome/addContext');
+
+const testReporterContext = {};
+testReporterContext.name = "mochawesome";
+testReporterContext.reportDir = "mochawesome-report";
+testReporterContext.log = addContext;
+testReporterContext.logImageTypes = [LogImageType.screenshots];
+nsCapabilities.testReporter = testReporterContext;
+
+before("start server", async function () {
+ nsCapabilities.testReporter.context = this;
+ await startServer();
+});
+
+after("stop server", async function () {
+ nsCapabilities.testReporter.context = this;
+ await stopServer();
+});
diff --git a/e2e/routable-animations/e2e/tests.e2e.ts b/e2e/routable-animations/e2e/tests.e2e.ts
new file mode 100644
index 000000000..6f693b745
--- /dev/null
+++ b/e2e/routable-animations/e2e/tests.e2e.ts
@@ -0,0 +1,53 @@
+import { AppiumDriver, createDriver, SearchOptions, nsCapabilities } from "nativescript-dev-appium";
+import { assert } from "chai";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+
+describe("sample scenario", function () {
+ const defaultWaitTime = 5000;
+ let driver: AppiumDriver;
+
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ });
+
+ after(async function () {
+ if (isSauceLab) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ it("should go to support page", async function () {
+ const btnGoToSupportPage = await driver.findElementByAutomationText("go to support page");
+ const homeImage = await driver.compareScreen("home", 5, 20, ImageOptions.pixel);
+ assert.isTrue(homeImage);
+ await btnGoToSupportPage.click();
+ const titleSupportPage = await driver.findElementByAutomationText("Support Page");
+ console.log(await titleSupportPage.text());
+ });
+
+ it("should go back to home page", async function () {
+ const btnGoBackToHomePage = await driver.findElementByAutomationText("go back to home page");
+ const supportImage = await driver.compareScreen("support", 5, 20, ImageOptions.pixel);
+ assert.isTrue(supportImage);
+ await btnGoBackToHomePage.click();
+ const titleHomePage = await driver.findElementByAutomationText("Home Page");
+ console.log(await titleHomePage.text());
+ });
+});
diff --git a/e2e/routable-animations/e2e/tsconfig.json b/e2e/routable-animations/e2e/tsconfig.json
new file mode 100644
index 000000000..6517ca58d
--- /dev/null
+++ b/e2e/routable-animations/e2e/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es6",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "importHelpers": false,
+ "sourceMap": true,
+ "types": [
+ "node",
+ "mocha",
+ "chai"
+ ],
+ "lib": [
+ "es2015",
+ "dom"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/e2e/routable-animations/package.json b/e2e/routable-animations/package.json
new file mode 100644
index 000000000..fb02e7d67
--- /dev/null
+++ b/e2e/routable-animations/package.json
@@ -0,0 +1,51 @@
+{
+ "description": "NativeScript Application",
+ "license": "SEE LICENSE IN ",
+ "readme": "NativeScript Application",
+ "repository": "",
+ "nativescript": {
+ "id": "org.nativescript.nsroanimations"
+ },
+ "dependencies": {
+ "@angular/animations": "~9.1.0",
+ "@angular/common": "~9.1.0",
+ "@angular/compiler": "~9.1.0",
+ "@angular/core": "~9.1.0",
+ "@angular/forms": "~9.1.0",
+ "@angular/platform-browser": "~9.1.0",
+ "@angular/platform-browser-dynamic": "~9.1.0",
+ "@angular/router": "~9.1.0",
+ "@nativescript/angular": "file:../../dist/nativescript-angular-scoped.tgz",
+ "nativescript-theme-core": "~1.0.2",
+ "reflect-metadata": "~0.1.8",
+ "rxjs": "~6.5.5",
+ "@nativescript/core": "next",
+ "zone.js": "^0.10.3"
+ },
+ "devDependencies": {
+ "@ngtools/webpack": "~9.1.0",
+ "@angular/compiler-cli": "~9.1.0",
+ "@types/chai": "~4.1.3",
+ "@types/mocha": "~5.2.1",
+ "@types/node": "~10.12.18",
+ "babel-traverse": "6.25.0",
+ "babel-types": "6.25.0",
+ "babylon": "6.17.4",
+ "chai": "^4.2.0",
+ "colors": "^1.1.2",
+ "lazy": "1.0.11",
+ "mocha": "~5.2.0",
+ "mochawesome": "~3.1.2",
+ "nativescript-css-loader": "~0.26.0",
+ "nativescript-dev-appium": "^6.0.0",
+ "nativescript-dev-webpack": "next",
+ "typescript": "~3.8.3"
+ },
+ "scripts": {
+ "ns-bundle": "ns-bundle",
+ "e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
+ "e2e-watch": "tsc -p e2e --watch",
+ "ns-verify-bundle": "ns-verify-bundle",
+ "update-ns-webpack": "update-ns-webpack"
+ }
+}
diff --git a/e2e/routable-animations/tsconfig.esm.json b/e2e/routable-animations/tsconfig.esm.json
new file mode 100644
index 000000000..a96f6bbc6
--- /dev/null
+++ b/e2e/routable-animations/tsconfig.esm.json
@@ -0,0 +1,7 @@
+{
+ "extends": "./tsconfig",
+ "compilerOptions": {
+ "module": "ESNext",
+ "moduleResolution": "node"
+ }
+}
diff --git a/e2e/routable-animations/tsconfig.json b/e2e/routable-animations/tsconfig.json
new file mode 100644
index 000000000..a3ba8b51a
--- /dev/null
+++ b/e2e/routable-animations/tsconfig.json
@@ -0,0 +1,40 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es5",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "noEmitHelpers": true,
+ "noEmitOnError": true,
+ "skipLibCheck": true,
+ "lib": [
+ "es2017",
+ "dom",
+ "es6"
+ ],
+ "baseUrl": ".",
+ "paths": {
+ "~/*": [
+ "app/*"
+ ],
+ "*": [
+ "./node_modules/*"
+ ]
+ }
+ },
+ "include": [
+ "../../nativescript-angular-package",
+ "../../nativescript-angular",
+ "**/*"
+ ],
+ "exclude": [
+ "../../nativescript-angular-package/node_modules",
+ "../../nativescript-angular-package/**/*.d.ts",
+ "../../nativescript-angular/node_modules",
+ "../../nativescript-angular/**/*.d.ts",
+ "node_modules",
+ "platforms",
+ "**/*.aot",
+ "e2e"
+ ]
+}
\ No newline at end of file
diff --git a/e2e/router-tab-view/.gitignore b/e2e/router-tab-view/.gitignore
new file mode 100644
index 000000000..e2c82407c
--- /dev/null
+++ b/e2e/router-tab-view/.gitignore
@@ -0,0 +1,13 @@
+.vscode
+
+platforms
+node_modules
+hooks
+
+/**/*.js
+/**/*.map
+e2e/reports
+test-results.xml
+
+instrumentscli*.trace
+mochawesome-report
\ No newline at end of file
diff --git a/e2e/router-tab-view/app/App_Resources/Android/app.gradle b/e2e/router-tab-view/app/App_Resources/Android/app.gradle
new file mode 100644
index 000000000..095407c3b
--- /dev/null
+++ b/e2e/router-tab-view/app/App_Resources/Android/app.gradle
@@ -0,0 +1,16 @@
+// Add your native dependencies here:
+
+// Uncomment to add recyclerview-v7 dependency
+//dependencies {
+// compile 'com.android.support:recyclerview-v7:+'
+//}
+
+android {
+ defaultConfig {
+ generatedDensities = []
+ applicationId = "org.nativescript.routertabview"
+ }
+ aaptOptions {
+ additionalParameters "--no-version-vectors"
+ }
+}
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/AndroidManifest.xml b/e2e/router-tab-view/app/App_Resources/Android/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..9db832151
--- /dev/null
+++ b/e2e/router-tab-view/app/App_Resources/Android/src/main/AndroidManifest.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png
new file mode 100644
index 000000000..eb381c258
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png
new file mode 100755
index 000000000..9cde84cd5
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png
new file mode 100644
index 000000000..5218f4c90
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png
new file mode 100644
index 000000000..748b2adf5
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png
new file mode 100755
index 000000000..4d6a674b3
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png
new file mode 100644
index 000000000..b9e102a76
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png
new file mode 100644
index 000000000..efeaf2907
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png
new file mode 100755
index 000000000..92ccc85a6
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png
new file mode 100644
index 000000000..626338766
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
new file mode 100644
index 000000000..ada77f92c
--- /dev/null
+++ b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
@@ -0,0 +1,8 @@
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png
new file mode 100644
index 000000000..612bbd072
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png
new file mode 100644
index 000000000..8bcde6277
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png
new file mode 100644
index 000000000..ad8ee2f4b
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png
new file mode 100644
index 000000000..0fa88e235
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png
new file mode 100644
index 000000000..9d81c85dc
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png
new file mode 100644
index 000000000..668327832
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png
new file mode 100644
index 000000000..c650f6438
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png
new file mode 100644
index 000000000..9a34d0d43
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png
new file mode 100644
index 000000000..fa6331c8d
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/values-v21/colors.xml b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/values-v21/colors.xml
new file mode 100644
index 000000000..a64641a9d
--- /dev/null
+++ b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/values-v21/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #3d5afe
+
\ No newline at end of file
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/values-v21/styles.xml b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/values-v21/styles.xml
new file mode 100644
index 000000000..dac8727c8
--- /dev/null
+++ b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/values-v21/styles.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/values/colors.xml b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/values/colors.xml
new file mode 100644
index 000000000..74ad8829c
--- /dev/null
+++ b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #F5F5F5
+ #757575
+ #33B5E5
+ #272734
+
\ No newline at end of file
diff --git a/e2e/router-tab-view/app/App_Resources/Android/src/main/res/values/styles.xml b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/values/styles.xml
new file mode 100644
index 000000000..c793e6d4c
--- /dev/null
+++ b/e2e/router-tab-view/app/App_Resources/Android/src/main/res/values/styles.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..4034b76e6
--- /dev/null
+++ b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "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"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "icon-1024.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png
new file mode 100644
index 000000000..a1d7eb479
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png
new file mode 100644
index 000000000..bb9b9e86d
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png
new file mode 100644
index 000000000..ec609dcf3
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png
new file mode 100644
index 000000000..a97180800
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png
new file mode 100644
index 000000000..214800ee6
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
new file mode 100644
index 000000000..8554b88a8
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
new file mode 100644
index 000000000..a22626bae
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
new file mode 100644
index 000000000..a22626bae
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
new file mode 100644
index 000000000..492c9c8e8
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png
new file mode 100644
index 000000000..9208113cf
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
new file mode 100644
index 000000000..24415e5a3
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png
new file mode 100644
index 000000000..b3ef1bf0c
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/Contents.json b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/Contents.json
new file mode 100644
index 000000000..da4a164c9
--- /dev/null
+++ b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 000000000..11bfcf55c
--- /dev/null
+++ b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,176 @@
+{
+ "images" : [
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "2436h",
+ "filename" : "Default-1125h.png",
+ "minimum-system-version" : "11.0",
+ "orientation" : "portrait",
+ "scale" : "3x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "iphone",
+ "extent" : "full-screen",
+ "filename" : "Default-Landscape-X.png",
+ "minimum-system-version" : "11.0",
+ "subtype" : "2436h",
+ "scale" : "3x"
+ },
+ {
+ "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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png
new file mode 100644
index 000000000..2913f85d9
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
new file mode 100644
index 000000000..cd94a3ac2
Binary files /dev/null and b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
new file mode 100644
index 000000000..4f4e9c506
--- /dev/null
+++ b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json b/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
new file mode 100644
index 000000000..23c0ffd7a
--- /dev/null
+++ b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png differ
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/Info.plist b/e2e/router-tab-view/app/App_Resources/iOS/Info.plist
new file mode 100644
index 000000000..ea3e3ea23
--- /dev/null
+++ b/e2e/router-tab-view/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-tab-view/app/App_Resources/iOS/LaunchScreen.storyboard b/e2e/router-tab-view/app/App_Resources/iOS/LaunchScreen.storyboard
new file mode 100644
index 000000000..2ad9471e1
--- /dev/null
+++ b/e2e/router-tab-view/app/App_Resources/iOS/LaunchScreen.storyboard
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/router-tab-view/app/App_Resources/iOS/build.xcconfig b/e2e/router-tab-view/app/App_Resources/iOS/build.xcconfig
new file mode 100644
index 000000000..4b0118490
--- /dev/null
+++ b/e2e/router-tab-view/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-tab-view/app/app.component.html b/e2e/router-tab-view/app/app.component.html
new file mode 100644
index 000000000..8872891ae
--- /dev/null
+++ b/e2e/router-tab-view/app/app.component.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/router-tab-view/app/app.component.ts b/e2e/router-tab-view/app/app.component.ts
new file mode 100644
index 000000000..385bd9c12
--- /dev/null
+++ b/e2e/router-tab-view/app/app.component.ts
@@ -0,0 +1,25 @@
+import { Component, ViewChild } from "@angular/core";
+import { TabViewDirective } from "@nativescript/angular/directives";
+import { Router, NavigationEnd } from "@angular/router";
+import { NSLocationStrategy } from "@nativescript/angular";
+
+@Component({
+ selector: "ns-app",
+ templateUrl: "app.component.html",
+})
+
+export class AppComponent {
+ private isInitialNavigation = true;
+
+ @ViewChild(TabViewDirective, { static: false }) tabView: TabViewDirective;
+
+ constructor(router: Router, location: NSLocationStrategy) {
+ router.events.subscribe(e => {
+ if (e instanceof NavigationEnd) {
+ this.isInitialNavigation = false;
+ console.log("[ROUTER]: " + e.toString());
+ console.log(location.toString());
+ }
+ })
+ }
+}
diff --git a/e2e/router-tab-view/app/app.css b/e2e/router-tab-view/app/app.css
new file mode 100644
index 000000000..d23504c4e
--- /dev/null
+++ b/e2e/router-tab-view/app/app.css
@@ -0,0 +1,12 @@
+/*
+In NativeScript, the app.css file is where you place CSS rules that
+you would like to apply to your entire application. Check out
+http://docs.nativescript.org/ui/styling for a full list of the CSS
+selectors and properties you can use to style UI components.
+
+/*
+In many cases you may want to use the NativeScript core theme instead
+of writing your own CSS rules. For a full list of class names in the theme
+refer to http://docs.nativescript.org/ui/theme.
+*/
+@import '~nativescript-theme-core/css/core.light.css';
diff --git a/e2e/router-tab-view/app/app.module.ngfactory.d.ts b/e2e/router-tab-view/app/app.module.ngfactory.d.ts
new file mode 100644
index 000000000..793157de3
--- /dev/null
+++ b/e2e/router-tab-view/app/app.module.ngfactory.d.ts
@@ -0,0 +1,4 @@
+/**
+ * A dynamically generated module when compiled with AoT.
+ */
+export const AppModuleNgFactory: any;
\ No newline at end of file
diff --git a/e2e/router-tab-view/app/app.module.ts b/e2e/router-tab-view/app/app.module.ts
new file mode 100644
index 000000000..d6142a91a
--- /dev/null
+++ b/e2e/router-tab-view/app/app.module.ts
@@ -0,0 +1,43 @@
+import { NgModule, NO_ERRORS_SCHEMA, ErrorHandler } from "@angular/core";
+import { NativeScriptModule } from "@nativescript/angular";
+import { AppRoutingModule, COMPONENTS } from "./app.routing";
+import { AppComponent } from "./app.component";
+
+import { DataService } from "./data.service";
+
+import { enable as traceEnable } from "@nativescript/core/trace";
+
+// addCategories(routerTraceCategory);
+traceEnable();
+
+export class MyErrorHandler implements ErrorHandler {
+ handleError(error) {
+ console.log("### ErrorHandler Error: " + error.toString());
+ console.log("### ErrorHandler Stack: " + error.stack);
+ }
+}
+
+@NgModule({
+ bootstrap: [
+ AppComponent
+ ],
+ imports: [
+ NativeScriptModule,
+ AppRoutingModule
+ ],
+ declarations: [
+ AppComponent,
+ ...COMPONENTS
+ ],
+ providers: [
+ DataService,
+ { provide: ErrorHandler, useClass: MyErrorHandler }
+ ],
+ schemas: [
+ NO_ERRORS_SCHEMA
+ ]
+})
+/*
+Pass your application module to the bootstrapModule function located in main.ts to start your app
+*/
+export class AppModule { }
\ No newline at end of file
diff --git a/e2e/router-tab-view/app/app.routing.ts b/e2e/router-tab-view/app/app.routing.ts
new file mode 100644
index 000000000..82cab77a3
--- /dev/null
+++ b/e2e/router-tab-view/app/app.routing.ts
@@ -0,0 +1,26 @@
+import { NgModule } from "@angular/core";
+import { NativeScriptRouterModule } from "@nativescript/angular/router";
+import { Routes } from "@angular/router";
+
+import { PlayerComponent } from "./player/players.component";
+import { PlayerDetailComponent } from "./player/player-detail.component";
+import { TeamsComponent } from "./team/teams.component";
+import { TeamDetailComponent } from "./team/team-detail.component";
+
+export const COMPONENTS = [PlayerComponent, PlayerDetailComponent, TeamsComponent, TeamDetailComponent];
+
+const routes: Routes = [
+ { path: "", redirectTo: "/(playerTab:players//teamTab:teams)", pathMatch: "full" },
+
+ { path: "players", component: PlayerComponent, outlet: "playerTab" },
+ { path: "player/:id", component: PlayerDetailComponent, outlet: "playerTab" },
+
+ { path: "teams", component: TeamsComponent, outlet: "teamTab" },
+ { path: "team/:id", component: TeamDetailComponent, outlet: "teamTab" },
+];
+
+@NgModule({
+ imports: [NativeScriptRouterModule.forRoot(routes)],
+ exports: [NativeScriptRouterModule],
+})
+export class AppRoutingModule {}
diff --git a/e2e/router-tab-view/app/data.service.ts b/e2e/router-tab-view/app/data.service.ts
new file mode 100644
index 000000000..583a37013
--- /dev/null
+++ b/e2e/router-tab-view/app/data.service.ts
@@ -0,0 +1,37 @@
+import { Injectable } from "@angular/core";
+
+export interface DataItem {
+ id: number;
+ name: string;
+}
+
+@Injectable()
+export class DataService {
+ private players = new Array(
+ { id: 1, name: "Player One" },
+ { id: 2, name: "Player Two" },
+ { id: 3, name: "Player Three" },
+ );
+
+ private teams = new Array(
+ { id: 1, name: "Team One" },
+ { id: 2, name: "Team Two" },
+ { id: 3, name: "Team Three" },
+ );
+
+ getPlayers(): DataItem[] {
+ return this.players;
+ }
+
+ getPlayer(id: number): DataItem {
+ return this.players.filter(item => item.id === id)[0];
+ }
+
+ getTeams(): DataItem[] {
+ return this.teams;
+ }
+
+ getTeam(id: number): DataItem {
+ return this.teams.filter(item => item.id === id)[0];
+ }
+}
diff --git a/e2e/router-tab-view/app/main.ts b/e2e/router-tab-view/app/main.ts
new file mode 100644
index 000000000..f61a19110
--- /dev/null
+++ b/e2e/router-tab-view/app/main.ts
@@ -0,0 +1,10 @@
+// this import should be first in order to load some required settings (like globals and reflect-metadata)
+import { platformNativeScriptDynamic } from "@nativescript/angular/platform";
+
+import { AppModule } from "./app.module";
+
+// A traditional NativeScript application starts by initializing global objects, setting up global CSS rules, creating, and navigating to the main page.
+// Angular applications need to take care of their own initialization: modules, components, directives, routes, DI providers.
+// A NativeScript Angular app needs to make both paradigms work together, so we provide a wrapper platform object, platformNativeScriptDynamic,
+// that sets up a NativeScript application and can bootstrap the Angular framework.
+platformNativeScriptDynamic().bootstrapModule(AppModule);
diff --git a/e2e/router-tab-view/app/package.json b/e2e/router-tab-view/app/package.json
new file mode 100644
index 000000000..e6521c70c
--- /dev/null
+++ b/e2e/router-tab-view/app/package.json
@@ -0,0 +1,9 @@
+{
+ "android": {
+ "v8Flags": "--expose_gc",
+ "markingMode": "none"
+ },
+ "main": "main.js",
+ "name": "tns-template-hello-world-ng",
+ "version": "3.4.3"
+}
\ No newline at end of file
diff --git a/e2e/router-tab-view/app/player/player-detail.component.html b/e2e/router-tab-view/app/player/player-detail.component.html
new file mode 100644
index 000000000..25be11a6d
--- /dev/null
+++ b/e2e/router-tab-view/app/player/player-detail.component.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/e2e/router-tab-view/app/player/player-detail.component.ts b/e2e/router-tab-view/app/player/player-detail.component.ts
new file mode 100644
index 000000000..8b18da264
--- /dev/null
+++ b/e2e/router-tab-view/app/player/player-detail.component.ts
@@ -0,0 +1,31 @@
+import { Component, OnInit } from "@angular/core";
+import { ActivatedRoute } from "@angular/router";
+
+import { DataService, DataItem } from "../data.service";
+import { Subscription } from "rxjs";
+
+@Component({
+ selector: "ns-player-details",
+ moduleId: module.id,
+ templateUrl: "./player-detail.component.html",
+})
+export class PlayerDetailComponent implements OnInit {
+ item: DataItem;
+ subscription: Subscription;
+
+ constructor(
+ private data: DataService,
+ private route: ActivatedRoute
+ ) { }
+
+ ngOnInit(): void {
+ this.subscription = this.route.params.subscribe(params => {
+ const id = +params["id"];
+ this.item = this.data.getPlayer(id);
+ })
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+}
diff --git a/e2e/router-tab-view/app/player/players.component.html b/e2e/router-tab-view/app/player/players.component.html
new file mode 100644
index 000000000..532703892
--- /dev/null
+++ b/e2e/router-tab-view/app/player/players.component.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/router-tab-view/app/player/players.component.ts b/e2e/router-tab-view/app/player/players.component.ts
new file mode 100644
index 000000000..934d76a45
--- /dev/null
+++ b/e2e/router-tab-view/app/player/players.component.ts
@@ -0,0 +1,22 @@
+import { Component, OnInit } from "@angular/core";
+import { DataService, DataItem } from "../data.service";
+import { RouterExtensions } from "@nativescript/angular/router";
+
+@Component({
+ selector: "ns-players",
+ moduleId: module.id,
+ templateUrl: "./players.component.html",
+})
+export class PlayerComponent implements OnInit {
+ items: DataItem[];
+
+ constructor(private itemService: DataService, private router: RouterExtensions) { }
+
+ ngOnInit(): void {
+ this.items = this.itemService.getPlayers();
+ }
+
+ navigateAbs() {
+ this.router.navigateByUrl("/(playerTab:players//teamTab:team/3)")
+ }
+}
\ No newline at end of file
diff --git a/e2e/router-tab-view/app/team/team-detail.component.html b/e2e/router-tab-view/app/team/team-detail.component.html
new file mode 100644
index 000000000..40d2f72fd
--- /dev/null
+++ b/e2e/router-tab-view/app/team/team-detail.component.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/e2e/router-tab-view/app/team/team-detail.component.ts b/e2e/router-tab-view/app/team/team-detail.component.ts
new file mode 100644
index 000000000..1f055e296
--- /dev/null
+++ b/e2e/router-tab-view/app/team/team-detail.component.ts
@@ -0,0 +1,31 @@
+import { Component, OnInit } from "@angular/core";
+import { ActivatedRoute } from "@angular/router";
+
+import { DataService, DataItem } from "../data.service";
+import { Subscription } from "rxjs";
+
+@Component({
+ selector: "ns-team-details",
+ moduleId: module.id,
+ templateUrl: "./team-detail.component.html",
+})
+export class TeamDetailComponent implements OnInit {
+ item: DataItem;
+ subscription: Subscription;
+
+ constructor(
+ private data: DataService,
+ private route: ActivatedRoute
+ ) { }
+
+ ngOnInit(): void {
+ this.subscription = this.route.params.subscribe(params => {
+ const id = +params["id"];
+ this.item = this.data.getTeam(id);
+ })
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+}
diff --git a/e2e/router-tab-view/app/team/teams.component.html b/e2e/router-tab-view/app/team/teams.component.html
new file mode 100644
index 000000000..dea20ffc7
--- /dev/null
+++ b/e2e/router-tab-view/app/team/teams.component.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/e2e/router-tab-view/app/team/teams.component.ts b/e2e/router-tab-view/app/team/teams.component.ts
new file mode 100644
index 000000000..119d1d2fe
--- /dev/null
+++ b/e2e/router-tab-view/app/team/teams.component.ts
@@ -0,0 +1,17 @@
+import { Component, OnInit } from "@angular/core";
+import { DataService, DataItem } from "../data.service";
+
+@Component({
+ selector: "ns-teams",
+ moduleId: module.id,
+ templateUrl: "./teams.component.html",
+})
+export class TeamsComponent implements OnInit {
+ items: DataItem[];
+
+ constructor(private itemService: DataService) { }
+
+ ngOnInit(): void {
+ this.items = this.itemService.getTeams();
+ }
+}
\ No newline at end of file
diff --git a/e2e/router-tab-view/e2e/setup.ts b/e2e/router-tab-view/e2e/setup.ts
new file mode 100644
index 000000000..cd73140bb
--- /dev/null
+++ b/e2e/router-tab-view/e2e/setup.ts
@@ -0,0 +1,18 @@
+import { startServer, stopServer, ITestReporter, nsCapabilities, LogImageType } from "nativescript-dev-appium";
+const addContext = require('mochawesome/addContext');
+
+const testReporterContext = {};
+testReporterContext.name = "mochawesome";
+testReporterContext.reportDir = "mochawesome-report";
+testReporterContext.log = addContext;
+testReporterContext.logImageTypes = [LogImageType.screenshots];
+nsCapabilities.testReporter = testReporterContext;
+
+before("start server", async function () {
+ nsCapabilities.testReporter.context = this;
+ await startServer();
+});
+
+after("stop server", async function () {
+ await stopServer();
+});
diff --git a/e2e/router-tab-view/e2e/tab-view-navigation.e2e-spec.ts b/e2e/router-tab-view/e2e/tab-view-navigation.e2e-spec.ts
new file mode 100644
index 000000000..7b316de75
--- /dev/null
+++ b/e2e/router-tab-view/e2e/tab-view-navigation.e2e-spec.ts
@@ -0,0 +1,215 @@
+import { AppiumDriver, createDriver, SearchOptions, nsCapabilities } from "nativescript-dev-appium";
+import { assert } from "chai";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+
+describe("TabView with page-router-outlet in each tab", async function () {
+ let driver: AppiumDriver;
+
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ });
+
+ after(async function () {
+ if (isSauceLab) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ it("should find any tabs by text", async function () {
+ await driver.findElementByAutomationText("Players");
+ await driver.findElementByAutomationText("Teams");
+ await driver.findElementByAutomationText("Player List");
+ });
+
+ it("should be able to switch between tabs", async function () {
+ await driver.findElementByAutomationText("Player List");
+
+ await selectTeamTab(driver);
+
+ await selectPlayerTab(driver);
+ });
+
+ it("should go forward and go back on first(player) tab", async function () {
+ await driver.findElementByAutomationText("Player List");
+ let player = await driver.findElementByAutomationText("Player One");
+ await player.click();
+ await driver.wait(1000);
+ player = await driver.findElementByAutomationText("Player One");
+ if (player) {
+ await player.click(); // First click does not open the details page
+ await driver.wait(2000); // Even its clicked twice it takes time to open the view
+ }
+ await driver.findElementByAutomationText("Player Details");
+ await driver.findElementByAutomationText("1");
+ await driver.findElementByAutomationText("Player One");
+
+ await driver.navBack();
+ await driver.findElementByAutomationText("Player List");
+ });
+
+ it("should go forward and go back on second(team) tab", async function () {
+ await driver.findElementByAutomationText("Player List");
+
+ await selectTeamTab(driver);
+
+ await navigateToTeamItem(driver, "Team Two", "2");
+
+ await driver.navBack();
+ await driver.findElementByAutomationText("Team List");
+
+ await selectPlayerTab(driver);
+ });
+
+ it("should navigate first(player) tab, second(team) tab and back in the same order ", async function () {
+ await driver.findElementByAutomationText("Player List");
+
+ // Go forward in player tab
+ await navigateToPlayerItem(driver, "Player Three", "3");
+
+ // Go forward in team tab
+ await selectTeamTab(driver);
+ await navigateToTeamItem(driver, "Team One", "1");
+
+ // Check both tabs
+ await selectPlayerTab(driver, false);
+ await selectTeamTab(driver, false);
+
+ // Go back in team tab
+ await driver.navBack();
+ await driver.findElementByAutomationText("Team List");
+
+ // Go back in player tab
+ await selectPlayerTab(driver, false);
+ await driver.navBack();
+ await driver.findElementByAutomationText("Player List");
+ });
+
+ it("should navigate second(team) tab, first(player) and back in the same order ", async function () {
+ await driver.findElementByAutomationText("Player List");
+
+ // Go forward in team tab
+ await selectTeamTab(driver);
+ await navigateToTeamItem(driver, "Team One", "1");
+
+ // Go forward in player tab
+ await selectPlayerTab(driver);
+ await navigateToPlayerItem(driver, "Player Three", "3");
+
+ // Check both tabs
+ await selectTeamTab(driver, false);
+ await selectPlayerTab(driver, false);
+
+ // Go back in player tab
+ await driver.navBack();
+ await driver.findElementByAutomationText("Player List");
+
+ // Go back in team tab
+ await selectTeamTab(driver, false);
+ await driver.navBack();
+ await driver.findElementByAutomationText("Team List");
+
+ await selectPlayerTab(driver);
+ });
+
+ it("should navigate first(player) tab, second(team) tab and back in reverse order ", async function () {
+ await driver.findElementByAutomationText("Player List");
+
+ // Go forward in player tab
+ await navigateToPlayerItem(driver, "Player Three", "3");
+
+ // Go forward in team tab
+ await selectTeamTab(driver);
+ await navigateToTeamItem(driver, "Team One", "1");
+
+ // Go back in player tab
+ await selectPlayerTab(driver, false);
+ await driver.navBack();
+ await driver.findElementByAutomationText("Player List");
+
+ // Go back in player tab
+ await selectTeamTab(driver, false);
+ await driver.findElementByAutomationText("1");
+ await driver.findElementByAutomationText("Team One");
+
+ await driver.navBack();
+ await driver.findElementByAutomationText("Team List");
+
+ await selectPlayerTab(driver);
+ });
+
+ it("should navigate second(team) tab, first(player) tab and back in reverse order ", async function () {
+ await driver.findElementByAutomationText("Player List");
+
+ // Go forward in team tab
+ await selectTeamTab(driver);
+ await navigateToTeamItem(driver, "Team One", "1");
+
+ // Go forward in player tab
+ await selectPlayerTab(driver);
+ await navigateToPlayerItem(driver, "Player Three", "3");
+
+ // Go back in team tab
+ await selectTeamTab(driver, false);
+ await driver.navBack();
+ await driver.findElementByAutomationText("Team List");
+
+ // Go back in player tab
+ await selectPlayerTab(driver, false);
+ await driver.findElementByAutomationText("3");
+ await driver.findElementByAutomationText("Player Three");
+
+ await driver.navBack();
+ await driver.findElementByAutomationText("Player List");
+ });
+
+});
+
+async function navigateToTeamItem(driver: AppiumDriver, name: string, id: string) {
+ const team = await driver.findElementByAutomationText(name);
+ await team.click();
+ await driver.findElementByAutomationText("Team Details");
+ await driver.findElementByAutomationText(id);
+ await driver.findElementByAutomationText(name);
+}
+
+async function navigateToPlayerItem(driver: AppiumDriver, name: string, id: string) {
+ let player = await driver.findElementByAutomationText(name);
+ await player.click();
+
+ await driver.findElementByAutomationText("Player Details");
+ await driver.findElementByAutomationText(id);
+ await driver.findElementByAutomationText(name);
+}
+
+async function selectTeamTab(driver: AppiumDriver, expectList = true) {
+ const teamsTab = await driver.findElementByAutomationText("Teams");
+ await teamsTab.click();
+
+ const expectedTitle = expectList ? "Team List" : "Team Details";
+ await driver.findElementByAutomationText(expectedTitle);
+}
+
+async function selectPlayerTab(driver: AppiumDriver, expectList = true) {
+ const playerTab = await driver.findElementByAutomationText("Players");
+ await playerTab.click();
+
+ const expectedTitle = expectList ? "Player List" : "Player Details";
+ await driver.findElementByAutomationText(expectedTitle);
+}
diff --git a/e2e/router-tab-view/e2e/tsconfig.json b/e2e/router-tab-view/e2e/tsconfig.json
new file mode 100644
index 000000000..c297b2347
--- /dev/null
+++ b/e2e/router-tab-view/e2e/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es6",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "importHelpers": false,
+ "types": [
+ "node",
+ "mocha",
+ "chai"
+ ],
+ "lib": [
+ "es2015",
+ "dom"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/e2e/router-tab-view/package.json b/e2e/router-tab-view/package.json
new file mode 100644
index 000000000..b2c52772b
--- /dev/null
+++ b/e2e/router-tab-view/package.json
@@ -0,0 +1,45 @@
+{
+ "description": "NativeScript Application",
+ "license": "SEE LICENSE IN ",
+ "readme": "NativeScript Application",
+ "repository": "",
+ "nativescript": {
+ "id": "org.nativescript.routertabview"
+ },
+ "dependencies": {
+ "@angular/animations": "~9.1.0",
+ "@angular/common": "~9.1.0",
+ "@angular/compiler": "~9.1.0",
+ "@angular/core": "~9.1.0",
+ "@angular/forms": "~9.1.0",
+ "@angular/platform-browser": "~9.1.0",
+ "@angular/platform-browser-dynamic": "~9.1.0",
+ "@angular/router": "~9.1.0",
+ "@nativescript/angular": "file:../../dist/nativescript-angular-scoped.tgz",
+ "nativescript-theme-core": "~1.0.4",
+ "reflect-metadata": "~0.1.8",
+ "rxjs": "~6.5.5",
+ "@nativescript/core": "next",
+ "zone.js": "^0.10.3"
+ },
+ "devDependencies": {
+ "@types/chai": "~4.1.7",
+ "@types/mocha": "~5.2.5",
+ "@types/node": "~10.12.18",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0",
+ "babylon": "6.18.0",
+ "lazy": "1.0.11",
+ "mocha": "~5.2.0",
+ "mochawesome": "~3.1.2",
+ "nativescript-dev-appium": "^6.0.0",
+ "nativescript-dev-webpack": "next",
+ "typescript": "~3.8.3",
+ "@angular/compiler-cli": "~9.1.0",
+ "@ngtools/webpack": "~9.1.0"
+ },
+ "scripts": {
+ "e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
+ "compile-tests": "tsc -p e2e --watch"
+ }
+}
diff --git a/e2e/router-tab-view/tsconfig.json b/e2e/router-tab-view/tsconfig.json
new file mode 100644
index 000000000..a3ba8b51a
--- /dev/null
+++ b/e2e/router-tab-view/tsconfig.json
@@ -0,0 +1,40 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es5",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "noEmitHelpers": true,
+ "noEmitOnError": true,
+ "skipLibCheck": true,
+ "lib": [
+ "es2017",
+ "dom",
+ "es6"
+ ],
+ "baseUrl": ".",
+ "paths": {
+ "~/*": [
+ "app/*"
+ ],
+ "*": [
+ "./node_modules/*"
+ ]
+ }
+ },
+ "include": [
+ "../../nativescript-angular-package",
+ "../../nativescript-angular",
+ "**/*"
+ ],
+ "exclude": [
+ "../../nativescript-angular-package/node_modules",
+ "../../nativescript-angular-package/**/*.d.ts",
+ "../../nativescript-angular/node_modules",
+ "../../nativescript-angular/**/*.d.ts",
+ "node_modules",
+ "platforms",
+ "**/*.aot",
+ "e2e"
+ ]
+}
\ No newline at end of file
diff --git a/e2e/router/.gitignore b/e2e/router/.gitignore
index 8e824499e..e3eb2283a 100644
--- a/e2e/router/.gitignore
+++ b/e2e/router/.gitignore
@@ -7,4 +7,5 @@ hooks
/**/*.js
/**/*.map
e2e/reports
-test-results.xml
\ No newline at end of file
+test-results.xml
+mochawesome-report
\ No newline at end of file
diff --git a/e2e/router/app/App_Resources/Android/src/main/AndroidManifest.xml b/e2e/router/app/App_Resources/Android/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..9db832151
--- /dev/null
+++ b/e2e/router/app/App_Resources/Android/src/main/AndroidManifest.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/router/app/App_Resources/Android/src/main/res/drawable-nodpi/background.png b/e2e/router/app/App_Resources/Android/src/main/res/drawable-nodpi/background.png
new file mode 100644
index 000000000..748b2adf5
Binary files /dev/null and b/e2e/router/app/App_Resources/Android/src/main/res/drawable-nodpi/background.png differ
diff --git a/tests/app/App_Resources/Android/drawable-ldpi/icon.png b/e2e/router/app/App_Resources/Android/src/main/res/drawable-nodpi/icon.png
similarity index 100%
rename from tests/app/App_Resources/Android/drawable-ldpi/icon.png
rename to e2e/router/app/App_Resources/Android/src/main/res/drawable-nodpi/icon.png
diff --git a/e2e/router/app/App_Resources/Android/src/main/res/drawable-nodpi/logo.png b/e2e/router/app/App_Resources/Android/src/main/res/drawable-nodpi/logo.png
new file mode 100644
index 000000000..b9e102a76
Binary files /dev/null and b/e2e/router/app/App_Resources/Android/src/main/res/drawable-nodpi/logo.png differ
diff --git a/e2e/router/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml b/e2e/router/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
new file mode 100644
index 000000000..ada77f92c
--- /dev/null
+++ b/e2e/router/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
@@ -0,0 +1,8 @@
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/e2e/router/app/App_Resources/Android/src/main/res/values-v21/colors.xml b/e2e/router/app/App_Resources/Android/src/main/res/values-v21/colors.xml
new file mode 100644
index 000000000..a64641a9d
--- /dev/null
+++ b/e2e/router/app/App_Resources/Android/src/main/res/values-v21/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #3d5afe
+
\ No newline at end of file
diff --git a/e2e/router/app/App_Resources/Android/src/main/res/values-v21/styles.xml b/e2e/router/app/App_Resources/Android/src/main/res/values-v21/styles.xml
new file mode 100644
index 000000000..dac8727c8
--- /dev/null
+++ b/e2e/router/app/App_Resources/Android/src/main/res/values-v21/styles.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/router/app/App_Resources/Android/src/main/res/values/colors.xml b/e2e/router/app/App_Resources/Android/src/main/res/values/colors.xml
new file mode 100644
index 000000000..74ad8829c
--- /dev/null
+++ b/e2e/router/app/App_Resources/Android/src/main/res/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/src/main/res/values/styles.xml b/e2e/router/app/App_Resources/Android/src/main/res/values/styles.xml
new file mode 100644
index 000000000..1e8c7f29b
--- /dev/null
+++ b/e2e/router/app/App_Resources/Android/src/main/res/values/styles.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/router/app/app-routing.module.ts b/e2e/router/app/app-routing.module.ts
index dcc881a54..302f562db 100644
--- a/e2e/router/app/app-routing.module.ts
+++ b/e2e/router/app/app-routing.module.ts
@@ -1,5 +1,5 @@
import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
-import { NativeScriptRouterModule } from "nativescript-angular/router";
+import { NativeScriptRouterModule, NSEmptyOutletComponent } from "@nativescript/angular";
import { FirstComponent } from "./first/first.component"
import { SecondComponent } from "./second/second.component"
@@ -17,10 +17,17 @@ export const routes = [
component: FirstComponent,
},
{
- path: "second/:depth", component: SecondComponent,
+ path: "second/:depth",
+ component: SecondComponent,
children: [
{ path: "", component: MasterComponent },
- { path: "detail/:id", component: DetailComponent }
+ { path: "detail/:id", component: DetailComponent },
+ {
+ path: "lazy-named",
+ outlet: "lazyNameOutlet",
+ component: NSEmptyOutletComponent,
+ loadChildren: () => import('./lazy-named/lazy-named.module').then(m => m.LazyNamedModule),
+ }
]
},
{
@@ -38,19 +45,13 @@ export const routes = [
},
{
path: "lazy",
- loadChildren: "./lazy/lazy.module#LazyModule",
+ component: NSEmptyOutletComponent,
+ loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule),
}
];
-export const navigatableComponents = [
- FirstComponent,
- SecondComponent,
- MasterComponent,
- DetailComponent
-];
-
@NgModule({
- imports: [NativeScriptRouterModule.forRoot(routes)],
+ imports: [NativeScriptRouterModule, NativeScriptRouterModule.forRoot(routes)],
exports: [NativeScriptRouterModule],
})
export class AppRoutingModule { }
diff --git a/e2e/router/app/app.css b/e2e/router/app/app.css
index 3890d9f3a..dc9ceffbf 100644
--- a/e2e/router/app/app.css
+++ b/e2e/router/app/app.css
@@ -4,7 +4,7 @@
}
.nested-header {
- font-size: 26;
+ font-size: 16;
}
.nested-outlet {
@@ -14,4 +14,9 @@
Label {
text-align: center;
+ font-size: 12;
+}
+
+Button {
+ font-size: 12;
}
\ No newline at end of file
diff --git a/e2e/router/app/app.module.ts b/e2e/router/app/app.module.ts
index 5d7dcbdbc..2df018787 100644
--- a/e2e/router/app/app.module.ts
+++ b/e2e/router/app/app.module.ts
@@ -1,32 +1,33 @@
-import { NgModule, NgModuleFactoryLoader, NO_ERRORS_SCHEMA } from "@angular/core";
-import { NativeScriptModule } from "nativescript-angular/nativescript.module";
-import { NSModuleFactoryLoader } from "nativescript-angular/router";
-
-import "./rxjs-operators";
+import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+import { NativeScriptModule, NativeScriptAnimationsModule } from "@nativescript/angular";
import {
AppRoutingModule,
- navigatableComponents,
} from "./app-routing.module";
+import { FirstComponent } from "./first/first.component"
+import { SecondComponent } from "./second/second.component"
+import { MasterComponent } from "./second/master.component"
+import { DetailComponent } from "./second/detail.component"
import { AppComponent } from "./app.component";
-import { rendererTraceCategory, viewUtilCategory, routeReuseStrategyTraceCategory, routerTraceCategory } from "nativescript-angular/trace";
-import { setCategories, enable } from "trace";
+import { rendererTraceCategory, viewUtilCategory, routeReuseStrategyTraceCategory, routerTraceCategory } from "@nativescript/angular/trace";
+import { setCategories, enable } from "@nativescript/core/trace";
setCategories(routerTraceCategory + "," + routeReuseStrategyTraceCategory);
enable();
@NgModule({
declarations: [
AppComponent,
- ...navigatableComponents,
+ FirstComponent,
+ SecondComponent,
+ MasterComponent,
+ DetailComponent,
],
bootstrap: [AppComponent],
- providers: [
- { provide: NgModuleFactoryLoader, useClass: NSModuleFactoryLoader }
- ],
imports: [
NativeScriptModule,
+ // NativeScriptAnimationsModule,
AppRoutingModule,
],
schemas: [NO_ERRORS_SCHEMA],
diff --git a/e2e/router/app/counter.service.ts b/e2e/router/app/counter.service.ts
new file mode 100644
index 000000000..15fe455c1
--- /dev/null
+++ b/e2e/router/app/counter.service.ts
@@ -0,0 +1,13 @@
+import { Injectable } from "@angular/core";
+import { BehaviorSubject } from "rxjs";
+
+@Injectable({
+ providedIn: "root"
+})
+export class CounterService {
+ counter$ = new BehaviorSubject(0);
+
+ tick() {
+ this.counter$.next(this.counter$.value + 1);
+ }
+}
\ No newline at end of file
diff --git a/e2e/router/app/first/first.component.ts b/e2e/router/app/first/first.component.ts
index 5748e7b2b..a792abc27 100644
--- a/e2e/router/app/first/first.component.ts
+++ b/e2e/router/app/first/first.component.ts
@@ -1,10 +1,10 @@
-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 { Component, OnInit, OnDestroy, OnChanges, DoCheck } from "@angular/core";
+import { RouterExtensions } from "@nativescript/angular";
+import { Page } from "@nativescript/core";
-import { Page } from "ui/page";
-import { Observable } from "rxjs/Observable";
+import { CounterService } from "../counter.service";
+import { ActivatedRoute } from "@angular/router";
+import { Subscription } from "rxjs";
@Component({
selector: "first",
@@ -12,31 +12,59 @@ import { Observable } from "rxjs/Observable";
-
-
+
+
-
-
-
+
+
-
+
+
+
+
`
})
-export class FirstComponent implements OnInit, OnDestroy {
+export class FirstComponent implements OnInit, OnDestroy, DoCheck {
public message: string = "";
- constructor(private routerExt: RouterExtensions, page: Page) {
+ public doCheckCount: number = 0;
+ sub: Subscription;
+
+ constructor(
+ private routerExt: RouterExtensions,
+ private route: ActivatedRoute,
+ public service: CounterService,
+ page: Page) {
+
console.log("FirstComponent - constructor() page: " + page);
}
+ tapMe() {
+ console.log('here!!!')
+ }
+
ngOnInit() {
console.log("FirstComponent - ngOnInit()");
+ this.sub = this.route.queryParams.subscribe((params) =>{
+ console.log("FIRST PARAMS:");
+ console.log(params);
+ });
}
ngOnDestroy() {
+ this.sub.unsubscribe();
console.log("FirstComponent - ngOnDestroy()");
}
+ ngDoCheck() {
+ this.doCheckCount++;
+ console.log("FirstComponent - ngDoCheck(): " + this.doCheckCount);
+ }
+
+ reset() {
+ this.doCheckCount = 0;
+ }
+
goBack() {
this.message = "";
if (this.routerExt.canGoBack()) {
diff --git a/e2e/router/app/lazy-named/lazy-named.module.ts b/e2e/router/app/lazy-named/lazy-named.module.ts
new file mode 100644
index 000000000..f0016b05c
--- /dev/null
+++ b/e2e/router/app/lazy-named/lazy-named.module.ts
@@ -0,0 +1,31 @@
+import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+import { Route } from "@angular/router";
+
+import { NativeScriptCommonModule, NativeScriptRouterModule } from "@nativescript/angular";
+
+import { NestedMasterComponent } from "./nested-master.component"
+import { NestedDetailComponent } from "./nested-detail.component"
+
+const routes: Route[] = [
+ { path: "", component: NestedMasterComponent },
+ { path: "detail/:id", component: NestedDetailComponent }
+];
+
+@NgModule({
+ schemas: [NO_ERRORS_SCHEMA],
+ imports: [
+ NativeScriptCommonModule,
+ NativeScriptRouterModule,
+ NativeScriptRouterModule.forChild(routes)
+ ],
+ declarations: [
+ NestedMasterComponent,
+ NestedDetailComponent
+ ],
+ exports:[
+ NativeScriptRouterModule,
+ NestedMasterComponent,
+ NestedDetailComponent
+ ]
+})
+export class LazyNamedModule { }
\ No newline at end of file
diff --git a/e2e/router/app/lazy-named/nested-detail.component.ts b/e2e/router/app/lazy-named/nested-detail.component.ts
new file mode 100644
index 000000000..705351aa6
--- /dev/null
+++ b/e2e/router/app/lazy-named/nested-detail.component.ts
@@ -0,0 +1,38 @@
+import { Component, OnInit, OnDestroy } from "@angular/core";
+import { ActivatedRoute, Router, Route } from "@angular/router";
+import { Location } from "@angular/common";
+import { Observable } from "rxjs";
+import { map } from "rxjs/operators";
+import { Page } from "@nativescript/core";
+import { RouterExtensions } from "@nativescript/angular";
+
+@Component({
+ selector: "nested-detail",
+ template: `
+
+
+
+
+ `
+})
+export class NestedDetailComponent {
+ public id$: Observable;
+
+ constructor(private router: Router, private route: ActivatedRoute, private page: Page, private routerExt: RouterExtensions) {
+ this.page.actionBar.title = "NamedNestedDetail";
+ console.log("DetailComponent - constructor()");
+ this.id$ = route.params.pipe(map(r => r["id"]));
+ }
+
+ ngOnInit() {
+ console.log("DetailComponent - ngOnInit()");
+ }
+
+ ngOnDestroy() {
+ console.log("DetailComponent - ngOnDestroy()");
+ }
+
+ goBack(){
+ this.routerExt.back();
+ }
+}
diff --git a/e2e/router/app/lazy-named/nested-master.component.ts b/e2e/router/app/lazy-named/nested-master.component.ts
new file mode 100644
index 000000000..f1d2c57bb
--- /dev/null
+++ b/e2e/router/app/lazy-named/nested-master.component.ts
@@ -0,0 +1,28 @@
+import { Component, OnInit, OnDestroy } from "@angular/core";
+import { Page } from "@nativescript/core";
+@Component({
+ selector: "nested-master",
+ template: `
+
+
+
+
+
+ `
+})
+export class NestedMasterComponent implements OnInit, OnDestroy {
+ public details: Array = [1, 2, 3];
+
+ constructor(private page: Page) {
+ this.page.actionBar.title = "NamedNestedMaster";
+ 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/lazy/lazy-componentless-route.component.ts b/e2e/router/app/lazy/lazy-componentless-route.component.ts
index a3df10fd5..a3ba7abce 100644
--- a/e2e/router/app/lazy/lazy-componentless-route.component.ts
+++ b/e2e/router/app/lazy/lazy-componentless-route.component.ts
@@ -1,13 +1,12 @@
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 { RouterExtensions } from "@nativescript/angular";
-import { Page } from "ui/page";
-import { Observable } from "rxjs/Observable";
+import { Page } from "@nativescript/core/ui/page";
@Component({
- selector: "lazy",
+ selector: "lazy-cmp-less-route",
template: `
diff --git a/e2e/router/app/lazy/lazy.component.ts b/e2e/router/app/lazy/lazy.component.ts
index 94e3f72b6..5e41be109 100644
--- a/e2e/router/app/lazy/lazy.component.ts
+++ b/e2e/router/app/lazy/lazy.component.ts
@@ -1,10 +1,9 @@
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 { RouterExtensions } from "@nativescript/angular";
-import { Page } from "ui/page";
-import { Observable } from "rxjs/Observable";
+import { Page } from "@nativescript/core";
@Component({
selector: "lazy",
diff --git a/e2e/router/app/lazy/lazy.module.ts b/e2e/router/app/lazy/lazy.module.ts
index 311bdbfe4..b136ea276 100644
--- a/e2e/router/app/lazy/lazy.module.ts
+++ b/e2e/router/app/lazy/lazy.module.ts
@@ -1,8 +1,7 @@
import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
import { Route } from "@angular/router";
-import { NativeScriptCommonModule } from "nativescript-angular/common";
-import { NativeScriptRouterModule } from "nativescript-angular/router";
+import { NativeScriptCommonModule, NativeScriptRouterModule } from "@nativescript/angular";
import { LazyComponent } from "./lazy.component";
import { LazyComponentlessRouteComponent } from "./lazy-componentless-route.component";
@@ -33,6 +32,9 @@ const routes: Route[] = [
declarations: [
LazyComponent,
LazyComponentlessRouteComponent
+ ],
+ exports: [
+ NativeScriptRouterModule
]
})
export class LazyModule { }
\ No newline at end of file
diff --git a/e2e/router/app/main.aot.ts b/e2e/router/app/main.aot.ts
deleted file mode 100644
index 98bf134fc..000000000
--- a/e2e/router/app/main.aot.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-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
index 639bfd513..1b1b3c521 100644
--- a/e2e/router/app/main.ts
+++ b/e2e/router/app/main.ts
@@ -1,4 +1,4 @@
-import { platformNativeScriptDynamic } from "nativescript-angular/platform";
+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
index d5356c0cc..706d1a31b 100644
--- a/e2e/router/app/package.json
+++ b/e2e/router/app/package.json
@@ -1,5 +1,9 @@
{
"main": "main.js",
"name": "nativescript-template-ng-tutorial",
- "version": "3.1.0"
+ "version": "3.1.0",
+ "android": {
+ "v8Flags": "--expose_gc",
+ "markingMode": "none"
+ }
}
\ No newline at end of file
diff --git a/e2e/router/app/rxjs-operators.ts b/e2e/router/app/rxjs-operators.ts
deleted file mode 100644
index 480bbe40f..000000000
--- a/e2e/router/app/rxjs-operators.ts
+++ /dev/null
@@ -1 +0,0 @@
-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
index e005f6dee..f8b9acf6c 100644
--- a/e2e/router/app/second/detail.component.ts
+++ b/e2e/router/app/second/detail.component.ts
@@ -1,8 +1,8 @@
-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";
+import { Component} from "@angular/core";
+import { ActivatedRoute } from "@angular/router";
+import { Page } from "@nativescript/core";
+import { Observable } from "rxjs";
+import { map } from "rxjs/operators";
@Component({
selector: "detail",
@@ -17,9 +17,10 @@ import { Observable } from "rxjs/Observable";
export class DetailComponent {
public id$: Observable;
- constructor(private router: Router, private route: ActivatedRoute) {
+ constructor(private route: ActivatedRoute, private page: Page) {
+ page.actionBarHidden = true;
console.log("DetailComponent - constructor()");
- this.id$ = route.params.map(r => r["id"]);
+ this.id$ = route.params.pipe(map(r => r["id"]));
}
ngOnInit() {
@@ -29,4 +30,4 @@ export class DetailComponent {
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
index 08605d5e5..a7b0d52b9 100644
--- a/e2e/router/app/second/master.component.ts
+++ b/e2e/router/app/second/master.component.ts
@@ -1,19 +1,22 @@
import { Component, OnInit, OnDestroy } from "@angular/core";
-
+import { Page } from "@nativescript/core";
@Component({
selector: "master",
template: `
-
+
-
-
-
+
+
+
+
+
`
})
export class MasterComponent implements OnInit, OnDestroy {
public details: Array = [1, 2, 3];
- constructor() {
+ constructor(private page: Page) {
+ this.page.actionBarHidden = true;
console.log("MasterComponent - constructor()");
}
diff --git a/e2e/router/app/second/second.component.ts b/e2e/router/app/second/second.component.ts
index 75acb847b..390dd1c5f 100644
--- a/e2e/router/app/second/second.component.ts
+++ b/e2e/router/app/second/second.component.ts
@@ -1,9 +1,11 @@
import { Component, OnInit, OnDestroy } from "@angular/core";
-import { ActivatedRoute, Router, Route } from "@angular/router";
+import { ActivatedRoute } from "@angular/router";
-import { RouterExtensions } from "nativescript-angular/router";
-import { Page } from "ui/page";
-import { Observable } from "rxjs/Observable";
+import { RouterExtensions } from "@nativescript/angular";
+import { Page } from "@nativescript/core";
+import { Observable, Subscription } from "rxjs";
+import { map } from "rxjs/operators";
+import { CounterService } from "../counter.service";
@Component({
selector: "second",
@@ -12,35 +14,57 @@ import { Observable } from "rxjs/Observable";
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
`
})
export class SecondComponent implements OnInit, OnDestroy {
public depth$: Observable;
public nextDepth$: Observable;
+ sub: Subscription;
- constructor(private routerExt: RouterExtensions, route: ActivatedRoute, page: Page) {
+ constructor(
+ private routerExt: RouterExtensions,
+ private route: ActivatedRoute,
+ public service: CounterService,
+ 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);
+ this.depth$ = route.params.pipe(map(r => r["depth"]));
+ this.nextDepth$ = route.params.pipe(map(r => +r["depth"] + 1));
}
ngOnInit() {
+ this.sub = this.route.queryParams.subscribe((params) => {
+ console.log("route.queryParams:", params);
+ });
console.log("SecondComponent - ngOnInit()");
}
ngOnDestroy() {
+ this.sub.unsubscribe();
console.log("SecondComponent - ngOnDestroy()");
}
goBack() {
- this.routerExt.back();
+ this.routerExt.back({ relativeTo: this.route });
+ }
+
+ loadNestedNamedOutlet() {
+ this.routerExt.navigate([{ outlets: { lazyNameOutlet: ["lazy-named"] } }], { relativeTo: this.route });
}
-}
\ No newline at end of file
+}
diff --git a/e2e/router/app/vendor-platform.android.ts b/e2e/router/app/vendor-platform.android.ts
deleted file mode 100644
index 719f26498..000000000
--- a/e2e/router/app/vendor-platform.android.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-require("application");
-if (!global["__snapshot"]) {
- // In case snapshot generation is enabled these modules will get into the bundle
- // but will not be required/evaluated.
- // The snapshot webpack plugin will add them to the tns-java-classes.js bundle file.
- // This way, they will be evaluated on app start as early as possible.
- require("ui/frame");
- require("ui/frame/activity");
-}
diff --git a/e2e/router/app/vendor-platform.ios.ts b/e2e/router/app/vendor-platform.ios.ts
deleted file mode 100644
index 7f3e7f039..000000000
--- a/e2e/router/app/vendor-platform.ios.ts
+++ /dev/null
@@ -1 +0,0 @@
-void 0;
diff --git a/e2e/router/app/vendor.ts b/e2e/router/app/vendor.ts
deleted file mode 100644
index 4526eca9b..000000000
--- a/e2e/router/app/vendor.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-// Snapshot the ~/app.css and the theme
-const application = require("application");
-require("ui/styling/style-scope");
-const appCssContext = require.context("~/", false, /^\.\/app\.(css|scss|less|sass)$/);
-global.registerWebpackModules(appCssContext);
-application.loadAppCss();
-
-require("./vendor-platform");
-
-require("reflect-metadata");
-require("@angular/platform-browser");
-require("@angular/core");
-require("@angular/common");
-require("@angular/forms");
-require("@angular/http");
-require("@angular/router");
-
-require("nativescript-angular/platform-static");
-require("nativescript-angular/forms");
-require("nativescript-angular/router");
diff --git a/e2e/router/e2e/router.e2e-spec.ts b/e2e/router/e2e/router.e2e-spec.ts
index 82dcda30c..e22514e2c 100644
--- a/e2e/router/e2e/router.e2e-spec.ts
+++ b/e2e/router/e2e/router.e2e-spec.ts
@@ -3,396 +3,540 @@ import {
UIElement,
createDriver,
SearchOptions,
+ nsCapabilities,
} from "nativescript-dev-appium";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
-describe("Simple navigate and back", () => {
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+
+describe("Router", async function () {
let driver: AppiumDriver;
- before(async () => {
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
await driver.resetApp();
});
- it("should find First", async () => {
- await assureFirstComponent(driver);
+ after(async function () {
+ if (isSauceLab) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
});
- it("should navigate to Second(1)/master", async () => {
- await findAndClick(driver, "GO TO SECOND");
-
- await assureSecondComponent(driver, 1);
- await assureNestedMasterComponent(driver);
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
});
- it("should navigate back to First", async () => {
- await goBack(driver);
- await assureFirstComponent(driver);
- });
-});
+ describe("Simple navigate and back", async function () {
-describe("Navigate inside nested outlet", () => {
- let driver: AppiumDriver;
+ it("should find First", async function () {
+ await assureFirstComponent(driver);
+ });
- before(async () => {
- driver = await createDriver();
- await driver.resetApp();
- });
+ it("should navigate to Second(1)\\master", async function () {
+ await findAndClick(driver, "GO TO SECOND");
+
+ await assureSecondComponent(driver, 1);
+ await assureNestedMasterComponent(driver);
+ });
- it("should find First", async () => {
- await assureFirstComponent(driver);
+ it("should navigate back to First", async function () {
+ await goBack(driver);
+ await assureFirstComponent(driver);
+ });
});
- it("should navigate to Second(1)/master", async () => {
- await findAndClick(driver, "GO TO SECOND");
+ describe("Navigate inside nested outlet", async function () {
- await assureSecondComponent(driver, 1)
- await assureNestedMasterComponent(driver);
- });
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.resetApp();
+ });
- it("should navigate to Second(1)/detail(1) and back", async () => {
- const detailBtn = await driver.findElementByText("DETAIL 1", SearchOptions.exact);
- detailBtn.click();
- await assureSecondComponent(driver, 1)
- await assureNestedDetailComponent(driver, 1);
+ it("should find First", async function () {
+ await assureFirstComponent(driver);
+ });
- await goBack(driver);
- await assureSecondComponent(driver, 1)
- await assureNestedMasterComponent(driver);
- });
+ it("should navigate to Second(1)\\master", async function () {
+ await findAndClick(driver, "GO TO SECOND");
- it("should navigate to Second(1)/detail(2) and back", async () => {
- const detailBtn = await driver.findElementByText("DETAIL 2", SearchOptions.exact);
- detailBtn.click();
- await assureSecondComponent(driver, 1)
- await assureNestedDetailComponent(driver, 2);
+ await assureSecondComponent(driver, 1)
+ await assureNestedMasterComponent(driver);
+ });
- await goBack(driver);
- await assureSecondComponent(driver, 1)
- await assureNestedMasterComponent(driver);
- });
+ it("should navigate to Second(1)\\detail(1) and back", async function () {
+ const detailBtn = await driver.findElementByAutomationText("DETAIL 1");
+ detailBtn.click();
+ await assureSecondComponent(driver, 1)
+ await assureNestedDetailComponent(driver, 1);
- it("should navigate back to First", async () => {
- await goBack(driver);
- await assureFirstComponent(driver);
- });
-});
+ await goBack(driver);
+ await assureSecondComponent(driver, 1)
+ await assureNestedMasterComponent(driver);
+ });
-describe("Navigate to same component with different param", () => {
- let driver: AppiumDriver;
+ it("should navigate to Second(1)\\detail(2) and back", async function () {
+ const detailBtn = await driver.findElementByAutomationText("DETAIL 2");
+ detailBtn.click();
+ await assureSecondComponent(driver, 1)
+ await assureNestedDetailComponent(driver, 2);
- before(async () => {
- driver = await createDriver();
- await driver.resetApp();
- });
+ await goBack(driver);
+ await assureSecondComponent(driver, 1)
+ await assureNestedMasterComponent(driver);
+ });
- it("should find First", async () => {
- await assureFirstComponent(driver);
+ it("should navigate back to First", async function () {
+ await goBack(driver);
+ await assureFirstComponent(driver);
+ });
});
- it("should navigate to Second(1)/master", async () => {
- await findAndClick(driver, "GO TO SECOND");
+ describe("Navigate to same component with different param", async function () {
- await assureSecondComponent(driver, 1)
- await assureNestedMasterComponent(driver);
- });
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.resetApp();
+ });
- it("should navigate to Second(2)/master", async () => {
- const navigationButton =
- await driver.findElementByText("GO TO NEXT SECOND", SearchOptions.exact);
- navigationButton.click();
+ it("should find First", async function () {
+ await assureFirstComponent(driver);
+ });
- await assureSecondComponent(driver, 2)
- await assureNestedMasterComponent(driver);
- });
+ it("should navigate to Second(1)\\master", async function () {
+ await findAndClick(driver, "GO TO SECOND");
- it("should navigate back to Second(1)/master", async () => {
- await goBack(driver);
+ await assureSecondComponent(driver, 1)
+ await assureNestedMasterComponent(driver);
+ });
- await assureSecondComponent(driver, 1)
- await assureNestedMasterComponent(driver);
- });
+ it("should navigate to Second(2)\\master", async function () {
+ const navigationButton =
+ await driver.findElementByAutomationText("GO TO NEXT SECOND");
+ navigationButton.click();
- it("should navigate back to First", async () => {
- await goBack(driver);
- await assureFirstComponent(driver);
- });
-});
+ await assureSecondComponent(driver, 2)
+ await assureNestedMasterComponent(driver);
+ });
-describe("Nested navigation + page navigation", () => {
- let driver: AppiumDriver;
+ it("should navigate back to Second(1)\\master", async function () {
+ await goBack(driver);
- before(async () => {
- driver = await createDriver();
- await driver.resetApp();
- });
+ await assureSecondComponent(driver, 1)
+ await assureNestedMasterComponent(driver);
+ });
- it("should find First", async () => {
- await assureFirstComponent(driver);
+ it("should navigate back to First", async function () {
+ await goBack(driver);
+ await assureFirstComponent(driver);
+ });
});
- it("should navigate to Second(1)/master", async () => {
- await findAndClick(driver, "GO TO SECOND");
+ describe("Nested navigation + page navigation", async function () {
- await assureSecondComponent(driver, 1)
- await assureNestedMasterComponent(driver);
- });
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.resetApp();
+ });
- it("should navigate to Second(1)/detail(1)", async () => {
- const detailBtn = await driver.findElementByText("DETAIL 1", SearchOptions.exact);
- detailBtn.click();
+ it("should find First", async function () {
+ await assureFirstComponent(driver);
+ });
- await assureSecondComponent(driver, 1)
- await assureNestedDetailComponent(driver, 1);
- });
+ it("should navigate to Second(1)\\master", async function () {
+ await findAndClick(driver, "GO TO SECOND");
- it("should navigate to Second(2)/master", async () => {
- const navigationButton =
- await driver.findElementByText("GO TO NEXT SECOND", SearchOptions.exact);
- navigationButton.click();
+ await assureSecondComponent(driver, 1)
+ await assureNestedMasterComponent(driver);
+ });
- await assureSecondComponent(driver, 2)
- await assureNestedMasterComponent(driver);
- });
+ it("should navigate to Second(1)\\detail(1)", async function () {
+ const detailBtn = await driver.findElementByAutomationText("DETAIL 1");
+ detailBtn.click();
- it("should navigate to Second(2)/detail(2)", async () => {
- const detailBtn = await driver.findElementByText("DETAIL 2", SearchOptions.exact);
- detailBtn.click();
+ await assureSecondComponent(driver, 1)
+ await assureNestedDetailComponent(driver, 1);
+ });
- await assureSecondComponent(driver, 2)
- await assureNestedDetailComponent(driver, 2);
- });
+ it("should navigate to Second(2)\\master", async function () {
+ const navigationButton =
+ await driver.findElementByAutomationText("GO TO NEXT SECOND");
+ navigationButton.click();
- it("should navigate to First", async () => {
- await findAndClick(driver, "GO TO FIRST");
+ await assureSecondComponent(driver, 2)
+ await assureNestedMasterComponent(driver);
+ });
- await assureFirstComponent(driver);
- });
+ it("should navigate to Second(2)\\detail(2)", async function () {
+ const detailBtn = await driver.findElementByAutomationText("DETAIL 2");
+ detailBtn.click();
- it("should navigate the whole stack", async () => {
- await goBack(driver);
- await assureSecondComponent(driver, 2)
- await assureNestedDetailComponent(driver, 2);
+ await assureSecondComponent(driver, 2)
+ await assureNestedDetailComponent(driver, 2);
+ });
- await goBack(driver);
- await assureSecondComponent(driver, 2)
- await assureNestedMasterComponent(driver);
+ it("should navigate to First", async function () {
+ await findAndClick(driver, "GO TO FIRST");
- await goBack(driver);
- await assureSecondComponent(driver, 1)
- await assureNestedDetailComponent(driver, 1);
+ await assureFirstComponent(driver);
+ });
- await goBack(driver);
- await assureSecondComponent(driver, 1)
- await assureNestedMasterComponent(driver);
+ it("should navigate the whole stack", async function () {
+ await goBack(driver);
+ await assureSecondComponent(driver, 2)
+ await assureNestedDetailComponent(driver, 2);
- await goBack(driver);
- await assureFirstComponent(driver);
- });
-});
+ await goBack(driver);
+ await assureSecondComponent(driver, 2)
+ await assureNestedMasterComponent(driver);
-describe("Shouldn't be able to navigate back on startup", () => {
- let driver: AppiumDriver;
+ await goBack(driver);
+ await assureSecondComponent(driver, 1)
+ await assureNestedDetailComponent(driver, 1);
- before(async () => {
- driver = await createDriver();
- await driver.resetApp();
- });
+ await goBack(driver);
+ await assureSecondComponent(driver, 1)
+ await assureNestedMasterComponent(driver);
- it("should find First", async () => {
- await assureFirstComponent(driver);
+ await goBack(driver);
+ await assureFirstComponent(driver);
+ });
});
- it("shouldn't be able to go back", async () => {
- await goBack(driver);
- await driver.findElementByText("canGoBack() - false", SearchOptions.exact);
- });
-});
+ describe("Nested name navigation + page navigation", async function () {
-describe("Shouldn't be able to navigate back after cleared history", () => {
- let driver: AppiumDriver;
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.resetApp();
+ });
- before(async () => {
- driver = await createDriver();
- await driver.resetApp();
- });
+ it("should find First", async function () {
+ await assureFirstComponent(driver);
+ });
- it("should find First", async () => {
- await assureFirstComponent(driver);
- });
+ it("should navigate to Second(1)\\master", async function () {
+ await findAndClick(driver, "GO TO SECOND");
- it("should navigate to Second(1)/master", async () => {
- await findAndClick(driver, "GO TO SECOND");
+ await assureSecondComponent(driver, 1)
+ await assureNestedMasterComponent(driver);
+ });
- await assureSecondComponent(driver, 1)
- await assureNestedMasterComponent(driver);
- });
+ it("should load nested named Master", async function () {
+ await findAndClick(driver, "LOAD NESTED NAMED OUTLET");
+ await assureNamedNestedMasterComponent(driver);
+ });
- it("should navigate to Second(1)/master", async () => {
- await findAndClick(driver, "GO TO FIRST(CLEAR)");
+ it("should navigate to nested named Master Detail\\1", async function () {
+ const navigationButton =
+ await driver.findElementByAutomationText("DETAIL-NAMED 1");
+ navigationButton.click();
- await assureFirstComponent(driver);
- });
+ await assureNamedNestedDetailComponent(driver, 1);
+ });
- it("shouldn't be able to go back", async () => {
- await goBack(driver);
- await driver.findElementByText("canGoBack() - false", SearchOptions.exact);
- });
-});
+ it("should navigate back to Master and navigate to Detail\\2", async function () {
+ let navigationButton =
+ await driver.findElementByAutomationText("BACK-NESTED");
+ navigationButton.click();
-describe("Navigate to componentless route", () => {
- let driver: AppiumDriver;
+ await assureNamedNestedMasterComponent(driver);
- before(async () => {
- driver = await createDriver();
- await driver.resetApp();
- });
+ navigationButton =
+ await driver.findElementByAutomationText("DETAIL-NAMED 2");
+ navigationButton.click();
- it("should find First", async () => {
- await assureFirstComponent(driver);
+ await assureNamedNestedDetailComponent(driver, 2);
+ });
});
- it("should navigate to ComponentlessSecond(100)/detail(200)", async () => {
- const navigationButton =
- await driver.findElementByText("GO TO C-LESS SECOND", SearchOptions.exact);
- navigationButton.click();
+ describe("Shouldn't be able to navigate back on startup", async function () {
- await assureSecondComponent(driver, 100)
- await assureNestedDetailComponent(driver, 200);
- });
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.resetApp();
+ });
- it("should navigate to First", async () => {
- await findAndClick(driver, "GO TO FIRST");
+ it("should find First", async function () {
+ await assureFirstComponent(driver);
+ });
- await assureFirstComponent(driver);
+ it("shouldn't be able to go back", async function () {
+ await goBack(driver);
+ await driver.findElementByAutomationText("canGoBack() - false");
+ });
});
- it("should navigate the whole stack", async () => {
- await goBack(driver);
- await assureSecondComponent(driver, 100)
- await assureNestedDetailComponent(driver, 200);
+ describe("Shouldn't be able to navigate back after cleared history", async function () {
- await goBack(driver);
- await assureFirstComponent(driver);
- });
-});
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.resetApp();
+ });
-describe("Navigate to lazy module", () => {
- let driver: AppiumDriver;
+ it("should find First", async function () {
+ await assureFirstComponent(driver);
+ });
- before(async () => {
- driver = await createDriver();
- await driver.resetApp();
- });
+ it("should navigate to Second(1)\\master", async function () {
+ await findAndClick(driver, "GO TO SECOND");
- it("should find First", async () => {
- await assureFirstComponent(driver);
- });
+ await assureSecondComponent(driver, 1)
+ await assureNestedMasterComponent(driver);
+ });
- it("should navigate to lazy/home", async () => {
- await findAndClick(driver, "GO TO LAZY HOME");
- await assureLazyComponent(driver);
- });
+ it("should navigate to Second(1)\\master", async function () {
+ await findAndClick(driver, "GO TO FIRST(CLEAR)");
- it("should navigate to First", async () => {
- await findAndClick(driver, "GO TO FIRST");
- await assureFirstComponent(driver);
- });
+ await assureFirstComponent(driver);
+ });
- it("should navigate back to lazy/home", async () => {
- await goBack(driver);
- await assureLazyComponent(driver);
+ it("shouldn't be able to go back", async function () {
+ await goBack(driver);
+ await driver.findElementByAutomationText("canGoBack() - false");
+ });
});
- it("should navigate to First again", async () => {
- await findAndClick(driver, "GO TO FIRST");
- await assureFirstComponent(driver);
- });
+ describe("Navigate to componentless route", async function () {
- it("should navigate the whole stack", async () => {
- await goBack(driver);
- await assureLazyComponent(driver);
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.resetApp();
+ });
- await goBack(driver);
- await assureFirstComponent(driver);
- });
-});
+ it("should find First", async function () {
+ await assureFirstComponent(driver);
+ });
-describe("Navigate to componentless lazy module route", () => {
- let driver: AppiumDriver;
+ it("should navigate to ComponentlessSecond(100)/detail(200)", async function () {
+ const navigationButton =
+ await driver.findElementByAutomationText("GO TO C-LESS SECOND");
+ navigationButton.click();
- before(async () => {
- driver = await createDriver();
- await driver.resetApp();
- });
+ await assureSecondComponent(driver, 100)
+ await assureNestedDetailComponent(driver, 200);
+ });
+
+ it("should navigate to First", async function () {
+ await findAndClick(driver, "GO TO FIRST");
+
+ await assureFirstComponent(driver);
+ });
+
+ it("should navigate the whole stack", async function () {
+ await goBack(driver);
+ await assureSecondComponent(driver, 100)
+ await assureNestedDetailComponent(driver, 200);
- it("should find First", async () => {
- await assureFirstComponent(driver);
+ await goBack(driver);
+ await assureFirstComponent(driver);
+ });
});
- it("should navigate to nest/more (componentless lazy route)", async () => {
- await findAndClick(driver, "GO TO C-LESS LAZY");
+ describe("Navigate to lazy module", async function () {
+
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.resetApp();
+ });
+
+ it("should find First", async function () {
+ await assureFirstComponent(driver);
+ });
+
+ it("should navigate to lazy\\home", async function () {
+ await findAndClick(driver, "GO TO LAZY HOME");
+ await assureLazyComponent(driver);
+ });
+
+ it("should navigate to First", async function () {
+ await findAndClick(driver, "GO TO FIRST");
+ await assureFirstComponent(driver);
+ });
+
+ it("should navigate back to lazy\\home", async function () {
+ await goBack(driver);
+ await assureLazyComponent(driver);
+ });
+
+ it("should navigate to First again", async function () {
+ await findAndClick(driver, "GO TO FIRST");
+ await assureFirstComponent(driver);
+ });
- await assureComponentlessLazyComponent(driver);
+ it("should navigate the whole stack", async function () {
+ await goBack(driver);
+ await assureLazyComponent(driver);
+
+ await goBack(driver);
+ await assureFirstComponent(driver);
+ });
});
- it("should navigate to lazy/home", async () => {
- await findAndClick(driver, "GO TO LAZY HOME");
+ describe("Navigate to componentless lazy module route", async function () {
+
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.resetApp();
+ });
+
+ it("should find First", async function () {
+ await assureFirstComponent(driver);
+ });
+
+ it("should navigate to nest\\more (componentless lazy route)", async function () {
+ await findAndClick(driver, "GO TO C-LESS LAZY");
+
+ await assureComponentlessLazyComponent(driver);
+ });
+
+ it("should navigate to lazy\\home", async function () {
+ await findAndClick(driver, "GO TO LAZY HOME");
+
+ await assureLazyComponent(driver);
+ });
- await assureLazyComponent(driver);
+ it("should navigate to First", async function () {
+ await findAndClick(driver, "GO TO FIRST");
+
+ await assureFirstComponent(driver);
+ });
+
+ it("should navigate the whole stack", async function () {
+ await goBack(driver);
+ await assureLazyComponent(driver);
+
+ await goBack(driver);
+ await assureComponentlessLazyComponent(driver);
+
+ await goBack(driver);
+ await assureFirstComponent(driver);
+ });
});
- it("should navigate to First", async () => {
- await findAndClick(driver, "GO TO FIRST");
+ describe("Simple navigate and back should trigger only one CD on FirstComponent", async function () {
+
+ before(async function () {
+ nsCapabilities.testReporter.context = this;
+ await driver.resetApp();
+ });
+
+ it("should find First", async function () {
+ await assureFirstComponent(driver);
+ });
- await assureFirstComponent(driver);
+ it("should reset counter", async function () {
+ await findAndClick(driver, "RESET");
+ await driver.waitForElement("CHECK: 1");
+ });
+
+ it("should navigate to Second(1)\\master", async function () {
+ await findAndClick(driver, "GO TO SECOND");
+
+ await assureSecondComponent(driver, 1);
+ await assureNestedMasterComponent(driver);
+ });
+
+ it("should navigate back to First", async function () {
+ await goBack(driver);
+ await assureFirstComponent(driver);
+ await driver.waitForElement("CHECK: 2");
+ });
});
- it("should navigate the whole stack", async () => {
- await goBack(driver);
- await assureLazyComponent(driver);
+ describe("Simple navigate and back should trigger only one CD on FirstComponent even with 3 changes in service", function () {
+
+ before(async function () {
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ await driver.resetApp();
+ });
+
+ it("should find First", async function () {
+ await assureFirstComponent(driver);
+ });
+
+ it("should reset counter", async function () {
+ await findAndClick(driver, "RESET");
+ await driver.waitForElement("CHECK: 1");
+ await driver.waitForElement("COUNTER: 0");
+ });
+
+ it("should navigate to Second(1)\\master", async function () {
+ await findAndClick(driver, "GO TO SECOND");
- await goBack(driver);
- await assureComponentlessLazyComponent(driver);
+ await assureSecondComponent(driver, 1);
+ await assureNestedMasterComponent(driver);
+ });
- await goBack(driver);
- await assureFirstComponent(driver);
+ it("should increase counter", async function () {
+ await findAndClick(driver, "TICK");
+ await findAndClick(driver, "TICK");
+ await findAndClick(driver, "TICK");
+ });
+
+ it("should navigate back to First", async function () {
+ await goBack(driver);
+ await assureFirstComponent(driver);
+ await driver.waitForElement("CHECK: 2");
+ await driver.waitForElement("COUNTER: 3");
+ });
});
});
async function assureFirstComponent(driver: AppiumDriver) {
- await driver.findElementByText("FirstComponent", SearchOptions.exact);
+ await driver.findElementByAutomationText("FirstComponent");
}
async function assureLazyComponent(driver: AppiumDriver) {
- await driver.findElementByText("LazyComponent", SearchOptions.exact);
+ await driver.findElementByAutomationText("LazyComponent");
}
async function assureComponentlessLazyComponent(driver: AppiumDriver) {
- await driver.findElementByText("Lazy Componentless Route", SearchOptions.exact);
+ await driver.findElementByAutomationText("Lazy Componentless Route");
+}
+
+async function assureNamedNestedMasterComponent(driver: AppiumDriver) {
+ await driver.findElementByAutomationText("NamedNestedMaster");
+}
+
+async function assureNamedNestedDetailComponent(driver: AppiumDriver, param: number) {
+ await driver.findElementByAutomationText("NamedNestedDetail");
+ await driver.findElementByAutomationText(`nested-named-param: ${param}`);
+
}
async function assureSecondComponent(driver: AppiumDriver, param: number) {
- await driver.findElementByText("SecondComponent", SearchOptions.exact);
- await driver.findElementByText(`param: ${param}`, SearchOptions.exact);
+ await driver.findElementByAutomationText("SecondComponent");
+ await driver.findElementByAutomationText(`param: ${param}`);
}
async function assureNestedMasterComponent(driver: AppiumDriver) {
- await driver.findElementByText("NestedMaster", SearchOptions.exact);
+ await driver.findElementByAutomationText("NestedMaster");
}
async function assureNestedDetailComponent(driver: AppiumDriver, param: number) {
- await driver.findElementByText("NestedDetail", SearchOptions.exact);
- await driver.findElementByText(`nested-param: ${param}`, SearchOptions.exact);
+ await driver.findElementByAutomationText("NestedDetail");
+ await driver.findElementByAutomationText(`nested-param: ${param}`);
}
async function goBack(driver: AppiumDriver) {
- const backButton = await driver.findElementByText("BACK", SearchOptions.exact);
+ const backButton = await driver.waitForElement("BACK");
await backButton.click();
- //await driver.navBack();
}
async function findAndClick(driver: AppiumDriver, text: string) {
- const navigationButton =
- await driver.findElementByText(text, SearchOptions.exact);
- navigationButton.click();
+ const navigationButton = await driver.waitForElement(text);
+ await navigationButton.click();
}
\ No newline at end of file
diff --git a/e2e/router/e2e/setup.ts b/e2e/router/e2e/setup.ts
index 0c0add14a..11b854fca 100644
--- a/e2e/router/e2e/setup.ts
+++ b/e2e/router/e2e/setup.ts
@@ -1,19 +1,19 @@
-import { startServer, stopServer , createDriver , AppiumDriver } from "nativescript-dev-appium";
+import { startServer, stopServer, ITestReporter, nsCapabilities, LogImageType } from "nativescript-dev-appium";
+const addContext = require('mochawesome/addContext');
-let driver: AppiumDriver;
+const testReporterContext = {};
+testReporterContext.name = "mochawesome";
+testReporterContext.reportDir = "mochawesome-report";
+testReporterContext.log = addContext;
+testReporterContext.logImageTypes = [LogImageType.screenshots];
+nsCapabilities.testReporter = testReporterContext;
-before("start server", async () => {
+before("start server", async function () {
+ nsCapabilities.testReporter.context = this;
await startServer();
- driver = await createDriver();
});
-after("stop server", async () => {
- await driver.quit();
+after("stop server", async function () {
+ nsCapabilities.testReporter.context = this;
await stopServer();
});
-
-afterEach(async function () {
- if (this.currentTest.state === "failed") {
- await driver.logScreenshot(this.currentTest.title);
- }
-});
\ No newline at end of file
diff --git a/e2e/router/e2e/tsconfig.json b/e2e/router/e2e/tsconfig.json
index 18b6c4302..43e3b7ee3 100644
--- a/e2e/router/e2e/tsconfig.json
+++ b/e2e/router/e2e/tsconfig.json
@@ -5,6 +5,7 @@
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"importHelpers": false,
+ "sourceMap": true,
"types": [
"node",
"mocha",
diff --git a/e2e/router/package.json b/e2e/router/package.json
index 8e2bfc4ad..b25f2ad6e 100644
--- a/e2e/router/package.json
+++ b/e2e/router/package.json
@@ -5,65 +5,56 @@
"repository": "",
"nativescript": {
"id": "org.nativescript.router",
- "tns-android": {
- "version": "3.4.0-2017-12-4-1"
- },
"tns-ios": {
- "version": "next"
+ "version": "6.5.1"
}
},
"dependencies": {
- "@angular/animations": "~5.2.0",
- "@angular/common": "~5.2.0",
- "@angular/compiler": "~5.2.0",
- "@angular/core": "~5.2.0",
- "@angular/forms": "~5.2.0",
- "@angular/http": "~5.2.0",
- "@angular/platform-browser": "~5.2.0",
- "@angular/platform-browser-dynamic": "~5.2.0",
- "@angular/router": "~5.2.0",
- "nativescript-angular": "file:../../nativescript-angular",
- "nativescript-intl": "^3.0.0",
+ "@angular/animations": "~9.1.0",
+ "@angular/common": "~9.1.0",
+ "@angular/compiler": "~9.1.0",
+ "@angular/core": "~9.1.0",
+ "@angular/forms": "~9.1.0",
+ "@angular/platform-browser": "~9.1.0",
+ "@angular/platform-browser-dynamic": "~9.1.0",
+ "@angular/router": "~9.1.0",
+ "@nativescript/angular": "file:../../dist/nativescript-angular-scoped.tgz",
"reflect-metadata": "~0.1.8",
- "rxjs": "^5.5.4",
- "tns-core-modules": "next",
- "zone.js": "^0.8.4"
+ "rxjs": "~6.5.5",
+ "@nativescript/core": "next",
+ "zone.js": "^0.10.3"
},
"devDependencies": {
- "@angular/compiler-cli": "~5.2.0",
- "@ngtools/webpack": "~1.9.4",
- "@types/chai": "^4.0.2",
- "@types/mocha": "^2.2.41",
- "@types/node": "^7.0.5",
+ "@angular/compiler-cli": "~9.1.0",
+ "@ngtools/webpack": "~9.1.0",
+ "@types/chai": "~4.1.7",
+ "@types/mocha": "~5.2.5",
+ "@types/node": "^10.12.12",
"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",
- "copy-webpack-plugin": "~4.3.0",
- "css-loader": "~0.28.7",
- "extract-text-webpack-plugin": "~3.0.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",
- "nativescript-dev-webpack": "^0.9.2",
- "nativescript-worker-loader": "~0.8.1",
- "raw-loader": "~0.5.1",
- "resolve-url-loader": "~2.2.1",
+ "mocha": "~5.2.0",
+ "mochawesome": "~3.1.2",
+ "nativescript-dev-appium": "^6.0.0",
+ "nativescript-dev-webpack": "next",
"tslib": "^1.7.1",
- "typescript": "~2.6.2",
- "uglifyjs-webpack-plugin": "~1.1.6",
- "webpack": "~3.10.0",
- "webpack-bundle-analyzer": "^2.9.1",
- "webpack-sources": "~1.1.0"
+ "typescript": "~3.8.3"
},
"scripts": {
+ "clean": "npx rimraf hooks node_modules platforms package-lock.json",
"e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
+ "e2e-watch": "tsc -p e2e --watch",
"compile-tests-w": "tsc -p e2e --watch",
- "update-app-ng-deps": "update-app-ng-deps"
+ "setup": "cd ../../nativescript-angular && npm run prep.apps && cd ../e2e/router && npm run clean",
+ "u": "update-ns-webpack",
+ "ns-verify-bundle": "ns-verify-bundle",
+ "update-ns-webpack": "update-ns-webpack",
+ "ngcc": "ngcc --properties es2015 module main --first-only",
+ "postinstall": "npm run ngcc",
+ "ios": "tns debug ios --env.aot --emulator --no-hmr",
+ "android": "tns debug android --env.aot --emulator --no-hmr"
}
}
diff --git a/e2e/router/references.d.ts b/e2e/router/references.d.ts
new file mode 100644
index 000000000..b14f3837d
--- /dev/null
+++ b/e2e/router/references.d.ts
@@ -0,0 +1 @@
+/// Needed for autocompletion and compilation.
\ No newline at end of file
diff --git a/e2e/router/tsconfig.esm.json b/e2e/router/tsconfig.esm.json
new file mode 100644
index 000000000..a96f6bbc6
--- /dev/null
+++ b/e2e/router/tsconfig.esm.json
@@ -0,0 +1,7 @@
+{
+ "extends": "./tsconfig",
+ "compilerOptions": {
+ "module": "ESNext",
+ "moduleResolution": "node"
+ }
+}
diff --git a/e2e/router/tsconfig.json b/e2e/router/tsconfig.json
index f89ae0f58..ac4e60f3a 100644
--- a/e2e/router/tsconfig.json
+++ b/e2e/router/tsconfig.json
@@ -6,22 +6,26 @@
"emitDecoratorMetadata": true,
"noEmitHelpers": true,
"noEmitOnError": true,
+ "skipLibCheck": true,
"lib": [
- "es6",
+ "es2017",
"dom",
- "es2015.iterable"
+ "es6"
],
"baseUrl": ".",
"paths": {
- "*": [
- "./node_modules/tns-core-modules/*",
- "./node_modules/*"
+ "~/*": [
+ "app/*"
]
}
},
+ "files": [
+ "./app/main.ts"
+ ],
"exclude": [
"node_modules",
"platforms",
+ "**/*.aot",
"e2e"
]
-}
+}
\ No newline at end of file
diff --git a/e2e/router/tsconfig.tns.json b/e2e/router/tsconfig.tns.json
new file mode 100644
index 000000000..a96f6bbc6
--- /dev/null
+++ b/e2e/router/tsconfig.tns.json
@@ -0,0 +1,7 @@
+{
+ "extends": "./tsconfig",
+ "compilerOptions": {
+ "module": "ESNext",
+ "moduleResolution": "node"
+ }
+}
diff --git a/e2e/single-page/.gitignore b/e2e/single-page/.gitignore
new file mode 100644
index 000000000..e3eb2283a
--- /dev/null
+++ b/e2e/single-page/.gitignore
@@ -0,0 +1,11 @@
+.vscode
+
+platforms
+node_modules
+hooks
+
+/**/*.js
+/**/*.map
+e2e/reports
+test-results.xml
+mochawesome-report
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/Android/app.gradle b/e2e/single-page/app/App_Resources/Android/app.gradle
new file mode 100644
index 000000000..23f5c481b
--- /dev/null
+++ b/e2e/single-page/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.singlepage"
+
+ //override supported platforms
+ // ndk {
+ // abiFilters.clear()
+ // abiFilters "armeabi-v7a"
+ // }
+
+ }
+ aaptOptions {
+ additionalParameters "--no-version-vectors"
+ }
+}
diff --git a/e2e/single-page/app/App_Resources/Android/src/main/AndroidManifest.xml b/e2e/single-page/app/App_Resources/Android/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..9db832151
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/Android/src/main/AndroidManifest.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/single-page/app/App_Resources/Android/src/main/res/drawable-nodpi/background.png b/e2e/single-page/app/App_Resources/Android/src/main/res/drawable-nodpi/background.png
new file mode 100644
index 000000000..748b2adf5
Binary files /dev/null and b/e2e/single-page/app/App_Resources/Android/src/main/res/drawable-nodpi/background.png differ
diff --git a/e2e/single-page/app/App_Resources/Android/src/main/res/drawable-nodpi/icon.png b/e2e/single-page/app/App_Resources/Android/src/main/res/drawable-nodpi/icon.png
new file mode 100755
index 000000000..ddfc17a71
Binary files /dev/null and b/e2e/single-page/app/App_Resources/Android/src/main/res/drawable-nodpi/icon.png differ
diff --git a/e2e/single-page/app/App_Resources/Android/src/main/res/drawable-nodpi/logo.png b/e2e/single-page/app/App_Resources/Android/src/main/res/drawable-nodpi/logo.png
new file mode 100644
index 000000000..b9e102a76
Binary files /dev/null and b/e2e/single-page/app/App_Resources/Android/src/main/res/drawable-nodpi/logo.png differ
diff --git a/e2e/single-page/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml b/e2e/single-page/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
new file mode 100644
index 000000000..ada77f92c
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml
@@ -0,0 +1,8 @@
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/Android/src/main/res/values-v21/colors.xml b/e2e/single-page/app/App_Resources/Android/src/main/res/values-v21/colors.xml
new file mode 100644
index 000000000..a64641a9d
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/Android/src/main/res/values-v21/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #3d5afe
+
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/Android/src/main/res/values-v21/styles.xml b/e2e/single-page/app/App_Resources/Android/src/main/res/values-v21/styles.xml
new file mode 100644
index 000000000..dac8727c8
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/Android/src/main/res/values-v21/styles.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/Android/src/main/res/values/colors.xml b/e2e/single-page/app/App_Resources/Android/src/main/res/values/colors.xml
new file mode 100644
index 000000000..74ad8829c
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/Android/src/main/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #F5F5F5
+ #757575
+ #33B5E5
+ #272734
+
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/Android/src/main/res/values/styles.xml b/e2e/single-page/app/App_Resources/Android/src/main/res/values/styles.xml
new file mode 100644
index 000000000..1e8c7f29b
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/Android/src/main/res/values/styles.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..1953734f4
--- /dev/null
+++ b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/Contents.json b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/Contents.json
new file mode 100644
index 000000000..da4a164c9
--- /dev/null
+++ b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 000000000..4414bad08
--- /dev/null
+++ b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
new file mode 100644
index 000000000..9f1f6ce3e
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
new file mode 100644
index 000000000..4f4e9c506
--- /dev/null
+++ b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
new file mode 100644
index 000000000..23c0ffd7a
--- /dev/null
+++ b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png b/e2e/single-page/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/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Info.plist b/e2e/single-page/app/App_Resources/iOS/Info.plist
new file mode 100644
index 000000000..ea3e3ea23
--- /dev/null
+++ b/e2e/single-page/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/single-page/app/App_Resources/iOS/LaunchScreen.storyboard b/e2e/single-page/app/App_Resources/iOS/LaunchScreen.storyboard
new file mode 100644
index 000000000..2ad9471e1
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/iOS/LaunchScreen.storyboard
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/single-page/app/App_Resources/iOS/build.xcconfig b/e2e/single-page/app/App_Resources/iOS/build.xcconfig
new file mode 100644
index 000000000..4b0118490
--- /dev/null
+++ b/e2e/single-page/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/single-page/app/README.md b/e2e/single-page/app/README.md
new file mode 100644
index 000000000..ebe60c416
--- /dev/null
+++ b/e2e/single-page/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/single-page/app/app-routing.module.ts b/e2e/single-page/app/app-routing.module.ts
new file mode 100644
index 000000000..a1528f7ff
--- /dev/null
+++ b/e2e/single-page/app/app-routing.module.ts
@@ -0,0 +1,32 @@
+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"
+
+export const routes = [
+ {
+ path: "",
+ redirectTo: "/first",
+ pathMatch: "full"
+ },
+ {
+ path: "first",
+ component: FirstComponent,
+ },
+ {
+ path: "second/:id", component: SecondComponent,
+ },
+];
+
+export const navigatableComponents = [
+ FirstComponent,
+ SecondComponent,
+];
+
+@NgModule({
+ imports: [NativeScriptRouterModule.forRoot(routes)],
+ exports: [NativeScriptRouterModule],
+})
+export class AppRoutingModule { }
+
diff --git a/e2e/single-page/app/app.component.ts b/e2e/single-page/app/app.component.ts
new file mode 100644
index 000000000..7b9069e2c
--- /dev/null
+++ b/e2e/single-page/app/app.component.ts
@@ -0,0 +1,19 @@
+import { Component } from "@angular/core";
+
+@Component({
+ template: `
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class AppComponent { }
+
diff --git a/e2e/single-page/app/app.css b/e2e/single-page/app/app.css
new file mode 100644
index 000000000..6e4e9cee1
--- /dev/null
+++ b/e2e/single-page/app/app.css
@@ -0,0 +1,24 @@
+.title {
+ font-size: 15;
+ margin: 16;
+}
+
+.nav {
+ orientation: horizontal;
+ horizontal-align: stretch;
+ vertical-align: bottom;
+ padding: 4;
+ background-color: lightblue;
+ justify-content: space-around;
+ align-content: center;
+}
+
+.link {
+ margin: 10 30;
+ horizontal-align: center;
+}
+
+.active {
+ color: orangered;
+ background-color: lightgreen;
+}
diff --git a/e2e/single-page/app/app.module.ngfactory.d.ts b/e2e/single-page/app/app.module.ngfactory.d.ts
new file mode 100644
index 000000000..793157de3
--- /dev/null
+++ b/e2e/single-page/app/app.module.ngfactory.d.ts
@@ -0,0 +1,4 @@
+/**
+ * A dynamically generated module when compiled with AoT.
+ */
+export const AppModuleNgFactory: any;
\ No newline at end of file
diff --git a/e2e/single-page/app/app.module.ts b/e2e/single-page/app/app.module.ts
new file mode 100644
index 000000000..cadf831d8
--- /dev/null
+++ b/e2e/single-page/app/app.module.ts
@@ -0,0 +1,39 @@
+import { NgModule, NgModuleFactoryLoader, NO_ERRORS_SCHEMA } from "@angular/core";
+import { NativeScriptModule } from "@nativescript/angular";
+import { NSModuleFactoryLoader } from "@nativescript/angular/router";
+ import { isNavigationButton } from "@nativescript/angular/directives/action-bar";
+
+import {
+ AppRoutingModule,
+ navigatableComponents,
+} from "./app-routing.module";
+
+import { AppComponent } from "./app.component";
+
+import { routeReuseStrategyTraceCategory, routerTraceCategory } from "@nativescript/angular/trace";
+import { setCategories, enable } from "@nativescript/core/trace";
+import { ModalComponent } from "./second/modal/modal.component";
+setCategories(routerTraceCategory + "," + routeReuseStrategyTraceCategory);
+enable();
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ ModalComponent,
+ ...navigatableComponents,
+ ],
+ entryComponents:[
+ ModalComponent
+ ],
+ bootstrap: [AppComponent],
+ providers: [
+ { provide: NgModuleFactoryLoader, useClass: NSModuleFactoryLoader }
+ ],
+ imports: [
+ NativeScriptModule,
+ AppRoutingModule,
+ ],
+ schemas: [NO_ERRORS_SCHEMA],
+})
+export class AppModule { }
+
diff --git a/e2e/single-page/app/first/first.component.ts b/e2e/single-page/app/first/first.component.ts
new file mode 100644
index 000000000..e2d49e3a9
--- /dev/null
+++ b/e2e/single-page/app/first/first.component.ts
@@ -0,0 +1,27 @@
+import { Component, OnInit, OnDestroy } from "@angular/core";
+
+import { Page } from "@nativescript/core/ui/page";
+
+@Component({
+ selector: "first",
+ template: `
+
+
+
+
+
+ `
+})
+export class FirstComponent implements OnInit, OnDestroy {
+ constructor(page: Page) {
+ console.log("FirstComponent - constructor() page: " + page);
+ }
+
+ ngOnInit() {
+ console.log("FirstComponent - ngOnInit()");
+ }
+
+ ngOnDestroy() {
+ console.log("FirstComponent - ngOnDestroy()");
+ }
+}
diff --git a/e2e/single-page/app/main.ts b/e2e/single-page/app/main.ts
new file mode 100644
index 000000000..52e4bc5a2
--- /dev/null
+++ b/e2e/single-page/app/main.ts
@@ -0,0 +1,4 @@
+import { platformNativeScriptDynamic } from "@nativescript/angular/platform";
+import { AppModule } from "./app.module";
+
+platformNativeScriptDynamic({ createFrameOnBootstrap: true }).bootstrapModule(AppModule);
diff --git a/e2e/single-page/app/package.json b/e2e/single-page/app/package.json
new file mode 100644
index 000000000..706d1a31b
--- /dev/null
+++ b/e2e/single-page/app/package.json
@@ -0,0 +1,9 @@
+{
+ "main": "main.js",
+ "name": "nativescript-template-ng-tutorial",
+ "version": "3.1.0",
+ "android": {
+ "v8Flags": "--expose_gc",
+ "markingMode": "none"
+ }
+}
\ No newline at end of file
diff --git a/e2e/single-page/app/second/modal/modal.component.html b/e2e/single-page/app/second/modal/modal.component.html
new file mode 100644
index 000000000..99e8c4358
--- /dev/null
+++ b/e2e/single-page/app/second/modal/modal.component.html
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/e2e/single-page/app/second/modal/modal.component.ts b/e2e/single-page/app/second/modal/modal.component.ts
new file mode 100644
index 000000000..895f47679
--- /dev/null
+++ b/e2e/single-page/app/second/modal/modal.component.ts
@@ -0,0 +1,19 @@
+import { Component } from '@angular/core';
+import { ModalDialogParams } from "@nativescript/angular/modal-dialog";
+
+@Component({
+ moduleId: module.id,
+ selector: 'modal',
+ templateUrl: './modal.component.html'
+})
+
+export class ModalComponent {
+
+ constructor(private params: ModalDialogParams) {
+ }
+
+ public close() {
+ this.params.closeCallback();
+ }
+
+}
\ No newline at end of file
diff --git a/e2e/single-page/app/second/second.component.ts b/e2e/single-page/app/second/second.component.ts
new file mode 100644
index 000000000..e36fb3aec
--- /dev/null
+++ b/e2e/single-page/app/second/second.component.ts
@@ -0,0 +1,60 @@
+import { Component, OnInit, OnDestroy, ViewContainerRef } from "@angular/core";
+import { ActivatedRoute, Router, Route } from "@angular/router";
+
+import { ModalDialogService, ModalDialogOptions } from "@nativescript/angular";
+import { Page } from "@nativescript/core/ui/page";
+import { Observable } from "rxjs";
+import { map } from "rxjs/operators";
+import { RouterExtensions } from "@nativescript/angular/router";
+import { ModalComponent } from "./modal/modal.component";
+
+@Component({
+ selector: "second",
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class SecondComponent implements OnInit, OnDestroy {
+ public id$: Observable;
+ constructor(route: ActivatedRoute,
+ private routerExtensions: RouterExtensions,
+ private viewContainerRef: ViewContainerRef,
+ private modalService: ModalDialogService) {
+ this.id$ = route.params.pipe(map(r => +r["id"]));
+ }
+
+ ngOnInit() {
+ console.log("SecondComponent - ngOnInit()");
+ }
+
+ ngOnDestroy() {
+ console.log("SecondComponent - ngOnDestroy()");
+ }
+
+ back() {
+ this.routerExtensions.back();
+ }
+
+ onShowModal() {
+ let options: ModalDialogOptions = {
+ viewContainerRef: this.viewContainerRef,
+ context: {
+ },
+ fullscreen: true
+ };
+
+ this.modalService.showModal(ModalComponent, options).then((dialogResult: string) => {
+ });
+ }
+}
\ No newline at end of file
diff --git a/e2e/single-page/e2e/setup.ts b/e2e/single-page/e2e/setup.ts
new file mode 100644
index 000000000..11b854fca
--- /dev/null
+++ b/e2e/single-page/e2e/setup.ts
@@ -0,0 +1,19 @@
+import { startServer, stopServer, ITestReporter, nsCapabilities, LogImageType } from "nativescript-dev-appium";
+const addContext = require('mochawesome/addContext');
+
+const testReporterContext = {};
+testReporterContext.name = "mochawesome";
+testReporterContext.reportDir = "mochawesome-report";
+testReporterContext.log = addContext;
+testReporterContext.logImageTypes = [LogImageType.screenshots];
+nsCapabilities.testReporter = testReporterContext;
+
+before("start server", async function () {
+ nsCapabilities.testReporter.context = this;
+ await startServer();
+});
+
+after("stop server", async function () {
+ nsCapabilities.testReporter.context = this;
+ await stopServer();
+});
diff --git a/e2e/single-page/e2e/tests.e2e-spec.ts b/e2e/single-page/e2e/tests.e2e-spec.ts
new file mode 100644
index 000000000..fb6d16d3c
--- /dev/null
+++ b/e2e/single-page/e2e/tests.e2e-spec.ts
@@ -0,0 +1,88 @@
+import {
+ AppiumDriver,
+ createDriver,
+ nsCapabilities,
+} from "nativescript-dev-appium";
+import { isSauceLab } from "nativescript-dev-appium/lib/parser";
+import { ImageOptions } from "nativescript-dev-appium/lib/image-options";
+
+const QUEUE_WAIT_TIME: number = 600000; // Sometimes SauceLabs threads are not available and the tests wait in a queue to start. Wait 10 min before timeout.
+
+describe("Single page app", async function () {
+ let driver: AppiumDriver;
+
+ before(async function () {
+ this.timeout(QUEUE_WAIT_TIME);
+ nsCapabilities.testReporter.context = this;
+ driver = await createDriver();
+ driver.imageHelper.defaultTolerance = 50;
+ driver.imageHelper.defaultToleranceType = ImageOptions.pixel;
+ });
+
+ after(async function () {
+ if (isSauceLab) {
+ driver.sessionId().then(function (sessionId) {
+ console.log("Report https://saucelabs.com/beta/tests/" + sessionId);
+ });
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ it("should load first page", async function () {
+ await driver.findElementByAutomationText("First Component");
+
+ // ActionBar Title and item
+ await driver.findElementByAutomationText("First Title");
+ await driver.findElementByAutomationText("ACTION1");
+ });
+
+ it("should load second(1) page", async function () {
+ await findAndClick(driver, "SECOND(1)");
+
+ await driver.findElementByAutomationText("Second Component: 1");
+
+ // ActionBar Title and item
+ await driver.findElementByAutomationText("Second Title");
+ await driver.findElementByAutomationText("ACTION2");
+ });
+
+ it("should load second(2) page", async function () {
+ await findAndClick(driver, "SECOND(2)");
+
+ await driver.findElementByAutomationText("Second Component: 2");
+
+ // ActionBar Title and items
+ await driver.findElementByAutomationText("Second Title");
+ await driver.findElementByAutomationText("ACTION2");
+ await driver.findElementByAutomationText("ADD");
+ });
+
+ it("should open and close modal view", async function () {
+ await findAndClick(driver, "Show Modal");
+
+ await driver.findElementByAutomationText("Welcome to modal");
+ await findAndClick(driver, "Close Modal");
+
+ await driver.findElementByAutomationText("Second Component: 2");
+ });
+
+ it("should go back to second(1) and first", async function () {
+ await findAndClick(driver, "Back");
+ await driver.findElementByAutomationText("Second Component: 1");
+ await findAndClick(driver, "Back");
+ await driver.findElementByAutomationText("First Title");
+ await driver.findElementByAutomationText("ACTION1");
+ });
+});
+
+async function findAndClick(driver: AppiumDriver, text: string) {
+ const navigationButton = await driver.findElementByAutomationText(text);
+ await navigationButton.click();
+}
\ No newline at end of file
diff --git a/e2e/single-page/e2e/tsconfig.json b/e2e/single-page/e2e/tsconfig.json
new file mode 100644
index 000000000..18b6c4302
--- /dev/null
+++ b/e2e/single-page/e2e/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es6",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "importHelpers": false,
+ "types": [
+ "node",
+ "mocha",
+ "chai"
+ ],
+ "lib": [
+ "es6",
+ "dom"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/e2e/single-page/package.json b/e2e/single-page/package.json
new file mode 100644
index 000000000..e03922757
--- /dev/null
+++ b/e2e/single-page/package.json
@@ -0,0 +1,47 @@
+{
+ "description": "NativeScript Application",
+ "license": "SEE LICENSE IN ",
+ "readme": "NativeScript Application",
+ "repository": "",
+ "nativescript": {
+ "id": "org.nativescript.singlepage"
+ },
+ "dependencies": {
+ "@angular/animations": "~9.1.0",
+ "@angular/common": "~9.1.0",
+ "@angular/compiler": "~9.1.0",
+ "@angular/core": "~9.1.0",
+ "@angular/forms": "~9.1.0",
+ "@angular/platform-browser": "~9.1.0",
+ "@angular/platform-browser-dynamic": "~9.1.0",
+ "@angular/router": "~9.1.0",
+ "@nativescript/angular": "file:../../dist/nativescript-angular-scoped.tgz",
+ "reflect-metadata": "~0.1.8",
+ "rxjs": "~6.5.5",
+ "@nativescript/core": "next",
+ "zone.js": "^0.10.3"
+ },
+ "devDependencies": {
+ "@angular/compiler-cli": "~9.1.0",
+ "@ngtools/webpack": "~9.1.0",
+ "@types/chai": "~4.1.7",
+ "@types/mocha": "~5.2.5",
+ "@types/node": "~10.12.18",
+ "babel-traverse": "6.25.0",
+ "babel-types": "6.25.0",
+ "babylon": "6.17.4",
+ "chai-as-promised": "~7.1.1",
+ "colors": "^1.1.2",
+ "lazy": "1.0.11",
+ "mocha": "~5.2.0",
+ "mochawesome": "~3.1.2",
+ "nativescript-dev-appium": "^6.0.0",
+ "nativescript-dev-webpack": "next",
+ "tslib": "^1.7.1",
+ "typescript": "~3.8.3"
+ },
+ "scripts": {
+ "e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
+ "compile-tests-w": "tsc -p e2e --watch"
+ }
+}
diff --git a/e2e/single-page/tsconfig.json b/e2e/single-page/tsconfig.json
new file mode 100644
index 000000000..a3ba8b51a
--- /dev/null
+++ b/e2e/single-page/tsconfig.json
@@ -0,0 +1,40 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es5",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "noEmitHelpers": true,
+ "noEmitOnError": true,
+ "skipLibCheck": true,
+ "lib": [
+ "es2017",
+ "dom",
+ "es6"
+ ],
+ "baseUrl": ".",
+ "paths": {
+ "~/*": [
+ "app/*"
+ ],
+ "*": [
+ "./node_modules/*"
+ ]
+ }
+ },
+ "include": [
+ "../../nativescript-angular-package",
+ "../../nativescript-angular",
+ "**/*"
+ ],
+ "exclude": [
+ "../../nativescript-angular-package/node_modules",
+ "../../nativescript-angular-package/**/*.d.ts",
+ "../../nativescript-angular/node_modules",
+ "../../nativescript-angular/**/*.d.ts",
+ "node_modules",
+ "platforms",
+ "**/*.aot",
+ "e2e"
+ ]
+}
\ No newline at end of file
diff --git a/e2e/tests-app-ng/.DS_Store b/e2e/tests-app-ng/.DS_Store
new file mode 100644
index 000000000..1fbac29ca
Binary files /dev/null and b/e2e/tests-app-ng/.DS_Store differ
diff --git a/e2e/tests-app-ng/.gitignore b/e2e/tests-app-ng/.gitignore
new file mode 100644
index 000000000..ecb5e0009
--- /dev/null
+++ b/e2e/tests-app-ng/.gitignore
@@ -0,0 +1,15 @@
+hooks
+node_modules
+platforms
+lib
+tags
+report
+
+app/**/*.js
+app/**/*.map
+package-lock.json
+
+# Webpack files
+tsconfig.esm.json
+webpack.config.js
+
diff --git a/e2e/tests-app-ng/.vscode/settings.json b/e2e/tests-app-ng/.vscode/settings.json
new file mode 100644
index 000000000..b17e73fe9
--- /dev/null
+++ b/e2e/tests-app-ng/.vscode/settings.json
@@ -0,0 +1,9 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+ "files.exclude": {
+ "**/*.js": {
+ "when": "$(basename).ts"
+ },
+ "**/*.js.map": true
+ }
+}
\ No newline at end of file
diff --git a/e2e/tests-app-ng/app/.DS_Store b/e2e/tests-app-ng/app/.DS_Store
new file mode 100644
index 000000000..7e319d2bf
Binary files /dev/null and b/e2e/tests-app-ng/app/.DS_Store differ
diff --git a/e2e/tests-app-ng/app/App_Resources/Android/app.gradle b/e2e/tests-app-ng/app/App_Resources/Android/app.gradle
new file mode 100644
index 000000000..725fb59b1
--- /dev/null
+++ b/e2e/tests-app-ng/app/App_Resources/Android/app.gradle
@@ -0,0 +1,15 @@
+// Add your native dependencies here:
+
+// Uncomment to add recyclerview-v7 dependency
+//dependencies {
+// compile 'com.android.support:recyclerview-v7:+'
+//}
+
+android {
+ defaultConfig {
+ generatedDensities = []
+ }
+ aaptOptions {
+ additionalParameters "--no-version-vectors"
+ }
+}
diff --git a/ng-sample/app/App_Resources/Android/AndroidManifest.xml b/e2e/tests-app-ng/app/App_Resources/Android/src/main/AndroidManifest.xml
similarity index 100%
rename from ng-sample/app/App_Resources/Android/AndroidManifest.xml
rename to e2e/tests-app-ng/app/App_Resources/Android/src/main/AndroidManifest.xml
diff --git a/tests/app/App_Resources/Android/drawable-hdpi/icon.png b/e2e/tests-app-ng/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png
similarity index 100%
rename from tests/app/App_Resources/Android/drawable-hdpi/icon.png
rename to e2e/tests-app-ng/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png
diff --git a/e2e/tests-app-ng/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png b/e2e/tests-app-ng/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png
new file mode 100755
index 000000000..ddfc17a71
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png b/e2e/tests-app-ng/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png
new file mode 100755
index 000000000..486e41091
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png differ
diff --git a/ng-sample/app/App_Resources/Android/drawable-nodpi/splashscreen.9.png b/e2e/tests-app-ng/app/App_Resources/Android/src/main/res/drawable-nodpi/splashscreen.9.png
similarity index 100%
rename from ng-sample/app/App_Resources/Android/drawable-nodpi/splashscreen.9.png
rename to e2e/tests-app-ng/app/App_Resources/Android/src/main/res/drawable-nodpi/splashscreen.9.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
rename to e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-50.png b/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-50.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-50.png
rename to e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-50.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-50@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-50@2x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-50@2x.png
rename to e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-50@2x.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-57.png b/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-57.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-57.png
rename to e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-57.png
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-57@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-57@2x.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-57@2x.png
rename to e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-57@2x.png
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ
diff --git a/ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-72.png b/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-72.png
similarity index 100%
rename from ng-sample/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-72.png
rename to e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-72.png
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png
new file mode 100755
index 000000000..4f69cb25b
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/Contents.json b/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/Contents.json
new file mode 100644
index 000000000..da4a164c9
--- /dev/null
+++ b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json b/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 000000000..4414bad08
--- /dev/null
+++ b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png b/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
new file mode 100644
index 000000000..9f1f6ce3e
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json b/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
new file mode 100644
index 000000000..4f4e9c506
--- /dev/null
+++ b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json b/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
new file mode 100644
index 000000000..23c0ffd7a
--- /dev/null
+++ b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Default-568h@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/Default-568h@2x.png
new file mode 100755
index 000000000..d7f17fcd2
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Default-568h@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Default-667h@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/Default-667h@2x.png
new file mode 100644
index 000000000..b88415405
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Default-667h@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Default-736h@3x.png b/e2e/tests-app-ng/app/App_Resources/iOS/Default-736h@3x.png
new file mode 100644
index 000000000..faab4b631
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Default-736h@3x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape-568h@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape-568h@2x.png
new file mode 100644
index 000000000..d21bd292b
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape-568h@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape-667h@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape-667h@2x.png
new file mode 100644
index 000000000..5305bbdf2
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape-667h@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape.png b/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape.png
new file mode 100755
index 000000000..3365ba3cd
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape@2x.png
new file mode 100755
index 000000000..a44945c1a
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape@3x.png b/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape@3x.png
new file mode 100644
index 000000000..e6dca6269
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Default-Landscape@3x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Default-Portrait.png b/e2e/tests-app-ng/app/App_Resources/iOS/Default-Portrait.png
new file mode 100755
index 000000000..1a5007962
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Default-Portrait.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Default-Portrait@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/Default-Portrait@2x.png
new file mode 100755
index 000000000..73d8b920f
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Default-Portrait@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Default.png b/e2e/tests-app-ng/app/App_Resources/iOS/Default.png
new file mode 100755
index 000000000..9f1f6ce3e
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Default.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Default@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/Default@2x.png
new file mode 100755
index 000000000..514fc5cde
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Default@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Icon-Small-50.png b/e2e/tests-app-ng/app/App_Resources/iOS/Icon-Small-50.png
new file mode 100755
index 000000000..4a62478f9
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Icon-Small-50.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Icon-Small-50@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/Icon-Small-50@2x.png
new file mode 100755
index 000000000..01ff7c16c
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Icon-Small-50@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Icon-Small.png b/e2e/tests-app-ng/app/App_Resources/iOS/Icon-Small.png
new file mode 100755
index 000000000..9e13a222a
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Icon-Small.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Icon-Small@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/Icon-Small@2x.png
new file mode 100755
index 000000000..89dd84cd8
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/Icon-Small@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/Info.plist b/e2e/tests-app-ng/app/App_Resources/iOS/Info.plist
new file mode 100644
index 000000000..ea3e3ea23
--- /dev/null
+++ b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/LaunchScreen.storyboard b/e2e/tests-app-ng/app/App_Resources/iOS/LaunchScreen.storyboard
new file mode 100644
index 000000000..2ad9471e1
--- /dev/null
+++ b/e2e/tests-app-ng/app/App_Resources/iOS/LaunchScreen.storyboard
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/build.xcconfig b/e2e/tests-app-ng/app/App_Resources/iOS/build.xcconfig
new file mode 100644
index 000000000..9d738435d
--- /dev/null
+++ b/e2e/tests-app-ng/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/tests-app-ng/app/App_Resources/iOS/icon-40.png b/e2e/tests-app-ng/app/App_Resources/iOS/icon-40.png
new file mode 100755
index 000000000..9b36ac4fe
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/icon-40.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/icon-40@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/icon-40@2x.png
new file mode 100755
index 000000000..8ce4b8899
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/icon-40@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/icon-60.png b/e2e/tests-app-ng/app/App_Resources/iOS/icon-60.png
new file mode 100755
index 000000000..d2e951831
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/icon-60.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/icon-60@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/icon-60@2x.png
new file mode 100755
index 000000000..693f67f66
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/icon-60@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/icon-72.png b/e2e/tests-app-ng/app/App_Resources/iOS/icon-72.png
new file mode 100755
index 000000000..556bdd688
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/icon-72.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/icon-72@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/icon-72@2x.png
new file mode 100755
index 000000000..4f69cb25b
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/icon-72@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/icon-76.png b/e2e/tests-app-ng/app/App_Resources/iOS/icon-76.png
new file mode 100755
index 000000000..1c659c62a
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/icon-76.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/icon-76@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/icon-76@2x.png
new file mode 100755
index 000000000..bcc126d30
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/icon-76@2x.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/icon.png b/e2e/tests-app-ng/app/App_Resources/iOS/icon.png
new file mode 100755
index 000000000..beea81999
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/icon.png differ
diff --git a/e2e/tests-app-ng/app/App_Resources/iOS/icon@2x.png b/e2e/tests-app-ng/app/App_Resources/iOS/icon@2x.png
new file mode 100755
index 000000000..c3dc7b035
Binary files /dev/null and b/e2e/tests-app-ng/app/App_Resources/iOS/icon@2x.png differ
diff --git a/e2e/tests-app-ng/app/action-bar/action-bar-first.component.ts b/e2e/tests-app-ng/app/action-bar/action-bar-first.component.ts
new file mode 100644
index 000000000..2eaac6aed
--- /dev/null
+++ b/e2e/tests-app-ng/app/action-bar/action-bar-first.component.ts
@@ -0,0 +1,64 @@
+import { Component } from "@angular/core";
+
+@Component({
+ selector: "first-action-bar",
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+ `,
+})
+export class FirstActionBarComponent {
+
+ public counterShare: number = 0;
+ public counterDelete: number = 0;
+ public show: boolean = true;
+
+ public get messageShare(): string {
+ if (this.counterShare === 1) {
+ return this.counterShare + " share tap";
+ } else {
+ return this.counterShare + " share taps";
+ }
+ }
+
+ public get messageDelete(): string {
+ if (this.counterDelete === 1) {
+ return this.counterDelete + " delete tap";
+ } else {
+ return this.counterDelete + " delete taps";
+ }
+ }
+
+ public onShare() {
+ this.counterShare++;
+ console.log("Share button tapped!");
+ }
+
+ public onTap() {
+ console.log("FirstComponent.Tapped!");
+ }
+
+ public onDelete() {
+ this.counterDelete++;
+ console.log("Delete button tapped!");
+ }
+}
diff --git a/e2e/tests-app-ng/app/action-bar/action-bar-nested.component.ts b/e2e/tests-app-ng/app/action-bar/action-bar-nested.component.ts
new file mode 100644
index 000000000..6e1f9a317
--- /dev/null
+++ b/e2e/tests-app-ng/app/action-bar/action-bar-nested.component.ts
@@ -0,0 +1,35 @@
+import { Component } from "@angular/core";
+
+@Component({
+ selector: "nested-component",
+ template: `
+
+
+
+
+
+
+
+
+
+
+ `,
+})
+export class NestedComponent {
+
+ public counter: number = 0;
+ public show: boolean = true;
+
+ public get message(): string {
+ if (this.counter === 1) {
+ return this.counter + " custom tap";
+ } else {
+ return this.counter + " custom taps";
+ }
+ }
+
+ public onTap() {
+ this.counter++;
+ console.log("NestedComponent.Tapped!");
+ }
+}
diff --git a/e2e/tests-app-ng/app/action-bar/action-bar-second.component.ts b/e2e/tests-app-ng/app/action-bar/action-bar-second.component.ts
new file mode 100644
index 000000000..c3d9e873f
--- /dev/null
+++ b/e2e/tests-app-ng/app/action-bar/action-bar-second.component.ts
@@ -0,0 +1,33 @@
+import { Component } from "@angular/core";
+
+@Component({
+ selector: "second-action-bar",
+ template: `
+
+
+
+
+
+
+
+
+
+ `,
+})
+export class SecondActionBarComponent {
+
+ public counter: number = 0;
+
+ public get message(): string {
+ if (this.counter === 1) {
+ return this.counter + " tap";
+ } else {
+ return this.counter + " taps";
+ }
+ }
+
+ public onTap() {
+ this.counter++;
+ console.log("SecondComponent.Tapped!");
+ }
+}
diff --git a/e2e/tests-app-ng/app/app.css b/e2e/tests-app-ng/app/app.css
new file mode 100644
index 000000000..067c61478
--- /dev/null
+++ b/e2e/tests-app-ng/app/app.css
@@ -0,0 +1,19 @@
+@import '~nativescript-theme-core/css/core.light.css';
+
+button {
+ font-size: 13;
+}
+
+label {
+ text-align: center;
+}
+
+.tab-view-container {
+ font-family: 'Courier'; /* Enabling this results in the error and shows a blank TabView */
+}
+
+.btnIcon {
+ font-family: 'FontAwesome';
+ font-size: 30
+}
+
diff --git a/e2e/tests-app-ng/app/app.module.ngfactory.d.ts b/e2e/tests-app-ng/app/app.module.ngfactory.d.ts
new file mode 100644
index 000000000..4ead4ec18
--- /dev/null
+++ b/e2e/tests-app-ng/app/app.module.ngfactory.d.ts
@@ -0,0 +1,5 @@
+/*
+* A dynamically generated module when compiled with AoT.
+*/
+// tslint:disable-next-line
+export const AppModuleNgFactory: any;
diff --git a/e2e/tests-app-ng/app/app.module.ts b/e2e/tests-app-ng/app/app.module.ts
new file mode 100644
index 000000000..0b0e641bd
--- /dev/null
+++ b/e2e/tests-app-ng/app/app.module.ts
@@ -0,0 +1,25 @@
+import { NgModule, NgModuleFactoryLoader, NO_ERRORS_SCHEMA } from "@angular/core";
+import { NativeScriptModule, NativeScriptRouterModule, NativeScriptFormsModule } from "@nativescript/angular";
+
+import { NavigationMainPageRouterComponent } from "./main/main-page-router-outlet";
+import { routableComponents, routes } from "./app.routes";
+import { NestedComponent } from "./action-bar/action-bar-nested.component";
+import { CustomTemplateComponent } from "./list-view/list-view-item-template.component";
+
+@NgModule({
+ declarations: [
+ NavigationMainPageRouterComponent,
+ NestedComponent,
+ CustomTemplateComponent,
+ ...routableComponents,
+ ],
+ bootstrap: [NavigationMainPageRouterComponent],
+ imports: [
+ NativeScriptModule,
+ NativeScriptFormsModule,
+ NativeScriptRouterModule,
+ NativeScriptRouterModule.forRoot(routes),
+ ],
+ schemas: [NO_ERRORS_SCHEMA],
+})
+export class AppModule { }
diff --git a/e2e/tests-app-ng/app/app.routes.ts b/e2e/tests-app-ng/app/app.routes.ts
new file mode 100644
index 000000000..c3f9276b9
--- /dev/null
+++ b/e2e/tests-app-ng/app/app.routes.ts
@@ -0,0 +1,198 @@
+import { Component } from "@angular/core";
+
+import { FirstActionBarComponent } from "./action-bar/action-bar-first.component";
+import { SecondActionBarComponent } from "./action-bar/action-bar-second.component";
+
+import { AppComponent } from "./template/app.component";
+
+import { FirstComponent } from "./router/router-outlet/first.component";
+import { SecondComponent } from "./router/router-outlet/second.component";
+import { NavigationComponent, NAVIGATION_SUBROUTES } from "./router/router-outlet/navigation.component";
+import { LazyNavigationComponent } from "./router/lazy-module-navigation/lazy-navigation.component";
+
+import { BindingComponent } from "./binding/binding-page.component";
+import { BottomNavigation } from "./bottom-navigation/bottom-navigation.component"
+
+import { ListViewComponent } from "./list-view/list-view-page.component";
+import { ListViewControlComponent } from "./list-view/list-view-item-template.component";
+import { ListViewAsyncPipeComponent } from "./list-view/async-pipe-template.component";
+import { ListViewMainPageComponent } from "./list-view/list-view-main-page.component";
+import { ListViewSegmentedBarPageComponent } from "./list-view/list-view-nested-segmented-bar-page.component";
+import { ListViewWithNestedTemplateComponent } from "./list-view/list-view-nested-template.component";
+import { ListViewMultipleTemplatesComponent } from "./list-view/multiple-templates.component";
+
+import { ListPickerMainPageComponent } from "./list-picker/list-picker-main-page.component";
+import { ListPickerComponent } from "./list-picker/list-picker.component";
+
+import {
+ ModalTestComponent,
+ ModalTestWithPushStrategyComponent,
+ ModalContentComponent
+} from "./modal/modal-dialogs/modal-dialog.component";
+import { ModalViewMainPageComponent } from "./modal/modal-view-main-page.component";
+import { LazyLoadModalComponent } from "./modal/lazy/lazy-load-modal.component";
+
+import { TabViewComponent } from "./tab-view/tab-view.component";
+import { TabsComponent } from "./tabs/tabs.component";
+
+import { NavigationOptionsComponent } from "./navigation-options/navigation-options.component";
+import { NavigationInfoComponent } from "./navigation-options/navigation-info.component";
+
+import { SegmentedBarMainPageComponent } from "./segmented-bar/segmented-bar-main-page.component";
+import { SegmentedBarIssue649Component } from "./segmented-bar/issue-649.component";
+
+import { DatePickerMainPageComponent } from "./date-picker/date-picker-main-page.component";
+import { DatePickerIssue324Component } from "./date-picker/issue-324.component";
+import { ButtonMainPageComponent } from "./button/button-main-page.component";
+import { ButtonTextAlignmentComponent } from "./button/button-text-alignment.component";
+
+import { MainComponent } from "./main/main-page-router-outlet";
+
+export const routableComponents = [
+ MainComponent,
+ ModalContentComponent,
+ AppComponent,
+
+ NavigationComponent,
+ LazyNavigationComponent,
+
+ FirstComponent,
+ SecondComponent,
+
+ FirstActionBarComponent,
+ SecondActionBarComponent,
+
+ BindingComponent,
+ BottomNavigation,
+
+ ListViewMainPageComponent,
+ ListViewComponent,
+ ListViewControlComponent,
+ ListViewAsyncPipeComponent,
+ ListViewSegmentedBarPageComponent,
+ ListViewWithNestedTemplateComponent,
+ ListViewMultipleTemplatesComponent,
+
+ ListPickerComponent,
+ ListPickerMainPageComponent,
+
+ ModalViewMainPageComponent,
+ ModalTestComponent,
+ ModalTestWithPushStrategyComponent,
+ LazyLoadModalComponent,
+
+ TabViewComponent,
+ TabsComponent,
+
+ NavigationOptionsComponent,
+ NavigationInfoComponent,
+ SegmentedBarMainPageComponent,
+ SegmentedBarIssue649Component,
+ DatePickerMainPageComponent,
+ DatePickerIssue324Component,
+ ButtonMainPageComponent,
+ ButtonTextAlignmentComponent,
+];
+
+// Set `isNavigatable: true` if the page is a main page to other sub pages
+export const routes = [
+ { path: "", pathMatch: "full", redirectTo: "main" },
+ { path: "main", component: MainComponent, data: { title: "" } },
+ // { path: "", component: ModalContentComponent, data: { title: "" } },
+ { path: "template", component: AppComponent, data: { title: "Template", isNavigatable: true } },
+
+ {
+ path: "router",
+ component: NavigationComponent,
+ children: NAVIGATION_SUBROUTES,
+ data: { title: "Router", isNavigatable: true }
+ },
+ { path: "lazy-router", component: LazyNavigationComponent, data: { title: "Lazy Router", isNavigatable: true } },
+
+ { path: "first", component: FirstComponent, data: { title: "First", isNavigatable: true } },
+ { path: "second", component: SecondComponent, data: { title: "Second", isNavigatable: true } },
+
+ {
+ path: "first-action-bar",
+ component: FirstActionBarComponent,
+ data: { title: "ActionBar1", isNavigatable: true }
+ },
+ {
+ path: "second-action-bar",
+ component: SecondActionBarComponent,
+ data: { title: "ActionBar2", isNavigatable: true }
+ },
+
+ { path: "binding", component: BindingComponent, data: { title: "Binding", isNavigatable: true } },
+ { path: "bottom-navigation", component: BottomNavigation, data: { title: "BottomNavigation", isNavigatable: true } },
+
+ {
+ path: "ListViewExamples",
+ component: ListViewMainPageComponent,
+ data: { title: "ListViewExamples", isNavigatable: true }
+ },
+ { path: "ListViewExamples/commonTemplate", component: ListViewComponent, data: { title: "commonTemplate" } },
+ { path: "ListViewExamples/customTemplate", component: ListViewControlComponent, data: { title: "customTemplate" } },
+ { path: "listView/asyncPipeTemplate", component: ListViewAsyncPipeComponent, data: { title: "asyncPipeTemplate" } },
+ {
+ path: "ListViewExamples/segmentedBarTemplate",
+ component: ListViewSegmentedBarPageComponent,
+ data: { title: "segmentedBarTemplate" } },
+ {
+ path: "listView/nestedTemplate",
+ component: ListViewWithNestedTemplateComponent,
+ data: { title: "nestedTemplate" }
+ },
+ {
+ path: "listView/multiple-templates",
+ component: ListViewMultipleTemplatesComponent,
+ data: { title: "multipleTemplates" }
+ },
+ {
+ path: "listPicker",
+ component: ListPickerMainPageComponent,
+ data: { title: "ListPicker", isNavigatable: true }
+ },
+ {
+ path: "listPicker/list-picker",
+ component: ListPickerComponent,
+ data: { title: "ListPicker", isNavigatable: false }
+ },
+ { path: "modal", component: ModalViewMainPageComponent, data: { title: "Modals", isNavigatable: true } },
+ { path: "modal/modal-dialogs", component: ModalTestComponent, data: { title: "modal" } },
+ {
+ path: "modal/modal-dialogs-push",
+ component: ModalTestWithPushStrategyComponent,
+ data: { title: "modal(onPush)" }
+ },
+ { path: "modal/lazy", component: LazyLoadModalComponent, data: { title: "modal(lazy)" } },
+
+ { path: "tab-view", component: TabViewComponent, data: { title: "tab-view", isNavigatable: true } },
+ { path: "tabs", component: TabsComponent, data: { title: "Tabs", isNavigatable: true } },
+
+ { path: "nav-options", component: NavigationOptionsComponent, data: { title: "nav-options", isNavigatable: true } },
+ { path: "nav-info", component: NavigationInfoComponent, data: { title: "nav-info" } },
+
+ {
+ path: "segmented-bar",
+ component: SegmentedBarMainPageComponent,
+ data: { title: "SegmentedBar", isNavigatable: true }
+ },
+ { path: "segmented-bar/issue-649", component: SegmentedBarIssue649Component, data: { title: "issue-649" } },
+
+ { path: "date-picker", component: DatePickerMainPageComponent, data: { title: "DatePicker", isNavigatable: true } },
+ { path: "date-picker/issue-324", component: DatePickerIssue324Component, data: { title: "issue-324" } },
+
+ { path: "button", component: ButtonMainPageComponent, data: { title: "Button", isNavigatable: true } },
+ {
+ path: "button/button-text-alignment",
+ component: ButtonTextAlignmentComponent,
+ data: { title: "button-text-alignment" }
+ },
+
+ // Needed for AoT compilation
+ {
+ path: "lazy",
+ loadChildren: () => import("./lazy/lazy.module").then(m => m.LazyModule)
+ },
+];
diff --git a/e2e/tests-app-ng/app/binding/binding-page.component.ts b/e2e/tests-app-ng/app/binding/binding-page.component.ts
new file mode 100644
index 000000000..7cf7d526a
--- /dev/null
+++ b/e2e/tests-app-ng/app/binding/binding-page.component.ts
@@ -0,0 +1,99 @@
+import { Component } from "@angular/core";
+
+@Component({
+ selector: "binding",
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+})
+
+export class BindingComponent {
+ private _oneWayDataBinding: string;
+ private _twoWayDataBinding: string;
+ private _curlyBracket: string;
+ private _result: string;
+ public completedDate: Date = new Date(2016, 5, 3);
+ labelShow = true;
+
+ constructor() {
+ this.refresh();
+ }
+
+ get oneWayDataBinding() {
+ return this._oneWayDataBinding;
+ }
+
+ get twoWayDataBinding() {
+ return this._twoWayDataBinding;
+ }
+
+ set twoWayDataBinding(value: string) {
+ this._twoWayDataBinding = value;
+ }
+
+ get curlyBracket() {
+ return this._curlyBracket;
+ }
+
+ set curlyBracket(value: string) {
+ this._curlyBracket = value;
+ }
+
+ get results() {
+ return this._result;
+ }
+
+ toggle() {
+ this.labelShow = !this.labelShow;
+ console.log(this.labelShow);
+ }
+
+ changeValues() {
+ this._oneWayDataBinding = this.twoWayDataBinding;
+ this._curlyBracket = this.twoWayDataBinding;
+ this._twoWayDataBinding = this.twoWayDataBinding;
+ }
+
+ getValues() {
+ this._result = "";
+ this._result += "one-way value is " + this.oneWayDataBinding + "; \n";
+ this._result += "two-way value is " + this.twoWayDataBinding + "; \n";
+ this._result += "curly-bracket value is " + this.curlyBracket + "; \n";
+ }
+
+ private refresh() {
+ this._oneWayDataBinding = "1";
+ this._twoWayDataBinding = "2";
+ this._curlyBracket = "5";
+ this._result = "";
+ }
+}
diff --git a/e2e/tests-app-ng/app/bottom-navigation/bottom-navigation.component.ts b/e2e/tests-app-ng/app/bottom-navigation/bottom-navigation.component.ts
new file mode 100644
index 000000000..5c547d9f6
--- /dev/null
+++ b/e2e/tests-app-ng/app/bottom-navigation/bottom-navigation.component.ts
@@ -0,0 +1,68 @@
+import { Component, OnInit } from "@angular/core";
+
+@Component({
+ selector: "bottom-navigation-component",
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+})
+
+export class BottomNavigation implements OnInit {
+
+ public ngOnInit(): void { }
+
+ goTo(bottomNavigation: any, index: number) {
+ bottomNavigation.selectedIndex = index;
+ }
+
+}
diff --git a/e2e/tests-app-ng/app/button/button-main-page.component.ts b/e2e/tests-app-ng/app/button/button-main-page.component.ts
new file mode 100644
index 000000000..eba145679
--- /dev/null
+++ b/e2e/tests-app-ng/app/button/button-main-page.component.ts
@@ -0,0 +1,11 @@
+import { Component } from "@angular/core";
+
+@Component({
+ selector: "main-button",
+ template: `
+
+
+
+ `,
+})
+export class ButtonMainPageComponent { }
diff --git a/e2e/tests-app-ng/app/button/button-text-alignment.component.ts b/e2e/tests-app-ng/app/button/button-text-alignment.component.ts
new file mode 100644
index 000000000..ddd4ec7d6
--- /dev/null
+++ b/e2e/tests-app-ng/app/button/button-text-alignment.component.ts
@@ -0,0 +1,52 @@
+import { Observable as RxObservable } from "rxjs";
+import {
+ Component,
+ Input,
+ ChangeDetectionStrategy
+} from "@angular/core";
+
+@Component({
+ styles: [ ".odd { background-color : yellow } " +
+ ".even{ background-color : green } " +
+ ".test{ font-family:FontAwesome; font-size:70 }"],
+ template: `
+
+
+
+
+
+ `
+})
+export class ButtonTextAlignmentComponent {
+
+ public get test(): string {
+ return "\ntest";
+ }
+}
diff --git a/e2e/tests-app-ng/app/date-picker/date-picker-main-page.component.ts b/e2e/tests-app-ng/app/date-picker/date-picker-main-page.component.ts
new file mode 100644
index 000000000..1642196e2
--- /dev/null
+++ b/e2e/tests-app-ng/app/date-picker/date-picker-main-page.component.ts
@@ -0,0 +1,10 @@
+import { Component } from "@angular/core";
+
+@Component({
+ template: `
+
+
+
+ `,
+})
+export class DatePickerMainPageComponent { }
diff --git a/e2e/tests-app-ng/app/date-picker/issue-324.component.ts b/e2e/tests-app-ng/app/date-picker/issue-324.component.ts
new file mode 100644
index 000000000..70aa9192b
--- /dev/null
+++ b/e2e/tests-app-ng/app/date-picker/issue-324.component.ts
@@ -0,0 +1,23 @@
+import { Component } from "@angular/core";
+
+@Component({
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+})
+
+export class DatePickerIssue324Component {
+}
diff --git a/e2e/tests-app-ng/app/fonts/FontAwesome.ttf b/e2e/tests-app-ng/app/fonts/FontAwesome.ttf
new file mode 100644
index 000000000..35acda2fa
Binary files /dev/null and b/e2e/tests-app-ng/app/fonts/FontAwesome.ttf differ
diff --git a/e2e/tests-app-ng/app/fonts/Pacifico.ttf b/e2e/tests-app-ng/app/fonts/Pacifico.ttf
new file mode 100644
index 000000000..6d47cdc9a
Binary files /dev/null and b/e2e/tests-app-ng/app/fonts/Pacifico.ttf differ
diff --git a/e2e/tests-app-ng/app/fonts/Sofia.otf b/e2e/tests-app-ng/app/fonts/Sofia.otf
new file mode 100644
index 000000000..1f76d2d58
Binary files /dev/null and b/e2e/tests-app-ng/app/fonts/Sofia.otf differ
diff --git a/e2e/tests-app-ng/app/lazy/lazy.component.css b/e2e/tests-app-ng/app/lazy/lazy.component.css
new file mode 100644
index 000000000..eec6173f7
--- /dev/null
+++ b/e2e/tests-app-ng/app/lazy/lazy.component.css
@@ -0,0 +1,8 @@
+
+.system-background-color-stack-layout {
+ background-color: white;
+}
+
+.ns-dark .system-background-color-stack-layout {
+ background-color: gray;
+}
diff --git a/e2e/tests-app-ng/app/lazy/lazy.component.html b/e2e/tests-app-ng/app/lazy/lazy.component.html
new file mode 100644
index 000000000..82d691f92
--- /dev/null
+++ b/e2e/tests-app-ng/app/lazy/lazy.component.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
diff --git a/e2e/tests-app-ng/app/lazy/lazy.component.ts b/e2e/tests-app-ng/app/lazy/lazy.component.ts
new file mode 100644
index 000000000..bb8d0e01c
--- /dev/null
+++ b/e2e/tests-app-ng/app/lazy/lazy.component.ts
@@ -0,0 +1,30 @@
+import { Component } from "@angular/core";
+
+import { RouterExtensions, ModalDialogParams } from "@nativescript/angular";
+
+@Component({
+ selector: "ns-lazy",
+ moduleId: module.id,
+ templateUrl: "./lazy.component.html",
+ styleUrls: ["./lazy.component.css"]
+})
+export class LazyComponent {
+ public isModal: boolean;
+
+ constructor(
+ private router: RouterExtensions,
+ private params: ModalDialogParams
+ ) {
+ if (params.context.isModal) {
+ this.isModal = true;
+ }
+ }
+
+ public close() {
+ if (this.isModal) {
+ this.params.closeCallback();
+ } else {
+ this.router.back();
+ }
+ }
+}
diff --git a/e2e/tests-app-ng/app/lazy/lazy.module.ts b/e2e/tests-app-ng/app/lazy/lazy.module.ts
new file mode 100644
index 000000000..eea1ca005
--- /dev/null
+++ b/e2e/tests-app-ng/app/lazy/lazy.module.ts
@@ -0,0 +1,37 @@
+import { NativeScriptCommonModule, NativeScriptRouterModule, ModalDialogParams } from "@nativescript/angular";
+
+import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+import { Routes } from "@angular/router";
+
+import { LazyComponent } from "./lazy.component";
+
+export function modalParamsFactory() {
+ return new ModalDialogParams({}, null);
+}
+
+const routes: Routes = [
+ {
+ path: "",
+ component: LazyComponent
+ }
+];
+
+@NgModule({
+ imports: [
+ NativeScriptCommonModule,
+ NativeScriptRouterModule.forChild(routes),
+ ],
+ declarations: [
+ LazyComponent
+ ],
+ entryComponents: [
+ LazyComponent
+ ],
+ providers: [
+ // allows same component to be routed to
+ // or lazily loaded via modal
+ { provide: ModalDialogParams, useFactory: modalParamsFactory }
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+})
+export class LazyModule { }
diff --git a/e2e/tests-app-ng/app/list-picker/list-picker-main-page.component.ts b/e2e/tests-app-ng/app/list-picker/list-picker-main-page.component.ts
new file mode 100644
index 000000000..27b3111e6
--- /dev/null
+++ b/e2e/tests-app-ng/app/list-picker/list-picker-main-page.component.ts
@@ -0,0 +1,11 @@
+import { Component } from "@angular/core";
+
+@Component({
+ selector: "main-list-picker",
+ template: `
+
+
+
+ `,
+})
+export class ListPickerMainPageComponent { }
diff --git a/e2e/tests-app-ng/app/list-picker/list-picker.component.ts b/e2e/tests-app-ng/app/list-picker/list-picker.component.ts
new file mode 100644
index 000000000..d6eff5efd
--- /dev/null
+++ b/e2e/tests-app-ng/app/list-picker/list-picker.component.ts
@@ -0,0 +1,42 @@
+import {
+ Component,
+ Input,
+ ChangeDetectionStrategy
+} from "@angular/core";
+
+@Component({
+ moduleId: module.id,
+ selector: "list",
+ styleUrls: ["./list-picker.css"],
+ template: `
+
+
+
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+
+export class ListPickerComponent {
+ public pokemons: Array;
+ public picked: string;
+ public selectedIndex: number;
+
+ private pokemonList = ["Bulbasaur", "Parasect", "Venonat", "Venomoth", "Diglett",
+ "Dugtrio", "Meowth", "Persian", "Psyduck", "Arcanine", "Poliwrath", "Machoke"];
+
+ constructor() {
+ this.pokemons = [];
+
+ for (let i = 0; i < this.pokemonList.length; i++) {
+ this.pokemons.push(this.pokemonList[i]);
+ }
+ }
+
+ public selectedIndexChanged(picker) {
+ console.log("picker selection: " + picker.selectedIndex);
+ this.picked = this.pokemons[picker.selectedIndex];
+ }
+}
diff --git a/e2e/tests-app-ng/app/list-picker/list-picker.css b/e2e/tests-app-ng/app/list-picker/list-picker.css
new file mode 100644
index 000000000..314c79fa9
--- /dev/null
+++ b/e2e/tests-app-ng/app/list-picker/list-picker.css
@@ -0,0 +1,4 @@
+.listPicker {
+ color: green;
+ background-color: black;
+}
diff --git a/e2e/tests-app-ng/app/list-view/async-pipe-template.component.ts b/e2e/tests-app-ng/app/list-view/async-pipe-template.component.ts
new file mode 100644
index 000000000..fab165dd4
--- /dev/null
+++ b/e2e/tests-app-ng/app/list-view/async-pipe-template.component.ts
@@ -0,0 +1,55 @@
+import { Observable as RxObservable } from "rxjs";
+import { Component, Input, ChangeDetectionStrategy } from "@angular/core";
+import { DataItem } from "./data-item";
+
+@Component({
+ selector: "list-test-async",
+ template: `
+
+
+
+
+
+
+
+
+
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ListViewAsyncPipeComponent {
+ public myItems: RxObservable>;
+ public output: string;
+
+ constructor() {
+ let items = [];
+ for (let i = 0; i < 3; i++) {
+ items.push(new DataItem(i, "data item " + i));
+ }
+
+ let subscr;
+ this.myItems = RxObservable.create(subscriber => {
+ subscr = subscriber;
+ subscriber.next(items);
+ return function () {
+ console.log("Unsubscribe called!!!");
+ this.output = "Unsubscribe called!!!";
+ };
+ });
+
+ let counter = 2;
+ const intervalId = setInterval(() => {
+ counter++;
+ items.push(new DataItem(counter, "data item " + counter));
+ subscr.next(items);
+ if (counter == 11) {
+ clearInterval(intervalId);
+ }
+ }, 1000);
+
+ setTimeout(() => {
+ clearInterval(intervalId);
+ }, 11000);
+ }
+}
diff --git a/e2e/tests-app-ng/app/list-view/data-item.ts b/e2e/tests-app-ng/app/list-view/data-item.ts
new file mode 100644
index 000000000..f9774a293
--- /dev/null
+++ b/e2e/tests-app-ng/app/list-view/data-item.ts
@@ -0,0 +1,3 @@
+export class DataItem {
+ constructor(public id: number, public name: string) { }
+}
diff --git a/e2e/tests-app-ng/app/list-view/list-view-item-template.component.ts b/e2e/tests-app-ng/app/list-view/list-view-item-template.component.ts
new file mode 100644
index 000000000..e0e31713b
--- /dev/null
+++ b/e2e/tests-app-ng/app/list-view/list-view-item-template.component.ts
@@ -0,0 +1,38 @@
+import { Component, Input, ChangeDetectionStrategy } from "@angular/core";
+
+@Component({
+ selector: "custom-template",
+ template: `
+
+
+
+ `
+})
+export class CustomTemplateComponent {
+ @Input() data: any;
+}
+
+@Component({
+ selector: "list-test",
+ template: `
+
+
+
+
+
+
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ListViewControlComponent {
+ public myItems: Array;
+ private counter: number;
+
+ constructor() {
+ const list = [{ "text": "a" }, { "text": "b" }];
+ const list1 = [{ "text": "c" }, { "text": "d" }];
+ this.myItems = [{ "list": list }, { "list": list1 }];
+ }
+}
diff --git a/e2e/tests-app-ng/app/list-view/list-view-main-page.component.ts b/e2e/tests-app-ng/app/list-view/list-view-main-page.component.ts
new file mode 100644
index 000000000..368c103da
--- /dev/null
+++ b/e2e/tests-app-ng/app/list-view/list-view-main-page.component.ts
@@ -0,0 +1,16 @@
+import { Component } from "@angular/core";
+
+@Component({
+ selector: "main-listview",
+ template: `
+
+
+
+
+
+
+
+
+ `,
+})
+export class ListViewMainPageComponent { }
diff --git a/e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts b/e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts
new file mode 100644
index 000000000..74e161d7b
--- /dev/null
+++ b/e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts
@@ -0,0 +1,125 @@
+import { Component, ViewChild, ElementRef, OnInit } from "@angular/core";
+import { SegmentedBarItem, SegmentedBar, ListView, EventData } from "@nativescript/core";
+
+interface DataItem {
+ id: number;
+ name: string;
+ type: string;
+}
+
+@Component({
+ moduleId: module.id,
+ selector: "segmented-bar-list-test",
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+})
+export class ListViewSegmentedBarPageComponent implements OnInit {
+ public displayedItems: Array = [];
+ public items: Array;
+ public segmentedBarItems: SegmentedBarItem[] = this.createSegmentedBarItems();
+
+ @ViewChild("listViewTest", { static: false })
+ private listViewTest?: ElementRef;
+
+ constructor() {
+ this.items = [];
+
+ for (let i = 0; i < 20; i++) {
+ const type = "dataItemTemplate";
+
+ this.items.push({
+ id: i,
+ name: `data item ${i}`,
+ type: type,
+ });
+ }
+ }
+
+ public ngOnInit() {
+ this.displayedItems = this.updateItems(true);
+ }
+
+ public onButtonPress() {
+ // tslint:disable-next-line: no-unused-expression
+ new Promise((resolve) => {
+ setTimeout(() => {
+ if (this.listViewTest) {
+ console.log("Scrolling to the top of the list...");
+ const listView = this.listViewTest.nativeElement as ListView;
+ listView.scrollToIndex(0);
+ }
+ resolve();
+ }, 150);
+ });
+
+ this.displayedItems = this.updateItems(false);
+ }
+
+ public onSegmentedBarPress(args: EventData) {
+ if (args && args.object) {
+ const segmentBar = args.object as SegmentedBar;
+ const selectedOdd = segmentBar.selectedIndex === 0;
+ this.displayedItems = this.updateItems(selectedOdd);
+ }
+ }
+
+ public createSegmentedBarItems() {
+ const itemOdd = new SegmentedBarItem();
+ itemOdd.title = "Odd Items";
+ const itemEven = new SegmentedBarItem();
+ itemEven.title = "Even Items";
+ return [itemOdd, itemEven];
+ }
+
+ public templateSelector(item: DataItem): string {
+ return item.type;
+ }
+
+ private updateItems(odd: boolean) {
+ const items = [
+ {
+ id: -1,
+ name: "Segmented Bar",
+ type: "segmentedBarTemplate",
+ },
+ ...(odd
+ ? this.items.filter((item) => item.id % 2 === 1)
+ : this.items.filter((item) => item.id % 2 === 0)),
+ {
+ id: 999,
+ name: "Refresh test",
+ type: "buttonTemplate",
+ },
+ ];
+ return items;
+ }
+}
diff --git a/e2e/tests-app-ng/app/list-view/list-view-nested-template.component.ts b/e2e/tests-app-ng/app/list-view/list-view-nested-template.component.ts
new file mode 100644
index 000000000..a1d73e47f
--- /dev/null
+++ b/e2e/tests-app-ng/app/list-view/list-view-nested-template.component.ts
@@ -0,0 +1,22 @@
+import { Component, ChangeDetectionStrategy } from "@angular/core";
+
+@Component({
+ selector: "list-test-nested",
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ListViewWithNestedTemplateComponent {
+ public myItems: string[] = ["one", "two"];
+}
diff --git a/e2e/tests-app-ng/app/list-view/list-view-page.component.ts b/e2e/tests-app-ng/app/list-view/list-view-page.component.ts
new file mode 100644
index 000000000..0a92cfdcc
--- /dev/null
+++ b/e2e/tests-app-ng/app/list-view/list-view-page.component.ts
@@ -0,0 +1,41 @@
+import { Component, Input, ChangeDetectionStrategy } from "@angular/core";
+import { DataItem } from "./data-item";
+
+@Component({
+ moduleId: module.id,
+ selector: "list-test",
+ styleUrls: ["./list-view-page.css"],
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+ `
+})
+
+export class ListViewComponent {
+ public myItems: Array;
+ public results: string;
+ private counter: number;
+
+ constructor() {
+ this.results = "";
+ this.myItems = [];
+ this.counter = 0;
+ for (let i = 0; i < 5; i++) {
+ this.myItems.push(new DataItem(i, "data item " + i));
+ this.counter = i;
+ }
+ }
+
+ public onItemTap(args) {
+ this.results += "ItemTapped: " + args.index + "; \n";
+ }
+}
diff --git a/e2e/tests-app-ng/app/list-view/list-view-page.css b/e2e/tests-app-ng/app/list-view/list-view-page.css
new file mode 100644
index 000000000..d29d67f85
--- /dev/null
+++ b/e2e/tests-app-ng/app/list-view/list-view-page.css
@@ -0,0 +1,7 @@
+.odd {
+ background-color: red;
+}
+
+.even {
+ background-color: blue;
+}
\ No newline at end of file
diff --git a/e2e/tests-app-ng/app/list-view/multiple-templates.component.ts b/e2e/tests-app-ng/app/list-view/multiple-templates.component.ts
new file mode 100644
index 000000000..00c92f288
--- /dev/null
+++ b/e2e/tests-app-ng/app/list-view/multiple-templates.component.ts
@@ -0,0 +1,36 @@
+import { Observable as RxObservable } from "rxjs";
+import { Component, Input, ChangeDetectionStrategy } from "@angular/core";
+import { DataItem } from "./data-item";
+
+@Component({
+ styles: [".odd { background-color : yellow } .even{ background-color : green }"],
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class ListViewMultipleTemplatesComponent {
+ public myItems: Array = [];
+
+ constructor() {
+ for (let i = 0; i < 12; i++) {
+ this.myItems.push(new DataItem(i, "data item " + i));
+ }
+ }
+}
diff --git a/e2e/tests-app-ng/app/main.ts b/e2e/tests-app-ng/app/main.ts
new file mode 100644
index 000000000..60b027e0c
--- /dev/null
+++ b/e2e/tests-app-ng/app/main.ts
@@ -0,0 +1,8 @@
+import { platformNativeScriptDynamic } from "@nativescript/angular";
+
+import { AppModule } from "./app.module";
+import { Trace } from "@nativescript/core";
+
+Trace.enable();
+
+platformNativeScriptDynamic().bootstrapModule(AppModule);
diff --git a/e2e/tests-app-ng/app/main/main-page-router-outlet.ts b/e2e/tests-app-ng/app/main/main-page-router-outlet.ts
new file mode 100644
index 000000000..cf2d31525
--- /dev/null
+++ b/e2e/tests-app-ng/app/main/main-page-router-outlet.ts
@@ -0,0 +1,63 @@
+import { Component } from "@angular/core";
+import * as platform from "@nativescript/core/platform";
+
+@Component({
+ selector: "main-page",
+ styles: [
+ ".main-btn{" +
+ "margin-right:5; margin-bottom:5;" +
+ "padding-left:5; padding-right:5;" +
+ "background-color:#28a745;color:white;" +
+ "border-radius:5;}"],
+ template: `
+
+
+
+ `,
+})
+export class MainComponent {
+ private _pages = [];
+ private _routes = require("../app.routes").routes;
+ private _orientation: string = "vertical";
+
+ constructor() {
+ const navigatableRoutes = this._routes.filter((item) => {
+ return item.data && item.data.isNavigatable && item.path;
+ });
+
+ const examples = navigatableRoutes.sort((a, b) => {
+ if (a.data.title > b.data.title) {
+ return 1;
+ }
+
+ if (a.data.title < b.data.title) {
+ return -1;
+ }
+
+ return 0;
+ });
+
+ this._pages = examples;
+ if (platform.isAndroid) {
+ this._orientation = "horizontal";
+ }
+ }
+
+ get pages() {
+ return this._pages;
+ }
+
+ get orientation() {
+ return this._orientation;
+ }
+}
+
+@Component({
+ selector: "navigation-main",
+ template: ``
+})
+export class NavigationMainPageRouterComponent { }
diff --git a/e2e/tests-app-ng/app/modal/lazy/lazy-load-modal.component.ts b/e2e/tests-app-ng/app/modal/lazy/lazy-load-modal.component.ts
new file mode 100644
index 000000000..3571090ab
--- /dev/null
+++ b/e2e/tests-app-ng/app/modal/lazy/lazy-load-modal.component.ts
@@ -0,0 +1,37 @@
+import {
+ Component,
+ ComponentFactory,
+ NgModuleFactory,
+ NgModuleFactoryLoader,
+ ViewContainerRef,
+} from "@angular/core";
+
+import { NSModuleFactoryLoader, ModalDialogService } from "@nativescript/angular";
+
+import { LazyComponent } from "../../lazy/lazy.component";
+
+@Component({
+ template: `
+
+ `
+})
+export class LazyLoadModalComponent {
+ constructor(
+ private moduleLoader: NgModuleFactoryLoader,
+ private vcRef: ViewContainerRef,
+ private modalService: ModalDialogService
+ ) { }
+
+ public openModal() {
+ this.moduleLoader.load("./lazy/lazy.module#LazyModule")
+ .then((module: NgModuleFactory) => {
+ const moduleRef = module.create(this.vcRef.parentInjector);
+
+ this.modalService.showModal(LazyComponent, {
+ moduleRef,
+ viewContainerRef: this.vcRef,
+ context: { isModal: true }
+ });
+ });
+ }
+}
diff --git a/e2e/tests-app-ng/app/modal/modal-dialogs/modal-dialog.component.ts b/e2e/tests-app-ng/app/modal/modal-dialogs/modal-dialog.component.ts
new file mode 100644
index 000000000..eb2c0f84a
--- /dev/null
+++ b/e2e/tests-app-ng/app/modal/modal-dialogs/modal-dialog.component.ts
@@ -0,0 +1,109 @@
+import {
+ Component,
+ ChangeDetectionStrategy,
+ ViewContainerRef,
+ ChangeDetectorRef
+} from "@angular/core";
+import {
+ ModalDialogService,
+ ModalDialogOptions,
+ ModalDialogParams
+} from "@nativescript/angular";
+
+@Component({
+ selector: "modal-content",
+ template: `
+
+
+
+
+
+
+
+
+
+ `
+})
+export class ModalContentComponent {
+ public prompt: string;
+ constructor(private params: ModalDialogParams) {
+ this.prompt = params.context.message;
+ }
+
+ public close(res: string) {
+ this.params.closeCallback(res);
+ }
+}
+
+@Component({
+ selector: "modal-test",
+ template: `
+
+
+
+
+
+ `
+})
+export class ModalTestComponent {
+ public result: string = "---";
+
+ constructor(private modal: ModalDialogService, private vcRef: ViewContainerRef) { }
+
+ public showModal() {
+ const options: ModalDialogOptions = {
+ viewContainerRef: this.vcRef,
+ context: { message: "Hello from dialog!" },
+ fullscreen: true
+ };
+
+ this.modal.showModal(ModalContentComponent, options).then((res: string) => {
+ this.result = res || "empty result";
+ });
+ }
+
+ public showModalAsync() {
+ setTimeout(() => {
+ this.showModal();
+ }, 10);
+ }
+}
+
+@Component({
+ selector: "modal-test-on-push",
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ template: `
+
+
+
+
+
+ `
+})
+export class ModalTestWithPushStrategyComponent {
+ public result: string = "---";
+
+ constructor(
+ private modal: ModalDialogService,
+ private vcRef: ViewContainerRef,
+ private cdRef: ChangeDetectorRef) { }
+
+ public showModal() {
+ const options: ModalDialogOptions = {
+ viewContainerRef: this.vcRef,
+ context: { message: "Hello from dialog (onPush)!" },
+ fullscreen: true
+ };
+
+ this.modal.showModal(ModalContentComponent, options).then((res: string) => {
+ this.result = res || "empty result";
+ this.cdRef.markForCheck();
+ });
+ }
+
+ public showModalAsync() {
+ setTimeout(() => {
+ this.showModal();
+ }, 10);
+ }
+}
diff --git a/e2e/tests-app-ng/app/modal/modal-view-main-page.component.ts b/e2e/tests-app-ng/app/modal/modal-view-main-page.component.ts
new file mode 100644
index 000000000..79a125ee5
--- /dev/null
+++ b/e2e/tests-app-ng/app/modal/modal-view-main-page.component.ts
@@ -0,0 +1,13 @@
+import { Component } from "@angular/core";
+
+@Component({
+ selector: "main-modal",
+ template: `
+
+
+
+
+
+ `,
+})
+export class ModalViewMainPageComponent { }
diff --git a/e2e/tests-app-ng/app/navigation-options/navigation-info.component.ts b/e2e/tests-app-ng/app/navigation-options/navigation-info.component.ts
new file mode 100644
index 000000000..c949dac24
--- /dev/null
+++ b/e2e/tests-app-ng/app/navigation-options/navigation-info.component.ts
@@ -0,0 +1,28 @@
+import { Component } from "@angular/core";
+import { NSLocationStrategy } from "@nativescript/angular";
+import { Frame } from "@nativescript/core/ui/frame";
+
+@Component({
+ selector: "nav-info",
+ template: `
+
+
+
+
+
+
+
+ `
+})
+export class NavigationInfoComponent {
+ public frameStack: number = -1;
+ public locationStack: number = -1;
+
+ constructor(private frame: Frame, private strategy: NSLocationStrategy) { }
+
+ update() {
+ // If history is cleared: frameStack = 0, locationStack = 1
+ this.frameStack = this.frame.backStack.length;
+ this.locationStack = this.strategy.findOutlet("primary").states.length;
+ }
+}
diff --git a/e2e/tests-app-ng/app/navigation-options/navigation-options.component.ts b/e2e/tests-app-ng/app/navigation-options/navigation-options.component.ts
new file mode 100644
index 000000000..06bdc3368
--- /dev/null
+++ b/e2e/tests-app-ng/app/navigation-options/navigation-options.component.ts
@@ -0,0 +1,27 @@
+import { Component } from "@angular/core";
+import { RouterExtensions } from "@nativescript/angular";
+
+@Component({
+ selector: "nav-options",
+ template: `
+
+
+
+
+ `
+})
+
+export class NavigationOptionsComponent {
+
+ constructor(private routerExtensions: RouterExtensions) { }
+
+ flipToNextPage() {
+ this.routerExtensions.navigate(["/nav-info"], {
+ transition: {
+ name: "flip",
+ duration: 17000,
+ curve: "linear"
+ }
+ });
+ }
+}
diff --git a/e2e/tests-app-ng/app/router/lazy-module-navigation/lazy-navigation.component.ts b/e2e/tests-app-ng/app/router/lazy-module-navigation/lazy-navigation.component.ts
new file mode 100644
index 000000000..dde337885
--- /dev/null
+++ b/e2e/tests-app-ng/app/router/lazy-module-navigation/lazy-navigation.component.ts
@@ -0,0 +1,7 @@
+import { Component } from "@angular/core";
+
+@Component({
+ template: ``,
+})
+export class LazyNavigationComponent {
+}
diff --git a/e2e/tests-app-ng/app/router/router-outlet/first.component.ts b/e2e/tests-app-ng/app/router/router-outlet/first.component.ts
new file mode 100644
index 000000000..2e421dfcb
--- /dev/null
+++ b/e2e/tests-app-ng/app/router/router-outlet/first.component.ts
@@ -0,0 +1,11 @@
+import { Component } from "@angular/core";
+
+@Component({
+ selector: "first",
+ template: `
+
+
+
+ `
+})
+export class FirstComponent { }
diff --git a/e2e/tests-app-ng/app/router/router-outlet/navigation.component.css b/e2e/tests-app-ng/app/router/router-outlet/navigation.component.css
new file mode 100644
index 000000000..59cc35a5f
--- /dev/null
+++ b/e2e/tests-app-ng/app/router/router-outlet/navigation.component.css
@@ -0,0 +1,15 @@
+.title {
+ font-size: 30;
+ margin: 16;
+}
+
+.nav {
+ orientation: horizontal;
+ horizontal-align: stretch;
+ padding: 4;
+ background-color: lightblue;
+}
+
+.link {
+ margin: 10 30;
+}
\ No newline at end of file
diff --git a/e2e/tests-app-ng/app/router/router-outlet/navigation.component.ts b/e2e/tests-app-ng/app/router/router-outlet/navigation.component.ts
new file mode 100644
index 000000000..41b0b71d5
--- /dev/null
+++ b/e2e/tests-app-ng/app/router/router-outlet/navigation.component.ts
@@ -0,0 +1,28 @@
+import { Component } from "@angular/core";
+import { FirstComponent } from "./first.component";
+import { SecondComponent } from "./second.component";
+
+@Component({
+ moduleId: module.id,
+ selector: "navigation-test",
+ styleUrls: ["./navigation.component.css"],
+ template: `
+
+
+
+
+
+
+
+
+ `
+})
+export class NavigationComponent { }
+
+export const NAVIGATION_SUBROUTES = [
+ { path: "", redirectTo: "first", pathMatch: "full" },
+ { path: "first", component: FirstComponent },
+ { path: "second", component: SecondComponent },
+];
diff --git a/e2e/tests-app-ng/app/router/router-outlet/second.component.ts b/e2e/tests-app-ng/app/router/router-outlet/second.component.ts
new file mode 100644
index 000000000..8a8f4dfdb
--- /dev/null
+++ b/e2e/tests-app-ng/app/router/router-outlet/second.component.ts
@@ -0,0 +1,12 @@
+import { Component } from "@angular/core";
+
+@Component({
+ selector: "second",
+ template: `
+
+
+
+ `
+})
+export class SecondComponent { }
+
diff --git a/e2e/tests-app-ng/app/segmented-bar/issue-649.component.ts b/e2e/tests-app-ng/app/segmented-bar/issue-649.component.ts
new file mode 100644
index 000000000..2c2bd85e4
--- /dev/null
+++ b/e2e/tests-app-ng/app/segmented-bar/issue-649.component.ts
@@ -0,0 +1,36 @@
+import { Component } from "@angular/core";
+import { SegmentedBar, SegmentedBarItem } from "@nativescript/core/ui/segmented-bar";
+
+@Component({
+ styles: ["#second { margin: 5; color: blue;" +
+ "background-color: yellow;" +
+ "font-weight: bold; font-size: 20;" +
+ "font-style: italic; font-family: monospace;" +
+ "height: 72; border-width: 2; border-radius: 7;" +
+ "border-color:green; selected-background-color: red; }"],
+ template: `
+
+
+
+
+
+ `
+})
+export class SegmentedBarIssue649Component {
+ public firstSegmentedBarItems: Array = [];
+ public secondSegmentedBarItems: Array = [];
+
+ constructor() {
+ for (let i = 1; i < 4; i++) {
+ let segmentedBarItem = new SegmentedBarItem();
+ segmentedBarItem.title = "View " + i;
+ this.firstSegmentedBarItems.push(segmentedBarItem);
+ let segmentedBarItem1 = new SegmentedBarItem();
+ segmentedBarItem1.title = "View " + i * 2;
+ this.secondSegmentedBarItems.push(segmentedBarItem1);
+ }
+ }
+}
diff --git a/e2e/tests-app-ng/app/segmented-bar/segmented-bar-main-page.component.ts b/e2e/tests-app-ng/app/segmented-bar/segmented-bar-main-page.component.ts
new file mode 100644
index 000000000..08ebbf765
--- /dev/null
+++ b/e2e/tests-app-ng/app/segmented-bar/segmented-bar-main-page.component.ts
@@ -0,0 +1,10 @@
+import { Component } from "@angular/core";
+
+@Component({
+ template: `
+
+
+
+ `,
+})
+export class SegmentedBarMainPageComponent { }
diff --git a/e2e/tests-app-ng/app/tab-view/tab-view.component.ts b/e2e/tests-app-ng/app/tab-view/tab-view.component.ts
new file mode 100644
index 000000000..fad7b9204
--- /dev/null
+++ b/e2e/tests-app-ng/app/tab-view/tab-view.component.ts
@@ -0,0 +1,30 @@
+import { Component, OnInit } from "@angular/core";
+
+@Component({
+ selector: "tab-view-component",
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+})
+
+export class TabViewComponent implements OnInit {
+ public isLoading: boolean = true;
+
+ public ngOnInit(): void {
+ setTimeout(() => {
+ this.isLoading = false;
+ }, 500);
+ }
+}
diff --git a/e2e/tests-app-ng/app/tabs/tabs.component.ts b/e2e/tests-app-ng/app/tabs/tabs.component.ts
new file mode 100644
index 000000000..a0e6efe8f
--- /dev/null
+++ b/e2e/tests-app-ng/app/tabs/tabs.component.ts
@@ -0,0 +1,68 @@
+import { Component, OnInit } from "@angular/core";
+
+@Component({
+ selector: "tabs-component",
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+})
+
+export class TabsComponent implements OnInit {
+
+ public ngOnInit(): void { }
+
+ goTo(tabs: any, index: number) {
+ tabs.selectedIndex = index;
+ }
+
+}
diff --git a/e2e/tests-app-ng/app/template/.DS_Store b/e2e/tests-app-ng/app/template/.DS_Store
new file mode 100644
index 000000000..5008ddfcf
Binary files /dev/null and b/e2e/tests-app-ng/app/template/.DS_Store differ
diff --git a/e2e/tests-app-ng/app/template/app.component.css b/e2e/tests-app-ng/app/template/app.component.css
new file mode 100644
index 000000000..8d4dce63d
--- /dev/null
+++ b/e2e/tests-app-ng/app/template/app.component.css
@@ -0,0 +1,19 @@
+button, label, stack-layout {
+ horizontal-align: center;
+}
+
+button {
+ font-size: 36;
+}
+
+.title {
+ font-size: 30;
+ margin: 20;
+}
+
+.message {
+ font-size: 20;
+ color: #284848;
+ text-align: center;
+ margin: 0 20;
+}
\ No newline at end of file
diff --git a/e2e/tests-app-ng/app/template/app.component.ts b/e2e/tests-app-ng/app/template/app.component.ts
new file mode 100644
index 000000000..5f3969aba
--- /dev/null
+++ b/e2e/tests-app-ng/app/template/app.component.ts
@@ -0,0 +1,30 @@
+import { Component } from "@angular/core";
+
+@Component({
+ moduleId: module.id,
+ selector: "my-app",
+ styles: ["button {color: lime}"],
+ styleUrls: ["./app.component.css"],
+ template: `
+
+
+
+
+
+`,
+})
+export class AppComponent {
+ public counter: number = 16;
+
+ public get message(): string {
+ if (this.counter > 0) {
+ return this.counter + " taps left";
+ } else {
+ return "Hoorraaay! \nYou are ready to start building!";
+ }
+ }
+
+ public onTap() {
+ this.counter--;
+ }
+}
diff --git a/e2e/tests-app-ng/nativescript.config.ts b/e2e/tests-app-ng/nativescript.config.ts
new file mode 100644
index 000000000..89dc02904
--- /dev/null
+++ b/e2e/tests-app-ng/nativescript.config.ts
@@ -0,0 +1,11 @@
+import { NativeScriptConfig } from '@nativescript/core'
+
+export default {
+ id: 'org.nativescript.testsappng',
+ appResourcesPath: 'app/App_Resources',
+ android: {
+ v8Flags: '--expose_gc',
+ markingMode: 'none',
+ },
+ appPath: 'app',
+} as NativeScriptConfig
diff --git a/e2e/tests-app-ng/package.json b/e2e/tests-app-ng/package.json
new file mode 100644
index 000000000..98cee8a7c
--- /dev/null
+++ b/e2e/tests-app-ng/package.json
@@ -0,0 +1,47 @@
+{
+ "description": "NativeScript Application",
+ "license": "SEE LICENSE IN ",
+ "readme": "NativeScript Application",
+ "repository": "",
+ "dependencies": {
+ "@angular/animations": "~10.1.0",
+ "@angular/common": "~10.1.0",
+ "@angular/compiler": "~10.1.0",
+ "@angular/core": "~10.1.0",
+ "@angular/forms": "~10.1.0",
+ "@angular/platform-browser": "~10.1.0",
+ "@angular/platform-browser-dynamic": "~10.1.0",
+ "@angular/router": "~10.1.0",
+ "@nativescript/angular": "file:../../dist/nativescript-angular-scoped.tgz",
+ "nativescript-theme-core": "^1.0.4",
+ "reflect-metadata": "~0.1.8",
+ "rxjs": "~6.6.0",
+ "@nativescript/core": "~7.0.0",
+ "zone.js": "^0.11.1"
+ },
+ "devDependencies": {
+ "@angular/compiler-cli": "~10.1.0",
+ "@nativescript/ios": "7.0.0",
+ "@nativescript/webpack": "~3.0.0",
+ "@ngtools/webpack": "~10.1.0",
+ "babel-traverse": "6.24.1",
+ "babel-types": "6.24.1",
+ "babylon": "6.17.0",
+ "codelyzer": "^5.1.0",
+ "filewalker": "^0.1.3",
+ "lazy": "1.0.11",
+ "typescript": "~3.9.0"
+ },
+ "scripts": {
+ "clean": "npx rimraf hooks node_modules platforms package-lock.json webpack.config.js && npm i",
+ "setup": "cd ../../nativescript-angular && npm run prep.apps && cd ../e2e/tests-app-ng && npm run clean",
+ "u": "update-ns-webpack",
+ "e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
+ "e2e-watch": "tsc -p e2e --watch",
+ "ns-verify-bundle": "ns-verify-bundle",
+ "update-ns-webpack": "update-ns-webpack",
+ "ios": "tns debug ios --emulator --no-hmr",
+ "android": "tns debug android --emulator --no-hmr"
+ },
+ "main": "main.js"
+}
diff --git a/e2e/tests-app-ng/references.d.ts b/e2e/tests-app-ng/references.d.ts
new file mode 100644
index 000000000..a5bb99810
--- /dev/null
+++ b/e2e/tests-app-ng/references.d.ts
@@ -0,0 +1 @@
+///
\ No newline at end of file
diff --git a/e2e/tests-app-ng/tsconfig.json b/e2e/tests-app-ng/tsconfig.json
new file mode 100644
index 000000000..29c1ff2d7
--- /dev/null
+++ b/e2e/tests-app-ng/tsconfig.json
@@ -0,0 +1,34 @@
+{
+ "compilerOptions": {
+ "module": "esnext",
+ "target": "es2017",
+ "moduleResolution": "node",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "noEmitHelpers": true,
+ "noEmitOnError": true,
+ "skipLibCheck": true,
+ "lib": [
+ "es2017",
+ "dom",
+ "es6"
+ ],
+ "baseUrl": ".",
+ "paths": {
+ "~/*": [
+ "app/*"
+ ]
+ },
+ "removeComments": false
+ },
+ "files": [
+ "./references.d.ts",
+ "./app/main.ts"
+ ],
+ "exclude": [
+ "node_modules",
+ "platforms",
+ "**/*.aot",
+ "e2e"
+ ]
+}
\ No newline at end of file
diff --git a/merge-guidance-schema.png b/merge-guidance-schema.png
new file mode 100644
index 000000000..dd882471e
Binary files /dev/null and b/merge-guidance-schema.png differ
diff --git a/nativescript-angular-package/.npmignore b/nativescript-angular-package/.npmignore
new file mode 100644
index 000000000..728e2a64d
--- /dev/null
+++ b/nativescript-angular-package/.npmignore
@@ -0,0 +1,13 @@
+*.tgz
+dist
+
+*.ts
+!*.d.ts
+
+*.js.map
+
+tsconfig.json
+global.d.ts
+.npmignore
+gulpfile.js
+tslint.json
diff --git a/nativescript-angular-package/animations/index.ts b/nativescript-angular-package/animations/index.ts
new file mode 100644
index 000000000..3add12158
--- /dev/null
+++ b/nativescript-angular-package/animations/index.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/animations";
\ No newline at end of file
diff --git a/nativescript-angular-package/app-host-view.ts b/nativescript-angular-package/app-host-view.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/app-host-view.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/common.ts b/nativescript-angular-package/common.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/common.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/common/detached-loader.ts b/nativescript-angular-package/common/detached-loader.ts
new file mode 100644
index 000000000..1bf4ba6f3
--- /dev/null
+++ b/nativescript-angular-package/common/detached-loader.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/common/detached-loader";
\ No newline at end of file
diff --git a/nativescript-angular-package/common/index.ts b/nativescript-angular-package/common/index.ts
new file mode 100644
index 000000000..c78d3aac7
--- /dev/null
+++ b/nativescript-angular-package/common/index.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/common";
diff --git a/nativescript-angular-package/common/utils.ts b/nativescript-angular-package/common/utils.ts
new file mode 100644
index 000000000..54c798aed
--- /dev/null
+++ b/nativescript-angular-package/common/utils.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/common/utils";
\ No newline at end of file
diff --git a/nativescript-angular-package/directives/action-bar.ts b/nativescript-angular-package/directives/action-bar.ts
new file mode 100644
index 000000000..5337b967b
--- /dev/null
+++ b/nativescript-angular-package/directives/action-bar.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/directives/action-bar";
\ No newline at end of file
diff --git a/nativescript-angular-package/directives/dialogs.ts b/nativescript-angular-package/directives/dialogs.ts
new file mode 100644
index 000000000..f7aa4b95b
--- /dev/null
+++ b/nativescript-angular-package/directives/dialogs.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/directives/dialogs";
\ No newline at end of file
diff --git a/nativescript-angular-package/directives/index.ts b/nativescript-angular-package/directives/index.ts
new file mode 100644
index 000000000..d32d1c9a8
--- /dev/null
+++ b/nativescript-angular-package/directives/index.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/directives";
\ No newline at end of file
diff --git a/nativescript-angular-package/directives/templated-items-comp.ts b/nativescript-angular-package/directives/templated-items-comp.ts
new file mode 100644
index 000000000..cdb635ff4
--- /dev/null
+++ b/nativescript-angular-package/directives/templated-items-comp.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/directives/templated-items-comp";
\ No newline at end of file
diff --git a/nativescript-angular-package/dom-adapter.ts b/nativescript-angular-package/dom-adapter.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/dom-adapter.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/element-registry.ts b/nativescript-angular-package/element-registry.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/element-registry.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/file-system/ns-file-system.ts b/nativescript-angular-package/file-system/ns-file-system.ts
new file mode 100644
index 000000000..e7b0b52dd
--- /dev/null
+++ b/nativescript-angular-package/file-system/ns-file-system.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/file-system/ns-file-system";
\ No newline at end of file
diff --git a/nativescript-angular-package/forms/index.ts b/nativescript-angular-package/forms/index.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/forms/index.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/forms/value-accessors/base-value-accessor.ts b/nativescript-angular-package/forms/value-accessors/base-value-accessor.ts
new file mode 100644
index 000000000..36c3a7f63
--- /dev/null
+++ b/nativescript-angular-package/forms/value-accessors/base-value-accessor.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/forms/value-accessors/base-value-accessor";
\ No newline at end of file
diff --git a/nativescript-angular-package/forms/value-accessors/checked-value-accessor.ts b/nativescript-angular-package/forms/value-accessors/checked-value-accessor.ts
new file mode 100644
index 000000000..27f9059a7
--- /dev/null
+++ b/nativescript-angular-package/forms/value-accessors/checked-value-accessor.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/forms/value-accessors/checked-value-accessor";
\ No newline at end of file
diff --git a/nativescript-angular-package/forms/value-accessors/date-value-accessor.ts b/nativescript-angular-package/forms/value-accessors/date-value-accessor.ts
new file mode 100644
index 000000000..f3640a34c
--- /dev/null
+++ b/nativescript-angular-package/forms/value-accessors/date-value-accessor.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/forms/value-accessors/date-value-accessor";
\ No newline at end of file
diff --git a/nativescript-angular-package/forms/value-accessors/index.ts b/nativescript-angular-package/forms/value-accessors/index.ts
new file mode 100644
index 000000000..37a248724
--- /dev/null
+++ b/nativescript-angular-package/forms/value-accessors/index.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/forms/value-accessors";
\ No newline at end of file
diff --git a/nativescript-angular-package/forms/value-accessors/number-value-accessor.ts b/nativescript-angular-package/forms/value-accessors/number-value-accessor.ts
new file mode 100644
index 000000000..5f9ff4ec8
--- /dev/null
+++ b/nativescript-angular-package/forms/value-accessors/number-value-accessor.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/forms/value-accessors/number-value-accessor";
\ No newline at end of file
diff --git a/nativescript-angular-package/forms/value-accessors/selectedIndex-value-accessor.ts b/nativescript-angular-package/forms/value-accessors/selectedIndex-value-accessor.ts
new file mode 100644
index 000000000..8ae5a0419
--- /dev/null
+++ b/nativescript-angular-package/forms/value-accessors/selectedIndex-value-accessor.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/forms/value-accessors/selectedIndex-value-accessor";
\ No newline at end of file
diff --git a/nativescript-angular-package/forms/value-accessors/text-value-accessor.ts b/nativescript-angular-package/forms/value-accessors/text-value-accessor.ts
new file mode 100644
index 000000000..81db7183d
--- /dev/null
+++ b/nativescript-angular-package/forms/value-accessors/text-value-accessor.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/forms/value-accessors/text-value-accessor";
\ No newline at end of file
diff --git a/nativescript-angular-package/forms/value-accessors/time-value-accessor.ts b/nativescript-angular-package/forms/value-accessors/time-value-accessor.ts
new file mode 100644
index 000000000..98647ee2e
--- /dev/null
+++ b/nativescript-angular-package/forms/value-accessors/time-value-accessor.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/forms/value-accessors/time-value-accessor";
\ No newline at end of file
diff --git a/nativescript-angular-package/http-client/index.ts b/nativescript-angular-package/http-client/index.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/http-client/index.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/index.ts b/nativescript-angular-package/index.ts
new file mode 100644
index 000000000..053585d6a
--- /dev/null
+++ b/nativescript-angular-package/index.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
diff --git a/nativescript-angular-package/lang-facade.ts b/nativescript-angular-package/lang-facade.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/lang-facade.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/modal-dialog.ts b/nativescript-angular-package/modal-dialog.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/modal-dialog.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/nativescript.module.ts b/nativescript-angular-package/nativescript.module.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/nativescript.module.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/package.json b/nativescript-angular-package/package.json
new file mode 100644
index 000000000..54867bcbb
--- /dev/null
+++ b/nativescript-angular-package/package.json
@@ -0,0 +1,61 @@
+{
+ "name": "nativescript-angular",
+ "version": "11.2.0",
+ "description": "Compatibility with old style nativescript-angular imports.",
+ "homepage": "https://www.nativescript.org/",
+ "bugs": "https://github.com/NativeScript/nativescript-angular/issues",
+ "author": {
+ "name": "NativeScript Team"
+ },
+ "nativescript": {
+ "platforms": {
+ "android": "6.0.0",
+ "ios": "6.0.0"
+ }
+ },
+ "keywords": [
+ "NativeScript",
+ "Angular"
+ ],
+ "license": "Apache-2.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/NativeScript/nativescript-angular.git"
+ },
+ "ngPackage": {
+ "lib": {
+ "entryFile": "index.ts",
+ "umdModuleIds": {
+ "@nativescript/core": "ns-core",
+ "@nativescript/angular": "ns-angular"
+ }
+ },
+ "allowedNonPeerDependencies": [
+ "."
+ ]
+ },
+ "devDependencies": {
+ "@angular/animations": "~11.0.0",
+ "@angular/common": "~11.0.0",
+ "@angular/compiler": "~11.0.0",
+ "@angular/compiler-cli": "~11.0.0",
+ "@angular/core": "~11.0.0",
+ "@angular/forms": "~11.0.0",
+ "@angular/platform-browser": "~11.0.0",
+ "@angular/platform-browser-dynamic": "~11.0.0",
+ "@angular/router": "~11.0.0",
+ "@nativescript/angular": "rc",
+ "@nativescript/core": "~8.0.0",
+ "ng-packagr": "^11.0.0",
+ "rxjs": "~6.6.0",
+ "typescript": "~4.0.0"
+ },
+ "scripts": {
+ "setup": "npx rimraf hooks node_modules package-lock.json && npm i",
+ "build": "ng-packagr -p package.json",
+ "build.pack": "npm run tsc && npm run build && cd dist && npm pack",
+ "ngc": "ngc -p tsconfig.json",
+ "tsc": "tsc",
+ "pack-with-scoped-version": "cd ../build/pack-scripts && npm i && npx ts-node pack-compat.ts"
+ }
+}
diff --git a/nativescript-angular-package/platform-common.ts b/nativescript-angular-package/platform-common.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/platform-common.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/platform-providers.ts b/nativescript-angular-package/platform-providers.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/platform-providers.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/platform.ts b/nativescript-angular-package/platform.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/platform.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/renderer.ts b/nativescript-angular-package/renderer.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/renderer.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/resource-loader.ts b/nativescript-angular-package/resource-loader.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/resource-loader.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/router/index.ts b/nativescript-angular-package/router/index.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/router/index.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/router/ns-location-strategy.ts b/nativescript-angular-package/router/ns-location-strategy.ts
new file mode 100644
index 000000000..3d94eb407
--- /dev/null
+++ b/nativescript-angular-package/router/ns-location-strategy.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/router/ns-location-strategy";
\ No newline at end of file
diff --git a/nativescript-angular-package/router/ns-route-reuse-strategy.ts b/nativescript-angular-package/router/ns-route-reuse-strategy.ts
new file mode 100644
index 000000000..fa6306cfc
--- /dev/null
+++ b/nativescript-angular-package/router/ns-route-reuse-strategy.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/router/ns-route-reuse-strategy";
\ No newline at end of file
diff --git a/nativescript-angular-package/router/ns-router-link.ts b/nativescript-angular-package/router/ns-router-link.ts
new file mode 100644
index 000000000..fa87694d9
--- /dev/null
+++ b/nativescript-angular-package/router/ns-router-link.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/router/ns-router-link";
\ No newline at end of file
diff --git a/nativescript-angular-package/router/page-router-outlet.ts b/nativescript-angular-package/router/page-router-outlet.ts
new file mode 100644
index 000000000..f181cf615
--- /dev/null
+++ b/nativescript-angular-package/router/page-router-outlet.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/router/page-router-outlet";
\ No newline at end of file
diff --git a/nativescript-angular-package/router/router-extensions.ts b/nativescript-angular-package/router/router-extensions.ts
new file mode 100644
index 000000000..f98a28146
--- /dev/null
+++ b/nativescript-angular-package/router/router-extensions.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/router/router-extensions";
\ No newline at end of file
diff --git a/nativescript-angular-package/router/router.module.ts b/nativescript-angular-package/router/router.module.ts
new file mode 100644
index 000000000..50344ad36
--- /dev/null
+++ b/nativescript-angular-package/router/router.module.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/router/router.module";
\ No newline at end of file
diff --git a/nativescript-angular-package/schema-registry.ts b/nativescript-angular-package/schema-registry.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/schema-registry.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/testing/index.ts b/nativescript-angular-package/testing/index.ts
new file mode 100644
index 000000000..af69144d0
--- /dev/null
+++ b/nativescript-angular-package/testing/index.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/testing";
\ No newline at end of file
diff --git a/nativescript-angular-package/testing/src/nativescript_test_component_renderer.ts b/nativescript-angular-package/testing/src/nativescript_test_component_renderer.ts
new file mode 100644
index 000000000..af69144d0
--- /dev/null
+++ b/nativescript-angular-package/testing/src/nativescript_test_component_renderer.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/testing";
\ No newline at end of file
diff --git a/nativescript-angular-package/testing/src/util.ts b/nativescript-angular-package/testing/src/util.ts
new file mode 100644
index 000000000..af69144d0
--- /dev/null
+++ b/nativescript-angular-package/testing/src/util.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular/testing";
\ No newline at end of file
diff --git a/nativescript-angular-package/trace.ts b/nativescript-angular-package/trace.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/trace.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/tsconfig.json b/nativescript-angular-package/tsconfig.json
new file mode 100644
index 000000000..85c737c2b
--- /dev/null
+++ b/nativescript-angular-package/tsconfig.json
@@ -0,0 +1,38 @@
+{
+ "compilerOptions": {
+ "target": "es2017",
+ "module": "esnext",
+ "moduleResolution": "node",
+ "sourceMap": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "noImplicitUseStrict": true,
+ "noEmitHelpers": true,
+ "declaration": true,
+ "removeComments": true,
+ "noEmitOnError": true,
+ "skipLibCheck": true,
+ "skipDefaultLibCheck": true,
+ "noImplicitAny": false,
+ "lib": [
+ "es2017",
+ "dom",
+ "es6"
+ ],
+ "baseUrl": "."
+ },
+ "angularCompilerOptions": {
+ "genDir": ".",
+ "skipMetadataEmit": false,
+ "skipTemplateCodegen": true,
+ "strictMetadataEmit": true,
+ "enableIvy": true
+ },
+ "include": [
+ "**/*.ts"
+ ],
+ "exclude": [
+ "node_modules",
+ "dist"
+ ]
+}
\ No newline at end of file
diff --git a/nativescript-angular-package/value-accessors/base-value-accessor.ts b/nativescript-angular-package/value-accessors/base-value-accessor.ts
new file mode 100644
index 000000000..aa3cfbdb5
--- /dev/null
+++ b/nativescript-angular-package/value-accessors/base-value-accessor.ts
@@ -0,0 +1,2 @@
+// This file is only for compatibility with pre 4.4.0 releases.
+export * from "@nativescript/angular";
diff --git a/nativescript-angular-package/view-util.ts b/nativescript-angular-package/view-util.ts
new file mode 100644
index 000000000..2d2419d19
--- /dev/null
+++ b/nativescript-angular-package/view-util.ts
@@ -0,0 +1 @@
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/zone-js/README.md b/nativescript-angular-package/zone-js/README.md
new file mode 100644
index 000000000..38a0ebc26
--- /dev/null
+++ b/nativescript-angular-package/zone-js/README.md
@@ -0,0 +1,10 @@
+Zone.js for NativeScript
+---
+
+Zone.js is a library that aims to intercept all asynchronous API calls made in an environment, in order
+to wrap them into coherent execution contexts over time.
+
+NativeScript executes inside an environment that Zone.js is not designed to work in, so a custom Zone.js output
+must be created.
+
+Find out more about this in the [Upgrading Zone.js document](../../doc/upgrading-zonejs.md)
\ No newline at end of file
diff --git a/nativescript-angular-package/zone-js/testing.jasmine.ts b/nativescript-angular-package/zone-js/testing.jasmine.ts
new file mode 100644
index 000000000..b52f2e881
--- /dev/null
+++ b/nativescript-angular-package/zone-js/testing.jasmine.ts
@@ -0,0 +1,4 @@
+// Bootstrap helper module for jasmine spec tests
+import "@nativescript/angular/platform";
+// import "@nativescript/angular/zone-js/dist/zone-nativescript.jasmine.js";
+import '@nativescript/zone-js';
diff --git a/nativescript-angular-package/zone-js/testing.mocha.ts b/nativescript-angular-package/zone-js/testing.mocha.ts
new file mode 100644
index 000000000..ce59f0e7b
--- /dev/null
+++ b/nativescript-angular-package/zone-js/testing.mocha.ts
@@ -0,0 +1,3 @@
+import "@nativescript/angular/platform";
+// import "@nativescript/angular/zone-js/dist/zone-nativescript.mocha.js";
+import '@nativescript/zone-js';
diff --git a/nativescript-angular/.npmignore b/nativescript-angular/.npmignore
index 3eb3853e2..6f87c2653 100644
--- a/nativescript-angular/.npmignore
+++ b/nativescript-angular/.npmignore
@@ -1,10 +1,9 @@
*.tgz
+dist
*.ts
!*.d.ts
-*.js.map
-
tsconfig.json
global.d.ts
.npmignore
diff --git a/nativescript-angular/.prettierignore b/nativescript-angular/.prettierignore
new file mode 100644
index 000000000..3cd5b3e47
--- /dev/null
+++ b/nativescript-angular/.prettierignore
@@ -0,0 +1,14 @@
+.github
+*.yml
+.vscode
+build
+dist
+doc
+e2e
+tests
+coverage
+platforms
+temp
+*.md
+*.json
+*.js
\ No newline at end of file
diff --git a/nativescript-angular/.prettierrc.json b/nativescript-angular/.prettierrc.json
new file mode 100644
index 000000000..098f6bab3
--- /dev/null
+++ b/nativescript-angular/.prettierrc.json
@@ -0,0 +1,6 @@
+{
+ "useTabs": true,
+ "printWidth": 600,
+ "tabWidth": 2,
+ "singleQuote": true
+}
\ No newline at end of file
diff --git a/nativescript-angular/README.md b/nativescript-angular/README.md
index 8f87f938c..b6d86f1a1 100644
--- a/nativescript-angular/README.md
+++ b/nativescript-angular/README.md
@@ -1 +1,45 @@
-[Get started with Angular and NativeScript.](http://docs.nativescript.org/angular/start/introduction.html)
+# NativeScript Angular
+[](https://travis-ci.org/NativeScript/nativescript-angular)
+
+This repository contains the code for integration of NativeScript with Angular.
+
+[NativeScript](https://www.nativescript.org/) is a framework which enables developers to write truly native mobile applications for Android and iOS using JavaScript and CSS. [Angular](https://angular.io/) is one of the most popular open source JavaScript frameworks for application development. We [worked closely with developers at Google](http://angularjs.blogspot.bg/2015/12/building-mobile-apps-with-angular-2-and.html) to make Angular in NativeScript a reality. The result is a software architecture that allows you to build mobile apps using the same framework—and in some cases the same code—that you use to build Angular web apps, with the performance you’d expect from native code. [Read more about building truly native mobile apps with NativeScript and Angular](https://docs.nativescript.org/tutorial/ng-chapter-0).
+
+
+
+
+- [NativeScript Angular](#nativescript-angular)
+ - [Watch the video explaining Angular and NativeScript](#watch-the-video-explaining-angular-and-nativescript)
+ - [Explore the examples](#explore-the-examples)
+ - [Contribute](#contribute)
+ - [Known issues](#known-issues)
+ - [Get Help](#get-help)
+
+
+
+
+## Watch the video explaining Angular and NativeScript
+[NativeScript session on AngularConnect conference](https://www.youtube.com/watch?v=4SbiiyRSIwo)
+
+## Explore the examples
+
+The `e2e` apps are meant for testing stuff. You can take a look at these additional sample apps that use the published builds from npm:
+
+* [Hello world starter](https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-hello-world-ng)
+* [Master-detail template](https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-master-detail-ng)
+* [Drawer navigation template](https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-drawer-navigation-ng)
+* [TabView navigation template](https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-tab-navigation-ng)
+* [NativeScript Angular SDK examples](https://github.com/NativeScript/nativescript-sdk-examples-ng)
+
+## Contribute
+We love PRs! Check out the [contributing guidelines](CONTRIBUTING.md) and [development workflow for local setup](DevelopmentWorkflow.md). If you want to contribute, but you are not sure where to start - look for [issues labeled `help wanted`](https://github.com/NativeScript/nativescript-angular/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22).
+
+## Known issues
+
+1. There are certain issues with the Parse5DomAdapter and we'll likely need to provide our own later on:
+ * Self-closing elements (``) get parsed wrong (in this case Button gets parsed as a Label child.
+
+## Get Help
+Please, use [github issues](https://github.com/NativeScript/nativescript-angular/issues) strictly for [reporting bugs](CONTRIBUTING.md#reporting-bugs) or [requesting features](CONTRIBUTING.md#requesting-new-features). For general questions and support, check out [Stack Overflow](https://stackoverflow.com/questions/tagged/nativescript) or ask our experts in [NativeScript community Slack channel](http://developer.telerik.com/wp-login.php?action=slack-invitation).
+
+
diff --git a/nativescript-angular/animations/animation-driver.ts b/nativescript-angular/animations/animation-driver.ts
index 5e1b3af6c..b534ba950 100644
--- a/nativescript-angular/animations/animation-driver.ts
+++ b/nativescript-angular/animations/animation-driver.ts
@@ -1,211 +1,159 @@
-import { AnimationPlayer } from "@angular/animations";
-import { AnimationDriver } from "@angular/animations/browser";
-import { createSelector, SelectorCore } from "tns-core-modules/ui/styling/css-selector";
-import { CssAnimationProperty } from "tns-core-modules/ui/core/properties";
-import { eachDescendant } from "tns-core-modules/ui/core/view";
-import { ProxyViewContainer } from "tns-core-modules/ui/proxy-view-container";
-
-import { NativeScriptAnimationPlayer } from "./animation-player";
-import {
- Keyframe,
- dashCaseToCamelCase,
-} from "./utils";
-import { NgView, InvisibleNode } from "../element-registry";
-import { animationsLog as traceLog } from "../trace";
+import { AnimationPlayer } from '@angular/animations';
+import { AnimationDriver } from '@angular/animations/browser';
+import { ProxyViewContainer, eachDescendant, CssAnimationProperty, CSSHelper } from '@nativescript/core';
+import { NativeScriptAnimationPlayer } from './animation-player';
+import { Keyframe, dashCaseToCamelCase } from './utils';
+import { NgView, InvisibleNode } from '../element-registry';
+import { NativeScriptDebug } from '../trace';
interface ViewMatchResult {
- found: boolean;
+ found: boolean;
}
interface ViewMatchParams {
- originalView: NgView;
+ originalView: NgView;
}
interface QueryParams {
- selector: Selector;
- multi: boolean;
+ selector: Selector;
+ multi: boolean;
}
interface QueryResult {
- matches: NgView[];
+ matches: NgView[];
}
class Selector {
- private nsSelectors: SelectorCore[];
- private classSelectors: string[];
-
- constructor(rawSelector: string) {
- this.parse(rawSelector);
- }
-
- match(element: NgView): boolean {
- return this.nsSelectorMatch(element) || this.classSelectorsMatch(element);
- }
-
- private parse(rawSelector: string) {
- const selectors = rawSelector.split(",").map(s => s.trim());
-
- this.nsSelectors = selectors.map(createSelector);
- this.classSelectors = selectors
- .filter(s => s.startsWith("."))
- .map(s => s.substring(1));
- }
-
- private nsSelectorMatch(element: NgView) {
- return this.nsSelectors.some(s => s.match(element));
- }
-
- private classSelectorsMatch(element: NgView) {
- return this.classSelectors.some(s => this.hasClass(element, s));
- }
-
- // we're using that instead of match for classes
- // that are dynamically added by the animation engine
- // such as .ng-trigger, that's added for every :enter view
- private hasClass(element: NgView, cls: string) {
- return element && element["$$classes"] && element["$$classes"][cls];
- }
+ private nsSelectors: Array;
+ private classSelectors: string[];
+
+ constructor(rawSelector: string) {
+ this.parse(rawSelector);
+ }
+
+ match(element: NgView): boolean {
+ return this.nsSelectorMatch(element) || this.classSelectorsMatch(element);
+ }
+
+ private parse(rawSelector: string) {
+ const selectors = rawSelector.split(',').map((s) => s.trim());
+
+ this.nsSelectors = selectors.map(CSSHelper.createSelector);
+ this.classSelectors = selectors.filter((s) => s.startsWith('.')).map((s) => s.substring(1));
+ }
+
+ private nsSelectorMatch(element: NgView) {
+ return this.nsSelectors.some((s) => s.match(element));
+ }
+
+ private classSelectorsMatch(element: NgView) {
+ return this.classSelectors.some((s) => this.hasClass(element, s));
+ }
+
+ // we're using that instead of match for classes
+ // that are dynamically added by the animation engine
+ // such as .ng-trigger, that's added for every :enter view
+ private hasClass(element: NgView, cls: string) {
+ return element && element['$$classes'] && element['$$classes'][cls];
+ }
}
export class NativeScriptAnimationDriver implements AnimationDriver {
- private static validProperties = [
- ...CssAnimationProperty._getPropertyNames(),
- "transform",
- ];
-
- validateStyleProperty(property: string): boolean {
- traceLog(`CssAnimationProperty.validateStyleProperty: ${property}`);
- return NativeScriptAnimationDriver.validProperties.indexOf(property) !== -1;
- }
-
- matchesElement(element: NgView, rawSelector: string): boolean {
- traceLog(
- `NativeScriptAnimationDriver.matchesElement ` +
- `element: ${element}, selector: ${rawSelector}`
- );
-
- const selector = this.makeSelector(rawSelector);
- return selector.match(element);
- }
-
-
- containsElement(elm1: NgView, elm2: NgView): boolean {
- traceLog(
- `NativeScriptAnimationDriver.containsElement ` +
- `element1: ${elm1}, element2: ${elm2}`
- );
-
- // Checking if the parent is our fake body object
- if (elm1["isOverride"]) {
- return true;
- }
-
- const params: ViewMatchParams = { originalView: elm2 };
- const result: ViewMatchResult = this.visitDescendants(elm1, viewMatches, params);
-
- return result.found;
- }
-
- query(element: NgView, rawSelector: string, multi: boolean): NgView[] {
- traceLog(
- `NativeScriptAnimationDriver.query ` +
- `element: ${element}, selector: ${rawSelector} ` +
- `multi: ${multi}`
- );
-
- const selector = this.makeSelector(rawSelector);
- const params: QueryParams = { selector, multi };
- const result: QueryResult = this.visitDescendants(element, queryDescendants, params);
-
- return result.matches || [];
- }
-
- computeStyle(element: NgView, prop: string): string {
- traceLog(
- `NativeScriptAnimationDriver.computeStyle ` +
- `element: ${element}, prop: ${prop}`
- );
-
- const camelCaseProp = dashCaseToCamelCase(prop);
- return element.style[camelCaseProp];
- }
-
- animate(
- element: NgView,
- keyframes: Keyframe[],
- duration: number,
- delay: number,
- easing: string
- ): AnimationPlayer {
- traceLog(
- `NativeScriptAnimationDriver.animate ` +
- `element: ${element}, keyframes: ${keyframes} ` +
- `duration: ${duration}, delay: ${delay} ` +
- `easing: ${easing}`
- );
-
- return new NativeScriptAnimationPlayer(
- element, keyframes, duration, delay, easing);
- }
-
- private makeSelector(rawSelector: string): Selector {
- return new Selector(rawSelector);
- }
-
- private visitDescendants(
- element: NgView,
- cb: (child: NgView, result: any, params: any) => boolean,
- cbParams: any): any {
-
- const result = {};
- // fill the result obj with the result from the callback function
- eachDescendant(element, (child: NgView) => cb(child, result, cbParams));
-
- return result;
- }
-}
+ private static validProperties = [...CssAnimationProperty._getPropertyNames(), 'transform'];
+
+ validateStyleProperty(property: string): boolean {
+ NativeScriptDebug.animationsLog(`CssAnimationProperty.validateStyleProperty: ${property}`);
+ return NativeScriptAnimationDriver.validProperties.indexOf(property) !== -1;
+ }
+
+ matchesElement(element: NgView, rawSelector: string): boolean {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationDriver.matchesElement ` + `element: ${element}, selector: ${rawSelector}`);
+
+ const selector = this.makeSelector(rawSelector);
+ return selector.match(element);
+ }
+
+ containsElement(elm1: NgView, elm2: NgView): boolean {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationDriver.containsElement ` + `element1: ${elm1}, element2: ${elm2}`);
+
+ // Checking if the parent is our fake body object
+ if (elm1['isOverride']) {
+ return true;
+ }
+
+ const params: ViewMatchParams = { originalView: elm2 };
+ const result: ViewMatchResult = this.visitDescendants(elm1, viewMatches, params);
+
+ return result.found;
+ }
+
+ query(element: NgView, rawSelector: string, multi: boolean): NgView[] {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationDriver.query ` + `element: ${element}, selector: ${rawSelector} ` + `multi: ${multi}`);
-function viewMatches(
- element: NgView,
- result: ViewMatchResult,
- params: ViewMatchParams
-): boolean {
+ const selector = this.makeSelector(rawSelector);
+ const params: QueryParams = { selector, multi };
+ const result: QueryResult = this.visitDescendants(element, queryDescendants, params);
+
+ return result.matches || [];
+ }
+
+ computeStyle(element: NgView, prop: string): string {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationDriver.computeStyle ` + `element: ${element}, prop: ${prop}`);
+
+ const camelCaseProp = dashCaseToCamelCase(prop);
+ return element.style[camelCaseProp];
+ }
+
+ animate(element: NgView, keyframes: Keyframe[], duration: number, delay: number, easing: string): AnimationPlayer {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationDriver.animate ` + `element: ${element}, keyframes: ${keyframes} ` + `duration: ${duration}, delay: ${delay} ` + `easing: ${easing}`);
+
+ return new NativeScriptAnimationPlayer(element, keyframes, duration, delay, easing);
+ }
+
+ private makeSelector(rawSelector: string): Selector {
+ return new Selector(rawSelector);
+ }
+
+ private visitDescendants(element: NgView, cb: (child: NgView, result: any, params: any) => boolean, cbParams: any): any {
+ const result = {};
+ // fill the result obj with the result from the callback function
+ eachDescendant(element, (child: NgView) => cb(child, result, cbParams));
+
+ return result;
+ }
+}
- if (element === params.originalView) {
- result.found = true;
- }
+function viewMatches(element: NgView, result: ViewMatchResult, params: ViewMatchParams): boolean {
+ if (element === params.originalView) {
+ result.found = true;
+ }
- return !result.found;
+ return !result.found;
}
-function queryDescendants(
- element: NgView,
- result: QueryResult,
- params: QueryParams
-): boolean {
-
- if (!result.matches) {
- result.matches = [];
- }
-
- const { selector, multi } = params;
-
- // skip comment and text nodes
- // because they are not actual Views
- // and cannot be animated
- if (element instanceof InvisibleNode || !selector.match(element)) {
- return true;
- }
-
- if (element instanceof ProxyViewContainer) {
- element.eachChild((child: NgView) => {
- result.matches.push(child);
- return true;
- });
- } else {
- result.matches.push(element);
- }
-
- return multi;
+function queryDescendants(element: NgView, result: QueryResult, params: QueryParams): boolean {
+ if (!result.matches) {
+ result.matches = [];
+ }
+
+ const { selector, multi } = params;
+
+ // skip comment and text nodes
+ // because they are not actual Views
+ // and cannot be animated
+ if (element instanceof InvisibleNode || !selector.match(element)) {
+ return true;
+ }
+
+ if (element instanceof ProxyViewContainer) {
+ element.eachChild((child: NgView) => {
+ result.matches.push(child);
+ return true;
+ });
+ } else {
+ result.matches.push(element);
+ }
+
+ return multi;
}
diff --git a/nativescript-angular/animations/animation-player.ts b/nativescript-angular/animations/animation-player.ts
index 3e0472cdd..d8fe447b7 100644
--- a/nativescript-angular/animations/animation-player.ts
+++ b/nativescript-angular/animations/animation-player.ts
@@ -1,114 +1,139 @@
-import { AnimationPlayer } from "@angular/animations";
-import { KeyframeAnimation }
- from "tns-core-modules/ui/animation/keyframe-animation";
+import { AnimationPlayer } from '@angular/animations';
+import { View, EventData, KeyframeAnimation } from '@nativescript/core';
-import { Keyframe, createKeyframeAnimation } from "./utils";
-import { NgView } from "../element-registry";
-import { animationsLog as traceLog } from "../trace";
+import { Keyframe, createKeyframeAnimation } from './utils';
+import { NgView } from '../element-registry';
+import { NativeScriptDebug } from '../trace';
export class NativeScriptAnimationPlayer implements AnimationPlayer {
- public parentPlayer: AnimationPlayer = null;
-
- private _startSubscriptions: Function[] = [];
- private _doneSubscriptions: Function[] = [];
- private _finished = false;
- private _started = false;
- private animation: KeyframeAnimation;
-
- constructor(
- private target: NgView,
- keyframes: Keyframe[],
- private duration: number,
- private delay: number,
- easing: string
- ) {
- this.initKeyframeAnimation(keyframes, duration, delay, easing);
- }
-
- get totalTime(): number {
- return this.delay + this.duration;
- }
-
- init(): void {
- }
-
- hasStarted(): boolean {
- return this._started;
- }
-
- onStart(fn: Function): void { this._startSubscriptions.push(fn); }
- onDone(fn: Function): void { this._doneSubscriptions.push(fn); }
- onDestroy(fn: Function): void { this._doneSubscriptions.push(fn); }
-
- play(): void {
- traceLog(`NativeScriptAnimationPlayer.play`);
-
- if (!this.animation) {
- return;
- }
-
- if (!this._started) {
- this._started = true;
- this._startSubscriptions.forEach(fn => fn());
- this._startSubscriptions = [];
- }
-
- this.animation.play(this.target)
- .then(() => this.onFinish())
- .catch((_e) => {});
- }
-
- pause(): void {
- }
-
- finish(): void {
- this.onFinish();
- }
-
- reset(): void {
- traceLog(`NativeScriptAnimationPlayer.reset`);
-
- if (this.animation && this.animation.isPlaying) {
- this.animation.cancel();
- }
- }
-
- restart(): void {
- traceLog(`NativeScriptAnimationPlayer.restart`);
-
- this.reset();
- this.play();
- }
-
- destroy(): void {
- traceLog(`NativeScriptAnimationPlayer.destroy`);
- this.onFinish();
- }
-
- setPosition(_p: any): void {
- throw new Error("AnimationPlayer.setPosition method is not supported!");
- }
-
- getPosition(): number {
- return 0;
- }
-
- private initKeyframeAnimation(keyframes: Keyframe[], duration: number, delay: number, easing: string) {
- traceLog(`NativeScriptAnimationPlayer.initKeyframeAnimation`);
-
- this.animation = createKeyframeAnimation(keyframes, duration, delay, easing);
- }
-
- private onFinish() {
- traceLog(`NativeScriptAnimationPlayer.onFinish`);
-
- if (this._finished) {
- return;
- }
-
- this._finished = true;
- this._started = false;
- this._doneSubscriptions.forEach(fn => fn());
- this._doneSubscriptions = [];
- }
+ public parentPlayer: AnimationPlayer = null;
+
+ private _startSubscriptions: Function[] = [];
+ private _doneSubscriptions: Function[] = [];
+ private _finished = false;
+ private _started = false;
+ private animation: KeyframeAnimation;
+
+ constructor(private target: NgView, keyframes: Keyframe[], private duration: number, private delay: number, easing: string) {
+ this.initKeyframeAnimation(keyframes, duration, delay, easing);
+ }
+
+ get totalTime(): number {
+ return this.delay + this.duration;
+ }
+
+ init(): void {}
+
+ hasStarted(): boolean {
+ return this._started;
+ }
+
+ onStart(fn: Function): void {
+ this._startSubscriptions.push(fn);
+ }
+ onDone(fn: Function): void {
+ this._doneSubscriptions.push(fn);
+ }
+ onDestroy(fn: Function): void {
+ this._doneSubscriptions.push(fn);
+ }
+
+ play(): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationPlayer.play`);
+ }
+
+ if (!this.animation) {
+ return;
+ }
+
+ if (!this._started) {
+ this._started = true;
+ this._startSubscriptions.forEach((fn) => fn());
+ this._startSubscriptions = [];
+ }
+
+ // When this issue https://github.com/NativeScript/NativeScript/issues/7984 is fixes in @nativescript/core
+ // we can change this fix and apply the one that is recommended in that issue.
+ if (this.target.isLoaded) {
+ this.playAnimation();
+ } else {
+ this.target.on(View.loadedEvent, this.onTargetLoaded.bind(this));
+ }
+ }
+
+ private onTargetLoaded(args: EventData) {
+ this.target.off(View.loadedEvent, this.onTargetLoaded);
+ this.playAnimation();
+ }
+
+ private playAnimation() {
+ this.animation
+ .play(this.target)
+ .then(() => this.onFinish())
+ .catch((_e) => {});
+ }
+
+ pause(): void {}
+
+ finish(): void {
+ this.onFinish();
+ }
+
+ reset(): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationPlayer.reset`);
+ }
+
+ if (this.animation && this.animation.isPlaying) {
+ this.animation.cancel();
+ }
+ }
+
+ restart(): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationPlayer.restart`);
+ }
+
+ this.reset();
+ this.play();
+ }
+
+ destroy(): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationPlayer.destroy`);
+ }
+ this.onFinish();
+ }
+
+ setPosition(_p: any): void {
+ throw new Error('AnimationPlayer.setPosition method is not supported!');
+ }
+
+ getPosition(): number {
+ return 0;
+ }
+
+ private initKeyframeAnimation(keyframes: Keyframe[], duration: number, delay: number, easing: string) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationPlayer.initKeyframeAnimation`);
+ }
+
+ this.animation = createKeyframeAnimation(keyframes, duration, delay, easing);
+ }
+
+ private onFinish() {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationPlayer.onFinish`);
+ }
+
+ if (this._finished) {
+ return;
+ }
+
+ this._finished = true;
+ this._started = false;
+ this._doneSubscriptions.forEach((fn) => fn());
+ this._doneSubscriptions = [];
+ }
}
diff --git a/nativescript-angular/animations/animations.module.ts b/nativescript-angular/animations/animations.module.ts
index b8965c06f..517773300 100644
--- a/nativescript-angular/animations/animations.module.ts
+++ b/nativescript-angular/animations/animations.module.ts
@@ -1,63 +1,59 @@
-import { NgModule, Injectable, NgZone, Provider, RendererFactory2, Optional, SkipSelf } from "@angular/core";
+import { NgModule, Injectable, Inject, NgZone, RendererFactory2, Optional, SkipSelf } from '@angular/core';
+import { DOCUMENT } from '@angular/common';
+import { AnimationBuilder } from '@angular/animations';
-import { AnimationBuilder } from "@angular/animations";
+import { AnimationDriver, ɵAnimationStyleNormalizer as AnimationStyleNormalizer, ɵWebAnimationsStyleNormalizer as WebAnimationsStyleNormalizer, ɵAnimationEngine as AnimationEngine } from '@angular/animations/browser';
-import {
- AnimationDriver,
- ɵAnimationStyleNormalizer as AnimationStyleNormalizer,
- ɵWebAnimationsStyleNormalizer as WebAnimationsStyleNormalizer,
- ɵAnimationEngine as AnimationEngine,
-} from "@angular/animations/browser";
+import { ɵAnimationRendererFactory as AnimationRendererFactory, ɵBrowserAnimationBuilder as BrowserAnimationBuilder } from '@angular/platform-browser/animations';
-import {
- ɵAnimationRendererFactory as AnimationRendererFactory,
- ɵBrowserAnimationBuilder as BrowserAnimationBuilder,
-} from "@angular/platform-browser/animations";
-
-import { NativeScriptModule } from "../nativescript.module";
-import { NativeScriptRendererFactory } from "../renderer";
-import { NativeScriptAnimationDriver } from "./animation-driver";
-import { throwIfAlreadyLoaded } from "../common/utils";
+// import { NativeScriptModule } from "../nativescript.module";
+import { NativeScriptRendererFactory } from '../renderer-factory';
+import { NativeScriptAnimationDriver } from './animation-driver';
+import { throwIfAlreadyLoaded } from '../common/utils';
+import { NativeScriptCommonModule } from '../common';
@Injectable()
export class InjectableAnimationEngine extends AnimationEngine {
- constructor(driver: AnimationDriver, normalizer: AnimationStyleNormalizer) {
- super(driver, normalizer);
- }
+ constructor(@Inject(DOCUMENT) doc: any, driver: AnimationDriver, normalizer: AnimationStyleNormalizer) {
+ super(doc.body, driver, normalizer);
+ }
}
export function instantiateSupportedAnimationDriver() {
- return new NativeScriptAnimationDriver();
+ return new NativeScriptAnimationDriver();
}
-export function instantiateRendererFactory(
- renderer: NativeScriptRendererFactory, engine: AnimationEngine, zone: NgZone) {
- return new AnimationRendererFactory(renderer, engine, zone);
+export function instantiateRendererFactory(renderer: NativeScriptRendererFactory, engine: AnimationEngine, zone: NgZone) {
+ return new AnimationRendererFactory(renderer, engine, zone);
}
export function instantiateDefaultStyleNormalizer() {
- return new WebAnimationsStyleNormalizer();
+ return new WebAnimationsStyleNormalizer();
}
-export const NATIVESCRIPT_ANIMATIONS_PROVIDERS: Provider[] = [
- {provide: AnimationDriver, useFactory: instantiateSupportedAnimationDriver},
- {provide: AnimationBuilder, useClass: BrowserAnimationBuilder},
- {provide: AnimationStyleNormalizer, useFactory: instantiateDefaultStyleNormalizer},
- {provide: AnimationEngine, useClass: InjectableAnimationEngine},
- {
- provide: RendererFactory2,
- useFactory: instantiateRendererFactory,
- deps: [NativeScriptRendererFactory, AnimationEngine, NgZone]
- }
-];
-
@NgModule({
- imports: [NativeScriptModule],
- providers: NATIVESCRIPT_ANIMATIONS_PROVIDERS,
+ imports: [NativeScriptCommonModule],
+ providers: [
+ {
+ provide: AnimationDriver,
+ useFactory: instantiateSupportedAnimationDriver,
+ },
+ { provide: AnimationBuilder, useClass: BrowserAnimationBuilder },
+ {
+ provide: AnimationStyleNormalizer,
+ useFactory: instantiateDefaultStyleNormalizer,
+ },
+ { provide: AnimationEngine, useClass: InjectableAnimationEngine },
+ {
+ provide: RendererFactory2,
+ useFactory: instantiateRendererFactory,
+ deps: [NativeScriptRendererFactory, AnimationEngine, NgZone],
+ },
+ ],
})
export class NativeScriptAnimationsModule {
- constructor(@Optional() @SkipSelf() parentModule: NativeScriptAnimationsModule) {
- // Prevents NativeScriptAnimationsModule from getting imported multiple times
- throwIfAlreadyLoaded(parentModule, "NativeScriptAnimationsModule");
- }
+ constructor(@Optional() @SkipSelf() parentModule: NativeScriptAnimationsModule) {
+ // Prevents NativeScriptAnimationsModule from getting imported multiple times
+ throwIfAlreadyLoaded(parentModule, 'NativeScriptAnimationsModule');
+ }
}
diff --git a/nativescript-angular/animations/index.ts b/nativescript-angular/animations/index.ts
index d5754740b..04e987533 100644
--- a/nativescript-angular/animations/index.ts
+++ b/nativescript-angular/animations/index.ts
@@ -1 +1,4 @@
-export * from "./animations.module";
+export * from './animations.module';
+export * from './animation-player';
+export * from './animation-driver';
+export * from './utils';
diff --git a/nativescript-angular/animations/utils.ts b/nativescript-angular/animations/utils.ts
index 60b0a1045..4eecb6fe2 100644
--- a/nativescript-angular/animations/utils.ts
+++ b/nativescript-angular/animations/utils.ts
@@ -1,59 +1,39 @@
-import {
- KeyframeAnimation,
- KeyframeAnimationInfo,
- KeyframeDeclaration,
- KeyframeInfo,
-} from "tns-core-modules/ui/animation/keyframe-animation";
-import { parseKeyframeDeclarations } from "tns-core-modules/ui/styling/css-animation-parser";
-import { animationTimingFunctionConverter } from "tns-core-modules/ui/styling/converters";
+import { KeyframeAnimation, KeyframeAnimationInfo, KeyframeDeclaration, KeyframeInfo, parseKeyframeDeclarations, animationTimingFunctionConverter } from '@nativescript/core';
export interface Keyframe {
- [key: string]: string | number;
- offset: number;
+ [key: string]: string | number;
+ offset: number;
}
const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
export function dashCaseToCamelCase(input: string): string {
- return input.replace(DASH_CASE_REGEXP, (...m: any[]) => m[1].toUpperCase());
+ return input.replace(DASH_CASE_REGEXP, (...m: any[]) => m[1].toUpperCase());
}
-export function createKeyframeAnimation(
- styles: Keyframe[],
- duration: number,
- delay: number,
- easing: string)
- : KeyframeAnimation {
-
- const info = createKeyframeAnimationInfo(styles, duration, delay, easing);
- return KeyframeAnimation.keyframeAnimationFromInfo(info);
+export function createKeyframeAnimation(styles: Keyframe[], duration: number, delay: number, easing: string): KeyframeAnimation {
+ const info = createKeyframeAnimationInfo(styles, duration, delay, easing);
+ return KeyframeAnimation.keyframeAnimationFromInfo(info);
}
-const createKeyframeAnimationInfo = (
- styles: Keyframe[],
- duration: number,
- delay: number,
- easing: string
- ): KeyframeAnimationInfo => ({
- isForwards: true,
- duration: duration || 0.01,
- delay,
- curve: getCurve(easing),
- keyframes: styles.map(parseAnimationKeyframe),
- }
-);
+const createKeyframeAnimationInfo = (styles: Keyframe[], duration: number, delay: number, easing: string): KeyframeAnimationInfo => ({
+ isForwards: true,
+ duration: duration || 0.01,
+ delay,
+ curve: getCurve(easing),
+ keyframes: styles.map(parseAnimationKeyframe),
+});
const getCurve = (value: string) => animationTimingFunctionConverter(value);
const parseAnimationKeyframe = (styles: Keyframe): KeyframeInfo => ({
- duration: getKeyframeDuration(styles),
- declarations: getDeclarations(styles),
+ duration: getKeyframeDuration(styles),
+ declarations: getDeclarations(styles),
});
const getKeyframeDuration = (styles: Keyframe): number => styles.offset;
function getDeclarations(styles: Keyframe): KeyframeDeclaration[] {
- const unparsedDeclarations: KeyframeDeclaration[] =
- Object.keys(styles).map(property => ({ property, value: styles[property] }));
+ const unparsedDeclarations: KeyframeDeclaration[] = Object.keys(styles).map((property) => ({ property, value: styles[property] }));
- return parseKeyframeDeclarations(unparsedDeclarations);
+ return parseKeyframeDeclarations(unparsedDeclarations);
}
diff --git a/nativescript-angular/app-host-view.ts b/nativescript-angular/app-host-view.ts
new file mode 100644
index 000000000..3f14d684d
--- /dev/null
+++ b/nativescript-angular/app-host-view.ts
@@ -0,0 +1,60 @@
+import { ContentView, View, ProxyViewContainer, GridLayout, Color } from '@nativescript/core';
+
+export class AppHostView extends ContentView {
+ private _ngAppRoot: View;
+ private _content: View;
+
+ constructor(backgroundColor: Color) {
+ super();
+ this.backgroundColor = backgroundColor;
+ }
+
+ get ngAppRoot(): View {
+ return this._ngAppRoot;
+ }
+
+ set ngAppRoot(value: View) {
+ this._ngAppRoot = value;
+ }
+
+ // @ts-ignore
+ get content(): View {
+ return this._content;
+ }
+
+ set content(value: View) {
+ if (this._content) {
+ this._content.parentNode = undefined;
+ }
+
+ this._content = value;
+
+ if (value) {
+ this._content.parentNode = this;
+ }
+
+ this.ngAppRoot = value;
+
+ if (this._content instanceof ProxyViewContainer) {
+ const grid = new GridLayout();
+ grid.backgroundColor = this.backgroundColor;
+ grid.addChild(this._content);
+ this.ngAppRoot = grid;
+ }
+ }
+}
+
+export class AppHostAsyncView extends GridLayout {
+ constructor(backgroundColor: Color) {
+ super();
+ this.backgroundColor = backgroundColor;
+ }
+
+ get ngAppRoot(): View {
+ return this;
+ }
+
+ set ngAppRoot(value: View) {
+ // ignored
+ }
+}
diff --git a/nativescript-angular/bin/update-app-ng-deps b/nativescript-angular/bin/update-app-ng-deps
deleted file mode 100755
index c73de3e66..000000000
--- a/nativescript-angular/bin/update-app-ng-deps
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/env node
-
-const path = require("path");
-const fs = require("fs");
-
-const browserDynamicDependency = "@angular/platform-browser-dynamic";
-const binPath = __dirname;
-const pluginPath = path.dirname(binPath);
-const pluginPackageJsonPath = path.join(pluginPath, "package.json");
-const pluginPackageJson = JSON.parse(fs.readFileSync(pluginPackageJsonPath, "utf8"));
-
-const isNgDependency = name => name.startsWith("@angular") ||
- name === "rxjs" ||
- name === "zone.js" ||
- name === "typescript";
-
-function updateDeps(deps, devDeps, newDeps) {
- Object.keys(newDeps)
- .filter(isNgDependency)
- .map(dependencyName => ({
- dependencyName,
- version: newDeps[dependencyName]
- }))
- .forEach(({ dependencyName, version }) => {
- if (devDeps[dependencyName]) {
- devDeps[dependencyName] = version;
- } else {
- deps[dependencyName] = version;
- }
-
- console.log(`Updated dependency ${dependencyName} to version: ${version}.`);
- });
-
- if (deps["@angular/animations"]) {
- const ngDep = Object.keys(newDeps).find(dep => dep.startsWith("@angular"));
- deps["@angular/animations"] = newDeps[ngDep];
- }
-}
-
-const pluginDeps = pluginPackageJson.peerDependencies;
-const projectPath = process.env.INIT_CWD || path.dirname(path.dirname(pluginPath));
-const appPackageJsonPath = path.join(projectPath, "package.json");
-const appPackageJson = JSON.parse(fs.readFileSync(appPackageJsonPath, "utf8"));
-
-const { dependencies, devDependencies } = appPackageJson;
-updateDeps(dependencies, devDependencies, pluginDeps);
-
-fs.writeFileSync(appPackageJsonPath, JSON.stringify(appPackageJson, null, 2));
-
-console.log("\nAngular dependencies updated. Don't forget to run `npm install`.");
diff --git a/nativescript-angular/bin/update-app-ng-deps.cmd b/nativescript-angular/bin/update-app-ng-deps.cmd
deleted file mode 100644
index a5e120718..000000000
--- a/nativescript-angular/bin/update-app-ng-deps.cmd
+++ /dev/null
@@ -1 +0,0 @@
-@node %~dp0\update-app-angular-deps %*
diff --git a/nativescript-angular/common.ts b/nativescript-angular/common.ts
index c6b119eef..81f6dd6e2 100644
--- a/nativescript-angular/common.ts
+++ b/nativescript-angular/common.ts
@@ -1,40 +1,21 @@
-import { CommonModule } from "@angular/common";
-import {
- NO_ERRORS_SCHEMA,
- NgModule,
-} from "@angular/core";
+import { CommonModule } from '@angular/common';
+import { NO_ERRORS_SCHEMA, NgModule } from '@angular/core';
-import {
- ModalDialogHost,
- ModalDialogService,
-} from "./directives/dialogs";
-import {
- defaultDeviceProvider,
- defaultFrameProvider,
- defaultPageProvider,
-} from "./platform-providers";
-import { NS_DIRECTIVES } from "./directives";
+import { ModalDialogService } from './directives/dialogs';
+import { defaultDeviceProvider, defaultFrameProvider, defaultPageProvider } from './platform-providers';
+import { ListViewComponent } from './directives/list-view-comp';
+import { TemplateKeyDirective } from './directives/templated-items-comp';
+import { TabViewDirective, TabViewItemDirective } from './directives/tab-view';
+import { ActionBarComponent, ActionBarScope, ActionItemDirective, NavigationButtonDirective } from './directives/action-bar';
+import { AndroidFilterComponent, IosFilterComponent } from './directives/platform-filters';
@NgModule({
- declarations: [
- ModalDialogHost,
- ...NS_DIRECTIVES,
- ],
- providers: [
- ModalDialogService,
- defaultDeviceProvider,
- defaultFrameProvider,
- defaultPageProvider,
- ],
- imports: [
- CommonModule,
- ],
- exports: [
- CommonModule,
- ModalDialogHost,
- ...NS_DIRECTIVES,
- ],
- schemas: [NO_ERRORS_SCHEMA]
+ declarations: [ListViewComponent, TemplateKeyDirective, TabViewDirective, TabViewItemDirective, ActionBarComponent, ActionBarScope, ActionItemDirective, NavigationButtonDirective, AndroidFilterComponent, IosFilterComponent],
+ providers: [ModalDialogService, defaultDeviceProvider, defaultFrameProvider, defaultPageProvider],
+ imports: [CommonModule],
+ exports: [CommonModule, ListViewComponent, TemplateKeyDirective, TabViewDirective, TabViewItemDirective, ActionBarComponent, ActionBarScope, ActionItemDirective, NavigationButtonDirective, AndroidFilterComponent, IosFilterComponent],
+ schemas: [NO_ERRORS_SCHEMA],
})
-export class NativeScriptCommonModule {
-}
+export class NativeScriptCommonModule {}
+
+export * from './directives';
diff --git a/nativescript-angular/common/detached-loader.ts b/nativescript-angular/common/detached-loader.ts
index d4ae6a73f..2a433f7d3 100644
--- a/nativescript-angular/common/detached-loader.ts
+++ b/nativescript-angular/common/detached-loader.ts
@@ -1,57 +1,61 @@
-import {
- ComponentRef, ComponentFactory, ViewContainerRef,
- Component, Type, ComponentFactoryResolver, ChangeDetectorRef
-} from "@angular/core";
-import { write } from "tns-core-modules/trace";
-
-export const CATEGORY = "detached-loader";
-function log(message: string) {
- write(message, CATEGORY);
-}
-
-
-/**
- * Wrapper component used for loading components when navigating
- * It uses DetachedContainer as selector so that it is containerRef is not attached to
- * the visual tree.
- */
-@Component({
- selector: "DetachedContainer",
- template: ``
-})
-export class DetachedLoader { // tslint:disable-line:component-class-suffix
- constructor(
- private resolver: ComponentFactoryResolver,
- private changeDetector: ChangeDetectorRef,
- private containerRef: ViewContainerRef
- ) { }
-
- private loadInLocation(componentType: Type): Promise> {
- const factory = this.resolver.resolveComponentFactory(componentType);
- const componentRef = this.containerRef.createComponent(
- factory, this.containerRef.length, this.containerRef.parentInjector);
-
- // Component is created, buit may not be checked if we are loading
- // inside component with OnPush CD strategy. Mark us for check to be sure CD will reach us.
- // We are inside a promise here so no need for setTimeout - CD should trigger
- // after the promise.
- log("DetachedLoader.loadInLocation component loaded -> markForCheck");
-
- return Promise.resolve(componentRef);
- }
-
- public detectChanges() {
- this.changeDetector.markForCheck();
- }
-
- // TODO: change this API -- async promises not needed here anymore.
- public loadComponent(componentType: Type): Promise> {
- log("DetachedLoader.loadComponent");
- return this.loadInLocation(componentType);
- }
-
- public loadWithFactory(factory: ComponentFactory): ComponentRef {
- return this.containerRef.createComponent(factory,
- this.containerRef.length, this.containerRef.parentInjector, null);
- }
-}
+import { ComponentRef, ComponentFactory, ViewContainerRef, Component, Type, ComponentFactoryResolver, ChangeDetectorRef, ApplicationRef, OnDestroy } from '@angular/core';
+import { Trace } from '@nativescript/core';
+
+/**
+ * Wrapper component used for loading components when navigating
+ * It uses DetachedContainer as selector so that it is containerRef is not attached to
+ * the visual tree.
+ */
+@Component({
+ selector: 'DetachedContainer',
+ template: ``,
+})
+export class DetachedLoader implements OnDestroy {
+ private disposeFunctions: Array<() => void> = [];
+ // tslint:disable-line:component-class-suffix
+ constructor(private resolver: ComponentFactoryResolver, private changeDetector: ChangeDetectorRef, private containerRef: ViewContainerRef, private appRef: ApplicationRef) {}
+
+ private loadInLocation(componentType: Type): Promise> {
+ const factory = this.resolver.resolveComponentFactory(componentType);
+ const componentRef = factory.create(this.containerRef.injector);
+ this.appRef.attachView(componentRef.hostView);
+
+ this.disposeFunctions.push(() => {
+ this.appRef.detachView(componentRef.hostView);
+ componentRef.destroy();
+ });
+
+ // Component is created, built may not be checked if we are loading
+ // inside component with OnPush CD strategy. Mark us for check to be sure CD will reach us.
+ // We are inside a promise here so no need for setTimeout - CD should trigger
+ // after the promise.
+ Trace.write('DetachedLoader.loadInLocation component loaded -> markForCheck', 'detached-loader');
+
+ return Promise.resolve(componentRef);
+ }
+
+ public ngOnDestroy() {
+ this.disposeFunctions.forEach((fn) => fn());
+ }
+
+ public detectChanges() {
+ this.changeDetector.markForCheck();
+ }
+
+ // TODO: change this API -- async promises not needed here anymore.
+ public loadComponent(componentType: Type): Promise> {
+ Trace.write('DetachedLoader.loadComponent', 'detached-loader');
+ return this.loadInLocation(componentType);
+ }
+
+ public loadWithFactory(factory: ComponentFactory): ComponentRef {
+ const componentRef = factory.create(this.containerRef.injector);
+ this.appRef.attachView(componentRef.hostView);
+
+ this.disposeFunctions.push(() => {
+ this.appRef.detachView(componentRef.hostView);
+ componentRef.destroy();
+ });
+ return componentRef;
+ }
+}
diff --git a/nativescript-angular/common/utils.ts b/nativescript-angular/common/utils.ts
index 7d00c6f89..5525ca7da 100644
--- a/nativescript-angular/common/utils.ts
+++ b/nativescript-angular/common/utils.ts
@@ -1,8 +1,27 @@
-export function throwIfAlreadyLoaded(
- parentModule: any,
- moduleName: string,
-) {
- if ( parentModule ) {
- throw new Error(`${moduleName} has already been loaded. Import ${moduleName} in the AppModule only.`);
- }
+/**
+ * Utility method to ensure a NgModule is only imported once in a codebase, otherwise will throw to help prevent accidental double importing
+ * @param parentModule Parent module name
+ * @param moduleName The module name
+ */
+export function throwIfAlreadyLoaded(parentModule: any, moduleName: string) {
+ if (parentModule) {
+ throw new Error(`${moduleName} has already been loaded. Import ${moduleName} in the AppModule only.`);
+ }
+}
+
+/**
+ * Utility method which will only fire the callback once ever
+ * @param fn callback to call only once
+ */
+export function once(fn: Function) {
+ let wasCalled = false;
+
+ return function wrapper() {
+ if (wasCalled) {
+ return;
+ }
+
+ wasCalled = true;
+ fn.apply(null, arguments);
+ };
}
diff --git a/nativescript-angular/directives/action-bar.ts b/nativescript-angular/directives/action-bar.ts
index d559d47b1..a6e20e937 100644
--- a/nativescript-angular/directives/action-bar.ts
+++ b/nativescript-angular/directives/action-bar.ts
@@ -1,165 +1,160 @@
-import { Directive, Component, ElementRef, Optional, OnDestroy } from "@angular/core";
-import {
- ActionBar,
- ActionItem,
- ActionItems,
- NavigationButton,
-} from "tns-core-modules/ui/action-bar";
-import { Page } from "tns-core-modules/ui/page";
-
-import { isBlank } from "../lang-facade";
-import {
- NgView,
- ViewClassMeta,
- ViewExtensions,
- isInvisibleNode,
- isView,
- registerElement,
-} from "../element-registry";
+import { Directive, Component, ElementRef, Optional, OnDestroy } from '@angular/core';
+import { ActionBar, ActionItem, ActionItems, NavigationButton, Page } from '@nativescript/core';
+
+import { isBlank } from '../lang-facade';
+import { NgView, ViewClassMeta, ViewExtensions, isInvisibleNode, isView, registerElement } from '../element-registry';
export function isActionItem(view: any): view is ActionItem {
- return view instanceof ActionItem;
+ return view instanceof ActionItem;
}
export function isNavigationButton(view: any): view is NavigationButton {
- return view instanceof NavigationButton;
+ return view instanceof NavigationButton;
}
-type NgActionBar = (ActionBar & ViewExtensions);
+type NgActionBar = ActionBar & ViewExtensions;
const actionBarMeta: ViewClassMeta = {
- skipAddToDom: true,
- insertChild: (parent: NgActionBar, child: NgView, next: any) => {
- if (isInvisibleNode(child)) {
- return;
- } else if (isNavigationButton(child)) {
- parent.navigationButton = child;
- child.parentNode = parent;
- } else if (isActionItem(child)) {
- addActionItem(parent, child, next);
- child.parentNode = parent;
- } else if (isView(child)) {
- parent.titleView = child;
- }
- },
- removeChild: (parent: NgActionBar, child: NgView) => {
- if (isInvisibleNode(child)) {
- return;
- } else if (isNavigationButton(child)) {
- if (parent.navigationButton === child) {
- parent.navigationButton = null;
- }
-
- child.parentNode = null;
- } else if (isActionItem(child)) {
- parent.actionItems.removeItem(child);
- child.parentNode = null;
- } else if (isView(child) && parent.titleView && parent.titleView === child) {
- parent.titleView = null;
- }
- },
+ skipAddToDom: true,
+ insertChild: (parent: NgActionBar, child: NgView, next: any) => {
+ if (isInvisibleNode(child)) {
+ return;
+ } else if (isNavigationButton(child)) {
+ parent.navigationButton = child;
+ child.parentNode = parent;
+ } else if (isActionItem(child)) {
+ addActionItem(parent, child, next);
+ child.parentNode = parent;
+ } else if (isView(child)) {
+ parent.titleView = child;
+ }
+ },
+ removeChild: (parent: NgActionBar, child: NgView) => {
+ if (isInvisibleNode(child)) {
+ return;
+ } else if (isNavigationButton(child)) {
+ if (parent.navigationButton === child) {
+ parent.navigationButton = null;
+ }
+
+ child.parentNode = null;
+ } else if (isActionItem(child)) {
+ parent.actionItems.removeItem(child);
+ child.parentNode = null;
+ } else if (isView(child) && parent.titleView && parent.titleView === child) {
+ parent.titleView = null;
+ }
+ },
};
const addActionItem = (bar: NgActionBar, item: ActionItem, next: ActionItem) => {
- if (next) {
- insertActionItemBefore(bar, item, next);
- } else {
- appendActionItem(bar, item);
- }
+ if (next) {
+ insertActionItemBefore(bar, item, next);
+ } else {
+ appendActionItem(bar, item);
+ }
};
const insertActionItemBefore = (bar: NgActionBar, item: ActionItem, next: ActionItem) => {
- const actionItems: ActionItems = bar.actionItems;
- const actionItemsCollection: ActionItem[] = actionItems.getItems();
+ const actionItems: ActionItems = bar.actionItems;
+ const actionItemsCollection: ActionItem[] = actionItems.getItems();
- const indexToInsert = actionItemsCollection.indexOf(next);
- actionItemsCollection.splice(indexToInsert, 0, item);
+ const indexToInsert = actionItemsCollection.indexOf(next);
+ actionItemsCollection.splice(indexToInsert, 0, item);
- (actionItems).setItems(actionItemsCollection);
+ (actionItems).setItems(actionItemsCollection);
};
const appendActionItem = (bar: NgActionBar, item: ActionItem) => {
- bar.actionItems.addItem(item);
+ bar.actionItems.addItem(item);
};
-registerElement("ActionBar", () => require("ui/action-bar").ActionBar, actionBarMeta);
-registerElement("ActionItem", () => require("ui/action-bar").ActionItem);
-registerElement("NavigationButton", () => require("ui/action-bar").NavigationButton);
+registerElement('ActionBar', () => ActionBar, actionBarMeta);
+registerElement('ActionItem', () => ActionItem);
+registerElement('NavigationButton', () => NavigationButton);
@Component({
- selector: "ActionBar",
- template: ""
+ selector: 'ActionBar',
+ template: '',
})
export class ActionBarComponent {
- constructor(public element: ElementRef, private page: Page) {
- if (isBlank(this.page.actionBarHidden)) {
- this.page.actionBarHidden = false;
- }
- this.page.actionBar = this.element.nativeElement;
- this.page.actionBar.update();
- }
+ constructor(public element: ElementRef, private page: Page) {
+ if (!this.page) {
+ throw new Error('Inside ActionBarComponent but no Page found in DI.');
+ }
+
+ if (isBlank(this.page.actionBarHidden)) {
+ this.page.actionBarHidden = false;
+ }
+ this.page.actionBar = this.element.nativeElement;
+ this.page.actionBar.update();
+ }
}
@Component({
- selector: "ActionBarExtension",
- template: ""
+ selector: 'ActionBarExtension',
+ template: '',
})
-export class ActionBarScope { // tslint:disable-line:component-class-suffix
- constructor(private page: Page) {
- }
-
- public onNavButtonInit(navBtn: NavigationButtonDirective) {
- this.page.actionBar.navigationButton = navBtn.element.nativeElement;
- }
-
- public onNavButtonDestroy(navBtn: NavigationButtonDirective) {
- const nav = navBtn.element.nativeElement;
- if (nav && this.page.actionBar.navigationButton === nav) {
- this.page.actionBar.navigationButton = null;
- }
- }
-
- public onActionInit(item: ActionItemDirective) {
- this.page.actionBar.actionItems.addItem(item.element.nativeElement);
- }
-
- public onActionDestroy(item: ActionItemDirective) {
- if (item.element.nativeElement.actionBar) {
- this.page.actionBar.actionItems.removeItem(item.element.nativeElement);
- }
- }
+export class ActionBarScope {
+ // tslint:disable-line:component-class-suffix
+ constructor(private page: Page) {
+ if (!this.page) {
+ throw new Error('Inside ActionBarScope but no Page found in DI.');
+ }
+ }
+
+ public onNavButtonInit(navBtn: NavigationButtonDirective) {
+ this.page.actionBar.navigationButton = navBtn.element.nativeElement;
+ }
+
+ public onNavButtonDestroy(navBtn: NavigationButtonDirective) {
+ const nav = navBtn.element.nativeElement;
+ if (nav && this.page.actionBar.navigationButton === nav) {
+ this.page.actionBar.navigationButton = null;
+ }
+ }
+
+ public onActionInit(item: ActionItemDirective) {
+ this.page.actionBar.actionItems.addItem(item.element.nativeElement);
+ }
+
+ public onActionDestroy(item: ActionItemDirective) {
+ if (item.element.nativeElement.actionBar) {
+ this.page.actionBar.actionItems.removeItem(item.element.nativeElement);
+ }
+ }
}
@Directive({
- selector: "ActionItem" // tslint:disable-line:directive-selector
+ selector: 'ActionItem', // tslint:disable-line:directive-selector
})
export class ActionItemDirective implements OnDestroy {
- constructor(public element: ElementRef, @Optional() private ownerScope: ActionBarScope) {
- if (this.ownerScope) {
- this.ownerScope.onActionInit(this);
- }
- }
-
- ngOnDestroy() {
- if (this.ownerScope) {
- this.ownerScope.onActionDestroy(this);
- }
- }
+ constructor(public element: ElementRef, @Optional() private ownerScope: ActionBarScope) {
+ if (this.ownerScope) {
+ this.ownerScope.onActionInit(this);
+ }
+ }
+
+ ngOnDestroy() {
+ if (this.ownerScope) {
+ this.ownerScope.onActionDestroy(this);
+ }
+ }
}
@Directive({
- selector: "NavigationButton" // tslint:disable-line:directive-selector
+ selector: 'NavigationButton', // tslint:disable-line:directive-selector
})
export class NavigationButtonDirective implements OnDestroy {
- constructor(public element: ElementRef, @Optional() private ownerScope: ActionBarScope) {
- if (this.ownerScope) {
- this.ownerScope.onNavButtonInit(this);
- }
- }
-
- ngOnDestroy() {
- if (this.ownerScope) {
- this.ownerScope.onNavButtonDestroy(this);
- }
- }
+ constructor(public element: ElementRef, @Optional() private ownerScope: ActionBarScope) {
+ if (this.ownerScope) {
+ this.ownerScope.onNavButtonInit(this);
+ }
+ }
+
+ ngOnDestroy() {
+ if (this.ownerScope) {
+ this.ownerScope.onNavButtonDestroy(this);
+ }
+ }
}
diff --git a/nativescript-angular/directives/dialogs.ts b/nativescript-angular/directives/dialogs.ts
index 8012cf1d1..7e5aa78bf 100644
--- a/nativescript-angular/directives/dialogs.ts
+++ b/nativescript-angular/directives/dialogs.ts
@@ -1,132 +1,134 @@
-import {
- ComponentFactoryResolver,
- ComponentRef,
- Directive,
- Injectable,
- NgModuleRef,
- ReflectiveInjector,
- Type,
- ViewContainerRef,
-} from "@angular/core";
-
-import { Page } from "tns-core-modules/ui/page";
-import { View } from "tns-core-modules/ui/core/view";
-
-import { DetachedLoader } from "../common/detached-loader";
-import { PageFactory, PAGE_FACTORY } from "../platform-providers";
-
-export interface ModalDialogOptions {
- context?: any;
- fullscreen?: boolean;
- viewContainerRef?: ViewContainerRef;
- moduleRef?: NgModuleRef;
+import { ComponentFactoryResolver, ComponentRef, Injectable, Injector, NgModuleRef, NgZone, Type, ViewContainerRef } from '@angular/core';
+import { Frame, View, ViewBase, ProxyViewContainer, ShowModalOptions } from '@nativescript/core';
+
+import { NSLocationStrategy } from '../router/ns-location-strategy';
+import { AppHostView, AppHostAsyncView } from '../app-host-view';
+import { DetachedLoader } from '../common/detached-loader';
+import { PageFactory, PAGE_FACTORY } from '../platform-providers';
+import { once } from '../common/utils';
+
+export type BaseShowModalOptions = Pick>;
+
+export interface ModalDialogOptions extends BaseShowModalOptions {
+ context?: any;
+ viewContainerRef?: ViewContainerRef;
+ moduleRef?: NgModuleRef;
+ target?: View;
}
-export class ModalDialogParams {
- constructor(
- public context: any = {},
- public closeCallback: (...args) => any) {
- }
+export interface ShowDialogOptions extends BaseShowModalOptions {
+ containerRef: ViewContainerRef;
+ context: any;
+ doneCallback;
+ pageFactory: PageFactory;
+ parentView: ViewBase;
+ resolver: ComponentFactoryResolver;
+ type: Type;
}
-interface ShowDialogOptions {
- containerRef: ViewContainerRef;
- context: any;
- doneCallback;
- fullscreen: boolean;
- pageFactory: PageFactory;
- parentPage: Page;
- resolver: ComponentFactoryResolver;
- type: Type;
+export class ModalDialogParams {
+ constructor(public context: any = {}, public closeCallback: (...args) => any) {}
}
@Injectable()
export class ModalDialogService {
- public showModal(type: Type,
- {viewContainerRef, moduleRef, context, fullscreen}: ModalDialogOptions
- ): Promise {
- if (!viewContainerRef) {
- throw new Error(
- "No viewContainerRef: " +
- "Make sure you pass viewContainerRef in ModalDialogOptions."
- );
- }
-
- const parentPage: Page = viewContainerRef.injector.get(Page);
- const pageFactory: PageFactory = viewContainerRef.injector.get(PAGE_FACTORY);
-
- // resolve from particular module (moduleRef)
- // or from same module as parentPage (viewContainerRef)
- const componentContainer = moduleRef || viewContainerRef;
- const resolver = componentContainer.injector.get(ComponentFactoryResolver);
-
- return new Promise(resolve => {
- setTimeout(() => ModalDialogService.showDialog({
- containerRef: viewContainerRef,
- context,
- doneCallback: resolve,
- fullscreen,
- pageFactory,
- parentPage,
- resolver,
- type,
- }), 10);
- });
- }
-
- private static showDialog({
- containerRef,
- context,
- doneCallback,
- fullscreen,
- pageFactory,
- parentPage,
- resolver,
- type,
- }: ShowDialogOptions): void {
- const page = pageFactory({ isModal: true, componentType: type });
-
- let detachedLoaderRef: ComponentRef;
- const closeCallback = (...args) => {
- doneCallback.apply(undefined, args);
- page.closeModal();
- detachedLoaderRef.instance.detectChanges();
- detachedLoaderRef.destroy();
- };
-
- const modalParams = new ModalDialogParams(context, closeCallback);
-
- const providers = ReflectiveInjector.resolve([
- { provide: Page, useValue: page },
- { provide: ModalDialogParams, useValue: modalParams },
- ]);
-
- const childInjector = ReflectiveInjector.fromResolvedProviders(
- providers, containerRef.parentInjector);
- const detachedFactory = resolver.resolveComponentFactory(DetachedLoader);
- detachedLoaderRef = containerRef.createComponent(detachedFactory, -1, childInjector, null);
- detachedLoaderRef.instance.loadComponent(type).then((compRef) => {
- const componentView = compRef.location.nativeElement;
-
- if (componentView.parent) {
- (componentView.parent).removeChild(componentView);
- }
-
- page.content = componentView;
- parentPage.showModal(page, context, closeCallback, fullscreen);
- });
- }
-}
-
-
-@Directive({
- selector: "[modal-dialog-host]" // tslint:disable-line:directive-selector
-})
-export class ModalDialogHost { // tslint:disable-line:directive-class-suffix
- constructor() {
- throw new Error("ModalDialogHost is deprecated. " +
- "Call ModalDialogService.showModal() " +
- "by passing ViewContainerRef in the options instead."
- );
- }
+ constructor(private location: NSLocationStrategy, private zone: NgZone) {}
+
+ public showModal(type: Type, options: ModalDialogOptions): Promise {
+ if (!options.viewContainerRef) {
+ throw new Error('No viewContainerRef: ' + 'Make sure you pass viewContainerRef in ModalDialogOptions.');
+ }
+
+ let parentView = options.viewContainerRef.element.nativeElement;
+ if (options.target) {
+ parentView = options.target;
+ }
+
+ if ((parentView instanceof AppHostView || parentView instanceof AppHostAsyncView) && parentView.ngAppRoot) {
+ parentView = parentView.ngAppRoot;
+ }
+
+ // _ngDialogRoot is the first child of the previously detached proxy.
+ // It should have 'viewController' (iOS) or '_dialogFragment' (Android) available for
+ // presenting future modal views.
+ if (parentView._ngDialogRoot) {
+ parentView = parentView._ngDialogRoot;
+ }
+
+ const pageFactory: PageFactory = options.viewContainerRef.injector.get(PAGE_FACTORY);
+
+ // resolve from particular module (moduleRef)
+ // or from same module as parentView (viewContainerRef)
+ const componentContainer = options.moduleRef || options.viewContainerRef;
+ const resolver = componentContainer.injector.get(ComponentFactoryResolver);
+
+ let frame = parentView;
+ if (!(parentView instanceof Frame)) {
+ frame = (parentView.page && parentView.page.frame) || Frame.topmost();
+ }
+
+ this.location._beginModalNavigation(frame);
+
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ try {
+ this._showDialog({
+ ...options,
+ containerRef: options.viewContainerRef,
+ context: options.context,
+ doneCallback: resolve,
+ pageFactory,
+ parentView,
+ resolver,
+ type,
+ });
+ } catch (err) {
+ reject(err);
+ }
+ }, 10);
+ });
+ }
+
+ private _showDialog(options: ShowDialogOptions): void {
+ let componentView: View;
+ let detachedLoaderRef: ComponentRef;
+
+ const closeCallback = once((...args) => {
+ options.doneCallback.apply(undefined, args);
+ if (componentView) {
+ componentView.closeModal();
+ this.location._closeModalNavigation();
+ this.zone.run(() => {
+ detachedLoaderRef.instance.detectChanges();
+ detachedLoaderRef.destroy();
+ });
+ }
+ });
+
+ const modalParams = new ModalDialogParams(options.context, closeCallback);
+
+ const childInjector = Injector.create({
+ providers: [{ provide: ModalDialogParams, useValue: modalParams }],
+ parent: options.containerRef.injector,
+ });
+ const detachedFactory = options.resolver.resolveComponentFactory(DetachedLoader);
+ detachedLoaderRef = options.containerRef.createComponent(detachedFactory, 0, childInjector, null);
+ this.zone.run(() => {
+ detachedLoaderRef.instance.loadComponent(options.type).then((compRef) => {
+ const detachedProxy = compRef.location.nativeElement;
+
+ if (detachedProxy.getChildrenCount() > 1) {
+ throw new Error('Modal content has more than one root view.');
+ }
+ componentView = detachedProxy.getChildAt(0);
+
+ if (componentView.parent) {
+ (componentView.parent)._ngDialogRoot = componentView;
+ (componentView.parent).removeChild(componentView);
+ }
+
+ options.parentView.showModal(componentView, { ...options, closeCallback });
+ });
+ });
+ }
}
diff --git a/nativescript-angular/directives/index.ts b/nativescript-angular/directives/index.ts
index 9a8a6fd69..29bef98f9 100644
--- a/nativescript-angular/directives/index.ts
+++ b/nativescript-angular/directives/index.ts
@@ -1,37 +1,10 @@
-import { ListViewComponent, TemplateKeyDirective } from "./list-view-comp";
-import { TabViewDirective, TabViewItemDirective } from "./tab-view";
-import {
- ActionBarComponent,
- ActionBarScope,
- ActionItemDirective,
- NavigationButtonDirective
-} from "./action-bar";
-import { AndroidFilterComponent, IosFilterComponent } from "./platform-filters";
+import { ListViewComponent } from './list-view-comp';
+import { TemplateKeyDirective, TemplatedItemsComponent, TEMPLATED_ITEMS_COMPONENT } from './templated-items-comp';
+import { TabViewDirective, TabViewItemDirective } from './tab-view';
+import { ActionBarComponent, ActionBarScope, ActionItemDirective, NavigationButtonDirective } from './action-bar';
+import { AndroidFilterComponent, IosFilterComponent } from './platform-filters';
+export { ModalDialogOptions, ModalDialogParams, ModalDialogService } from './dialogs';
-export {
- ListViewComponent,
- SetupItemViewArgs,
- TemplateKeyDirective
-} from "./list-view-comp";
+export const NS_DIRECTIVES = [ListViewComponent, TemplateKeyDirective, TabViewDirective, TabViewItemDirective, ActionBarComponent, ActionBarScope, ActionItemDirective, NavigationButtonDirective, AndroidFilterComponent, IosFilterComponent];
-export { TabViewDirective, TabViewItemDirective } from "./tab-view";
-export {
- ActionBarComponent,
- ActionBarScope,
- ActionItemDirective,
- NavigationButtonDirective
-} from "./action-bar";
-export { AndroidFilterComponent, IosFilterComponent } from "./platform-filters";
-
-export const NS_DIRECTIVES = [
- ListViewComponent,
- TemplateKeyDirective,
- TabViewDirective,
- TabViewItemDirective,
- ActionBarComponent,
- ActionBarScope,
- ActionItemDirective,
- NavigationButtonDirective,
- AndroidFilterComponent,
- IosFilterComponent,
-];
+export { ListViewComponent, TemplateKeyDirective, TemplatedItemsComponent, TabViewDirective, TabViewItemDirective, ActionBarComponent, ActionBarScope, ActionItemDirective, NavigationButtonDirective, AndroidFilterComponent, IosFilterComponent, TEMPLATED_ITEMS_COMPONENT };
diff --git a/nativescript-angular/directives/list-view-comp.ts b/nativescript-angular/directives/list-view-comp.ts
index a04f244b4..62e1eadfe 100644
--- a/nativescript-angular/directives/list-view-comp.ts
+++ b/nativescript-angular/directives/list-view-comp.ts
@@ -1,242 +1,23 @@
-import {
- AfterContentInit,
- ChangeDetectionStrategy,
- Component,
- ContentChild,
- Directive,
- DoCheck,
- ElementRef,
- EmbeddedViewRef,
- EventEmitter,
- Host,
- Input,
- IterableDiffer,
- IterableDiffers,
- OnDestroy,
- Output,
- TemplateRef,
- ViewChild,
- ViewContainerRef,
- ɵisListLikeIterable as isListLikeIterable
-} from "@angular/core";
-import { ListView, ItemEventData } from "tns-core-modules/ui/list-view";
-import { View, KeyedTemplate } from "tns-core-modules/ui/core/view";
-import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base";
-import { ObservableArray } from "tns-core-modules/data/observable-array";
-import { profile } from "tns-core-modules/profiling";
-
-import { getSingleViewRecursive } from "../element-registry";
-import { listViewLog, listViewError } from "../trace";
-
-const NG_VIEW = "_ngViewRef";
-
-export class ListItemContext {
- constructor(
- public $implicit?: any,
- public item?: any,
- public index?: number,
- public even?: boolean,
- public odd?: boolean
- ) {
- }
-}
-
-export interface SetupItemViewArgs {
- view: EmbeddedViewRef;
- data: any;
- index: number;
- context: ListItemContext;
-}
+import { ChangeDetectionStrategy, Component, ElementRef, IterableDiffers, forwardRef, NgZone } from '@angular/core';
+import { ListView } from '@nativescript/core';
+import { TEMPLATED_ITEMS_COMPONENT, TemplatedItemsComponent } from './templated-items-comp';
@Component({
- selector: "ListView",
- template: `
-
-
- `,
- changeDetection: ChangeDetectionStrategy.OnPush
+ selector: 'ListView',
+ template: `
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ providers: [{ provide: TEMPLATED_ITEMS_COMPONENT, useExisting: forwardRef(() => ListViewComponent) }],
})
-export class ListViewComponent implements DoCheck, OnDestroy, AfterContentInit {
- public get nativeElement(): ListView {
- return this.listView;
- }
-
- private listView: ListView;
- private _items: any;
- private _differ: IterableDiffer;
- private _templateMap: Map;
-
- @ViewChild("loader", { read: ViewContainerRef }) loader: ViewContainerRef;
-
- @Output() public setupItemView = new EventEmitter();
-
- @ContentChild(TemplateRef) itemTemplateQuery: TemplateRef;
-
- itemTemplate: TemplateRef;
-
- @Input()
- get items() {
- return this._items;
- }
-
- set items(value: any) {
- this._items = value;
- let needDiffer = true;
- if (value instanceof ObservableArray) {
- needDiffer = false;
- }
- if (needDiffer && !this._differ && isListLikeIterable(value)) {
- this._differ = this._iterableDiffers.find(this._items)
- .create((_index, item) => { return item; });
- }
-
- this.listView.items = this._items;
- }
-
- constructor(_elementRef: ElementRef,
- private _iterableDiffers: IterableDiffers) {
- this.listView = _elementRef.nativeElement;
-
- this.listView.on("itemLoading", this.onItemLoading, this);
- }
-
- ngAfterContentInit() {
- listViewLog("ListView.ngAfterContentInit()");
- this.setItemTemplates();
- }
-
- ngOnDestroy() {
- this.listView.off("itemLoading", this.onItemLoading, this);
- }
-
- private setItemTemplates() {
- // The itemTemplateQuery may be changed after list items are added that contain inside,
- // so cache and use only the original template to avoid errors.
- this.itemTemplate = this.itemTemplateQuery;
-
- if (this._templateMap) {
- listViewLog("Setting templates");
-
- const templates: KeyedTemplate[] = [];
- this._templateMap.forEach(value => {
- templates.push(value);
- });
- this.listView.itemTemplates = templates;
- }
- }
-
- public registerTemplate(key: string, template: TemplateRef) {
- listViewLog("registerTemplate for key: " + key);
- if (!this._templateMap) {
- this._templateMap = new Map();
- }
-
- const keyedTemplate = {
- key,
- createView: () => {
- listViewLog("registerTemplate for key: " + key);
-
- const viewRef = this.loader.createEmbeddedView(template, new ListItemContext(), 0);
- const resultView = getItemViewRoot(viewRef);
- resultView[NG_VIEW] = viewRef;
-
- return resultView;
- }
- };
-
- this._templateMap.set(key, keyedTemplate);
- }
-
- @profile
- public onItemLoading(args: ItemEventData) {
- if (!args.view && !this.itemTemplate) {
- return;
- }
-
- const index = args.index;
- const items = (args.object).items;
- const currentItem = typeof items.getItem === "function" ? items.getItem(index) : items[index];
- let viewRef: EmbeddedViewRef;
-
- if (args.view) {
- listViewLog("onItemLoading: " + index + " - Reusing existing view");
- viewRef = args.view[NG_VIEW];
- // Getting angular view from original element (in cases when ProxyViewContainer
- // is used NativeScript internally wraps it in a StackLayout)
- if (!viewRef && args.view instanceof LayoutBase && args.view.getChildrenCount() > 0) {
- viewRef = args.view.getChildAt(0)[NG_VIEW];
- }
-
- if (!viewRef) {
- listViewError("ViewReference not found for item " + index + ". View recycling is not working");
- }
- }
-
- if (!viewRef) {
- listViewLog("onItemLoading: " + index + " - Creating view from template");
- viewRef = this.loader.createEmbeddedView(this.itemTemplate, new ListItemContext(), 0);
- args.view = getItemViewRoot(viewRef);
- args.view[NG_VIEW] = viewRef;
- }
-
- this.setupViewRef(viewRef, currentItem, index);
-
- this.detectChangesOnChild(viewRef, index);
- }
-
- public setupViewRef(viewRef: EmbeddedViewRef, data: any, index: number): void {
- const context = viewRef.context;
- context.$implicit = data;
- context.item = data;
- context.index = index;
- context.even = (index % 2 === 0);
- context.odd = !context.even;
-
- this.setupItemView.next({ view: viewRef, data: data, index: index, context: context });
- }
-
- @profile
- private detectChangesOnChild(viewRef: EmbeddedViewRef, index: number) {
- listViewLog("Manually detect changes in child: " + index);
- viewRef.markForCheck();
- viewRef.detectChanges();
- }
-
- ngDoCheck() {
- if (this._differ) {
- listViewLog("ngDoCheck() - execute differ");
- const changes = this._differ.diff(this._items);
- if (changes) {
- listViewLog("ngDoCheck() - refresh");
- this.listView.refresh();
- }
- }
- }
-}
-
-export interface ComponentView {
- rootNodes: Array;
- destroy(): void;
-}
-
-export type RootLocator = (nodes: Array, nestLevel: number) => View;
-
-export function getItemViewRoot(viewRef: ComponentView, rootLocator: RootLocator = getSingleViewRecursive): View {
- const rootView = rootLocator(viewRef.rootNodes, 0);
- return rootView;
-}
+export class ListViewComponent extends TemplatedItemsComponent {
+ public get nativeElement(): ListView {
+ return this.templatedItemsView;
+ }
-@Directive({ selector: "[nsTemplateKey]" })
-export class TemplateKeyDirective {
- constructor(
- private templateRef: TemplateRef,
- @Host() private list: ListViewComponent) {
- }
+ protected templatedItemsView: ListView;
- @Input()
- set nsTemplateKey(value: any) {
- if (this.list && this.templateRef) {
- this.list.registerTemplate(value, this.templateRef);
- }
- }
+ constructor(_elementRef: ElementRef, _iterableDiffers: IterableDiffers, zone: NgZone) {
+ super(_elementRef, _iterableDiffers, zone);
+ }
}
diff --git a/nativescript-angular/directives/platform-filters.ts b/nativescript-angular/directives/platform-filters.ts
index 22105987a..55f5aac5b 100644
--- a/nativescript-angular/directives/platform-filters.ts
+++ b/nativescript-angular/directives/platform-filters.ts
@@ -1,25 +1,24 @@
-import { Component, Inject } from "@angular/core";
-import { Device, platformNames } from "tns-core-modules/platform";
-import { DEVICE } from "../platform-providers";
-
-@Component({
- selector: "android",
- template: ``,
-})
-export class AndroidFilterComponent {
- public show: boolean;
- constructor( @Inject(DEVICE) device: Device) {
- this.show = (device.os === platformNames.android);
- }
-}
-
-@Component({
- selector: "ios",
- template: ``,
-})
-export class IosFilterComponent {
- public show: boolean;
- constructor( @Inject(DEVICE) device: Device) {
- this.show = (device.os === platformNames.ios);
- }
-}
+import { Component, Inject } from '@angular/core';
+import { isIOS, isAndroid } from '@nativescript/core';
+
+@Component({
+ selector: 'android',
+ template: ``,
+})
+export class AndroidFilterComponent {
+ public show: boolean;
+ constructor() {
+ this.show = isAndroid;
+ }
+}
+
+@Component({
+ selector: 'ios',
+ template: ``,
+})
+export class IosFilterComponent {
+ public show: boolean;
+ constructor() {
+ this.show = isIOS;
+ }
+}
diff --git a/nativescript-angular/directives/tab-view.ts b/nativescript-angular/directives/tab-view.ts
index a945531f9..2fa2a272d 100644
--- a/nativescript-angular/directives/tab-view.ts
+++ b/nativescript-angular/directives/tab-view.ts
@@ -1,117 +1,137 @@
-import {
- AfterViewInit,
- Directive,
- ElementRef,
- Input,
- OnInit,
- TemplateRef,
- ViewContainerRef,
-} from "@angular/core";
-import { TabView, TabViewItem } from "tns-core-modules/ui/tab-view";
-
-import { InvisibleNode } from "../element-registry";
-import { rendererLog } from "../trace";
-import { isBlank } from "../lang-facade";
+import { AfterViewInit, Directive, ElementRef, Input, OnInit, TemplateRef, ViewContainerRef } from '@angular/core';
+import { TabView, TabViewItem } from '@nativescript/core';
+
+import { InvisibleNode } from '../element-registry';
+import { NativeScriptDebug } from '../trace';
+import { isBlank } from '../lang-facade';
+
+type TextTransform = 'initial' | 'none' | 'capitalize' | 'uppercase' | 'lowercase';
+export interface TabViewItemDef {
+ title?: string;
+ iconSource?: string;
+ textTransform?: TextTransform;
+}
@Directive({
- selector: "TabView", // tslint:disable-line:directive-selector
+ selector: 'TabView', // tslint:disable-line:directive-selector
})
export class TabViewDirective implements AfterViewInit {
- public tabView: TabView;
- private _selectedIndex: number;
- private viewInitialized: boolean;
-
- @Input()
- get selectedIndex(): number {
- return this._selectedIndex;
- }
-
- set selectedIndex(value) {
- this._selectedIndex = value;
- if (this.viewInitialized) {
- this.tabView.selectedIndex = this._selectedIndex;
- }
- }
-
- constructor(element: ElementRef) {
- this.tabView = element.nativeElement;
- }
-
- ngAfterViewInit() {
- this.viewInitialized = true;
- rendererLog("this._selectedIndex: " + this._selectedIndex);
- if (!isBlank(this._selectedIndex)) {
- this.tabView.selectedIndex = this._selectedIndex;
- }
- }
+ public tabView: TabView;
+ private _selectedIndex: number;
+ private viewInitialized: boolean;
+
+ @Input()
+ get selectedIndex(): number {
+ return this._selectedIndex;
+ }
+
+ set selectedIndex(value) {
+ this._selectedIndex = value;
+ if (this.viewInitialized) {
+ this.tabView.selectedIndex = this._selectedIndex;
+ }
+ }
+
+ constructor(element: ElementRef) {
+ this.tabView = element.nativeElement;
+ }
+
+ ngAfterViewInit() {
+ this.viewInitialized = true;
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog('this._selectedIndex: ' + this._selectedIndex);
+ }
+ if (!isBlank(this._selectedIndex)) {
+ this.tabView.selectedIndex = this._selectedIndex;
+ }
+ }
}
@Directive({
- selector: "[tabItem]" // tslint:disable-line:directive-selector
+ selector: '[tabItem]', // tslint:disable-line:directive-selector
})
export class TabViewItemDirective implements OnInit {
- private item: TabViewItem;
- private _title: string;
- private _iconSource: string;
-
- constructor(
- private owner: TabViewDirective,
- private templateRef: TemplateRef,
- private viewContainer: ViewContainerRef
- ) {
- }
-
- @Input("tabItem") config: any; // tslint:disable-line:no-input-rename
-
- @Input()
- get title() {
- return this._title;
- }
-
- set title(value: string) {
- if (this._title !== value) {
- this._title = value;
- this.ensureItem();
- this.item.title = this._title;
- }
- }
-
- @Input()
- get iconSource() {
- return this._iconSource;
- }
-
- set iconSource(value: string) {
- if (this._iconSource !== value) {
- this._iconSource = value;
- this.ensureItem();
- this.item.iconSource = this._iconSource;
- }
- }
-
- private ensureItem() {
- if (!this.item) {
- this.item = new TabViewItem();
- }
- }
-
- ngOnInit() {
- this.ensureItem();
- if (this.config) {
- this.item.title = this._title || this.config.title;
- this.item.iconSource = this._iconSource || this.config.iconSource;
- }
-
- const viewRef = this.viewContainer.createEmbeddedView(this.templateRef);
- // Filter out text nodes and comments
- const realViews = viewRef.rootNodes.filter(node =>
- !(node instanceof InvisibleNode));
-
- if (realViews.length > 0) {
- this.item.view = realViews[0];
-
- const newItems = (this.owner.tabView.items || []).concat([this.item]);
- this.owner.tabView.items = newItems;
- }
- }
+ private item: TabViewItem;
+ private _config: TabViewItemDef;
+
+ constructor(private owner: TabViewDirective, private templateRef: TemplateRef, private viewContainer: ViewContainerRef) {}
+
+ @Input('tabItem')
+ set config(config: TabViewItemDef) {
+ if (!this._config || this._config.iconSource !== config.iconSource || this._config.title !== config.title || this._config.textTransform !== config.textTransform) {
+ this._config = config;
+ this.applyConfig();
+ }
+ }
+
+ get config(): TabViewItemDef {
+ // tslint:disable-line:no-input-rename
+ return this._config || {};
+ }
+
+ @Input()
+ set title(title: string) {
+ this.config = Object.assign(this.config, { title });
+ }
+
+ get title() {
+ return this.config.title;
+ }
+
+ @Input()
+ set iconSource(iconSource: string) {
+ this.config = Object.assign(this.config, { iconSource });
+ }
+
+ get iconSource() {
+ return this.config.iconSource;
+ }
+
+ @Input()
+ set textTransform(textTransform: TextTransform) {
+ this.config = Object.assign(this.config, { textTransform });
+ }
+
+ get textTransform() {
+ return this.config.textTransform;
+ }
+
+ private ensureItem() {
+ if (!this.item) {
+ this.item = new TabViewItem();
+ }
+ }
+
+ private applyConfig() {
+ this.ensureItem();
+
+ if (this.config.title) {
+ this.item.title = this.config.title;
+ }
+
+ if (this.config.iconSource) {
+ this.item.iconSource = this.config.iconSource;
+ }
+
+ // TabViewItem textTransform has a default value for Android that kick in
+ // only if no value (even a null value) is set.
+ if (this.config.textTransform) {
+ this.item.textTransform = this.config.textTransform;
+ }
+ }
+
+ ngOnInit() {
+ this.applyConfig();
+
+ const viewRef = this.viewContainer.createEmbeddedView(this.templateRef);
+ // Filter out text nodes and comments
+ const realViews = viewRef.rootNodes.filter((node) => !(node instanceof InvisibleNode));
+
+ if (realViews.length > 0) {
+ this.item.view = realViews[0];
+
+ const newItems = (this.owner.tabView.items || []).concat([this.item]);
+ this.owner.tabView.items = newItems;
+ }
+ }
}
diff --git a/nativescript-angular/directives/templated-items-comp.ts b/nativescript-angular/directives/templated-items-comp.ts
new file mode 100644
index 000000000..5514ffa7b
--- /dev/null
+++ b/nativescript-angular/directives/templated-items-comp.ts
@@ -0,0 +1,239 @@
+import { AfterContentInit, ContentChild, Directive, DoCheck, ElementRef, EmbeddedViewRef, EventEmitter, Host, Inject, InjectionToken, Input, IterableDiffer, IterableDiffers, OnDestroy, Output, TemplateRef, ViewChild, ViewContainerRef, ɵisListLikeIterable as isListLikeIterable, Injectable, NgZone } from '@angular/core';
+import { ObservableArray, View, KeyedTemplate, LayoutBase, ItemEventData, TemplatedItemsView, profile } from '@nativescript/core';
+
+import { getSingleViewRecursive } from '../element-registry';
+import { NativeScriptDebug } from '../trace';
+
+const NG_VIEW = '_ngViewRef';
+
+export class ItemContext {
+ constructor(public $implicit?: any, public item?: any, public index?: number, public even?: boolean, public odd?: boolean) {}
+}
+
+export interface SetupItemViewArgs {
+ view: EmbeddedViewRef;
+ data: any;
+ index: number;
+ context: ItemContext;
+}
+
+@Directive()
+export abstract class TemplatedItemsComponent implements DoCheck, OnDestroy, AfterContentInit {
+ public abstract get nativeElement(): TemplatedItemsView;
+
+ protected templatedItemsView: TemplatedItemsView;
+ protected _items: any;
+ protected _differ: IterableDiffer;
+ protected _templateMap: Map;
+
+ @ViewChild('loader', { read: ViewContainerRef, static: false }) loader: ViewContainerRef;
+
+ @Output() public setupItemView = new EventEmitter();
+
+ @ContentChild(TemplateRef, { read: TemplateRef, static: false }) itemTemplateQuery: TemplateRef;
+
+ itemTemplate: TemplateRef;
+
+ @Input()
+ get items() {
+ return this._items;
+ }
+
+ set items(value: any) {
+ this._items = value;
+ let needDiffer = true;
+ if (value instanceof ObservableArray) {
+ needDiffer = false;
+ }
+ if (needDiffer && !this._differ && isListLikeIterable(value)) {
+ this._differ = this._iterableDiffers.find(this._items).create((_index, item) => {
+ return item;
+ });
+ }
+
+ this.templatedItemsView.items = this._items;
+ }
+
+ constructor(_elementRef: ElementRef, private _iterableDiffers: IterableDiffers, private zone: NgZone) {
+ this.templatedItemsView = _elementRef.nativeElement;
+
+ this.templatedItemsView.on('itemLoading', this.onItemLoading, this);
+ }
+
+ ngAfterContentInit() {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog('TemplatedItemsView.ngAfterContentInit()');
+ }
+
+ this.setItemTemplates();
+ }
+
+ ngOnDestroy() {
+ this.templatedItemsView.off('itemLoading', this.onItemLoading, this);
+ this.templatedItemsView = null;
+
+ if (this._templateMap) {
+ this._templateMap.clear();
+ }
+ }
+
+ private setItemTemplates() {
+ // The itemTemplateQuery may be changed after list items are added that contain inside,
+ // so cache and use only the original template to avoid errors.
+ this.itemTemplate = this.itemTemplateQuery;
+
+ if (this._templateMap) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog('Setting templates');
+ }
+
+ const templates: KeyedTemplate[] = [];
+ this._templateMap.forEach((value) => {
+ templates.push(value);
+ });
+ this.templatedItemsView.itemTemplates = templates;
+ }
+ }
+
+ public registerTemplate(key: string, template: TemplateRef) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog(`registerTemplate for key: ${key}`);
+ }
+
+ if (!this._templateMap) {
+ this._templateMap = new Map();
+ }
+
+ const keyedTemplate = {
+ key,
+ createView: this.getItemTemplateViewFactory(template),
+ };
+
+ this._templateMap.set(key, keyedTemplate);
+ }
+
+ @profile
+ public onItemLoading(args: ItemEventData) {
+ if (!args.view && !this.itemTemplate) {
+ return;
+ }
+
+ const index = args.index;
+ const items = (args.object).items;
+ const currentItem = typeof items.getItem === 'function' ? items.getItem(index) : items[index];
+ let viewRef: EmbeddedViewRef;
+
+ if (args.view) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog(`onItemLoading: ${index} - Reusing existing view`);
+ }
+
+ viewRef = args.view[NG_VIEW];
+
+ // Getting angular view from original element (in cases when ProxyViewContainer
+ // is used NativeScript internally wraps it in a StackLayout)
+ if (!viewRef && args.view instanceof LayoutBase && args.view.getChildrenCount() > 0) {
+ viewRef = args.view.getChildAt(0)[NG_VIEW];
+ }
+
+ if (!viewRef && NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewError(`ViewReference not found for item ${index}. View recycling is not working`);
+ }
+
+ // No ng-template is setup, continue with 'defaultTemplate'
+ if (!viewRef) {
+ return;
+ }
+ }
+
+ if (!viewRef) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog(`onItemLoading: ${index} - Creating view from template`);
+ }
+
+ viewRef = this.loader.createEmbeddedView(this.itemTemplate, new ItemContext(), 0);
+ args.view = getItemViewRoot(viewRef);
+ args.view[NG_VIEW] = viewRef;
+ }
+
+ this.setupViewRef(viewRef, currentItem, index);
+
+ this.detectChangesOnChild(viewRef, index);
+ }
+
+ public setupViewRef(viewRef: EmbeddedViewRef, data: any, index: number): void {
+ const context = viewRef.context;
+ context.$implicit = data;
+ context.item = data;
+ context.index = index;
+ context.even = index % 2 === 0;
+ context.odd = !context.even;
+
+ this.setupItemView.next({ view: viewRef, data: data, index: index, context: context });
+ }
+
+ protected getItemTemplateViewFactory(template: TemplateRef): () => View {
+ return () => {
+ const viewRef = this.loader.createEmbeddedView(template, new ItemContext(), 0);
+ const resultView = getItemViewRoot(viewRef);
+ resultView[NG_VIEW] = viewRef;
+
+ return resultView;
+ };
+ }
+
+ @profile
+ private detectChangesOnChild(viewRef: EmbeddedViewRef, index: number) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog(`Manually detect changes in child: ${index}`);
+ }
+
+ this.zone.run(() => {
+ viewRef.markForCheck();
+ viewRef.detectChanges();
+ });
+ }
+
+ ngDoCheck() {
+ if (this._differ) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog('ngDoCheck() - execute differ');
+ }
+
+ const changes = this._differ.diff(this._items);
+ if (changes) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog('ngDoCheck() - refresh');
+ }
+
+ this.templatedItemsView.refresh();
+ }
+ }
+ }
+}
+
+export interface ComponentView {
+ rootNodes: Array;
+ destroy(): void;
+}
+
+export type RootLocator = (nodes: Array, nestLevel: number) => View;
+
+export function getItemViewRoot(viewRef: ComponentView, rootLocator: RootLocator = getSingleViewRecursive): View {
+ const rootView = rootLocator(viewRef.rootNodes, 0);
+ return rootView;
+}
+
+export const TEMPLATED_ITEMS_COMPONENT = new InjectionToken('TemplatedItemsComponent');
+
+@Directive({ selector: '[nsTemplateKey]' })
+export class TemplateKeyDirective {
+ constructor(private templateRef: TemplateRef, @Inject(TEMPLATED_ITEMS_COMPONENT) @Host() private comp: TemplatedItemsComponent) {}
+
+ @Input()
+ set nsTemplateKey(value: any) {
+ if (this.comp && this.templateRef) {
+ this.comp.registerTemplate(value, this.templateRef);
+ }
+ }
+}
diff --git a/nativescript-angular/dom-adapter.ts b/nativescript-angular/dom-adapter.ts
index 2a112782f..09f4bfecd 100644
--- a/nativescript-angular/dom-adapter.ts
+++ b/nativescript-angular/dom-adapter.ts
@@ -1,187 +1,386 @@
/* tslint:disable */
-import { Type } from "@angular/core";
-import { ɵDomAdapter } from "@angular/platform-browser";
-import { rendererLog } from "./trace";
+import { Type } from '@angular/core';
+import { ɵDomAdapter, ɵsetRootDomAdapter } from '@angular/common';
+import { NativeScriptDebug } from './trace';
export class NativeScriptDomAdapter implements ɵDomAdapter {
- static makeCurrent() {
+ static makeCurrent() {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog('Setting root DOM adapter...');
+ }
- // Don't register when bundling (likely AoT setup).
- if (!global.TNS_WEBPACK) {
- try {
- const privateAPI = global.require("@angular/platform-browser");
- const setRootDomAdapter = privateAPI.ɵsetRootDomAdapter;
+ ɵsetRootDomAdapter(new NativeScriptDomAdapter());
+ }
- rendererLog("Setting root DOM adapter...");
- setRootDomAdapter(new NativeScriptDomAdapter());
- } catch (e) {
- rendererLog("@angular/platform-browser package not present. NOT setting root DOM adapter...");
- }
- }
- }
+ hasProperty(_element: any, _name: string) {
+ // TODO: actually check if the property exists.
+ return true;
+ }
- hasProperty(_element: any, _name: string) {
- // TODO: actually check if the property exists.
- return true;
- }
+ log(arg: any): void {
+ console.log(arg);
+ }
- log(arg: any): void {
- console.log(arg);
- }
+ logError(arg: any): void {
+ console.log(arg);
+ }
- logError(arg: any): void {
- console.log(arg);
- }
+ logGroup(arg: any): void {
+ console.log(arg);
+ }
- logGroup(arg: any): void {
- console.log(arg);
- }
+ logGroupEnd(): void {}
- logGroupEnd(): void {
- }
+ get attrToPropMap(): { [key: string]: string } {
+ throw new Error('Not implemented!');
+ }
+ set attrToPropMap(_value: { [key: string]: string }) {
+ throw new Error('Not implemented!');
+ }
- get attrToPropMap(): {[key: string]: string} { throw new Error("Not implemented!"); };
- set attrToPropMap(_value: {[key: string]: string}) { throw new Error("Not implemented!"); };
+ public resourceLoaderType: Type = null;
+ setProperty(_el: Element, _name: string, _value: any): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ getProperty(_el: Element, _name: string): any {
+ throw new Error('Not implemented!');
+ }
+ invoke(_el: Element, _methodName: string, _args: any[]): any {
+ throw new Error('Not implemented!');
+ }
- public resourceLoaderType: Type = null;
- setProperty(_el: Element, _name: string, _value: any): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- getProperty(_el: Element, _name: string): any { throw new Error("Not implemented!") }
- invoke(_el: Element, _methodName: string, _args: any[]): any { throw new Error("Not implemented!") }
+ contains(_nodeA: any, _nodeB: any): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ parse(_templateHtml: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ query(_selector: string): any {
+ throw new Error('Not implemented!');
+ }
+ querySelector(_el: any /** TODO #9100 */, _selector: string): HTMLElement {
+ throw new Error('Not implemented!');
+ }
+ querySelectorAll(_el: any /** TODO #9100 */, _selector: string): any[] {
+ throw new Error('Not implemented!');
+ }
+ on(_el: any /** TODO #9100 */, _evt: any /** TODO #9100 */, _listener: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ onAndCancel(_el: any /** TODO #9100 */, _evt: any /** TODO #9100 */, _listener: any /** TODO #9100 */): Function {
+ throw new Error('Not implemented!');
+ }
+ dispatchEvent(_el: any /** TODO #9100 */, _evt: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ createMouseEvent(_eventType: any /** TODO #9100 */): any {
+ throw new Error('Not implemented!');
+ }
+ createEvent(_eventType: string): any {
+ throw new Error('Not implemented!');
+ }
+ preventDefault(_evt: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ isPrevented(_evt: any /** TODO #9100 */): boolean {
+ throw new Error('Not implemented!');
+ }
+ getInnerHTML(_el: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
- contains(_nodeA: any, _nodeB: any): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- parse(_templateHtml: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- query(_selector: string): any { throw new Error("Not implemented!") }
- querySelector(_el: any /** TODO #9100 */, _selector: string): HTMLElement { throw new Error("Not implemented!") }
- querySelectorAll(_el: any /** TODO #9100 */, _selector: string): any[] { throw new Error("Not implemented!") }
- on(
- _el: any /** TODO #9100 */, _evt: any /** TODO #9100 */, _listener: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- onAndCancel(
- _el: any /** TODO #9100 */, _evt: any /** TODO #9100 */,
- _listener: any /** TODO #9100 */): Function { throw new Error("Not implemented!") }
- dispatchEvent(_el: any /** TODO #9100 */, _evt: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- createMouseEvent(_eventType: any /** TODO #9100 */): any { throw new Error("Not implemented!") }
- createEvent(_eventType: string): any { throw new Error("Not implemented!") }
- preventDefault(_evt: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- isPrevented(_evt: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") }
- getInnerHTML(_el: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
+ getTemplateContent(_el: any /** TODO #9100 */): any {
+ throw new Error('Not implemented!');
+ }
+ getOuterHTML(_el: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ nodeName(_node: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ nodeValue(_node: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ type(_node: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ content(_node: any /** TODO #9100 */): any {
+ throw new Error('Not implemented!');
+ }
+ firstChild(_el: any /** TODO #9100 */): Node {
+ throw new Error('Not implemented!');
+ }
+ nextSibling(_el: any /** TODO #9100 */): Node {
+ throw new Error('Not implemented!');
+ }
+ parentElement(_el: any /** TODO #9100 */): Node {
+ throw new Error('Not implemented!');
+ }
+ childNodes(_el: any /** TODO #9100 */): Node[] {
+ throw new Error('Not implemented!');
+ }
+ childNodesAsList(_el: any /** TODO #9100 */): Node[] {
+ throw new Error('Not implemented!');
+ }
+ clearNodes(_el: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ appendChild(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ removeChild(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ replaceChild(_el: any /** TODO #9100 */, _newNode: any /** TODO #9100 */, _oldNode: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ remove(_el: any /** TODO #9100 */): Node {
+ throw new Error('Not implemented!');
+ }
+ insertBefore(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ insertAllBefore(_el: any /** TODO #9100 */, _nodes: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ insertAfter(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ setInnerHTML(_el: any /** TODO #9100 */, _value: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ getText(_el: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ setText(_el: any /** TODO #9100 */, _value: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ getValue(_el: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ setValue(_el: any /** TODO #9100 */, _value: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ getChecked(_el: any /** TODO #9100 */): boolean {
+ throw new Error('Not implemented!');
+ }
+ setChecked(_el: any /** TODO #9100 */, _value: boolean): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ createComment(_text: string): any {
+ throw new Error('Not implemented!');
+ }
+ createTemplate(_html: any /** TODO #9100 */): HTMLElement {
+ throw new Error('Not implemented!');
+ }
+ createElement(_tagName: any /** TODO #9100 */, _doc?: any /** TODO #9100 */): HTMLElement {
+ throw new Error('Not implemented!');
+ }
+ createElementNS(_ns: string, _tagName: string, _doc?: any /** TODO #9100 */): Element {
+ throw new Error('Not implemented!');
+ }
+ createTextNode(_text: string, _doc?: any /** TODO #9100 */): Text {
+ throw new Error('Not implemented!');
+ }
+ createScriptTag(_attrName: string, _attrValue: string, _doc?: any /** TODO #9100 */): HTMLElement {
+ throw new Error('Not implemented!');
+ }
+ createStyleElement(_css: string, _doc?: any /** TODO #9100 */): HTMLStyleElement {
+ throw new Error('Not implemented!');
+ }
+ createShadowRoot(_el: any /** TODO #9100 */): any {
+ throw new Error('Not implemented!');
+ }
+ getShadowRoot(_el: any /** TODO #9100 */): any {
+ throw new Error('Not implemented!');
+ }
+ getHost(_el: any /** TODO #9100 */): any {
+ throw new Error('Not implemented!');
+ }
+ getDistributedNodes(_el: any /** TODO #9100 */): Node[] {
+ throw new Error('Not implemented!');
+ }
+ clone /**/(_node: Node /*T*/): Node /*T*/ {
+ throw new Error('Not implemented!');
+ }
+ getElementsByClassName(_element: any /** TODO #9100 */, _name: string): HTMLElement[] {
+ throw new Error('Not implemented!');
+ }
+ getElementsByTagName(_element: any /** TODO #9100 */, _name: string): HTMLElement[] {
+ throw new Error('Not implemented!');
+ }
+ classList(_element: any /** TODO #9100 */): any[] {
+ throw new Error('Not implemented!');
+ }
+ addClass(_element: any /** TODO #9100 */, _className: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ removeClass(_element: any /** TODO #9100 */, _className: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ hasClass(_element: any /** TODO #9100 */, _className: string): boolean {
+ throw new Error('Not implemented!');
+ }
+ setStyle(_element: any /** TODO #9100 */, _styleName: string, _styleValue: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ removeStyle(_element: any /** TODO #9100 */, _styleName: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ getStyle(_element: any /** TODO #9100 */, _styleName: string): string {
+ throw new Error('Not implemented!');
+ }
+ hasStyle(_element: any /** TODO #9100 */, _styleName: string, _styleValue?: string): boolean {
+ throw new Error('Not implemented!');
+ }
+ tagName(_element: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ attributeMap(_element: any /** TODO #9100 */): Map {
+ throw new Error('Not implemented!');
+ }
+ hasAttribute(_element: any /** TODO #9100 */, _attribute: string): boolean {
+ throw new Error('Not implemented!');
+ }
+ hasAttributeNS(_element: any /** TODO #9100 */, _ns: string, _attribute: string): boolean {
+ throw new Error('Not implemented!');
+ }
+ getAttribute(_element: any /** TODO #9100 */, _attribute: string): string {
+ throw new Error('Not implemented!');
+ }
+ getAttributeNS(_element: any /** TODO #9100 */, _ns: string, _attribute: string): string {
+ throw new Error('Not implemented!');
+ }
+ setAttribute(_element: any /** TODO #9100 */, _name: string, _value: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ setAttributeNS(_element: any /** TODO #9100 */, _ns: string, _name: string, _value: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ removeAttribute(_element: any /** TODO #9100 */, _attribute: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ removeAttributeNS(_element: any /** TODO #9100 */, _ns: string, _attribute: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ templateAwareRoot(_el: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ createHtmlDocument(): HTMLDocument {
+ throw new Error('Not implemented!');
+ }
+ defaultDoc(): HTMLDocument {
+ throw new Error('Not implemented!');
+ }
+ getDefaultDocument(): Document {
+ throw new Error('Not implemented!');
+ }
+ getBoundingClientRect(_el: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ getTitle(): string {
+ throw new Error('Not implemented!');
+ }
+ setTitle(_doc: Document, _newTitle: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ elementMatches(_n: any /** TODO #9100 */, _selector: string): boolean {
+ throw new Error('Not implemented!');
+ }
+ isTemplateElement(_el: any): boolean {
+ throw new Error('Not implemented!');
+ }
+ isTextNode(_node: any /** TODO #9100 */): boolean {
+ throw new Error('Not implemented!');
+ }
+ isCommentNode(_node: any /** TODO #9100 */): boolean {
+ throw new Error('Not implemented!');
+ }
+ isElementNode(_node: any /** TODO #9100 */): boolean {
+ throw new Error('Not implemented!');
+ }
+ hasShadowRoot(_node: any /** TODO #9100 */): boolean {
+ throw new Error('Not implemented!');
+ }
+ isShadowRoot(_node: any /** TODO #9100 */): boolean {
+ throw new Error('Not implemented!');
+ }
+ importIntoDoc /**/(_node: Node /*T*/): Node /*T*/ {
+ throw new Error('Not implemented!');
+ }
+ adoptNode /**/(_node: Node /*T*/): Node /*T*/ {
+ throw new Error('Not implemented!');
+ }
+ getHref(_element: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ getEventKey(_event: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ resolveAndSetHref(_element: any /** TODO #9100 */, _baseUrl: string, _href: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ supportsDOMEvents(): boolean {
+ throw new Error('Not implemented!');
+ }
+ supportsNativeShadowDOM(): boolean {
+ throw new Error('Not implemented!');
+ }
+ getGlobalEventTarget(_doc: Document, _target: string): any {
+ throw new Error('Not implemented!');
+ }
+ getHistory(): History {
+ throw new Error('Not implemented!');
+ }
+ getLocation(): Location {
+ throw new Error('Not implemented!');
+ }
+ getBaseHref(): string {
+ throw new Error('Not implemented!');
+ }
+ resetBaseElement(): void {
+ throw new Error('Not implemented!');
+ }
+ getUserAgent(): string {
+ return 'Fake user agent';
+ }
+ setData(_element: any /** TODO #9100 */, _name: string, _value: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ getComputedStyle(_element: any /** TODO #9100 */): any {
+ throw new Error('Not implemented!');
+ }
+ getData(_element: any /** TODO #9100 */, _name: string): string {
+ throw new Error('Not implemented!');
+ }
+ setGlobalVar(_name: string, _value: any): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ supportsWebAnimation(): boolean {
+ throw new Error('Not implemented!');
+ }
+ performanceNow(): number {
+ throw new Error('Not implemented!');
+ }
+ getAnimationPrefix(): string {
+ throw new Error('Not implemented!');
+ }
+ getTransitionEnd(): string {
+ throw new Error('Not implemented!');
+ }
+ supportsAnimation(): boolean {
+ throw new Error('Not implemented!');
+ }
- getTemplateContent(_el: any /** TODO #9100 */): any { throw new Error("Not implemented!") }
- getOuterHTML(_el: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- nodeName(_node: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- nodeValue(_node: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- type(_node: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- content(_node: any /** TODO #9100 */): any { throw new Error("Not implemented!") }
- firstChild(_el: any /** TODO #9100 */): Node { throw new Error("Not implemented!") }
- nextSibling(_el: any /** TODO #9100 */): Node { throw new Error("Not implemented!") }
- parentElement(_el: any /** TODO #9100 */): Node { throw new Error("Not implemented!") }
- childNodes(_el: any /** TODO #9100 */): Node[] { throw new Error("Not implemented!") }
- childNodesAsList(_el: any /** TODO #9100 */): Node[] { throw new Error("Not implemented!") }
- clearNodes(_el: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- appendChild(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- removeChild(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- replaceChild(
- _el: any /** TODO #9100 */, _newNode: any /** TODO #9100 */,
- _oldNode: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- remove(_el: any /** TODO #9100 */): Node { throw new Error("Not implemented!") }
- insertBefore(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- insertAllBefore(_el: any /** TODO #9100 */, _nodes: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- insertAfter(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- setInnerHTML(_el: any /** TODO #9100 */, _value: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- getText(_el: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- setText(_el: any /** TODO #9100 */, _value: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- getValue(_el: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- setValue(_el: any /** TODO #9100 */, _value: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- getChecked(_el: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") }
- setChecked(_el: any /** TODO #9100 */, _value: boolean): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- createComment(_text: string): any { throw new Error("Not implemented!") }
- createTemplate(_html: any /** TODO #9100 */): HTMLElement { throw new Error("Not implemented!") }
- createElement(_tagName: any /** TODO #9100 */, _doc?: any /** TODO #9100 */): HTMLElement { throw new Error("Not implemented!") }
- createElementNS(_ns: string, _tagName: string, _doc?: any /** TODO #9100 */): Element { throw new Error("Not implemented!") }
- createTextNode(_text: string, _doc?: any /** TODO #9100 */): Text { throw new Error("Not implemented!") }
- createScriptTag(_attrName: string, _attrValue: string, _doc?: any /** TODO #9100 */):
- HTMLElement { throw new Error("Not implemented!") }
- createStyleElement(_css: string, _doc?: any /** TODO #9100 */): HTMLStyleElement { throw new Error("Not implemented!") }
- createShadowRoot(_el: any /** TODO #9100 */): any { throw new Error("Not implemented!") }
- getShadowRoot(_el: any /** TODO #9100 */): any { throw new Error("Not implemented!") }
- getHost(_el: any /** TODO #9100 */): any { throw new Error("Not implemented!") }
- getDistributedNodes(_el: any /** TODO #9100 */): Node[] { throw new Error("Not implemented!") }
- clone /**/ (_node: Node /*T*/): Node /*T*/ { throw new Error("Not implemented!") }
- getElementsByClassName(_element: any /** TODO #9100 */, _name: string): HTMLElement[] { throw new Error("Not implemented!") }
- getElementsByTagName(_element: any /** TODO #9100 */, _name: string): HTMLElement[] { throw new Error("Not implemented!") }
- classList(_element: any /** TODO #9100 */): any[] { throw new Error("Not implemented!") }
- addClass(_element: any /** TODO #9100 */, _className: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- removeClass(_element: any /** TODO #9100 */, _className: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- hasClass(_element: any /** TODO #9100 */, _className: string): boolean { throw new Error("Not implemented!") }
- setStyle(_element: any /** TODO #9100 */, _styleName: string, _styleValue: string): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- removeStyle(_element: any /** TODO #9100 */, _styleName: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- getStyle(_element: any /** TODO #9100 */, _styleName: string): string { throw new Error("Not implemented!") }
- hasStyle(_element: any /** TODO #9100 */, _styleName: string, _styleValue?: string):
- boolean { throw new Error("Not implemented!") }
- tagName(_element: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- attributeMap(_element: any /** TODO #9100 */): Map { throw new Error("Not implemented!") }
- hasAttribute(_element: any /** TODO #9100 */, _attribute: string): boolean { throw new Error("Not implemented!") }
- hasAttributeNS(_element: any /** TODO #9100 */, _ns: string, _attribute: string): boolean { throw new Error("Not implemented!") }
- getAttribute(_element: any /** TODO #9100 */, _attribute: string): string { throw new Error("Not implemented!") }
- getAttributeNS(_element: any /** TODO #9100 */, _ns: string, _attribute: string): string { throw new Error("Not implemented!") }
- setAttribute(_element: any /** TODO #9100 */, _name: string, _value: string): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- setAttributeNS(_element: any /** TODO #9100 */, _ns: string, _name: string, _value: string):
- any /** TODO #9100 */ { throw new Error("Not implemented!") }
- removeAttribute(_element: any /** TODO #9100 */, _attribute: string): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- removeAttributeNS(_element: any /** TODO #9100 */, _ns: string, _attribute: string): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- templateAwareRoot(_el: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- createHtmlDocument(): HTMLDocument { throw new Error("Not implemented!") }
- defaultDoc(): HTMLDocument { throw new Error("Not implemented!") }
- getDefaultDocument(): Document { throw new Error("Not implemented!") }
- getBoundingClientRect(_el: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- getTitle(): string { throw new Error("Not implemented!") }
- setTitle(_doc: Document, _newTitle: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- elementMatches(_n: any /** TODO #9100 */, _selector: string): boolean { throw new Error("Not implemented!") }
- isTemplateElement(_el: any): boolean { throw new Error("Not implemented!") }
- isTextNode(_node: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") }
- isCommentNode(_node: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") }
- isElementNode(_node: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") }
- hasShadowRoot(_node: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") }
- isShadowRoot(_node: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") }
- importIntoDoc /**/ (_node: Node /*T*/): Node /*T*/ { throw new Error("Not implemented!") }
- adoptNode /**/ (_node: Node /*T*/): Node /*T*/ { throw new Error("Not implemented!") }
- getHref(_element: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- getEventKey(_event: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- resolveAndSetHref(_element: any /** TODO #9100 */, _baseUrl: string, _href: string): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- supportsDOMEvents(): boolean { throw new Error("Not implemented!") }
- supportsNativeShadowDOM(): boolean { throw new Error("Not implemented!") }
- getGlobalEventTarget(_doc: Document, _target: string): any { throw new Error("Not implemented!") }
- getHistory(): History { throw new Error("Not implemented!") }
- getLocation(): Location { throw new Error("Not implemented!") }
- getBaseHref(): string { throw new Error("Not implemented!") }
- resetBaseElement(): void { throw new Error("Not implemented!") }
- getUserAgent(): string { return "Fake user agent"; }
- setData(_element: any /** TODO #9100 */, _name: string, _value: string): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- getComputedStyle(_element: any /** TODO #9100 */): any { throw new Error("Not implemented!") }
- getData(_element: any /** TODO #9100 */, _name: string): string { throw new Error("Not implemented!") }
- setGlobalVar(_name: string, _value: any): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- supportsWebAnimation(): boolean { throw new Error("Not implemented!") }
- performanceNow(): number { throw new Error("Not implemented!") }
- getAnimationPrefix(): string { throw new Error("Not implemented!") }
- getTransitionEnd(): string { throw new Error("Not implemented!") }
- supportsAnimation(): boolean { throw new Error("Not implemented!") }
-
- supportsCookies(): boolean { return false; }
- getCookie(_name: string): string { throw new Error("Not implemented!") }
- setCookie(_name: string, _value: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
+ supportsCookies(): boolean {
+ return false;
+ }
+ getCookie(_name: string): string {
+ throw new Error('Not implemented!');
+ }
+ setCookie(_name: string, _value: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
}
NativeScriptDomAdapter.makeCurrent();
diff --git a/nativescript-angular/element-registry.ts b/nativescript-angular/element-registry.ts
index 3a86b93f2..d42c91259 100644
--- a/nativescript-angular/element-registry.ts
+++ b/nativescript-angular/element-registry.ts
@@ -1,200 +1,207 @@
-import { View } from "tns-core-modules/ui/core/view";
-import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base";
-
-export type NgView = (View & ViewExtensions);
+import { View, LayoutBase, Page, Frame, AbsoluteLayout, ActivityIndicator, Button, ContentView, DatePicker, DockLayout, GridLayout, HtmlView, Image, Label, ListPicker, ListView, Placeholder, Progress, ProxyViewContainer, Repeater, ScrollView, SearchBar, SegmentedBar, SegmentedBarItem, Slider, StackLayout, FlexboxLayout, Switch, TabView, TextField, TextView, TimePicker, WebView, WrapLayout, FormattedString, Span, RootLayout } from '@nativescript/core';
+export interface ViewClass {
+ new (): View;
+}
export interface ViewExtensions {
- meta: ViewClassMeta;
- nodeType: number;
- nodeName: string;
- parentNode: NgView;
- nextSibling: NgView;
- firstChild: NgView;
- lastChild: NgView;
- ngCssClasses: Map;
+ meta: ViewClassMeta;
+ nodeType: number;
+ nodeName: string;
+ parentNode: NgView;
+ nextSibling: NgView;
+ previousSibling: NgView;
+ firstChild: NgView;
+ lastChild: NgView;
+ ngCssClasses: Map;
}
-export interface ViewClass {
- new (): View;
-}
+export type NgView = View & ViewExtensions;
export abstract class InvisibleNode extends View implements NgView {
- meta: { skipAddToDom: boolean };
- nodeType: number;
- nodeName: string;
- parentNode: NgView;
- nextSibling: NgView;
- firstChild: NgView;
- lastChild: NgView;
- ngCssClasses: Map;
-
- constructor() {
- super();
-
- this.nodeType = 1;
- this.nodeName = getClassName(this);
- }
-
- toString() {
- return `${this.nodeName}(${this.id})`;
- }
+ meta: { skipAddToDom: boolean };
+ nodeType: number;
+ nodeName: string;
+ parentNode: NgView;
+ nextSibling: NgView;
+ previousSibling: NgView;
+ firstChild: NgView;
+ lastChild: NgView;
+ ngCssClasses: Map;
+
+ constructor() {
+ super();
+
+ this.nodeType = 1;
+ this.nodeName = getClassName(this);
+ }
+
+ toString() {
+ return `${this.nodeName}(${this.id})`;
+ }
}
export class CommentNode extends InvisibleNode {
- protected static id = 0;
+ protected static id = 0;
- constructor() {
- super();
+ constructor() {
+ super();
- this.meta = {
- skipAddToDom: true,
- };
- this.id = CommentNode.id.toString();
- CommentNode.id += 1;
- }
+ this.meta = {
+ skipAddToDom: true,
+ };
+ this.id = CommentNode.id.toString();
+ CommentNode.id += 1;
+ }
}
export class TextNode extends InvisibleNode {
- protected static id = 0;
+ protected static id = 0;
- constructor() {
- super();
+ constructor() {
+ super();
- this.meta = {
- skipAddToDom: true,
- };
- this.id = TextNode.id.toString();
- TextNode.id += 1;
- }
+ this.meta = {
+ skipAddToDom: true,
+ };
+ this.id = TextNode.id.toString();
+ TextNode.id += 1;
+ }
}
-const getClassName = instance => instance.constructor.name;
+const getClassName = (instance) => instance.constructor.name;
export interface ViewClassMeta {
- skipAddToDom?: boolean;
- insertChild?: (parent: any, child: any, next?: any) => void;
- removeChild?: (parent: any, child: any) => void;
+ skipAddToDom?: boolean;
+ insertChild?: (parent: any, child: any, next?: any) => void;
+ removeChild?: (parent: any, child: any) => void;
}
export function isDetachedElement(element): boolean {
- return (element && element.meta && element.meta.skipAddToDom);
+ return element && element.meta && element.meta.skipAddToDom;
}
export function isView(view: any): view is NgView {
- return view instanceof View;
+ return view instanceof View;
}
export function isInvisibleNode(view: any): view is InvisibleNode {
- return view instanceof InvisibleNode;
+ return view instanceof InvisibleNode;
}
-export type ViewResolver = () => ViewClass;
+export type ViewResolver = () => any;
-const elementMap = new Map();
+const elementMap = new Map();
const camelCaseSplit = /([a-z0-9])([A-Z])/g;
const defaultViewMeta: ViewClassMeta = { skipAddToDom: false };
-export function registerElement(
- elementName: string,
- resolver: ViewResolver,
- meta?: ViewClassMeta
-): void {
- if (elementMap.has(elementName)) {
- throw new Error(`Element for ${elementName} already registered.`);
- } else {
- const entry = { resolver: resolver, meta: meta };
- elementMap.set(elementName, entry);
- elementMap.set(elementName.toLowerCase(), entry);
- elementMap.set(elementName.replace(camelCaseSplit, "$1-$2").toLowerCase(), entry);
- }
+export function registerElement(elementName: string, resolver: ViewResolver, meta?: ViewClassMeta): void {
+ const entry = { resolver, meta };
+ elementMap.set(elementName, entry);
+ elementMap.set(elementName.toLowerCase(), entry);
+ elementMap.set(elementName.replace(camelCaseSplit, '$1-$2').toLowerCase(), entry);
}
-export function getViewClass(elementName: string): ViewClass {
- const entry = elementMap.get(elementName) ||
- elementMap.get(elementName.toLowerCase());
- if (!entry) {
- throw new TypeError(`No known component for element ${elementName}.`);
- }
-
- try {
- return entry.resolver();
- } catch (e) {
- throw new TypeError(`Could not load view for: ${elementName}.${e}`);
- }
+export function getViewClass(elementName: string): any {
+ const entry = elementMap.get(elementName) || elementMap.get(elementName.toLowerCase());
+ if (!entry) {
+ throw new TypeError(`No known component for element ${elementName}.`);
+ }
+
+ try {
+ return entry.resolver();
+ } catch (e) {
+ throw new TypeError(`Could not load view for: ${elementName}.${e}`);
+ }
}
export function getViewMeta(nodeName: string): ViewClassMeta {
- const entry = elementMap.get(nodeName) || elementMap.get(nodeName.toLowerCase());
- return (entry && entry.meta) || defaultViewMeta;
+ const entry = elementMap.get(nodeName) || elementMap.get(nodeName.toLowerCase());
+ return (entry && entry.meta) || defaultViewMeta;
}
export function isKnownView(elementName: string): boolean {
- return elementMap.has(elementName) ||
- elementMap.has(elementName.toLowerCase());
+ return elementMap.has(elementName) || elementMap.has(elementName.toLowerCase());
}
export function getSingleViewRecursive(nodes: Array, nestLevel: number): View {
- const actualNodes = nodes.filter(node => !(node instanceof InvisibleNode));
-
- if (actualNodes.length === 0) {
- throw new Error(`No suitable views found in list template! ` +
- `Nesting level: ${nestLevel}`);
- } else if (actualNodes.length > 1) {
- throw new Error(`More than one view found in list template!` +
- `Nesting level: ${nestLevel}`);
- }
-
- const rootLayout = actualNodes[0];
- if (!rootLayout) {
- return getSingleViewRecursive(rootLayout.children, nestLevel + 1);
- }
-
- const parentLayout = rootLayout.parent;
- if (parentLayout instanceof LayoutBase) {
- parentLayout.removeChild(rootLayout);
- }
-
- return rootLayout;
+ const actualNodes = nodes.filter((node) => !(node instanceof InvisibleNode));
+
+ if (actualNodes.length === 0) {
+ throw new Error(`No suitable views found in list template! ` + `Nesting level: ${nestLevel}`);
+ } else if (actualNodes.length > 1) {
+ throw new Error(`More than one view found in list template!` + `Nesting level: ${nestLevel}`);
+ }
+
+ const rootLayout = actualNodes[0];
+ if (!rootLayout) {
+ return getSingleViewRecursive(rootLayout.children, nestLevel + 1);
+ }
+
+ const parentLayout = rootLayout.parent;
+ if (parentLayout instanceof LayoutBase) {
+ let node = rootLayout.parentNode;
+ parentLayout.removeChild(rootLayout);
+ rootLayout.parentNode = node;
+ }
+
+ return rootLayout;
}
+const frameMeta: ViewClassMeta = {
+ insertChild: (parent: Frame, child: NgView, next: any) => {
+ // Page cannot be added to Frame with _addChildFromBuilder (thwos "use defaultPage" error)
+ if (isInvisibleNode(child)) {
+ return;
+ } else if (child instanceof Page) {
+ parent.navigate({ create: () => child });
+ } else {
+ throw new Error('Only a Page can be a child of Frame');
+ }
+ },
+};
+
// Register default NativeScript components
// Note: ActionBar related components are registerd together with action-bar directives.
-registerElement("AbsoluteLayout", () => require("tns-core-modules/ui/layouts/absolute-layout").AbsoluteLayout);
-registerElement("ActivityIndicator", () => require("tns-core-modules/ui/activity-indicator").ActivityIndicator);
-registerElement("Border", () => require("tns-core-modules/ui/border").Border);
-registerElement("Button", () => require("tns-core-modules/ui/button").Button);
-registerElement("ContentView", () => require("tns-core-modules/ui/content-view").ContentView);
-registerElement("DatePicker", () => require("tns-core-modules/ui/date-picker").DatePicker);
-registerElement("DockLayout", () => require("tns-core-modules/ui/layouts/dock-layout").DockLayout);
-registerElement("GridLayout", () => require("tns-core-modules/ui/layouts/grid-layout").GridLayout);
-registerElement("HtmlView", () => require("tns-core-modules/ui/html-view").HtmlView);
-registerElement("Image", () => require("tns-core-modules/ui/image").Image);
+registerElement('AbsoluteLayout', () => AbsoluteLayout);
+registerElement('ActivityIndicator', () => ActivityIndicator);
+registerElement('Button', () => Button);
+registerElement('ContentView', () => ContentView);
+registerElement('DatePicker', () => DatePicker);
+registerElement('DockLayout', () => DockLayout);
+registerElement('Frame', () => Frame, frameMeta);
+registerElement('GridLayout', () => GridLayout);
+registerElement('HtmlView', () => HtmlView);
+registerElement('Image', () => Image);
// Parse5 changes tags to
. WTF!
-registerElement("img", () => require("tns-core-modules/ui/image").Image);
-registerElement("Label", () => require("tns-core-modules/ui/label").Label);
-registerElement("ListPicker", () => require("tns-core-modules/ui/list-picker").ListPicker);
-registerElement("ListView", () => require("tns-core-modules/ui/list-view").ListView);
-registerElement("Page", () => require("tns-core-modules/ui/page").Page);
-registerElement("Placeholder", () => require("tns-core-modules/ui/placeholder").Placeholder);
-registerElement("Progress", () => require("tns-core-modules/ui/progress").Progress);
-registerElement("ProxyViewContainer", () => require("tns-core-modules/ui/proxy-view-container").ProxyViewContainer);
-registerElement("Repeater", () => require("tns-core-modules/ui/repeater").Repeater);
-registerElement("ScrollView", () => require("tns-core-modules/ui/scroll-view").ScrollView);
-registerElement("SearchBar", () => require("tns-core-modules/ui/search-bar").SearchBar);
-registerElement("SegmentedBar", () => require("tns-core-modules/ui/segmented-bar").SegmentedBar);
-registerElement("SegmentedBarItem", () => require("tns-core-modules/ui/segmented-bar").SegmentedBarItem);
-registerElement("Slider", () => require("tns-core-modules/ui/slider").Slider);
-registerElement("StackLayout", () => require("tns-core-modules/ui/layouts/stack-layout").StackLayout);
-registerElement("FlexboxLayout", () => require("tns-core-modules/ui/layouts/flexbox-layout").FlexboxLayout);
-registerElement("Switch", () => require("tns-core-modules/ui/switch").Switch);
-registerElement("TabView", () => require("tns-core-modules/ui/tab-view").TabView);
-registerElement("TextField", () => require("tns-core-modules/ui/text-field").TextField);
-registerElement("TextView", () => require("tns-core-modules/ui/text-view").TextView);
-registerElement("TimePicker", () => require("tns-core-modules/ui/time-picker").TimePicker);
-registerElement("WebView", () => require("tns-core-modules/ui/web-view").WebView);
-registerElement("WrapLayout", () => require("tns-core-modules/ui/layouts/wrap-layout").WrapLayout);
-registerElement("FormattedString", () => require("tns-core-modules/text/formatted-string").FormattedString);
-registerElement("Span", () => require("tns-core-modules/text/span").Span);
-
-registerElement("DetachedContainer", () => require("tns-core-modules/ui/proxy-view-container").ProxyViewContainer,
- { skipAddToDom: true });
+registerElement('img', () => Image);
+registerElement('Label', () => Label);
+registerElement('ListPicker', () => ListPicker);
+registerElement('ListView', () => ListView);
+registerElement('Page', () => Page);
+registerElement('Placeholder', () => Placeholder);
+registerElement('Progress', () => Progress);
+registerElement('ProxyViewContainer', () => ProxyViewContainer);
+registerElement('Repeater', () => Repeater);
+registerElement('RootLayout', () => RootLayout);
+registerElement('ScrollView', () => ScrollView);
+registerElement('SearchBar', () => SearchBar);
+registerElement('SegmentedBar', () => SegmentedBar);
+registerElement('SegmentedBarItem', () => SegmentedBarItem);
+registerElement('Slider', () => Slider);
+registerElement('StackLayout', () => StackLayout);
+registerElement('FlexboxLayout', () => FlexboxLayout);
+registerElement('Switch', () => Switch);
+registerElement('TabView', () => TabView);
+registerElement('TextField', () => TextField);
+registerElement('TextView', () => TextView);
+registerElement('TimePicker', () => TimePicker);
+registerElement('WebView', () => WebView);
+registerElement('WrapLayout', () => WrapLayout);
+registerElement('FormattedString', () => FormattedString);
+registerElement('Span', () => Span);
+
+registerElement('DetachedContainer', () => ProxyViewContainer, {
+ skipAddToDom: true,
+});
+
+registerElement('page-router-outlet', () => Frame);
diff --git a/nativescript-angular/file-system/index.ts b/nativescript-angular/file-system/index.ts
new file mode 100644
index 000000000..755af9427
--- /dev/null
+++ b/nativescript-angular/file-system/index.ts
@@ -0,0 +1 @@
+export * from './ns-file-system';
diff --git a/nativescript-angular/file-system/ns-file-system.ts b/nativescript-angular/file-system/ns-file-system.ts
index 222a54c12..a55b63f15 100644
--- a/nativescript-angular/file-system/ns-file-system.ts
+++ b/nativescript-angular/file-system/ns-file-system.ts
@@ -1,20 +1,20 @@
-import { Injectable } from "@angular/core";
-import { knownFolders, Folder, File } from "tns-core-modules/file-system";
+import { Injectable } from '@angular/core';
+import { knownFolders, Folder, File } from '@nativescript/core';
// Allows greater flexibility with `file-system` and Angular
// Also provides a way for `file-system` to be mocked for testing
@Injectable()
export class NSFileSystem {
- public currentApp(): Folder {
- return knownFolders.currentApp();
- }
+ public currentApp(): Folder {
+ return knownFolders.currentApp();
+ }
- public fileFromPath(path: string): File {
- return File.fromPath(path);
- }
+ public fileFromPath(path: string): File {
+ return File.fromPath(path);
+ }
- public fileExists(path: string): boolean {
- return File.exists(path);
- }
+ public fileExists(path: string): boolean {
+ return File.exists(path);
+ }
}
diff --git a/nativescript-angular/forms/forms.module.ts b/nativescript-angular/forms/forms.module.ts
index 814b08727..69ba95069 100644
--- a/nativescript-angular/forms/forms.module.ts
+++ b/nativescript-angular/forms/forms.module.ts
@@ -1,34 +1,13 @@
-import { NgModule } from "@angular/core";
-import { FormsModule } from "@angular/forms";
-import {
- TextValueAccessor,
- CheckedValueAccessor,
- DateValueAccessor,
- TimeValueAccessor,
- NumberValueAccessor,
- SelectedIndexValueAccessor
-} from "./value-accessors";
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { TextValueAccessor, CheckedValueAccessor, DateValueAccessor, TimeValueAccessor, NumberValueAccessor, SelectedIndexValueAccessor } from './value-accessors';
-export const FORMS_DIRECTIVES = [
- TextValueAccessor,
- CheckedValueAccessor,
- DateValueAccessor,
- TimeValueAccessor,
- SelectedIndexValueAccessor,
- NumberValueAccessor,
-];
+export * from './value-accessors';
@NgModule({
- declarations: FORMS_DIRECTIVES,
- providers: [
- ],
- imports: [
- FormsModule
- ],
- exports: [
- FormsModule,
- FORMS_DIRECTIVES,
- ]
+ declarations: [TextValueAccessor, CheckedValueAccessor, DateValueAccessor, TimeValueAccessor, SelectedIndexValueAccessor, NumberValueAccessor],
+ providers: [],
+ imports: [FormsModule],
+ exports: [FormsModule, TextValueAccessor, CheckedValueAccessor, DateValueAccessor, TimeValueAccessor, SelectedIndexValueAccessor, NumberValueAccessor],
})
-export class NativeScriptFormsModule {
-}
+export class NativeScriptFormsModule {}
diff --git a/nativescript-angular/forms/index.ts b/nativescript-angular/forms/index.ts
index a95e3e129..c88691333 100644
--- a/nativescript-angular/forms/index.ts
+++ b/nativescript-angular/forms/index.ts
@@ -1 +1 @@
-export * from "./forms.module";
+export * from './forms.module';
diff --git a/nativescript-angular/forms/value-accessors/base-value-accessor.ts b/nativescript-angular/forms/value-accessors/base-value-accessor.ts
index 5f46ca98a..abc3bf4a3 100644
--- a/nativescript-angular/forms/value-accessors/base-value-accessor.ts
+++ b/nativescript-angular/forms/value-accessors/base-value-accessor.ts
@@ -1,38 +1,38 @@
-import { ControlValueAccessor } from "@angular/forms";
-import { View, unsetValue } from "tns-core-modules/ui/core/view";
+import { ControlValueAccessor } from '@angular/forms';
+import { View, unsetValue } from '@nativescript/core';
-import { isBlank } from "../../lang-facade";
+import { isBlank } from '../../lang-facade';
export class BaseValueAccessor implements ControlValueAccessor {
- private pendingChangeNotification: any = 0;
- onChange = (_) => { };
- onTouched = () => {};
-
- constructor(public view: TView) { }
-
- registerOnChange(fn: (_: any) => void): void {
- this.onChange = (arg) => {
- if (this.pendingChangeNotification) {
- clearTimeout(this.pendingChangeNotification);
- }
- this.pendingChangeNotification = setTimeout(() => {
- this.pendingChangeNotification = 0;
- fn(arg);
- }, 20);
- };
- }
-
- registerOnTouched(fn: () => void): void {
- this.onTouched = fn;
- }
-
- setDisabledState(isDisabled: boolean): void {
- this.view.isEnabled = !isDisabled;
- }
-
- writeValue(_: any) {}
-
- protected normalizeValue(value: any): any {
- return isBlank(value) ? unsetValue : value;
- }
+ private pendingChangeNotification: any = 0;
+ onChange = (_) => {};
+ onTouched = () => {};
+
+ constructor(public view: TView) {}
+
+ registerOnChange(fn: (_: any) => void): void {
+ this.onChange = (arg) => {
+ if (this.pendingChangeNotification) {
+ clearTimeout(this.pendingChangeNotification);
+ }
+ this.pendingChangeNotification = setTimeout(() => {
+ this.pendingChangeNotification = 0;
+ fn(arg);
+ }, 20);
+ };
+ }
+
+ registerOnTouched(fn: () => void): void {
+ this.onTouched = fn;
+ }
+
+ setDisabledState(isDisabled: boolean): void {
+ this.view.isEnabled = !isDisabled;
+ }
+
+ writeValue(_: any) {}
+
+ protected normalizeValue(value: any): any {
+ return isBlank(value) ? unsetValue : value;
+ }
}
diff --git a/nativescript-angular/forms/value-accessors/checked-value-accessor.ts b/nativescript-angular/forms/value-accessors/checked-value-accessor.ts
index 6053ee700..b394554d4 100644
--- a/nativescript-angular/forms/value-accessors/checked-value-accessor.ts
+++ b/nativescript-angular/forms/value-accessors/checked-value-accessor.ts
@@ -1,12 +1,12 @@
-import { Directive, ElementRef, forwardRef } from "@angular/core";
-import { NG_VALUE_ACCESSOR } from "@angular/forms";
-import { BaseValueAccessor } from "./base-value-accessor";
-import { Switch } from "tns-core-modules/ui/switch";
+import { Directive, ElementRef, forwardRef } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { BaseValueAccessor } from './base-value-accessor';
+import { Switch } from '@nativescript/core';
const CHECKED_VALUE_ACCESSOR = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => CheckedValueAccessor),
- multi: true,
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => CheckedValueAccessor),
+ multi: true,
};
/**
@@ -19,22 +19,20 @@ const CHECKED_VALUE_ACCESSOR = {
* ```
*/
@Directive({
- selector:
- "Switch[ngModel],Switch[formControlName],Switch[formControl]," +
- "switch[ngModel],switch[formControlName],switch[formControl]",
- providers: [CHECKED_VALUE_ACCESSOR],
- host: {
- "(touch)": "onTouched()",
- "(checkedChange)": "onChange($event.value)",
- },
+ selector: 'Switch[ngModel],Switch[formControlName],Switch[formControl],' + 'switch[ngModel],switch[formControlName],switch[formControl]',
+ providers: [CHECKED_VALUE_ACCESSOR],
+ host: {
+ '(checkedChange)': 'onChange($event.value)',
+ },
})
-export class CheckedValueAccessor extends BaseValueAccessor { // tslint:disable-line:directive-class-suffix
- constructor(elementRef: ElementRef) {
- super(elementRef.nativeElement);
- }
+export class CheckedValueAccessor extends BaseValueAccessor {
+ // tslint:disable-line:directive-class-suffix
+ constructor(elementRef: ElementRef) {
+ super(elementRef.nativeElement);
+ }
- writeValue(value: any): void {
- const normalized = super.normalizeValue(value);
- this.view.checked = normalized;
- }
+ writeValue(value: any): void {
+ const normalized = super.normalizeValue(value);
+ this.view.checked = normalized;
+ }
}
diff --git a/nativescript-angular/forms/value-accessors/date-value-accessor.ts b/nativescript-angular/forms/value-accessors/date-value-accessor.ts
index c7f8419a9..2417ef1b2 100644
--- a/nativescript-angular/forms/value-accessors/date-value-accessor.ts
+++ b/nativescript-angular/forms/value-accessors/date-value-accessor.ts
@@ -1,12 +1,12 @@
-import { Directive, ElementRef, forwardRef } from "@angular/core";
-import { NG_VALUE_ACCESSOR } from "@angular/forms";
-import { BaseValueAccessor } from "./base-value-accessor";
-import { DatePicker } from "tns-core-modules/ui/date-picker";
+import { Directive, ElementRef, forwardRef } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { BaseValueAccessor } from './base-value-accessor';
+import { DatePicker } from '@nativescript/core';
const DATE_VALUE_ACCESSOR = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => DateValueAccessor),
- multi: true,
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => DateValueAccessor),
+ multi: true,
};
/**
@@ -19,22 +19,20 @@ const DATE_VALUE_ACCESSOR = {
* ```
*/
@Directive({
- selector: "DatePicker[ngModel],DatePicker[formControlName],DatePicker[formControl]," +
- "datepicker[ngModel],datepicker[formControlName],datepicker[formControl]," +
- "datePicker[ngModel],datePicker[formControlName],datePicker[formControl]," +
- "date-picker[ngModel],date-picker[formControlName],date-picker[formControl]",
- providers: [DATE_VALUE_ACCESSOR],
- host: {
- "(dateChange)": "onChange($event.value)",
- },
+ selector: 'DatePicker[ngModel],DatePicker[formControlName],DatePicker[formControl],' + 'datepicker[ngModel],datepicker[formControlName],datepicker[formControl],' + 'datePicker[ngModel],datePicker[formControlName],datePicker[formControl],' + 'date-picker[ngModel],date-picker[formControlName],date-picker[formControl]',
+ providers: [DATE_VALUE_ACCESSOR],
+ host: {
+ '(dateChange)': 'onChange($event.value)',
+ },
})
-export class DateValueAccessor extends BaseValueAccessor { // tslint:disable-line:directive-class-suffix
- constructor(elementRef: ElementRef) {
- super(elementRef.nativeElement);
- }
+export class DateValueAccessor extends BaseValueAccessor {
+ // tslint:disable-line:directive-class-suffix
+ constructor(elementRef: ElementRef) {
+ super(elementRef.nativeElement);
+ }
- writeValue(value: any): void {
- const normalized = super.normalizeValue(value);
- this.view.date = normalized;
- }
+ writeValue(value: any): void {
+ const normalized = super.normalizeValue(value);
+ this.view.date = normalized;
+ }
}
diff --git a/nativescript-angular/forms/value-accessors/index.ts b/nativescript-angular/forms/value-accessors/index.ts
index 351151606..c3b2a05a2 100644
--- a/nativescript-angular/forms/value-accessors/index.ts
+++ b/nativescript-angular/forms/value-accessors/index.ts
@@ -1,7 +1,7 @@
-export * from "./base-value-accessor";
-export * from "./text-value-accessor";
-export * from "./checked-value-accessor";
-export * from "./date-value-accessor";
-export * from "./time-value-accessor";
-export * from "./number-value-accessor";
-export * from "./selectedIndex-value-accessor";
+export { BaseValueAccessor } from './base-value-accessor';
+export { TextValueAccessor, TextView } from './text-value-accessor';
+export { CheckedValueAccessor } from './checked-value-accessor';
+export { DateValueAccessor } from './date-value-accessor';
+export { TimeValueAccessor } from './time-value-accessor';
+export { NumberValueAccessor } from './number-value-accessor';
+export { SelectedIndexValueAccessor, SelectableView } from './selectedIndex-value-accessor';
diff --git a/nativescript-angular/forms/value-accessors/number-value-accessor.ts b/nativescript-angular/forms/value-accessors/number-value-accessor.ts
index 32275d4ea..8ec1481bf 100644
--- a/nativescript-angular/forms/value-accessors/number-value-accessor.ts
+++ b/nativescript-angular/forms/value-accessors/number-value-accessor.ts
@@ -1,12 +1,12 @@
-import { Directive, ElementRef, forwardRef } from "@angular/core";
-import { NG_VALUE_ACCESSOR } from "@angular/forms";
-import { BaseValueAccessor } from "./base-value-accessor";
-import { Slider } from "tns-core-modules/ui/slider";
+import { Directive, ElementRef, forwardRef } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { BaseValueAccessor } from './base-value-accessor';
+import { Slider } from '@nativescript/core';
const NUMBER_VALUE_ACCESSOR = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => NumberValueAccessor),
- multi: true,
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => NumberValueAccessor),
+ multi: true,
};
/**
@@ -19,21 +19,20 @@ const NUMBER_VALUE_ACCESSOR = {
* ```
*/
@Directive({
- selector:
- "Slider[ngModel],Slider[formControlName],Slider[formControl]," +
- "slider[ngModel],slider[formControlName],slider[formControl]",
- providers: [NUMBER_VALUE_ACCESSOR],
- host: {
- "(valueChange)": "onChange($event.value)",
- },
+ selector: 'Slider[ngModel],Slider[formControlName],Slider[formControl],' + 'slider[ngModel],slider[formControlName],slider[formControl]',
+ providers: [NUMBER_VALUE_ACCESSOR],
+ host: {
+ '(valueChange)': 'onChange($event.value)',
+ },
})
-export class NumberValueAccessor extends BaseValueAccessor { // tslint:disable-line:directive-class-suffix
- constructor(elementRef: ElementRef) {
- super(elementRef.nativeElement);
- }
+export class NumberValueAccessor extends BaseValueAccessor {
+ // tslint:disable-line:directive-class-suffix
+ constructor(elementRef: ElementRef) {
+ super(elementRef.nativeElement);
+ }
- writeValue(value: any): void {
- const normalized = super.normalizeValue(value);
- this.view.value = normalized;
- }
+ writeValue(value: any): void {
+ const normalized = super.normalizeValue(value);
+ this.view.value = normalized;
+ }
}
diff --git a/nativescript-angular/forms/value-accessors/selectedIndex-value-accessor.ts b/nativescript-angular/forms/value-accessors/selectedIndex-value-accessor.ts
index 2c9a7ddf1..9e98be412 100644
--- a/nativescript-angular/forms/value-accessors/selectedIndex-value-accessor.ts
+++ b/nativescript-angular/forms/value-accessors/selectedIndex-value-accessor.ts
@@ -1,15 +1,15 @@
-import { Directive, ElementRef, forwardRef, AfterViewInit } from "@angular/core";
-import { NG_VALUE_ACCESSOR } from "@angular/forms";
-import { BaseValueAccessor } from "./base-value-accessor";
-import { View } from "tns-core-modules/ui/core/view";
+import { Directive, ElementRef, forwardRef, AfterViewInit } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { BaseValueAccessor } from './base-value-accessor';
+import { View } from '@nativescript/core';
const SELECTED_INDEX_VALUE_ACCESSOR = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => SelectedIndexValueAccessor),
- multi: true,
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => SelectedIndexValueAccessor),
+ multi: true,
};
-export type SelectableView = {selectedIndex: number} & View;
+export type SelectableView = { selectedIndex: number } & View;
/**
* The accessor for setting a selectedIndex and listening to changes that is used by the
@@ -21,45 +21,44 @@ export type SelectableView = {selectedIndex: number} & View;
* ```
*/
@Directive({
- selector:
- "SegmentedBar[ngModel],SegmentedBar[formControlName],SegmentedBar[formControl]," +
- "segmentedBar[ngModel],segmentedBar[formControlName],segmentedBar[formControl]," +
- "segmentedbar[ngModel],segmentedbar[formControlName],segmentedbar[formControl]," +
- "segmented-bar[ngModel],segmented-bar[formControlName],segmented-bar[formControl]," +
-
- "ListPicker[ngModel],ListPicker[formControlName],ListPicker[formControl]," +
- "listPicker[ngModel],listPicker[formControlName],listPicker[formControl]," +
- "listpicker[ngModel],listpicker[formControlName],listpicker[formControl]," +
- "list-picker[ngModel],list-picker[formControlName],list-picker[formControl]," +
-
- "TabView[ngModel],TabView[formControlName],TabView[formControl]," +
- "tabView[ngModel],tabView[formControlName],tabView[formControl]," +
- "tabview[ngModel],tabview[formControlName],tabview[formControl]," +
- "tab-view[ngModel],tab-view[formControlName],tab-view[formControl]",
- providers: [SELECTED_INDEX_VALUE_ACCESSOR],
- host: {
- "(selectedIndexChange)": "onChange($event.value)",
- },
+ selector:
+ 'SegmentedBar[ngModel],SegmentedBar[formControlName],SegmentedBar[formControl],' +
+ 'segmentedBar[ngModel],segmentedBar[formControlName],segmentedBar[formControl],' +
+ 'segmentedbar[ngModel],segmentedbar[formControlName],segmentedbar[formControl],' +
+ 'segmented-bar[ngModel],segmented-bar[formControlName],segmented-bar[formControl],' +
+ 'ListPicker[ngModel],ListPicker[formControlName],ListPicker[formControl],' +
+ 'listPicker[ngModel],listPicker[formControlName],listPicker[formControl],' +
+ 'listpicker[ngModel],listpicker[formControlName],listpicker[formControl],' +
+ 'list-picker[ngModel],list-picker[formControlName],list-picker[formControl],' +
+ 'TabView[ngModel],TabView[formControlName],TabView[formControl],' +
+ 'tabView[ngModel],tabView[formControlName],tabView[formControl],' +
+ 'tabview[ngModel],tabview[formControlName],tabview[formControl],' +
+ 'tab-view[ngModel],tab-view[formControlName],tab-view[formControl]',
+ providers: [SELECTED_INDEX_VALUE_ACCESSOR],
+ host: {
+ '(selectedIndexChange)': 'onChange($event.value)',
+ },
})
-export class SelectedIndexValueAccessor extends BaseValueAccessor implements AfterViewInit { // tslint:disable-line:max-line-length directive-class-suffix
- constructor(elementRef: ElementRef) {
- super(elementRef.nativeElement);
- }
+export class SelectedIndexValueAccessor extends BaseValueAccessor implements AfterViewInit {
+ // tslint:disable-line:max-line-length directive-class-suffix
+ constructor(elementRef: ElementRef) {
+ super(elementRef.nativeElement);
+ }
- private value: number;
- private viewInitialized: boolean;
+ private value: number;
+ private viewInitialized: boolean;
- writeValue(value: any): void {
- const normalized = super.normalizeValue(value);
- this.value = normalized;
+ writeValue(value: any): void {
+ const normalized = super.normalizeValue(value);
+ this.value = normalized;
- if (this.viewInitialized) {
- this.view.selectedIndex = this.value;
- }
- }
+ if (this.viewInitialized) {
+ this.view.selectedIndex = this.value;
+ }
+ }
- ngAfterViewInit() {
- this.viewInitialized = true;
- this.view.selectedIndex = this.value;
- }
+ ngAfterViewInit() {
+ this.viewInitialized = true;
+ this.view.selectedIndex = this.value;
+ }
}
diff --git a/nativescript-angular/forms/value-accessors/text-value-accessor.ts b/nativescript-angular/forms/value-accessors/text-value-accessor.ts
index de41fae85..c993e3490 100644
--- a/nativescript-angular/forms/value-accessors/text-value-accessor.ts
+++ b/nativescript-angular/forms/value-accessors/text-value-accessor.ts
@@ -1,15 +1,15 @@
-import { Directive, ElementRef, forwardRef } from "@angular/core";
-import { NG_VALUE_ACCESSOR } from "@angular/forms";
-import { BaseValueAccessor } from "./base-value-accessor";
-import { View } from "tns-core-modules/ui/core/view";
+import { Directive, ElementRef, forwardRef } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { View } from '@nativescript/core';
+import { BaseValueAccessor } from './base-value-accessor';
const TEXT_VALUE_ACCESSOR = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => TextValueAccessor),
- multi: true,
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => TextValueAccessor),
+ multi: true,
};
-export type TextView = {text: string} & View;
+export type TextView = { text: string } & View;
/**
* The accessor for writing a text and listening to changes that is used by the
@@ -21,34 +21,33 @@ export type TextView = {text: string} & View;
* ```
*/
@Directive({
- selector:
- "TextField[ngModel],TextField[formControlName],TextField[formControl]," +
- "textField[ngModel],textField[formControlName],textField[formControl]," +
- "textfield[ngModel],textfield[formControlName],textfield[formControl]," +
- "text-field[ngModel],text-field[formControlName],text-field[formControl]," +
-
- "TextView[ngModel],TextView[formControlName],TextView[formControl]," +
- "textView[ngModel],textView[formControlName],textView[formControl]," +
- "textview[ngModel],textview[formControlName],textview[formControl]," +
- "text-view[ngModel],text-view[formControlName],text-view[formControl]," +
-
- "SearchBar[ngModel],SearchBar[formControlName],SearchBar[formControl]," +
- "searchBar[ngModel],searchBar[formControlName],searchBar[formControl]," +
- "searchbar[ngModel],searchbar[formControlName],searchbar[formControl]," +
- "search-bar[ngModel], search-bar[formControlName],search-bar[formControl]",
- providers: [TEXT_VALUE_ACCESSOR],
- host: {
- "(touch)": "onTouched()",
- "(textChange)": "onChange($event.value)",
- },
+ selector:
+ 'TextField[ngModel],TextField[formControlName],TextField[formControl],' +
+ 'textField[ngModel],textField[formControlName],textField[formControl],' +
+ 'textfield[ngModel],textfield[formControlName],textfield[formControl],' +
+ 'text-field[ngModel],text-field[formControlName],text-field[formControl],' +
+ 'TextView[ngModel],TextView[formControlName],TextView[formControl],' +
+ 'textView[ngModel],textView[formControlName],textView[formControl],' +
+ 'textview[ngModel],textview[formControlName],textview[formControl],' +
+ 'text-view[ngModel],text-view[formControlName],text-view[formControl],' +
+ 'SearchBar[ngModel],SearchBar[formControlName],SearchBar[formControl],' +
+ 'searchBar[ngModel],searchBar[formControlName],searchBar[formControl],' +
+ 'searchbar[ngModel],searchbar[formControlName],searchbar[formControl],' +
+ 'search-bar[ngModel], search-bar[formControlName],search-bar[formControl]',
+ providers: [TEXT_VALUE_ACCESSOR],
+ host: {
+ '(blur)': 'onTouched()',
+ '(textChange)': 'onChange($event.value)',
+ },
})
-export class TextValueAccessor extends BaseValueAccessor { // tslint:disable-line:directive-class-suffix
- constructor(elementRef: ElementRef) {
- super(elementRef.nativeElement);
- }
+export class TextValueAccessor extends BaseValueAccessor {
+ // tslint:disable-line:directive-class-suffix
+ constructor(elementRef: ElementRef) {
+ super(elementRef.nativeElement);
+ }
- writeValue(value: any): void {
- const normalized = super.normalizeValue(value);
- this.view.text = normalized;
- }
+ writeValue(value: any): void {
+ const normalized = super.normalizeValue(value);
+ this.view.text = normalized;
+ }
}
diff --git a/nativescript-angular/forms/value-accessors/time-value-accessor.ts b/nativescript-angular/forms/value-accessors/time-value-accessor.ts
index 6be6851ee..b15aa6c34 100644
--- a/nativescript-angular/forms/value-accessors/time-value-accessor.ts
+++ b/nativescript-angular/forms/value-accessors/time-value-accessor.ts
@@ -1,12 +1,12 @@
-import { Directive, ElementRef, forwardRef } from "@angular/core";
-import { NG_VALUE_ACCESSOR } from "@angular/forms";
-import { BaseValueAccessor } from "./base-value-accessor";
-import { TimePicker } from "tns-core-modules/ui/time-picker";
+import { Directive, ElementRef, forwardRef } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { BaseValueAccessor } from './base-value-accessor';
+import { TimePicker } from '@nativescript/core';
const TIME_VALUE_ACCESSOR = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => TimeValueAccessor),
- multi: true,
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => TimeValueAccessor),
+ multi: true,
};
/**
@@ -19,24 +19,20 @@ const TIME_VALUE_ACCESSOR = {
* ```
*/
@Directive({
- selector:
- "TimePicker[ngModel],TimePicker[formControlName],TimePicker[formControl]," +
- "timepicker[ngModel],timepicker[formControlName],timepicker[formControl]," +
- "timePicker[ngModel],timePicker[formControlName],timePicker[formControl]," +
- "time-picker[ngModel],time-picker[formControlName],time-picker[formControl]",
- providers: [TIME_VALUE_ACCESSOR],
- host: {
- "(touch)": "onTouched()",
- "(timeChange)": "onChange($event.value)",
- },
+ selector: 'TimePicker[ngModel],TimePicker[formControlName],TimePicker[formControl],' + 'timepicker[ngModel],timepicker[formControlName],timepicker[formControl],' + 'timePicker[ngModel],timePicker[formControlName],timePicker[formControl],' + 'time-picker[ngModel],time-picker[formControlName],time-picker[formControl]',
+ providers: [TIME_VALUE_ACCESSOR],
+ host: {
+ '(timeChange)': 'onChange($event.value)',
+ },
})
-export class TimeValueAccessor extends BaseValueAccessor { // tslint:disable-line:directive-class-suffix
- constructor(elementRef: ElementRef) {
- super(elementRef.nativeElement);
- }
+export class TimeValueAccessor extends BaseValueAccessor {
+ // tslint:disable-line:directive-class-suffix
+ constructor(elementRef: ElementRef) {
+ super(elementRef.nativeElement);
+ }
- writeValue(value: any): void {
- const normalized = super.normalizeValue(value);
- this.view.time = normalized;
- }
+ writeValue(value: any): void {
+ const normalized = super.normalizeValue(value);
+ this.view.time = normalized;
+ }
}
diff --git a/nativescript-angular/frame.service.ts b/nativescript-angular/frame.service.ts
new file mode 100644
index 000000000..952a25d24
--- /dev/null
+++ b/nativescript-angular/frame.service.ts
@@ -0,0 +1,13 @@
+import { Injectable } from '@angular/core';
+import { Frame } from '@nativescript/core';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class FrameService {
+ // TODO: Add any methods that are needed to handle frame/page navigation
+ getFrame(): Frame {
+ let topmostFrame = Frame.topmost();
+ return topmostFrame;
+ }
+}
diff --git a/nativescript-angular/global.d.ts b/nativescript-angular/global.d.ts
index 6954b2e13..6bb03980d 100644
--- a/nativescript-angular/global.d.ts
+++ b/nativescript-angular/global.d.ts
@@ -1 +1 @@
-///
+///
diff --git a/nativescript-angular/hooks/before-livesync.js b/nativescript-angular/hooks/before-livesync.js
deleted file mode 100644
index 9642f1c12..000000000
--- a/nativescript-angular/hooks/before-livesync.js
+++ /dev/null
@@ -1,3 +0,0 @@
-module.exports = function ($usbLiveSyncService) {
- $usbLiveSyncService.forceExecuteFullSync = false;
-};
diff --git a/nativescript-angular/hooks/hook-helper.js b/nativescript-angular/hooks/hook-helper.js
deleted file mode 100644
index 3a23ae688..000000000
--- a/nativescript-angular/hooks/hook-helper.js
+++ /dev/null
@@ -1,33 +0,0 @@
-"use strict";
-var fs = require("fs");
-var path = require("path");
-
-exports.findProjectDir = function findProjectDir() {
- var candidateDir = path.join(__dirname, "..");
- while (true) {
- var oldCandidateDir = candidateDir;
- candidateDir = path.dirname(candidateDir);
- if (path.basename(candidateDir) === 'node_modules') {
- continue;
- }
- var packageJsonFile = path.join(candidateDir, 'package.json');
- if (fs.existsSync(packageJsonFile)) {
- return candidateDir;
- }
- if (oldCandidateDir === candidateDir) {
- return;
- }
- }
-};
-
-exports.getHooksDir = function getHooksDir() {
- return path.join(exports.findProjectDir(), 'hooks');
-};
-
-exports.getBeforeLivesyncHookDir = function getBeforeLivesyncHookDir() {
- return path.join(exports.getHooksDir(), "before-livesync");
-};
-
-exports.getHookFilePath = function getHookFilePath() {
- return path.join(exports.getBeforeLivesyncHookDir(), "nativescript-angular-sync.js");
-};
diff --git a/nativescript-angular/http-client/http-client.module.ts b/nativescript-angular/http-client/http-client.module.ts
index ea6623d5b..2f1c9a084 100644
--- a/nativescript-angular/http-client/http-client.module.ts
+++ b/nativescript-angular/http-client/http-client.module.ts
@@ -1,26 +1,13 @@
-import { NgModule } from "@angular/core";
+import { NgModule } from '@angular/core';
-// IMPORTant: Importing "@angular/common/http" for the first time overwrites the
-// global.__extends function.
-const cachedExtends = global.__extends;
-import { HttpClientModule, HttpBackend } from "@angular/common/http";
-global.__extends = cachedExtends;
+import { HttpClientModule, HttpBackend } from '@angular/common/http';
-import { NSFileSystem } from "../file-system/ns-file-system";
-import { NsHttpBackEnd } from "./ns-http-backend";
+import { NSFileSystem } from '../file-system/ns-file-system';
+import { NsHttpBackEnd } from './ns-http-backend';
@NgModule({
- providers: [
- NSFileSystem,
- NsHttpBackEnd,
- { provide: HttpBackend, useExisting: NsHttpBackEnd },
- ],
- imports: [
- HttpClientModule,
- ],
- exports: [
- HttpClientModule,
- ]
+ providers: [NSFileSystem, NsHttpBackEnd, { provide: HttpBackend, useExisting: NsHttpBackEnd }],
+ imports: [HttpClientModule],
+ exports: [HttpClientModule],
})
-export class NativeScriptHttpClientModule {
-}
+export class NativeScriptHttpClientModule {}
diff --git a/nativescript-angular/http-client/http-utils.ts b/nativescript-angular/http-client/http-utils.ts
index ae0ecc6f8..c719e4c2f 100644
--- a/nativescript-angular/http-client/http-utils.ts
+++ b/nativescript-angular/http-client/http-utils.ts
@@ -1,52 +1,47 @@
-import { NSFileSystem } from "../file-system/ns-file-system";
+import { NSFileSystem } from '../file-system/ns-file-system';
-import { Observable } from "rxjs/Observable";
-import { Observer } from "rxjs/Observer";
-import { path } from "tns-core-modules/file-system/file-system";
+import { Observable, Observer } from 'rxjs';
+import { path } from '@nativescript/core';
export type httpResponseFactory = (url: string, body: any, status: number) => T;
export type httpErrorFactory = (url: string, body: any, status: number) => any;
export function isLocalRequest(url: string): boolean {
- return url.indexOf("~") === 0 || url.indexOf("/") === 0;
+ return url.indexOf('~') === 0 || url.indexOf('/') === 0;
}
export function getAbsolutePath(url: string, nsFileSystem: NSFileSystem): string {
- url = url.replace("~", "").replace("/", "");
- url = path.join(nsFileSystem.currentApp().path, url);
- return url;
+ url = url.replace('~', '').replace('/', '');
+ url = path.join(nsFileSystem.currentApp().path, url);
+ return url;
}
-export function processLocalFileRequest(
- url: string,
- nsFileSystem: NSFileSystem,
- successResponse: httpResponseFactory,
- errorResponse: httpErrorFactory): Observable {
-
- url = getAbsolutePath(url, nsFileSystem);
-
- // request from local app resources
- return new Observable((observer: Observer) => {
- if (nsFileSystem.fileExists(url)) {
- const localFile = nsFileSystem.fileFromPath(url);
- localFile.readText()
- .then((data) => {
- try {
- const json = JSON.parse(data);
- observer.next(successResponse(url, json, 200));
- observer.complete();
- } catch (error) {
- // Even though the response status was 2xx, this is still an error.
- // The parse error contains the text of the body that failed to parse.
- const errorResult = { error, text: data };
- observer.error(errorResponse(url, errorResult, 200));
- }
- }, (err: Object) => {
- observer.error(errorResponse(url, err, 400));
-
- });
- } else {
- observer.error(errorResponse(url, "Not Found", 404));
- }
- });
+export function processLocalFileRequest(url: string, nsFileSystem: NSFileSystem, successResponse: httpResponseFactory, errorResponse: httpErrorFactory): Observable {
+ url = getAbsolutePath(url, nsFileSystem);
+
+ // request from local app resources
+ return new Observable((observer: Observer) => {
+ if (nsFileSystem.fileExists(url)) {
+ const localFile = nsFileSystem.fileFromPath(url);
+ localFile.readText().then(
+ (data) => {
+ try {
+ const json = JSON.parse(data);
+ observer.next(successResponse(url, json, 200));
+ observer.complete();
+ } catch (error) {
+ // Even though the response status was 2xx, this is still an error.
+ // The parse error contains the text of the body that failed to parse.
+ const errorResult = { error, text: data };
+ observer.error(errorResponse(url, errorResult, 200));
+ }
+ },
+ (err: Object) => {
+ observer.error(errorResponse(url, err, 400));
+ }
+ );
+ } else {
+ observer.error(errorResponse(url, 'Not Found', 404));
+ }
+ });
}
diff --git a/nativescript-angular/http-client/index.ts b/nativescript-angular/http-client/index.ts
index da2e04db0..f4174328b 100644
--- a/nativescript-angular/http-client/index.ts
+++ b/nativescript-angular/http-client/index.ts
@@ -1,2 +1,2 @@
-export * from "./http-client.module";
-export * from "./ns-http-backend";
+export * from './http-client.module';
+export * from './ns-http-backend';
diff --git a/nativescript-angular/http-client/ns-http-backend.ts b/nativescript-angular/http-client/ns-http-backend.ts
index 6cf2f2bf4..a4ef9bc2a 100644
--- a/nativescript-angular/http-client/ns-http-backend.ts
+++ b/nativescript-angular/http-client/ns-http-backend.ts
@@ -1,62 +1,47 @@
-import { Injectable } from "@angular/core";
-import {
- HttpRequest, HttpEvent,
- XhrFactory, HttpResponse,
- HttpErrorResponse, HttpXhrBackend
-} from "@angular/common/http";
-import { Observable } from "rxjs/Observable";
+import { Injectable } from '@angular/core';
+import { HttpRequest, HttpEvent, XhrFactory, HttpResponse, HttpErrorResponse, HttpXhrBackend } from '@angular/common/http';
+import { Observable } from 'rxjs';
-import { NSFileSystem } from "../file-system/ns-file-system";
-import { isLocalRequest, processLocalFileRequest } from "./http-utils";
+import { NSFileSystem } from '../file-system/ns-file-system';
+import { isLocalRequest, processLocalFileRequest } from './http-utils';
@Injectable()
export class NsHttpBackEnd extends HttpXhrBackend {
- constructor(xhrFactory: XhrFactory, private nsFileSystem: NSFileSystem) {
- super(xhrFactory);
- }
-
- handle(req: HttpRequest): Observable> {
- let result: Observable>;
-
- if (isLocalRequest(req.url)) {
- result = this.handleLocalFileRequest(req.url);
- } else {
- result = super.handle(req);
- }
-
- return result;
- }
-
- private handleLocalFileRequest(url: string): Observable> {
- return processLocalFileRequest(
- url,
- this.nsFileSystem,
- createSuccessResponse,
- createErrorResponse
- );
- }
+ constructor(xhrFactory: XhrFactory, private nsFileSystem: NSFileSystem) {
+ super(xhrFactory);
+ }
+
+ handle(req: HttpRequest): Observable> {
+ let result: Observable>;
+
+ if (isLocalRequest(req.url)) {
+ result = this.handleLocalFileRequest(req.url);
+ } else {
+ result = super.handle(req);
+ }
+
+ return result;
+ }
+
+ private handleLocalFileRequest(url: string): Observable> {
+ return processLocalFileRequest(url, this.nsFileSystem, createSuccessResponse, createErrorResponse);
+ }
}
-function createSuccessResponse(
- url: string,
- body: any,
- status: number): HttpEvent {
- return new HttpResponse({
- url,
- body,
- status,
- statusText: "OK"
- });
+function createSuccessResponse(url: string, body: any, status: number): HttpEvent {
+ return new HttpResponse({
+ url,
+ body,
+ status,
+ statusText: 'OK',
+ });
}
-function createErrorResponse(
- url: string,
- body: any,
- status: number): HttpErrorResponse {
- return new HttpErrorResponse({
- url,
- error: body,
- status,
- statusText: "ERROR"
- });
+function createErrorResponse(url: string, body: any, status: number): HttpErrorResponse {
+ return new HttpErrorResponse({
+ url,
+ error: body,
+ status,
+ statusText: 'ERROR',
+ });
}
diff --git a/nativescript-angular/http/http.module.ts b/nativescript-angular/http/http.module.ts
deleted file mode 100644
index b6d35172c..000000000
--- a/nativescript-angular/http/http.module.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { NgModule } from "@angular/core";
-import { Http, XHRBackend, RequestOptions, HttpModule, XSRFStrategy } from "@angular/http";
-
-import { NSXSRFStrategy, NSHttp } from "./ns-http";
-import { NSFileSystem } from "../file-system/ns-file-system";
-
-export { NSHttp } from "./ns-http";
-
-export function nsHttpFactory(backend, options, nsFileSystem) {
- return new NSHttp(backend, options, nsFileSystem);
-}
-
-export function nsXSRFStrategyFactory() {
- return new NSXSRFStrategy();
-}
-
-@NgModule({
- providers: [
- { provide: XSRFStrategy, useFactory: nsXSRFStrategyFactory },
- NSFileSystem,
- {
- provide: Http, useFactory: nsHttpFactory,
- deps: [XHRBackend, RequestOptions, NSFileSystem]
- }
- ],
- imports: [
- HttpModule,
- ],
- exports: [
- HttpModule,
- ]
-})
-export class NativeScriptHttpModule {
-}
diff --git a/nativescript-angular/http/index.ts b/nativescript-angular/http/index.ts
deleted file mode 100644
index 720657322..000000000
--- a/nativescript-angular/http/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from "./http.module";
diff --git a/nativescript-angular/http/ns-http.ts b/nativescript-angular/http/ns-http.ts
deleted file mode 100644
index e0f04b661..000000000
--- a/nativescript-angular/http/ns-http.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { Injectable } from "@angular/core";
-import {
- Http,
- ConnectionBackend,
- Request,
- RequestOptions,
- RequestOptionsArgs,
- ResponseOptions,
- ResponseType,
- Response
-} from "@angular/http";
-import { Observable } from "rxjs/Observable";
-import "rxjs/add/observable/fromPromise";
-
-import { isLocalRequest, processLocalFileRequest } from "../http-client/http-utils";
-
-import { NSFileSystem } from "../file-system/ns-file-system";
-
-export class NSXSRFStrategy {
- public configureRequest(_req: any) {
- // noop
- }
-}
-
-@Injectable()
-export class NSHttp extends Http {
- constructor(backend: ConnectionBackend, defaultOptions: RequestOptions, private nsFileSystem: NSFileSystem) {
- super(backend, defaultOptions);
- }
-
- /**
- * Performs a request with `request` http method.
- */
- request(req: string | Request, options?: RequestOptionsArgs): Observable {
- const urlString = typeof req === "string" ? req : req.url;
- if (isLocalRequest(urlString)) {
- return this.requestLocalFile(urlString);
- } else {
- return super.request(req, options);
- }
- }
-
- /**
- * Performs a request with `get` http method.
- */
- get(url: string, options?: RequestOptionsArgs): Observable {
- if (isLocalRequest(url)) {
- return this.requestLocalFile(url);
- } else {
- return super.get(url, options);
- }
- }
-
- private requestLocalFile(url: string): Observable {
- return processLocalFileRequest(
- url,
- this.nsFileSystem,
- createResponse,
- createResponse
- );
- }
-}
-
-function createResponse(url: string, body: string | Object, status: number): Response {
- return new Response(new ResponseOptions({
- body: body,
- status: status,
- statusText: "OK",
- type: status === 200 ? ResponseType.Default : ResponseType.Error,
- url: url
- }));
-}
diff --git a/nativescript-angular/index.ts b/nativescript-angular/index.ts
index e5f4bfb8b..f0e97a09c 100644
--- a/nativescript-angular/index.ts
+++ b/nativescript-angular/index.ts
@@ -1,29 +1,38 @@
-import "tns-core-modules/application";
+// Initial imports and polyfills
+import '@nativescript/core';
+import '@nativescript/zone-js';
+// TODO: migrate to standard zone.js if possible
+// investigate Ivy with templated-items-comp to allow standard zone below to be used instead of patched {N} zone above
+// import 'zone.js/dist/zone';
+import './dom-adapter';
+// import "./polyfills/array";
+import './polyfills/console';
+import 'nativescript-intl';
-export * from "./platform-common";
-export * from "./platform";
-export * from "./platform-static";
-export * from "./router";
-export * from "./forms";
-export * from "./http";
-export * from "./directives";
-export * from "./common/detached-loader";
-export * from "./trace";
-export * from "./platform-providers";
-export * from "./file-system/ns-file-system";
-export * from "./modal-dialog";
-export * from "./renderer";
-export * from "./view-util";
-export * from "./resource-loader";
+export * from './platform-common';
+export * from './platform-providers';
+export * from './platform';
+export * from './resource-loader';
-export {
- ViewClass,
- ViewClassMeta,
- ViewResolver,
- getViewClass,
- getViewMeta,
- isKnownView,
- registerElement,
-} from "./element-registry";
+export * from './nativescript.module';
+export * from './common';
+export * from './common/detached-loader';
+export * from './common/utils';
-export * from "./forms/value-accessors/base-value-accessor";
+export { NativeScriptAnimationsModule } from './animations';
+export * from './file-system';
+export * from './http-client';
+export * from './forms';
+export * from './directives';
+export * from './router';
+export * from './frame.service';
+
+export { NativeScriptRenderer } from './renderer';
+export { EmulatedRenderer } from './renderer-emulated';
+export { NativeScriptRendererFactory } from './renderer-factory';
+
+// utils
+export { NativeScriptDebug } from './trace';
+export * from './app-host-view';
+export { getViewClass, getViewMeta, isKnownView, registerElement, CommentNode, getSingleViewRecursive, isInvisibleNode, isView } from './element-registry';
+export type { NgView, ViewClass, ViewClassMeta, ViewResolver, ViewExtensions } from './element-registry';
diff --git a/nativescript-angular/lang-facade.ts b/nativescript-angular/lang-facade.ts
index 31b567065..62b677fd3 100644
--- a/nativescript-angular/lang-facade.ts
+++ b/nativescript-angular/lang-facade.ts
@@ -1,7 +1,7 @@
export function isPresent(obj: any): boolean {
- return obj !== undefined && obj !== null;
+ return obj !== undefined && obj !== null;
}
export function isBlank(obj: any): boolean {
- return obj === undefined || obj === null;
+ return obj === undefined || obj === null;
}
diff --git a/nativescript-angular/modal-dialog.ts b/nativescript-angular/modal-dialog.ts
deleted file mode 100644
index 0aa0ee079..000000000
--- a/nativescript-angular/modal-dialog.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export {
- ModalDialogHost,
- ModalDialogOptions,
- ModalDialogParams,
- ModalDialogService
-} from "./directives/dialogs";
diff --git a/nativescript-angular/nativescript.module.ts b/nativescript-angular/nativescript.module.ts
index 311c82227..27f682d33 100644
--- a/nativescript-angular/nativescript.module.ts
+++ b/nativescript-angular/nativescript.module.ts
@@ -1,59 +1,30 @@
-import "tns-core-modules/globals";
-// Require application early to work around a circular import
-import "tns-core-modules/application";
-import "./zone-js/dist/zone-nativescript";
+import { ApplicationModule, ErrorHandler, NO_ERRORS_SCHEMA, NgModule, RendererFactory2, SystemJsNgModuleLoader, Optional, SkipSelf, ɵINJECTOR_SCOPE } from '@angular/core';
-import "reflect-metadata";
-import "./polyfills/array";
-import "./polyfills/console";
+import { ViewportScroller, ɵNullViewportScroller as NullViewportScroller } from '@angular/common';
-import {
- ApplicationModule,
- ErrorHandler,
- NO_ERRORS_SCHEMA,
- NgModule,
- RendererFactory2,
- SystemJsNgModuleLoader,
- Optional,
- SkipSelf,
-} from "@angular/core";
-
-import { NativeScriptCommonModule } from "./common";
-import { NativeScriptRendererFactory } from "./renderer";
-import { DetachedLoader } from "./common/detached-loader";
-import { throwIfAlreadyLoaded } from "./common/utils";
+import { NativeScriptCommonModule } from './common';
+import { NativeScriptRendererFactory } from './renderer-factory';
+import { DetachedLoader } from './common/detached-loader';
+import { throwIfAlreadyLoaded } from './common/utils';
+import { FrameService } from './frame.service';
export function errorHandlerFactory() {
- return new ErrorHandler();
+ return new ErrorHandler();
}
+export { DetachedLoader } from './common/detached-loader';
+
@NgModule({
- declarations: [
- DetachedLoader,
- ],
- providers: [
- NativeScriptRendererFactory,
- SystemJsNgModuleLoader,
- { provide: ErrorHandler, useFactory: errorHandlerFactory },
- { provide: RendererFactory2, useExisting: NativeScriptRendererFactory },
- ],
- entryComponents: [
- DetachedLoader,
- ],
- imports: [
- ApplicationModule,
- NativeScriptCommonModule,
- ],
- exports: [
- ApplicationModule,
- NativeScriptCommonModule,
- DetachedLoader,
- ],
- schemas: [NO_ERRORS_SCHEMA]
+ declarations: [DetachedLoader],
+ providers: [FrameService, NativeScriptRendererFactory, SystemJsNgModuleLoader, { provide: ɵINJECTOR_SCOPE, useValue: 'root' }, { provide: ErrorHandler, useFactory: errorHandlerFactory }, { provide: RendererFactory2, useExisting: NativeScriptRendererFactory }, { provide: ViewportScroller, useClass: NullViewportScroller }],
+ entryComponents: [DetachedLoader],
+ imports: [ApplicationModule, NativeScriptCommonModule],
+ exports: [ApplicationModule, NativeScriptCommonModule, DetachedLoader],
+ schemas: [NO_ERRORS_SCHEMA],
})
export class NativeScriptModule {
- constructor(@Optional() @SkipSelf() parentModule: NativeScriptModule) {
- // Prevents NativeScriptModule from getting imported multiple times
- throwIfAlreadyLoaded(parentModule, "NativeScriptModule");
- }
+ constructor(@Optional() @SkipSelf() parentModule: NativeScriptModule) {
+ // Prevents NativeScriptModule from getting imported multiple times
+ throwIfAlreadyLoaded(parentModule, 'NativeScriptModule');
+ }
}
diff --git a/nativescript-angular/package.json b/nativescript-angular/package.json
index 39c4e6608..574fab819 100644
--- a/nativescript-angular/package.json
+++ b/nativescript-angular/package.json
@@ -1,22 +1,19 @@
{
- "private": true,
- "name": "nativescript-angular",
- "version": "5.2.0",
+ "name": "@nativescript/angular",
+ "version": "11.8.0",
"description": "An Angular renderer that lets you build mobile apps with NativeScript.",
"homepage": "https://www.nativescript.org/",
"bugs": "https://github.com/NativeScript/nativescript-angular/issues",
+ "main": "index.js",
+ "module": "index.js",
+ "types": "index.d.ts",
"author": {
"name": "NativeScript Team"
},
- "contributors": [
- "Hristo Deshev ",
- "Alexander Vakrilov ",
- "Stanimira Vlaeva "
- ],
"nativescript": {
"platforms": {
- "android": "3.0.0",
- "ios": "3.0.0"
+ "android": "6.0.0",
+ "ios": "6.0.0"
}
},
"keywords": [
@@ -29,49 +26,73 @@
"url": "https://github.com/NativeScript/nativescript-angular.git"
},
"scripts": {
- "tslint": "tslint --project tsconfig.json --config tslint.json --type-check",
- "postinstall": "node postinstall.js",
+ "setup": "npx rimraf hooks node_modules package-lock.json && npm i && npm run prep.apps",
+ "prep.apps": "npm run build.pack && cd ../build/pack-scripts && npm i && npx ts-node pack-scoped.ts",
+ "build": "ng-packagr -p package.json",
+ "build.pack": "npm run tsc && npm run build && cd dist && npm pack",
+ "format": "npx prettier --write .",
+ "format-check": "npx prettier --check .",
"tsc": "tsc -p tsconfig.json",
- "ngc": "ngc -p tsconfig.json",
- "prepare": "npm run ngc"
+ "tsc-w": "tsc -p tsconfig.json -w",
+ "ngc": "ngc",
+ "ngcc": "ngcc",
+ "ngcc-run": "npm run ngcc && ngc -p tsconfig.json",
+ "changelog": "conventional-changelog -p angular -i ../CHANGELOG.md -s",
+ "version": "rm -rf package-lock.json && npm run changelog && git add ../CHANGELOG.md",
+ "typedoc": "typedoc --tsconfig \"./tsconfig.typedoc.json\" --out ./bin/dist/ng-api-reference --includeDeclarations --name \"NativeScript Angular\" --theme ./node_modules/nativescript-typedoc-theme --excludeExternals --externalPattern \"**/+(tns-core-modules|module|declarations).d.ts\""
},
- "bin": {
- "update-app-ng-deps": "./bin/update-app-ng-deps"
+ "sideEffects": false,
+ "ngPackage": {
+ "assets": [
+ "../README.md"
+ ],
+ "lib": {
+ "entryFile": "index.ts",
+ "umdModuleIds": {
+ "@nativescript/core": "ns-core"
+ }
+ },
+ "allowedNonPeerDependencies": [
+ "."
+ ]
},
"dependencies": {
- "nativescript-intl": "^3.0.0",
- "reflect-metadata": "^0.1.8"
- },
- "peerDependencies": {
- "@angular/platform-browser-dynamic": "~5.2.0",
- "@angular/common": "~5.2.0",
- "@angular/compiler": "~5.2.0",
- "@angular/core": "~5.2.0",
- "@angular/forms": "~5.2.0",
- "@angular/http": "~5.2.0",
- "@angular/platform-browser": "~5.2.0",
- "@angular/router": "~5.2.0",
- "rxjs": "^5.5.0",
- "tns-core-modules": "^3.4.0 || >4.0.0-",
- "zone.js": "^0.8.4",
- "typescript": "~2.6.2"
+ "@nativescript/zone-js": "~1.0.0",
+ "nativescript-intl": "^4.0.0"
},
"devDependencies": {
- "@angular/platform-browser-dynamic": "~5.2.0",
- "@angular/animations": "~5.2.0",
- "@angular/common": "~5.2.0",
- "@angular/compiler": "~5.2.0",
- "@angular/compiler-cli": "~5.2.0",
- "@angular/core": "~5.2.0",
- "@angular/forms": "~5.2.0",
- "@angular/http": "~5.2.0",
- "@angular/platform-browser": "~5.2.0",
- "@angular/router": "~5.2.0",
- "codelyzer": "^4.0.0",
- "rxjs": "^5.5.0",
- "tns-core-modules": "next",
- "tslint": "^5.5.0",
- "typescript": "~2.6.2",
- "zone.js": "^0.8.12"
+ "@angular/animations": "~11.2.0",
+ "@angular/common": "~11.2.0",
+ "@angular/compiler": "~11.2.0",
+ "@angular/compiler-cli": "~11.2.0",
+ "@angular/core": "~11.2.0",
+ "@angular/forms": "~11.2.0",
+ "@angular/platform-browser": "~11.2.0",
+ "@angular/platform-browser-dynamic": "~11.2.0",
+ "@angular/router": "~11.2.0",
+ "@nativescript/core": "~8.0.0",
+ "codelyzer": "^5.2.0",
+ "conventional-changelog-cli": "^2.0.34",
+ "husky": "^4.2.5",
+ "lint-staged": "^10.2.11",
+ "nativescript-typedoc-theme": "git://github.com/NativeScript/nativescript-typedoc-theme.git#master",
+ "ng-packagr": "~11.2.0",
+ "prettier": "^2.0.5",
+ "rxjs": "~6.6.0",
+ "tslint": "^6.1.2",
+ "tslint-config-prettier": "^1.18.0",
+ "typedoc": "~0.17.0",
+ "typescript": "~4.0.0",
+ "zone.js": "^0.11.1"
+ },
+ "husky": {
+ "hooks": {
+ "pre-commit": "lint-staged"
+ }
+ },
+ "lint-staged": {
+ ".": [
+ "prettier --write ."
+ ]
}
}
diff --git a/nativescript-angular/platform-common.ts b/nativescript-angular/platform-common.ts
index 30f7d6f66..48ffde08f 100644
--- a/nativescript-angular/platform-common.ts
+++ b/nativescript-angular/platform-common.ts
@@ -1,237 +1,344 @@
-// Initial imports and polyfills
-import "tns-core-modules/globals";
-// Require application early to work around a circular import
-import "tns-core-modules/application";
-import "./zone-js/dist/zone-nativescript";
-import "reflect-metadata";
-import "./polyfills/array";
-import "./polyfills/console";
-import { profile, log, uptime } from "tns-core-modules/profiling";
-
-import {
- Type,
- Injector,
- CompilerOptions,
- PlatformRef,
- NgModuleFactory,
- NgModuleRef,
- EventEmitter,
- Sanitizer,
- InjectionToken,
- StaticProvider
-} from "@angular/core";
-import { DOCUMENT } from "@angular/common";
-
-import { rendererLog, rendererError } from "./trace";
-import {
- PAGE_FACTORY,
- PageFactory,
- defaultPageFactoryProvider,
- setRootPage
-} from "./platform-providers";
-
-import { start, setCssFileName } from "tns-core-modules/application";
-import { topmost, NavigationEntry } from "tns-core-modules/ui/frame";
-import { Page } from "tns-core-modules/ui/page";
-import { TextView } from "tns-core-modules/ui/text-view";
-
-import "nativescript-intl";
+import { Application, TextView, Color, View, Frame, GridLayout, LaunchEventData, ApplicationEventData, profile, profilingUptime, Page, LayoutBase } from '@nativescript/core';
+// import './dom-adapter';
+// import 'nativescript-intl';
+
+import { Type, Injector, CompilerOptions, PlatformRef, NgModuleFactory, NgModuleRef, EventEmitter, Sanitizer, InjectionToken, StaticProvider } from '@angular/core';
+import { DOCUMENT } from '@angular/common';
+
+import { NativeScriptDebug } from './trace';
+import { defaultPageFactoryProvider, setRootPage, PageFactory, PAGE_FACTORY, getRootPage } from './platform-providers';
+import { AppHostView, AppHostAsyncView } from './app-host-view';
export const onBeforeLivesync = new EventEmitter>();
-export const onAfterLivesync = new EventEmitter>();
+export const onAfterLivesync = new EventEmitter<{
+ moduleRef?: NgModuleRef;
+ error?: Error;
+}>();
let lastBootstrappedModule: WeakRef>;
type BootstrapperAction = () => Promise>;
// Work around a TS bug requiring an import of OpaqueToken without using it
-if ((global).___TS_UNUSED) {
- (() => {
- return InjectionToken;
- })();
+// if ((global).___TS_UNUSED) {
+// (() => {
+// return InjectionToken;
+// })();
+// }
+
+// tslint:disable:max-line-length
+/**
+ * Options to be passed when HMR is enabled
+ */
+export interface HmrOptions {
+ /**
+ * A factory function that returns either Module type or NgModuleFactory type.
+ * This needs to be a factory function as the types will change when modules are replaced.
+ */
+ moduleTypeFactory?: () => Type | NgModuleFactory;
+
+ /**
+ * A livesync callback that will be called instead of the original livesync.
+ * It gives the HMR a hook to apply the module replacement.
+ * @param bootstrapPlatform - A bootstrap callback to be called after HMR is done. It will bootstrap a new angular app within the exisiting platform, using the moduleTypeFactory to get the Module or NgModuleFactory to be used.
+ */
+ livesyncCallback: (bootstrapPlatform: () => void) => void;
+}
+// tslint:enable:max-line-length
+
+export interface AppLaunchView extends LayoutBase {
+ // called when the animation is to begin
+ startAnimation?: () => void;
+ // called when bootstrap is complete and cleanup can begin
+ // should resolve when animation is completely finished
+ cleanup?: () => Promise;
}
+
export interface AppOptions {
- bootInExistingPage?: boolean;
- cssFile?: string;
- startPageActionBarHidden?: boolean;
+ bootInExistingPage?: boolean;
+ cssFile?: string;
+ startPageActionBarHidden?: boolean;
+ hmrOptions?: HmrOptions;
+ /**
+ * Background color of the root view
+ */
+ backgroundColor?: string;
+ /**
+ * Use animated launch view (async by default)
+ */
+ launchView?: AppLaunchView;
+ /**
+ * When using Async APP_INITIALIZER, set this to `true`.
+ * (Not needed when using launchView)
+ */
+ async?: boolean;
}
export type PlatformFactory = (extraProviders?: StaticProvider[]) => PlatformRef;
export class NativeScriptSanitizer extends Sanitizer {
- sanitize(_context: any, value: string): string {
- return value;
- }
+ sanitize(_context: any, value: string): string {
+ return value;
+ }
}
-// Add a fake polyfill for the document object
-(global).document = (global).document || {};
-const doc = (global).document;
-doc.body = Object.assign((doc.body || {}), {
- isOverride: true,
-});
-
export class NativeScriptDocument {
- createElement(tag: string) {
- throw new Error("NativeScriptDocument is not DOM Document. There is no createElement() method.");
- }
+ // Required by the AnimationDriver
+ public body: any = {
+ isOverride: true,
+ };
+
+ createElement(tag: string) {
+ throw new Error('NativeScriptDocument is not DOM Document. There is no createElement() method.');
+ }
}
-export const COMMON_PROVIDERS = [
- defaultPageFactoryProvider,
- { provide: Sanitizer, useClass: NativeScriptSanitizer, deps: [] },
- { provide: DOCUMENT, useValue: doc },
-];
+export const COMMON_PROVIDERS = [defaultPageFactoryProvider, { provide: Sanitizer, useClass: NativeScriptSanitizer, deps: [] }, { provide: DOCUMENT, useClass: NativeScriptDocument, deps: [] }];
export class NativeScriptPlatformRef extends PlatformRef {
- private _bootstrapper: BootstrapperAction;
-
- constructor(private platform: PlatformRef, private appOptions?: AppOptions) {
- super();
- }
-
- @profile
- bootstrapModuleFactory(moduleFactory: NgModuleFactory): Promise> {
- this._bootstrapper = () => this.platform.bootstrapModuleFactory(moduleFactory);
-
- this.bootstrapApp();
-
- return null; // Make the compiler happy
- }
-
- @profile
- bootstrapModule(
- moduleType: Type,
- compilerOptions: CompilerOptions | CompilerOptions[] = []
- ): Promise> {
- this._bootstrapper = () => this.platform.bootstrapModule(moduleType, compilerOptions);
-
- this.bootstrapApp();
-
- return null; // Make the compiler happy
- }
-
- @profile
- private bootstrapApp() {
- (global).__onLiveSyncCore = () => this.livesyncModule();
-
- const mainPageEntry = this.createNavigationEntry(this._bootstrapper);
-
- if (this.appOptions && typeof this.appOptions.cssFile === "string") {
- // TODO: All exported fields in ES6 modules should be read-only
- // Change the case when tns-core-modules become ES6 compatible and there is a legal way to set cssFile
- setCssFileName(this.appOptions.cssFile);
- }
- start(mainPageEntry);
- }
-
- livesyncModule(): void {
- rendererLog("ANGULAR LiveSync Started");
-
- onBeforeLivesync.next(lastBootstrappedModule ? lastBootstrappedModule.get() : null);
-
- const mainPageEntry = this.createNavigationEntry(
- this._bootstrapper,
- compRef => onAfterLivesync.next(compRef),
- error => onAfterLivesync.error(error),
- true
- );
- mainPageEntry.animated = false;
- mainPageEntry.clearHistory = true;
-
- const frame = topmost();
- if (frame) {
- if (frame.currentPage && frame.currentPage.modal) {
- frame.currentPage.modal.closeModal();
- }
- frame.navigate(mainPageEntry);
- }
- }
-
- onDestroy(callback: () => void): void {
- this.platform.onDestroy(callback);
- }
-
- get injector(): Injector {
- return this.platform.injector;
- }
-
- destroy(): void {
- this.platform.destroy();
- }
-
- get destroyed(): boolean {
- return this.platform.destroyed;
- }
-
- @profile
- private createNavigationEntry(
- bootstrapAction: BootstrapperAction,
- resolve?: (comp: NgModuleRef) => void,
- reject?: (e: Error) => void,
- isLivesync: boolean = false,
- isReboot: boolean = false): NavigationEntry {
-
- const pageFactory: PageFactory = this.platform.injector.get(PAGE_FACTORY);
-
- const navEntry: NavigationEntry = {
- create: (): Page => {
- const page = pageFactory({ isBootstrap: true, isLivesync });
- setRootPage(page);
- if (this.appOptions) {
- page.actionBarHidden = this.appOptions.startPageActionBarHidden;
- }
-
- const initHandlerMethodName =
- "nativescript-angular/platform-common.initHandler";
- const initHandler = profile(initHandlerMethodName, () => {
- page.off(Page.navigatingToEvent, initHandler);
- // profiling.stop("application-start");
- rendererLog("Page loaded");
-
- // profiling.start("ng-bootstrap");
- rendererLog("BOOTSTRAPPING...");
-
- const bootstrapMethodName =
- "nativescript-angular/platform-common.postBootstrapAction";
- bootstrapAction().then(profile(bootstrapMethodName, moduleRef => {
- // profiling.stop("ng-bootstrap");
- log(`ANGULAR BOOTSTRAP DONE. ${uptime()}`);
- lastBootstrappedModule = new WeakRef(moduleRef);
-
- if (resolve) {
- resolve(moduleRef);
- }
- return moduleRef;
- }), err => {
- rendererError("ERROR BOOTSTRAPPING ANGULAR");
- const errorMessage = err.message + "\n\n" + err.stack;
- rendererError(errorMessage);
-
- let view = new TextView();
- view.text = errorMessage;
- page.content = view;
-
- if (reject) {
- reject(err);
- }
- });
-
- (global).Zone.drainMicroTaskQueue();
- });
-
- page.on(Page.navigatingToEvent, initHandler);
-
- return page;
- },
- animated: false
- };
-
- if (isReboot) {
- navEntry.clearHistory = true;
- }
-
- return navEntry;
- }
-
- liveSyncApp() {
- }
+ private _bootstrapper: BootstrapperAction;
+
+ constructor(private platform: PlatformRef, private appOptions: AppOptions = {}) {
+ super();
+ }
+
+ @profile
+ bootstrapModuleFactory(moduleFactory: NgModuleFactory): Promise> {
+ this._bootstrapper = () => {
+ let bootstrapFactory = moduleFactory;
+ if (this.appOptions.hmrOptions) {
+ bootstrapFactory = >this.appOptions.hmrOptions.moduleTypeFactory();
+ }
+
+ return this.platform.bootstrapModuleFactory(bootstrapFactory);
+ };
+
+ this.bootstrapApp();
+
+ return null; // Make the compiler happy
+ }
+
+ @profile
+ bootstrapModule(moduleType: Type, compilerOptions: CompilerOptions | CompilerOptions[] = []): Promise> {
+ this._bootstrapper = () => {
+ let bootstrapType = moduleType;
+ if (this.appOptions.hmrOptions) {
+ bootstrapType = >this.appOptions.hmrOptions.moduleTypeFactory();
+ }
+
+ return this.platform.bootstrapModule(bootstrapType, compilerOptions);
+ };
+ this.bootstrapApp();
+
+ return null; // Make the compiler happy
+ }
+
+ @profile
+ private bootstrapApp() {
+ (global).__onLiveSyncCore = () => {
+ if (this.appOptions.hmrOptions) {
+ const rootView = Application.getRootView();
+ if (rootView) {
+ rootView._closeAllModalViewsInternal();
+ }
+
+ this.appOptions.hmrOptions.livesyncCallback(() => this._livesync());
+ } else {
+ this._livesync();
+ }
+ };
+
+ if (this.appOptions && typeof this.appOptions.cssFile === 'string') {
+ Application.setCssFileName(this.appOptions.cssFile);
+ }
+
+ this.bootstrapNativeScriptApp();
+ }
+
+ onDestroy(callback: () => void): void {
+ this.platform.onDestroy(callback);
+ }
+
+ get injector(): Injector {
+ return this.platform.injector;
+ }
+
+ destroy(): void {
+ this.platform.destroy();
+ }
+
+ get destroyed(): boolean {
+ return this.platform.destroyed;
+ }
+
+ @profile
+ private bootstrapNativeScriptApp() {
+ let rootContent: View;
+
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLog('NativeScriptPlatform bootstrap started.');
+ }
+ const launchCallback = profile('@nativescript/angular/platform-common.launchCallback', (args: LaunchEventData) => {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLog('Application launch event fired');
+ }
+
+ // Create a temp page for root of the renderer
+ let tempAppHostView: AppHostView;
+ let tempAppHostAsyncView: AppHostAsyncView;
+ if (this.appOptions && (this.appOptions.async || this.appOptions.launchView)) {
+ tempAppHostAsyncView = new AppHostAsyncView(new Color(this.appOptions && this.appOptions.backgroundColor ? this.appOptions.backgroundColor : '#fff'));
+ if (this.appOptions.launchView) {
+ this.appOptions.launchView.style.zIndex = 1000;
+ tempAppHostAsyncView.addChild(this.appOptions.launchView);
+ }
+ rootContent = tempAppHostAsyncView.ngAppRoot;
+ setRootPage(tempAppHostAsyncView);
+ } else {
+ tempAppHostView = new AppHostView(new Color(this.appOptions && this.appOptions.backgroundColor ? this.appOptions.backgroundColor : '#fff'));
+ setRootPage(tempAppHostView);
+ }
+
+ let bootstrapPromiseCompleted = false;
+ const bootstrap = () => {
+ this._bootstrapper().then(
+ (moduleRef) => {
+ bootstrapPromiseCompleted = true;
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLog(`Angular bootstrap bootstrap done. uptime: ${profilingUptime()}`);
+ }
+
+ if (this.appOptions.launchView && this.appOptions.launchView.cleanup) {
+ this.appOptions.launchView.cleanup().then(() => {
+ // cleanup any custom launch views
+ tempAppHostAsyncView.removeChild(this.appOptions.launchView);
+ this.appOptions.launchView = null;
+ });
+ } else if (tempAppHostView) {
+ rootContent = tempAppHostView.content;
+ }
+
+ lastBootstrappedModule = new WeakRef(moduleRef);
+ },
+ (err) => {
+ bootstrapPromiseCompleted = true;
+ const errorMessage = err.message + '\n\n' + err.stack;
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLogError('ERROR BOOTSTRAPPING ANGULAR');
+ }
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLogError(errorMessage);
+ }
+
+ rootContent = this.createErrorUI(errorMessage);
+ }
+ );
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLog('bootstrapAction called, draining micro tasks queue. Root: ' + rootContent);
+ }
+ (global).Zone.drainMicroTaskQueue();
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLog('bootstrapAction called, draining micro tasks queue finished! Root: ' + rootContent);
+ }
+ };
+
+ if (this.appOptions && this.appOptions.launchView && this.appOptions.launchView.startAnimation) {
+ // start animations on next tick (after initial boot)
+ setTimeout(() => {
+ // ensure launch animation is executed after launchView added to view stack
+ this.appOptions.launchView.startAnimation();
+ });
+ }
+ bootstrap();
+ // if (!bootstrapPromiseCompleted) {
+ // const errorMessage = "Bootstrap promise didn't resolve";
+ // if (NativeScriptDebug.isLogEnabled()) {
+ // NativeScriptDebug.bootstrapLogError(errorMessage);
+ // }
+ // rootContent = this.createErrorUI(errorMessage);
+ // }
+ args.root = rootContent;
+ });
+ const exitCallback = profile('@nativescript/angular/platform-common.exitCallback', (args: ApplicationEventData) => {
+ const androidActivity = args.android;
+ if (androidActivity && !androidActivity.isFinishing()) {
+ // Exit event was triggered as a part of a restart of the app.
+ return;
+ }
+
+ const lastModuleRef = lastBootstrappedModule ? lastBootstrappedModule.get() : null;
+ if (lastModuleRef) {
+ // Make sure the module is only destroyed once
+ lastBootstrappedModule = null;
+
+ lastModuleRef.destroy();
+ }
+
+ rootContent = null;
+ });
+
+ Application.on(Application.launchEvent, launchCallback);
+ Application.on(Application.exitEvent, exitCallback);
+
+ Application.run();
+ }
+
+ @profile
+ public _livesync() {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLog('Angular livesync started.');
+ }
+
+ const lastModuleRef = lastBootstrappedModule ? lastBootstrappedModule.get() : null;
+ onBeforeLivesync.next(lastModuleRef);
+ if (lastModuleRef) {
+ lastModuleRef.destroy();
+ }
+
+ this._bootstrapper().then(
+ (moduleRef) => {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLog('Angular livesync done.');
+ }
+ onAfterLivesync.next({ moduleRef });
+
+ lastBootstrappedModule = new WeakRef(moduleRef);
+ Application.resetRootView({
+ create: () => (getRootPage() instanceof AppHostView ? ((getRootPage()) as AppHostView).ngAppRoot : getRootPage()),
+ });
+ },
+ (error) => {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLogError('ERROR LIVESYNC BOOTSTRAPPING ANGULAR');
+ }
+ const errorMessage = error.message + '\n\n' + error.stack;
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLogError(errorMessage);
+ }
+
+ Application.resetRootView({
+ create: () => this.createErrorUI(errorMessage),
+ });
+ onAfterLivesync.next({ error });
+ }
+ );
+ }
+
+ private createErrorUI(message: string): View {
+ const errorTextBox = new TextView();
+ errorTextBox.text = message;
+ errorTextBox.color = new Color('red');
+ return errorTextBox;
+ }
+
+ private createFrameAndPage(isLivesync: boolean) {
+ const frame = new Frame();
+ const pageFactory: PageFactory = this.platform.injector.get(PAGE_FACTORY);
+ const page = pageFactory({ isBootstrap: true, isLivesync });
+
+ frame.navigate({
+ create: () => {
+ return page;
+ },
+ });
+ return { page, frame };
+ }
}
diff --git a/nativescript-angular/platform-providers.ts b/nativescript-angular/platform-providers.ts
index 86d47e0a2..a7ad87f2b 100644
--- a/nativescript-angular/platform-providers.ts
+++ b/nativescript-angular/platform-providers.ts
@@ -1,55 +1,64 @@
-import { InjectionToken } from "@angular/core";
+import { InjectionToken, Injectable, OnDestroy } from '@angular/core';
-import { topmost, Frame } from "tns-core-modules/ui/frame";
-import { View } from "tns-core-modules/ui/core/view";
-import { Page } from "tns-core-modules/ui/page";
-import { device, Device } from "tns-core-modules/platform";
+import { Frame, View, Page, IDevice, Device } from '@nativescript/core';
-export const APP_ROOT_VIEW = new InjectionToken("App Root View");
-export const DEVICE = new InjectionToken("platform device");
-export const PAGE_FACTORY = new InjectionToken("page factory");
+export const APP_ROOT_VIEW = new InjectionToken('NativeScriptAppRootView');
+export const DeviceToken = new InjectionToken('NativeScriptPlatformDeviceToken');
+
+export type PageFactory = (options: PageFactoryOptions) => Page;
+export interface PageFactoryOptions {
+ isBootstrap?: boolean;
+ isLivesync?: boolean;
+ isModal?: boolean;
+ isNavigation?: boolean;
+ componentType?: any;
+}
+export const PAGE_FACTORY = new InjectionToken('NativeScriptPageFactory');
+export const defaultPageFactory: PageFactory = function (_opts: PageFactoryOptions) {
+ return new Page();
+};
+export const defaultPageFactoryProvider = { provide: PAGE_FACTORY, useValue: defaultPageFactory };
let _rootPageRef: WeakRef;
export function setRootPage(page: Page): void {
- _rootPageRef = new WeakRef(page);
+ _rootPageRef = new WeakRef(page);
}
export function getRootPage(): Page {
- return _rootPageRef && _rootPageRef.get();
+ return _rootPageRef && _rootPageRef.get();
}
// Use an exported function to make the AoT compiler happy.
export function getDefaultPage(): Page {
- const frame = topmost();
- return getRootPage() || (frame && frame.currentPage);
+ const rootPage = getRootPage();
+ if (rootPage instanceof Page) {
+ return rootPage;
+ }
+ // if (rootPage) {
+ // return rootPage;
+ // }
+
+ const frame = Frame.topmost();
+ if (frame && frame.currentPage) {
+ return frame.currentPage;
+ }
+
+ return null;
}
export const defaultPageProvider = { provide: Page, useFactory: getDefaultPage };
// Use an exported function to make the AoT compiler happy.
export function getDefaultFrame(): Frame {
- return topmost();
+ return Frame.topmost();
}
export const defaultFrameProvider = { provide: Frame, useFactory: getDefaultFrame };
// Use an exported function to make the AoT compiler happy.
-export function getDefaultDevice(): Device {
- return device;
+export function getDefaultDevice(): IDevice {
+ return Device;
}
-export const defaultDeviceProvider = { provide: DEVICE, useFactory: getDefaultDevice };
-
-export type PageFactory = (options: PageFactoryOptions) => Page;
-export interface PageFactoryOptions {
- isBootstrap?: boolean;
- isLivesync?: boolean;
- isModal?: boolean;
- isNavigation?: boolean;
- componentType?: any;
-}
-export const defaultPageFactory: PageFactory = function (_opts: PageFactoryOptions) {
- return new Page();
-};
-export const defaultPageFactoryProvider = { provide: PAGE_FACTORY, useValue: defaultPageFactory };
+export const defaultDeviceProvider = { provide: DeviceToken, useFactory: getDefaultDevice };
diff --git a/nativescript-angular/platform-static.ts b/nativescript-angular/platform-static.ts
deleted file mode 100644
index ed85f38e6..000000000
--- a/nativescript-angular/platform-static.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-// Always import platform-common first - because polyfills
-import {
- NativeScriptPlatformRef,
- AppOptions,
- COMMON_PROVIDERS,
- PlatformFactory
-} from "./platform-common";
-
-import { platformCore, PlatformRef, createPlatformFactory } from "@angular/core";
-
-// "Static" platform
-const _platformNativeScript: PlatformFactory = createPlatformFactory(
- platformCore, "nativeScript", [...COMMON_PROVIDERS]);
-
-export function platformNativeScript(options?: AppOptions, extraProviders?: any[]): PlatformRef {
- // Return raw platform to advanced users only if explicitly requested
- if (options && options.bootInExistingPage === true) {
- return _platformNativeScript(extraProviders);
- } else {
- return new NativeScriptPlatformRef(_platformNativeScript(extraProviders), options);
- }
-}
diff --git a/nativescript-angular/platform.ts b/nativescript-angular/platform.ts
index eb9666917..12e83ffbb 100644
--- a/nativescript-angular/platform.ts
+++ b/nativescript-angular/platform.ts
@@ -1,78 +1,15 @@
-// Always import platform-common first - because polyfills
-import {
- NativeScriptPlatformRef,
- AppOptions,
- PlatformFactory,
- COMMON_PROVIDERS
-} from "./platform-common";
+import { PlatformRef, platformCore, createPlatformFactory } from '@angular/core';
-import { NSFileSystem } from "./file-system/ns-file-system";
+import { NativeScriptPlatformRef, AppOptions, PlatformFactory, COMMON_PROVIDERS } from './platform-common';
-import {
- ElementSchemaRegistry,
- ResourceLoader,
-} from "@angular/compiler";
+// "Static" platform
+const _platformNativeScript: PlatformFactory = createPlatformFactory(platformCore, 'nativeScript', [...COMMON_PROVIDERS]);
-import {
- ɵplatformCoreDynamic as platformCoreDynamic
-} from "@angular/platform-browser-dynamic";
-
-import {
- ɵINTERNAL_BROWSER_PLATFORM_PROVIDERS as INTERNAL_BROWSER_PLATFORM_PROVIDERS
-} from "@angular/platform-browser";
-
-import {
- COMPILER_OPTIONS,
- PlatformRef,
- InjectionToken,
- ViewEncapsulation,
- createPlatformFactory,
- MissingTranslationStrategy,
- StaticProvider,
-} from "@angular/core";
-
-// Work around a TS bug requiring an imports of
-// InjectionToken, ViewEncapsulation and MissingTranslationStrategy
-// without using them
-if ((global).___TS_UNUSED) {
- (() => InjectionToken)();
- (() => ViewEncapsulation)();
- (() => MissingTranslationStrategy)();
-}
-
-// Register DOM adapter, if possible. Dynamic platform only!
-import "./dom-adapter";
-
-import { NativeScriptElementSchemaRegistry } from "./schema-registry";
-import { FileSystemResourceLoader } from "./resource-loader";
-
-export const NS_COMPILER_PROVIDERS: StaticProvider[] = [
- INTERNAL_BROWSER_PLATFORM_PROVIDERS,
- {
- provide: COMPILER_OPTIONS,
- useValue: {
- providers: [
- { provide: NSFileSystem, deps: []},
- { provide: ResourceLoader, useClass: FileSystemResourceLoader, deps: [NSFileSystem] },
- { provide: ElementSchemaRegistry, useClass: NativeScriptElementSchemaRegistry, deps: [] },
- ]
- },
- multi: true
- },
-];
-
-// Dynamic platform
-const _platformNativeScriptDynamic: PlatformFactory = createPlatformFactory(
- platformCoreDynamic, "nativeScriptDynamic", [...COMMON_PROVIDERS, ...NS_COMPILER_PROVIDERS]);
-
-export function platformNativeScriptDynamic(
- options?: AppOptions,
- extraProviders?: any[]
-): PlatformRef {
- // Return raw platform to advanced users only if explicitly requested
- if (options && options.bootInExistingPage === true) {
- return _platformNativeScriptDynamic(extraProviders);
- } else {
- return new NativeScriptPlatformRef(_platformNativeScriptDynamic(extraProviders), options);
- }
+export function platformNativeScriptDynamic(options?: AppOptions, extraProviders?: any[]): PlatformRef {
+ // Return raw platform to advanced users only if explicitly requested
+ if (options && options.bootInExistingPage === true) {
+ return _platformNativeScript(extraProviders);
+ } else {
+ return new NativeScriptPlatformRef(_platformNativeScript(extraProviders), options);
+ }
}
diff --git a/nativescript-angular/polyfills/array.ts b/nativescript-angular/polyfills/array.ts
index a08882398..43e3e0b68 100644
--- a/nativescript-angular/polyfills/array.ts
+++ b/nativescript-angular/polyfills/array.ts
@@ -1,50 +1,43 @@
-if (!(