diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..00c0cf4037
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,226 @@
+version: 2
+updates:
+ - package-ecosystem: github-actions
+ directory: /
+ schedule:
+ interval: monthly
+
+ - package-ecosystem: npm
+ directory: /apps/automated
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /apps/automated/src/pages
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /apps/automated/src/ui/lifecycle
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /apps/automated/src/ui/root-view/mymodule
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /apps/automated/src/xml-declaration/mymodulewithxml
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /apps/automated/src/xml-declaration
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /apps/toolbox
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /apps/ui
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /packages/core/css-value
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /packages/core/css
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /packages/core/js-libs/easysax
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /packages/core
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /packages/devtools
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /packages/types-android
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /packages/types-ios
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /packages/types-minimal
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /packages/types
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /packages/ui-mobile-base
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /packages/webpack5
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /packages/winter-tc
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: npm
+ directory: /tools/workspace-plugin
+ schedule:
+ interval: monthly
+ time: "23:00"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
\ No newline at end of file
diff --git a/.github/workflows/apps_automated_android.yml b/.github/workflows/apps_automated_android.yml
index 23aaa687f5..d8483b4448 100644
--- a/.github/workflows/apps_automated_android.yml
+++ b/.github/workflows/apps_automated_android.yml
@@ -1,4 +1,7 @@
name: 'apps/automated/android'
+permissions:
+ contents: read
+ pull-requests: write
on:
push:
@@ -8,6 +11,9 @@ on:
pull_request:
workflow_dispatch:
+env:
+ NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
+
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
@@ -17,26 +23,32 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
+
- - uses: actions/setup-node@v4
+ - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
- node-version: 20.10.0
+ node-version: 23.5.0
- - uses: actions/setup-java@v4
+ - name: Derive appropriate SHAs for base and head for `nx affected` commands
+ uses: nrwl/nx-set-shas@826660b82addbef3abff5fa871492ebad618c9e1 # v4.3.3
+ with:
+ main-branch-name: 'main'
+
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin'
- java-version: '17'
+ java-version: '21'
- name: Install Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: '3'
- name: Install NativeScript
run: |
python3 -m pip install --upgrade pip six
- npm i -g nativescript --ignore-scripts --legacy-peer-deps
+ npm i -g nativescript --ignore-scripts
ns usage-reporting disable
ns error-reporting disable
@@ -53,8 +65,8 @@ jobs:
sudo udevadm trigger --name-match=kvm
- name: Run tests on Android Emulator
- uses: reactivecircus/android-emulator-runner@v2
+ uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b # v2.35.0
with:
- api-level: 34
+ api-level: 35
arch: x86_64
- script: node tools/scripts/run-automated.js android
+ script: npx nx test apps-automated -c=android
diff --git a/.github/workflows/apps_automated_ios.yml b/.github/workflows/apps_automated_ios.yml
index 5b3650a53c..7fcce34749 100644
--- a/.github/workflows/apps_automated_ios.yml
+++ b/.github/workflows/apps_automated_ios.yml
@@ -1,4 +1,7 @@
name: 'apps/automated/ios'
+permissions:
+ contents: read
+ pull-requests: write
on:
push:
@@ -8,33 +11,39 @@ on:
pull_request:
workflow_dispatch:
+env:
+ NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
+
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test-ios:
- runs-on: macos-latest
+ # runs-on: macos-latest
+ runs-on: warp-macos-15-arm64-6x
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- - uses: actions/setup-node@v4
+ # - name: ActionDebugger By Warpbuild
+ # uses: Warpbuilds/action-debugger@v1.3
+
+ - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
- node-version: 20.10.0
+ node-version: 23.5.0
- - name: Install Python
- uses: actions/setup-python@v4
+ - name: Derive appropriate SHAs for base and head for `nx affected` commands
+ uses: nrwl/nx-set-shas@826660b82addbef3abff5fa871492ebad618c9e1 # v4.3.3
with:
- python-version: '3'
+ main-branch-name: 'main'
- name: Install NativeScript
run: |
- python3 -m pip install --upgrade pip six
- npm i -g nativescript --ignore-scripts --legacy-peer-deps
+ npm i -g nativescript --ignore-scripts
ns usage-reporting disable
ns error-reporting disable
- ns doctor
+ # ns doctor
- name: Setup
run: npm run setup
@@ -43,7 +52,9 @@ jobs:
run: npx nx run-many --target=test --configuration=ci --projects=core
- name: Start iOS Simulator
- uses: futureware-tech/simulator-action@v3
+ uses: futureware-tech/simulator-action@dab10d813144ef59b48d401cd95da151222ef8cd # v4
+ with:
+ model: 'iPhone 16 Pro'
- name: Run tests on iOS Simulator
- run: node tools/scripts/run-automated.js ios
+ run: npx nx test apps-automated -c=ios
diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
new file mode 100644
index 0000000000..d69dfb2474
--- /dev/null
+++ b/.github/workflows/dependency-review.yml
@@ -0,0 +1,22 @@
+# Dependency Review Action
+#
+# This Action will scan dependency manifest files that change as part of a Pull Request,
+# surfacing known-vulnerable versions of the packages declared or updated in the PR.
+# Once installed, if the workflow run is marked as required,
+# PRs introducing known-vulnerable packages will be blocked from merging.
+#
+# Source repository: https://github.com/actions/dependency-review-action
+name: 'Dependency Review'
+on: [pull_request]
+
+permissions:
+ contents: read
+
+jobs:
+ dependency-review:
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Checkout Repository'
+ uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
+ - name: 'Dependency Review'
+ uses: actions/dependency-review-action@05fe4576374b728f0c523d6a13d64c25081e0803 # v4.8.3
\ No newline at end of file
diff --git a/.github/workflows/npm_release_core.yml b/.github/workflows/npm_release_core.yml
deleted file mode 100644
index 67e6359f82..0000000000
--- a/.github/workflows/npm_release_core.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: '@nativescript/core -> npm'
-
-on:
- push:
- branches: [ 'main' ]
- paths:
- - 'packages/core/**'
- workflow_dispatch:
-
-env:
- NPM_TAG: 'next'
-
-jobs:
- release:
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v2
-
- - name: Setup
- run: npm run setup
-
- - name: Generate Version
- working-directory: packages/core
- run: |
- echo NPM_VERSION=$(node -e "console.log(require('./package.json').version);")-$NPM_TAG-$(date +"%m-%d-%Y")-$GITHUB_RUN_ID >> $GITHUB_ENV
-
- - name: Bump Version
- working-directory: packages/core
- run: npm version $NPM_VERSION
-
- # TODO: build ui-mobile-base first
- - name: Build @nativescript/core
- run: npx nx run core:build
-
- - name: Publish @nativescript/core
- working-directory: dist/packages
- env:
- NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
- run: |
- echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ../../.npmrc
- echo "Publishing @nativescript/core@$NPM_VERSION to NPM with tag $NPM_TAG..."
- npm publish nativescript-core-$NPM_VERSION.tgz --tag $NPM_TAG
diff --git a/.github/workflows/npm_release_tns_core.yml b/.github/workflows/npm_release_tns_core.yml
index 5b3849e1b6..b0c6ab3bbe 100644
--- a/.github/workflows/npm_release_tns_core.yml
+++ b/.github/workflows/npm_release_tns_core.yml
@@ -1,4 +1,7 @@
name: 'tns-core-modules -> npm'
+permissions:
+ contents: read
+ pull-requests: write
on:
push:
@@ -15,10 +18,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - name: Harden the runner (Audit all outbound calls)
+ uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
+ with:
+ egress-policy: audit
+
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Setup
- run: npm install --legacy-peer-deps
+ run: npm install
- name: Generate Version
run: |
diff --git a/.github/workflows/npm_release_types.yml b/.github/workflows/npm_release_types.yml
index 440df76ad5..d99b1ad1cf 100644
--- a/.github/workflows/npm_release_types.yml
+++ b/.github/workflows/npm_release_types.yml
@@ -1,5 +1,8 @@
# TODO: modify to build android & ios types first and then merge into types
name: '@nativescript/types -> npm'
+permissions:
+ contents: read
+ pull-requests: write
on:
push:
@@ -15,10 +18,15 @@ jobs:
runs-on: ubuntu-latest
steps:
+ - name: Harden the runner (Audit all outbound calls)
+ uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
+ with:
+ egress-policy: audit
+
- name: Todo
run: |
echo "TODO: implement action"
-# - uses: actions/checkout@v2
+# - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0
#
# - name: Setup
# run: npm install
diff --git a/.github/workflows/npm_release_webpack.yml b/.github/workflows/npm_release_webpack.yml
index 700aed5222..3df7b98875 100644
--- a/.github/workflows/npm_release_webpack.yml
+++ b/.github/workflows/npm_release_webpack.yml
@@ -1,4 +1,7 @@
name: '@nativescript/webpack -> npm'
+permissions:
+ contents: read
+ pull-requests: write
on:
push:
@@ -14,10 +17,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - name: Harden the runner (Audit all outbound calls)
+ uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
+ with:
+ egress-policy: audit
+
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Setup
- run: npm install --legacy-peer-deps
+ run: npm install
- name: Generate Version
working-directory: packages/webpack
@@ -32,10 +40,10 @@ jobs:
run: npx nx run webpack:build
- name: Publish @nativescript/webpack
- working-directory: dist/packages
+ working-directory: dist/packages/webpack5
env:
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
run: |
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ../../.npmrc
echo "Publishing @nativescript/webpack@$NPM_VERSION to NPM with tag $NPM_TAG..."
- npm publish nativescript-webpack.tgz --tag $NPM_TAG --dry-run
+ npm publish --tag $NPM_TAG --dry-run
diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml
new file mode 100644
index 0000000000..a4a2873931
--- /dev/null
+++ b/.github/workflows/ossf-scorecard.yml
@@ -0,0 +1,78 @@
+# This workflow uses actions that are not certified by GitHub. They are provided
+# by a third-party and are governed by separate terms of service, privacy
+# policy, and support documentation.
+
+name: Scorecard supply-chain security
+on:
+ # For Branch-Protection check. Only the default branch is supported. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
+ branch_protection_rule:
+ # To guarantee Maintained check is occasionally updated. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
+ schedule:
+ - cron: '23 13 * * 3'
+ push:
+ branches: [ "main" ]
+
+# Declare default permissions as read only.
+permissions: read-all
+
+jobs:
+ analysis:
+ name: Scorecard analysis
+ runs-on: ubuntu-latest
+ # `publish_results: true` only works when run from the default branch. conditional can be removed if disabled.
+ if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request'
+ permissions:
+ # Needed to upload the results to code-scanning dashboard.
+ security-events: write
+ # Needed to publish results and get a badge (see publish_results below).
+ id-token: write
+ # Uncomment the permissions below if installing in a private repository.
+ # contents: read
+ # actions: read
+
+ steps:
+ - name: "Checkout code"
+ uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
+ with:
+ persist-credentials: false
+
+ - name: "Run analysis"
+ uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
+ with:
+ results_file: results.sarif
+ results_format: sarif
+ # (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
+ # - you want to enable the Branch-Protection check on a *public* repository, or
+ # - you are installing Scorecard on a *private* repository
+ # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
+ # repo_token: ${{ secrets.SCORECARD_TOKEN }}
+
+ # Public repositories:
+ # - Publish results to OpenSSF REST API for easy access by consumers
+ # - Allows the repository to include the Scorecard badge.
+ # - See https://github.com/ossf/scorecard-action#publishing-results.
+ # For private repositories:
+ # - `publish_results` will always be set to `false`, regardless
+ # of the value entered here.
+ publish_results: true
+
+ # (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore
+ # file_mode: git
+
+ # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
+ # format to the repository Actions tab.
+ - name: "Upload artifact"
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
+ with:
+ name: SARIF file
+ path: results.sarif
+ retention-days: 5
+
+ # Upload the results to GitHub's code scanning dashboard (optional).
+ # Commenting out will disable upload of results to your repo's Code Scanning dashboard
+ - name: "Upload to code-scanning"
+ uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
+ with:
+ sarif_file: results.sarif
diff --git a/.github/workflows/secure_nx_release.yml b/.github/workflows/secure_nx_release.yml
new file mode 100644
index 0000000000..643e3ff14b
--- /dev/null
+++ b/.github/workflows/secure_nx_release.yml
@@ -0,0 +1,411 @@
+name: Release Workflow
+
+on:
+ push:
+ branches:
+ - main
+ tags:
+ # Matches the Nx releaseTag pattern in nx.json: "{version}-{projectName}"
+ - '*-*'
+ workflow_dispatch:
+ inputs:
+ dist-tag:
+ description: "npm dist-tag to use (e.g. latest | next | canary)"
+ required: false
+ type: string
+ default: next
+ dry-run:
+ description: "Run release steps without making changes (no git push, no publish)"
+ required: false
+ type: boolean
+ default: false
+ release-group:
+ description: "Optional Nx project pattern to scope the release (empty = default behavior)"
+ required: false
+ type: string
+ default: ""
+
+concurrency:
+ # Avoid overlapping publishes on the same ref/branch
+ group: nx-release-${{ github.ref }}
+ cancel-in-progress: false
+
+permissions:
+ contents: write # needed to push version commits and tags
+ id-token: write # required for npm provenance / trusted publishing (OIDC)
+
+jobs:
+ release:
+ name: Version and Publish (gated by environment)
+ if: ${{ github.actor != 'github-actions[bot]' }}
+ runs-on: ubuntu-latest
+ environment:
+ name: ${{ (github.event_name == 'workflow_dispatch' && inputs.dry-run) && 'npm-publish-dry-run' || 'npm-publish' }}
+
+ env:
+ # Optional: provide Nx Cloud token if used in this repo
+ NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
+
+ steps:
+ - name: Harden the runner (Audit all outbound calls)
+ uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
+ with:
+ egress-policy: audit
+
+ - name: Checkout repository (full history for tagging)
+ uses: actions/checkout@v6.0.0
+ with:
+ fetch-depth: 0
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '24'
+ registry-url: 'https://registry.npmjs.org'
+ cache: 'npm'
+
+ - name: Update npm (required for OIDC trusted publishing)
+ run: |
+ npm install -g npm@^11.5.1
+ npm --version
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Repo setup
+ run: npm run setup
+
+ - name: Resolve release context
+ id: ctx
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
+ dist_tag="${{ inputs['dist-tag'] }}"
+ scope="${{ inputs['release-group'] }}"
+ dry_run="${{ inputs['dry-run'] }}"
+ mode="dispatch"
+ elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then
+ dist_tag=""
+ scope=""
+ dry_run="false"
+ mode="tag"
+ else
+ dist_tag="next"
+ scope=""
+ dry_run="false"
+ mode="main"
+ fi
+
+ echo "mode=${mode}" >> "$GITHUB_OUTPUT"
+ echo "dist_tag=${dist_tag}" >> "$GITHUB_OUTPUT"
+ echo "scope=${scope}" >> "$GITHUB_OUTPUT"
+ echo "dry_run=${dry_run}" >> "$GITHUB_OUTPUT"
+
+ - name: Determine affected release projects (main)
+ id: affected
+ if: ${{ steps.ctx.outputs.mode == 'main' }}
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ base='${{ github.event.before }}'
+ head='${{ github.sha }}'
+
+ # Handle edge cases where base commit doesn't exist (first push, force-push, etc.)
+ # Use HEAD~1 as fallback, or just compare against HEAD if no parent exists
+ if [[ "$base" == "0000000000000000000000000000000000000000" ]] || ! git cat-file -e "$base" 2>/dev/null; then
+ echo "Base commit not available, falling back to HEAD~1"
+ base="HEAD~1"
+ # If HEAD~1 doesn't exist (first commit), use empty tree
+ if ! git cat-file -e "$base" 2>/dev/null; then
+ base="$(git hash-object -t tree /dev/null)"
+ fi
+ fi
+
+ # Only consider libs under packages/* and exclude items configured as non-releaseable.
+ affected_json=$(npx nx show projects --affected --base "$base" --head "$head" --type lib --projects "packages/*" --exclude "ui-mobile-base,types-minimal,winter-tc,types,types-ios,types-android" --json)
+ affected_list=$(printf '%s' "$affected_json" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const a=JSON.parse(s||"[]");process.stdout.write(a.join(","));});')
+ affected_count=$(printf '%s' "$affected_json" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const a=JSON.parse(s||"[]");process.stdout.write(String(a.length));});')
+
+ echo "projects=${affected_list}" >> "$GITHUB_OUTPUT"
+ echo "count=${affected_count}" >> "$GITHUB_OUTPUT"
+
+ - name: Determine tag release project and dist-tag (tags)
+ id: taginfo
+ if: ${{ steps.ctx.outputs.mode == 'tag' }}
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ tag_name="${GITHUB_REF_NAME}"
+
+ # Find the project by matching the tag suffix against known releaseable packages.
+ projects=$(npx nx show projects --projects "packages/*" --type lib --exclude "ui-mobile-base,types-minimal,winter-tc,types,types-ios,types-android" --sep ' ')
+
+ best_match=""
+ best_len=0
+ for p in $projects; do
+ suffix="-${p}"
+ if [[ "$tag_name" == *"$suffix" ]]; then
+ if (( ${#p} > best_len )); then
+ best_match="$p"
+ best_len=${#p}
+ fi
+ fi
+ done
+
+ if [[ -z "$best_match" ]]; then
+ echo "Could not determine project from tag '$tag_name'. Expected '{version}-{projectName}'." >&2
+ exit 1
+ fi
+
+ version_part="${tag_name%-$best_match}"
+ if [[ "$version_part" == *-* ]]; then
+ dist_tag="next"
+ else
+ dist_tag="latest"
+ fi
+
+ echo "project=${best_match}" >> "$GITHUB_OUTPUT"
+ echo "version=${version_part}" >> "$GITHUB_OUTPUT"
+ echo "dist_tag=${dist_tag}" >> "$GITHUB_OUTPUT"
+
+ - name: Configure git user for automated commits
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
+
+ # VERSION: updates versions and creates git tags following nx.json releaseTag.pattern.
+ - name: nx release version (main)
+ if: ${{ steps.ctx.outputs.mode == 'main' && steps.affected.outputs.count != '0' }}
+ shell: bash
+ run: |
+ set -euo pipefail
+ npx nx release version prerelease \
+ --preid next \
+ --projects "${{ steps.affected.outputs.projects }}" \
+ --git-commit \
+ --git-push \
+ --verbose
+
+ - name: nx release version (main, no-op)
+ if: ${{ steps.ctx.outputs.mode == 'main' && steps.affected.outputs.count == '0' }}
+ run: echo "No affected release projects on main; skipping version + publish."
+
+ - name: nx release version (dispatch)
+ if: ${{ steps.ctx.outputs.mode == 'dispatch' && !inputs.dry-run }}
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ scope="${{ steps.ctx.outputs.scope }}"
+ if [[ -n "$scope" ]]; then
+ projects_arg=(--projects "$scope")
+ else
+ projects_arg=()
+ fi
+
+ npx nx release version prerelease \
+ --preid "${{ steps.ctx.outputs.dist_tag }}" \
+ "${projects_arg[@]}" \
+ --git-commit \
+ --git-push \
+ --verbose
+
+ - name: nx release version (dispatch, dry-run)
+ if: ${{ steps.ctx.outputs.mode == 'dispatch' && inputs.dry-run }}
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ scope="${{ steps.ctx.outputs.scope }}"
+ if [[ -n "$scope" ]]; then
+ projects_arg=(--projects "$scope")
+ else
+ projects_arg=()
+ fi
+
+ npx nx release version prerelease \
+ --preid "${{ steps.ctx.outputs.dist_tag }}" \
+ "${projects_arg[@]}" \
+ --verbose \
+ --dry-run
+
+ # BUILD: Ensure projects are built before publishing
+ - name: Build affected projects (main)
+ if: ${{ steps.ctx.outputs.mode == 'main' && steps.affected.outputs.count != '0' }}
+ run: npx nx run-many -t build --projects "${{ steps.affected.outputs.projects }}" --verbose
+
+ - name: Build projects (dispatch)
+ if: ${{ steps.ctx.outputs.mode == 'dispatch' }}
+ shell: bash
+ run: |
+ set -euo pipefail
+ scope="${{ steps.ctx.outputs.scope }}"
+ if [[ -n "$scope" ]]; then
+ npx nx run-many -t build --projects "$scope" --verbose
+ else
+ npx nx run-many -t build --all --verbose
+ fi
+
+ # PUBLISH: OIDC trusted publishing (default). Avoid any lingering token auth.
+ - name: nx release publish (OIDC, main)
+ if: ${{ steps.ctx.outputs.mode == 'main' && steps.affected.outputs.count != '0' && vars.USE_NPM_TOKEN != 'true' }}
+ shell: bash
+ env:
+ NPM_CONFIG_PROVENANCE: true
+ NODE_AUTH_TOKEN: ""
+ run: |
+ set -euo pipefail
+ unset NODE_AUTH_TOKEN
+ rm -f ~/.npmrc || true
+ if [[ -n "${NPM_CONFIG_USERCONFIG:-}" ]]; then
+ rm -f "$NPM_CONFIG_USERCONFIG" || true
+ fi
+
+ npx nx release publish \
+ --projects "${{ steps.affected.outputs.projects }}" \
+ --tag "${{ steps.ctx.outputs.dist_tag }}" \
+ --access public \
+ --verbose
+
+ - name: nx release publish (OIDC, dispatch)
+ if: ${{ steps.ctx.outputs.mode == 'dispatch' && steps.ctx.outputs.dry_run != 'true' && vars.USE_NPM_TOKEN != 'true' }}
+ shell: bash
+ env:
+ NPM_CONFIG_PROVENANCE: true
+ NODE_AUTH_TOKEN: ""
+ run: |
+ set -euo pipefail
+ unset NODE_AUTH_TOKEN
+ rm -f ~/.npmrc || true
+ if [[ -n "${NPM_CONFIG_USERCONFIG:-}" ]]; then
+ rm -f "$NPM_CONFIG_USERCONFIG" || true
+ fi
+
+ scope="${{ steps.ctx.outputs.scope }}"
+ if [[ -n "$scope" ]]; then
+ projects_arg="--projects $scope"
+ else
+ projects_arg=""
+ fi
+
+ npx nx release publish \
+ $projects_arg \
+ --tag "${{ steps.ctx.outputs.dist_tag }}" \
+ --access public \
+ --verbose
+
+ - name: nx release publish (OIDC, dispatch dry-run)
+ if: ${{ steps.ctx.outputs.mode == 'dispatch' && inputs.dry-run && vars.USE_NPM_TOKEN != 'true' }}
+ shell: bash
+ env:
+ NPM_CONFIG_PROVENANCE: true
+ NODE_AUTH_TOKEN: ""
+ run: |
+ set -euo pipefail
+ unset NODE_AUTH_TOKEN
+ rm -f ~/.npmrc || true
+ if [[ -n "${NPM_CONFIG_USERCONFIG:-}" ]]; then
+ rm -f "$NPM_CONFIG_USERCONFIG" || true
+ fi
+
+ scope="${{ steps.ctx.outputs.scope }}"
+ if [[ -n "$scope" ]]; then
+ projects_arg="--projects $scope"
+ else
+ projects_arg=""
+ fi
+
+ npx nx release publish \
+ $projects_arg \
+ --tag "${{ steps.ctx.outputs.dist_tag }}" \
+ --access public \
+ --verbose \
+ --dry-run
+
+ # PUBLISH: token fallback (only when explicitly enabled via repo/environment variable USE_NPM_TOKEN=true).
+ - name: nx release publish (token, main)
+ if: ${{ steps.ctx.outputs.mode == 'main' && steps.affected.outputs.count != '0' && vars.USE_NPM_TOKEN == 'true' }}
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
+ NPM_CONFIG_PROVENANCE: true
+ run: |
+ npx nx release publish --projects "${{ steps.affected.outputs.projects }}" --tag "${{ steps.ctx.outputs.dist_tag }}" --access public --verbose
+
+ - name: nx release publish (token, dispatch)
+ if: ${{ steps.ctx.outputs.mode == 'dispatch' && steps.ctx.outputs.dry_run != 'true' && vars.USE_NPM_TOKEN == 'true' }}
+ shell: bash
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
+ NPM_CONFIG_PROVENANCE: true
+ run: |
+ set -euo pipefail
+ scope="${{ steps.ctx.outputs.scope }}"
+ if [[ -n "$scope" ]]; then
+ projects_arg="--projects $scope"
+ else
+ projects_arg=""
+ fi
+ npx nx release publish $projects_arg --tag "${{ steps.ctx.outputs.dist_tag }}" --access public --verbose
+
+ - name: nx release publish (token, dispatch dry-run)
+ if: ${{ steps.ctx.outputs.mode == 'dispatch' && inputs.dry-run && vars.USE_NPM_TOKEN == 'true' }}
+ shell: bash
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
+ NPM_CONFIG_PROVENANCE: true
+ run: |
+ set -euo pipefail
+ scope="${{ steps.ctx.outputs.scope }}"
+ if [[ -n "$scope" ]]; then
+ projects_arg="--projects $scope"
+ else
+ projects_arg=""
+ fi
+ npx nx release publish $projects_arg --tag "${{ steps.ctx.outputs.dist_tag }}" --access public --verbose --dry-run
+
+ # Tag-triggered publishing: publish the single package referenced by the tag.
+ - name: Build project before publish (tag)
+ if: ${{ steps.ctx.outputs.mode == 'tag' }}
+ run: npx nx build "${{ steps.taginfo.outputs.project }}" --verbose
+
+ - name: nx release publish (tag)
+ if: ${{ steps.ctx.outputs.mode == 'tag' && vars.USE_NPM_TOKEN != 'true' }}
+ shell: bash
+ env:
+ NPM_CONFIG_PROVENANCE: true
+ NODE_AUTH_TOKEN: ""
+ run: |
+ set -euo pipefail
+ unset NODE_AUTH_TOKEN
+ rm -f ~/.npmrc || true
+ if [[ -n "${NPM_CONFIG_USERCONFIG:-}" ]]; then
+ rm -f "$NPM_CONFIG_USERCONFIG" || true
+ fi
+
+ npx nx release publish \
+ --projects "${{ steps.taginfo.outputs.project }}" \
+ --tag "${{ steps.taginfo.outputs.dist_tag }}" \
+ --access public \
+ --verbose
+
+ - name: nx release publish (tag, token)
+ if: ${{ steps.ctx.outputs.mode == 'tag' && vars.USE_NPM_TOKEN == 'true' }}
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
+ NPM_CONFIG_PROVENANCE: true
+ run: |
+ npx nx release publish --projects "${{ steps.taginfo.outputs.project }}" --tag "${{ steps.taginfo.outputs.dist_tag }}" --access public --verbose
+
+ - name: Summary
+ if: always()
+ run: |
+ echo "Nx Release completed."
+ echo "- mode: ${{ steps.ctx.outputs.mode }}"
+ echo "- dist-tag: ${{ steps.ctx.outputs.mode == 'tag' && steps.taginfo.outputs.dist_tag || steps.ctx.outputs.dist_tag }}"
+ echo "- scope: '${{ steps.ctx.outputs.scope }}'"
+ echo "- dry-run: ${{ steps.ctx.outputs.dry_run }}"
+ echo "- use-token: ${{ vars.USE_NPM_TOKEN == 'true' }}"
diff --git a/.gitignore b/.gitignore
index 40b510aa45..05192106d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
/dist
/tmp
/out-tsc
+.ns-vite-build
# dependencies
**/node_modules
@@ -57,4 +58,8 @@ Thumbs.db
ios-typings-prj
.nx/cache
-.nx/workspace-data
\ No newline at end of file
+.nx/workspace-data
+vite.config.*.timestamp*
+vitest.config.*.timestamp*
+.cursor/rules/nx-rules.mdc
+.github/instructions/nx.instructions.md
diff --git a/.husky/pre-commit b/.husky/pre-commit
index a3cebf1ec5..abf1181ca3 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,7 +1,4 @@
-#!/bin/sh
-. "$(dirname "$0")/_/husky.sh"
+#!/usr/bin/env sh
+. "$(dirname -- "$0")/_/husky.sh"
-export NVM_DIR="$HOME/.nvm"
-[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
-
-npx lint-staged
+npx lint-staged --allow-empty
diff --git a/README.md b/README.md
index 87bfd5a6b1..02b3bc6655 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,34 @@
[](https://app.fossa.com/projects/git%2Bgithub.com%2FNativeScript%2FNativeScript?ref=badge_large)
+## Quick Start
+
+To get started with NativeScript, follow these steps:
+
+1. **Install the NativeScript CLI globally:**
+ ```bash
+ npm install -g nativescript
+ ```
+
+2. **Create a new project:**
+ ```bash
+ ns create my-app
+ ```
+
+3. **Navigate into your project directory:**
+ ```bash
+ cd my-app
+ ```
+
+4. **Run your app on an emulator or device:**
+ ```bash
+ ns run android
+ ```
+ or
+ ```bash
+ ns run ios
+ ```
+
## Contribute
1. [Setup your local development environment](https://docs.nativescript.org/setup/)
@@ -67,6 +95,7 @@ We love you and your pull requests 🤗. Please follow our [contributing guide](
- Solid starter: https://nativescript.new/solid
- Svelte starter: https://nativescript.new/svelte
- Vue starter: https://nativescript.new/vue
+- Vue 3 starter: https://nativescript.new/vue3
- [NativeScript on Twitter](http://twitter.com/NativeScript)
- [NativeScript on Discord](https://nativescript.org/discord)
- [NativeScript on Stack Overflow](http://stackoverflow.com/questions/tagged/nativescript)
diff --git a/apps/automated/.gitignore b/apps/automated/.gitignore
index 512c68e15a..e9dc6c6f24 100644
--- a/apps/automated/.gitignore
+++ b/apps/automated/.gitignore
@@ -1 +1,2 @@
!webpack.config.js
+hooks
\ No newline at end of file
diff --git a/apps/automated/.npmrc b/apps/automated/.npmrc
deleted file mode 100644
index 3b59692060..0000000000
--- a/apps/automated/.npmrc
+++ /dev/null
@@ -1,6 +0,0 @@
-legacy-peer-deps=true
-
-# npm 9+ this defaults to `true` meaning `file:` dependencies are packed and then installed
-# but since we want to link the source files in this case, we disable this behavior and
-# opt to link the dependency as-is instead. (eg. @nativescript/core)
-install-links=false
diff --git a/apps/automated/nativescript.config.ts b/apps/automated/nativescript.config.ts
index 043f59c668..7dfd261e33 100644
--- a/apps/automated/nativescript.config.ts
+++ b/apps/automated/nativescript.config.ts
@@ -9,5 +9,8 @@ export default {
},
cli: {
packageManager: 'npm',
+ additionalPathsToClean: ['.ns-vite-build'],
},
+ // bundler: 'vite',
+ // bundlerConfigPath: 'vite.config.ts',
} as NativeScriptConfig;
diff --git a/apps/automated/package.json b/apps/automated/package.json
index 41de6a023d..56964cd456 100644
--- a/apps/automated/package.json
+++ b/apps/automated/package.json
@@ -11,12 +11,13 @@
"nativescript-theme-core": "file:../../node_modules/nativescript-theme-core"
},
"devDependencies": {
- "@nativescript/android": "~8.8.0",
- "@nativescript/ios": "~8.8.0",
- "@nativescript/visionos": "~8.7.0",
- "@nativescript/webpack": "file:../../dist/packages/nativescript-webpack.tgz",
+ "@nativescript/android": "~9.0.0",
+ "@nativescript/ios": "~9.0.0",
+ "@nativescript/visionos": "~9.0.0",
+ "@nativescript/vite": "file:../../dist/packages/vite",
+ "@nativescript/webpack": "file:../../dist/packages/webpack5",
"circular-dependency-plugin": "^5.2.2",
- "typescript": "~5.4.0"
+ "typescript": "~5.8.0"
},
"gitHead": "c06800e52ee1a184ea2dffd12a6702aaa43be4e3",
"readme": "NativeScript Application"
diff --git a/apps/automated/project.json b/apps/automated/project.json
index 8244cf69af..6aae5b2a9a 100644
--- a/apps/automated/project.json
+++ b/apps/automated/project.json
@@ -11,53 +11,67 @@
"targets": {
"build": {
"executor": "@nativescript/nx:build",
+ "inputs": ["default", "^production"],
"options": {
"noHmr": true,
"production": true,
"uglify": true,
"release": true,
"forDevice": true
- }
- },
- "ios": {
- "executor": "@nativescript/nx:build",
- "inputs": ["default", "^production"],
- "outputs": [],
- "options": {
- "noHmr": true,
- "platform": "ios"
- }
+ },
+ "configurations": {},
+ "dependsOn": ["^build"]
},
- "vision": {
- "executor": "@nativescript/nx:build",
+ "debug": {
+ "executor": "@nativescript/nx:debug",
"inputs": ["default", "^production"],
- "outputs": [],
"options": {
"noHmr": true,
"debug": false,
- "platform": "vision"
- }
+ "uglify": false,
+ "release": false,
+ "forDevice": false,
+ "prepare": false
+ },
+ "dependsOn": ["^build"]
},
- "android": {
- "executor": "@nativescript/nx:build",
+ "prepare": {
+ "executor": "@nativescript/nx:prepare",
"inputs": ["default", "^production"],
- "outputs": [],
"options": {
"noHmr": true,
- "platform": "android"
- }
+ "production": true,
+ "uglify": true,
+ "release": true,
+ "forDevice": true,
+ "prepare": true
+ },
+ "configurations": {},
+ "dependsOn": ["^build"]
+ },
+ "test": {
+ "executor": "nx:run-commands",
+ "defaultConfiguration": "ios",
+ "configurations": {
+ "ios": {
+ "commands": ["node tools/scripts/run-automated.js ios"]
+ },
+ "android": {
+ "commands": ["node tools/scripts/run-automated.js android"]
+ }
+ },
+ "dependsOn": ["^build"]
},
"clean": {
- "executor": "@nativescript/nx:build",
- "options": {
- "clean": true
- }
+ "executor": "@nativescript/nx:clean",
+ "options": {}
},
"lint": {
- "executor": "@nrwl/linter:eslint",
+ "executor": "@nx/eslint:lint",
"options": {
"lintFilePatterns": ["apps/automated/**/*.ts", "apps/automated/src/**/*.html"]
}
}
- }
+ },
+ "implicitDependencies": ["webpack5", "vite"]
}
diff --git a/apps/automated/src/app-root.ts b/apps/automated/src/app-root.ts
new file mode 100644
index 0000000000..2c052a575f
--- /dev/null
+++ b/apps/automated/src/app-root.ts
@@ -0,0 +1,31 @@
+import { Application, Frame, Page } from '@nativescript/core';
+
+export function onLoaded(args) {
+ try {
+ console.log('[automated] app-root onLoaded');
+ const rootPage = args.object as Page;
+ // Create a Frame and navigate to main-page to ensure code-behind is bound
+ const frame = new Frame();
+ try {
+ frame.navigate('main-page');
+ } catch (e) {
+ try {
+ console.error('[automated] app-root onLoaded: navigate to main-page failed', e);
+ } catch {}
+ }
+ // Replace the temporary Page root with the Frame containing the page
+ try {
+ if ((Application as any).resetRootView) {
+ (Application as any).resetRootView({ create: () => frame });
+ }
+ } catch (e) {
+ try {
+ console.error('[automated] app-root onLoaded: resetRootView failed', e);
+ } catch {}
+ }
+ } catch (e) {
+ try {
+ console.error('[automated] app-root onLoaded failed', e);
+ } catch {}
+ }
+}
diff --git a/apps/automated/src/app-root.xml b/apps/automated/src/app-root.xml
index 54e70d9760..3d2c920bc8 100644
--- a/apps/automated/src/app-root.xml
+++ b/apps/automated/src/app-root.xml
@@ -1,2 +1,3 @@
-
-
+
+
+
diff --git a/apps/automated/src/application/application-tests.android.ts b/apps/automated/src/application/application-tests.android.ts
index c5695ea7e3..3371a7cd0b 100644
--- a/apps/automated/src/application/application-tests.android.ts
+++ b/apps/automated/src/application/application-tests.android.ts
@@ -43,13 +43,14 @@ export function testAndroidApplicationInitialized() {
TKUnit.assert(
// @ts-expect-error
Application.android.foregroundActivity.isNativeScriptActivity,
- 'Android foregroundActivity.isNativeScriptActivity is false.'
+ 'Android foregroundActivity.isNativeScriptActivity is false.',
);
TKUnit.assert(Application.android.startActivity, 'Android startActivity not initialized.');
TKUnit.assert(Application.android.nativeApp, 'Android nativeApp not initialized.');
TKUnit.assert(Application.android.orientation(), 'Android orientation not initialized.');
TKUnit.assert(Utils.android.getPackageName(), 'Android packageName not initialized.');
TKUnit.assert(Application.android.systemAppearance(), 'Android system appearance not initialized.');
+ TKUnit.assert(Application.android.layoutDirection(), 'Android layout direction not initialized.');
}
export function testSystemAppearance() {
diff --git a/apps/automated/src/application/application-tests.ios.ts b/apps/automated/src/application/application-tests.ios.ts
index 19695328da..b60c32f234 100644
--- a/apps/automated/src/application/application-tests.ios.ts
+++ b/apps/automated/src/application/application-tests.ios.ts
@@ -52,6 +52,7 @@ export function testIOSApplicationInitialized() {
TKUnit.assert(Application.ios.systemAppearance(), 'iOS system appearance not initialized.');
}
+ TKUnit.assert(Application.ios.layoutDirection(), 'iOS layout direction not initialized.');
TKUnit.assert(Application.ios.window, 'iOS window not initialized.');
TKUnit.assert(Application.ios.rootController, 'iOS root controller not initialized.');
}
diff --git a/apps/automated/src/xml-parser-tests/xml-with-namespaces.xml b/apps/automated/src/assets/xml-with-namespaces.xml
similarity index 100%
rename from apps/automated/src/xml-parser-tests/xml-with-namespaces.xml
rename to apps/automated/src/assets/xml-with-namespaces.xml
diff --git a/apps/automated/src/xml-parser-tests/xml.expected b/apps/automated/src/assets/xml.expected
similarity index 96%
rename from apps/automated/src/xml-parser-tests/xml.expected
rename to apps/automated/src/assets/xml.expected
index 5cdb47fc44..45a1441970 100644
--- a/apps/automated/src/xml-parser-tests/xml.expected
+++ b/apps/automated/src/assets/xml.expected
@@ -1 +1 @@
-{"eventType":"StartElement","position":{"line":2,"column":1},"elementName":"DocumentElement","attributes":{"param":"value"}}{"eventType":"StartElement","position":{"line":3,"column":3},"elementName":"First.Element","attributes":{"some.attr":"some.value"}}{"eventType":"Text","position":{"line":3,"column":41},"data":"\n ¶ Some Text ®\n "}{"eventType":"EndElement","position":{"line":5,"column":3},"elementName":"First.Element"}{"eventType":"StartElement","position":{"line":7,"column":3},"elementName":"SecondElement","attributes":{"param2":"something"}}{"eventType":"Text","position":{"line":7,"column":37},"data":"\n Pre-Text "}{"eventType":"StartElement","position":{"line":8,"column":14},"elementName":"Inline"}{"eventType":"Text","position":{"line":8,"column":22},"data":"Inlined text"}{"eventType":"EndElement","position":{"line":8,"column":34},"elementName":"Inline"}{"eventType":"Text","position":{"line":8,"column":43},"data":" Post-text.\n "}{"eventType":"EndElement","position":{"line":9,"column":3},"elementName":"SecondElement"}{"eventType":"StartElement","position":{"line":10,"column":3},"elementName":"entities"}{"eventType":"Text","position":{"line":10,"column":13},"data":"Xml tags begin with \"<\" and end with \">\" Ampersand is & and apostrophe is '"}{"eventType":"EndElement","position":{"line":10,"column":123},"elementName":"entities"}{"eventType":"StartElement","position":{"line":11,"column":3},"elementName":"script"}{"eventType":"CDATA","position":{"line":12,"column":5},"data":"\nfunction sum(a,b)\n{\n return a+b;\n}\n"}{"eventType":"EndElement","position":{"line":18,"column":3},"elementName":"script"}{"eventType":"Comment","position":{"line":19,"column":3},"data":"\n Hello,\n I am a multi-line XML comment.\n"}{"eventType":"EndElement","position":{"line":23,"column":1},"elementName":"DocumentElement"}
+{"eventType":"StartElement","position":{"line":2,"column":1},"elementName":"DocumentElement","attributes":{"param":"value"}}{"eventType":"StartElement","position":{"line":3,"column":3},"elementName":"First.Element","attributes":{"some.attr":"some.value"}}{"eventType":"Text","position":{"line":3,"column":41},"data":"\n ¶ Some Text ®\n "}{"eventType":"EndElement","position":{"line":5,"column":3},"elementName":"First.Element"}{"eventType":"StartElement","position":{"line":7,"column":3},"elementName":"SecondElement","attributes":{"param2":"something"}}{"eventType":"Text","position":{"line":7,"column":37},"data":"\n Pre-Text "}{"eventType":"StartElement","position":{"line":8,"column":14},"elementName":"Inline"}{"eventType":"Text","position":{"line":8,"column":22},"data":"Inlined text"}{"eventType":"EndElement","position":{"line":8,"column":34},"elementName":"Inline"}{"eventType":"Text","position":{"line":8,"column":43},"data":" Post-text.\n "}{"eventType":"EndElement","position":{"line":9,"column":3},"elementName":"SecondElement"}{"eventType":"StartElement","position":{"line":10,"column":3},"elementName":"entities"}{"eventType":"Text","position":{"line":10,"column":13},"data":"Xml tags begin with \"<\" and end with \">\" Ampersand is & and apostrophe is '"}{"eventType":"EndElement","position":{"line":10,"column":123},"elementName":"entities"}{"eventType":"StartElement","position":{"line":11,"column":3},"elementName":"script"}{"eventType":"CDATA","position":{"line":12,"column":5},"data":"\nfunction sum(a,b)\n{\n return a+b;\n}\n"}{"eventType":"EndElement","position":{"line":18,"column":3},"elementName":"script"}{"eventType":"Comment","position":{"line":19,"column":3},"data":"\n Hello,\n I am a multi-line XML comment.\n"}{"eventType":"EndElement","position":{"line":23,"column":1},"elementName":"DocumentElement"}
\ No newline at end of file
diff --git a/apps/automated/src/xml-parser-tests/xml.xml b/apps/automated/src/assets/xml.xml
similarity index 96%
rename from apps/automated/src/xml-parser-tests/xml.xml
rename to apps/automated/src/assets/xml.xml
index 93a55b0186..3b9a16b9a5 100644
--- a/apps/automated/src/xml-parser-tests/xml.xml
+++ b/apps/automated/src/assets/xml.xml
@@ -20,4 +20,4 @@ function sum(a,b)
Hello,
I am a multi-line XML comment.
-->
-
+
\ No newline at end of file
diff --git a/apps/automated/src/debugger/dom-node-tests.ts b/apps/automated/src/debugger/dom-node-tests.ts
index 52b8b8ad88..80d76e21c4 100644
--- a/apps/automated/src/debugger/dom-node-tests.ts
+++ b/apps/automated/src/debugger/dom-node-tests.ts
@@ -1,15 +1,9 @@
import { assert, assertEqual } from '../tk-unit';
-import { DOMNode } from '@nativescript/core/debugger/dom-node';
+import { DOMNode } from '@nativescript/core/debugger/dom-types';
import { attachDOMInspectorCommandCallbacks, attachCSSInspectorCommandCallbacks, attachDOMInspectorEventCallbacks } from '@nativescript/core/debugger/devtools-elements';
import { InspectorCommands, InspectorEvents } from '@nativescript/core/debugger/devtools-elements';
-import { unsetValue } from '@nativescript/core/ui/core/properties';
-import { Button } from '@nativescript/core/ui/button';
-import { Slider } from '@nativescript/core/ui/slider';
-import { Label } from '@nativescript/core/ui/label';
+import { unsetValue, Button, Slider, Label, TextView, StackLayout, isAndroid } from '@nativescript/core';
import { textProperty } from '@nativescript/core/ui/text-base';
-import { TextView } from '@nativescript/core/ui/text-view';
-import { StackLayout } from '@nativescript/core/ui/layouts/stack-layout';
-import { isAndroid } from '@nativescript/core/platform';
let originalInspectorGlobal: InspectorCommands & InspectorEvents;
diff --git a/apps/automated/src/file-system/file-system-tests.ts b/apps/automated/src/file-system/file-system-tests.ts
index 559d3126cc..b78254ae01 100644
--- a/apps/automated/src/file-system/file-system-tests.ts
+++ b/apps/automated/src/file-system/file-system-tests.ts
@@ -186,15 +186,15 @@ export var testFileRead = function () {
// << file-system-example-text
};
-export var testFileReadWriteBinary = function () {
+export function testFileReadWriteBinary() {
// >> file-system-read-binary
- var fileName = 'logo.png';
- var error;
+ const fileName = 'logo.png';
+ let error;
+ const appFolder = fs.knownFolders.currentApp().path;
+ const sourceFile = fs.File.fromPath(appFolder + '/assets/' + fileName);
+ const destinationFile = fs.knownFolders.documents().getFile(fileName);
- var sourceFile = fs.File.fromPath(__dirname + '/assets/' + fileName);
- var destinationFile = fs.knownFolders.documents().getFile(fileName);
-
- var source = sourceFile.readSync((e) => {
+ const source = sourceFile.readSync((e) => {
error = e;
});
@@ -203,7 +203,7 @@ export var testFileReadWriteBinary = function () {
});
// >> (hide)
- var destination = destinationFile.readSync((e) => {
+ const destination = destinationFile.readSync((e) => {
error = e;
});
TKUnit.assertNull(error);
@@ -216,14 +216,14 @@ export var testFileReadWriteBinary = function () {
destinationFile.removeSync();
// << (hide)
// << file-system-read-binary
-};
+}
-export var testFileReadWriteBinaryAsync = function () {
+export function testFileReadWriteBinaryAsync() {
// >> file-system-read-binary-async
- var fileName = 'logo.png';
-
- var sourceFile = fs.File.fromPath(__dirname + '/assets/' + fileName);
- var destinationFile = fs.knownFolders.documents().getFile(fileName);
+ const fileName = 'logo.png';
+ const appFolder = fs.knownFolders.currentApp().path;
+ const sourceFile = fs.File.fromPath(appFolder + '/assets/' + fileName);
+ const destinationFile = fs.knownFolders.documents().getFile(fileName);
// Read the file
sourceFile.read().then(
@@ -263,7 +263,7 @@ export var testFileReadWriteBinaryAsync = function () {
},
);
// << file-system-read-binary-async
-};
+}
export var testGetKnownFolders = function () {
// >> file-system-known-folders
@@ -425,11 +425,11 @@ export var testFileNameExtension = function () {
var file = documents.getFile('Test.txt');
// Getting the file name "Test.txt".
var fileName = file.name;
- // Getting the file extension ".txt".
+ // Getting the file extension "txt".
var fileExtension = file.extension;
// >> (hide)
TKUnit.assert(fileName === 'Test.txt', 'Wrong file name.');
- TKUnit.assert(fileExtension === '.txt', 'Wrong extension.');
+ TKUnit.assert(fileExtension === 'txt', 'Wrong extension.');
file.remove();
// << (hide)
// << file-system-extension
@@ -633,7 +633,7 @@ export function test_FSEntity_Properties() {
var documents = fs.knownFolders.documents();
var file = documents.getFile('Test_File.txt');
- TKUnit.assert(file.extension === '.txt', 'FileEntity.extension not working.');
+ TKUnit.assert(file.extension === 'txt', 'FileEntity.extension not working.');
TKUnit.assert(file.isLocked === false, 'FileEntity.isLocked not working.');
TKUnit.assert(file.lastModified instanceof Date, 'FileEntity.lastModified not working.');
TKUnit.assert(file.size === 0, 'FileEntity.size not working.');
@@ -746,7 +746,7 @@ export function testAndroidCreate() {
export function test_FileAppend(done) {
const content = 'Hello World';
- const hello_world = global.isIOS ? NSString.stringWithString(content).dataUsingEncoding(NSUTF8StringEncoding) : new java.lang.String(content).getBytes('UTF-8');
+ const hello_world = __APPLE__ ? NSString.stringWithString(content).dataUsingEncoding(NSUTF8StringEncoding) : new java.lang.String(content).getBytes('UTF-8');
const file = fs.File.fromPath(fs.path.join(fs.knownFolders.temp().path, `${Date.now()}-app.txt`));
file
.appendText('Hello')
diff --git a/apps/automated/src/globals/globals-tests.ts b/apps/automated/src/globals/globals-tests.ts
index 65c10e14d4..9bd0244651 100644
--- a/apps/automated/src/globals/globals-tests.ts
+++ b/apps/automated/src/globals/globals-tests.ts
@@ -23,8 +23,8 @@ export function test_global_registerModule() {
TKUnit.assert(typeof global.registerModule === 'function', 'global.registerModule not a function');
}
-export function test_global_registerWebpackModules() {
- TKUnit.assert(typeof global.registerWebpackModules === 'function', 'global.registerWebpackModules not a function');
+export function test_global_registerBundlerModules() {
+ TKUnit.assert(typeof global.registerBundlerModules === 'function', 'global.registerBundlerModules not a function');
}
export function test_global_loadModule() {
diff --git a/apps/automated/src/http/http-tests.ts b/apps/automated/src/http/http-tests.ts
index 5901bdb880..5272f8759a 100644
--- a/apps/automated/src/http/http-tests.ts
+++ b/apps/automated/src/http/http-tests.ts
@@ -2,7 +2,7 @@ import { ImageSource } from '@nativescript/core';
import * as TKUnit from '../tk-unit';
import * as http from '@nativescript/core/http';
import * as fs from '@nativescript/core/file-system';
-import { addHeader } from '@nativescript/core/http/http-request';
+import { requestInternal, addHeader, BaseHttpContent } from '@nativescript/core/http/http-request-internal';
export var test_getString_isDefined = function () {
TKUnit.assert(typeof http.getString !== 'undefined', 'Method http.getString() should be defined!');
@@ -17,7 +17,7 @@ export var test_getString = function (done: (err: Error, res?: string) => void)
function (e) {
//// Argument (e) is Error!
done(e);
- }
+ },
);
};
@@ -67,7 +67,7 @@ export var test_getJSON = function (done) {
//// Argument (e) is Error!
//console.log(e);
done(e);
- }
+ },
);
};
@@ -115,7 +115,7 @@ export var test_getJSONP = function (done) {
},
function (e) {
done(e);
- }
+ },
);
};
@@ -156,7 +156,7 @@ export var test_gzip_request_explicit = function (done) {
},
function (e) {
done(e);
- }
+ },
);
};
@@ -180,7 +180,7 @@ export var test_gzip_request_implicit = function (done) {
},
function (e) {
done(e);
- }
+ },
);
};
@@ -205,7 +205,7 @@ export var test_getImage = function (done) {
(err) => {
// Argument (e) is Error!
done(err);
- }
+ },
);
};
@@ -258,7 +258,7 @@ export var test_getFile = function (done) {
function (e) {
//// Argument (e) is Error!
done(e);
- }
+ },
);
};
@@ -280,7 +280,7 @@ export var test_getContentAsFile = function (done) {
function (e) {
//// Argument (e) is Error!
done(e);
- }
+ },
);
};
@@ -329,6 +329,89 @@ export var test_request_requestShouldTimeout = function (done) {
});
};
+export var test_requestInternal_responseStatusCodeShouldBeDefined = function (done) {
+ requestInternal({ url: 'https://http-echo.nativescript.org/get', method: 'GET' }).then(
+ function (response) {
+ //// Argument (response) is HttpResponse!
+ var statusCode = response.statusCode;
+ try {
+ TKUnit.assert(typeof statusCode !== 'undefined', 'response.statusCode should be defined!');
+ done(null);
+ } catch (err) {
+ done(err);
+ }
+ },
+ function (e) {
+ //// Argument (e) is Error!
+ done(e);
+ },
+ );
+};
+
+export var test_requestInternal_responseContentShouldExposeNativeContentFunctions = function (done) {
+ requestInternal({ url: 'https://http-echo.nativescript.org/get', method: 'GET' }).then(
+ function (response) {
+ try {
+ TKUnit.assert(typeof response.content.toNativeImage === 'function' && typeof response.content.toNativeString === 'function', `response.content should expose native content functions!`);
+ done(null);
+ } catch (err) {
+ done(err);
+ }
+ },
+ function (e) {
+ //// Argument (e) is Error!
+ done(e);
+ },
+ );
+};
+
+export var test_requestInternal_responseContentShouldExposeHandlerFunctions = function (done) {
+ const responseHandler = {
+ toDummy1: () => 'dummy1',
+ toDummy2: () => 'dummy2',
+ };
+
+ requestInternal({ url: 'https://http-echo.nativescript.org/get', method: 'GET' }, responseHandler).then(
+ function (response) {
+ try {
+ TKUnit.assert(typeof response.content.toDummy1 === 'function' && typeof response.content.toDummy2 === 'function', `response.content should expose content handler functions!`);
+ done(null);
+ } catch (err) {
+ done(err);
+ }
+ },
+ function (e) {
+ //// Argument (e) is Error!
+ done(e);
+ },
+ );
+};
+
+export var test_requestInternal_responseHandlerShouldBeAvailable = function (done) {
+ const suffix = '-nsformatted';
+ const responseHandler = {
+ toFormattedString: function (this: BaseHttpContent) {
+ return this.toNativeString() + suffix;
+ },
+ };
+
+ requestInternal({ url: 'https://http-echo.nativescript.org/get', method: 'GET' }, responseHandler).then(
+ function (response) {
+ const value = response.content.toFormattedString();
+ try {
+ TKUnit.assert(typeof value === 'string' && value.endsWith(suffix), `response.content.toFormattedString should return the response string appended with ${suffix} at the end!`);
+ done(null);
+ } catch (err) {
+ done(err);
+ }
+ },
+ function (e) {
+ //// Argument (e) is Error!
+ done(e);
+ },
+ );
+};
+
export var test_request_responseStatusCodeShouldBeDefined = function (done) {
var result: http.HttpResponse;
@@ -347,7 +430,7 @@ export var test_request_responseStatusCodeShouldBeDefined = function (done) {
function (e) {
//// Argument (e) is Error!
done(e);
- }
+ },
);
};
@@ -363,7 +446,7 @@ export var test_headRequest_responseStatusCodeShouldBeDefined = function (done)
},
function (e) {
done(e);
- }
+ },
);
};
@@ -387,7 +470,7 @@ export var test_request_responseHeadersShouldBeDefined = function (done) {
function (e) {
//// Argument (e) is Error!
done(e);
- }
+ },
);
};
@@ -409,7 +492,7 @@ export var test_request_responseContentShouldBeDefined = function (done) {
function (e) {
//// Argument (e) is Error!
done(e);
- }
+ },
);
};
@@ -428,7 +511,7 @@ export var test_request_responseContentToStringShouldReturnString = function (do
},
function (e) {
done(e);
- }
+ },
);
};
@@ -447,7 +530,7 @@ export var test_request_responseContentToJSONShouldReturnJSON = function (done)
},
function (e) {
done(e);
- }
+ },
);
};
@@ -468,7 +551,7 @@ export var test_request_responseContentToImageShouldReturnCorrectImage = functio
},
function (e) {
done(e);
- }
+ },
);
};
@@ -487,7 +570,7 @@ export var test_request_responseContentToFileFromUrlShouldReturnCorrectFile = fu
},
function (e) {
done(e);
- }
+ },
);
};
export var test_request_responseContentToFileFromUrlShouldReturnCorrectFileAndCreateDirPathIfNecesary = function (done) {
@@ -507,7 +590,7 @@ export var test_request_responseContentToFileFromUrlShouldReturnCorrectFileAndCr
},
function (e) {
done(e);
- }
+ },
);
};
@@ -526,7 +609,7 @@ export var test_request_responseContentToFileFromContentShouldReturnCorrectFile
},
function (e) {
done(e);
- }
+ },
);
};
@@ -551,7 +634,7 @@ export var test_request_headersSentAndReceivedProperly = function (done) {
},
function (e) {
done(e);
- }
+ },
);
};
@@ -597,7 +680,7 @@ export var test_request_contentSentAndReceivedProperly = function (done) {
},
function (e) {
done(e);
- }
+ },
);
};
@@ -627,7 +710,7 @@ export var test_request_FormDataContentSentAndReceivedProperly = function (done)
},
function (e) {
done(e);
- }
+ },
);
};
@@ -655,7 +738,7 @@ export var test_request_NonStringHeadersSentAndReceivedProperly = function (done
},
function (e) {
done(e);
- }
+ },
);
};
@@ -684,12 +767,12 @@ export var test_request_jsonAsContentSentAndReceivedProperly = function (done) {
function (e) {
done(e);
// console.log("Error occurred " + e);
- }
+ },
);
};
export var test_getString_WorksProperlyInWorker = function (done) {
- const worker = new Worker('./http-string-worker');
+ const worker = new Worker(new URL('./http-string-worker', import.meta.url));
console.log('Worker Created');
worker.onmessage = function (msg) {
console.log('Message received');
diff --git a/apps/automated/src/main-page.ts b/apps/automated/src/main-page.ts
index 5a62b4412d..4b15c8855f 100644
--- a/apps/automated/src/main-page.ts
+++ b/apps/automated/src/main-page.ts
@@ -1,7 +1,5 @@
import { Trace, Page } from '@nativescript/core';
-import * as tests from './test-runner';
-
let executeTests = true;
Trace.enable();
@@ -18,10 +16,18 @@ Trace.addCategories(Trace.categories.Test + ',' + Trace.categories.Error);
// ));
function runTests() {
- setTimeout(() => tests.runAll(''), 10);
+ setTimeout(async () => {
+ try {
+ const tests = await import('./test-runner');
+ tests.runAll('');
+ } catch (e) {
+ console.error('[automated] failed to load test-runner', e);
+ }
+ }, 10);
}
export function onNavigatedTo(args) {
+ console.log('onNavigatedTo');
args.object.off(Page.loadedEvent, onNavigatedTo);
if (executeTests) {
executeTests = false;
diff --git a/apps/automated/src/main.ts b/apps/automated/src/main.ts
index 818500f9d3..5271a64c9e 100644
--- a/apps/automated/src/main.ts
+++ b/apps/automated/src/main.ts
@@ -87,13 +87,13 @@ Application.on(Application.lowMemoryEvent, function (args: ApplicationEventData)
// Error events.
Application.on(Application.uncaughtErrorEvent, function (args: UnhandledErrorEventData) {
console.log('NativeScriptError:', args.error);
- console.log(args.error.nativeException ?? (args.error).nativeError);
+ console.log(args.error.nativeException ?? (args.error as any).nativeError);
console.log(args.error.stackTrace ?? args.error.stack);
});
Application.on(Application.discardedErrorEvent, function (args: DiscardedErrorEventData) {
console.log('[Discarded] NativeScriptError:', args.error);
- console.log(args.error.nativeException ?? (args.error).nativeError);
+ console.log(args.error.nativeException ?? (args.error as any).nativeError);
console.log(args.error.stackTrace ?? args.error.stack);
});
@@ -149,5 +149,4 @@ if (typeof NSDate !== 'undefined') {
}
console.log(`TIME TO LOAD APP: ${time} ms`);
-
Application.run({ moduleName: 'app-root' });
diff --git a/apps/automated/src/navigation/custom-transition.ios.ts b/apps/automated/src/navigation/custom-transition.ios.ts
index 16a0138899..644b9c4743 100644
--- a/apps/automated/src/navigation/custom-transition.ios.ts
+++ b/apps/automated/src/navigation/custom-transition.ios.ts
@@ -1,5 +1,4 @@
import { PageTransition, SharedTransition, SharedTransitionAnimationType, SharedTransitionHelper, Transition, Utils } from '@nativescript/core';
-import { CORE_ANIMATION_DEFAULTS } from '@nativescript/core/utils';
export class CustomTransition extends Transition {
constructor(duration: number, curve: any) {
@@ -32,7 +31,7 @@ export class CustomTransition extends Transition {
},
(finished) => {
transitionContext.completeTransition(finished);
- }
+ },
);
}
}
@@ -90,7 +89,7 @@ class PageTransitionController extends NSObject implements UIViewControllerAnima
}
}
}
- return CORE_ANIMATION_DEFAULTS.duration;
+ return Utils.CORE_ANIMATION_DEFAULTS.duration;
}
animateTransition(transitionContext: UIViewControllerContextTransitioning): void {
diff --git a/apps/automated/src/pages/property-bindings.ts b/apps/automated/src/pages/property-bindings.ts
index 12a9ec9fcd..cb7c3f8bca 100644
--- a/apps/automated/src/pages/property-bindings.ts
+++ b/apps/automated/src/pages/property-bindings.ts
@@ -8,7 +8,7 @@ import { Trace } from '@nativescript/core';
import * as gridModule from '@nativescript/core/ui/layouts/grid-layout';
import * as sliders from '@nativescript/core/ui/slider';
import * as switches from '@nativescript/core/ui/switch';
-import * as bindable from '@nativescript/core/ui/core/bindable';
+import type { BindingOptions } from '@nativescript/core/ui/core/bindable/bindable-types';
Trace.enable();
//Trace.setCategories(Trace.categories.Style + " ," + Trace.categories.Binding);
@@ -64,7 +64,7 @@ export function createPage() {
slider.value = desc.value;
stack.addChild(slider);
- var options: bindable.BindingOptions = {
+ var options: BindingOptions = {
sourceProperty: 'value',
targetProperty: desc.name,
};
@@ -82,7 +82,7 @@ export function createPage() {
sw.horizontalAlignment = 'left';
stack.addChild(sw);
- var options: bindable.BindingOptions = {
+ var options: BindingOptions = {
sourceProperty: 'checked',
targetProperty: desc.name,
};
@@ -99,7 +99,7 @@ export function createPage() {
txt.text = desc.value;
stack.addChild(txt);
- var options: bindable.BindingOptions = {
+ var options: BindingOptions = {
sourceProperty: 'text',
targetProperty: desc.name,
};
diff --git a/apps/automated/src/test-runner.ts b/apps/automated/src/test-runner.ts
index 6990aca1a1..fd4ffbca88 100644
--- a/apps/automated/src/test-runner.ts
+++ b/apps/automated/src/test-runner.ts
@@ -2,7 +2,7 @@
import * as TKUnit from './tk-unit';
import './ui-test';
-import { isIOS, isAndroid, Application, Device, platformNames, Trace, Button, Frame, StackLayout, Page, TextView, Utils, Color } from '@nativescript/core';
+import { isAndroid, Device, platformNames, Trace, Button, Frame, StackLayout, Page, TextView, Utils, Color } from '@nativescript/core';
Frame.defaultAnimatedNavigation = false;
export function isRunningOnEmulator(): boolean {
@@ -147,7 +147,7 @@ import * as scrollViewSafeAreaTests from './ui/scroll-view/scroll-view-safe-area
import * as repeaterSafeAreaTests from './ui/repeater/repeater-safe-area-tests';
import * as webViewSafeAreaTests from './ui/web-view/web-view-safe-area-tests';
-if (isIOS && Utils.ios.MajorVersion > 10) {
+if (__APPLE__ && Utils.SDK_VERSION > 10) {
allTests['SAFEAREALAYOUT'] = safeAreaLayoutTests;
allTests['SAFEAREA-LISTVIEW'] = safeAreaListViewtTests;
allTests['SAFEAREA-SCROLL-VIEW'] = scrollViewSafeAreaTests;
@@ -176,6 +176,9 @@ allTests['STYLE'] = styleTests;
import * as visualStateTests from './ui/styling/visual-state-tests';
allTests['VISUAL-STATE'] = visualStateTests;
+import * as cssKeywordsTests from './ui/styling/css-keywords-tests';
+allTests['CSS-KEYWORDS'] = cssKeywordsTests;
+
import * as valueSourceTests from './ui/styling/value-source-tests';
allTests['VALUE-SOURCE'] = valueSourceTests;
@@ -348,6 +351,8 @@ function printRunTestStats() {
let finalMessage = `\n` + `=== ALL TESTS COMPLETE ===\n` + `${allTests.length - failedTestCount} OK, ${failedTestCount} failed\n` + `DURATION: ${totalTime} ms\n` + `=== END OF TESTS ===\n`;
+ Trace.setCategories(Trace.categories.Test);
+ Trace.enable();
TKUnit.write(finalMessage, Trace.messageType.info);
failedTestInfo.forEach((message, i, arr) => {
@@ -494,7 +499,11 @@ export function runAll(testSelector?: string) {
const testModule = allTests[name];
- const test = testModule.createTestCase ? testModule.createTestCase() : testModule;
+ // In ESM environments (like Vite), module namespace objects are not extensible.
+ // Some tests expect to set arbitrary properties like `name` on the test instance.
+ // If a module doesn't provide `createTestCase()`, wrap its exports in a plain
+ // mutable object to safely attach metadata without mutating the namespace object.
+ const test = testModule.createTestCase ? testModule.createTestCase() : ({ ...testModule } as any);
test.name = name;
testsQueue.push(new TestInfo(startLog, test));
diff --git a/apps/automated/src/tk-unit.ts b/apps/automated/src/tk-unit.ts
index c875f60f4c..3c79c6dffc 100644
--- a/apps/automated/src/tk-unit.ts
+++ b/apps/automated/src/tk-unit.ts
@@ -11,8 +11,7 @@
*/
-import { isIOS, Trace, Device } from '@nativescript/core';
-import * as types from '@nativescript/core/utils/types';
+import { Trace, Device, Utils } from '@nativescript/core';
const sdkVersion = parseInt(Device.sdkVersion);
@@ -172,10 +171,10 @@ export function assertFalse(test: boolean, message?: string) {
export function assertNotEqual(actual: any, expected: any, message?: string) {
let equals = false;
- if (types.isUndefined(actual) && types.isUndefined(expected)) {
+ if (Utils.isUndefined(actual) && Utils.isUndefined(expected)) {
equals = true;
- } else if (!types.isNullOrUndefined(actual) && !types.isNullOrUndefined(expected)) {
- if (types.isFunction(actual.equals)) {
+ } else if (!Utils.isNullOrUndefined(actual) && !Utils.isNullOrUndefined(expected)) {
+ if (Utils.isFunction(actual.equals)) {
// Use the equals method
if (actual.equals(expected)) {
equals = true;
@@ -191,7 +190,7 @@ export function assertNotEqual(actual: any, expected: any, message?: string) {
}
export function assertEqual(actual: T, expected: T, message: string = '') {
- if (!types.isNullOrUndefined(actual) && !types.isNullOrUndefined(expected) && types.getClass(actual) === types.getClass(expected) && types.isFunction((actual).equals)) {
+ if (!Utils.isNullOrUndefined(actual) && !Utils.isNullOrUndefined(expected) && Utils.getClass(actual) === Utils.getClass(expected) && Utils.isFunction((actual).equals)) {
// Use the equals method
if (!(actual).equals(expected)) {
throw new Error(`${message} Actual: <${actual}>(${typeof actual}). Expected: <${expected}>(${typeof expected})`);
@@ -352,7 +351,7 @@ export function waitUntilReady(isReady: () => boolean, timeoutSec: number = 5, s
return;
}
- if (isIOS) {
+ if (__APPLE__) {
const timeoutMs = timeoutSec * 1000;
let totalWaitTime = 0;
while (true) {
diff --git a/apps/automated/src/ui-helper.ts b/apps/automated/src/ui-helper.ts
index 487ba37cf0..3e1879dbec 100644
--- a/apps/automated/src/ui-helper.ts
+++ b/apps/automated/src/ui-helper.ts
@@ -1,4 +1,4 @@
-import { Color, Button, FormattedString, Span, ActionBar, Builder, isIOS, unsetValue, View, ViewBase, Frame, NavigationEntry, LayoutBase, StackLayout, Page, TabView, TabViewItem, Utils } from '@nativescript/core';
+import { Color, Button, FormattedString, Span, ActionBar, Builder, unsetValue, View, ViewBase, Frame, NavigationEntry, LayoutBase, StackLayout, Page, TabView, TabViewItem, Utils } from '@nativescript/core';
import * as TKUnit from './tk-unit';
@@ -255,7 +255,7 @@ export function assertTabSelectedTabTextColor(testView: ViewBase, hexColor: stri
}
export function forceGC() {
- if (isIOS) {
+ if (__APPLE__) {
/* tslint:disable:no-unused-expression */
// Could cause GC on the next call.
new ArrayBuffer(4 * 1024 * 1024);
@@ -295,7 +295,7 @@ export function _generateFormattedString(): FormattedString {
export function nativeView_recycling_test(createNew: () => View, createLayout?: () => LayoutBase, nativeGetters?: Map any>, customSetters?: Map) {
return;
- // if (isIOS) {
+ // if (__APPLE__) {
// // recycling not implemented yet.
// return;
// }
diff --git a/apps/automated/src/ui/action-bar/action-bar-tests-common.ts b/apps/automated/src/ui/action-bar/action-bar-tests-common.ts
index b0abe4c7ed..708414ff1f 100644
--- a/apps/automated/src/ui/action-bar/action-bar-tests-common.ts
+++ b/apps/automated/src/ui/action-bar/action-bar-tests-common.ts
@@ -1,16 +1,6 @@
import * as TKUnit from '../../tk-unit';
import * as helper from '../../ui-helper';
-import { Builder } from '@nativescript/core/ui/builder';
-import { Label } from '@nativescript/core/ui/label';
-import { Button } from '@nativescript/core/ui/button';
-import { Page } from '@nativescript/core/ui/page';
-import { View, isIOS } from '@nativescript/core';
-import { fromObject } from '@nativescript/core/data/observable';
-import { Frame } from '@nativescript/core/ui/frame';
-
-// >> actionbar-common-require
-import * as actionBarModule from '@nativescript/core/ui/action-bar';
-// << actionbar-common-require
+import { View, fromObject, Frame, Page, Button, Label, Builder, ActionItem, ActionBar } from '@nativescript/core';
export function test_actionItem_inherit_bindingContext() {
let page: Page;
@@ -20,7 +10,7 @@ export function test_actionItem_inherit_bindingContext() {
const pageFactory = function (): Page {
page = new Page();
page.bindingContext = context;
- const actionItem = new actionBarModule.ActionItem();
+ const actionItem = new ActionItem();
actionItem.bind({
sourceProperty: 'text',
@@ -164,7 +154,7 @@ export function test_Setting_ActionItems_doesnt_thrown() {
const pageFactory = function (): Page {
page = new Page();
- const actionItem = new actionBarModule.ActionItem();
+ const actionItem = new ActionItem();
actionItem.text = 'Item';
page.actionBar.actionItems.addItem(actionItem);
@@ -286,12 +276,12 @@ export function test_ActionBarVisibility_Never_ShouldNotShowDeclaredActionBar()
- `
+ `,
);
helper.navigate(() => page);
let actionBarHidden = false;
- if (isIOS) {
+ if (__APPLE__) {
actionBarHidden = page.actionBar.nativeView.hidden;
} else {
actionBarHidden = !page.actionBar.nativeView.isShown();
@@ -314,12 +304,12 @@ export function test_ActionBarVisibility_Always_ShouldShownHiddenActionBar() {
- `
+ `,
);
helper.navigate(() => page);
let actionBarHidden = false;
- if (isIOS) {
+ if (__APPLE__) {
actionBarHidden = page.actionBar.nativeView.hidden;
} else {
actionBarHidden = !page.actionBar.nativeView.isShown();
@@ -342,12 +332,12 @@ export function test_ActionBarVisibility_Auto_ShouldRespectPageActionBarHiddenPr
- `
+ `,
);
helper.navigate(() => page);
let actionBarHidden = false;
- if (isIOS) {
+ if (__APPLE__) {
actionBarHidden = page.actionBar.nativeView.hidden;
} else {
actionBarHidden = !page.actionBar.nativeView.isShown();
@@ -391,5 +381,5 @@ export function createPageAndNavigate() {
}
export function test_recycling() {
- helper.nativeView_recycling_test(() => new actionBarModule.ActionBar());
+ helper.nativeView_recycling_test(() => new ActionBar());
}
diff --git a/apps/automated/src/ui/activity-indicator/activity-indicator-tests.ts b/apps/automated/src/ui/activity-indicator/activity-indicator-tests.ts
index 2b9c32a3a4..b06a470ec8 100644
--- a/apps/automated/src/ui/activity-indicator/activity-indicator-tests.ts
+++ b/apps/automated/src/ui/activity-indicator/activity-indicator-tests.ts
@@ -49,8 +49,8 @@ export function test_set_TNS_value_updates_native_value() {
}
// Uncomment this when find way to check android Drawable color set by setColorFilter() method.
-if (__APPLE__) {
- exports.test_set_color = function () {
+export function test_set_color() {
+ if (__APPLE__) {
var ai = new activityIndicatorModule.ActivityIndicator();
ai.color = new color.Color('red');
@@ -59,7 +59,7 @@ if (__APPLE__) {
}
helper.buildUIAndRunTest(ai, testAction);
- };
+ }
}
// This method is only for the code snippet
diff --git a/apps/automated/src/ui/animation/animation-tests.ts b/apps/automated/src/ui/animation/animation-tests.ts
index ad75839ce4..75bbbd6f0d 100644
--- a/apps/automated/src/ui/animation/animation-tests.ts
+++ b/apps/automated/src/ui/animation/animation-tests.ts
@@ -1,11 +1,6 @@
import * as TKUnit from '../../tk-unit';
import * as helper from '../../ui-helper';
-import * as viewModule from '@nativescript/core/ui/core/view';
-import { Label } from '@nativescript/core/ui/label';
-import { StackLayout } from '@nativescript/core/ui/layouts/stack-layout';
-import * as colorModule from '@nativescript/core/color';
-import { CoreTypes, PercentLength } from '@nativescript/core';
-import { AnimationPromise, AnimationDefinition, Animation } from '@nativescript/core/ui/animation';
+import { AnimationPromise, AnimationDefinition, Animation, CoreTypes, PercentLength, Label, StackLayout, View, Color } from '@nativescript/core';
// >> animation-require
// import * as animation from '@nativescript/core/ui/animation';
@@ -38,7 +33,7 @@ export function test_AnimatingProperties(done) {
label
.animate({
opacity: 0.75,
- backgroundColor: new colorModule.Color('Red'),
+ backgroundColor: new Color('Red'),
translate: { x: 100, y: 100 },
scale: { x: 2, y: 2 },
rotate: 180,
@@ -79,7 +74,7 @@ export function test_PlayRejectsWhenAlreadyPlayingAnimation(done) {
if (e === 'Animation is already playing.') {
done();
}
- }
+ },
);
}
@@ -269,7 +264,7 @@ export function test_AnimateOpacity(done) {
export function test_AnimateOpacity_ShouldThrow_IfNotNumber() {
var label = new Label();
- helper.buildUIAndRunTest(label, (views: Array) => {
+ helper.buildUIAndRunTest(label, (views: Array) => {
TKUnit.assertThrows(() => {
label.animate({ opacity: '0.75' });
}, 'Setting opacity to a non number should throw.');
@@ -278,7 +273,7 @@ export function test_AnimateOpacity_ShouldThrow_IfNotNumber() {
export function test_AnimateDelay_ShouldThrow_IfNotNumber() {
var label = new Label();
- helper.buildUIAndRunTest(label, (views: Array) => {
+ helper.buildUIAndRunTest(label, (views: Array) => {
TKUnit.assertThrows(() => {
label.animate({ delay: '1' });
}, 'Setting delay to a non number should throw.');
@@ -287,7 +282,7 @@ export function test_AnimateDelay_ShouldThrow_IfNotNumber() {
export function test_AnimateDuration_ShouldThrow_IfNotNumber() {
var label = new Label();
- helper.buildUIAndRunTest(label, (views: Array) => {
+ helper.buildUIAndRunTest(label, (views: Array) => {
TKUnit.assertThrows(() => {
label.animate({ duration: '1' });
}, 'Setting duration to a non number should throw.');
@@ -296,7 +291,7 @@ export function test_AnimateDuration_ShouldThrow_IfNotNumber() {
export function test_AnimateIterations_ShouldThrow_IfNotNumber() {
var label = new Label();
- helper.buildUIAndRunTest(label, (views: Array) => {
+ helper.buildUIAndRunTest(label, (views: Array) => {
TKUnit.assertThrows(() => {
label.animate({ iterations: '1' });
}, 'Setting iterations to a non number should throw.');
@@ -305,7 +300,7 @@ export function test_AnimateIterations_ShouldThrow_IfNotNumber() {
export function test_AnimateRotate_ShouldThrow_IfNotNumber() {
var label = new Label();
- helper.buildUIAndRunTest(label, (views: Array) => {
+ helper.buildUIAndRunTest(label, (views: Array) => {
TKUnit.assertThrows(() => {
label.animate({ rotate: '1' });
}, 'Setting rotate to a non number should throw.');
@@ -314,7 +309,7 @@ export function test_AnimateRotate_ShouldThrow_IfNotNumber() {
export function test_AnimateScale_ShouldThrow_IfNotPair() {
var label = new Label();
- helper.buildUIAndRunTest(label, (views: Array) => {
+ helper.buildUIAndRunTest(label, (views: Array) => {
TKUnit.assertThrows(() => {
label.animate({ scale: '1' });
}, 'Setting scale to a non Pair should throw.');
@@ -323,7 +318,7 @@ export function test_AnimateScale_ShouldThrow_IfNotPair() {
export function test_AnimateTranslate_ShouldThrow_IfNotPair() {
var label = new Label();
- helper.buildUIAndRunTest(label, (views: Array) => {
+ helper.buildUIAndRunTest(label, (views: Array) => {
TKUnit.assertThrows(() => {
label.animate({ translate: '1' });
}, 'Setting translate to a non Pair should throw.');
@@ -332,7 +327,7 @@ export function test_AnimateTranslate_ShouldThrow_IfNotPair() {
export function test_AnimateBackgroundColor_ShouldThrow_IfNotValidColorStringOrColor() {
var label = new Label();
- helper.buildUIAndRunTest(label, (views: Array) => {
+ helper.buildUIAndRunTest(label, (views: Array) => {
TKUnit.assertThrows(() => {
label.animate({ backgroundColor: 'test' });
}, 'Setting backgroundColor to a not valid color string or Color should throw.');
@@ -341,12 +336,12 @@ export function test_AnimateBackgroundColor_ShouldThrow_IfNotValidColorStringOrC
export function test_AnimateBackgroundColor(done) {
let label = prepareTest();
- var red = new colorModule.Color('Red');
+ var red = new Color('Red');
label
.animate({ backgroundColor: red, duration: 5 })
.then(() => {
- TKUnit.assert((label.backgroundColor).equals(red));
+ TKUnit.assert((label.backgroundColor).equals(red));
done();
})
.catch((e) => {
@@ -357,12 +352,12 @@ export function test_AnimateBackgroundColor(done) {
export function test_AnimateBackgroundColor_FromString(done) {
let label = prepareTest();
var expected = 'Red';
- var clr = new colorModule.Color(expected);
+ var clr = new Color(expected);
label
.animate({ backgroundColor: expected, duration: 5 })
.then(() => {
- TKUnit.assert((label.backgroundColor).equals(clr));
+ TKUnit.assert((label.backgroundColor).equals(clr));
done();
})
.catch((e) => {
@@ -505,7 +500,7 @@ export function test_AnimateExtent_Should_AcceptNumberValuesAsDip(done) {
export function test_AnimateExtent_Should_ThrowIfCannotParsePercentLength() {
const label = new Label();
- helper.buildUIAndRunTest(label, (views: Array) => {
+ helper.buildUIAndRunTest(label, (views: Array) => {
TKUnit.assertThrows(() => {
label.animate({ width: '-frog%' });
}, 'Invalid percent string should throw');
@@ -610,7 +605,7 @@ export function test_PlayPromiseIsResolvedWhenAnimationFinishes(done) {
function onRejected(e) {
TKUnit.assert(false, 'Animation play promise should be resolved, not rejected.');
done(e);
- }
+ },
);
}
@@ -627,14 +622,14 @@ export function test_PlayPromiseIsRejectedWhenAnimationIsCancelled(done) {
function onRejected(e) {
TKUnit.assert(animation.isPlaying === false, 'Animation.isPlaying should be false when animation play promise is rejected.');
done();
- }
+ },
);
animation.cancel();
}
-function assertIOSNativeTransformIsCorrect(view: viewModule.View) {
- if (global.isIOS) {
+function assertIOSNativeTransformIsCorrect(view: View) {
+ if (__APPLE__) {
var errorMessage = (require('@nativescript/core/ui/animation'))._getTransformMismatchErrorMessage(view);
if (errorMessage) {
TKUnit.assert(false, errorMessage);
diff --git a/apps/automated/src/ui/button/button-tests.ts b/apps/automated/src/ui/button/button-tests.ts
index cc3dfd7036..07f9a1b112 100644
--- a/apps/automated/src/ui/button/button-tests.ts
+++ b/apps/automated/src/ui/button/button-tests.ts
@@ -1,12 +1,7 @@
import * as TKUnit from '../../tk-unit';
import * as helper from '../../ui-helper';
-import { View, EventData, Button, Observable, Color, Page, FormattedString } from '@nativescript/core';
+import { View, EventData, Button, Observable, Color, Page, FormattedString, BindingOptions, Span } from '@nativescript/core';
import * as buttonTestsNative from './button-tests-native';
-import * as spanModule from '@nativescript/core/text/span';
-
-// >> button-require-others
-import { BindingOptions } from '@nativescript/core/ui/core/bindable';
-// << button-require-others
export function test_recycling() {
helper.nativeView_recycling_test(() => new Button());
@@ -274,7 +269,7 @@ export var test_StateHighlighted_also_fires_pressedState = function () {
helper.waitUntilLayoutReady(view);
- view._goToVisualState('highlighted');
+ view._addVisualState('highlighted');
var actualResult = buttonTestsNative.getNativeBackgroundColor(view);
TKUnit.assert(actualResult.hex === expectedNormalizedColor, 'Actual: ' + actualResult.hex + '; Expected: ' + expectedNormalizedColor);
@@ -291,7 +286,7 @@ export var test_StateHighlighted_also_fires_activeState = function () {
helper.waitUntilLayoutReady(view);
- view._goToVisualState('highlighted');
+ view._addVisualState('highlighted');
var actualResult = buttonTestsNative.getNativeBackgroundColor(view);
TKUnit.assert(actualResult.hex === expectedNormalizedColor, 'Actual: ' + actualResult.hex + '; Expected: ' + expectedNormalizedColor);
@@ -326,13 +321,13 @@ export var testNativeTextAlignmentFromLocal = function () {
};
export var test_WhenFormattedTextPropertyChanges_TextIsUpdated_Button = function () {
- var firstSpan = new spanModule.Span();
+ var firstSpan = new Span();
firstSpan.fontSize = 10;
firstSpan.text = 'First';
- var secondSpan = new spanModule.Span();
+ var secondSpan = new Span();
secondSpan.fontSize = 15;
secondSpan.text = 'Second';
- var thirdSpan = new spanModule.Span();
+ var thirdSpan = new Span();
thirdSpan.fontSize = 20;
thirdSpan.text = 'Third';
var formattedString1 = new FormattedString();
@@ -392,7 +387,7 @@ export function test_setting_formattedText_With_UnknownFont_DoesNotCrash() {
helper.buildUIAndRunTest(btn, function (views) {
TKUnit.waitUntilReady(() => btn.isLayoutValid);
- let span = new spanModule.Span();
+ let span = new Span();
span.text = 'Login';
let formattedString = new FormattedString();
formattedString.spans.push(span);
diff --git a/apps/automated/src/ui/core/bindable/bindable-tests.ts b/apps/automated/src/ui/core/bindable/bindable-tests.ts
index 8007ee0ff3..825db61c91 100644
--- a/apps/automated/src/ui/core/bindable/bindable-tests.ts
+++ b/apps/automated/src/ui/core/bindable/bindable-tests.ts
@@ -1,11 +1,7 @@
-import { Observable, fromObject, fromObjectRecursive } from '@nativescript/core/data/observable';
-import { ViewBase } from '@nativescript/core/ui/core/view-base';
-import { BindingOptions } from '@nativescript/core/ui/core/bindable';
+import { Application, View, Button, Page, StackLayout, Label, TextField, Trace, BindingOptions, ViewBase, Observable, fromObject, fromObjectRecursive, Utils } from '@nativescript/core';
import * as TKUnit from '../../../tk-unit';
-import * as types from '@nativescript/core/utils/types';
import * as helper from '../../../ui-helper';
import * as bindingBuilder from '@nativescript/core/ui/builder/binding-builder';
-import { Application, View, Button, Page, StackLayout, Label, TextField, Trace } from '@nativescript/core';
declare var WeakRef: any;
//
// For information and examples how to use bindings please refer to special [**Data binding**](../../../../bindings.md) topic.
@@ -17,8 +13,8 @@ declare var WeakRef: any;
export function test_Bindable_Members() {
const obj = new Label();
- TKUnit.assert(types.isDefined(obj.bind), 'Bindable.bind not defined');
- TKUnit.assert(types.isDefined(obj.unbind), 'Bindable.unbind not defined');
+ TKUnit.assert(Utils.isDefined(obj.bind), 'Bindable.bind not defined');
+ TKUnit.assert(Utils.isDefined(obj.unbind), 'Bindable.unbind not defined');
}
export function test_Binding_to_bindingContext_of_View() {
@@ -502,7 +498,7 @@ export function test_bindingToNestedPropertyWithValueSyntax() {
sourceProperty: '$value.testProperty',
targetProperty: 'targetPropertyName',
},
- bindingSource
+ bindingSource,
);
TKUnit.assertEqual(testElement.get('targetPropertyName'), 'testValue');
@@ -706,7 +702,7 @@ export function test_UpdatingNestedPropertyViaBinding() {
targetProperty: 'text',
twoWay: true,
},
- viewModel
+ viewModel,
);
const testElement2 = new Label();
@@ -717,7 +713,7 @@ export function test_UpdatingNestedPropertyViaBinding() {
targetProperty: 'text',
twoWay: true,
},
- viewModel
+ viewModel,
);
TKUnit.assertEqual(testElement.get('text'), expectedValue1);
@@ -803,7 +799,7 @@ export function test_NestedPropertiesBinding() {
targetProperty: 'targetProperty',
twoWay: true,
},
- viewModel
+ viewModel,
);
TKUnit.assertEqual(target1.get('targetProperty'), expectedValue);
@@ -835,7 +831,7 @@ export function test_WrongNestedPropertiesBinding() {
targetProperty: 'targetProperty',
twoWay: true,
},
- viewModel
+ viewModel,
);
TKUnit.assertNotEqual(errorMessage, undefined);
@@ -856,7 +852,7 @@ export function test_NestedPropertiesBindingTwoTargets() {
targetProperty: 'targetProperty',
twoWay: true,
},
- viewModel
+ viewModel,
);
const target2 = new Label();
@@ -866,7 +862,7 @@ export function test_NestedPropertiesBindingTwoTargets() {
targetProperty: 'targetProp',
twoWay: true,
},
- viewModel
+ viewModel,
);
TKUnit.assertEqual(target1.get('targetProperty'), expectedText);
@@ -897,7 +893,7 @@ export function test_NestedPropertiesBindingTwoTargetsAndSecondChange() {
targetProperty: 'targetProperty',
twoWay: true,
},
- viewModel
+ viewModel,
);
const target2 = new Label();
@@ -907,7 +903,7 @@ export function test_NestedPropertiesBindingTwoTargetsAndSecondChange() {
targetProperty: 'targetProp',
twoWay: true,
},
- viewModel
+ viewModel,
);
TKUnit.assertEqual(target1.get('targetProperty'), expectedText);
@@ -948,7 +944,7 @@ export function test_NestedPropertiesBindingTwoTargetsAndRegularChange() {
targetProperty: 'targetProperty',
twoWay: true,
},
- viewModel
+ viewModel,
);
const target2 = new Label();
@@ -958,7 +954,7 @@ export function test_NestedPropertiesBindingTwoTargetsAndRegularChange() {
targetProperty: 'targetProp',
twoWay: true,
},
- viewModel
+ viewModel,
);
TKUnit.assertEqual(target1.get('targetProperty'), expectedText);
@@ -998,7 +994,7 @@ export function test_NestedPropertiesBindingTwoTargetsAndReplacingSomeNestedObje
targetProperty: 'targetProperty',
twoWay: true,
},
- viewModel
+ viewModel,
);
const target2 = new Label();
@@ -1008,7 +1004,7 @@ export function test_NestedPropertiesBindingTwoTargetsAndReplacingSomeNestedObje
targetProperty: 'targetProp',
twoWay: true,
},
- viewModel
+ viewModel,
);
TKUnit.assertEqual(target1.get('targetProperty'), expectedText);
@@ -1050,7 +1046,7 @@ export function test_NullSourcePropertyShouldNotCrash() {
targetProperty: 'targetProp',
expression: 'convFunc(field)',
},
- model
+ model,
);
TKUnit.assertEqual(target.get('targetProp'), convFunc(expectedValue));
@@ -1123,7 +1119,7 @@ export function test_SupportFunctionsInExpressions() {
targetProperty: 'text',
expression: "isVisible() ? 'visible' : 'collapsed'",
},
- model
+ model,
);
model.set('anyColor', 'blue');
@@ -1151,7 +1147,7 @@ export function test_$ValueSupportWithinExpression() {
targetProperty: 'text',
expression: "$value.anyColor === 'red' ? 'red' : 'blue'",
},
- model
+ model,
);
model.set('anyColor', 'blue');
@@ -1230,7 +1226,7 @@ export function test_BindingToPropertiesWithSameNames() {
targetProperty: 'targetProperty',
twoWay: true,
},
- model
+ model,
);
const target2 = new Label();
@@ -1240,7 +1236,7 @@ export function test_BindingToPropertiesWithSameNames() {
targetProperty: 'targetProp',
twoWay: true,
},
- model
+ model,
);
model.item.set('seconds', model.item.seconds + 1);
@@ -1273,7 +1269,7 @@ export function test_BindingToPropertiesWithSameNamesSecondCase() {
targetProperty: 'targetProperty',
twoWay: true,
},
- model
+ model,
);
const target2 = new Label();
@@ -1283,7 +1279,7 @@ export function test_BindingToPropertiesWithSameNamesSecondCase() {
targetProperty: 'targetProp',
twoWay: true,
},
- model
+ model,
);
model.item.set('seconds', model.item.seconds + 1);
@@ -1372,7 +1368,7 @@ export function test_Observable_from_nested_json_binds_correctly() {
sourceProperty: 'firstObject.secondObject.dummyProperty',
targetProperty: 'text',
},
- model
+ model,
);
model.get('firstObject').get('secondObject').set('dummyProperty', expectedValue);
@@ -1396,7 +1392,7 @@ export function test_Observable_from_nested_json_binds_correctly_when_upper_obje
sourceProperty: 'firstObject.secondObject.dummyProperty',
targetProperty: 'text',
},
- model
+ model,
);
model.get('firstObject').set('secondObject', fromObject({ dummyProperty: expectedValue }));
diff --git a/apps/automated/src/ui/frame/frame-tests.android.ts b/apps/automated/src/ui/frame/frame-tests.android.ts
index f4df1d9160..0a18547250 100644
--- a/apps/automated/src/ui/frame/frame-tests.android.ts
+++ b/apps/automated/src/ui/frame/frame-tests.android.ts
@@ -1,7 +1,5 @@
-import { Frame } from '@nativescript/core/ui/frame';
import * as TKUnit from '../../tk-unit';
-import { unsetValue } from '@nativescript/core/ui/core/view';
-import { PercentLength } from '@nativescript/core/ui/styling/style-properties';
+import { PercentLength, unsetValue, Frame } from '@nativescript/core';
export * from './frame-tests-common';
export function test_percent_width_and_height_set_to_page_support() {
diff --git a/apps/automated/src/ui/image/image-tests.ts b/apps/automated/src/ui/image/image-tests.ts
index 0ea34140c8..20e129e235 100644
--- a/apps/automated/src/ui/image/image-tests.ts
+++ b/apps/automated/src/ui/image/image-tests.ts
@@ -15,7 +15,7 @@ import { ImageSource } from '@nativescript/core/image-source';
import * as ViewModule from '@nativescript/core/ui/core/view';
import * as helper from '../../ui-helper';
import * as color from '@nativescript/core/color';
-import * as backgroundModule from '@nativescript/core/ui/styling/background';
+import * as appHelpers from '@nativescript/core/application/helpers-common';
import { Application } from '@nativescript/core';
const imagePath = '~/assets/logo.png';
@@ -23,8 +23,8 @@ export function test_recycling() {
helper.nativeView_recycling_test(() => new Image());
}
-if (global.isAndroid) {
- (backgroundModule).initImageCache(Application.android.startActivity, (backgroundModule).CacheMode.memory); // use memory cache only.
+if (__ANDROID__) {
+ appHelpers.initImageCache(Application.android.startActivity, appHelpers.CacheMode.memory); // use memory cache only.
}
export const test_Image_Members = function () {
@@ -63,7 +63,7 @@ function runImageTestSync(image: ImageModule.Image, src: string) {
image.src = src;
- let imageSourceAvailable = global.isIOS ? !!image.imageSource : true;
+ let imageSourceAvailable = __APPLE__ ? !!image.imageSource : true;
TKUnit.assertFalse(image.isLoading, 'Image.isLoading should be false.');
TKUnit.assertTrue(imageSourceAvailable, 'imageSource should be set.');
}
@@ -76,7 +76,7 @@ function runImageTestAsync(image: ImageModule.Image, src: string, done: (e: any)
image.off(IMAGE_LOADED_EVENT, handler);
try {
- let imageSourceAvailable = global.isIOS ? !!image.imageSource : true;
+ let imageSourceAvailable = __APPLE__ ? !!image.imageSource : true;
TKUnit.assertFalse(image.isLoading, 'Image.isLoading should be false.');
TKUnit.assertTrue(imageSourceAvailable, 'imageSource should be set.');
done(null);
@@ -253,7 +253,7 @@ export const test_SettingStretch_none = function () {
};
function ios(func: T): T {
- return global.isIOS ? func : undefined;
+ return __APPLE__ ? func : undefined;
}
export const test_SettingImageSourceWhenSizedToParentDoesNotRequestLayout = ios(() => {
@@ -267,7 +267,11 @@ export const test_SettingImageSourceWhenSizedToParentDoesNotRequestLayout = ios(
let mainPage = helper.getCurrentPage();
mainPage.content = host;
- TKUnit.waitUntilReady(() => host.isLoaded);
+
+ const nativeHostView = host.nativeViewProtected as UIView;
+
+ // Check if native view layer is still marked as dirty before proceeding
+ TKUnit.waitUntilReady(() => host.isLoaded && nativeHostView?.layer && !nativeHostView.layer.needsLayout());
let called = false;
image.requestLayout = () => (called = true);
@@ -285,7 +289,11 @@ export const test_SettingImageSourceWhenFixedWidthAndHeightDoesNotRequestLayout
let mainPage = helper.getCurrentPage();
mainPage.content = host;
- TKUnit.waitUntilReady(() => host.isLoaded);
+
+ const nativeHostView = host.nativeViewProtected as UIView;
+
+ // Check if native view layer is still marked as dirty before proceeding
+ TKUnit.waitUntilReady(() => host.isLoaded && nativeHostView?.layer && !nativeHostView.layer.needsLayout());
let called = false;
image.requestLayout = () => (called = true);
diff --git a/apps/automated/src/ui/label/label-tests-native.android.ts b/apps/automated/src/ui/label/label-tests-native.android.ts
index c91ea1afd1..8437beab29 100644
--- a/apps/automated/src/ui/label/label-tests-native.android.ts
+++ b/apps/automated/src/ui/label/label-tests-native.android.ts
@@ -2,22 +2,29 @@ import * as labelModule from '@nativescript/core/ui/label';
import { Color, CoreTypes } from '@nativescript/core';
import { AndroidHelper } from '@nativescript/core/ui/core/view';
+const UNEXPECTED_VALUE = 'unexpected value';
+
export function getNativeTextAlignment(label: labelModule.Label): string {
- let gravity = label.android.getGravity();
+ let hGravity = label.android.getGravity() & android.view.Gravity.HORIZONTAL_GRAVITY_MASK;
+ const alignment = label.android.getTextAlignment();
+
+ if (hGravity === android.view.Gravity.START && alignment === android.view.View.TEXT_ALIGNMENT_VIEW_START) {
+ return 'initial';
+ }
- if ((gravity & android.view.Gravity.HORIZONTAL_GRAVITY_MASK) === android.view.Gravity.LEFT) {
+ if (hGravity === android.view.Gravity.LEFT && alignment === android.view.View.TEXT_ALIGNMENT_GRAVITY) {
return CoreTypes.TextAlignment.left;
}
- if ((gravity & android.view.Gravity.HORIZONTAL_GRAVITY_MASK) === android.view.Gravity.CENTER_HORIZONTAL) {
+ if (hGravity === android.view.Gravity.CENTER_HORIZONTAL && alignment === android.view.View.TEXT_ALIGNMENT_CENTER) {
return CoreTypes.TextAlignment.center;
}
- if ((gravity & android.view.Gravity.HORIZONTAL_GRAVITY_MASK) === android.view.Gravity.RIGHT) {
+ if (hGravity === android.view.Gravity.RIGHT && alignment === android.view.View.TEXT_ALIGNMENT_GRAVITY) {
return CoreTypes.TextAlignment.right;
}
- return 'unexpected value';
+ return UNEXPECTED_VALUE;
}
export function getNativeBackgroundColor(label: labelModule.Label): Color {
diff --git a/apps/automated/src/ui/label/label-tests-native.d.ts b/apps/automated/src/ui/label/label-tests-native.d.ts
index dd7dea90ae..77037e0cf4 100644
--- a/apps/automated/src/ui/label/label-tests-native.d.ts
+++ b/apps/automated/src/ui/label/label-tests-native.d.ts
@@ -3,5 +3,4 @@ import * as labelModule from '@nativescript/core/ui/label';
import * as colorModule from '@nativescript/core/color';
export declare function getNativeTextAlignment(label: labelModule.Label): string;
-
export declare function getNativeBackgroundColor(label: labelModule.Label): colorModule.Color;
diff --git a/apps/automated/src/ui/label/label-tests.ts b/apps/automated/src/ui/label/label-tests.ts
index 72648865fa..9696aca3fe 100644
--- a/apps/automated/src/ui/label/label-tests.ts
+++ b/apps/automated/src/ui/label/label-tests.ts
@@ -1,49 +1,32 @@
import * as TKUnit from '../../tk-unit';
import * as testModule from '../../ui-test';
-
-//>> label-require
-import * as LabelModule from '@nativescript/core/ui/label';
-// << label-require
-
-import * as types from '@nativescript/core/utils/types';
-import * as colorModule from '@nativescript/core/color';
-import * as utils from '@nativescript/core/utils';
-import * as observableModule from '@nativescript/core/data/observable';
-import * as bindable from '@nativescript/core/ui/core/bindable';
-import { CoreTypes, Span, FormattedString } from '@nativescript/core';
+import { Label, GridLayout, LayoutBase, StackLayout, BindingOptions, CoreTypes, Span, FormattedString, Utils, Color, Observable, path } from '@nativescript/core';
import * as labelTestsNative from './label-tests-native';
-import * as fs from '@nativescript/core/file-system';
-
-import { StackLayout } from '@nativescript/core/ui/layouts/stack-layout';
-import { GridLayout } from '@nativescript/core/ui/layouts/grid-layout';
-import { isIOS, isAndroid, isApple } from '@nativescript/core/platform';
-import { Label } from '@nativescript/core/ui/label';
-import { LayoutBase } from '@nativescript/core/ui/layouts/layout-base';
import * as helper from '../../ui-helper';
const testDir = 'ui/label';
-export class LabelTest extends testModule.UITest {
- public create(): LabelModule.Label {
- const label = new LabelModule.Label();
+export class LabelTest extends testModule.UITest